# Adblock Module for Leaf-Web Browser

Complete WebKitUserContentFilter-based ad blocker implementation for WebKit2GTK.

## Overview

This module provides production-ready ad blocking functionality using WebKit's built-in content filtering API. It:

- Loads filter lists in AdBlock Plus format (e.g., EasyList)
- Converts them to WebKit's JSON content blocker format
- Compiles and caches filters efficiently
- Applies filters to all WebViews in your browser

## Features

✅ **Asynchronous filter compilation** - doesn't block the UI  
✅ **Persistent filter caching** - filters compile once and persist  
✅ **Multiple filter list support** - load EasyList, EasyPrivacy, etc.  
✅ **Enable/disable toggle** - runtime control  
✅ **GObject conventions** - clean C API  
✅ **Production-ready** - full error handling and logging  

## Files

```
src/extensions/adblock/
├── adblock.h                  - Public API header
├── adblock.c                  - Implementation
├── easylist.txt               - EasyList filter (77k+ rules)
├── EXAMPLE_INTEGRATION.c      - Integration examples
└── README.md                  - This file
```

## Dependencies

Add these to your build system:

```meson
dependencies: [
  dependency('webkit2gtk-4.0'),
  dependency('json-glib-1.0'),
  dependency('gtk+-3.0')
]
```

Or for pkg-config:

```bash
pkg-config --cflags --libs webkit2gtk-4.0 json-glib-1.0 gtk+-3.0
```

## Quick Start

### 1. Initialize the adblock manager at startup

```c
#include "extensions/adblock/adblock.h"

static AdblockManager *adblock_mgr = NULL;

void on_filter_loaded(AdblockManager *manager, gboolean success,
                      GError *error, gpointer user_data) {
    if (success) {
        g_print("✓ Filters loaded: %u\n",
                adblock_manager_get_filter_count(manager));
    } else {
        g_warning("Failed to load filters: %s",
                  error ? error->message : "Unknown");
    }
}

// In your app initialization
void browser_init(void) {
    // Create manager (NULL = default store path)
    adblock_mgr = adblock_manager_new(NULL);
    
    // Load EasyList
    GError *error = NULL;
    adblock_manager_load_from_file(
        adblock_mgr,
        "src/extensions/adblock/easylist.txt",
        "easylist",
        on_filter_loaded,
        NULL,
        &error);
    
    if (error) {
        g_warning("Error: %s", error->message);
        g_error_free(error);
    }
}
```

### 2. Apply filters to each WebView

```c
// When creating a new tab or window
WebKitWebView *create_webview(void) {
    WebKitWebView *web_view = WEBKIT_WEB_VIEW(webkit_web_view_new());
    
    // Apply adblock filters
    GError *error = NULL;
    if (!adblock_manager_apply_to_web_view(adblock_mgr, web_view, &error)) {
        g_warning("Failed to apply filters: %s", error->message);
        g_error_free(error);
    }
    
    return web_view;
}
```

### 3. Toggle adblock on/off

```c
// In settings UI
void on_toggle_adblock(gboolean enabled) {
    adblock_manager_set_enabled(adblock_mgr, enabled);
}

gboolean is_adblock_on(void) {
    return adblock_manager_is_enabled(adblock_mgr);
}
```

### 4. Cleanup on shutdown

```c
void browser_cleanup(void) {
    adblock_manager_free(adblock_mgr);
}
```

## API Reference

### AdblockManager

Opaque structure managing filters and their lifecycle.

#### Functions

**`adblock_manager_new(const gchar *store_path)`**
- Creates a new manager
- `store_path`: Path to filter store (NULL for default: `~/.config/leaf-web/content-filters`)
- Returns: New manager or NULL on failure

**`adblock_manager_free(AdblockManager *manager)`**
- Frees all resources
- Call on app shutdown

**`adblock_manager_load_from_file(...)`**
```c
gboolean adblock_manager_load_from_file(
    AdblockManager *manager,
    const gchar *filter_list_path,
    const gchar *filter_identifier,
    AdblockCallback callback,
    gpointer user_data,
    GError **error);
```
- Asynchronously loads and compiles a filter list
- `filter_list_path`: Path to filter file (e.g., easylist.txt)
- `filter_identifier`: Unique ID (e.g., "easylist")
- `callback`: Called when compilation completes
- Returns: TRUE if started successfully

**`adblock_manager_apply_to_web_view(...)`**
```c
gboolean adblock_manager_apply_to_web_view(
    AdblockManager *manager,
    WebKitWebView *web_view,
    GError **error);
```
- Applies all loaded filters to a WebView
- Call for each new WebView
- Returns: TRUE on success

**`adblock_manager_set_enabled(AdblockManager *manager, gboolean enabled)`**
- Enable or disable ad blocking globally

**`adblock_manager_is_enabled(AdblockManager *manager)`**
- Check if ad blocking is enabled
- Returns: TRUE if enabled

**`adblock_manager_get_filter_count(AdblockManager *manager)`**
- Get number of loaded filters
- Returns: Filter count

**`adblock_manager_reload_filters(...)`**
```c
void adblock_manager_reload_filters(
    AdblockManager *manager,
    AdblockCallback callback,
    gpointer user_data);
```
- Reloads all filter lists (useful for updates)

### AdblockCallback

```c
typedef void (*AdblockCallback)(
    AdblockManager *manager,
    gboolean success,
    GError *error,
    gpointer user_data);
```
- Called when async operations complete

## Filter Lists

### EasyList (Included)

The included `easylist.txt` contains **77,000+ blocking rules** covering:
- Ad servers
- Ad networks
- Tracking scripts
- Social media trackers
- Analytics services

**Update regularly** from: https://easylist.to/easylist/easylist.txt

### Additional Filter Lists

You can load multiple lists:

```c
// Load EasyList
adblock_manager_load_from_file(mgr, "easylist.txt", "easylist", callback, NULL, &error);

// Load EasyPrivacy (tracking protection)
adblock_manager_load_from_file(mgr, "easyprivacy.txt", "easyprivacy", callback, NULL, &error);

// Load Fanboy's Annoyances
adblock_manager_load_from_file(mgr, "fanboy-annoyance.txt", "fanboy", callback, NULL, &error);
```

Popular lists:
- **EasyList**: https://easylist.to/easylist/easylist.txt
- **EasyPrivacy**: https://easylist.to/easylist/easyprivacy.txt
- **Fanboy's Annoyances**: https://easylist.to/easylist/fanboy-annoyance.txt

## How It Works

### 1. Filter Conversion

AdBlock Plus format filters are converted to WebKit's JSON content blocker format:

```
AdBlock: ||doubleclick.net^
↓
JSON: {
  "trigger": {
    "url-filter": ".*doubleclick\\.net.*",
    "resource-type": ["image", "script", ...]
  },
  "action": {
    "type": "block"
  }
}
```

### 2. Compilation

WebKit compiles the JSON rules into a highly optimized bytecode format that's:
- Fast to evaluate (minimal performance impact)
- Cached to disk (no recompilation needed)
- Applied at the network layer (blocks before download)

### 3. Application

Filters are added to WebView's `WebKitUserContentManager`, which:
- Intercepts all network requests
- Checks against compiled rules
- Blocks matching requests before they load

## Performance

- **Minimal overhead**: WebKit's content blocker is highly optimized
- **Async compilation**: First load compiles filters, subsequent loads use cache
- **No runtime parsing**: Compiled bytecode is evaluated, not text rules
- **Early blocking**: Requests blocked before network layer

## Troubleshooting

### Filters not blocking ads

1. **Check if loaded**:
   ```c
   guint count = adblock_manager_get_filter_count(mgr);
   g_print("Filters loaded: %u\n", count);
   ```

2. **Verify applied to WebView**:
   Ensure you call `adblock_manager_apply_to_web_view()` for each WebView

3. **Check enabled status**:
   ```c
   if (!adblock_manager_is_enabled(mgr)) {
       g_print("Adblock is disabled!\n");
   }
   ```

4. **Look for errors**: Check console output for warnings

### Compilation errors

- Ensure `json-glib-1.0` is installed
- Verify filter file exists and is readable
- Check file format (should be UTF-8 text)

### High memory usage

The compiled filters are cached in memory. This is normal and improves performance.

## Building

### Meson

```meson
# meson.build
adblock_sources = files(
  'src/extensions/adblock/adblock.c',
)

adblock_deps = [
  dependency('webkit2gtk-4.0'),
  dependency('json-glib-1.0'),
]

# Add to your main executable
executable('leaf-web',
  sources: adblock_sources + your_other_sources,
  dependencies: adblock_deps + your_other_deps
)
```

### Makefile

```makefile
CFLAGS += $(shell pkg-config --cflags webkit2gtk-4.0 json-glib-1.0)
LDFLAGS += $(shell pkg-config --libs webkit2gtk-4.0 json-glib-1.0)

SOURCES += src/extensions/adblock/adblock.c
```

## Known Limitations

1. **Element hiding rules ignored**: CSS-based hiding (e.g., `##.ad`) not supported by WebKit's content blocker
2. **Exception rules limited**: `@@` exception rules not fully implemented
3. **Advanced selectors**: Complex AdBlock syntax may not convert perfectly

These limitations are minor - the core blocking (network-level) works excellently.

## Example Integration

See [`EXAMPLE_INTEGRATION.c`](EXAMPLE_INTEGRATION.c) for complete examples including:
- Application lifecycle integration
- Settings UI toggle
- Multiple WebView handling
- Error handling patterns

## License

This implementation follows the same license as Leaf-Web browser.
The included EasyList filter follows the EasyList license:
https://easylist.to/pages/licence.html

## Support

For issues or questions:
1. Check console output for error messages
2. Verify WebKit2GTK version (requires 2.24+)
3. Test with minimal filter list first
4. File an issue on Leaf-Web repository

---

**Happy ad-free browsing! 🚀**
