Skip to content

Latest commit

 

History

History
414 lines (296 loc) · 15 KB

declarative-net-request.md

File metadata and controls

414 lines (296 loc) · 15 KB

declarativeNetRequest

Documentation: Chrome Declarative Net Request API, Firefox Declarative Net Request API, Safari content blocking.

Declarative Net Request (DNR) lets the browser block or redirect requests and modify headers using rules supplied by an extension. The browser applies those rules without invoking extension JavaScript for each request. This module provides Promise-based native methods and an unsubscribe-based debug subscription. Public function names use Dnr; the native namespace remains declarativeNetRequest.

Methods

Events

Permissions and browser support

Declare declarativeNetRequest or declarativeNetRequestWithHostAccess. The latter requires host access for rule actions. Redirects and header changes also require appropriate host permissions; Safari specifically requires declarativeNetRequestWithHostAccess for these actions, including user-granted access to both the source and destination of redirects.

For example, a Manifest V3 extension operating on one site can declare:

{
  "permissions": ["declarativeNetRequestWithHostAccess"],
  "host_permissions": ["https://example.com/*"]
}

Use the API from an extension context such as a background script or extension page. Debugging has additional restrictions described below; the availability check does not verify them.

Browser Supported operations and differences
Chrome / Chromium All thirteen methods and the debug event; individual features have different minimum versions.
Microsoft Edge / Opera Chromium-based implementations; check the target browser version and individual methods.
Firefox Dynamic/session rules, ruleset toggling, individual static rule toggling, available static count and regex support. testMatchOutcome requires extensions.dnr.feedback = true. No getMatchedRules, setExtensionActionOptions or onRuleMatchedDebug.
Safari / iOS Safari Dynamic/session rules, ruleset toggling, regex support, matched-request history and action count options. No individual static rule toggling, available static count, hypothetical matching or debug event in the compatibility data. Rule actions and conditions have additional limits.

See MDN compatibility data and the Edge API list. Firefox added DNR in 113 and individual static rule toggling in 128. Dynamic/session ruleIds filters require Chrome 111+, Firefox 127+ or Safari 18.5+. Safari added dynamic/session rules in 15.4 and action count options in 16.4. Do not infer support for a particular operation from namespace presence alone.

Types and native behavior

Arguments use chrome.declarativeNetRequest types supplied by @types/chrome. DnrTestMatchOptions adds Firefox's includeOtherExtensions option; matched rules may include Firefox's extensionId. These declarations are a cross-browser superset, not a promise that every field is supported by every browser. Unsupported options, invalid rules, missing methods, permissions and quota failures retain native errors. The wrappers do not retry, fall back to webRequest, or resolve a failed operation as a no-op.

DnrRulesMatchedDetails describes the different history results: Chromium provides rule metadata; Safari provides request.url. The wrapper preserves the returned object. See the WebKit implementation.

Dynamic rules persist across browser restarts and extension updates. Session rules are cleared at browser shutdown or extension update. Static rule content comes from JSON files declared in the manifest; toggling rules does not edit those files. Enabled static rulesets and individual disabled static rules persist across browser restarts but reset on extension update.

A single dynamic/session update applies removals and additions atomically. Separate API calls are not a transaction. Rule IDs are numeric and scoped to their ruleset; ruleset IDs are strings. Browser quotas, supported regex syntax, rule priorities and action restrictions remain native. urlFilter is DNR filter syntax, not a WebExtension match pattern. See the official API reference for rule matching and limits rather than assuming identical behavior across browsers.


isAvailableDnr

isAvailableDnr(method?: ApiMethodName<typeof chrome.declarativeNetRequest>): boolean

The optional method is a native function name, for example isAvailableDnr("getDynamicRules"). Import the public ApiMethodName type from @addon-core/browser for your own typed helpers. This checks callable presence without invoking the method; permissions and argument support still require handling.

Checks the current native namespace on each call. Returns false if the environment or namespace is missing or access throws. It does not log, probe native operations or cache results.

import {getDnrDynamicRules, isAvailableDnr} from "@addon-core/browser";

if (isAvailableDnr()) {
    const rules = await getDnrDynamicRules();
    console.log(rules);
}

getDnrDynamicRules

getDnrDynamicRules(filter?: chrome.declarativeNetRequest.GetRulesFilter): Promise<chrome.declarativeNetRequest.Rule[]>

Returns dynamic rules, optionally restricted to ruleIds. Omitting the filter uses the native no-filter overload. An empty native array remains empty.

import {getDnrDynamicRules} from "@addon-core/browser";

const rules = await getDnrDynamicRules({ruleIds: [1, 2]});

updateDnrDynamicRules

updateDnrDynamicRules(options: chrome.declarativeNetRequest.UpdateRuleOptions): Promise<void>

Adds and removes dynamic rules atomically. Replace an existing rule by removing and adding its ID in the same call. A failed validation leaves the existing rules unchanged.

import {updateDnrDynamicRules} from "@addon-core/browser";

await updateDnrDynamicRules({
    removeRuleIds: [1],
    addRules: [{
        id: 1, priority: 1,
        action: {type: "block"},
        condition: {urlFilter: "||example.com/ads/", resourceTypes: ["script", "image"]},
    }],
});

getDnrSessionRules

getDnrSessionRules(filter?: chrome.declarativeNetRequest.GetRulesFilter): Promise<chrome.declarativeNetRequest.Rule[]>

Returns rules for the current browser session, optionally filtered by numeric IDs.

import {getDnrSessionRules} from "@addon-core/browser";

const rules = await getDnrSessionRules();

updateDnrSessionRules

updateDnrSessionRules(options: chrome.declarativeNetRequest.UpdateRuleOptions): Promise<void>

Adds/removes temporary session rules atomically, using the same options as dynamic updates.

import {updateDnrSessionRules} from "@addon-core/browser";

await updateDnrSessionRules({
    addRules: [{
        id: 1, priority: 1, action: {type: "block"},
        condition: {urlFilter: "||example.com/tracking/", resourceTypes: ["xmlhttprequest"]},
    }],
});

// Later in the same session:
await updateDnrSessionRules({removeRuleIds: [1]});

getDnrEnabledRulesets

getDnrEnabledRulesets(): Promise<string[]>

Returns enabled static ruleset IDs. It does not include dynamic or session rulesets.

import {getDnrEnabledRulesets} from "@addon-core/browser";

const ids = await getDnrEnabledRulesets();

updateDnrEnabledRulesets

updateDnrEnabledRulesets(options: chrome.declarativeNetRequest.UpdateRulesetOptions): Promise<void>

Enables/disables static rulesets declared in declarative_net_request.rule_resources:

{
  "declarative_net_request": {
    "rule_resources": [{"id": "tracking", "enabled": false, "path": "rules/tracking.json"}]
  }
}

rules/tracking.json must contain a JSON array of valid rules packaged with the extension.

import {updateDnrEnabledRulesets} from "@addon-core/browser";

await updateDnrEnabledRulesets({enableRulesetIds: ["tracking"]});

getDnrDisabledRuleIds

getDnrDisabledRuleIds(options: chrome.declarativeNetRequest.GetDisabledRuleIdsOptions): Promise<number[]>

Returns individually disabled rule IDs for a declared static ruleset. Chromium and Firefox 128+; not available in Safari.

import {getDnrDisabledRuleIds} from "@addon-core/browser";

const ids = await getDnrDisabledRuleIds({rulesetId: "tracking"});

updateDnrStaticRules

updateDnrStaticRules(options: chrome.declarativeNetRequest.UpdateStaticRulesOptions): Promise<void>

Enables/disables individual rules inside a static ruleset. It cannot add or edit static rules, and enabling a rule does not enable its parent ruleset. Chromium and Firefox 128+; not Safari.

import {updateDnrStaticRules} from "@addon-core/browser";

await updateDnrStaticRules({rulesetId: "tracking", disableRuleIds: [1], enableRuleIds: [2]});

getDnrAvailableStaticRuleCount

getDnrAvailableStaticRuleCount(): Promise<number>

Returns the browser's available static rule allowance. This is not the remaining dynamic/session quota and does not guarantee that a later ruleset update succeeds. Not available in Safari.

import {getDnrAvailableStaticRuleCount} from "@addon-core/browser";

const available = await getDnrAvailableStaticRuleCount();

getDnrMatchedRules

getDnrMatchedRules(filter?: chrome.declarativeNetRequest.MatchedRulesFilter): Promise<DnrRulesMatchedDetails>

Returns recent matched-rule history, optionally filtered by tabId or minTimeStamp. Requires declarativeNetRequestFeedback or an activeTab grant for the specified tab. Native call quotas and history retention apply; this is not an exhaustive traffic log. Unsupported in Firefox.

import {getDnrMatchedRules} from "@addon-core/browser";

const result = await getDnrMatchedRules();
for (const info of result.rulesMatchedInfo) {
    if ("rule" in info) {
        console.log(info.rule.ruleId, info.rule.rulesetId); // Chromium
    } else {
        console.log(info.request.url); // Safari
    }
}

setDnrExtensionActionOptions

setDnrExtensionActionOptions(options: chrome.declarativeNetRequest.ExtensionActionOptions): Promise<void>

Configures displaying the action count as badge text and native per-tab count adjustments. Use an extension with an action declared in its manifest. Chromium and Safari 16.4+; not Firefox.

import {setDnrExtensionActionOptions} from "@addon-core/browser";

await setDnrExtensionActionOptions({displayActionCountAsBadgeText: true});

testDnrMatchOutcome

testDnrMatchOutcome(
    request: chrome.declarativeNetRequest.TestMatchRequestDetails,
    options?: DnrTestMatchOptions
): Promise<chrome.declarativeNetRequest.TestMatchOutcomeResult>

Evaluates a hypothetical request without issuing it. This does not verify a real network response. Chrome restricts this debugging method to unpacked extensions. Firefox requires the extensions.dnr.feedback preference to be enabled; it is disabled by default. Safari does not support it.

import {testDnrMatchOutcome} from "@addon-core/browser";

const outcome = await testDnrMatchOutcome({url: "https://example.com/ads/banner.js", type: "script"});
console.log(outcome.matchedRules);

Only Firefox supports the second argument. The wrapper forwards it unchanged using the native Promise overload; Chromium rejects it. Rules from other extensions may include extensionId.

import {testDnrMatchOutcome} from "@addon-core/browser";

// Firefox with extensions.dnr.feedback enabled:
const outcome = await testDnrMatchOutcome(
    {url: "https://example.com/", type: "main_frame"},
    {includeOtherExtensions: true},
);

getDnrRegexSupport

getDnrRegexSupport(options: chrome.declarativeNetRequest.RegexOptions): Promise<chrome.declarativeNetRequest.IsRegexSupportedResult>

Wraps native isRegexSupported, which returns an object, not a boolean. A regular expression that the browser does not support returns {isSupported: false, reason: ...}; invocation errors still reject. Regex syntax and resource limits differ between browsers.

import {getDnrRegexSupport} from "@addon-core/browser";

const support = await getDnrRegexSupport({regex: "^https://example\\.com/", requireCapturing: false});
if (!support.isSupported) {
    console.log(support.reason);
}

onDnrRuleMatchedDebug

onDnrRuleMatchedDebug(callback: (info: chrome.declarativeNetRequest.MatchedRuleInfoDebug) => void): () => void

Subscribes to Chromium's native debug event. Requires declarativeNetRequestFeedback and an unpacked extension. This is a development diagnostic, not a production request listener. Firefox does not expose this event even with its feedback preference enabled; Safari's compatibility data does not list support. Check the member when targeting multiple browsers. Missing events retain native subscription failures.

import {browser, isAvailableDnr, onDnrRuleMatchedDebug} from "@addon-core/browser";

if (isAvailableDnr() && browser().declarativeNetRequest.onRuleMatchedDebug) {
    const unsubscribe = onDnrRuleMatchedDebug(({rule, request}) => {
        console.log(rule.ruleId, rule.rulesetId, request.url);
    });

    // When the subscription is no longer needed:
    unsubscribe();
}

The callback receives the original native payload and may be asynchronous. Shared listener utilities log callback exceptions and rejected Promises. Cleanup is independent for each subscription and can be called more than once.