UNPKG

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