UNPKG

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