@crawlee/browser-pool 3.13.6-beta.0 → 4.0.0-beta.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.
Files changed (72) hide show
  1. package/abstract-classes/browser-controller.d.ts +4 -4
  2. package/abstract-classes/browser-controller.d.ts.map +1 -1
  3. package/abstract-classes/browser-controller.js +43 -117
  4. package/abstract-classes/browser-controller.js.map +1 -1
  5. package/abstract-classes/browser-plugin.d.ts +5 -16
  6. package/abstract-classes/browser-plugin.d.ts.map +1 -1
  7. package/abstract-classes/browser-plugin.js +19 -71
  8. package/abstract-classes/browser-plugin.js.map +1 -1
  9. package/anonymize-proxy.js +4 -8
  10. package/anonymize-proxy.js.map +1 -1
  11. package/browser-pool.d.ts +6 -6
  12. package/browser-pool.d.ts.map +1 -1
  13. package/browser-pool.js +77 -209
  14. package/browser-pool.js.map +1 -1
  15. package/container-proxy-server.js +3 -6
  16. package/container-proxy-server.js.map +1 -1
  17. package/events.js +4 -7
  18. package/events.js.map +1 -1
  19. package/fingerprinting/hooks.d.ts +3 -3
  20. package/fingerprinting/hooks.d.ts.map +1 -1
  21. package/fingerprinting/hooks.js +13 -18
  22. package/fingerprinting/hooks.js.map +1 -1
  23. package/fingerprinting/types.js +6 -9
  24. package/fingerprinting/types.js.map +1 -1
  25. package/fingerprinting/utils.d.ts +2 -2
  26. package/fingerprinting/utils.d.ts.map +1 -1
  27. package/fingerprinting/utils.js +9 -13
  28. package/fingerprinting/utils.js.map +1 -1
  29. package/index.d.ts +13 -13
  30. package/index.d.ts.map +1 -1
  31. package/index.js +11 -26
  32. package/index.js.map +1 -1
  33. package/launch-context.d.ts +2 -9
  34. package/launch-context.d.ts.map +1 -1
  35. package/launch-context.js +12 -73
  36. package/launch-context.js.map +1 -1
  37. package/logger.js +2 -6
  38. package/logger.js.map +1 -1
  39. package/package.json +19 -25
  40. package/playwright/playwright-browser.js +6 -30
  41. package/playwright/playwright-browser.js.map +1 -1
  42. package/playwright/playwright-controller.d.ts +2 -2
  43. package/playwright/playwright-controller.d.ts.map +1 -1
  44. package/playwright/playwright-controller.js +9 -75
  45. package/playwright/playwright-controller.js.map +1 -1
  46. package/playwright/playwright-plugin.d.ts +6 -6
  47. package/playwright/playwright-plugin.d.ts.map +1 -1
  48. package/playwright/playwright-plugin.js +19 -106
  49. package/playwright/playwright-plugin.js.map +1 -1
  50. package/proxy-server.js +3 -6
  51. package/proxy-server.js.map +1 -1
  52. package/puppeteer/puppeteer-controller.d.ts +1 -1
  53. package/puppeteer/puppeteer-controller.d.ts.map +1 -1
  54. package/puppeteer/puppeteer-controller.js +13 -17
  55. package/puppeteer/puppeteer-controller.js.map +1 -1
  56. package/puppeteer/puppeteer-plugin.d.ts +5 -5
  57. package/puppeteer/puppeteer-plugin.d.ts.map +1 -1
  58. package/puppeteer/puppeteer-plugin.js +16 -23
  59. package/puppeteer/puppeteer-plugin.js.map +1 -1
  60. package/tsconfig.build.tsbuildinfo +1 -1
  61. package/utils.d.ts +3 -3
  62. package/utils.d.ts.map +1 -1
  63. package/utils.js +1 -4
  64. package/utils.js.map +1 -1
  65. package/index.mjs +0 -19
  66. package/playwright/load-firefox-addon.d.ts +0 -2
  67. package/playwright/load-firefox-addon.d.ts.map +0 -1
  68. package/playwright/load-firefox-addon.js +0 -86
  69. package/playwright/load-firefox-addon.js.map +0 -1
  70. package/tab-as-a-container/background.js +0 -433
  71. package/tab-as-a-container/content.js +0 -611
  72. package/tab-as-a-container/manifest.json +0 -21
package/utils.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import type { BrowserPlugin } from './abstract-classes/browser-plugin';
2
- import type { PlaywrightPlugin } from './playwright/playwright-plugin';
3
- import type { PuppeteerPlugin } from './puppeteer/puppeteer-plugin';
1
+ import type { BrowserPlugin } from './abstract-classes/browser-plugin.js';
2
+ import type { PlaywrightPlugin } from './playwright/playwright-plugin.js';
3
+ import type { PuppeteerPlugin } from './puppeteer/puppeteer-plugin.js';
4
4
  export type UnwrapPromise<T> = T extends PromiseLike<infer R> ? UnwrapPromise<R> : T;
5
5
  export declare function noop(..._args: unknown[]): void;
6
6
  /**
package/utils.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mCAAmC,CAAC;AACvE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAEpE,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI,CAAC,SAAS,WAAW,CAAC,MAAM,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;AAErF,wBAAgB,IAAI,CAAC,GAAG,KAAK,EAAE,OAAO,EAAE,GAAG,IAAI,CAAG;AAElD;;;GAGG;AACH,MAAM,MAAM,cAAc,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,GAAG,KAAK,GAAG,IAAI,OAAO,EAAE,SAAS,UAAU,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;AAEpH,MAAM,MAAM,uBAAuB,CAE/B,KAAK,SAAS,SAAS,OAAO,EAAE,EAEhC,MAAM,SAAS,aAAa,EAAE,GAAG,EAAE,IACnC,KAAK,SAAS,SAAS,CAAC,MAAM,UAAU,EAAE,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,MAAM,UAAU,EAAE,GAAG,MAAM,IAAI,CAAC,GAE1F,UAAU,SAAS,gBAAgB,GAE/B,uBAAuB,CAAC,IAAI,EAAE,CAAC,GAAG,MAAM,EAAE,gBAAgB,CAAC,CAAC,GAE5D,UAAU,SAAS,eAAe,GAEhC,uBAAuB,CAAC,IAAI,EAAE,CAAC,GAAG,MAAM,EAAE,eAAe,CAAC,CAAC,GAE3D,KAAK,GAEX,KAAK,SAAS,EAAE,GAEd,MAAM,GAEN,KAAK,SAAS,SAAS,CAAC,MAAM,CAAC,CAAC,EAAE,GAEhC;IAAC,CAAC;CAAC,SAAS,CAAC,eAAe,GAAG,gBAAgB,CAAC,GAE5C,CAAC,EAAE,GAEH,KAAK,GAET,MAAM,CAAC"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sCAAsC,CAAC;AAC1E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAC1E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAEvE,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI,CAAC,SAAS,WAAW,CAAC,MAAM,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;AAErF,wBAAgB,IAAI,CAAC,GAAG,KAAK,EAAE,OAAO,EAAE,GAAG,IAAI,CAAG;AAElD;;;GAGG;AACH,MAAM,MAAM,cAAc,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,GAAG,KAAK,GAAG,IAAI,OAAO,EAAE,SAAS,UAAU,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;AAEpH,MAAM,MAAM,uBAAuB,CAE/B,KAAK,SAAS,SAAS,OAAO,EAAE,EAEhC,MAAM,SAAS,aAAa,EAAE,GAAG,EAAE,IACnC,KAAK,SAAS,SAAS,CAAC,MAAM,UAAU,EAAE,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,MAAM,UAAU,EAAE,GAAG,MAAM,IAAI,CAAC,GAE1F,UAAU,SAAS,gBAAgB,GAE/B,uBAAuB,CAAC,IAAI,EAAE,CAAC,GAAG,MAAM,EAAE,gBAAgB,CAAC,CAAC,GAE5D,UAAU,SAAS,eAAe,GAEhC,uBAAuB,CAAC,IAAI,EAAE,CAAC,GAAG,MAAM,EAAE,eAAe,CAAC,CAAC,GAE3D,KAAK,GAEX,KAAK,SAAS,EAAE,GAEd,MAAM,GAEN,KAAK,SAAS,SAAS,CAAC,MAAM,CAAC,CAAC,EAAE,GAEhC;IAAC,CAAC;CAAC,SAAS,CAAC,eAAe,GAAG,gBAAgB,CAAC,GAE5C,CAAC,EAAE,GAEH,KAAK,GAET,MAAM,CAAC"}
package/utils.js CHANGED
@@ -1,5 +1,2 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.noop = noop;
4
- function noop(..._args) { }
1
+ export function noop(..._args) { }
5
2
  //# sourceMappingURL=utils.js.map
package/utils.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":";;AAMA,oBAAkD;AAAlD,SAAgB,IAAI,CAAC,GAAG,KAAgB,IAAS,CAAC"}
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAMA,MAAM,UAAU,IAAI,CAAC,GAAG,KAAgB,IAAS,CAAC"}
package/index.mjs DELETED
@@ -1,19 +0,0 @@
1
- import mod from "./index.js";
2
-
3
- export default mod;
4
- export const BROWSER_CONTROLLER_EVENTS = mod.BROWSER_CONTROLLER_EVENTS;
5
- export const BROWSER_POOL_EVENTS = mod.BROWSER_POOL_EVENTS;
6
- export const BrowserController = mod.BrowserController;
7
- export const BrowserLaunchError = mod.BrowserLaunchError;
8
- export const BrowserName = mod.BrowserName;
9
- export const BrowserPlugin = mod.BrowserPlugin;
10
- export const BrowserPool = mod.BrowserPool;
11
- export const DEFAULT_USER_AGENT = mod.DEFAULT_USER_AGENT;
12
- export const DeviceCategory = mod.DeviceCategory;
13
- export const LaunchContext = mod.LaunchContext;
14
- export const OperatingSystemsName = mod.OperatingSystemsName;
15
- export const PlaywrightBrowser = mod.PlaywrightBrowser;
16
- export const PlaywrightController = mod.PlaywrightController;
17
- export const PlaywrightPlugin = mod.PlaywrightPlugin;
18
- export const PuppeteerController = mod.PuppeteerController;
19
- export const PuppeteerPlugin = mod.PuppeteerPlugin;
@@ -1,2 +0,0 @@
1
- export declare const loadFirefoxAddon: (port: number, host: string, addonPath: string) => Promise<boolean>;
2
- //# sourceMappingURL=load-firefox-addon.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"load-firefox-addon.d.ts","sourceRoot":"","sources":["../../src/playwright/load-firefox-addon.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,gBAAgB,GAAU,MAAM,MAAM,EAAE,MAAM,MAAM,EAAE,WAAW,MAAM,qBAoGnF,CAAC"}
@@ -1,86 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.loadFirefoxAddon = void 0;
4
- const tslib_1 = require("tslib");
5
- const node_buffer_1 = require("node:buffer");
6
- const node_net_1 = tslib_1.__importDefault(require("node:net"));
7
- const loadFirefoxAddon = async (port, host, addonPath) => {
8
- return new Promise((resolve) => {
9
- const socket = node_net_1.default.connect({
10
- port,
11
- host,
12
- });
13
- let success = false;
14
- socket.once('error', () => { });
15
- socket.once('close', () => {
16
- resolve(success);
17
- });
18
- const send = (data) => {
19
- const raw = node_buffer_1.Buffer.from(JSON.stringify(data));
20
- socket.write(`${raw.length}`);
21
- socket.write(':');
22
- socket.write(raw);
23
- };
24
- send({
25
- to: 'root',
26
- type: 'getRoot',
27
- });
28
- const onMessage = (message) => {
29
- if (message.addonsActor) {
30
- send({
31
- to: message.addonsActor,
32
- type: 'installTemporaryAddon',
33
- addonPath,
34
- });
35
- }
36
- if (message.addon) {
37
- success = true;
38
- socket.end();
39
- }
40
- if (message.error) {
41
- socket.end();
42
- }
43
- };
44
- const buffers = [];
45
- let remainingBytes = 0;
46
- socket.on('data', (data) => {
47
- while (true) {
48
- if (remainingBytes === 0) {
49
- const index = data.indexOf(':');
50
- buffers.push(data);
51
- if (index === -1) {
52
- return;
53
- }
54
- const buffer = node_buffer_1.Buffer.concat(buffers);
55
- const bufferIndex = buffer.indexOf(':');
56
- buffers.length = 0;
57
- remainingBytes = Number(buffer.subarray(0, bufferIndex).toString());
58
- if (!Number.isFinite(remainingBytes)) {
59
- throw new Error('Invalid state');
60
- }
61
- data = buffer.subarray(bufferIndex + 1);
62
- }
63
- if (data.length < remainingBytes) {
64
- remainingBytes -= data.length;
65
- buffers.push(data);
66
- break;
67
- }
68
- buffers.push(data.subarray(0, remainingBytes));
69
- const buffer = node_buffer_1.Buffer.concat(buffers);
70
- buffers.length = 0;
71
- const json = JSON.parse(buffer.toString());
72
- queueMicrotask(() => {
73
- onMessage(json);
74
- });
75
- const remainder = data.subarray(remainingBytes);
76
- remainingBytes = 0;
77
- if (remainder.length === 0) {
78
- break;
79
- }
80
- data = remainder;
81
- }
82
- });
83
- });
84
- };
85
- exports.loadFirefoxAddon = loadFirefoxAddon;
86
- //# sourceMappingURL=load-firefox-addon.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"load-firefox-addon.js","sourceRoot":"","sources":["../../src/playwright/load-firefox-addon.ts"],"names":[],"mappings":";;;;AAAA,6CAAqC;AACrC,gEAA2B;AAEpB,MAAM,gBAAgB,GAAG,KAAK,EAAE,IAAY,EAAE,IAAY,EAAE,SAAiB,EAAE,EAAE;IACpF,OAAO,IAAI,OAAO,CAAU,CAAC,OAAO,EAAE,EAAE;QACpC,MAAM,MAAM,GAAG,kBAAG,CAAC,OAAO,CAAC;YACvB,IAAI;YACJ,IAAI;SACP,CAAC,CAAC;QAEH,IAAI,OAAO,GAAG,KAAK,CAAC;QAEpB,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QAC/B,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE;YACtB,OAAO,CAAC,OAAO,CAAC,CAAC;QACrB,CAAC,CAAC,CAAC;QAEH,MAAM,IAAI,GAAG,CAAC,IAA4B,EAAE,EAAE;YAC1C,MAAM,GAAG,GAAG,oBAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;YAE9C,MAAM,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;YAC9B,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAClB,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACtB,CAAC,CAAC;QAEF,IAAI,CAAC;YACD,EAAE,EAAE,MAAM;YACV,IAAI,EAAE,SAAS;SAClB,CAAC,CAAC;QAEH,MAAM,SAAS,GAAG,CAAC,OAAY,EAAE,EAAE;YAC/B,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;gBACtB,IAAI,CAAC;oBACD,EAAE,EAAE,OAAO,CAAC,WAAW;oBACvB,IAAI,EAAE,uBAAuB;oBAC7B,SAAS;iBACZ,CAAC,CAAC;YACP,CAAC;YAED,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;gBAChB,OAAO,GAAG,IAAI,CAAC;gBACf,MAAM,CAAC,GAAG,EAAE,CAAC;YACjB,CAAC;YAED,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;gBAChB,MAAM,CAAC,GAAG,EAAE,CAAC;YACjB,CAAC;QACL,CAAC,CAAC;QAEF,MAAM,OAAO,GAAa,EAAE,CAAC;QAC7B,IAAI,cAAc,GAAG,CAAC,CAAC;QAEvB,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;YACvB,OAAO,IAAI,EAAE,CAAC;gBACV,IAAI,cAAc,KAAK,CAAC,EAAE,CAAC;oBACvB,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;oBAEhC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;oBAEnB,IAAI,KAAK,KAAK,CAAC,CAAC,EAAE,CAAC;wBACf,OAAO;oBACX,CAAC;oBAED,MAAM,MAAM,GAAG,oBAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;oBACtC,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;oBAExC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;oBACnB,cAAc,GAAG,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC;oBAEpE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC;wBACnC,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;oBACrC,CAAC;oBAED,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,WAAW,GAAG,CAAC,CAAC,CAAC;gBAC5C,CAAC;gBAED,IAAI,IAAI,CAAC,MAAM,GAAG,cAAc,EAAE,CAAC;oBAC/B,cAAc,IAAI,IAAI,CAAC,MAAM,CAAC;oBAC9B,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;oBACnB,MAAM;gBACV,CAAC;gBAED,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC,CAAC;gBAE/C,MAAM,MAAM,GAAG,oBAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;gBACtC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;gBAEnB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC;gBAC3C,cAAc,CAAC,GAAG,EAAE;oBAChB,SAAS,CAAC,IAAI,CAAC,CAAC;gBACpB,CAAC,CAAC,CAAC;gBAEH,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;gBAChD,cAAc,GAAG,CAAC,CAAC;gBAEnB,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;oBACzB,MAAM;gBACV,CAAC;gBAED,IAAI,GAAG,SAAS,CAAC;YACrB,CAAC;QACL,CAAC,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;AACP,CAAC,CAAC;AApGW,QAAA,gBAAgB,oBAoG3B"}
@@ -1,433 +0,0 @@
1
- 'use strict';
2
-
3
- /* eslint-disable no-undef */
4
-
5
- const isFirefox = navigator.userAgent.includes('Firefox');
6
-
7
- const webRequestPermissions = {
8
- blockingRequest: isFirefox ? ['blocking', 'requestHeaders'] : ['blocking', 'requestHeaders', 'extraHeaders'],
9
- blockingResponse: isFirefox ? ['blocking', 'responseHeaders'] : ['blocking', 'responseHeaders', 'extraHeaders'],
10
- };
11
-
12
- chrome.privacy.network.networkPredictionEnabled.set({ value: false });
13
-
14
- const translator = new Map();
15
- const counter = new Map();
16
-
17
- const getOpenerId = (id) => {
18
- if (typeof id !== 'number' || !Number.isFinite(id)) {
19
- throw new Error('Expected `id` to be a number');
20
- }
21
-
22
- if (translator.has(id)) {
23
- const opener = translator.get(id);
24
-
25
- if (translator.has(opener)) {
26
- throw new Error('Opener is not the most ascendent');
27
- }
28
-
29
- // console.log(`getopener ${id} -> ${opener}`);
30
- return opener;
31
- }
32
-
33
- return id;
34
- };
35
-
36
- const keyFromTabId = (tabId) => `.${tabId}.`;
37
-
38
- const getCookieURL = (cookie) => {
39
- const protocol = cookie.secure ? 'https:' : 'http:';
40
- const fixedDomain = cookie.domain[0] === '.' ? cookie.domain.slice(1) : cookie.domain;
41
- const url = `${protocol}//${fixedDomain}${cookie.path}`;
42
-
43
- return url;
44
- };
45
-
46
- // Rewrite cookies that were programmatically set to tabId instead of openerId.
47
- // This is required because we cannot reliably get openerId inside Playwright.
48
- chrome.cookies.onChanged.addListener(async (changeInfo) => {
49
- if (!changeInfo.removed) {
50
- const { cookie } = changeInfo;
51
-
52
- if (cookie.name[0] !== '.') {
53
- return;
54
- }
55
-
56
- const dotIndex = cookie.name.indexOf('.', 1);
57
- if (dotIndex === -1) {
58
- return;
59
- }
60
-
61
- const tabId = Number(cookie.name.slice(1, dotIndex));
62
-
63
- if (!Number.isFinite(tabId)) {
64
- return;
65
- }
66
-
67
- const realCookieName = cookie.name.slice(dotIndex + 1);
68
- const opener = getOpenerId(tabId);
69
-
70
- if (tabId !== opener) {
71
- console.log(`${realCookieName} -> ${keyFromTabId(opener)}`);
72
-
73
- await chrome.cookies.remove({
74
- name: cookie.name,
75
- url: getCookieURL(cookie),
76
- storeId: cookie.storeId,
77
- });
78
-
79
- delete cookie.hostOnly;
80
- delete cookie.session;
81
-
82
- await chrome.cookies.set({
83
- ...cookie,
84
- name: `${keyFromTabId(opener)}${realCookieName}`,
85
- url: getCookieURL(cookie),
86
- });
87
- }
88
- }
89
- });
90
-
91
- chrome.webRequest.onBeforeSendHeaders.addListener(
92
- (details) => {
93
- for (const header of details.requestHeaders) {
94
- if (header.name.toLowerCase() === 'cookie') {
95
- const id = keyFromTabId(getOpenerId(details.tabId));
96
-
97
- const fixedCookies = header.value
98
- .split('; ')
99
- .filter((x) => x.startsWith(id))
100
- .map((x) => x.slice(id.length))
101
- .join('; ');
102
- header.value = fixedCookies;
103
- }
104
-
105
- // Sometimes Chrome makes a request on a ghost tab.
106
- // We don't want these in order to prevent cluttering cookies.
107
- // Yes, `webNavigation.onCommitted` is emitted and `webNavigation.onCreatedNavigationTarget` is not.
108
- if (header.name.toLowerCase() === 'purpose' && header.value === 'prefetch' && !counter.has(details.tabId)) {
109
- console.log(details);
110
- return {
111
- cancel: true,
112
- };
113
- }
114
-
115
- // This one is for Firefox
116
- if (header.name.toLowerCase() === 'x-moz' && header.value === 'prefetch' && !counter.has(details.tabId)) {
117
- console.log(details);
118
- return {
119
- cancel: true,
120
- };
121
- }
122
-
123
- if (['beacon', 'csp_report', 'ping', 'speculative'].includes(details.type)) {
124
- console.log(details);
125
- return {
126
- cancel: true,
127
- };
128
- }
129
-
130
- if (details.tabId === -1) {
131
- console.log(details);
132
- }
133
- }
134
-
135
- return {
136
- requestHeaders: details.requestHeaders.filter(
137
- (header) => header.name.toLowerCase() !== 'cookie' || header.value !== '',
138
- ),
139
- };
140
- },
141
- { urls: ['<all_urls>'] },
142
- webRequestPermissions.blockingRequest,
143
- );
144
-
145
- // Firefox Bug: doesn't catch https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/report-uri
146
- chrome.webRequest.onHeadersReceived.addListener(
147
- (details) => {
148
- for (const header of details.responseHeaders) {
149
- if (header.name.toLowerCase() === 'set-cookie') {
150
- const parts = header.value.split('\n');
151
-
152
- // `details.tabId` === -1 when Chrome is making internal requests, such downloading a service worker.
153
-
154
- const openerId = getOpenerId(details.tabId);
155
-
156
- header.value = parts
157
- .map((part) => {
158
- const equalsIndex = part.indexOf('=');
159
- if (equalsIndex === -1) {
160
- return `${keyFromTabId(openerId)}=${part.trimStart()}`;
161
- }
162
- return keyFromTabId(openerId) + part.trimStart();
163
- })
164
- .join('\n');
165
- }
166
- }
167
-
168
- return {
169
- responseHeaders: details.responseHeaders,
170
- };
171
- },
172
- { urls: ['<all_urls>'] },
173
- webRequestPermissions.blockingResponse,
174
- );
175
-
176
- chrome.tabs.onRemoved.addListener(async (tabId) => {
177
- const opener = getOpenerId(tabId);
178
- translator.delete(tabId);
179
-
180
- if (counter.has(opener)) {
181
- counter.set(opener, counter.get(opener) - 1);
182
-
183
- if (counter.get(opener) < 1) {
184
- counter.delete(opener);
185
- } else {
186
- return;
187
- }
188
- }
189
-
190
- const id = keyFromTabId(opener);
191
-
192
- chrome.cookies.getAll({}, async (cookies) => {
193
- await Promise.allSettled(
194
- cookies
195
- .filter((cookie) => cookie.name.startsWith(id))
196
- .map((cookie) => {
197
- return chrome.cookies.remove({
198
- name: cookie.name,
199
- url: getCookieURL(cookie),
200
- storeId: cookie.storeId,
201
- });
202
- }),
203
- );
204
- });
205
- });
206
-
207
- // Proxy per tab
208
- const getProxyConfiguration = (scheme, host, port) => {
209
- return {
210
- mode: 'fixed_servers',
211
- rules: {
212
- proxyForHttp: {
213
- scheme,
214
- host,
215
- port,
216
- },
217
- proxyForHttps: {
218
- scheme,
219
- host,
220
- port,
221
- },
222
- },
223
- };
224
- };
225
-
226
- const localhostIpCache = new Map();
227
- const localHostIp = [127, 0, 0, 1];
228
- const getNextLocalhostIp = (openerId) => {
229
- if (localhostIpCache.has(openerId)) {
230
- return localhostIpCache.get(openerId);
231
- }
232
-
233
- const result = localHostIp.join('.');
234
-
235
- localhostIpCache.set(openerId, result);
236
-
237
- if (localHostIp[3] === 254) {
238
- if (localHostIp[2] === 255) {
239
- if (localHostIp[1] === 255) {
240
- localHostIp[1] = 0;
241
- } else {
242
- localHostIp[1]++;
243
- }
244
-
245
- localHostIp[2] = 0;
246
- } else {
247
- localHostIp[2]++;
248
- }
249
-
250
- localHostIp[3] = 1;
251
- } else {
252
- localHostIp[3]++;
253
- }
254
-
255
- // [127.0.0.1 - 127.255.255.254] = 1 * 255 * 255 * 254 = 16 516 350
256
- while (localhostIpCache.length >= 1 * 255 * 255 * 254) {
257
- localhostIpCache.delete(localhostIpCache.keys().next().value);
258
- }
259
-
260
- return result;
261
- };
262
-
263
- let proxyPort;
264
-
265
- // Clear extension's proxy settings on reload
266
- if (isFirefox) {
267
- browser.proxy.settings.clear({});
268
- } else {
269
- chrome.proxy.settings.clear({});
270
- }
271
-
272
- // Proxy per tab
273
- if (isFirefox) {
274
- // On Firefox, we could use the `dns` permission to enforce DoH
275
- // but then the extension would not be compatible with Chrome.
276
- // Therefore users need to manually set the DNS settings.
277
-
278
- browser.proxy.onRequest.addListener(
279
- (details) => {
280
- const openerId = getOpenerId(details.tabId);
281
-
282
- if (typeof proxyPort === 'number') {
283
- return {
284
- type: 'http',
285
- host: getNextLocalhostIp(openerId),
286
- port: proxyPort,
287
- };
288
- }
289
- return {
290
- type: 'direct',
291
- };
292
- },
293
- { urls: ['<all_urls>'] },
294
- );
295
- } else {
296
- // The connection is not yet created with `onBeforeSendHeaders`, but is with `onSendHeaders`.
297
- chrome.webRequest.onBeforeSendHeaders.addListener(
298
- (details) => {
299
- const openerId = getOpenerId(details.tabId);
300
-
301
- if (typeof proxyPort === 'number') {
302
- chrome.proxy.settings.set({
303
- value: getProxyConfiguration('http', getNextLocalhostIp(openerId), proxyPort),
304
- scope: 'regular',
305
- });
306
- } else {
307
- chrome.proxy.settings.clear({});
308
- }
309
- },
310
- { urls: ['<all_urls>'] },
311
- webRequestPermissions.blockingRequest,
312
- );
313
- }
314
-
315
- // External communication. Note: the JSON keys are lowercased by the browser.
316
- const routes = Object.assign(Object.create(null), {
317
- async tabid(details) {
318
- return { tabid: details.tabId, proxyip: getNextLocalhostIp(details.tabId) };
319
- },
320
- async proxy(details, body) {
321
- proxyPort = body.port;
322
-
323
- return '';
324
- },
325
- });
326
-
327
- const onCompleted = async (details) => {
328
- const textPlain = 'data:text/plain,';
329
-
330
- if (details.frameId === 0 && details.url.startsWith(textPlain)) {
331
- try {
332
- const url = new URL(details.url);
333
- const route = url.pathname.slice('text/plain,'.length);
334
-
335
- if (route in routes) {
336
- const hash = url.hash.slice(1);
337
-
338
- let body = {};
339
-
340
- if (hash !== '') {
341
- try {
342
- body = JSON.parse(decodeURIComponent(hash));
343
- } catch {
344
- // Empty on purpose.
345
- }
346
- }
347
-
348
- // Different protocols are required, otherwise `onCompleted` won't be emitted.
349
- const result = await routes[route](details, body);
350
- if (result !== undefined) {
351
- await chrome.tabs.update(details.tabId, {
352
- url: `about:blank#${encodeURIComponent(JSON.stringify(result))}`,
353
- });
354
- }
355
- }
356
- } catch {
357
- // Invalid URL, ignore.
358
- }
359
- }
360
- };
361
-
362
- chrome.webNavigation.onCompleted.addListener(onCompleted);
363
-
364
- // Load content scripts.
365
- void (async () => {
366
- const contentResponse = await fetch(chrome.runtime.getURL('content.js'));
367
- const contentText = await contentResponse.text();
368
-
369
- // `tabs.onCreated` doesn't work here when manually creating new tabs,
370
- // because the opener is the current tab active.
371
- //
372
- // This events only fires when the page opens something.
373
- chrome.webNavigation.onCreatedNavigationTarget.addListener((details) => {
374
- translator.set(details.tabId, getOpenerId(details.sourceTabId));
375
-
376
- const opener = getOpenerId(details.tabId);
377
-
378
- if (counter.has(opener)) {
379
- counter.set(opener, counter.get(opener) + 1);
380
- } else {
381
- counter.set(opener, 2); // the current one + opener = 2
382
- }
383
- });
384
-
385
- chrome.webNavigation.onCommitted.addListener(async (details) => {
386
- if (details.url.startsWith('chrome')) {
387
- return;
388
- }
389
-
390
- const executeCodeInPageContext = `
391
- const script = document.createElement('script');
392
- script.textContent = code;
393
-
394
- const destination = document.head ?? document.documentElement;
395
-
396
- if (document instanceof HTMLDocument) {
397
- destination.append(script);
398
- script.remove();
399
- }
400
- `;
401
-
402
- // Race condition: website scripts may run first
403
- await chrome.tabs.executeScript(details.tabId, {
404
- code: `'use strict';
405
- (() => {
406
- if (window.totallyRandomString) {
407
- return;
408
- }
409
-
410
- window.totallyRandomString = true;
411
-
412
- const code = "'use strict'; const tabId = '${getOpenerId(
413
- details.tabId,
414
- )}'; (() => {\\n" + ${JSON.stringify(contentText)} + "\\n})();\\n";
415
- ${executeCodeInPageContext}
416
- })();
417
- `,
418
- matchAboutBlank: true,
419
- allFrames: true,
420
- runAt: 'document_start',
421
- });
422
- });
423
-
424
- chrome.tabs.query({}, async (tabs) => {
425
- for (const tab of tabs) {
426
- await onCompleted({
427
- frameId: 0,
428
- url: tab.url,
429
- tabId: tab.id,
430
- });
431
- }
432
- });
433
- })();