This page documents the dispatch layer for all "extended" filter types in uBlock Origin — those that operate on page content rather than network requests — along with the HTML filtering engine and the reverse lookup subsystem that identifies which filter list a given filter came from.
Extended filters are the ##, #@#, ##+js(...), and ##^ family of rules. For the cosmetic filtering engine (which processes ## CSS selectors), see 5.2. For the scriptlet engine that executes ##+js(...) rules at page load, see 6.1. For the redirect engine and resource injection, see 6.2. For the static network filtering engine (||example.com^), see 3.1.
src/js/static-ext-filtering.js is a thin coordination module. It does not implement any filtering logic itself. Instead, it imports the four specialized engines and exposes a single unified interface: staticExtFilteringEngine.
During filter list compilation, every extended filter line passes through staticExtFilteringEngine.compile(parser, writer). The method inspects the parsed AST node type and routes to the correct sub-engine:
src/js/static-ext-filtering.js92-136
| Priority | Guard method | Target engine |
|---|---|---|
| 1 | parser.isScriptletFilter() | scriptletFilteringEngine |
| 2 | parser.isResponseheaderFilter() | httpheaderFilteringEngine |
| 3 | parser.isHtmlFilter() | htmlFilteringEngine |
| 4 | parser.isCosmeticFilter() | cosmeticFilteringEngine |
If the parser flagged the filter with an error (parser.hasError()), the error is logged and the filter is dropped before dispatch. If none of the guards match, an error is logged for the unknown filter type.
Dispatch Flow Diagram
Sources: src/js/static-ext-filtering.js1-168
staticExtFilteringEngine delegates all lifecycle operations to all four sub-engines simultaneously:
| Method | What it does |
|---|---|
reset() | Clears all sub-engine state (called before reloading filter lists) |
freeze() | Finalizes sub-engine data structures after compilation |
fromCompiledContent(reader, options) | Reads compiled filter blocks into each sub-engine |
toSelfie() | Returns a serializable snapshot of all four sub-engines |
fromSelfie(selfie) | Restores all four sub-engines from a stored snapshot |
acceptedCount and discardedCount are computed properties that sum the corresponding values from all four sub-engines.
src/js/static-ext-filtering.js59-163
Sources: src/js/static-ext-filtering.js59-168
src/js/html-filtering.js implements response-body HTML filtering. This is the engine behind ##^ rules (e.g., example.com##^script:has-text(evil)). It operates on fetched response bodies before they are rendered — not on the live DOM — making it more powerful than cosmetic filters for certain content removal tasks.
Note: This engine only activates when
µb.canFilterResponseDataistrue(i.e., the browser supports stream filtering viabrowser.webRequest.filterResponseData). The check is performed infromCompiledContent.
Compiled HTML filters are stored in a StaticExtFilteringHostnameDB instance (filterDB) imported from static-ext-filtering-db.js. This is the same hostname-keyed trie used by other extended engines.
htmlFilteringEngine.compile(parser, writer) writes compiled entries into the HTML_FILTERS block of the compiled list. Each entry is an array [64, hostname, prefixed_selector]:
64, is the filter type code for HTML filters.hostname is the target hostname string (empty string for global exception filters).+ (apply) or - (exception).Only exception filters may be global (no hostname). A filter with only negated hostnames is rejected as invalid.
src/js/html-filtering.js320-354
htmlFilteringEngine.retrieve(fctxt) queries filterDB for all applicable selectors for the current hostname and entity, then splits them into two groups:
plains — plain CSS selectors, applied with querySelectorAllprocedurals — JSON-encoded procedural selector objectsException entries (prefixed with -) cancel out their matching non-exception selectors. Cancelled selectors are logged but not applied.
src/js/html-filtering.js375-410
htmlFilteringEngine.apply(doc, details, selectors) operates on a parsed DOM document (not the live page DOM). For each plain selector, it calls applyCSSSelector. For each procedural selector, it calls applyProceduralSelector, which instantiates a PSelector object and runs its task pipeline.
src/js/html-filtering.js412-427
The PSelector class chains transpose() calls across a sequence of task objects. Each task filters or transforms the current node list.
Supported task operators in the HTML context:
| Task class | Operator keyword |
|---|---|
PSelectorHasTextTask | has-text |
PSelectorIfTask | if, has |
PSelectorIfNotTask | if-not, not |
PSelectorMinTextLengthTask | min-text-length |
PSelectorSpathTask | spath |
PSelectorUpwardTask | upward, nth-ancestor |
PSelectorXpathTask | xpath |
src/js/html-filtering.js61-251
Sources: src/js/html-filtering.js1-443
The reverse lookup subsystem answers the question: Given a filter currently being enforced on this page, which filter list did it come from? This is used by the logger UI to display the list name alongside each matched filter.
The lookup is computationally expensive (full-text search over compiled list content), so it runs in a dedicated Web Worker and is shut down after 1.5 minutes of inactivity.
Architecture Diagram
Sources: src/js/reverselookup.js1-219 src/js/reverselookup-worker.js1-324
reverselookup.js)src/js/reverselookup.js exports a staticFilteringReverseLookup object with three public functions:
| Function | Description |
|---|---|
fromNetFilter(rawFilter) | Compiles the raw filter text, sends the compiled form to the worker, returns a Promise of matching list entries |
fromExtendedFilter(details) | Sends a raw extended filter and page URL to the worker, returns a Promise of matching list entries |
resetLists() | Signals the worker that filter lists have changed; next query will reload them |
initWorker)On first use (or after a reset), initWorker loads the compiled content of every enabled filter list and sends each one to the worker via a setList message. It calls µb.getCompiledFilterList(listKey) for each active list. After this, needLists is set to false until resetLists() is called again.
fromNetFilter re-parses the raw filter text using AstFilterParser, then compiles it via staticNetFilteringEngine.createCompiler().compile(parser, writer), and sends the compiled line (via writer.last()) to the worker. The worker searches for an exact line match.
src/js/reverselookup.js123-150
fromExtendedFilter sends the raw filter string plus the page URL, domain, and a needle string. For scriptlet filters, the needle is JSON.stringify(parser.getScriptletArgs()); for response header filters, it is the header name. The worker uses the needle as a fast pre-filter before doing structural comparison.
The call also checks whether generichide or specifichide static filters apply to the current URL via staticNetFilteringEngine.matchRequestReverse(), which are used by the worker to skip generic or specific filter matches accordingly.
src/js/reverselookup.js152-199
Sources: src/js/reverselookup.js1-219
reverselookup-worker.js)The worker receives compiled filter list content in full (as a string) and lazily extracts the relevant blocks on first use.
extractBlocks)Compiled list files are divided into named blocks using marker lines like #block-start-NETWORK_FILTERS:GOOD and #block-end-NETWORK_FILTERS:GOOD. extractBlocks(content, ...ids) slices out and concatenates only the requested sections.
src/js/reverselookup-worker.js31-42
The blocks searched for extended filters are:
| Block ID | Content |
|---|---|
COSMETIC_FILTERS:SPECIFIC | Specific cosmetic (per-hostname) filters |
COSMETIC_FILTERS:GENERIC | Generic cosmetic filters |
SCRIPTLET_FILTERS | Scriptlet injection filters |
HTML_FILTERS | HTML body filters |
HTTPHEADER_FILTERS | Response header filters |
src/js/reverselookup-worker.js194-205
Each compiled entry in those blocks is a JSON array. The first element is a numeric filter type code. The worker parses each line as a JSON array and applies type-specific matching rules:
| Type code | Meaning | Match logic |
|---|---|---|
0 | Lowly generic cosmetic | Selector must equal fargs[2] exactly |
4, 5 | Highly generic cosmetic | Selector must equal fargs[1] exactly |
8 | Specific cosmetic / generic exception | Target hostname in fargs[1] must match current page URL |
32 | Scriptlet injection | Needle (scriptlet args JSON) must match fargs[2] slice |
64 | HTML / response header filter | Target hostname in fargs[1] must match current page URL |
src/js/reverselookup-worker.js207-295
The targetMatchesURL function handles several target formats: plain hostname, hostname with path, and regex-encoded hostnames prefixed with /.
src/js/reverselookup-worker.js179-187
Sources: src/js/reverselookup-worker.js1-324
Compilation Time (filter list loading)
Runtime (page load)
Sources: src/js/static-ext-filtering.js92-163 src/js/html-filtering.js356-427
Refresh this wiki
This wiki was recently refreshed. Please wait 2 days to refresh again.