barebrowse 0.17.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +29 -0
- package/README.md +5 -3
- package/barebrowse.context.md +1 -1
- package/package.json +1 -1
- package/src/bidi.js +8 -2
- package/src/blocklist-firefox.js +60 -0
- package/src/blocklist.js +49 -0
- package/src/firefox-page.js +67 -5
- package/src/index.js +17 -8
- package/types/blocklist-firefox.d.ts +15 -0
- package/types/blocklist.d.ts +34 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.18.0] - 2026-07-11
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **Firefox/BiDi noise-reduction + dialogs (parity plan Phase 3).** Two more
|
|
8
|
+
Chromium/CDP capabilities now work the same on the Firefox engine:
|
|
9
|
+
- **Ad/tracker blocking** (`blockAds`, on by default; `blockUrls` to extend).
|
|
10
|
+
BiDi's `network.addIntercept` can't express our glob patterns — its
|
|
11
|
+
`urlPatterns` reject `*` outright and have no subdomain wildcard — so the
|
|
12
|
+
Firefox path registers a *catch-all* `beforeRequestSent` intercept and
|
|
13
|
+
matches each request URL in-process against the shared `blocklist.js`
|
|
14
|
+
(new `makeBlockMatcher` compiles the CDP globs to a predicate, single-sourced
|
|
15
|
+
across engines). Matches are failed like CDP's `ERR_BLOCKED_BY_CLIENT`; the
|
|
16
|
+
rest continue. Default is **on** (Firefox is always a launched throwaway
|
|
17
|
+
profile, never attach mode). `src/blocklist-firefox.js`.
|
|
18
|
+
- **JS dialog handling** (`alert`/`confirm`/`prompt`/`beforeunload`). The BiDi
|
|
19
|
+
session is created with `unhandledPromptBehavior:'ignore'` so Firefox no
|
|
20
|
+
longer auto-dismisses prompts before we can act; `page.dialogLog` records
|
|
21
|
+
every dialog and `page.onDialog(handler)` overrides the default (accept all
|
|
22
|
+
except `beforeunload`), mirroring the CDP surface exactly.
|
|
23
|
+
|
|
24
|
+
BiDi mechanics were POC-measured against real Firefox before wiring (the "no
|
|
25
|
+
such alert" auto-dismiss race, and that `urlPatterns` reject `*`). Guarded by
|
|
26
|
+
`test/unit/blocklist-firefox.test.js` (10) and Firefox integration tests
|
|
27
|
+
(ad-block via a hermetic CORS server so the `blockAds:false` control genuinely
|
|
28
|
+
passes through; dialog auto-accept + `dialogLog` + custom `onDialog`).
|
|
29
|
+
Remaining Firefox gaps (Phase 4+): hybrid mode, `saveState`,
|
|
30
|
+
`waitForNavigation`, downloads, `reload({ignoreCache})`.
|
|
31
|
+
|
|
3
32
|
## [0.17.0] - 2026-07-10
|
|
4
33
|
|
|
5
34
|
### Added
|
package/README.md
CHANGED
|
@@ -145,9 +145,11 @@ const page = await connect({ engine: 'firefox' }); // headless by default
|
|
|
145
145
|
|
|
146
146
|
From the CLI: `barebrowse open <url> --engine firefox`. From MCP: set
|
|
147
147
|
`BAREBROWSE_ENGINE=firefox`. Firefox cookies (plaintext) reuse into the same
|
|
148
|
-
engine. Consent auto-dismiss and headless stealth
|
|
149
|
-
|
|
150
|
-
blocking
|
|
148
|
+
engine. Consent auto-dismiss and headless stealth (v0.16.0), the daemon's
|
|
149
|
+
console/network capture and `waitForNetworkIdle` (v0.17.0), and ad/tracker
|
|
150
|
+
blocking plus JS dialog handling (`dialogLog`/`onDialog`, v0.18.0) all work on
|
|
151
|
+
Firefox too. Chromium (CDP) remains the default; only `hybrid` mode is still
|
|
152
|
+
Chromium-only.
|
|
151
153
|
|
|
152
154
|
No clone profile, no fresh cookies — the agent sees what you see.
|
|
153
155
|
|
package/barebrowse.context.md
CHANGED
|
@@ -95,7 +95,7 @@ const snapshot = await browse('https://example.com', {
|
|
|
95
95
|
| `close()` | -- | void | Close page, disconnect CDP, kill browser (if headless) |
|
|
96
96
|
|
|
97
97
|
**connect() options** (in addition to mode/port/consent):
|
|
98
|
-
- `engine: 'chromium'|'firefox'` — Browser engine. Default `chromium` (CDP). `firefox` drives Firefox over WebDriver BiDi (CDP is deprecated there) — a separate transport with the same `page.*` API; the ARIA snapshot is reconstructed in-page since BiDi has no `getFullAXTree`. Consent auto-dismiss and stealth (headless anti-detection) work on Firefox as of v0.16.0; the CLI daemon's console/network capture and `waitForNetworkIdle` work as of v0.17.0. Still Chromium-only: `hybrid` mode
|
|
98
|
+
- `engine: 'chromium'|'firefox'` — Browser engine. Default `chromium` (CDP). `firefox` drives Firefox over WebDriver BiDi (CDP is deprecated there) — a separate transport with the same `page.*` API; the ARIA snapshot is reconstructed in-page since BiDi has no `getFullAXTree`. Consent auto-dismiss and stealth (headless anti-detection) work on Firefox as of v0.16.0; the CLI daemon's console/network capture and `waitForNetworkIdle` work as of v0.17.0; ad/tracker blocking (`blockAds`/`blockUrls`) and JS dialog handling (`dialogLog`/`onDialog`) work as of Phase 3. Still Chromium-only: `hybrid` mode. `reload()` ignores `ignoreCache` on Firefox (BiDi limitation).
|
|
99
99
|
- `port: 9222` — Attach to a Chromium already running with `--remote-debugging-port=N` instead of spawning one. The browser keeps running on `close()`. Stealth + permission denial + download capture are skipped to avoid mutating the user's running browser.
|
|
100
100
|
- `proxy: 'http://...'` — HTTP/SOCKS proxy for browser
|
|
101
101
|
- `viewport: '1280x720'` — Set viewport dimensions
|
package/package.json
CHANGED
package/src/bidi.js
CHANGED
|
@@ -135,8 +135,14 @@ export async function createBiDi(wsUrl) {
|
|
|
135
135
|
close() { ws.close(); },
|
|
136
136
|
};
|
|
137
137
|
|
|
138
|
-
// Open the session.
|
|
139
|
-
|
|
138
|
+
// Open the session. unhandledPromptBehavior:'ignore' stops Firefox from
|
|
139
|
+
// auto-dismissing JS dialogs (alert/confirm/prompt) before we can handle them
|
|
140
|
+
// — without it, browsingContext.handleUserPrompt loses the race and fails
|
|
141
|
+
// with "no such alert" (measured). firefox-page.js wires a userPromptOpened
|
|
142
|
+
// handler before any navigation, so no dialog is ever left hanging.
|
|
143
|
+
const session = await client.send('session.new', {
|
|
144
|
+
capabilities: { alwaysMatch: { unhandledPromptBehavior: 'ignore' } },
|
|
145
|
+
});
|
|
140
146
|
client.sessionId = session.sessionId;
|
|
141
147
|
client.capabilities = session.capabilities;
|
|
142
148
|
return client;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* blocklist-firefox.js — Ad/tracker blocking on the Firefox/BiDi engine.
|
|
3
|
+
*
|
|
4
|
+
* The BiDi analogue of index.js's applyBlocklist (CDP Network.setBlockedURLs).
|
|
5
|
+
* BiDi's network.addIntercept can't express our glob patterns — its urlPatterns
|
|
6
|
+
* reject '*' ("forbidden character *") and have no subdomain wildcard — so we
|
|
7
|
+
* register a *catch-all* intercept (empty urlPatterns) at the beforeRequestSent
|
|
8
|
+
* phase and decide per request in-process, matching each URL against the shared
|
|
9
|
+
* blocklist (makeBlockMatcher). Matches are failed (net error, like CDP's
|
|
10
|
+
* ERR_BLOCKED_BY_CLIENT); everything else is continued immediately.
|
|
11
|
+
*
|
|
12
|
+
* Cost vs. CDP: CDP matches browser-side with zero round-trips. Here every
|
|
13
|
+
* request pauses for one continue/fail round-trip to Node. On a typical page
|
|
14
|
+
* (tens–low-hundreds of requests over a local socket) that's negligible, and
|
|
15
|
+
* it's the only route that preserves glob parity without a second list.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { resolveBlocklistPatterns, makeBlockMatcher } from './blocklist.js';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Install ad/tracker blocking on a BiDi session. Idempotent per session is the
|
|
22
|
+
* caller's concern; call once at connect time before the first navigation.
|
|
23
|
+
*
|
|
24
|
+
* @param {object} bidi - BiDi client (send, subscribe, on).
|
|
25
|
+
* @param {object} pageOpts
|
|
26
|
+
* @param {boolean} [pageOpts.blockAds] - false disables the default list.
|
|
27
|
+
* @param {string[]} [pageOpts.blockUrls] - extra CDP-format globs; extend the
|
|
28
|
+
* default unless blockAds is false, in which case they're the whole list.
|
|
29
|
+
* @returns {Promise<void>}
|
|
30
|
+
*/
|
|
31
|
+
export async function applyFirefoxBlocklist(bidi, pageOpts = {}) {
|
|
32
|
+
const patterns = resolveBlocklistPatterns(pageOpts);
|
|
33
|
+
if (!patterns.length) return;
|
|
34
|
+
|
|
35
|
+
const isBlocked = makeBlockMatcher(patterns);
|
|
36
|
+
|
|
37
|
+
await bidi.send('network.addIntercept', {
|
|
38
|
+
phases: ['beforeRequestSent'],
|
|
39
|
+
urlPatterns: [],
|
|
40
|
+
});
|
|
41
|
+
await bidi.subscribe(['network.beforeRequestSent']);
|
|
42
|
+
|
|
43
|
+
bidi.on('network.beforeRequestSent', async (e) => {
|
|
44
|
+
// Only paused (intercepted) events need a decision; capture-only listeners
|
|
45
|
+
// see the same event with isBlocked=false and must be ignored here.
|
|
46
|
+
if (!e.isBlocked) return;
|
|
47
|
+
const id = e.request?.request;
|
|
48
|
+
if (!id) return;
|
|
49
|
+
try {
|
|
50
|
+
if (isBlocked(e.request.url)) {
|
|
51
|
+
await bidi.send('network.failRequest', { request: id });
|
|
52
|
+
} else {
|
|
53
|
+
await bidi.send('network.continueRequest', { request: id });
|
|
54
|
+
}
|
|
55
|
+
} catch {
|
|
56
|
+
// "no such request" — the request completed or was cancelled between the
|
|
57
|
+
// event and our reply (redundant nav requests, aborted fetches). Harmless.
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
}
|
package/src/blocklist.js
CHANGED
|
@@ -200,3 +200,52 @@ export const DEFAULT_BLOCKLIST = [
|
|
|
200
200
|
'*://*.bluekai.com/*', // Oracle Data Cloud
|
|
201
201
|
'*://*.krxd.net/*', // Salesforce / Krux
|
|
202
202
|
];
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Resolve the effective blocklist from a page's blockAds/blockUrls options.
|
|
206
|
+
* Single-sourced across engines (CDP applyBlocklist + BiDi applyFirefoxBlocklist)
|
|
207
|
+
* so the merge/extend rule can't drift between them:
|
|
208
|
+
* - blockAds !== false → DEFAULT_BLOCKLIST plus any blockUrls (extend);
|
|
209
|
+
* - blockAds === false → only blockUrls (the default list is dropped);
|
|
210
|
+
* - neither → empty (blocking disabled).
|
|
211
|
+
*
|
|
212
|
+
* @param {object} [pageOpts]
|
|
213
|
+
* @param {boolean} [pageOpts.blockAds] - false drops the default list.
|
|
214
|
+
* @param {string[]} [pageOpts.blockUrls] - extra CDP-format globs.
|
|
215
|
+
* @returns {string[]} the patterns to block (possibly empty).
|
|
216
|
+
*/
|
|
217
|
+
export function resolveBlocklistPatterns(pageOpts = {}) {
|
|
218
|
+
return pageOpts.blockAds === false
|
|
219
|
+
? (pageOpts.blockUrls || [])
|
|
220
|
+
: [...DEFAULT_BLOCKLIST, ...(pageOpts.blockUrls || [])];
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Compile CDP-format glob patterns into a single URL-matching predicate.
|
|
225
|
+
*
|
|
226
|
+
* CDP blocks natively via Network.setBlockedURLs, which does the glob matching
|
|
227
|
+
* browser-side. WebDriver BiDi has no glob-capable equivalent — network.
|
|
228
|
+
* addIntercept's urlPatterns reject '*' outright ("forbidden character *") and
|
|
229
|
+
* can't express subdomain wildcards like *.doubleclick.net. So the Firefox
|
|
230
|
+
* path intercepts *all* requests and matches each URL here, in-process, against
|
|
231
|
+
* the same patterns — keeping the blocklist single-sourced across engines.
|
|
232
|
+
*
|
|
233
|
+
* Matches CDP's glob semantics: '*' = any run of characters, '?' = exactly
|
|
234
|
+
* one character, whole-URL (anchored) match; every other character is literal.
|
|
235
|
+
*
|
|
236
|
+
* @param {string[]} patterns - CDP-format globs (e.g. DEFAULT_BLOCKLIST).
|
|
237
|
+
* @returns {(url: string) => boolean} true when `url` matches any pattern.
|
|
238
|
+
*/
|
|
239
|
+
export function makeBlockMatcher(patterns) {
|
|
240
|
+
const regexes = patterns.map((p) => {
|
|
241
|
+
// Escape every regex metachar EXCEPT the two glob wildcards, then expand
|
|
242
|
+
// them: '*' -> '.*', '?' -> '.'. (Escaping runs first so it never touches
|
|
243
|
+
// the '.' / '*' we insert next.)
|
|
244
|
+
const escaped = p
|
|
245
|
+
.replace(/[.+^${}()|[\]\\]/g, '\\$&')
|
|
246
|
+
.replace(/\*/g, '.*')
|
|
247
|
+
.replace(/\?/g, '.');
|
|
248
|
+
return new RegExp('^' + escaped + '$');
|
|
249
|
+
});
|
|
250
|
+
return (url) => regexes.some((re) => re.test(url));
|
|
251
|
+
}
|
package/src/firefox-page.js
CHANGED
|
@@ -170,6 +170,51 @@ export async function createFirefoxPage(bidi, opts = {}) {
|
|
|
170
170
|
return Promise.race([p, t]).finally(() => clearTimeout(timer));
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
+
// JS dialog handling (alert/confirm/prompt/beforeunload), parity with the
|
|
174
|
+
// CDP path in index.js. The BiDi session is created with
|
|
175
|
+
// unhandledPromptBehavior:'ignore' (see bidi.js) so prompts stay open until
|
|
176
|
+
// we respond; setupDialogs() must run before the first navigation or an
|
|
177
|
+
// 'ignore' prompt would hang the page. Default: accept everything except
|
|
178
|
+
// beforeunload (dismiss = stay), mirroring the CDP default. A caller can
|
|
179
|
+
// override per-dialog via page.onDialog(handler).
|
|
180
|
+
const dialogLog = [];
|
|
181
|
+
let onDialogHandler = null;
|
|
182
|
+
async function setupDialogs() {
|
|
183
|
+
await bidi.subscribe(['browsingContext.userPromptOpened']);
|
|
184
|
+
bidi.on('browsingContext.userPromptOpened', async (e) => {
|
|
185
|
+
dialogLog.push({
|
|
186
|
+
type: e.type,
|
|
187
|
+
message: e.message || '',
|
|
188
|
+
timestamp: new Date().toISOString(),
|
|
189
|
+
});
|
|
190
|
+
let accept = e.type !== 'beforeunload';
|
|
191
|
+
let userText = e.defaultValue || '';
|
|
192
|
+
if (onDialogHandler) {
|
|
193
|
+
try {
|
|
194
|
+
const decision = await onDialogHandler({
|
|
195
|
+
type: e.type,
|
|
196
|
+
message: e.message || '',
|
|
197
|
+
defaultPrompt: e.defaultValue || '',
|
|
198
|
+
});
|
|
199
|
+
if (decision && typeof decision === 'object') {
|
|
200
|
+
if (typeof decision.accept === 'boolean') accept = decision.accept;
|
|
201
|
+
if (typeof decision.promptText === 'string') userText = decision.promptText;
|
|
202
|
+
}
|
|
203
|
+
} catch {
|
|
204
|
+
// Handler threw — fall back to defaults so the prompt is still
|
|
205
|
+
// answered and the page doesn't hang on an 'ignore' dialog.
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
try {
|
|
209
|
+
await bidi.send('browsingContext.handleUserPrompt', {
|
|
210
|
+
context: e.context, accept, userText,
|
|
211
|
+
});
|
|
212
|
+
} catch {
|
|
213
|
+
// Prompt already gone (closed by page JS / navigation). Nothing to do.
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
|
|
173
218
|
const page = {
|
|
174
219
|
/** The BiDi escape hatch, analogous to connect()'s page.cdp. */
|
|
175
220
|
get bidi() { return bidi; },
|
|
@@ -413,12 +458,25 @@ export async function createFirefoxPage(bidi, opts = {}) {
|
|
|
413
458
|
|
|
414
459
|
// --- CDP-only surfaces, stubbed for daemon parity ---------------------
|
|
415
460
|
// The daemon (src/daemon.js) dispatches these unconditionally. On the
|
|
416
|
-
// Firefox/BiDi engine download tracking
|
|
417
|
-
//
|
|
418
|
-
//
|
|
419
|
-
//
|
|
461
|
+
// Firefox/BiDi engine download tracking isn't wired (Phase 4), so downloads
|
|
462
|
+
// is genuinely empty; saveState/waitForNavigation are CDP-only and fail with
|
|
463
|
+
// a clear, intentional message instead of an incidental TypeError.
|
|
464
|
+
// (dialogLog + onDialog ARE wired — Phase 3, see setupDialogs above.)
|
|
465
|
+
// Documented as a known gap in CHANGELOG.
|
|
420
466
|
get downloads() { return []; },
|
|
421
|
-
|
|
467
|
+
|
|
468
|
+
dialogLog,
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* Install a custom JS dialog handler, mirroring connect()'s page.onDialog.
|
|
472
|
+
* Called with `{ type, message, defaultPrompt }`; may return (sync or async)
|
|
473
|
+
* `{ accept: bool, promptText: string }` to override the auto-accept
|
|
474
|
+
* default. Pass null to restore default behavior.
|
|
475
|
+
*/
|
|
476
|
+
onDialog(handler) {
|
|
477
|
+
onDialogHandler = handler;
|
|
478
|
+
},
|
|
479
|
+
|
|
422
480
|
async saveState() {
|
|
423
481
|
throw new Error('saveState() is not supported on the Firefox/BiDi engine (CDP-only)');
|
|
424
482
|
},
|
|
@@ -441,5 +499,9 @@ export async function createFirefoxPage(bidi, opts = {}) {
|
|
|
441
499
|
await new Promise((r) => setTimeout(r, 500));
|
|
442
500
|
}
|
|
443
501
|
|
|
502
|
+
// Wire the dialog handler before returning — must precede any navigation so
|
|
503
|
+
// an 'ignore' prompt (see bidi.js capability) is never left hanging.
|
|
504
|
+
await setupDialogs();
|
|
505
|
+
|
|
444
506
|
return page;
|
|
445
507
|
}
|
package/src/index.js
CHANGED
|
@@ -20,7 +20,8 @@ import { click as cdpClick, type as cdpType, scroll as cdpScroll, press as cdpPr
|
|
|
20
20
|
import { dismissConsent } from './consent.js';
|
|
21
21
|
import { applyStealth } from './stealth.js';
|
|
22
22
|
import { applyFirefoxStealth } from './stealth-firefox.js';
|
|
23
|
-
import {
|
|
23
|
+
import { applyFirefoxBlocklist } from './blocklist-firefox.js';
|
|
24
|
+
import { resolveBlocklistPatterns } from './blocklist.js';
|
|
24
25
|
import { waitForNetworkIdle } from './network-idle.js';
|
|
25
26
|
import { readable as extractReadable } from './readable.js';
|
|
26
27
|
import { assertNavigable, assertUploadAllowed } from './url-guard.js';
|
|
@@ -747,9 +748,12 @@ async function suppressPermissions(cdp) {
|
|
|
747
748
|
* ref model, and AX-tree source all differ (see firefox-page.js). close() is
|
|
748
749
|
* wrapped to also reap the Firefox process + temp profile.
|
|
749
750
|
*
|
|
750
|
-
* Chromium-only options NOT applied on this path: `
|
|
751
|
-
* `
|
|
752
|
-
*
|
|
751
|
+
* Chromium-only options NOT applied on this path: `hybrid` mode. `saveState`,
|
|
752
|
+
* `waitForNavigation`, and download capture are CDP-only too (the page object
|
|
753
|
+
* stubs them — see firefox-page.js). `blockAds`/`blockUrls` and JS dialog
|
|
754
|
+
* handling (`dialogLog`/`onDialog`) DO apply as of Phase 3 (ad-block via a
|
|
755
|
+
* catch-all `network.addIntercept` + in-process glob match; dialogs via
|
|
756
|
+
* `browsingContext.userPromptOpened` → `handleUserPrompt`).
|
|
753
757
|
* `waitForNetworkIdle` and daemon console/network capture DO apply as of Phase
|
|
754
758
|
* 2 (over BiDi `network.*` and `log.entryAdded` events). `consent`
|
|
755
759
|
* (auto-dismiss) and
|
|
@@ -775,6 +779,14 @@ async function connectFirefox(opts) {
|
|
|
775
779
|
if (opts.mode !== 'headed') {
|
|
776
780
|
await applyFirefoxStealth(bidi);
|
|
777
781
|
}
|
|
782
|
+
// Ad/tracker blocking (parity with the CDP applyBlocklist). Firefox is always
|
|
783
|
+
// a launched, throwaway profile (never attach mode), so the default is on —
|
|
784
|
+
// unlike the CDP path, which defaults off in attach mode. Must run before the
|
|
785
|
+
// first navigation so the catch-all intercept is live when the page loads.
|
|
786
|
+
await applyFirefoxBlocklist(bidi, {
|
|
787
|
+
blockAds: opts.blockAds !== undefined ? opts.blockAds : true,
|
|
788
|
+
blockUrls: opts.blockUrls,
|
|
789
|
+
});
|
|
778
790
|
const page = await createFirefoxPage(bidi, {
|
|
779
791
|
pruneMode: opts.pruneMode,
|
|
780
792
|
urlGuard: { allowLocalUrls: opts.allowLocalUrls, blockPrivateNetwork: opts.blockPrivateNetwork },
|
|
@@ -911,10 +923,7 @@ let blocklistWarned = false;
|
|
|
911
923
|
* API surface.
|
|
912
924
|
*/
|
|
913
925
|
export async function applyBlocklist(session, pageOpts) {
|
|
914
|
-
|
|
915
|
-
const patterns = pageOpts.blockAds === false
|
|
916
|
-
? (pageOpts.blockUrls || [])
|
|
917
|
-
: [...DEFAULT_BLOCKLIST, ...(pageOpts.blockUrls || [])];
|
|
926
|
+
const patterns = resolveBlocklistPatterns(pageOpts);
|
|
918
927
|
if (!patterns.length) return;
|
|
919
928
|
try {
|
|
920
929
|
await session.send('Network.setBlockedURLs', { urls: patterns });
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Install ad/tracker blocking on a BiDi session. Idempotent per session is the
|
|
3
|
+
* caller's concern; call once at connect time before the first navigation.
|
|
4
|
+
*
|
|
5
|
+
* @param {object} bidi - BiDi client (send, subscribe, on).
|
|
6
|
+
* @param {object} pageOpts
|
|
7
|
+
* @param {boolean} [pageOpts.blockAds] - false disables the default list.
|
|
8
|
+
* @param {string[]} [pageOpts.blockUrls] - extra CDP-format globs; extend the
|
|
9
|
+
* default unless blockAds is false, in which case they're the whole list.
|
|
10
|
+
* @returns {Promise<void>}
|
|
11
|
+
*/
|
|
12
|
+
export function applyFirefoxBlocklist(bidi: object, pageOpts?: {
|
|
13
|
+
blockAds?: boolean | undefined;
|
|
14
|
+
blockUrls?: string[] | undefined;
|
|
15
|
+
}): Promise<void>;
|
package/types/blocklist.d.ts
CHANGED
|
@@ -1,3 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve the effective blocklist from a page's blockAds/blockUrls options.
|
|
3
|
+
* Single-sourced across engines (CDP applyBlocklist + BiDi applyFirefoxBlocklist)
|
|
4
|
+
* so the merge/extend rule can't drift between them:
|
|
5
|
+
* - blockAds !== false → DEFAULT_BLOCKLIST plus any blockUrls (extend);
|
|
6
|
+
* - blockAds === false → only blockUrls (the default list is dropped);
|
|
7
|
+
* - neither → empty (blocking disabled).
|
|
8
|
+
*
|
|
9
|
+
* @param {object} [pageOpts]
|
|
10
|
+
* @param {boolean} [pageOpts.blockAds] - false drops the default list.
|
|
11
|
+
* @param {string[]} [pageOpts.blockUrls] - extra CDP-format globs.
|
|
12
|
+
* @returns {string[]} the patterns to block (possibly empty).
|
|
13
|
+
*/
|
|
14
|
+
export function resolveBlocklistPatterns(pageOpts?: {
|
|
15
|
+
blockAds?: boolean | undefined;
|
|
16
|
+
blockUrls?: string[] | undefined;
|
|
17
|
+
}): string[];
|
|
18
|
+
/**
|
|
19
|
+
* Compile CDP-format glob patterns into a single URL-matching predicate.
|
|
20
|
+
*
|
|
21
|
+
* CDP blocks natively via Network.setBlockedURLs, which does the glob matching
|
|
22
|
+
* browser-side. WebDriver BiDi has no glob-capable equivalent — network.
|
|
23
|
+
* addIntercept's urlPatterns reject '*' outright ("forbidden character *") and
|
|
24
|
+
* can't express subdomain wildcards like *.doubleclick.net. So the Firefox
|
|
25
|
+
* path intercepts *all* requests and matches each URL here, in-process, against
|
|
26
|
+
* the same patterns — keeping the blocklist single-sourced across engines.
|
|
27
|
+
*
|
|
28
|
+
* Matches CDP's glob semantics: '*' = any run of characters, '?' = exactly
|
|
29
|
+
* one character, whole-URL (anchored) match; every other character is literal.
|
|
30
|
+
*
|
|
31
|
+
* @param {string[]} patterns - CDP-format globs (e.g. DEFAULT_BLOCKLIST).
|
|
32
|
+
* @returns {(url: string) => boolean} true when `url` matches any pattern.
|
|
33
|
+
*/
|
|
34
|
+
export function makeBlockMatcher(patterns: string[]): (url: string) => boolean;
|
|
1
35
|
/**
|
|
2
36
|
* blocklist.js — Ad/tracker URL patterns for CDP Network.setBlockedURLs.
|
|
3
37
|
*
|