# Toxic Content Filter

## Overview

Toxic Content Filter is a browser extension that automatically identifies and hides toxic posts and comments on supported social-media websites.

The extension performs all inference locally. Social-media content is never sent to an external classification service.

The initial classifier produces a simple binary classification:

* **Toxic**
* **Non-toxic**

The model should also expose its confidence/probability, allowing the user to configure the threshold at which content is filtered.

## Goals

The extension should:

* Detect toxic social-media posts and comments as they appear.
* Hide or collapse content whose toxicity score exceeds a configurable threshold.
* Perform inference entirely on the user's computer.
* Support multiple simultaneously open social-media tabs without loading multiple copies of the model.
* Avoid interfering with normal page responsiveness.
* Support both Firefox and Chromium-based browsers.
* Allow new websites to be supported through site-specific post parsers.

## Classification Model

The initial implementation will use a small transformer model trained specifically for toxicity classification.

A candidate is Citizen Lab's multilingual DistilBERT toxicity classifier, which produces:

```text
toxic
not_toxic
```

along with classification probabilities.

The model should be converted to ONNX and quantized as aggressively as practical while retaining acceptable classification accuracy.

The deployed extension does not require Python or a native companion application.

Conceptually:

```text
DistilBERT toxicity model
          |
          v
        ONNX
          |
          v
Transformers.js / ONNX Runtime Web
          |
          v
       WASM CPU
```

CPU/WASM is used for browser compatibility and to avoid unnecessary GPU utilization.

CPU WASM inference uses ONNX Runtime's browser-selected defaults.

## Site Integration

The extension supports websites through declarative, user-configurable site definitions rather than site-specific code. A definition supplies URL patterns, page markers, post selectors, text selectors, and stable-ID sources. The content script is loaded broadly but exits without examining content unless a definition matches.

Each active definition is executed by the same generic discovery engine.

Conceptually:

```text
Site definition
    |
    +-- Generic discovery engine
```

A definition is responsible for declaring:

* Where posts and comments appear.
* Which rendered text should be extracted.
* Which attributes or links provide stable identifiers.

A normalized post might resemble:

```js
{
    id,
    text,
    element
}
```

Dynamic websites should be monitored with `MutationObserver` so newly loaded content can be discovered during infinite scrolling and navigation.

## Filtering

The general processing pipeline is:

```text
Social-media DOM
       |
       v
Site-specific parser
       |
       v
Shared inference queue
       |
       v
Toxicity classifier
       |
       v
Toxicity probability
       |
       v
User-configured threshold
       |
       +---- below ----> display normally
       |
       +---- above ----> hide/collapse
```

Rather than permanently removing filtered DOM elements, the extension should preferably collapse them.

An optional placeholder may allow the user to reveal incorrectly classified content:

```text
Content hidden as toxic (91%)    [Show]
```

The exact presentation should be configurable and may include an option to hide filtered content completely.

## Shared Inference Architecture

The ML model must **not** be instantiated independently inside each social-media tab.

All tabs share a single inference backend:

```text
Facebook tab ----\
Twitter tab ------\
Reddit tab --------> inference queue --> classifier
Twitter tab ------/
```

This provides:

* One loaded model.
* One tokenizer.
* One ONNX runtime.
* One set of inference buffers.
* Centralized scheduling and batching.

Content scripts should remain lightweight and communicate with the shared backend through browser extension messaging APIs.

## Firefox Architecture

For Firefox with Manifest V2:

```text
Content scripts
      |
      v
Persistent background page
      |
      v
Inference Web Worker
      |
      v
ONNX Runtime
      |
      v
Toxicity model
```

The persistent background context owns the inference worker and therefore the single loaded model.

## Chromium Manifest V3 Architecture

Manifest V3 still permits content scripts to inspect and modify page DOMs. Its restrictions on network-request interception that affected extensions such as uBlock Origin do not prevent this extension's core functionality.

Chromium MV3 service workers are ephemeral and therefore should **not** own the loaded ML model.

Instead:

```text
Content scripts
      |
      v
MV3 service worker
      |
      v
Offscreen document
      |
      v
Inference Web Worker
      |
      v
ONNX Runtime
      |
      v
Toxicity model
```

The service worker acts primarily as a coordinator/router.

An offscreen extension document provides a longer-lived context and creates the inference Web Worker. Chromium's Offscreen API explicitly provides a `WORKERS` reason for offscreen documents that need to spawn workers.

The inference worker owns the model and queue.

## Inference Queue

Every supported tab submits discovered content to one global inference queue.

Requests should be batched:

```text
Twitter:    7 posts --\
Reddit:    12 posts ---+--> queue --> batch --> classifier
Facebook:   4 posts --/
```

Batch size should be determined experimentally based on latency, memory consumption, and throughput.

The queue should support prioritization. A possible initial policy is:

```text
0 - Visible content in active tab
1 - Nearby/upcoming content in active tab
2 - Content in background tabs
3 - Preloaded/speculative content
```

Interactive responsiveness is more important than maximizing raw classifier throughput.

The queue should also be bounded. Stale requests should be discarded when:

* Their originating tab closes.
* Navigation invalidates the associated content.
* The associated DOM element disappears.
* Newer work makes speculative classification unnecessary.

## Result Cache

The inference backend should maintain a shared cache:

```text
hash(normalizedText) -> toxicity probability
```

This prevents unnecessary inference when:

* A framework destroys and recreates a DOM element.
* Identical posts appear multiple times.
* Content appears in multiple tabs.
* Previously classified content is encountered again.

An in-memory cache should be implemented initially. Persistent caching across browser sessions can be considered later.

## Configuration

Initial user-facing configuration should remain deliberately simple.

At minimum:

```text
Enable filtering:       Yes
Toxicity threshold:     0.80
Filtered content:       Collapse
```

Site-specific enable/disable controls should also be available.

More sophisticated filtering options should only be added if they prove useful.

## Privacy

Privacy is an important architectural requirement.

Post text must remain local to the browser. The extension should not require an account, remote API, or cloud inference service.

The basic data path is therefore:

```text
Website
   |
   v
Extension
   |
   v
Local model
   |
   X
 Internet
```

No social-media content needs to leave the user's computer for classification.

## Model Distribution

The initial model may be several hundred megabytes before optimization.

The project should investigate:

* ONNX graph optimization.
* INT8/Q8 quantization.
* More aggressive quantization where supported.
* Smaller toxicity models if classification quality remains acceptable.

The model may either ship with the extension or be downloaded once after installation and cached locally.

Downloading the model separately may substantially reduce extension package size.

## Future Work

Possible later improvements include:

* Additional supported websites.
* Improved batching and scheduling.
* Persistent classification cache.
* Per-site thresholds.
* User feedback on incorrect classifications.
* Locally collected examples for personalization.
* Fine-tuning or distillation of a smaller toxicity model specifically for browser filtering.
* Additional languages.

The initial implementation should avoid these features unless necessary to establish the basic architecture.

## Core Design Principle

The project should maintain three distinct responsibilities:

```text
Site parser
    |
    | discovers and normalizes content
    v
Shared inference backend
    |
    | produces toxicity probability
    v
Filtering policy
    |
    | applies user threshold
    v
Page presentation
```

Site definitions and their discovery engine should not contain ML logic. The inference backend should not understand individual websites. The classifier should only determine toxicity probability, while filtering policy determines whether that probability warrants hiding content.

This separation should allow website support, ML implementation, and user-facing filtering behavior to evolve independently.
