@addonova/browser 0.1.0 → 0.2.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 (99) hide show
  1. package/LICENSE +20 -20
  2. package/README.md +259 -0
  3. package/dist/api/scripting/index.d.ts.map +1 -1
  4. package/dist/api/scripting/index.js.map +1 -1
  5. package/dist/api/storage/area.d.ts +4 -0
  6. package/dist/api/storage/area.d.ts.map +1 -0
  7. package/dist/api/storage/area.js +29 -0
  8. package/dist/api/storage/area.js.map +1 -0
  9. package/dist/api/storage/index.d.ts +4 -3
  10. package/dist/api/storage/index.d.ts.map +1 -1
  11. package/dist/api/storage/index.js +7 -27
  12. package/dist/api/storage/index.js.map +1 -1
  13. package/dist/api/storage/item.d.ts +4 -0
  14. package/dist/api/storage/item.d.ts.map +1 -0
  15. package/dist/api/storage/item.js +109 -0
  16. package/dist/api/storage/item.js.map +1 -0
  17. package/dist/content/context.d.ts +24 -0
  18. package/dist/content/context.d.ts.map +1 -0
  19. package/dist/content/context.js +157 -0
  20. package/dist/content/context.js.map +1 -0
  21. package/dist/content/index.d.ts +7 -0
  22. package/dist/content/index.d.ts.map +1 -0
  23. package/dist/content/index.js +4 -0
  24. package/dist/content/index.js.map +1 -0
  25. package/dist/content/inject-root.d.ts +2 -0
  26. package/dist/content/inject-root.d.ts.map +1 -0
  27. package/dist/content/inject-root.js +4 -0
  28. package/dist/content/inject-root.js.map +1 -0
  29. package/dist/content/inject-script.d.ts +16 -0
  30. package/dist/content/inject-script.d.ts.map +1 -0
  31. package/dist/content/inject-script.js +39 -0
  32. package/dist/content/inject-script.js.map +1 -0
  33. package/dist/content/ui.d.ts +50 -0
  34. package/dist/content/ui.d.ts.map +1 -0
  35. package/dist/content/ui.js +331 -0
  36. package/dist/content/ui.js.map +1 -0
  37. package/dist/entries/chrome.d.ts +3 -0
  38. package/dist/entries/chrome.d.ts.map +1 -1
  39. package/dist/entries/chrome.js +7 -0
  40. package/dist/entries/chrome.js.map +1 -1
  41. package/dist/entries/chromium.d.ts +3 -0
  42. package/dist/entries/chromium.d.ts.map +1 -1
  43. package/dist/entries/chromium.js +7 -0
  44. package/dist/entries/chromium.js.map +1 -1
  45. package/dist/entries/edge.d.ts +3 -0
  46. package/dist/entries/edge.d.ts.map +1 -1
  47. package/dist/entries/edge.js +7 -0
  48. package/dist/entries/edge.js.map +1 -1
  49. package/dist/entries/firefox.d.ts +3 -0
  50. package/dist/entries/firefox.d.ts.map +1 -1
  51. package/dist/entries/firefox.js +7 -0
  52. package/dist/entries/firefox.js.map +1 -1
  53. package/dist/entries/shared-exports.d.ts +10 -0
  54. package/dist/entries/shared-exports.d.ts.map +1 -1
  55. package/dist/entries/shared-exports.js +5 -0
  56. package/dist/entries/shared-exports.js.map +1 -1
  57. package/dist/entries/utilities.d.ts +6 -0
  58. package/dist/entries/utilities.d.ts.map +1 -0
  59. package/dist/entries/utilities.js +7 -0
  60. package/dist/entries/utilities.js.map +1 -0
  61. package/dist/i18n/index.d.ts +16 -0
  62. package/dist/i18n/index.d.ts.map +1 -0
  63. package/dist/i18n/index.js +52 -0
  64. package/dist/i18n/index.js.map +1 -0
  65. package/dist/index.d.ts +13 -0
  66. package/dist/index.d.ts.map +1 -1
  67. package/dist/index.js +8 -0
  68. package/dist/index.js.map +1 -1
  69. package/dist/messaging/index.d.ts +22 -0
  70. package/dist/messaging/index.d.ts.map +1 -0
  71. package/dist/messaging/index.js +100 -0
  72. package/dist/messaging/index.js.map +1 -0
  73. package/dist/messaging/proxy.d.ts +17 -0
  74. package/dist/messaging/proxy.d.ts.map +1 -0
  75. package/dist/messaging/proxy.js +61 -0
  76. package/dist/messaging/proxy.js.map +1 -0
  77. package/dist/messaging/root.d.ts +4 -0
  78. package/dist/messaging/root.d.ts.map +1 -0
  79. package/dist/messaging/root.js +9 -0
  80. package/dist/messaging/root.js.map +1 -0
  81. package/dist/testing/fake-browser.d.ts +184 -0
  82. package/dist/testing/fake-browser.d.ts.map +1 -0
  83. package/dist/testing/fake-browser.js +994 -0
  84. package/dist/testing/fake-browser.js.map +1 -0
  85. package/dist/testing/index.d.ts +3 -0
  86. package/dist/testing/index.d.ts.map +1 -0
  87. package/dist/testing/index.js +2 -0
  88. package/dist/testing/index.js.map +1 -0
  89. package/dist/testing/setup.d.ts +2 -0
  90. package/dist/testing/setup.d.ts.map +1 -0
  91. package/dist/testing/setup.js +6 -0
  92. package/dist/testing/setup.js.map +1 -0
  93. package/dist/types/index.d.ts +1 -1
  94. package/dist/types/index.d.ts.map +1 -1
  95. package/dist/types/scripting.d.ts +5 -5
  96. package/dist/types/scripting.d.ts.map +1 -1
  97. package/dist/types/storage.d.ts +23 -0
  98. package/dist/types/storage.d.ts.map +1 -1
  99. package/package.json +30 -2
@@ -0,0 +1,994 @@
1
+ import { clearAdapterCache } from '../browser/resolve.js';
2
+ export function createFakeEvent() {
3
+ const listeners = new Set();
4
+ return {
5
+ addListener(listener) {
6
+ listeners.add(listener);
7
+ },
8
+ removeListener(listener) {
9
+ listeners.delete(listener);
10
+ },
11
+ hasListener(listener) {
12
+ return listeners.has(listener);
13
+ },
14
+ hasListeners() {
15
+ return listeners.size > 0;
16
+ },
17
+ emit(...args) {
18
+ this.dispatch(...args);
19
+ },
20
+ dispatch(...args) {
21
+ return [...listeners].map((listener) => listener(...args));
22
+ },
23
+ clear() {
24
+ listeners.clear();
25
+ },
26
+ get listenerCount() {
27
+ return listeners.size;
28
+ },
29
+ };
30
+ }
31
+ export function createFakeBrowser(options = {}) {
32
+ const target = options.target ?? 'chrome';
33
+ const id = options.id ?? 'fake-extension-id';
34
+ const manifest = {
35
+ manifest_version: 3,
36
+ name: 'Fake extension',
37
+ version: '1.0.0',
38
+ ...(options.messages === undefined
39
+ ? {}
40
+ : {
41
+ default_locale: 'en',
42
+ }),
43
+ ...options.manifest,
44
+ };
45
+ const state = {
46
+ storage: {
47
+ local: new Map(),
48
+ sync: new Map(),
49
+ session: new Map(),
50
+ managed: new Map(),
51
+ },
52
+ tabs: new Map(),
53
+ windows: new Map(),
54
+ currentWindowId: 1,
55
+ nextTabId: 1,
56
+ nextWindowId: 2,
57
+ nextNotificationId: 1,
58
+ alarms: new Map(),
59
+ permissions: new Set(options.permissions?.permissions ?? []),
60
+ origins: new Set(options.permissions?.origins ?? []),
61
+ notifications: new Map(),
62
+ badgeText: undefined,
63
+ title: undefined,
64
+ };
65
+ const events = [];
66
+ const event = () => {
67
+ const created = createFakeEvent();
68
+ events.push(created);
69
+ return created;
70
+ };
71
+ for (const [area, values] of Object.entries(options.initialStorage ?? {})) {
72
+ for (const [key, value] of Object.entries(values ?? {})) {
73
+ state.storage[area].set(key, value);
74
+ }
75
+ }
76
+ const onMessage = event();
77
+ const onInstalled = event();
78
+ const onStartup = event();
79
+ const onSuspend = event();
80
+ const storageOnChanged = event();
81
+ const onCreated = event();
82
+ const onUpdated = event();
83
+ const onRemoved = event();
84
+ const onActivated = event();
85
+ const actionOnClicked = event();
86
+ const onAlarm = event();
87
+ const permissionsOnAdded = event();
88
+ const permissionsOnRemoved = event();
89
+ const notificationsOnClicked = event();
90
+ const notificationsOnClosed = event();
91
+ const notificationsOnButtonClicked = event();
92
+ const commandsOnCommand = event();
93
+ const contextMenusOnClicked = event();
94
+ const cookiesOnChanged = event();
95
+ const downloadsOnChanged = event();
96
+ const sessionsOnChanged = event();
97
+ const defaultSender = {
98
+ id,
99
+ url: `chrome-extension://${id}/content.js`,
100
+ };
101
+ const runtime = {
102
+ id,
103
+ lastError: undefined,
104
+ getManifest() {
105
+ return manifest;
106
+ },
107
+ getURL(path) {
108
+ const prefix = target === 'firefox'
109
+ ? `moz-extension://${id}/`
110
+ : `chrome-extension://${id}/`;
111
+ return `${prefix}${path.replace(/^\//, '')}`;
112
+ },
113
+ sendMessage(...args) {
114
+ const callback = takeCallback(args);
115
+ const response = dispatchMessage(args[0]);
116
+ if (callback !== undefined) {
117
+ void response.then((value) => callback(value));
118
+ }
119
+ return response;
120
+ },
121
+ connect(connectInfo) {
122
+ const portOnMessage = createFakeEvent();
123
+ const portOnDisconnect = createFakeEvent();
124
+ let connected = true;
125
+ const port = {
126
+ name: connectInfo?.name ?? '',
127
+ sender: defaultSender,
128
+ postMessage() { },
129
+ disconnect() {
130
+ if (connected) {
131
+ connected = false;
132
+ portOnDisconnect.emit(port);
133
+ }
134
+ },
135
+ onMessage: portOnMessage,
136
+ onDisconnect: portOnDisconnect,
137
+ };
138
+ return port;
139
+ },
140
+ onMessage,
141
+ onInstalled,
142
+ onStartup,
143
+ onSuspend,
144
+ };
145
+ function dispatchMessage(message) {
146
+ return new Promise((resolve) => {
147
+ let settled = false;
148
+ let keepOpen = false;
149
+ const finish = (response) => {
150
+ if (settled) {
151
+ return;
152
+ }
153
+ settled = true;
154
+ resolve(response);
155
+ };
156
+ const results = onMessage.dispatch(message, defaultSender, finish);
157
+ for (const result of results) {
158
+ if (result === true) {
159
+ keepOpen = true;
160
+ }
161
+ }
162
+ if (!keepOpen) {
163
+ queueMicrotask(() => finish(undefined));
164
+ }
165
+ });
166
+ }
167
+ const storage = {
168
+ local: createArea('local'),
169
+ sync: createArea('sync'),
170
+ session: createArea('session'),
171
+ managed: createArea('managed'),
172
+ onChanged: storageOnChanged,
173
+ };
174
+ function createArea(area) {
175
+ const data = state.storage[area];
176
+ return {
177
+ get(...args) {
178
+ const callback = takeCallback(args);
179
+ const keys = args[0];
180
+ const value = readStorage(data, keys);
181
+ deliver(callback, value);
182
+ return Promise.resolve(value);
183
+ },
184
+ set(...args) {
185
+ const callback = takeCallback(args);
186
+ const items = args[0];
187
+ const changes = {};
188
+ for (const [key, value] of Object.entries(items)) {
189
+ const change = data.has(key)
190
+ ? { oldValue: data.get(key), newValue: value }
191
+ : { newValue: value };
192
+ changes[key] = change;
193
+ data.set(key, value);
194
+ }
195
+ if (Object.keys(changes).length > 0) {
196
+ storageOnChanged.emit(changes, area);
197
+ }
198
+ deliver(callback, undefined);
199
+ return Promise.resolve();
200
+ },
201
+ remove(...args) {
202
+ const callback = takeCallback(args);
203
+ const keys = args[0];
204
+ const names = normalizeKeys(keys);
205
+ const changes = {};
206
+ for (const key of names) {
207
+ if (data.has(key)) {
208
+ changes[key] = { oldValue: data.get(key) };
209
+ data.delete(key);
210
+ }
211
+ }
212
+ if (Object.keys(changes).length > 0) {
213
+ storageOnChanged.emit(changes, area);
214
+ }
215
+ deliver(callback, undefined);
216
+ return Promise.resolve();
217
+ },
218
+ clear(...args) {
219
+ const callback = takeCallback(args);
220
+ const changes = {};
221
+ for (const [key, value] of data) {
222
+ changes[key] = { oldValue: value };
223
+ }
224
+ data.clear();
225
+ if (Object.keys(changes).length > 0) {
226
+ storageOnChanged.emit(changes, area);
227
+ }
228
+ deliver(callback, undefined);
229
+ return Promise.resolve();
230
+ },
231
+ getBytesInUse(...args) {
232
+ const callback = takeCallback(args);
233
+ const bytes = JSON.stringify(Object.fromEntries(data)).length;
234
+ deliver(callback, bytes);
235
+ return Promise.resolve(bytes);
236
+ },
237
+ };
238
+ }
239
+ const tabs = {
240
+ query(...args) {
241
+ const callback = takeCallback(args);
242
+ const queryInfo = args[0] ?? {};
243
+ const matches = [...state.tabs.values()].filter((tab) => matchesTab(tab, queryInfo));
244
+ deliver(callback, matches);
245
+ return Promise.resolve(matches);
246
+ },
247
+ get(...args) {
248
+ const callback = takeCallback(args);
249
+ const tabId = args[0];
250
+ const tab = requireTab(state, tabId);
251
+ deliver(callback, tab);
252
+ return Promise.resolve(tab);
253
+ },
254
+ create(...args) {
255
+ const callback = takeCallback(args);
256
+ const properties = args[0] ?? {};
257
+ const tab = addTab(state, properties);
258
+ if (tab.active) {
259
+ for (const other of state.tabs.values()) {
260
+ if (other.windowId === tab.windowId && other.id !== tab.id) {
261
+ setTabActive(other, false);
262
+ }
263
+ }
264
+ onActivated.emit({ tabId: tab.id, windowId: tab.windowId });
265
+ }
266
+ onCreated.emit(tab);
267
+ deliver(callback, tab);
268
+ return Promise.resolve(tab);
269
+ },
270
+ update(...args) {
271
+ const callback = takeCallback(args);
272
+ const tabId = args[0];
273
+ const updates = args[1] ?? {};
274
+ const tab = requireTab(state, tabId);
275
+ const updated = { ...tab, ...updates };
276
+ state.tabs.set(tabId, updated);
277
+ if (updates.active === true) {
278
+ onActivated.emit({ tabId, windowId: updated.windowId });
279
+ }
280
+ onUpdated.emit(tabId, updates, updated);
281
+ deliver(callback, updated);
282
+ return Promise.resolve(updated);
283
+ },
284
+ remove(...args) {
285
+ const callback = takeCallback(args);
286
+ const tabIds = args[0];
287
+ const ids = Array.isArray(tabIds)
288
+ ? tabIds
289
+ : [tabIds];
290
+ for (const tabId of ids) {
291
+ if (state.tabs.delete(tabId)) {
292
+ onRemoved.emit(tabId, { windowId: state.currentWindowId });
293
+ }
294
+ }
295
+ deliver(callback, undefined);
296
+ return Promise.resolve();
297
+ },
298
+ reload(...args) {
299
+ const callback = takeCallback(args);
300
+ const tabId = typeof args[0] === 'number'
301
+ ? args[0]
302
+ : [...state.tabs.values()].find((tab) => tab.active)?.id;
303
+ if (tabId === undefined) {
304
+ throw new Error('No tab to reload.');
305
+ }
306
+ const tab = requireTab(state, tabId);
307
+ onUpdated.emit(tabId, { status: 'loading' }, tab);
308
+ deliver(callback, undefined);
309
+ return Promise.resolve();
310
+ },
311
+ sendMessage(...args) {
312
+ const callback = takeCallback(args);
313
+ deliver(callback, undefined);
314
+ return Promise.resolve(undefined);
315
+ },
316
+ onCreated,
317
+ onUpdated,
318
+ onRemoved,
319
+ onActivated,
320
+ };
321
+ const windows = {
322
+ getCurrent(...args) {
323
+ const callback = takeCallback(args);
324
+ const value = requireWindow(state, state.currentWindowId);
325
+ deliver(callback, value);
326
+ return Promise.resolve(value);
327
+ },
328
+ create(...args) {
329
+ const callback = takeCallback(args);
330
+ const properties = args[0] ?? {};
331
+ const window = {
332
+ id: state.nextWindowId,
333
+ focused: true,
334
+ type: 'normal',
335
+ state: 'normal',
336
+ ...properties,
337
+ };
338
+ state.nextWindowId += 1;
339
+ state.windows.set(window.id, window);
340
+ deliver(callback, window);
341
+ return Promise.resolve(window);
342
+ },
343
+ update(...args) {
344
+ const callback = takeCallback(args);
345
+ const windowId = args[0];
346
+ const updates = args[1] ?? {};
347
+ const window = {
348
+ ...requireWindow(state, windowId),
349
+ ...updates,
350
+ };
351
+ state.windows.set(windowId, window);
352
+ deliver(callback, window);
353
+ return Promise.resolve(window);
354
+ },
355
+ remove(...args) {
356
+ const callback = takeCallback(args);
357
+ state.windows.delete(args[0]);
358
+ deliver(callback, undefined);
359
+ return Promise.resolve();
360
+ },
361
+ getAll(...args) {
362
+ const callback = takeCallback(args);
363
+ const values = [...state.windows.values()];
364
+ deliver(callback, values);
365
+ return Promise.resolve(values);
366
+ },
367
+ };
368
+ const action = {
369
+ area: 'action',
370
+ setBadgeText(...args) {
371
+ const callback = takeCallback(args);
372
+ state.badgeText = args[0].text;
373
+ deliver(callback, undefined);
374
+ return Promise.resolve();
375
+ },
376
+ setBadgeBackgroundColor(...args) {
377
+ const callback = takeCallback(args);
378
+ deliver(callback, undefined);
379
+ return Promise.resolve();
380
+ },
381
+ setTitle(...args) {
382
+ const callback = takeCallback(args);
383
+ state.title = args[0].title;
384
+ deliver(callback, undefined);
385
+ return Promise.resolve();
386
+ },
387
+ setIcon(...args) {
388
+ const callback = takeCallback(args);
389
+ deliver(callback, undefined);
390
+ return Promise.resolve();
391
+ },
392
+ openPopup(...args) {
393
+ const callback = takeCallback(args);
394
+ deliver(callback, undefined);
395
+ return Promise.resolve();
396
+ },
397
+ onClicked: actionOnClicked,
398
+ get badgeText() {
399
+ return state.badgeText;
400
+ },
401
+ get title() {
402
+ return state.title;
403
+ },
404
+ };
405
+ const alarms = {
406
+ create(...args) {
407
+ const callback = takeCallback(args);
408
+ const [nameOrInfo, infoOrCallback] = args;
409
+ const name = typeof nameOrInfo === 'string' ? nameOrInfo : '';
410
+ const info = typeof nameOrInfo === 'string'
411
+ ? (infoOrCallback ?? {})
412
+ : (nameOrInfo ?? {});
413
+ const scheduledTime = typeof info.when === 'number'
414
+ ? info.when
415
+ : Date.now() + Number(info.delayInMinutes ?? 0) * 60_000;
416
+ state.alarms.set(name, {
417
+ name,
418
+ scheduledTime,
419
+ ...(info.periodInMinutes === undefined
420
+ ? {}
421
+ : { periodInMinutes: info.periodInMinutes }),
422
+ });
423
+ deliver(callback, undefined);
424
+ return Promise.resolve();
425
+ },
426
+ get(...args) {
427
+ const callback = takeCallback(args);
428
+ const value = state.alarms.get(args[0] ?? '');
429
+ deliver(callback, value);
430
+ return Promise.resolve(value);
431
+ },
432
+ getAll(...args) {
433
+ const callback = takeCallback(args);
434
+ const values = [...state.alarms.values()];
435
+ deliver(callback, values);
436
+ return Promise.resolve(values);
437
+ },
438
+ clear(...args) {
439
+ const callback = takeCallback(args);
440
+ const cleared = state.alarms.delete(args[0] ?? '');
441
+ deliver(callback, cleared);
442
+ return Promise.resolve(cleared);
443
+ },
444
+ clearAll(...args) {
445
+ const callback = takeCallback(args);
446
+ const cleared = state.alarms.size > 0;
447
+ state.alarms.clear();
448
+ deliver(callback, cleared);
449
+ return Promise.resolve(cleared);
450
+ },
451
+ onAlarm,
452
+ };
453
+ const messages = options.messages ?? {};
454
+ const i18n = {
455
+ getMessage(messageName, substitutions) {
456
+ const entry = messages[messageName];
457
+ const message = typeof entry === 'string' ? entry : entry?.message ?? '';
458
+ return applySubstitutions(message, substitutions);
459
+ },
460
+ getUILanguage() {
461
+ return options.uiLanguage ?? 'en-US';
462
+ },
463
+ detectLanguage() {
464
+ return Promise.resolve({
465
+ isReliable: true,
466
+ languages: [{ language: options.uiLanguage ?? 'en', percentage: 100 }],
467
+ });
468
+ },
469
+ };
470
+ const permissions = {
471
+ contains(...args) {
472
+ const callback = takeCallback(args);
473
+ const value = containsPermissions(state, args[0]);
474
+ deliver(callback, value);
475
+ return Promise.resolve(value);
476
+ },
477
+ request(...args) {
478
+ const callback = takeCallback(args);
479
+ const requested = args[0];
480
+ const added = addPermissions(state, requested);
481
+ if (added.permissions.length > 0 || added.origins.length > 0) {
482
+ permissionsOnAdded.emit(added);
483
+ }
484
+ deliver(callback, true);
485
+ return Promise.resolve(true);
486
+ },
487
+ remove(...args) {
488
+ const callback = takeCallback(args);
489
+ const removed = removePermissions(state, args[0]);
490
+ if (removed.permissions.length > 0 || removed.origins.length > 0) {
491
+ permissionsOnRemoved.emit(removed);
492
+ }
493
+ deliver(callback, true);
494
+ return Promise.resolve(true);
495
+ },
496
+ getAll(...args) {
497
+ const callback = takeCallback(args);
498
+ const value = {
499
+ permissions: [...state.permissions],
500
+ origins: [...state.origins],
501
+ };
502
+ deliver(callback, value);
503
+ return Promise.resolve(value);
504
+ },
505
+ onAdded: permissionsOnAdded,
506
+ onRemoved: permissionsOnRemoved,
507
+ };
508
+ const scripting = {
509
+ executeScript(...args) {
510
+ const callback = takeCallback(args);
511
+ const input = args[0];
512
+ const injections = Array.isArray(input) ? input : [input];
513
+ const results = [];
514
+ for (const injection of injections) {
515
+ const frameId = injection.target.frameIds?.[0] ?? 0;
516
+ try {
517
+ const result = injection.func === undefined
518
+ ? undefined
519
+ : injection.func(...(injection.args ?? []));
520
+ results.push(result === undefined
521
+ ? { frameId }
522
+ : { frameId, result });
523
+ }
524
+ catch (error) {
525
+ results.push({
526
+ frameId,
527
+ error: error instanceof Error ? error.message : String(error),
528
+ });
529
+ }
530
+ }
531
+ deliver(callback, results);
532
+ return Promise.resolve(results);
533
+ },
534
+ insertCSS(...args) {
535
+ const callback = takeCallback(args);
536
+ deliver(callback, undefined);
537
+ return Promise.resolve();
538
+ },
539
+ removeCSS(...args) {
540
+ const callback = takeCallback(args);
541
+ deliver(callback, undefined);
542
+ return Promise.resolve();
543
+ },
544
+ };
545
+ const notifications = {
546
+ create(...args) {
547
+ const callback = takeCallback(args);
548
+ const id = `notification-${state.nextNotificationId}`;
549
+ state.nextNotificationId += 1;
550
+ state.notifications.set(id, args[0] ?? {});
551
+ deliver(callback, id);
552
+ return Promise.resolve(id);
553
+ },
554
+ update(...args) {
555
+ const callback = takeCallback(args);
556
+ const id = args[0];
557
+ const updated = state.notifications.has(id);
558
+ if (updated) {
559
+ state.notifications.set(id, args[1]);
560
+ }
561
+ deliver(callback, updated);
562
+ return Promise.resolve(updated);
563
+ },
564
+ clear(...args) {
565
+ const callback = takeCallback(args);
566
+ const cleared = state.notifications.delete(args[0]);
567
+ deliver(callback, cleared);
568
+ return Promise.resolve(cleared);
569
+ },
570
+ getAll(...args) {
571
+ const callback = takeCallback(args);
572
+ const value = Object.fromEntries([...state.notifications.keys()].map((key) => [key, true]));
573
+ deliver(callback, value);
574
+ return Promise.resolve(value);
575
+ },
576
+ onClicked: notificationsOnClicked,
577
+ onClosed: notificationsOnClosed,
578
+ onButtonClicked: notificationsOnButtonClicked,
579
+ };
580
+ const commands = {
581
+ getAll(...args) {
582
+ const callback = takeCallback(args);
583
+ deliver(callback, []);
584
+ return Promise.resolve([]);
585
+ },
586
+ onCommand: commandsOnCommand,
587
+ };
588
+ const contextMenus = {
589
+ create(...args) {
590
+ const id = args[0].id ?? 'context-menu';
591
+ takeCallback(args);
592
+ return id;
593
+ },
594
+ update(...args) {
595
+ const callback = takeCallback(args);
596
+ deliver(callback, undefined);
597
+ return Promise.resolve();
598
+ },
599
+ remove(...args) {
600
+ const callback = takeCallback(args);
601
+ deliver(callback, undefined);
602
+ return Promise.resolve();
603
+ },
604
+ removeAll(...args) {
605
+ const callback = takeCallback(args);
606
+ deliver(callback, undefined);
607
+ return Promise.resolve();
608
+ },
609
+ onClicked: contextMenusOnClicked,
610
+ };
611
+ const cookies = {
612
+ get(...args) {
613
+ const callback = takeCallback(args);
614
+ deliver(callback, null);
615
+ return Promise.resolve(null);
616
+ },
617
+ getAll(...args) {
618
+ const callback = takeCallback(args);
619
+ deliver(callback, []);
620
+ return Promise.resolve([]);
621
+ },
622
+ set(...args) {
623
+ const callback = takeCallback(args);
624
+ const value = args[0];
625
+ deliver(callback, value);
626
+ return Promise.resolve(value);
627
+ },
628
+ remove(...args) {
629
+ const callback = takeCallback(args);
630
+ deliver(callback, undefined);
631
+ return Promise.resolve();
632
+ },
633
+ onChanged: cookiesOnChanged,
634
+ };
635
+ const downloads = {
636
+ download(...args) {
637
+ const callback = takeCallback(args);
638
+ const id = state.nextNotificationId;
639
+ state.nextNotificationId += 1;
640
+ deliver(callback, id);
641
+ return Promise.resolve(id);
642
+ },
643
+ cancel(...args) {
644
+ const callback = takeCallback(args);
645
+ deliver(callback, undefined);
646
+ return Promise.resolve();
647
+ },
648
+ search(...args) {
649
+ const callback = takeCallback(args);
650
+ deliver(callback, []);
651
+ return Promise.resolve([]);
652
+ },
653
+ onChanged: downloadsOnChanged,
654
+ };
655
+ const sessions = {
656
+ getRecentlyClosed(...args) {
657
+ const callback = takeCallback(args);
658
+ deliver(callback, []);
659
+ return Promise.resolve([]);
660
+ },
661
+ restore(...args) {
662
+ const callback = takeCallback(args);
663
+ deliver(callback, undefined);
664
+ return Promise.resolve(undefined);
665
+ },
666
+ onChanged: sessionsOnChanged,
667
+ };
668
+ const bookmarks = {
669
+ getTree(...args) {
670
+ const callback = takeCallback(args);
671
+ deliver(callback, []);
672
+ return Promise.resolve([]);
673
+ },
674
+ search(...args) {
675
+ const callback = takeCallback(args);
676
+ deliver(callback, []);
677
+ return Promise.resolve([]);
678
+ },
679
+ get(...args) {
680
+ const callback = takeCallback(args);
681
+ deliver(callback, []);
682
+ return Promise.resolve([]);
683
+ },
684
+ getSubTree(...args) {
685
+ const callback = takeCallback(args);
686
+ deliver(callback, []);
687
+ return Promise.resolve([]);
688
+ },
689
+ getChildren(...args) {
690
+ const callback = takeCallback(args);
691
+ deliver(callback, []);
692
+ return Promise.resolve([]);
693
+ },
694
+ create(...args) {
695
+ const callback = takeCallback(args);
696
+ const value = args[0];
697
+ deliver(callback, value);
698
+ return Promise.resolve(value);
699
+ },
700
+ move(...args) {
701
+ const callback = takeCallback(args);
702
+ const value = args[0];
703
+ deliver(callback, value);
704
+ return Promise.resolve(value);
705
+ },
706
+ update(...args) {
707
+ const callback = takeCallback(args);
708
+ const value = args[0];
709
+ deliver(callback, value);
710
+ return Promise.resolve(value);
711
+ },
712
+ remove(...args) {
713
+ const callback = takeCallback(args);
714
+ deliver(callback, undefined);
715
+ return Promise.resolve();
716
+ },
717
+ removeTree(...args) {
718
+ const callback = takeCallback(args);
719
+ deliver(callback, undefined);
720
+ return Promise.resolve();
721
+ },
722
+ onCreated: event(),
723
+ onRemoved: event(),
724
+ onChanged: event(),
725
+ onMoved: event(),
726
+ };
727
+ const webNavigation = {
728
+ onBeforeNavigate: event(),
729
+ onCommitted: event(),
730
+ onDOMContentLoaded: event(),
731
+ onCompleted: event(),
732
+ onErrorOccurred: event(),
733
+ onCreatedNavigationTarget: event(),
734
+ onReferenceFragmentUpdated: event(),
735
+ onHistoryStateUpdated: event(),
736
+ };
737
+ const webRequest = {
738
+ onBeforeRequest: event(),
739
+ onBeforeSendHeaders: event(),
740
+ onSendHeaders: event(),
741
+ onHeadersReceived: event(),
742
+ onResponseStarted: event(),
743
+ onCompleted: event(),
744
+ onErrorOccurred: event(),
745
+ };
746
+ if (state.windows.size === 0) {
747
+ state.windows.set(state.currentWindowId, {
748
+ id: state.currentWindowId,
749
+ focused: true,
750
+ type: 'normal',
751
+ state: 'normal',
752
+ });
753
+ }
754
+ for (const tab of options.initialTabs ?? []) {
755
+ addTab(state, tab);
756
+ }
757
+ const fake = {
758
+ target,
759
+ runtime,
760
+ storage,
761
+ tabs,
762
+ windows,
763
+ action,
764
+ alarms,
765
+ i18n,
766
+ permissions,
767
+ scripting,
768
+ notifications,
769
+ commands,
770
+ contextMenus,
771
+ cookies,
772
+ downloads,
773
+ sessions,
774
+ bookmarks,
775
+ webNavigation,
776
+ webRequest,
777
+ reset() {
778
+ for (const area of Object.values(state.storage)) {
779
+ area.clear();
780
+ }
781
+ state.tabs.clear();
782
+ state.windows.clear();
783
+ state.windows.set(state.currentWindowId, {
784
+ id: state.currentWindowId,
785
+ focused: true,
786
+ type: 'normal',
787
+ state: 'normal',
788
+ });
789
+ state.nextTabId = 1;
790
+ state.nextWindowId = 2;
791
+ state.nextNotificationId = 1;
792
+ state.alarms.clear();
793
+ state.permissions.clear();
794
+ state.origins.clear();
795
+ state.notifications.clear();
796
+ state.badgeText = undefined;
797
+ state.title = undefined;
798
+ for (const created of events) {
799
+ created.clear();
800
+ }
801
+ },
802
+ };
803
+ return fake;
804
+ }
805
+ export const fakeBrowser = /*#__PURE__*/ createFakeBrowser();
806
+ let installedFakeBrowser;
807
+ export function resetFakeBrowser() {
808
+ (installedFakeBrowser ?? fakeBrowser).reset();
809
+ }
810
+ export function installFakeBrowser(options = {}) {
811
+ const scope = (options.scope ?? globalThis);
812
+ const fake = Object.keys(options).length === 0
813
+ ? fakeBrowser
814
+ : createFakeBrowser(options);
815
+ const target = options.target ?? fake.target;
816
+ if (target === 'firefox') {
817
+ scope.browser = fake;
818
+ delete scope.chrome;
819
+ }
820
+ else {
821
+ scope.chrome = fake;
822
+ scope.browser = fake;
823
+ }
824
+ installedFakeBrowser = fake;
825
+ clearAdapterCache();
826
+ return fake;
827
+ }
828
+ export function uninstallFakeBrowser(scope = globalThis) {
829
+ const value = scope;
830
+ delete value.browser;
831
+ delete value.chrome;
832
+ installedFakeBrowser = undefined;
833
+ clearAdapterCache();
834
+ }
835
+ function readStorage(data, keys) {
836
+ if (keys === undefined || keys === null || typeof keys === 'function') {
837
+ return Object.fromEntries(data);
838
+ }
839
+ if (typeof keys === 'string') {
840
+ return data.has(keys) ? { [keys]: data.get(keys) } : {};
841
+ }
842
+ if (Array.isArray(keys)) {
843
+ return Object.fromEntries(keys
844
+ .filter((key) => typeof key === 'string')
845
+ .filter((key) => data.has(key))
846
+ .map((key) => [key, data.get(key)]));
847
+ }
848
+ if (typeof keys === 'object') {
849
+ const result = {};
850
+ for (const [key, fallback] of Object.entries(keys)) {
851
+ result[key] = data.has(key) ? data.get(key) : fallback;
852
+ }
853
+ return result;
854
+ }
855
+ return {};
856
+ }
857
+ function normalizeKeys(keys) {
858
+ if (keys === undefined || keys === null) {
859
+ return [];
860
+ }
861
+ return Array.isArray(keys)
862
+ ? keys.filter((key) => typeof key === 'string')
863
+ : typeof keys === 'string'
864
+ ? [keys]
865
+ : [];
866
+ }
867
+ function takeCallback(args) {
868
+ const last = args[args.length - 1];
869
+ if (typeof last === 'function') {
870
+ args.pop();
871
+ return last;
872
+ }
873
+ return undefined;
874
+ }
875
+ function deliver(callback, value) {
876
+ if (callback !== undefined) {
877
+ queueMicrotask(() => callback(value));
878
+ }
879
+ }
880
+ function matchesTab(tab, query) {
881
+ for (const [key, value] of Object.entries(query)) {
882
+ if (value === undefined ||
883
+ key === 'currentWindow' ||
884
+ key === 'lastFocusedWindow' ||
885
+ key === 'windowType' ||
886
+ key === 'populate') {
887
+ continue;
888
+ }
889
+ if (key === 'url') {
890
+ const urls = Array.isArray(value) ? value : [value];
891
+ if (!urls.some((url) => matchesPattern(String(url), tab.url ?? ''))) {
892
+ return false;
893
+ }
894
+ continue;
895
+ }
896
+ if (tab[key] !== value) {
897
+ return false;
898
+ }
899
+ }
900
+ return true;
901
+ }
902
+ function matchesPattern(pattern, value) {
903
+ if (pattern === value || pattern === '<all_urls>' || pattern === '*://*/*') {
904
+ return true;
905
+ }
906
+ const expression = pattern
907
+ .replace(/[.+?^${}()|[\]\\]/g, '\\$&')
908
+ .replaceAll('*', '.*');
909
+ return new RegExp(`^${expression}$`).test(value);
910
+ }
911
+ function addTab(state, properties) {
912
+ const id = typeof properties.id === 'number' ? properties.id : state.nextTabId;
913
+ state.nextTabId = Math.max(state.nextTabId, id + 1);
914
+ const windowId = typeof properties.windowId === 'number'
915
+ ? properties.windowId
916
+ : state.currentWindowId;
917
+ const index = typeof properties.index === 'number'
918
+ ? properties.index
919
+ : [...state.tabs.values()].filter((tab) => tab.windowId === windowId)
920
+ .length;
921
+ const tab = {
922
+ id,
923
+ index,
924
+ windowId,
925
+ active: properties.active === true,
926
+ highlighted: properties.highlighted === true,
927
+ pinned: properties.pinned === true,
928
+ incognito: properties.incognito === true,
929
+ ...properties,
930
+ };
931
+ state.tabs.set(id, tab);
932
+ return tab;
933
+ }
934
+ function setTabActive(tab, active) {
935
+ const mutable = tab;
936
+ mutable.active = active;
937
+ }
938
+ function requireTab(state, tabId) {
939
+ const tab = state.tabs.get(tabId);
940
+ if (tab === undefined) {
941
+ throw new Error(`No tab with id ${tabId}.`);
942
+ }
943
+ return tab;
944
+ }
945
+ function requireWindow(state, windowId) {
946
+ const window = state.windows.get(windowId);
947
+ if (window === undefined) {
948
+ throw new Error(`No window with id ${windowId}.`);
949
+ }
950
+ return window;
951
+ }
952
+ function containsPermissions(state, requested) {
953
+ const permissions = requested.permissions ?? [];
954
+ const origins = requested.origins ?? [];
955
+ return ((Array.isArray(permissions) &&
956
+ permissions.every((permission) => state.permissions.has(String(permission)))) &&
957
+ (Array.isArray(origins) &&
958
+ origins.every((origin) => state.origins.has(String(origin)))));
959
+ }
960
+ function addPermissions(state, requested) {
961
+ const permissions = [];
962
+ const origins = [];
963
+ for (const permission of toArray(requested.permissions)) {
964
+ if (!state.permissions.has(permission)) {
965
+ state.permissions.add(permission);
966
+ permissions.push(permission);
967
+ }
968
+ }
969
+ for (const origin of toArray(requested.origins)) {
970
+ if (!state.origins.has(origin)) {
971
+ state.origins.add(origin);
972
+ origins.push(origin);
973
+ }
974
+ }
975
+ return { permissions, origins };
976
+ }
977
+ function removePermissions(state, requested) {
978
+ const permissions = toArray(requested.permissions).filter((permission) => state.permissions.delete(permission));
979
+ const origins = toArray(requested.origins).filter((origin) => state.origins.delete(origin));
980
+ return { permissions, origins };
981
+ }
982
+ function toArray(value) {
983
+ return Array.isArray(value)
984
+ ? value.map((item) => String(item))
985
+ : [];
986
+ }
987
+ function applySubstitutions(message, substitutions) {
988
+ if (substitutions === undefined) {
989
+ return message;
990
+ }
991
+ const values = typeof substitutions === 'string' ? [substitutions] : substitutions;
992
+ return values.reduce((result, value, index) => result.replaceAll(`$${index + 1}`, value), message);
993
+ }
994
+ //# sourceMappingURL=fake-browser.js.map