2018-08-31 22:27:02 +00:00
( 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 ;
}
} ) ( this , function ( module ) {
2019-04-01 21:53:28 +00:00
/* webextension-polyfill - v0.4.0 - Wed Feb 06 2019 11:58:31 */
2018-08-31 22:27:02 +00:00
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
/ * T h i s S o u r c e C o d e F o r m i s s u b j e c t t o t h e t e r m s o f t h e M o z i l l a P u b l i c
* 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.
2019-04-01 21:53:28 +00:00
const wrapAPIs = extensionAPIs => {
2018-08-31 22:27:02 +00:00
// 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 ,
2019-04-01 21:53:28 +00:00
"maxArgs" : 2 ,
"singleCallbackArg" : false
2018-08-31 22:27:02 +00:00
}
} ,
"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
} ,
"getBrowserInfo" : {
"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 ) => {
2019-04-01 21:53:28 +00:00
if ( extensionAPIs . runtime . lastError ) {
promise . reject ( extensionAPIs . runtime . lastError ) ;
} else if ( metadata . singleCallbackArg || callbackArgs . length <= 1 && metadata . singleCallbackArg !== false ) {
2018-08-31 22:27:02 +00:00
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 {
// 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
} ) ;
} ) . catch ( err => {
// Print an error on the console if unable to send the response.
console . error ( "Failed to send onMessage rejected reply" , err ) ;
} ) ;
} ;
// If the listener returned a Promise, send the resolved value as a
// result, otherwise wait the promise related to the wrappedSendResponse
// callback to resolve and send it as a response.
if ( isResultThenable ) {
sendPromisedResult ( result ) ;
} else {
sendPromisedResult ( sendResponsePromise ) ;
}
// Let Chrome know that the listener is replying.
return true ;
} ;
} ) ;
const wrappedSendMessageCallback = ( { reject , resolve } , reply ) => {
2019-04-01 21:53:28 +00:00
if ( extensionAPIs . runtime . lastError ) {
2018-08-31 22:27:02 +00:00
// Detect when none of the listeners replied to the sendMessage call and resolve
// the promise to undefined as in Firefox.
// See https://github.com/mozilla/webextension-polyfill/issues/130
2019-04-01 21:53:28 +00:00
if ( extensionAPIs . runtime . lastError . message === CHROME _SEND _MESSAGE _CALLBACK _NO _RESPONSE _MESSAGE ) {
2018-08-31 22:27:02 +00:00
resolve ( ) ;
} else {
2019-04-01 21:53:28 +00:00
reject ( extensionAPIs . runtime . lastError ) ;
2018-08-31 22:27:02 +00:00
}
} else if ( reply && reply . _ _mozWebExtensionPolyfillReject _ _ ) {
// Convert back the JSON representation of the error into
// an Error instance.
reject ( new Error ( reply . message ) ) ;
} else {
resolve ( reply ) ;
}
} ;
const wrappedSendMessage = ( name , metadata , apiNamespaceObj , ... 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 ) => {
const wrappedCb = wrappedSendMessageCallback . bind ( null , { resolve , reject } ) ;
args . push ( wrappedCb ) ;
apiNamespaceObj . sendMessage ( ... args ) ;
} ) ;
} ;
const staticWrappers = {
runtime : {
onMessage : wrapEvent ( onMessageWrappers ) ,
onMessageExternal : wrapEvent ( onMessageWrappers ) ,
sendMessage : wrappedSendMessage . bind ( null , "sendMessage" , { minArgs : 1 , maxArgs : 3 } )
} ,
tabs : {
sendMessage : wrappedSendMessage . bind ( null , "sendMessage" , { minArgs : 2 , maxArgs : 3 } )
}
} ;
const settingMetadata = {
clear : { minArgs : 1 , maxArgs : 1 } ,
get : { minArgs : 1 , maxArgs : 1 } ,
set : { minArgs : 1 , maxArgs : 1 }
} ;
apiMetadata . privacy = {
network : {
networkPredictionEnabled : settingMetadata ,
webRTCIPHandlingPolicy : settingMetadata
} ,
services : {
passwordSavingEnabled : settingMetadata
} ,
websites : {
hyperlinkAuditingEnabled : settingMetadata ,
referrersEnabled : settingMetadata
}
} ;
2019-04-01 21:53:28 +00:00
return wrapObject ( extensionAPIs , staticWrappers , apiMetadata ) ;
2018-08-31 22:27:02 +00:00
} ;
// The build process adds a UMD wrapper around this file, which makes the
// `module` variable available.
2019-04-01 21:53:28 +00:00
module . exports = wrapAPIs ( chrome ) ;
2018-08-31 22:27:02 +00:00
} else {
2019-04-01 21:53:28 +00:00
module . exports = browser ;
2018-08-31 22:27:02 +00:00
}
} ) ;