UNPKG

26.5 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.2.1 - Thu Oct 12 2017 12:31:04 */
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") {
23 // Wrapping the bulk of this polyfill in a one-time-use function is a minor
24 // optimization for Firefox. Since Spidermonkey does not fully parse the
25 // contents of a function until the first time it's called, and since it will
26 // never actually need to be called, this allows the polyfill to be included
27 // in Firefox nearly for free.
28 const wrapAPIs = () => {
29 // NOTE: apiMetadata is associated to the content of the api-metadata.json file
30 // at build time by replacing the following "include" with the content of the
31 // JSON file.
32 const apiMetadata = {
33 "alarms": {
34 "clear": {
35 "minArgs": 0,
36 "maxArgs": 1
37 },
38 "clearAll": {
39 "minArgs": 0,
40 "maxArgs": 0
41 },
42 "get": {
43 "minArgs": 0,
44 "maxArgs": 1
45 },
46 "getAll": {
47 "minArgs": 0,
48 "maxArgs": 0
49 }
50 },
51 "bookmarks": {
52 "create": {
53 "minArgs": 1,
54 "maxArgs": 1
55 },
56 "export": {
57 "minArgs": 0,
58 "maxArgs": 0
59 },
60 "get": {
61 "minArgs": 1,
62 "maxArgs": 1
63 },
64 "getChildren": {
65 "minArgs": 1,
66 "maxArgs": 1
67 },
68 "getRecent": {
69 "minArgs": 1,
70 "maxArgs": 1
71 },
72 "getTree": {
73 "minArgs": 0,
74 "maxArgs": 0
75 },
76 "getSubTree": {
77 "minArgs": 1,
78 "maxArgs": 1
79 },
80 "import": {
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 "getBadgeBackgroundColor": {
107 "minArgs": 1,
108 "maxArgs": 1
109 },
110 "getBadgeText": {
111 "minArgs": 1,
112 "maxArgs": 1
113 },
114 "getPopup": {
115 "minArgs": 1,
116 "maxArgs": 1
117 },
118 "getTitle": {
119 "minArgs": 1,
120 "maxArgs": 1
121 },
122 "setIcon": {
123 "minArgs": 1,
124 "maxArgs": 1
125 }
126 },
127 "commands": {
128 "getAll": {
129 "minArgs": 0,
130 "maxArgs": 0
131 }
132 },
133 "contextMenus": {
134 "update": {
135 "minArgs": 2,
136 "maxArgs": 2
137 },
138 "remove": {
139 "minArgs": 1,
140 "maxArgs": 1
141 },
142 "removeAll": {
143 "minArgs": 0,
144 "maxArgs": 0
145 }
146 },
147 "cookies": {
148 "get": {
149 "minArgs": 1,
150 "maxArgs": 1
151 },
152 "getAll": {
153 "minArgs": 1,
154 "maxArgs": 1
155 },
156 "getAllCookieStores": {
157 "minArgs": 0,
158 "maxArgs": 0
159 },
160 "remove": {
161 "minArgs": 1,
162 "maxArgs": 1
163 },
164 "set": {
165 "minArgs": 1,
166 "maxArgs": 1
167 }
168 },
169 "devtools": {
170 "inspectedWindow": {
171 "eval": {
172 "minArgs": 1,
173 "maxArgs": 2
174 }
175 },
176 "panels": {
177 "create": {
178 "minArgs": 3,
179 "maxArgs": 3,
180 "singleCallbackArg": true
181 }
182 }
183 },
184 "downloads": {
185 "download": {
186 "minArgs": 1,
187 "maxArgs": 1
188 },
189 "cancel": {
190 "minArgs": 1,
191 "maxArgs": 1
192 },
193 "erase": {
194 "minArgs": 1,
195 "maxArgs": 1
196 },
197 "getFileIcon": {
198 "minArgs": 1,
199 "maxArgs": 2
200 },
201 "open": {
202 "minArgs": 1,
203 "maxArgs": 1
204 },
205 "pause": {
206 "minArgs": 1,
207 "maxArgs": 1
208 },
209 "removeFile": {
210 "minArgs": 1,
211 "maxArgs": 1
212 },
213 "resume": {
214 "minArgs": 1,
215 "maxArgs": 1
216 },
217 "search": {
218 "minArgs": 1,
219 "maxArgs": 1
220 },
221 "show": {
222 "minArgs": 1,
223 "maxArgs": 1
224 }
225 },
226 "extension": {
227 "isAllowedFileSchemeAccess": {
228 "minArgs": 0,
229 "maxArgs": 0
230 },
231 "isAllowedIncognitoAccess": {
232 "minArgs": 0,
233 "maxArgs": 0
234 }
235 },
236 "history": {
237 "addUrl": {
238 "minArgs": 1,
239 "maxArgs": 1
240 },
241 "getVisits": {
242 "minArgs": 1,
243 "maxArgs": 1
244 },
245 "deleteAll": {
246 "minArgs": 0,
247 "maxArgs": 0
248 },
249 "deleteRange": {
250 "minArgs": 1,
251 "maxArgs": 1
252 },
253 "deleteUrl": {
254 "minArgs": 1,
255 "maxArgs": 1
256 },
257 "search": {
258 "minArgs": 1,
259 "maxArgs": 1
260 }
261 },
262 "i18n": {
263 "detectLanguage": {
264 "minArgs": 1,
265 "maxArgs": 1
266 },
267 "getAcceptLanguages": {
268 "minArgs": 0,
269 "maxArgs": 0
270 }
271 },
272 "idle": {
273 "queryState": {
274 "minArgs": 1,
275 "maxArgs": 1
276 }
277 },
278 "management": {
279 "get": {
280 "minArgs": 1,
281 "maxArgs": 1
282 },
283 "getAll": {
284 "minArgs": 0,
285 "maxArgs": 0
286 },
287 "getSelf": {
288 "minArgs": 0,
289 "maxArgs": 0
290 },
291 "uninstallSelf": {
292 "minArgs": 0,
293 "maxArgs": 1
294 }
295 },
296 "notifications": {
297 "clear": {
298 "minArgs": 1,
299 "maxArgs": 1
300 },
301 "create": {
302 "minArgs": 1,
303 "maxArgs": 2
304 },
305 "getAll": {
306 "minArgs": 0,
307 "maxArgs": 0
308 },
309 "getPermissionLevel": {
310 "minArgs": 0,
311 "maxArgs": 0
312 },
313 "update": {
314 "minArgs": 2,
315 "maxArgs": 2
316 }
317 },
318 "pageAction": {
319 "getPopup": {
320 "minArgs": 1,
321 "maxArgs": 1
322 },
323 "getTitle": {
324 "minArgs": 1,
325 "maxArgs": 1
326 },
327 "hide": {
328 "minArgs": 0,
329 "maxArgs": 0
330 },
331 "setIcon": {
332 "minArgs": 1,
333 "maxArgs": 1
334 },
335 "show": {
336 "minArgs": 0,
337 "maxArgs": 0
338 }
339 },
340 "runtime": {
341 "getBackgroundPage": {
342 "minArgs": 0,
343 "maxArgs": 0
344 },
345 "getBrowserInfo": {
346 "minArgs": 0,
347 "maxArgs": 0
348 },
349 "getPlatformInfo": {
350 "minArgs": 0,
351 "maxArgs": 0
352 },
353 "openOptionsPage": {
354 "minArgs": 0,
355 "maxArgs": 0
356 },
357 "requestUpdateCheck": {
358 "minArgs": 0,
359 "maxArgs": 0
360 },
361 "sendMessage": {
362 "minArgs": 1,
363 "maxArgs": 3
364 },
365 "sendNativeMessage": {
366 "minArgs": 2,
367 "maxArgs": 2
368 },
369 "setUninstallURL": {
370 "minArgs": 1,
371 "maxArgs": 1
372 }
373 },
374 "storage": {
375 "local": {
376 "clear": {
377 "minArgs": 0,
378 "maxArgs": 0
379 },
380 "get": {
381 "minArgs": 0,
382 "maxArgs": 1
383 },
384 "getBytesInUse": {
385 "minArgs": 0,
386 "maxArgs": 1
387 },
388 "remove": {
389 "minArgs": 1,
390 "maxArgs": 1
391 },
392 "set": {
393 "minArgs": 1,
394 "maxArgs": 1
395 }
396 },
397 "managed": {
398 "get": {
399 "minArgs": 0,
400 "maxArgs": 1
401 },
402 "getBytesInUse": {
403 "minArgs": 0,
404 "maxArgs": 1
405 }
406 },
407 "sync": {
408 "clear": {
409 "minArgs": 0,
410 "maxArgs": 0
411 },
412 "get": {
413 "minArgs": 0,
414 "maxArgs": 1
415 },
416 "getBytesInUse": {
417 "minArgs": 0,
418 "maxArgs": 1
419 },
420 "remove": {
421 "minArgs": 1,
422 "maxArgs": 1
423 },
424 "set": {
425 "minArgs": 1,
426 "maxArgs": 1
427 }
428 }
429 },
430 "tabs": {
431 "create": {
432 "minArgs": 1,
433 "maxArgs": 1
434 },
435 "captureVisibleTab": {
436 "minArgs": 0,
437 "maxArgs": 2
438 },
439 "detectLanguage": {
440 "minArgs": 0,
441 "maxArgs": 1
442 },
443 "duplicate": {
444 "minArgs": 1,
445 "maxArgs": 1
446 },
447 "executeScript": {
448 "minArgs": 1,
449 "maxArgs": 2
450 },
451 "get": {
452 "minArgs": 1,
453 "maxArgs": 1
454 },
455 "getCurrent": {
456 "minArgs": 0,
457 "maxArgs": 0
458 },
459 "getZoom": {
460 "minArgs": 0,
461 "maxArgs": 1
462 },
463 "getZoomSettings": {
464 "minArgs": 0,
465 "maxArgs": 1
466 },
467 "highlight": {
468 "minArgs": 1,
469 "maxArgs": 1
470 },
471 "insertCSS": {
472 "minArgs": 1,
473 "maxArgs": 2
474 },
475 "move": {
476 "minArgs": 2,
477 "maxArgs": 2
478 },
479 "reload": {
480 "minArgs": 0,
481 "maxArgs": 2
482 },
483 "remove": {
484 "minArgs": 1,
485 "maxArgs": 1
486 },
487 "query": {
488 "minArgs": 1,
489 "maxArgs": 1
490 },
491 "removeCSS": {
492 "minArgs": 1,
493 "maxArgs": 2
494 },
495 "sendMessage": {
496 "minArgs": 2,
497 "maxArgs": 3
498 },
499 "setZoom": {
500 "minArgs": 1,
501 "maxArgs": 2
502 },
503 "setZoomSettings": {
504 "minArgs": 1,
505 "maxArgs": 2
506 },
507 "update": {
508 "minArgs": 1,
509 "maxArgs": 2
510 }
511 },
512 "webNavigation": {
513 "getAllFrames": {
514 "minArgs": 1,
515 "maxArgs": 1
516 },
517 "getFrame": {
518 "minArgs": 1,
519 "maxArgs": 1
520 }
521 },
522 "webRequest": {
523 "handlerBehaviorChanged": {
524 "minArgs": 0,
525 "maxArgs": 0
526 }
527 },
528 "windows": {
529 "create": {
530 "minArgs": 0,
531 "maxArgs": 1
532 },
533 "get": {
534 "minArgs": 1,
535 "maxArgs": 2
536 },
537 "getAll": {
538 "minArgs": 0,
539 "maxArgs": 1
540 },
541 "getCurrent": {
542 "minArgs": 0,
543 "maxArgs": 1
544 },
545 "getLastFocused": {
546 "minArgs": 0,
547 "maxArgs": 1
548 },
549 "remove": {
550 "minArgs": 1,
551 "maxArgs": 1
552 },
553 "update": {
554 "minArgs": 2,
555 "maxArgs": 2
556 }
557 }
558 };
559
560 if (Object.keys(apiMetadata).length === 0) {
561 throw new Error("api-metadata.json has not been included in browser-polyfill");
562 }
563
564 /**
565 * A WeakMap subclass which creates and stores a value for any key which does
566 * not exist when accessed, but behaves exactly as an ordinary WeakMap
567 * otherwise.
568 *
569 * @param {function} createItem
570 * A function which will be called in order to create the value for any
571 * key which does not exist, the first time it is accessed. The
572 * function receives, as its only argument, the key being created.
573 */
574 class DefaultWeakMap extends WeakMap {
575 constructor(createItem, items = undefined) {
576 super(items);
577 this.createItem = createItem;
578 }
579
580 get(key) {
581 if (!this.has(key)) {
582 this.set(key, this.createItem(key));
583 }
584
585 return super.get(key);
586 }
587 }
588
589 /**
590 * Returns true if the given object is an object with a `then` method, and can
591 * therefore be assumed to behave as a Promise.
592 *
593 * @param {*} value The value to test.
594 * @returns {boolean} True if the value is thenable.
595 */
596 const isThenable = value => {
597 return value && typeof value === "object" && typeof value.then === "function";
598 };
599
600 /**
601 * Creates and returns a function which, when called, will resolve or reject
602 * the given promise based on how it is called:
603 *
604 * - If, when called, `chrome.runtime.lastError` contains a non-null object,
605 * the promise is rejected with that value.
606 * - If the function is called with exactly one argument, the promise is
607 * resolved to that value.
608 * - Otherwise, the promise is resolved to an array containing all of the
609 * function's arguments.
610 *
611 * @param {object} promise
612 * An object containing the resolution and rejection functions of a
613 * promise.
614 * @param {function} promise.resolve
615 * The promise's resolution function.
616 * @param {function} promise.rejection
617 * The promise's rejection function.
618 * @param {object} metadata
619 * Metadata about the wrapped method which has created the callback.
620 * @param {integer} metadata.maxResolvedArgs
621 * The maximum number of arguments which may be passed to the
622 * callback created by the wrapped async function.
623 *
624 * @returns {function}
625 * The generated callback function.
626 */
627 const makeCallback = (promise, metadata) => {
628 return (...callbackArgs) => {
629 if (chrome.runtime.lastError) {
630 promise.reject(chrome.runtime.lastError);
631 } else if (metadata.singleCallbackArg || callbackArgs.length === 1) {
632 promise.resolve(callbackArgs[0]);
633 } else {
634 promise.resolve(callbackArgs);
635 }
636 };
637 };
638
639 /**
640 * Creates a wrapper function for a method with the given name and metadata.
641 *
642 * @param {string} name
643 * The name of the method which is being wrapped.
644 * @param {object} metadata
645 * Metadata about the method being wrapped.
646 * @param {integer} metadata.minArgs
647 * The minimum number of arguments which must be passed to the
648 * function. If called with fewer than this number of arguments, the
649 * wrapper will raise an exception.
650 * @param {integer} metadata.maxArgs
651 * The maximum number of arguments which may be passed to the
652 * function. If called with more than this number of arguments, the
653 * wrapper will raise an exception.
654 * @param {integer} metadata.maxResolvedArgs
655 * The maximum number of arguments which may be passed to the
656 * callback created by the wrapped async function.
657 *
658 * @returns {function(object, ...*)}
659 * The generated wrapper function.
660 */
661 const wrapAsyncFunction = (name, metadata) => {
662 const pluralizeArguments = numArgs => numArgs == 1 ? "argument" : "arguments";
663
664 return function asyncFunctionWrapper(target, ...args) {
665 if (args.length < metadata.minArgs) {
666 throw new Error(`Expected at least ${metadata.minArgs} ${pluralizeArguments(metadata.minArgs)} for ${name}(), got ${args.length}`);
667 }
668
669 if (args.length > metadata.maxArgs) {
670 throw new Error(`Expected at most ${metadata.maxArgs} ${pluralizeArguments(metadata.maxArgs)} for ${name}(), got ${args.length}`);
671 }
672
673 return new Promise((resolve, reject) => {
674 target[name](...args, makeCallback({ resolve, reject }, metadata));
675 });
676 };
677 };
678
679 /**
680 * Wraps an existing method of the target object, so that calls to it are
681 * intercepted by the given wrapper function. The wrapper function receives,
682 * as its first argument, the original `target` object, followed by each of
683 * the arguments passed to the orginal method.
684 *
685 * @param {object} target
686 * The original target object that the wrapped method belongs to.
687 * @param {function} method
688 * The method being wrapped. This is used as the target of the Proxy
689 * object which is created to wrap the method.
690 * @param {function} wrapper
691 * The wrapper function which is called in place of a direct invocation
692 * of the wrapped method.
693 *
694 * @returns {Proxy<function>}
695 * A Proxy object for the given method, which invokes the given wrapper
696 * method in its place.
697 */
698 const wrapMethod = (target, method, wrapper) => {
699 return new Proxy(method, {
700 apply(targetMethod, thisObj, args) {
701 return wrapper.call(thisObj, target, ...args);
702 }
703 });
704 };
705
706 let hasOwnProperty = Function.call.bind(Object.prototype.hasOwnProperty);
707
708 /**
709 * Wraps an object in a Proxy which intercepts and wraps certain methods
710 * based on the given `wrappers` and `metadata` objects.
711 *
712 * @param {object} target
713 * The target object to wrap.
714 *
715 * @param {object} [wrappers = {}]
716 * An object tree containing wrapper functions for special cases. Any
717 * function present in this object tree is called in place of the
718 * method in the same location in the `target` object tree. These
719 * wrapper methods are invoked as described in {@see wrapMethod}.
720 *
721 * @param {object} [metadata = {}]
722 * An object tree containing metadata used to automatically generate
723 * Promise-based wrapper functions for asynchronous. Any function in
724 * the `target` object tree which has a corresponding metadata object
725 * in the same location in the `metadata` tree is replaced with an
726 * automatically-generated wrapper function, as described in
727 * {@see wrapAsyncFunction}
728 *
729 * @returns {Proxy<object>}
730 */
731 const wrapObject = (target, wrappers = {}, metadata = {}) => {
732 let cache = Object.create(null);
733
734 let handlers = {
735 has(target, prop) {
736 return prop in target || prop in cache;
737 },
738
739 get(target, prop, receiver) {
740 if (prop in cache) {
741 return cache[prop];
742 }
743
744 if (!(prop in target)) {
745 return undefined;
746 }
747
748 let value = target[prop];
749
750 if (typeof value === "function") {
751 // This is a method on the underlying object. Check if we need to do
752 // any wrapping.
753
754 if (typeof wrappers[prop] === "function") {
755 // We have a special-case wrapper for this method.
756 value = wrapMethod(target, target[prop], wrappers[prop]);
757 } else if (hasOwnProperty(metadata, prop)) {
758 // This is an async method that we have metadata for. Create a
759 // Promise wrapper for it.
760 let wrapper = wrapAsyncFunction(prop, metadata[prop]);
761 value = wrapMethod(target, target[prop], wrapper);
762 } else {
763 // This is a method that we don't know or care about. Return the
764 // original method, bound to the underlying object.
765 value = value.bind(target);
766 }
767 } else if (typeof value === "object" && value !== null && (hasOwnProperty(wrappers, prop) || hasOwnProperty(metadata, prop))) {
768 // This is an object that we need to do some wrapping for the children
769 // of. Create a sub-object wrapper for it with the appropriate child
770 // metadata.
771 value = wrapObject(value, wrappers[prop], metadata[prop]);
772 } else {
773 // We don't need to do any wrapping for this property,
774 // so just forward all access to the underlying object.
775 Object.defineProperty(cache, prop, {
776 configurable: true,
777 enumerable: true,
778 get() {
779 return target[prop];
780 },
781 set(value) {
782 target[prop] = value;
783 }
784 });
785
786 return value;
787 }
788
789 cache[prop] = value;
790 return value;
791 },
792
793 set(target, prop, value, receiver) {
794 if (prop in cache) {
795 cache[prop] = value;
796 } else {
797 target[prop] = value;
798 }
799 return true;
800 },
801
802 defineProperty(target, prop, desc) {
803 return Reflect.defineProperty(cache, prop, desc);
804 },
805
806 deleteProperty(target, prop) {
807 return Reflect.deleteProperty(cache, prop);
808 }
809 };
810
811 return new Proxy(target, handlers);
812 };
813
814 /**
815 * Creates a set of wrapper functions for an event object, which handles
816 * wrapping of listener functions that those messages are passed.
817 *
818 * A single wrapper is created for each listener function, and stored in a
819 * map. Subsequent calls to `addListener`, `hasListener`, or `removeListener`
820 * retrieve the original wrapper, so that attempts to remove a
821 * previously-added listener work as expected.
822 *
823 * @param {DefaultWeakMap<function, function>} wrapperMap
824 * A DefaultWeakMap object which will create the appropriate wrapper
825 * for a given listener function when one does not exist, and retrieve
826 * an existing one when it does.
827 *
828 * @returns {object}
829 */
830 const wrapEvent = wrapperMap => ({
831 addListener(target, listener, ...args) {
832 target.addListener(wrapperMap.get(listener), ...args);
833 },
834
835 hasListener(target, listener) {
836 return target.hasListener(wrapperMap.get(listener));
837 },
838
839 removeListener(target, listener) {
840 target.removeListener(wrapperMap.get(listener));
841 }
842 });
843
844 const onMessageWrappers = new DefaultWeakMap(listener => {
845 if (typeof listener !== "function") {
846 return listener;
847 }
848
849 /**
850 * Wraps a message listener function so that it may send responses based on
851 * its return value, rather than by returning a sentinel value and calling a
852 * callback. If the listener function returns a Promise, the response is
853 * sent when the promise either resolves or rejects.
854 *
855 * @param {*} message
856 * The message sent by the other end of the channel.
857 * @param {object} sender
858 * Details about the sender of the message.
859 * @param {function(*)} sendResponse
860 * A callback which, when called with an arbitrary argument, sends
861 * that value as a response.
862 * @returns {boolean}
863 * True if the wrapped listener returned a Promise, which will later
864 * yield a response. False otherwise.
865 */
866 return function onMessage(message, sender, sendResponse) {
867 let result = listener(message, sender);
868
869 if (isThenable(result)) {
870 result.then(sendResponse, error => {
871 console.error(error);
872 sendResponse(error);
873 });
874
875 return true;
876 } else if (result !== undefined) {
877 sendResponse(result);
878 }
879 };
880 });
881
882 const staticWrappers = {
883 runtime: {
884 onMessage: wrapEvent(onMessageWrappers)
885 }
886 };
887
888 // Create a new empty object and copy the properties of the original chrome object
889 // to prevent a Proxy violation exception for the devtools API getter
890 // (which is a read-only non-configurable property on the original target).
891 const targetObject = Object.assign({}, chrome);
892
893 return wrapObject(targetObject, staticWrappers, apiMetadata);
894 };
895
896 // The build process adds a UMD wrapper around this file, which makes the
897 // `module` variable available.
898 module.exports = wrapAPIs(); // eslint-disable-line no-undef
899 } else {
900 module.exports = browser; // eslint-disable-line no-undef
901 }
902});
903//# sourceMappingURL=browser-polyfill.js.map