UNPKG

38.4 kBJavaScriptView Raw
1(function (global, factory) {
2 if (typeof define === "function" && define.amd) {
3 define("webextension-polyfill", ["module"], factory);
4 } else if (typeof exports !== "undefined") {
5 factory(module);
6 } else {
7 var mod = {
8 exports: {}
9 };
10 factory(mod);
11 global.browser = mod.exports;
12 }
13})(typeof globalThis !== "undefined" ? globalThis : typeof self !== "undefined" ? self : this, function (module) {
14 /* webextension-polyfill - v0.10.0 - Fri Aug 12 2022 19:42:44 */
15
16 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
17
18 /* vim: set sts=2 sw=2 et tw=80: */
19
20 /* This Source Code Form is subject to the terms of the Mozilla Public
21 * License, v. 2.0. If a copy of the MPL was not distributed with this
22 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
23 "use strict";
24
25 if (!globalThis.chrome?.runtime?.id) {
26 throw new Error("This script should only be loaded in a browser extension.");
27 }
28
29 if (typeof globalThis.browser === "undefined" || Object.getPrototypeOf(globalThis.browser) !== Object.prototype) {
30 const CHROME_SEND_MESSAGE_CALLBACK_NO_RESPONSE_MESSAGE = "The message port closed before a response was received."; // Wrapping the bulk of this polyfill in a one-time-use function is a minor
31 // optimization for Firefox. Since Spidermonkey does not fully parse the
32 // contents of a function until the first time it's called, and since it will
33 // never actually need to be called, this allows the polyfill to be included
34 // in Firefox nearly for free.
35
36 const wrapAPIs = extensionAPIs => {
37 // NOTE: apiMetadata is associated to the content of the api-metadata.json file
38 // at build time by replacing the following "include" with the content of the
39 // JSON file.
40 const apiMetadata = {
41 "alarms": {
42 "clear": {
43 "minArgs": 0,
44 "maxArgs": 1
45 },
46 "clearAll": {
47 "minArgs": 0,
48 "maxArgs": 0
49 },
50 "get": {
51 "minArgs": 0,
52 "maxArgs": 1
53 },
54 "getAll": {
55 "minArgs": 0,
56 "maxArgs": 0
57 }
58 },
59 "bookmarks": {
60 "create": {
61 "minArgs": 1,
62 "maxArgs": 1
63 },
64 "get": {
65 "minArgs": 1,
66 "maxArgs": 1
67 },
68 "getChildren": {
69 "minArgs": 1,
70 "maxArgs": 1
71 },
72 "getRecent": {
73 "minArgs": 1,
74 "maxArgs": 1
75 },
76 "getSubTree": {
77 "minArgs": 1,
78 "maxArgs": 1
79 },
80 "getTree": {
81 "minArgs": 0,
82 "maxArgs": 0
83 },
84 "move": {
85 "minArgs": 2,
86 "maxArgs": 2
87 },
88 "remove": {
89 "minArgs": 1,
90 "maxArgs": 1
91 },
92 "removeTree": {
93 "minArgs": 1,
94 "maxArgs": 1
95 },
96 "search": {
97 "minArgs": 1,
98 "maxArgs": 1
99 },
100 "update": {
101 "minArgs": 2,
102 "maxArgs": 2
103 }
104 },
105 "browserAction": {
106 "disable": {
107 "minArgs": 0,
108 "maxArgs": 1,
109 "fallbackToNoCallback": true
110 },
111 "enable": {
112 "minArgs": 0,
113 "maxArgs": 1,
114 "fallbackToNoCallback": true
115 },
116 "getBadgeBackgroundColor": {
117 "minArgs": 1,
118 "maxArgs": 1
119 },
120 "getBadgeText": {
121 "minArgs": 1,
122 "maxArgs": 1
123 },
124 "getPopup": {
125 "minArgs": 1,
126 "maxArgs": 1
127 },
128 "getTitle": {
129 "minArgs": 1,
130 "maxArgs": 1
131 },
132 "openPopup": {
133 "minArgs": 0,
134 "maxArgs": 0
135 },
136 "setBadgeBackgroundColor": {
137 "minArgs": 1,
138 "maxArgs": 1,
139 "fallbackToNoCallback": true
140 },
141 "setBadgeText": {
142 "minArgs": 1,
143 "maxArgs": 1,
144 "fallbackToNoCallback": true
145 },
146 "setIcon": {
147 "minArgs": 1,
148 "maxArgs": 1
149 },
150 "setPopup": {
151 "minArgs": 1,
152 "maxArgs": 1,
153 "fallbackToNoCallback": true
154 },
155 "setTitle": {
156 "minArgs": 1,
157 "maxArgs": 1,
158 "fallbackToNoCallback": true
159 }
160 },
161 "browsingData": {
162 "remove": {
163 "minArgs": 2,
164 "maxArgs": 2
165 },
166 "removeCache": {
167 "minArgs": 1,
168 "maxArgs": 1
169 },
170 "removeCookies": {
171 "minArgs": 1,
172 "maxArgs": 1
173 },
174 "removeDownloads": {
175 "minArgs": 1,
176 "maxArgs": 1
177 },
178 "removeFormData": {
179 "minArgs": 1,
180 "maxArgs": 1
181 },
182 "removeHistory": {
183 "minArgs": 1,
184 "maxArgs": 1
185 },
186 "removeLocalStorage": {
187 "minArgs": 1,
188 "maxArgs": 1
189 },
190 "removePasswords": {
191 "minArgs": 1,
192 "maxArgs": 1
193 },
194 "removePluginData": {
195 "minArgs": 1,
196 "maxArgs": 1
197 },
198 "settings": {
199 "minArgs": 0,
200 "maxArgs": 0
201 }
202 },
203 "commands": {
204 "getAll": {
205 "minArgs": 0,
206 "maxArgs": 0
207 }
208 },
209 "contextMenus": {
210 "remove": {
211 "minArgs": 1,
212 "maxArgs": 1
213 },
214 "removeAll": {
215 "minArgs": 0,
216 "maxArgs": 0
217 },
218 "update": {
219 "minArgs": 2,
220 "maxArgs": 2
221 }
222 },
223 "cookies": {
224 "get": {
225 "minArgs": 1,
226 "maxArgs": 1
227 },
228 "getAll": {
229 "minArgs": 1,
230 "maxArgs": 1
231 },
232 "getAllCookieStores": {
233 "minArgs": 0,
234 "maxArgs": 0
235 },
236 "remove": {
237 "minArgs": 1,
238 "maxArgs": 1
239 },
240 "set": {
241 "minArgs": 1,
242 "maxArgs": 1
243 }
244 },
245 "devtools": {
246 "inspectedWindow": {
247 "eval": {
248 "minArgs": 1,
249 "maxArgs": 2,
250 "singleCallbackArg": false
251 }
252 },
253 "panels": {
254 "create": {
255 "minArgs": 3,
256 "maxArgs": 3,
257 "singleCallbackArg": true
258 },
259 "elements": {
260 "createSidebarPane": {
261 "minArgs": 1,
262 "maxArgs": 1
263 }
264 }
265 }
266 },
267 "downloads": {
268 "cancel": {
269 "minArgs": 1,
270 "maxArgs": 1
271 },
272 "download": {
273 "minArgs": 1,
274 "maxArgs": 1
275 },
276 "erase": {
277 "minArgs": 1,
278 "maxArgs": 1
279 },
280 "getFileIcon": {
281 "minArgs": 1,
282 "maxArgs": 2
283 },
284 "open": {
285 "minArgs": 1,
286 "maxArgs": 1,
287 "fallbackToNoCallback": true
288 },
289 "pause": {
290 "minArgs": 1,
291 "maxArgs": 1
292 },
293 "removeFile": {
294 "minArgs": 1,
295 "maxArgs": 1
296 },
297 "resume": {
298 "minArgs": 1,
299 "maxArgs": 1
300 },
301 "search": {
302 "minArgs": 1,
303 "maxArgs": 1
304 },
305 "show": {
306 "minArgs": 1,
307 "maxArgs": 1,
308 "fallbackToNoCallback": true
309 }
310 },
311 "extension": {
312 "isAllowedFileSchemeAccess": {
313 "minArgs": 0,
314 "maxArgs": 0
315 },
316 "isAllowedIncognitoAccess": {
317 "minArgs": 0,
318 "maxArgs": 0
319 }
320 },
321 "history": {
322 "addUrl": {
323 "minArgs": 1,
324 "maxArgs": 1
325 },
326 "deleteAll": {
327 "minArgs": 0,
328 "maxArgs": 0
329 },
330 "deleteRange": {
331 "minArgs": 1,
332 "maxArgs": 1
333 },
334 "deleteUrl": {
335 "minArgs": 1,
336 "maxArgs": 1
337 },
338 "getVisits": {
339 "minArgs": 1,
340 "maxArgs": 1
341 },
342 "search": {
343 "minArgs": 1,
344 "maxArgs": 1
345 }
346 },
347 "i18n": {
348 "detectLanguage": {
349 "minArgs": 1,
350 "maxArgs": 1
351 },
352 "getAcceptLanguages": {
353 "minArgs": 0,
354 "maxArgs": 0
355 }
356 },
357 "identity": {
358 "launchWebAuthFlow": {
359 "minArgs": 1,
360 "maxArgs": 1
361 }
362 },
363 "idle": {
364 "queryState": {
365 "minArgs": 1,
366 "maxArgs": 1
367 }
368 },
369 "management": {
370 "get": {
371 "minArgs": 1,
372 "maxArgs": 1
373 },
374 "getAll": {
375 "minArgs": 0,
376 "maxArgs": 0
377 },
378 "getSelf": {
379 "minArgs": 0,
380 "maxArgs": 0
381 },
382 "setEnabled": {
383 "minArgs": 2,
384 "maxArgs": 2
385 },
386 "uninstallSelf": {
387 "minArgs": 0,
388 "maxArgs": 1
389 }
390 },
391 "notifications": {
392 "clear": {
393 "minArgs": 1,
394 "maxArgs": 1
395 },
396 "create": {
397 "minArgs": 1,
398 "maxArgs": 2
399 },
400 "getAll": {
401 "minArgs": 0,
402 "maxArgs": 0
403 },
404 "getPermissionLevel": {
405 "minArgs": 0,
406 "maxArgs": 0
407 },
408 "update": {
409 "minArgs": 2,
410 "maxArgs": 2
411 }
412 },
413 "pageAction": {
414 "getPopup": {
415 "minArgs": 1,
416 "maxArgs": 1
417 },
418 "getTitle": {
419 "minArgs": 1,
420 "maxArgs": 1
421 },
422 "hide": {
423 "minArgs": 1,
424 "maxArgs": 1,
425 "fallbackToNoCallback": true
426 },
427 "setIcon": {
428 "minArgs": 1,
429 "maxArgs": 1
430 },
431 "setPopup": {
432 "minArgs": 1,
433 "maxArgs": 1,
434 "fallbackToNoCallback": true
435 },
436 "setTitle": {
437 "minArgs": 1,
438 "maxArgs": 1,
439 "fallbackToNoCallback": true
440 },
441 "show": {
442 "minArgs": 1,
443 "maxArgs": 1,
444 "fallbackToNoCallback": true
445 }
446 },
447 "permissions": {
448 "contains": {
449 "minArgs": 1,
450 "maxArgs": 1
451 },
452 "getAll": {
453 "minArgs": 0,
454 "maxArgs": 0
455 },
456 "remove": {
457 "minArgs": 1,
458 "maxArgs": 1
459 },
460 "request": {
461 "minArgs": 1,
462 "maxArgs": 1
463 }
464 },
465 "runtime": {
466 "getBackgroundPage": {
467 "minArgs": 0,
468 "maxArgs": 0
469 },
470 "getPlatformInfo": {
471 "minArgs": 0,
472 "maxArgs": 0
473 },
474 "openOptionsPage": {
475 "minArgs": 0,
476 "maxArgs": 0
477 },
478 "requestUpdateCheck": {
479 "minArgs": 0,
480 "maxArgs": 0
481 },
482 "sendMessage": {
483 "minArgs": 1,
484 "maxArgs": 3
485 },
486 "sendNativeMessage": {
487 "minArgs": 2,
488 "maxArgs": 2
489 },
490 "setUninstallURL": {
491 "minArgs": 1,
492 "maxArgs": 1
493 }
494 },
495 "sessions": {
496 "getDevices": {
497 "minArgs": 0,
498 "maxArgs": 1
499 },
500 "getRecentlyClosed": {
501 "minArgs": 0,
502 "maxArgs": 1
503 },
504 "restore": {
505 "minArgs": 0,
506 "maxArgs": 1
507 }
508 },
509 "storage": {
510 "local": {
511 "clear": {
512 "minArgs": 0,
513 "maxArgs": 0
514 },
515 "get": {
516 "minArgs": 0,
517 "maxArgs": 1
518 },
519 "getBytesInUse": {
520 "minArgs": 0,
521 "maxArgs": 1
522 },
523 "remove": {
524 "minArgs": 1,
525 "maxArgs": 1
526 },
527 "set": {
528 "minArgs": 1,
529 "maxArgs": 1
530 }
531 },
532 "managed": {
533 "get": {
534 "minArgs": 0,
535 "maxArgs": 1
536 },
537 "getBytesInUse": {
538 "minArgs": 0,
539 "maxArgs": 1
540 }
541 },
542 "sync": {
543 "clear": {
544 "minArgs": 0,
545 "maxArgs": 0
546 },
547 "get": {
548 "minArgs": 0,
549 "maxArgs": 1
550 },
551 "getBytesInUse": {
552 "minArgs": 0,
553 "maxArgs": 1
554 },
555 "remove": {
556 "minArgs": 1,
557 "maxArgs": 1
558 },
559 "set": {
560 "minArgs": 1,
561 "maxArgs": 1
562 }
563 }
564 },
565 "tabs": {
566 "captureVisibleTab": {
567 "minArgs": 0,
568 "maxArgs": 2
569 },
570 "create": {
571 "minArgs": 1,
572 "maxArgs": 1
573 },
574 "detectLanguage": {
575 "minArgs": 0,
576 "maxArgs": 1
577 },
578 "discard": {
579 "minArgs": 0,
580 "maxArgs": 1
581 },
582 "duplicate": {
583 "minArgs": 1,
584 "maxArgs": 1
585 },
586 "executeScript": {
587 "minArgs": 1,
588 "maxArgs": 2
589 },
590 "get": {
591 "minArgs": 1,
592 "maxArgs": 1
593 },
594 "getCurrent": {
595 "minArgs": 0,
596 "maxArgs": 0
597 },
598 "getZoom": {
599 "minArgs": 0,
600 "maxArgs": 1
601 },
602 "getZoomSettings": {
603 "minArgs": 0,
604 "maxArgs": 1
605 },
606 "goBack": {
607 "minArgs": 0,
608 "maxArgs": 1
609 },
610 "goForward": {
611 "minArgs": 0,
612 "maxArgs": 1
613 },
614 "highlight": {
615 "minArgs": 1,
616 "maxArgs": 1
617 },
618 "insertCSS": {
619 "minArgs": 1,
620 "maxArgs": 2
621 },
622 "move": {
623 "minArgs": 2,
624 "maxArgs": 2
625 },
626 "query": {
627 "minArgs": 1,
628 "maxArgs": 1
629 },
630 "reload": {
631 "minArgs": 0,
632 "maxArgs": 2
633 },
634 "remove": {
635 "minArgs": 1,
636 "maxArgs": 1
637 },
638 "removeCSS": {
639 "minArgs": 1,
640 "maxArgs": 2
641 },
642 "sendMessage": {
643 "minArgs": 2,
644 "maxArgs": 3
645 },
646 "setZoom": {
647 "minArgs": 1,
648 "maxArgs": 2
649 },
650 "setZoomSettings": {
651 "minArgs": 1,
652 "maxArgs": 2
653 },
654 "update": {
655 "minArgs": 1,
656 "maxArgs": 2
657 }
658 },
659 "topSites": {
660 "get": {
661 "minArgs": 0,
662 "maxArgs": 0
663 }
664 },
665 "webNavigation": {
666 "getAllFrames": {
667 "minArgs": 1,
668 "maxArgs": 1
669 },
670 "getFrame": {
671 "minArgs": 1,
672 "maxArgs": 1
673 }
674 },
675 "webRequest": {
676 "handlerBehaviorChanged": {
677 "minArgs": 0,
678 "maxArgs": 0
679 }
680 },
681 "windows": {
682 "create": {
683 "minArgs": 0,
684 "maxArgs": 1
685 },
686 "get": {
687 "minArgs": 1,
688 "maxArgs": 2
689 },
690 "getAll": {
691 "minArgs": 0,
692 "maxArgs": 1
693 },
694 "getCurrent": {
695 "minArgs": 0,
696 "maxArgs": 1
697 },
698 "getLastFocused": {
699 "minArgs": 0,
700 "maxArgs": 1
701 },
702 "remove": {
703 "minArgs": 1,
704 "maxArgs": 1
705 },
706 "update": {
707 "minArgs": 2,
708 "maxArgs": 2
709 }
710 }
711 };
712
713 if (Object.keys(apiMetadata).length === 0) {
714 throw new Error("api-metadata.json has not been included in browser-polyfill");
715 }
716 /**
717 * A WeakMap subclass which creates and stores a value for any key which does
718 * not exist when accessed, but behaves exactly as an ordinary WeakMap
719 * otherwise.
720 *
721 * @param {function} createItem
722 * A function which will be called in order to create the value for any
723 * key which does not exist, the first time it is accessed. The
724 * function receives, as its only argument, the key being created.
725 */
726
727
728 class DefaultWeakMap extends WeakMap {
729 constructor(createItem, items = undefined) {
730 super(items);
731 this.createItem = createItem;
732 }
733
734 get(key) {
735 if (!this.has(key)) {
736 this.set(key, this.createItem(key));
737 }
738
739 return super.get(key);
740 }
741
742 }
743 /**
744 * Returns true if the given object is an object with a `then` method, and can
745 * therefore be assumed to behave as a Promise.
746 *
747 * @param {*} value The value to test.
748 * @returns {boolean} True if the value is thenable.
749 */
750
751
752 const isThenable = value => {
753 return value && typeof value === "object" && typeof value.then === "function";
754 };
755 /**
756 * Creates and returns a function which, when called, will resolve or reject
757 * the given promise based on how it is called:
758 *
759 * - If, when called, `chrome.runtime.lastError` contains a non-null object,
760 * the promise is rejected with that value.
761 * - If the function is called with exactly one argument, the promise is
762 * resolved to that value.
763 * - Otherwise, the promise is resolved to an array containing all of the
764 * function's arguments.
765 *
766 * @param {object} promise
767 * An object containing the resolution and rejection functions of a
768 * promise.
769 * @param {function} promise.resolve
770 * The promise's resolution function.
771 * @param {function} promise.reject
772 * The promise's rejection function.
773 * @param {object} metadata
774 * Metadata about the wrapped method which has created the callback.
775 * @param {boolean} metadata.singleCallbackArg
776 * Whether or not the promise is resolved with only the first
777 * argument of the callback, alternatively an array of all the
778 * callback arguments is resolved. By default, if the callback
779 * function is invoked with only a single argument, that will be
780 * resolved to the promise, while all arguments will be resolved as
781 * an array if multiple are given.
782 *
783 * @returns {function}
784 * The generated callback function.
785 */
786
787
788 const makeCallback = (promise, metadata) => {
789 return (...callbackArgs) => {
790 if (extensionAPIs.runtime.lastError) {
791 promise.reject(new Error(extensionAPIs.runtime.lastError.message));
792 } else if (metadata.singleCallbackArg || callbackArgs.length <= 1 && metadata.singleCallbackArg !== false) {
793 promise.resolve(callbackArgs[0]);
794 } else {
795 promise.resolve(callbackArgs);
796 }
797 };
798 };
799
800 const pluralizeArguments = numArgs => numArgs == 1 ? "argument" : "arguments";
801 /**
802 * Creates a wrapper function for a method with the given name and metadata.
803 *
804 * @param {string} name
805 * The name of the method which is being wrapped.
806 * @param {object} metadata
807 * Metadata about the method being wrapped.
808 * @param {integer} metadata.minArgs
809 * The minimum number of arguments which must be passed to the
810 * function. If called with fewer than this number of arguments, the
811 * wrapper will raise an exception.
812 * @param {integer} metadata.maxArgs
813 * The maximum number of arguments which may be passed to the
814 * function. If called with more than this number of arguments, the
815 * wrapper will raise an exception.
816 * @param {boolean} metadata.singleCallbackArg
817 * Whether or not the promise is resolved with only the first
818 * argument of the callback, alternatively an array of all the
819 * callback arguments is resolved. By default, if the callback
820 * function is invoked with only a single argument, that will be
821 * resolved to the promise, while all arguments will be resolved as
822 * an array if multiple are given.
823 *
824 * @returns {function(object, ...*)}
825 * The generated wrapper function.
826 */
827
828
829 const wrapAsyncFunction = (name, metadata) => {
830 return function asyncFunctionWrapper(target, ...args) {
831 if (args.length < metadata.minArgs) {
832 throw new Error(`Expected at least ${metadata.minArgs} ${pluralizeArguments(metadata.minArgs)} for ${name}(), got ${args.length}`);
833 }
834
835 if (args.length > metadata.maxArgs) {
836 throw new Error(`Expected at most ${metadata.maxArgs} ${pluralizeArguments(metadata.maxArgs)} for ${name}(), got ${args.length}`);
837 }
838
839 return new Promise((resolve, reject) => {
840 if (metadata.fallbackToNoCallback) {
841 // This API method has currently no callback on Chrome, but it return a promise on Firefox,
842 // and so the polyfill will try to call it with a callback first, and it will fallback
843 // to not passing the callback if the first call fails.
844 try {
845 target[name](...args, makeCallback({
846 resolve,
847 reject
848 }, metadata));
849 } catch (cbError) {
850 console.warn(`${name} API method doesn't seem to support the callback parameter, ` + "falling back to call it without a callback: ", cbError);
851 target[name](...args); // Update the API method metadata, so that the next API calls will not try to
852 // use the unsupported callback anymore.
853
854 metadata.fallbackToNoCallback = false;
855 metadata.noCallback = true;
856 resolve();
857 }
858 } else if (metadata.noCallback) {
859 target[name](...args);
860 resolve();
861 } else {
862 target[name](...args, makeCallback({
863 resolve,
864 reject
865 }, metadata));
866 }
867 });
868 };
869 };
870 /**
871 * Wraps an existing method of the target object, so that calls to it are
872 * intercepted by the given wrapper function. The wrapper function receives,
873 * as its first argument, the original `target` object, followed by each of
874 * the arguments passed to the original method.
875 *
876 * @param {object} target
877 * The original target object that the wrapped method belongs to.
878 * @param {function} method
879 * The method being wrapped. This is used as the target of the Proxy
880 * object which is created to wrap the method.
881 * @param {function} wrapper
882 * The wrapper function which is called in place of a direct invocation
883 * of the wrapped method.
884 *
885 * @returns {Proxy<function>}
886 * A Proxy object for the given method, which invokes the given wrapper
887 * method in its place.
888 */
889
890
891 const wrapMethod = (target, method, wrapper) => {
892 return new Proxy(method, {
893 apply(targetMethod, thisObj, args) {
894 return wrapper.call(thisObj, target, ...args);
895 }
896
897 });
898 };
899
900 let hasOwnProperty = Function.call.bind(Object.prototype.hasOwnProperty);
901 /**
902 * Wraps an object in a Proxy which intercepts and wraps certain methods
903 * based on the given `wrappers` and `metadata` objects.
904 *
905 * @param {object} target
906 * The target object to wrap.
907 *
908 * @param {object} [wrappers = {}]
909 * An object tree containing wrapper functions for special cases. Any
910 * function present in this object tree is called in place of the
911 * method in the same location in the `target` object tree. These
912 * wrapper methods are invoked as described in {@see wrapMethod}.
913 *
914 * @param {object} [metadata = {}]
915 * An object tree containing metadata used to automatically generate
916 * Promise-based wrapper functions for asynchronous. Any function in
917 * the `target` object tree which has a corresponding metadata object
918 * in the same location in the `metadata` tree is replaced with an
919 * automatically-generated wrapper function, as described in
920 * {@see wrapAsyncFunction}
921 *
922 * @returns {Proxy<object>}
923 */
924
925 const wrapObject = (target, wrappers = {}, metadata = {}) => {
926 let cache = Object.create(null);
927 let handlers = {
928 has(proxyTarget, prop) {
929 return prop in target || prop in cache;
930 },
931
932 get(proxyTarget, prop, receiver) {
933 if (prop in cache) {
934 return cache[prop];
935 }
936
937 if (!(prop in target)) {
938 return undefined;
939 }
940
941 let value = target[prop];
942
943 if (typeof value === "function") {
944 // This is a method on the underlying object. Check if we need to do
945 // any wrapping.
946 if (typeof wrappers[prop] === "function") {
947 // We have a special-case wrapper for this method.
948 value = wrapMethod(target, target[prop], wrappers[prop]);
949 } else if (hasOwnProperty(metadata, prop)) {
950 // This is an async method that we have metadata for. Create a
951 // Promise wrapper for it.
952 let wrapper = wrapAsyncFunction(prop, metadata[prop]);
953 value = wrapMethod(target, target[prop], wrapper);
954 } else {
955 // This is a method that we don't know or care about. Return the
956 // original method, bound to the underlying object.
957 value = value.bind(target);
958 }
959 } else if (typeof value === "object" && value !== null && (hasOwnProperty(wrappers, prop) || hasOwnProperty(metadata, prop))) {
960 // This is an object that we need to do some wrapping for the children
961 // of. Create a sub-object wrapper for it with the appropriate child
962 // metadata.
963 value = wrapObject(value, wrappers[prop], metadata[prop]);
964 } else if (hasOwnProperty(metadata, "*")) {
965 // Wrap all properties in * namespace.
966 value = wrapObject(value, wrappers[prop], metadata["*"]);
967 } else {
968 // We don't need to do any wrapping for this property,
969 // so just forward all access to the underlying object.
970 Object.defineProperty(cache, prop, {
971 configurable: true,
972 enumerable: true,
973
974 get() {
975 return target[prop];
976 },
977
978 set(value) {
979 target[prop] = value;
980 }
981
982 });
983 return value;
984 }
985
986 cache[prop] = value;
987 return value;
988 },
989
990 set(proxyTarget, prop, value, receiver) {
991 if (prop in cache) {
992 cache[prop] = value;
993 } else {
994 target[prop] = value;
995 }
996
997 return true;
998 },
999
1000 defineProperty(proxyTarget, prop, desc) {
1001 return Reflect.defineProperty(cache, prop, desc);
1002 },
1003
1004 deleteProperty(proxyTarget, prop) {
1005 return Reflect.deleteProperty(cache, prop);
1006 }
1007
1008 }; // Per contract of the Proxy API, the "get" proxy handler must return the
1009 // original value of the target if that value is declared read-only and
1010 // non-configurable. For this reason, we create an object with the
1011 // prototype set to `target` instead of using `target` directly.
1012 // Otherwise we cannot return a custom object for APIs that
1013 // are declared read-only and non-configurable, such as `chrome.devtools`.
1014 //
1015 // The proxy handlers themselves will still use the original `target`
1016 // instead of the `proxyTarget`, so that the methods and properties are
1017 // dereferenced via the original targets.
1018
1019 let proxyTarget = Object.create(target);
1020 return new Proxy(proxyTarget, handlers);
1021 };
1022 /**
1023 * Creates a set of wrapper functions for an event object, which handles
1024 * wrapping of listener functions that those messages are passed.
1025 *
1026 * A single wrapper is created for each listener function, and stored in a
1027 * map. Subsequent calls to `addListener`, `hasListener`, or `removeListener`
1028 * retrieve the original wrapper, so that attempts to remove a
1029 * previously-added listener work as expected.
1030 *
1031 * @param {DefaultWeakMap<function, function>} wrapperMap
1032 * A DefaultWeakMap object which will create the appropriate wrapper
1033 * for a given listener function when one does not exist, and retrieve
1034 * an existing one when it does.
1035 *
1036 * @returns {object}
1037 */
1038
1039
1040 const wrapEvent = wrapperMap => ({
1041 addListener(target, listener, ...args) {
1042 target.addListener(wrapperMap.get(listener), ...args);
1043 },
1044
1045 hasListener(target, listener) {
1046 return target.hasListener(wrapperMap.get(listener));
1047 },
1048
1049 removeListener(target, listener) {
1050 target.removeListener(wrapperMap.get(listener));
1051 }
1052
1053 });
1054
1055 const onRequestFinishedWrappers = new DefaultWeakMap(listener => {
1056 if (typeof listener !== "function") {
1057 return listener;
1058 }
1059 /**
1060 * Wraps an onRequestFinished listener function so that it will return a
1061 * `getContent()` property which returns a `Promise` rather than using a
1062 * callback API.
1063 *
1064 * @param {object} req
1065 * The HAR entry object representing the network request.
1066 */
1067
1068
1069 return function onRequestFinished(req) {
1070 const wrappedReq = wrapObject(req, {}
1071 /* wrappers */
1072 , {
1073 getContent: {
1074 minArgs: 0,
1075 maxArgs: 0
1076 }
1077 });
1078 listener(wrappedReq);
1079 };
1080 });
1081 const onMessageWrappers = new DefaultWeakMap(listener => {
1082 if (typeof listener !== "function") {
1083 return listener;
1084 }
1085 /**
1086 * Wraps a message listener function so that it may send responses based on
1087 * its return value, rather than by returning a sentinel value and calling a
1088 * callback. If the listener function returns a Promise, the response is
1089 * sent when the promise either resolves or rejects.
1090 *
1091 * @param {*} message
1092 * The message sent by the other end of the channel.
1093 * @param {object} sender
1094 * Details about the sender of the message.
1095 * @param {function(*)} sendResponse
1096 * A callback which, when called with an arbitrary argument, sends
1097 * that value as a response.
1098 * @returns {boolean}
1099 * True if the wrapped listener returned a Promise, which will later
1100 * yield a response. False otherwise.
1101 */
1102
1103
1104 return function onMessage(message, sender, sendResponse) {
1105 let didCallSendResponse = false;
1106 let wrappedSendResponse;
1107 let sendResponsePromise = new Promise(resolve => {
1108 wrappedSendResponse = function (response) {
1109 didCallSendResponse = true;
1110 resolve(response);
1111 };
1112 });
1113 let result;
1114
1115 try {
1116 result = listener(message, sender, wrappedSendResponse);
1117 } catch (err) {
1118 result = Promise.reject(err);
1119 }
1120
1121 const isResultThenable = result !== true && isThenable(result); // If the listener didn't returned true or a Promise, or called
1122 // wrappedSendResponse synchronously, we can exit earlier
1123 // because there will be no response sent from this listener.
1124
1125 if (result !== true && !isResultThenable && !didCallSendResponse) {
1126 return false;
1127 } // A small helper to send the message if the promise resolves
1128 // and an error if the promise rejects (a wrapped sendMessage has
1129 // to translate the message into a resolved promise or a rejected
1130 // promise).
1131
1132
1133 const sendPromisedResult = promise => {
1134 promise.then(msg => {
1135 // send the message value.
1136 sendResponse(msg);
1137 }, error => {
1138 // Send a JSON representation of the error if the rejected value
1139 // is an instance of error, or the object itself otherwise.
1140 let message;
1141
1142 if (error && (error instanceof Error || typeof error.message === "string")) {
1143 message = error.message;
1144 } else {
1145 message = "An unexpected error occurred";
1146 }
1147
1148 sendResponse({
1149 __mozWebExtensionPolyfillReject__: true,
1150 message
1151 });
1152 }).catch(err => {
1153 // Print an error on the console if unable to send the response.
1154 console.error("Failed to send onMessage rejected reply", err);
1155 });
1156 }; // If the listener returned a Promise, send the resolved value as a
1157 // result, otherwise wait the promise related to the wrappedSendResponse
1158 // callback to resolve and send it as a response.
1159
1160
1161 if (isResultThenable) {
1162 sendPromisedResult(result);
1163 } else {
1164 sendPromisedResult(sendResponsePromise);
1165 } // Let Chrome know that the listener is replying.
1166
1167
1168 return true;
1169 };
1170 });
1171
1172 const wrappedSendMessageCallback = ({
1173 reject,
1174 resolve
1175 }, reply) => {
1176 if (extensionAPIs.runtime.lastError) {
1177 // Detect when none of the listeners replied to the sendMessage call and resolve
1178 // the promise to undefined as in Firefox.
1179 // See https://github.com/mozilla/webextension-polyfill/issues/130
1180 if (extensionAPIs.runtime.lastError.message === CHROME_SEND_MESSAGE_CALLBACK_NO_RESPONSE_MESSAGE) {
1181 resolve();
1182 } else {
1183 reject(new Error(extensionAPIs.runtime.lastError.message));
1184 }
1185 } else if (reply && reply.__mozWebExtensionPolyfillReject__) {
1186 // Convert back the JSON representation of the error into
1187 // an Error instance.
1188 reject(new Error(reply.message));
1189 } else {
1190 resolve(reply);
1191 }
1192 };
1193
1194 const wrappedSendMessage = (name, metadata, apiNamespaceObj, ...args) => {
1195 if (args.length < metadata.minArgs) {
1196 throw new Error(`Expected at least ${metadata.minArgs} ${pluralizeArguments(metadata.minArgs)} for ${name}(), got ${args.length}`);
1197 }
1198
1199 if (args.length > metadata.maxArgs) {
1200 throw new Error(`Expected at most ${metadata.maxArgs} ${pluralizeArguments(metadata.maxArgs)} for ${name}(), got ${args.length}`);
1201 }
1202
1203 return new Promise((resolve, reject) => {
1204 const wrappedCb = wrappedSendMessageCallback.bind(null, {
1205 resolve,
1206 reject
1207 });
1208 args.push(wrappedCb);
1209 apiNamespaceObj.sendMessage(...args);
1210 });
1211 };
1212
1213 const staticWrappers = {
1214 devtools: {
1215 network: {
1216 onRequestFinished: wrapEvent(onRequestFinishedWrappers)
1217 }
1218 },
1219 runtime: {
1220 onMessage: wrapEvent(onMessageWrappers),
1221 onMessageExternal: wrapEvent(onMessageWrappers),
1222 sendMessage: wrappedSendMessage.bind(null, "sendMessage", {
1223 minArgs: 1,
1224 maxArgs: 3
1225 })
1226 },
1227 tabs: {
1228 sendMessage: wrappedSendMessage.bind(null, "sendMessage", {
1229 minArgs: 2,
1230 maxArgs: 3
1231 })
1232 }
1233 };
1234 const settingMetadata = {
1235 clear: {
1236 minArgs: 1,
1237 maxArgs: 1
1238 },
1239 get: {
1240 minArgs: 1,
1241 maxArgs: 1
1242 },
1243 set: {
1244 minArgs: 1,
1245 maxArgs: 1
1246 }
1247 };
1248 apiMetadata.privacy = {
1249 network: {
1250 "*": settingMetadata
1251 },
1252 services: {
1253 "*": settingMetadata
1254 },
1255 websites: {
1256 "*": settingMetadata
1257 }
1258 };
1259 return wrapObject(extensionAPIs, staticWrappers, apiMetadata);
1260 }; // The build process adds a UMD wrapper around this file, which makes the
1261 // `module` variable available.
1262
1263
1264 module.exports = wrapAPIs(chrome);
1265 } else {
1266 module.exports = globalThis.browser;
1267 }
1268});
1269//# sourceMappingURL=browser-polyfill.js.map