aboutsummaryrefslogtreecommitdiffstats
path: root/extension/vendor
diff options
context:
space:
mode:
authorOmar Rizwan <omar.rizwan@gmail.com>2020-10-23 12:00:25 -0700
committerOmar Rizwan <omar.rizwan@gmail.com>2020-10-23 12:00:25 -0700
commit44d5a7ed3c8435aab7851bb8382ec5210de358d7 (patch)
tree90544a8bc0c0a10669eef952fcc7422e91122fcc /extension/vendor
parent543dc7d4b2a5bc2c3166b512678f9bdd9a86beda (diff)
Switch to browser namespace so we can use Promises.
Diffstat (limited to 'extension/vendor')
-rw-r--r--extension/vendor/browser-polyfill.js1224
1 files changed, 1224 insertions, 0 deletions
diff --git a/extension/vendor/browser-polyfill.js b/extension/vendor/browser-polyfill.js
new file mode 100644
index 0000000..c0b5dfd
--- /dev/null
+++ b/extension/vendor/browser-polyfill.js
@@ -0,0 +1,1224 @@
+(function (global, factory) {
+ if (typeof define === "function" && define.amd) {
+ define("webextension-polyfill", ["module"], factory);
+ } else if (typeof exports !== "undefined") {
+ factory(module);
+ } else {
+ var mod = {
+ exports: {}
+ };
+ factory(mod);
+ global.browser = mod.exports;
+ }
+})(typeof globalThis !== "undefined" ? globalThis : typeof self !== "undefined" ? self : this, function (module) {
+ /* webextension-polyfill - v0.6.0 - Mon Dec 23 2019 12:32:53 */
+
+ /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+
+ /* vim: set sts=2 sw=2 et tw=80: */
+
+ /* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ "use strict";
+
+ if (typeof browser === "undefined" || Object.getPrototypeOf(browser) !== Object.prototype) {
+ const CHROME_SEND_MESSAGE_CALLBACK_NO_RESPONSE_MESSAGE = "The message port closed before a response was received.";
+ 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
+ // optimization for Firefox. Since Spidermonkey does not fully parse the
+ // contents of a function until the first time it's called, and since it will
+ // never actually need to be called, this allows the polyfill to be included
+ // in Firefox nearly for free.
+
+ const wrapAPIs = extensionAPIs => {
+ // NOTE: apiMetadata is associated to the content of the api-metadata.json file
+ // at build time by replacing the following "include" with the content of the
+ // JSON file.
+ const apiMetadata = {
+ "alarms": {
+ "clear": {
+ "minArgs": 0,
+ "maxArgs": 1
+ },
+ "clearAll": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "get": {
+ "minArgs": 0,
+ "maxArgs": 1
+ },
+ "getAll": {
+ "minArgs": 0,
+ "maxArgs": 0
+ }
+ },
+ "bookmarks": {
+ "create": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "get": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "getChildren": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "getRecent": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "getSubTree": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "getTree": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "move": {
+ "minArgs": 2,
+ "maxArgs": 2
+ },
+ "remove": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "removeTree": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "search": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "update": {
+ "minArgs": 2,
+ "maxArgs": 2
+ }
+ },
+ "browserAction": {
+ "disable": {
+ "minArgs": 0,
+ "maxArgs": 1,
+ "fallbackToNoCallback": true
+ },
+ "enable": {
+ "minArgs": 0,
+ "maxArgs": 1,
+ "fallbackToNoCallback": true
+ },
+ "getBadgeBackgroundColor": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "getBadgeText": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "getPopup": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "getTitle": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "openPopup": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "setBadgeBackgroundColor": {
+ "minArgs": 1,
+ "maxArgs": 1,
+ "fallbackToNoCallback": true
+ },
+ "setBadgeText": {
+ "minArgs": 1,
+ "maxArgs": 1,
+ "fallbackToNoCallback": true
+ },
+ "setIcon": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "setPopup": {
+ "minArgs": 1,
+ "maxArgs": 1,
+ "fallbackToNoCallback": true
+ },
+ "setTitle": {
+ "minArgs": 1,
+ "maxArgs": 1,
+ "fallbackToNoCallback": true
+ }
+ },
+ "browsingData": {
+ "remove": {
+ "minArgs": 2,
+ "maxArgs": 2
+ },
+ "removeCache": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "removeCookies": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "removeDownloads": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "removeFormData": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "removeHistory": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "removeLocalStorage": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "removePasswords": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "removePluginData": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "settings": {
+ "minArgs": 0,
+ "maxArgs": 0
+ }
+ },
+ "commands": {
+ "getAll": {
+ "minArgs": 0,
+ "maxArgs": 0
+ }
+ },
+ "contextMenus": {
+ "remove": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "removeAll": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "update": {
+ "minArgs": 2,
+ "maxArgs": 2
+ }
+ },
+ "cookies": {
+ "get": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "getAll": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "getAllCookieStores": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "remove": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "set": {
+ "minArgs": 1,
+ "maxArgs": 1
+ }
+ },
+ "devtools": {
+ "inspectedWindow": {
+ "eval": {
+ "minArgs": 1,
+ "maxArgs": 2,
+ "singleCallbackArg": false
+ }
+ },
+ "panels": {
+ "create": {
+ "minArgs": 3,
+ "maxArgs": 3,
+ "singleCallbackArg": true
+ }
+ }
+ },
+ "downloads": {
+ "cancel": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "download": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "erase": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "getFileIcon": {
+ "minArgs": 1,
+ "maxArgs": 2
+ },
+ "open": {
+ "minArgs": 1,
+ "maxArgs": 1,
+ "fallbackToNoCallback": true
+ },
+ "pause": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "removeFile": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "resume": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "search": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "show": {
+ "minArgs": 1,
+ "maxArgs": 1,
+ "fallbackToNoCallback": true
+ }
+ },
+ "extension": {
+ "isAllowedFileSchemeAccess": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "isAllowedIncognitoAccess": {
+ "minArgs": 0,
+ "maxArgs": 0
+ }
+ },
+ "history": {
+ "addUrl": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "deleteAll": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "deleteRange": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "deleteUrl": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "getVisits": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "search": {
+ "minArgs": 1,
+ "maxArgs": 1
+ }
+ },
+ "i18n": {
+ "detectLanguage": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "getAcceptLanguages": {
+ "minArgs": 0,
+ "maxArgs": 0
+ }
+ },
+ "identity": {
+ "launchWebAuthFlow": {
+ "minArgs": 1,
+ "maxArgs": 1
+ }
+ },
+ "idle": {
+ "queryState": {
+ "minArgs": 1,
+ "maxArgs": 1
+ }
+ },
+ "management": {
+ "get": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "getAll": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "getSelf": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "setEnabled": {
+ "minArgs": 2,
+ "maxArgs": 2
+ },
+ "uninstallSelf": {
+ "minArgs": 0,
+ "maxArgs": 1
+ }
+ },
+ "notifications": {
+ "clear": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "create": {
+ "minArgs": 1,
+ "maxArgs": 2
+ },
+ "getAll": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "getPermissionLevel": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "update": {
+ "minArgs": 2,
+ "maxArgs": 2
+ }
+ },
+ "pageAction": {
+ "getPopup": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "getTitle": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "hide": {
+ "minArgs": 1,
+ "maxArgs": 1,
+ "fallbackToNoCallback": true
+ },
+ "setIcon": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "setPopup": {
+ "minArgs": 1,
+ "maxArgs": 1,
+ "fallbackToNoCallback": true
+ },
+ "setTitle": {
+ "minArgs": 1,
+ "maxArgs": 1,
+ "fallbackToNoCallback": true
+ },
+ "show": {
+ "minArgs": 1,
+ "maxArgs": 1,
+ "fallbackToNoCallback": true
+ }
+ },
+ "permissions": {
+ "contains": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "getAll": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "remove": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "request": {
+ "minArgs": 1,
+ "maxArgs": 1
+ }
+ },
+ "runtime": {
+ "getBackgroundPage": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "getPlatformInfo": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "openOptionsPage": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "requestUpdateCheck": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "sendMessage": {
+ "minArgs": 1,
+ "maxArgs": 3
+ },
+ "sendNativeMessage": {
+ "minArgs": 2,
+ "maxArgs": 2
+ },
+ "setUninstallURL": {
+ "minArgs": 1,
+ "maxArgs": 1
+ }
+ },
+ "sessions": {
+ "getDevices": {
+ "minArgs": 0,
+ "maxArgs": 1
+ },
+ "getRecentlyClosed": {
+ "minArgs": 0,
+ "maxArgs": 1
+ },
+ "restore": {
+ "minArgs": 0,
+ "maxArgs": 1
+ }
+ },
+ "storage": {
+ "local": {
+ "clear": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "get": {
+ "minArgs": 0,
+ "maxArgs": 1
+ },
+ "getBytesInUse": {
+ "minArgs": 0,
+ "maxArgs": 1
+ },
+ "remove": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "set": {
+ "minArgs": 1,
+ "maxArgs": 1
+ }
+ },
+ "managed": {
+ "get": {
+ "minArgs": 0,
+ "maxArgs": 1
+ },
+ "getBytesInUse": {
+ "minArgs": 0,
+ "maxArgs": 1
+ }
+ },
+ "sync": {
+ "clear": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "get": {
+ "minArgs": 0,
+ "maxArgs": 1
+ },
+ "getBytesInUse": {
+ "minArgs": 0,
+ "maxArgs": 1
+ },
+ "remove": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "set": {
+ "minArgs": 1,
+ "maxArgs": 1
+ }
+ }
+ },
+ "tabs": {
+ "captureVisibleTab": {
+ "minArgs": 0,
+ "maxArgs": 2
+ },
+ "create": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "detectLanguage": {
+ "minArgs": 0,
+ "maxArgs": 1
+ },
+ "discard": {
+ "minArgs": 0,
+ "maxArgs": 1
+ },
+ "duplicate": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "executeScript": {
+ "minArgs": 1,
+ "maxArgs": 2
+ },
+ "get": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "getCurrent": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "getZoom": {
+ "minArgs": 0,
+ "maxArgs": 1
+ },
+ "getZoomSettings": {
+ "minArgs": 0,
+ "maxArgs": 1
+ },
+ "highlight": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "insertCSS": {
+ "minArgs": 1,
+ "maxArgs": 2
+ },
+ "move": {
+ "minArgs": 2,
+ "maxArgs": 2
+ },
+ "query": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "reload": {
+ "minArgs": 0,
+ "maxArgs": 2
+ },
+ "remove": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "removeCSS": {
+ "minArgs": 1,
+ "maxArgs": 2
+ },
+ "sendMessage": {
+ "minArgs": 2,
+ "maxArgs": 3
+ },
+ "setZoom": {
+ "minArgs": 1,
+ "maxArgs": 2
+ },
+ "setZoomSettings": {
+ "minArgs": 1,
+ "maxArgs": 2
+ },
+ "update": {
+ "minArgs": 1,
+ "maxArgs": 2
+ }
+ },
+ "topSites": {
+ "get": {
+ "minArgs": 0,
+ "maxArgs": 0
+ }
+ },
+ "webNavigation": {
+ "getAllFrames": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "getFrame": {
+ "minArgs": 1,
+ "maxArgs": 1
+ }
+ },
+ "webRequest": {
+ "handlerBehaviorChanged": {
+ "minArgs": 0,
+ "maxArgs": 0
+ }
+ },
+ "windows": {
+ "create": {
+ "minArgs": 0,
+ "maxArgs": 1
+ },
+ "get": {
+ "minArgs": 1,
+ "maxArgs": 2
+ },
+ "getAll": {
+ "minArgs": 0,
+ "maxArgs": 1
+ },
+ "getCurrent": {
+ "minArgs": 0,
+ "maxArgs": 1
+ },
+ "getLastFocused": {
+ "minArgs": 0,
+ "maxArgs": 1
+ },
+ "remove": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "update": {
+ "minArgs": 2,
+ "maxArgs": 2
+ }
+ }
+ };
+
+ if (Object.keys(apiMetadata).length === 0) {
+ throw new Error("api-metadata.json has not been included in browser-polyfill");
+ }
+ /**
+ * A WeakMap subclass which creates and stores a value for any key which does
+ * not exist when accessed, but behaves exactly as an ordinary WeakMap
+ * otherwise.
+ *
+ * @param {function} createItem
+ * A function which will be called in order to create the value for any
+ * key which does not exist, the first time it is accessed. The
+ * function receives, as its only argument, the key being created.
+ */
+
+
+ class DefaultWeakMap extends WeakMap {
+ constructor(createItem, items = undefined) {
+ super(items);
+ this.createItem = createItem;
+ }
+
+ get(key) {
+ if (!this.has(key)) {
+ this.set(key, this.createItem(key));
+ }
+
+ return super.get(key);
+ }
+
+ }
+ /**
+ * Returns true if the given object is an object with a `then` method, and can
+ * therefore be assumed to behave as a Promise.
+ *
+ * @param {*} value The value to test.
+ * @returns {boolean} True if the value is thenable.
+ */
+
+
+ const isThenable = value => {
+ return value && typeof value === "object" && typeof value.then === "function";
+ };
+ /**
+ * Creates and returns a function which, when called, will resolve or reject
+ * the given promise based on how it is called:
+ *
+ * - If, when called, `chrome.runtime.lastError` contains a non-null object,
+ * the promise is rejected with that value.
+ * - If the function is called with exactly one argument, the promise is
+ * resolved to that value.
+ * - Otherwise, the promise is resolved to an array containing all of the
+ * function's arguments.
+ *
+ * @param {object} promise
+ * An object containing the resolution and rejection functions of a
+ * promise.
+ * @param {function} promise.resolve
+ * The promise's resolution function.
+ * @param {function} promise.rejection
+ * The promise's rejection function.
+ * @param {object} metadata
+ * Metadata about the wrapped method which has created the callback.
+ * @param {integer} metadata.maxResolvedArgs
+ * The maximum number of arguments which may be passed to the
+ * callback created by the wrapped async function.
+ *
+ * @returns {function}
+ * The generated callback function.
+ */
+
+
+ const makeCallback = (promise, metadata) => {
+ return (...callbackArgs) => {
+ if (extensionAPIs.runtime.lastError) {
+ promise.reject(extensionAPIs.runtime.lastError);
+ } else if (metadata.singleCallbackArg || callbackArgs.length <= 1 && metadata.singleCallbackArg !== false) {
+ promise.resolve(callbackArgs[0]);
+ } else {
+ promise.resolve(callbackArgs);
+ }
+ };
+ };
+
+ const pluralizeArguments = numArgs => numArgs == 1 ? "argument" : "arguments";
+ /**
+ * Creates a wrapper function for a method with the given name and metadata.
+ *
+ * @param {string} name
+ * The name of the method which is being wrapped.
+ * @param {object} metadata
+ * Metadata about the method being wrapped.
+ * @param {integer} metadata.minArgs
+ * The minimum number of arguments which must be passed to the
+ * function. If called with fewer than this number of arguments, the
+ * wrapper will raise an exception.
+ * @param {integer} metadata.maxArgs
+ * The maximum number of arguments which may be passed to the
+ * function. If called with more than this number of arguments, the
+ * wrapper will raise an exception.
+ * @param {integer} metadata.maxResolvedArgs
+ * The maximum number of arguments which may be passed to the
+ * callback created by the wrapped async function.
+ *
+ * @returns {function(object, ...*)}
+ * The generated wrapper function.
+ */
+
+
+ const wrapAsyncFunction = (name, metadata) => {
+ return function asyncFunctionWrapper(target, ...args) {
+ if (args.length < metadata.minArgs) {
+ throw new Error(`Expected at least ${metadata.minArgs} ${pluralizeArguments(metadata.minArgs)} for ${name}(), got ${args.length}`);
+ }
+
+ if (args.length > metadata.maxArgs) {
+ throw new Error(`Expected at most ${metadata.maxArgs} ${pluralizeArguments(metadata.maxArgs)} for ${name}(), got ${args.length}`);
+ }
+
+ return new Promise((resolve, reject) => {
+ if (metadata.fallbackToNoCallback) {
+ // This API method has currently no callback on Chrome, but it return a promise on Firefox,
+ // and so the polyfill will try to call it with a callback first, and it will fallback
+ // to not passing the callback if the first call fails.
+ try {
+ target[name](...args, makeCallback({
+ resolve,
+ reject
+ }, metadata));
+ } catch (cbError) {
+ console.warn(`${name} API method doesn't seem to support the callback parameter, ` + "falling back to call it without a callback: ", cbError);
+ target[name](...args); // Update the API method metadata, so that the next API calls will not try to
+ // use the unsupported callback anymore.
+
+ metadata.fallbackToNoCallback = false;
+ metadata.noCallback = true;
+ resolve();
+ }
+ } else if (metadata.noCallback) {
+ target[name](...args);
+ resolve();
+ } else {
+ target[name](...args, makeCallback({
+ resolve,
+ reject
+ }, metadata));
+ }
+ });
+ };
+ };
+ /**
+ * Wraps an existing method of the target object, so that calls to it are
+ * intercepted by the given wrapper function. The wrapper function receives,
+ * as its first argument, the original `target` object, followed by each of
+ * the arguments passed to the original method.
+ *
+ * @param {object} target
+ * The original target object that the wrapped method belongs to.
+ * @param {function} method
+ * The method being wrapped. This is used as the target of the Proxy
+ * object which is created to wrap the method.
+ * @param {function} wrapper
+ * The wrapper function which is called in place of a direct invocation
+ * of the wrapped method.
+ *
+ * @returns {Proxy<function>}
+ * A Proxy object for the given method, which invokes the given wrapper
+ * method in its place.
+ */
+
+
+ const wrapMethod = (target, method, wrapper) => {
+ return new Proxy(method, {
+ apply(targetMethod, thisObj, args) {
+ return wrapper.call(thisObj, target, ...args);
+ }
+
+ });
+ };
+
+ let hasOwnProperty = Function.call.bind(Object.prototype.hasOwnProperty);
+ /**
+ * Wraps an object in a Proxy which intercepts and wraps certain methods
+ * based on the given `wrappers` and `metadata` objects.
+ *
+ * @param {object} target
+ * The target object to wrap.
+ *
+ * @param {object} [wrappers = {}]
+ * An object tree containing wrapper functions for special cases. Any
+ * function present in this object tree is called in place of the
+ * method in the same location in the `target` object tree. These
+ * wrapper methods are invoked as described in {@see wrapMethod}.
+ *
+ * @param {object} [metadata = {}]
+ * An object tree containing metadata used to automatically generate
+ * Promise-based wrapper functions for asynchronous. Any function in
+ * the `target` object tree which has a corresponding metadata object
+ * in the same location in the `metadata` tree is replaced with an
+ * automatically-generated wrapper function, as described in
+ * {@see wrapAsyncFunction}
+ *
+ * @returns {Proxy<object>}
+ */
+
+ const wrapObject = (target, wrappers = {}, metadata = {}) => {
+ let cache = Object.create(null);
+ let handlers = {
+ has(proxyTarget, prop) {
+ return prop in target || prop in cache;
+ },
+
+ get(proxyTarget, prop, receiver) {
+ if (prop in cache) {
+ return cache[prop];
+ }
+
+ if (!(prop in target)) {
+ return undefined;
+ }
+
+ let value = target[prop];
+
+ if (typeof value === "function") {
+ // This is a method on the underlying object. Check if we need to do
+ // any wrapping.
+ if (typeof wrappers[prop] === "function") {
+ // We have a special-case wrapper for this method.
+ value = wrapMethod(target, target[prop], wrappers[prop]);
+ } else if (hasOwnProperty(metadata, prop)) {
+ // This is an async method that we have metadata for. Create a
+ // Promise wrapper for it.
+ let wrapper = wrapAsyncFunction(prop, metadata[prop]);
+ value = wrapMethod(target, target[prop], wrapper);
+ } else {
+ // This is a method that we don't know or care about. Return the
+ // original method, bound to the underlying object.
+ value = value.bind(target);
+ }
+ } else if (typeof value === "object" && value !== null && (hasOwnProperty(wrappers, prop) || hasOwnProperty(metadata, prop))) {
+ // This is an object that we need to do some wrapping for the children
+ // of. Create a sub-object wrapper for it with the appropriate child
+ // metadata.
+ value = wrapObject(value, wrappers[prop], metadata[prop]);
+ } else if (hasOwnProperty(metadata, "*")) {
+ // Wrap all properties in * namespace.
+ value = wrapObject(value, wrappers[prop], metadata["*"]);
+ } else {
+ // We don't need to do any wrapping for this property,
+ // so just forward all access to the underlying object.
+ Object.defineProperty(cache, prop, {
+ configurable: true,
+ enumerable: true,
+
+ get() {
+ return target[prop];
+ },
+
+ set(value) {
+ target[prop] = value;
+ }
+
+ });
+ return value;
+ }
+
+ cache[prop] = value;
+ return value;
+ },
+
+ set(proxyTarget, prop, value, receiver) {
+ if (prop in cache) {
+ cache[prop] = value;
+ } else {
+ target[prop] = value;
+ }
+
+ return true;
+ },
+
+ defineProperty(proxyTarget, prop, desc) {
+ return Reflect.defineProperty(cache, prop, desc);
+ },
+
+ deleteProperty(proxyTarget, prop) {
+ return Reflect.deleteProperty(cache, prop);
+ }
+
+ }; // Per contract of the Proxy API, the "get" proxy handler must return the
+ // original value of the target if that value is declared read-only and
+ // non-configurable. For this reason, we create an object with the
+ // prototype set to `target` instead of using `target` directly.
+ // Otherwise we cannot return a custom object for APIs that
+ // are declared read-only and non-configurable, such as `chrome.devtools`.
+ //
+ // The proxy handlers themselves will still use the original `target`
+ // instead of the `proxyTarget`, so that the methods and properties are
+ // dereferenced via the original targets.
+
+ let proxyTarget = Object.create(target);
+ return new Proxy(proxyTarget, handlers);
+ };
+ /**
+ * Creates a set of wrapper functions for an event object, which handles
+ * wrapping of listener functions that those messages are passed.
+ *
+ * A single wrapper is created for each listener function, and stored in a
+ * map. Subsequent calls to `addListener`, `hasListener`, or `removeListener`
+ * retrieve the original wrapper, so that attempts to remove a
+ * previously-added listener work as expected.
+ *
+ * @param {DefaultWeakMap<function, function>} wrapperMap
+ * A DefaultWeakMap object which will create the appropriate wrapper
+ * for a given listener function when one does not exist, and retrieve
+ * an existing one when it does.
+ *
+ * @returns {object}
+ */
+
+
+ const wrapEvent = wrapperMap => ({
+ addListener(target, listener, ...args) {
+ target.addListener(wrapperMap.get(listener), ...args);
+ },
+
+ hasListener(target, listener) {
+ return target.hasListener(wrapperMap.get(listener));
+ },
+
+ removeListener(target, listener) {
+ target.removeListener(wrapperMap.get(listener));
+ }
+
+ }); // Keep track if the deprecation warning has been logged at least once.
+
+
+ let loggedSendResponseDeprecationWarning = false;
+ const onMessageWrappers = new DefaultWeakMap(listener => {
+ if (typeof listener !== "function") {
+ return listener;
+ }
+ /**
+ * Wraps a message listener function so that it may send responses based on
+ * its return value, rather than by returning a sentinel value and calling a
+ * callback. If the listener function returns a Promise, the response is
+ * sent when the promise either resolves or rejects.
+ *
+ * @param {*} message
+ * The message sent by the other end of the channel.
+ * @param {object} sender
+ * Details about the sender of the message.
+ * @param {function(*)} sendResponse
+ * A callback which, when called with an arbitrary argument, sends
+ * that value as a response.
+ * @returns {boolean}
+ * True if the wrapped listener returned a Promise, which will later
+ * yield a response. False otherwise.
+ */
+
+
+ return function onMessage(message, sender, sendResponse) {
+ let didCallSendResponse = false;
+ let wrappedSendResponse;
+ let sendResponsePromise = new Promise(resolve => {
+ wrappedSendResponse = function (response) {
+ if (!loggedSendResponseDeprecationWarning) {
+ console.warn(SEND_RESPONSE_DEPRECATION_WARNING, new Error().stack);
+ loggedSendResponseDeprecationWarning = true;
+ }
+
+ didCallSendResponse = true;
+ resolve(response);
+ };
+ });
+ let result;
+
+ try {
+ result = listener(message, sender, wrappedSendResponse);
+ } catch (err) {
+ result = Promise.reject(err);
+ }
+
+ const isResultThenable = result !== true && isThenable(result); // If the listener didn't returned true or a Promise, or called
+ // wrappedSendResponse synchronously, we can exit earlier
+ // because there will be no response sent from this listener.
+
+ if (result !== true && !isResultThenable && !didCallSendResponse) {
+ return false;
+ } // A small helper to send the message if the promise resolves
+ // and an error if the promise rejects (a wrapped sendMessage has
+ // to translate the message into a resolved promise or a rejected
+ // promise).
+
+
+ const sendPromisedResult = promise => {
+ promise.then(msg => {
+ // send the message value.
+ sendResponse(msg);
+ }, error => {
+ // Send a JSON representation of the error if the rejected value
+ // is an instance of error, or the object itself otherwise.
+ let message;
+
+ if (error && (error instanceof Error || typeof error.message === "string")) {
+ message = error.message;
+ } else {
+ message = "An unexpected error occurred";
+ }
+
+ sendResponse({
+ __mozWebExtensionPolyfillReject__: true,
+ message
+ });