├── .gitignore ├── .travis.yml ├── README.md ├── content-api ├── chrome-api-bridge.js ├── event.js ├── footer.js ├── header.js └── utils.js ├── crx.js ├── data ├── chrome-api-child.js └── default-background.html ├── definitions └── stubs.json ├── index.js ├── lib ├── api │ ├── storage.js │ └── tabs.js ├── chrome-api-controller.js ├── content-script-utils.js └── utils.js ├── manifest-actions ├── background.js ├── browser_action.js ├── chrome_url_overrides.js └── content_scripts.js ├── package.json ├── scripts ├── build-chrome-api-child.js └── create-stubs.js └── test ├── disabled ├── test-content-script.js ├── test-runtime-getManifest.js ├── test-runtime-sendMessage.js └── test-tabs-sendMessage.js ├── fixtures.js ├── fixtures ├── addons │ ├── chrome.runtime.getManifest │ │ ├── background.js │ │ └── manifest.json │ ├── chrome.runtime.sendMessage │ │ ├── background.js │ │ ├── content_script.js │ │ └── manifest.json │ ├── chrome.storage.set │ │ ├── content_script.js │ │ └── manifest.json │ ├── chrome.tabs.sendMessage │ │ ├── background.js │ │ ├── content_script.js │ │ └── manifest.json │ └── simple-content-script │ │ ├── index.js │ │ └── manifest.json ├── chrome.tabs.create.js ├── chrome.tabs.duplicate.js └── chrome.tabs.remove.js ├── test-storage-set.js ├── test-tabs-create.js ├── test-tabs-duplicate.js ├── test-tabs-getcurrent.js └── test-tabs-remove.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .DS_Store 3 | *.xpi 4 | *.swp 5 | *.swo 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - "0.10" 5 | 6 | before_install: 7 | - "export DISPLAY=:99.0" 8 | - "/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac -screen 0 1280x1024x16 -extension RANDR" 9 | 10 | before_script: 11 | - npm install jpm -g 12 | - npm install fx-download -g 13 | - cd .. 14 | - fx-download --branch nightly -c prerelease --host ftp.mozilla.org firefox 15 | - export JPM_FIREFOX_BINARY=$TRAVIS_BUILD_DIR/../firefox/firefox 16 | - cd $TRAVIS_BUILD_DIR 17 | 18 | script: 19 | - jpm test -v 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # chrome-tailor-jetpack [](https://travis-ci.org/jetpack-labs/chrome-tailor-jetpack) 2 | 3 | Skeleton Firefox extension that can run a Chrome extension. 4 | 5 | ## Supported APIs 6 | 7 | ### Chrome APIs 8 | 9 | APIs supported from the [Chrome extension API](https://developer.chrome.com/extensions/api_index). Only supporting stable APIs. 10 | 11 | #### Supported 12 | 13 | * [chrome.topSites](https://developer.chrome.com/extensions/topSites) 14 | 15 | #### Partial Support 16 | 17 | * [chrome.browserAction](https://developer.chrome.com/extensions/browserAction) 18 | * `chrome.browserAction.onClick.addListener` supported 19 | * [chrome.extension](https://developer.chrome.com/extensions/extension) 20 | * `getURL` supported 21 | * [chrome.history](https://developer.chrome.com/extensions/history) 22 | * `addUrl`, `deleteAll`, `deleteUrl` supported 23 | * More detailed needed for models used here 24 | * [chrome.runtime](https://developer.chrome.com/extensions/runtime) 25 | * `getURL`, `getManifest`, `onMessage`, `sendMessage` 26 | * [chrome.storage.local](https://developer.chrome.com/extensions/storage) 27 | * `get`, `getBytesInUse`, `set` 28 | * [chrome.tabs](https://developer.chrome.com/extensions/tabs) 29 | * `create`, `duplicate`, `executeScript`, `getCurrent`, `query`, `remove`, `sendMessage` 30 | 31 | #### Not Yet Supported 32 | 33 | * [chrome.accessibilityFeatures](https://developer.chrome.com/extensions/accessibilityFeatures) 34 | * [chrome.alarms](https://developer.chrome.com/extensions/alarms) 35 | * [chrome.bookmarks](https://developer.chrome.com/extensions/bookmarks) 36 | * [chrome.browsingData](https://developer.chrome.com/extensions/browsingData) 37 | * [chrome.commands](https://developer.chrome.com/extensions/commands) 38 | * [chrome.contentSettings](https://developer.chrome.com/extensions/contentSettings) 39 | * [chrome.contextMenus](https://developer.chrome.com/extensions/contextMenus) 40 | * [chrome.cookies](https://developer.chrome.com/extensions/cookies) 41 | * [chrome.debugger](https://developer.chrome.com/extensions/debugger) 42 | * [chrome.declaritiveContent](https://developer.chrome.com/extensions/declarativeContent) 43 | * [chrome.desktopCapture](https://developer.chrome.com/extensions/desktopCapture) 44 | * [chrome.devtoolsInspectedWindow](https://developer.chrome.com/extensions/devtools.inspectedWindow) 45 | * [chrome.devtools.network](https://developer.chrome.com/extensions/devtools.network) 46 | * [chrome.devtools.panel](https://developer.chrome.com/extensions/devtools.panels) 47 | * [chrome.downloads](https://developer.chrome.com/extensions/downloads) 48 | * [chrome.enterprise.platformKeys](https://developer.chrome.com/extensions/enterprise.platformKeys) 49 | * [chrome.events](https://developer.chrome.com/extensions/events) 50 | * [chrome.extensionTypes](https://developer.chrome.com/extensions/extensionTypes) 51 | * [chrome.fileBrowserHandler](https://developer.chrome.com/extensions/fileBrowserHandler) 52 | * [chrome.fileSystemProvider](https://developer.chrome.com/extensions/fileSystemProvider) 53 | * [chrome.fontSettings](https://developer.chrome.com/extensions/fontSettings) 54 | * [chrome.gcm](https://developer.chrome.com/extensions/gcm) 55 | * [chrome.i18n](https://developer.chrome.com/extensions/i18n) 56 | * [chrome.identity](https://developer.chrome.com/extensions/identity) 57 | * [chrome.idle](https://developer.chrome.com/extensions/idle) 58 | * [chrome.input.ime](https://developer.chrome.com/extensions/input.ime) 59 | * [chrome.management](https://developer.chrome.com/extensions/management) 60 | * [chrome.notifications](https://developer.chrome.com/extensions/notifications) 61 | * [chrome.omnibox](https://developer.chrome.com/extensions/omnibox) 62 | * [chrome.pageAction](https://developer.chrome.com/extensions/pageAction) 63 | * [chrome.permissions](https://developer.chrome.com/extensions/permissions) 64 | * [chrome.power](https://developer.chrome.com/extensions/power) 65 | * [chrome.privacy](https://developer.chrome.com/extensions/privacy) 66 | * [chrome.proxy](https://developer.chrome.com/extensions/proxy) 67 | * [chrome.sessions](https://developer.chrome.com/extensions/sessions) 68 | * [chrome.storage](https://developer.chrome.com/extensions/storage) 69 | * [chrome.system.cpu](https://developer.chrome.com/extensions/system.cpu) 70 | * [chrome.system.memory](https://developer.chrome.com/extensions/system.memory) 71 | * [chrome.system.storage](https://developer.chrome.com/extensions/system.storage) 72 | * [chrome.tabCapture](https://developer.chrome.com/extensions/tabCapture) 73 | * [chrome.tts](https://developer.chrome.com/extensions/tts) 74 | * [chrome.ttsEngine](https://developer.chrome.com/extensions/ttsEngine) 75 | * [chrome.types](https://developer.chrome.com/extensions/types) 76 | * [chrome.webNavigation](https://developer.chrome.com/extensions/webNavigation) 77 | * [chrome.webRequest](https://developer.chrome.com/extensions/webRequest) 78 | * [chrome.webstore](https://developer.chrome.com/extensions/webstore) 79 | * [chrome.windows](https://developer.chrome.com/extensions/windows) 80 | 81 | #### Not Supported 82 | 83 | * [chrome.wallpaper](https://developer.chrome.com/extensions/wallpaper) 84 | 85 | ### Manifest Declarations 86 | 87 | Commands from [manifest.json](https://developer.chrome.com/extensions/manifest) currently supported. Not listing the metadata fields such as `name`, `version`, etc. 88 | 89 | #### Supported 90 | 91 | * [background](https://developer.chrome.com/extensions/background) 92 | * [browser_action](https://developer.chrome.com/extensions/browserAction) 93 | * [chrome_url_overrides](https://developer.chrome.com/extensions/ui_override) 94 | 95 | #### Partial Support 96 | 97 | * [content_scripts (in-progress)](https://developer.chrome.com/extensions/content_scripts) 98 | 99 | #### Not Yet Supported 100 | 101 | * [background.persistent (event pages)](https://developer.chrome.com/extensions/event_pages) 102 | * [chrome_settings_overrides](https://developer.chrome.com/extensions/settings_override) 103 | * [chrome_url_overrides](https://developer.chrome.com/extensions/override) 104 | * [commands](https://developer.chrome.com/extensions/commands) 105 | * [content_security_policy](https://developer.chrome.com/extensions/contentSecurityPolicy) 106 | * [devtools_page](https://developer.chrome.com/extensions/devtools) 107 | * [externally_connectable](https://developer.chrome.com/extensions/manifest/externally_connectable) 108 | * [import](https://developer.chrome.com/extensions/shared_modules) 109 | * [file_browser_handlers](https://developer.chrome.com/extensions/fileBrowserHandler) 110 | * [incognito](https://developer.chrome.com/extensions/manifest/incognito) 111 | * [omnibox](https://developer.chrome.com/extensions/omnibox) 112 | * [optional_permissions](https://developer.chrome.com/extensions/permissions) 113 | * [options_page](https://developer.chrome.com/extensions/options) 114 | * [options_ui](https://developer.chrome.com/extensions/optionsV2) 115 | * [permissions](https://developer.chrome.com/extensions/declare_permissions) 116 | * [sandbox](https://developer.chrome.com/extensions/manifest/sandbox) 117 | * [storage](https://developer.chrome.com/extensions/manifest/storage) 118 | * [tts_engine](https://developer.chrome.com/extensions/ttsEngine) 119 | * [web_accessible_resources](https://developer.chrome.com/extensions/manifest/web_accessible_resources) 120 | 121 | #### Not Supported 122 | 123 | * [nacl_modules](https://developer.chrome.com/extensions/manifest/nacl_modules) 124 | * [requirements](https://developer.chrome.com/extensions/manifest/requirements) 125 | -------------------------------------------------------------------------------- /content-api/chrome-api-bridge.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Prebound with the `config` settings in the build script `./scripts/build-chrome-api-child.js`, 3 | * this function is then called with additional arguments, exposed to the user as a chrome API: 4 | * `tabs.duplicate(tab)` 5 | * Handles the message communication with the main Jetpack proc. 6 | */ 7 | let INC_ID = 0; 8 | 9 | JETPACK.RPC = function (config) { 10 | var id = INC_ID++; 11 | var args = Array.prototype.slice.call(arguments); 12 | // Pop off the configuration; 13 | args.shift(); 14 | var successCallback = config.success != null ? args[config.success] : null; 15 | // Not really supporting failureCallback at the moment, as only one API uses it. 16 | var failureCallback = config.failure != null ? args[config.failure] : null; 17 | 18 | self.port.on("chrome-api:response", handler); 19 | self.port.emit("chrome-api:request", { 20 | method: config.method, 21 | args: args, 22 | id: id, 23 | name: config.name, 24 | success: config.success, 25 | failure: config.failure 26 | }); 27 | 28 | function handler (data) { 29 | if (data.id !== id) { 30 | return; 31 | } 32 | self.port.removeListener("chrome-api:response", handler); 33 | var callback = data.error ? failureCallback : successCallback; 34 | if (typeof callback === "function") { 35 | if (data.res != null) { 36 | callback.apply(null, cleanse(data.res)); 37 | } else { 38 | callback(); 39 | } 40 | } 41 | } 42 | }; 43 | 44 | function cleanse (obj) { 45 | return unsafeWindow.JSON.parse(JSON.stringify(obj)); 46 | } 47 | -------------------------------------------------------------------------------- /content-api/event.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Handles event calls from platform. Takes an object with the following config: 3 | * 4 | * @param {string} name 5 | * Name of the event, including namespace, like "tabs.onCreated". 6 | * @param {Array} args 7 | */ 8 | self.port.on("chrome-api:event", function ({ name, args }) { 9 | JETPACK.EventManager.handleEvent(name, args); 10 | }); 11 | 12 | JETPACK.EventManager = { 13 | events: new Map(), 14 | getEvent: function (name) { 15 | if (this.events.has(name)) { 16 | return this.events.get(name); 17 | } 18 | let event = Event(name); 19 | this.events.set(name, event); 20 | return event; 21 | }, 22 | handleEvent: function (name, args) { 23 | let event = this.events.get(name); 24 | 25 | if (!event) { 26 | return; 27 | } 28 | 29 | event._execute(args); 30 | } 31 | } 32 | 33 | /** 34 | * Definition for chrome.events.Event object defined: 35 | * https://developer.chrome.com/extensions/events#type-Event 36 | * 37 | * Does not yet support the Declarative Event API consisting of addRules, removeRules, and getRules. 38 | * https://developer.chrome.com/extensions/events#declarative 39 | * 40 | * TODO once Declarative Event API implemented, should make this more general 41 | * for both exposed events (chrome.alarm.onAlarm) and declarative events. 42 | * 43 | * This is not directly exposed to chrome.* APIs, but already attached 44 | * on some namespaces (`chrome.alarm.onAlarm`). 45 | */ 46 | let Event = JETPACK.Class({ 47 | initialize: function (name) { 48 | this._name = name; 49 | this._events = new Set(); 50 | }, 51 | addListener: function addListener (callback) { 52 | this._events.add(callback); 53 | }, 54 | removeListener: function removeListener (callback) { 55 | this._events.remove(callback); 56 | }, 57 | hasListener: function hasListener (callback) { 58 | return this._events.has(callback); 59 | }, 60 | hasListeners: function hasListeners () { 61 | return !!this._events.length; 62 | }, 63 | _execute: function (args) { 64 | for (let ev of this._events) { 65 | ev.apply(unsafeWindow, cloneInto(args, unsafeWindow)); 66 | } 67 | } 68 | }); 69 | -------------------------------------------------------------------------------- /content-api/footer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The last piece injected into the content, takes the definition stub 3 | * from ./definitions/stub.json (stored at self.JETPACK.API_DEFINITIONS) 4 | * and creates the API objects and function/event hooks in the proper scope. 5 | */ 6 | let definitions = JETPACK.API_DEFINITIONS; 7 | let chrome = unsafeWindow.chrome = createObjectIn(unsafeWindow); 8 | let apiTypes = {}; 9 | 10 | Object.keys(definitions).map(namespace => { 11 | // First, register all the types upfront. 12 | bindTypes(namespace, definitions[namespace].types); 13 | return namespace; 14 | }).forEach(namespace => { 15 | // Then bind everything else 16 | let def = definitions[namespace]; 17 | bindDefinition(namespace, def); 18 | }); 19 | 20 | function bindDefinition (namespace, def) { 21 | bindFunctions(namespace, def.functions); 22 | bindEvents(namespace, def.events); 23 | bindProperties(namespace, def.properties); 24 | } 25 | 26 | /** 27 | * Returns the corresponding object related to a string name of 28 | * a namespace. Converts "devtools.inspectedWindow", and returns 29 | * the object found at `unsafeWindow.chrome.devtools.inspectedWindow`, creating 30 | * objects along the path if needed in the unsafeWindow scope. 31 | */ 32 | function getNamespace (namespace) { 33 | let namespaces = namespace.split("."); 34 | let parent = chrome; 35 | for (let name of namespaces) { 36 | parent[name] = parent[name] || createObjectIn(unsafeWindow); 37 | parent = parent[name]; 38 | } 39 | return parent; 40 | } 41 | 42 | /** 43 | * Binds functions to their parent namespace, creating the namespace 44 | * itself if it does not exist. 45 | */ 46 | function bindFunctions (namespace, functions=[]) { 47 | let ns = getNamespace(namespace); 48 | functions.forEach(fnDef => { 49 | let { name: method, successCallbackIndex: success, failureCallbackIndex: failure } = fnDef; 50 | let name = `${namespace}.${method}`; 51 | exportFunction(JETPACK.RPC.bind(null, { name, success, failure }), ns, { defineAs: method }); 52 | }); 53 | } 54 | 55 | /** 56 | * Stores type information if used elsewhere. Only used for 57 | * contentSettings.ContentSetting, storage.StorageArea, and types.ChromeSetting. 58 | * `bindProperties` ends up using this data. 59 | */ 60 | function bindTypes (namespace, types={}) { 61 | Object.keys(types).forEach(type=> { 62 | let typeName = `${namespace}.${type}`; 63 | apiTypes[typeName] = types[type]; 64 | }); 65 | } 66 | 67 | function bindEvents (namespace, events=[]) { 68 | let ns = getNamespace(namespace); 69 | events.forEach(name => { 70 | // EventManager.getEvent caches the event instance, 71 | // so we can lazily load event objects 72 | Object.defineProperty(ns, name, { 73 | get: exportFunction(() => JETPACK.EventManager.getEvent(`${namespace}.${name}`).getShadow(), ns) 74 | }); 75 | }); 76 | } 77 | 78 | function bindProperties (namespace, properties={}) { 79 | Object.keys(properties).forEach(prop => { 80 | let def = properties[prop]; 81 | if (def.getter) { 82 | // Not yet implemented 83 | } 84 | // For containers, recursively bind its children functions/events/props, etc 85 | else if (def.container) { 86 | bindDefinition(`${namespace}.${prop}`, def); 87 | } 88 | // If this object a class, (storage.StorageArea, etc), implement its props and functions 89 | // as well 90 | else if (def.class) { 91 | // If type isn't scoped, it means it's part of the same namespace. 92 | let typeName = def.class.indexOf(".") === -1 ? `${namespace}.${def.class}` : def.class; 93 | let typeDef = apiTypes[typeName]; 94 | if (!typeDef) { 95 | throw new Error(`No type definition found for ${def.class} in ${namespace}.`); 96 | } 97 | bindDefinition(`${namespace}.${prop}`, typeDef); 98 | 99 | // Also bind the original definition, as it can contain additional 100 | // properties 101 | bindDefinition(`${namespace}.${prop}`, def); 102 | } 103 | // If this is just a value, probably an enum or constant -- just set 104 | else if (def.value != null) { 105 | getNamespace(namespace)[prop] = def.value; 106 | } 107 | }); 108 | } 109 | -------------------------------------------------------------------------------- /content-api/header.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | /** 6 | * THIS FILE AUTOMATICALLY GENERATED BY ./scripts/build-chrome-api-child.js 7 | * DO NOT EDIT MANUALLY.\n"; 8 | */ 9 | 10 | const JETPACK = {}; 11 | 12 | -------------------------------------------------------------------------------- /content-api/utils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Takes an object with functions, and constructs a factory for this object. 3 | * Instances also have a new method, `getShadow()`, returning a shimmed 4 | * version of the instance for use in `unsafeWindow` context, whose function calls 5 | * are stitched together here with the real instance. 6 | * 7 | * This is totally overengineered, but every other method was tripped up by 8 | * something with security, sandboxes, or xrays. 9 | */ 10 | function Class (Base) { 11 | let InstanceMap = new Map(); 12 | 13 | // Strip out `initialize` function and any `_` properties 14 | // for the shadow API. 15 | let shadowAPI = Object.keys(Base).reduce((shadow, prop) => { 16 | if (prop === "initialize" || prop[0] === "_") { 17 | return shadow; 18 | } 19 | 20 | let proxy = function () { 21 | return Base[prop].apply(InstanceMap.get(this), arguments); 22 | }; 23 | 24 | shadow[prop] = exportFunction(proxy, unsafeWindow); 25 | 26 | return shadow; 27 | }, createObjectIn(unsafeWindow)); 28 | 29 | return function () { 30 | let realInstance = Object.create(Base); 31 | realInstance.initialize.apply(realInstance, arguments); 32 | let shadowInstance = cloneInto(shadowAPI, unsafeWindow, { cloneFunctions: true }); 33 | InstanceMap.set(shadowInstance, realInstance); 34 | 35 | realInstance.getShadow = () => shadowInstance; 36 | 37 | return realInstance; 38 | }; 39 | } 40 | JETPACK.Class = Class; 41 | -------------------------------------------------------------------------------- /crx.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | "use strict"; 5 | 6 | const contentScripts = require("./manifest-actions/content_scripts"); 7 | const background = require("./manifest-actions/background"); 8 | const browserAction = require("./manifest-actions/browser_action"); 9 | const overrides = require("./manifest-actions/chrome_url_overrides"); 10 | const events = require("sdk/system/events"); 11 | 12 | function load(options) { 13 | const rootURI = options.rootURI; 14 | const manifest = require(rootURI + "manifest.json"); 15 | var browserActionBtn; 16 | var backgroundPage; 17 | var contentScriptMod; 18 | 19 | function getURL(path) { 20 | return rootURI + path; 21 | } 22 | 23 | if (manifest.background) { 24 | let options = JSON.parse(JSON.stringify(manifest.background)); 25 | options.scripts = (Array.isArray(options.scripts) ? options.scripts : []).map(script => getURL(script)); 26 | if (options.page) { 27 | options.page = getURL(options.page); 28 | } 29 | options.rootURI = rootURI; 30 | 31 | backgroundPage = background(options); 32 | } 33 | 34 | if (manifest.browser_action) { 35 | let options = JSON.parse(JSON.stringify(manifest.browser_action)); 36 | options.default_popup = getURL(options.default_popup); 37 | options.default_icon = getURL(options.default_icon); 38 | options.rootURI = rootURI; 39 | 40 | browserActionBtn = browserAction(options); 41 | } 42 | 43 | if (manifest.chrome_url_overrides) { 44 | let options = JSON.parse(JSON.stringify(manifest.chrome_url_overrides)); 45 | if (options.newtab) { 46 | options.newtab = getURL(options.newtab); 47 | } 48 | options.rootURI = rootURI; 49 | 50 | overrides(options); 51 | } 52 | 53 | if (manifest.content_scripts) { 54 | manifest.content_scripts.forEach(def => { 55 | let options = JSON.parse(JSON.stringify(def)); 56 | options.rootURI = rootURI; 57 | if (options.js) { 58 | options.js = options.js.map(getURL); 59 | } 60 | if (options.css) { 61 | options.css = options.css.map(getURL); 62 | } 63 | 64 | contentScriptMod = contentScripts(options) 65 | }); 66 | } 67 | 68 | return { 69 | unload: function() { 70 | events.emit("crx-unload", { 71 | subject: { 72 | name: manifest.name 73 | } 74 | }); 75 | 76 | // TODO: close all overriden pages 77 | } 78 | } 79 | } 80 | exports.load = load; 81 | -------------------------------------------------------------------------------- /data/chrome-api-child.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | /** 6 | * THIS FILE AUTOMATICALLY GENERATED BY ./scripts/build-chrome-api-child.js 7 | * DO NOT EDIT MANUALLY.\n"; 8 | */ 9 | 10 | const JETPACK = {}; 11 | 12 | 13 | (function(){ 14 | /** 15 | * Takes an object with functions, and constructs a factory for this object. 16 | * Instances also have a new method, `getShadow()`, returning a shimmed 17 | * version of the instance for use in `unsafeWindow` context, whose function calls 18 | * are stitched together here with the real instance. 19 | * 20 | * This is totally overengineered, but every other method was tripped up by 21 | * something with security, sandboxes, or xrays. 22 | */ 23 | function Class (Base) { 24 | let InstanceMap = new Map(); 25 | 26 | // Strip out `initialize` function and any `_` properties 27 | // for the shadow API. 28 | let shadowAPI = Object.keys(Base).reduce((shadow, prop) => { 29 | if (prop === "initialize" || prop[0] === "_") { 30 | return shadow; 31 | } 32 | 33 | let proxy = function () { 34 | return Base[prop].apply(InstanceMap.get(this), arguments); 35 | }; 36 | 37 | shadow[prop] = exportFunction(proxy, unsafeWindow); 38 | 39 | return shadow; 40 | }, createObjectIn(unsafeWindow)); 41 | 42 | return function () { 43 | let realInstance = Object.create(Base); 44 | realInstance.initialize.apply(realInstance, arguments); 45 | let shadowInstance = cloneInto(shadowAPI, unsafeWindow, { cloneFunctions: true }); 46 | InstanceMap.set(shadowInstance, realInstance); 47 | 48 | realInstance.getShadow = () => shadowInstance; 49 | 50 | return realInstance; 51 | }; 52 | } 53 | JETPACK.Class = Class; 54 | 55 | })(); 56 | 57 | (function(){ 58 | /** 59 | * Prebound with the `config` settings in the build script `./scripts/build-chrome-api-child.js`, 60 | * this function is then called with additional arguments, exposed to the user as a chrome API: 61 | * `tabs.duplicate(tab)` 62 | * Handles the message communication with the main Jetpack proc. 63 | */ 64 | let INC_ID = 0; 65 | 66 | JETPACK.RPC = function (config) { 67 | var id = INC_ID++; 68 | var args = Array.prototype.slice.call(arguments); 69 | // Pop off the configuration; 70 | args.shift(); 71 | var successCallback = config.success != null ? args[config.success] : null; 72 | // Not really supporting failureCallback at the moment, as only one API uses it. 73 | var failureCallback = config.failure != null ? args[config.failure] : null; 74 | 75 | self.port.on("chrome-api:response", handler); 76 | self.port.emit("chrome-api:request", { 77 | method: config.method, 78 | args: args, 79 | id: id, 80 | name: config.name, 81 | success: config.success, 82 | failure: config.failure 83 | }); 84 | 85 | function handler (data) { 86 | if (data.id !== id) { 87 | return; 88 | } 89 | self.port.removeListener("chrome-api:response", handler); 90 | var callback = data.error ? failureCallback : successCallback; 91 | if (typeof callback === "function") { 92 | if (data.res != null) { 93 | callback.apply(null, cleanse(data.res)); 94 | } else { 95 | callback(); 96 | } 97 | } 98 | } 99 | }; 100 | 101 | function cleanse (obj) { 102 | return unsafeWindow.JSON.parse(JSON.stringify(obj)); 103 | } 104 | 105 | })(); 106 | 107 | (function(){ 108 | /** 109 | * Handles event calls from platform. Takes an object with the following config: 110 | * 111 | * @param {string} name 112 | * Name of the event, including namespace, like "tabs.onCreated". 113 | * @param {Array} args 114 | */ 115 | self.port.on("chrome-api:event", function ({ name, args }) { 116 | JETPACK.EventManager.handleEvent(name, args); 117 | }); 118 | 119 | JETPACK.EventManager = { 120 | events: new Map(), 121 | getEvent: function (name) { 122 | if (this.events.has(name)) { 123 | return this.events.get(name); 124 | } 125 | let event = Event(name); 126 | this.events.set(name, event); 127 | return event; 128 | }, 129 | handleEvent: function (name, args) { 130 | let event = this.events.get(name); 131 | 132 | if (!event) { 133 | return; 134 | } 135 | 136 | event._execute(args); 137 | } 138 | } 139 | 140 | /** 141 | * Definition for chrome.events.Event object defined: 142 | * https://developer.chrome.com/extensions/events#type-Event 143 | * 144 | * Does not yet support the Declarative Event API consisting of addRules, removeRules, and getRules. 145 | * https://developer.chrome.com/extensions/events#declarative 146 | * 147 | * TODO once Declarative Event API implemented, should make this more general 148 | * for both exposed events (chrome.alarm.onAlarm) and declarative events. 149 | * 150 | * This is not directly exposed to chrome.* APIs, but already attached 151 | * on some namespaces (`chrome.alarm.onAlarm`). 152 | */ 153 | let Event = JETPACK.Class({ 154 | initialize: function (name) { 155 | this._name = name; 156 | this._events = new Set(); 157 | }, 158 | addListener: function addListener (callback) { 159 | this._events.add(callback); 160 | }, 161 | removeListener: function removeListener (callback) { 162 | this._events.remove(callback); 163 | }, 164 | hasListener: function hasListener (callback) { 165 | return this._events.has(callback); 166 | }, 167 | hasListeners: function hasListeners () { 168 | return !!this._events.length; 169 | }, 170 | _execute: function (args) { 171 | for (let ev of this._events) { 172 | ev.apply(unsafeWindow, cloneInto(args, unsafeWindow)); 173 | } 174 | } 175 | }); 176 | 177 | })(); 178 | JETPACK.API_DEFINITIONS = {"alarms":{"functions":[{"name":"create"},{"name":"get","successCallbackIndex":1},{"name":"getAll","successCallbackIndex":0},{"name":"clear","successCallbackIndex":1},{"name":"clearAll","successCallbackIndex":0}],"events":["onAlarm"]},"bookmarks":{"functions":[{"name":"get","successCallbackIndex":1},{"name":"getChildren","successCallbackIndex":1},{"name":"getRecent","successCallbackIndex":1},{"name":"getTree","successCallbackIndex":0},{"name":"getSubTree","successCallbackIndex":1},{"name":"search","successCallbackIndex":1},{"name":"create","successCallbackIndex":1},{"name":"move","successCallbackIndex":2},{"name":"update","successCallbackIndex":2},{"name":"remove","successCallbackIndex":1},{"name":"removeTree","successCallbackIndex":1},{"name":"import","successCallbackIndex":0},{"name":"export","successCallbackIndex":0}],"events":["onCreated","onRemoved","onChanged","onMoved","onChildrenReordered","onImportBegan","onImportEnded"],"properties":{"MAX_WRITE_OPERATIONS_PER_HOUR":{"value":1000000},"MAX_SUSTAINED_WRITE_OPERATIONS_PER_MINUTE":{"value":1000000}}},"browserAction":{"functions":[{"name":"setTitle"},{"name":"getTitle","successCallbackIndex":1},{"name":"setIcon","successCallbackIndex":1},{"name":"setPopup"},{"name":"getPopup","successCallbackIndex":1},{"name":"setBadgeText"},{"name":"getBadgeText","successCallbackIndex":1},{"name":"setBadgeBackgroundColor"},{"name":"getBadgeBackgroundColor","successCallbackIndex":1},{"name":"enable"},{"name":"disable"},{"name":"openPopup","successCallbackIndex":0}],"events":["onClicked"]},"browsingData":{"functions":[{"name":"settings","successCallbackIndex":0},{"name":"remove","successCallbackIndex":2},{"name":"removeAppcache","successCallbackIndex":1},{"name":"removeCache","successCallbackIndex":1},{"name":"removeCookies","successCallbackIndex":1},{"name":"removeDownloads","successCallbackIndex":1},{"name":"removeFileSystems","successCallbackIndex":1},{"name":"removeFormData","successCallbackIndex":1},{"name":"removeHistory","successCallbackIndex":1},{"name":"removeIndexedDB","successCallbackIndex":1},{"name":"removeLocalStorage","successCallbackIndex":1},{"name":"removePluginData","successCallbackIndex":1},{"name":"removePasswords","successCallbackIndex":1},{"name":"removeWebSQL","successCallbackIndex":1}],"events":[]},"commands":{"functions":[{"name":"getAll","successCallbackIndex":0}],"events":["onCommand"]},"contentSettings":{"functions":[],"events":[],"types":{"ContentSetting":{"functions":[{"name":"clear","successCallbackIndex":1},{"name":"get","successCallbackIndex":1},{"name":"set","successCallbackIndex":1},{"name":"getResourceIdentifiers","successCallbackIndex":0}],"events":[]}},"properties":{"cookies":{"class":"ContentSetting"},"images":{"class":"ContentSetting"},"javascript":{"class":"ContentSetting"},"location":{"class":"ContentSetting"},"plugins":{"class":"ContentSetting"},"popups":{"class":"ContentSetting"},"notifications":{"class":"ContentSetting"},"fullscreen":{"class":"ContentSetting"},"mouselock":{"class":"ContentSetting"},"unsandboxedPlugins":{"class":"ContentSetting"},"automaticDownloads":{"class":"ContentSetting"}}},"contextMenus":{"functions":[{"name":"create","successCallbackIndex":1},{"name":"update","successCallbackIndex":2},{"name":"remove","successCallbackIndex":1},{"name":"removeAll","successCallbackIndex":0}],"events":["onClicked"],"properties":{"ACTION_MENU_TOP_LEVEL_LIMIT":{"value":6}}},"cookies":{"functions":[{"name":"get","successCallbackIndex":1},{"name":"getAll","successCallbackIndex":1},{"name":"set","successCallbackIndex":1},{"name":"remove","successCallbackIndex":1},{"name":"getAllCookieStores","successCallbackIndex":0}],"events":["onChanged"]},"debugger":{"functions":[{"name":"attach","successCallbackIndex":2},{"name":"detach","successCallbackIndex":1},{"name":"sendCommand","successCallbackIndex":3},{"name":"getTargets","successCallbackIndex":0}],"events":["onEvent","onDetach"]},"declarativeContent":{"functions":[],"events":["onPageChanged"]},"desktopCapture":{"functions":[{"name":"chooseDesktopMedia","successCallbackIndex":2},{"name":"cancelChooseDesktopMedia"}],"events":[]},"devtools.inspectedWindow":{"functions":[{"name":"eval","successCallbackIndex":2},{"name":"reload"},{"name":"getResources","successCallbackIndex":0}],"events":["onResourceAdded","onResourceContentCommitted"],"properties":{"tabId":{"getter":true}}},"devtools.network":{"functions":[{"name":"getHAR","successCallbackIndex":0}],"events":["onRequestFinished","onNavigated"]},"devtools.panels":{"functions":[{"name":"create","successCallbackIndex":3},{"name":"setOpenResourceHandler","successCallbackIndex":0},{"name":"openResource","successCallbackIndex":2}],"events":[],"types":{"ElementsPanel":{"functions":[{"name":"createSidebarPane","successCallbackIndex":1}],"events":["onSelectionChanged"]},"SourcesPanel":{"functions":[{"name":"createSidebarPane","successCallbackIndex":1}],"events":["onSelectionChanged"]}},"properties":{"elements":{"class":"ElementsPanel"},"sources":{"class":"SourcesPanel"}}},"downloads":{"functions":[{"name":"download","successCallbackIndex":1},{"name":"search","successCallbackIndex":1},{"name":"pause","successCallbackIndex":1},{"name":"resume","successCallbackIndex":1},{"name":"cancel","successCallbackIndex":1},{"name":"getFileIcon","successCallbackIndex":2},{"name":"open"},{"name":"show"},{"name":"showDefaultFolder"},{"name":"erase","successCallbackIndex":1},{"name":"removeFile","successCallbackIndex":1},{"name":"acceptDanger","successCallbackIndex":1},{"name":"drag"},{"name":"setShelfEnabled"}],"events":["onCreated","onErased","onChanged","onDeterminingFilename"]},"events":{"functions":[],"events":[]},"extension":{"functions":[{"name":"sendRequest"},{"name":"getURL"},{"name":"getViews"},{"name":"getBackgroundPage"},{"name":"getExtensionTabs"},{"name":"isAllowedIncognitoAccess","successCallbackIndex":0},{"name":"isAllowedFileSchemeAccess","successCallbackIndex":0},{"name":"setUpdateUrlData"}],"events":["onRequest","onRequestExternal"],"properties":{"lastError":{"getter":true,"properties":{"message":{"getter":true}}},"inIncognitoContext":{"getter":true}}},"extensionTypes":{"functions":[],"events":[]},"fontSettings":{"functions":[{"name":"clearFont","successCallbackIndex":1},{"name":"getFont","successCallbackIndex":1},{"name":"setFont","successCallbackIndex":1},{"name":"getFontList","successCallbackIndex":0},{"name":"clearDefaultFontSize","successCallbackIndex":1},{"name":"getDefaultFontSize","successCallbackIndex":1},{"name":"setDefaultFontSize","successCallbackIndex":1},{"name":"clearDefaultFixedFontSize","successCallbackIndex":1},{"name":"getDefaultFixedFontSize","successCallbackIndex":1},{"name":"setDefaultFixedFontSize","successCallbackIndex":1},{"name":"clearMinimumFontSize","successCallbackIndex":1},{"name":"getMinimumFontSize","successCallbackIndex":1},{"name":"setMinimumFontSize","successCallbackIndex":1}],"events":["onFontChanged","onDefaultFontSizeChanged","onDefaultFixedFontSizeChanged","onMinimumFontSizeChanged"]},"gcm":{"functions":[{"name":"register","successCallbackIndex":1},{"name":"unregister","successCallbackIndex":0},{"name":"send","successCallbackIndex":1}],"events":["onMessage","onMessagesDeleted","onSendError"],"properties":{"MAX_MESSAGE_SIZE":{"value":4096}}},"history":{"functions":[{"name":"search","successCallbackIndex":1},{"name":"getVisits","successCallbackIndex":1},{"name":"addUrl","successCallbackIndex":1},{"name":"deleteUrl","successCallbackIndex":1},{"name":"deleteRange","successCallbackIndex":1},{"name":"deleteAll","successCallbackIndex":0}],"events":["onVisited","onVisitRemoved"]},"i18n":{"functions":[{"name":"getAcceptLanguages","successCallbackIndex":0},{"name":"getMessage"},{"name":"getUILanguage"}],"events":[]},"identity":{"functions":[{"name":"getAccounts","successCallbackIndex":0},{"name":"getAuthToken","successCallbackIndex":1},{"name":"getProfileUserInfo","successCallbackIndex":0},{"name":"removeCachedAuthToken","successCallbackIndex":1},{"name":"launchWebAuthFlow","successCallbackIndex":1},{"name":"getRedirectURL"}],"events":["onSignInChanged"]},"idle":{"functions":[{"name":"queryState","successCallbackIndex":1},{"name":"setDetectionInterval"}],"events":["onStateChanged"]},"management":{"functions":[{"name":"getAll","successCallbackIndex":0},{"name":"get","successCallbackIndex":1},{"name":"getSelf","successCallbackIndex":0},{"name":"getPermissionWarningsById","successCallbackIndex":1},{"name":"getPermissionWarningsByManifest","successCallbackIndex":1},{"name":"setEnabled","successCallbackIndex":2},{"name":"uninstall","successCallbackIndex":2},{"name":"uninstallSelf","successCallbackIndex":1},{"name":"launchApp","successCallbackIndex":1},{"name":"createAppShortcut","successCallbackIndex":1},{"name":"setLaunchType","successCallbackIndex":2},{"name":"generateAppForLink","successCallbackIndex":2}],"events":["onInstalled","onUninstalled","onEnabled","onDisabled"]},"notifications":{"functions":[{"name":"create","successCallbackIndex":2},{"name":"update","successCallbackIndex":2},{"name":"clear","successCallbackIndex":1},{"name":"getAll","successCallbackIndex":0},{"name":"getPermissionLevel","successCallbackIndex":0}],"events":["onClosed","onClicked","onButtonClicked","onPermissionLevelChanged","onShowSettings"]},"omnibox":{"functions":[{"name":"sendSuggestions"},{"name":"setDefaultSuggestion"}],"events":["onInputStarted","onInputChanged","onInputEntered","onInputCancelled"]},"pageAction":{"functions":[{"name":"show"},{"name":"hide"},{"name":"setTitle"},{"name":"getTitle","successCallbackIndex":1},{"name":"setIcon","successCallbackIndex":1},{"name":"setPopup"},{"name":"getPopup","successCallbackIndex":1}],"events":["onClicked"]},"pageCapture":{"functions":[{"name":"saveAsMHTML","successCallbackIndex":1}],"events":[]},"permissions":{"functions":[{"name":"getAll","successCallbackIndex":0},{"name":"contains","successCallbackIndex":1},{"name":"request","successCallbackIndex":1},{"name":"remove","successCallbackIndex":1}],"events":["onAdded","onRemoved"]},"power":{"functions":[{"name":"requestKeepAwake"},{"name":"releaseKeepAwake"}],"events":[]},"privacy":{"functions":[],"events":[],"properties":{"network":{"container":true,"properties":{"networkPredictionEnabled":{"class":"types.ChromeSetting"},"webRTCMultipleRoutesEnabled":{"class":"types.ChromeSetting"}}},"services":{"container":true,"properties":{"alternateErrorPagesEnabled":{"class":"types.ChromeSetting"},"autofillEnabled":{"class":"types.ChromeSetting"},"hotwordSearchEnabled":{"class":"types.ChromeSetting"},"passwordSavingEnabled":{"class":"types.ChromeSetting"},"safeBrowsingEnabled":{"class":"types.ChromeSetting"},"safeBrowsingExtendedReportingEnabled":{"class":"types.ChromeSetting"},"searchSuggestEnabled":{"class":"types.ChromeSetting"},"spellingServiceEnabled":{"class":"types.ChromeSetting"},"translationServiceEnabled":{"class":"types.ChromeSetting"}}},"websites":{"container":true,"properties":{"thirdPartyCookiesAllowed":{"class":"types.ChromeSetting"},"hyperlinkAuditingEnabled":{"class":"types.ChromeSetting"},"referrersEnabled":{"class":"types.ChromeSetting"},"protectedContentEnabled":{"class":"types.ChromeSetting"}}}}},"proxy":{"functions":[],"events":["onProxyError"],"properties":{"settings":{"class":"types.ChromeSetting"}}},"runtime":{"functions":[{"name":"getBackgroundPage","successCallbackIndex":0},{"name":"openOptionsPage","successCallbackIndex":0},{"name":"getManifest"},{"name":"getURL"},{"name":"setUninstallURL"},{"name":"reload"},{"name":"requestUpdateCheck","successCallbackIndex":0},{"name":"restart"},{"name":"connect"},{"name":"connectNative"},{"name":"sendMessage"},{"name":"sendNativeMessage"},{"name":"getPlatformInfo","successCallbackIndex":0},{"name":"getPackageDirectoryEntry","successCallbackIndex":0}],"events":["onStartup","onInstalled","onSuspend","onSuspendCanceled","onUpdateAvailable","onBrowserUpdateAvailable","onConnect","onConnectExternal","onMessage","onMessageExternal","onRestartRequired"],"properties":{"lastError":{"getter":true,"properties":{"message":{"getter":true}}},"id":{"getter":true}}},"sessions":{"functions":[{"name":"getRecentlyClosed","successCallbackIndex":1},{"name":"getDevices","successCallbackIndex":1},{"name":"restore","successCallbackIndex":1}],"events":["onChanged"],"properties":{"MAX_SESSION_RESULTS":{"value":25}}},"storage":{"functions":[],"events":["onChanged"],"types":{"StorageArea":{"functions":[{"name":"get","successCallbackIndex":1},{"name":"getBytesInUse","successCallbackIndex":1},{"name":"set","successCallbackIndex":1},{"name":"remove","successCallbackIndex":1},{"name":"clear","successCallbackIndex":0}],"events":[]}},"properties":{"sync":{"class":"StorageArea","properties":{"QUOTA_BYTES":{"value":102400},"QUOTA_BYTES_PER_ITEM":{"value":8192},"MAX_ITEMS":{"value":512},"MAX_WRITE_OPERATIONS_PER_HOUR":{"value":1800},"MAX_WRITE_OPERATIONS_PER_MINUTE":{"value":120},"MAX_SUSTAINED_WRITE_OPERATIONS_PER_MINUTE":{"value":1000000}}},"local":{"class":"StorageArea","properties":{"QUOTA_BYTES":{"value":5242880}}},"managed":{"class":"StorageArea"}}},"system.cpu":{"functions":[{"name":"getInfo","successCallbackIndex":0}],"events":[]},"system.memory":{"functions":[{"name":"getInfo","successCallbackIndex":0}],"events":[]},"system.storage":{"functions":[{"name":"getInfo","successCallbackIndex":0},{"name":"ejectDevice","successCallbackIndex":1},{"name":"getAvailableCapacity","successCallbackIndex":1}],"events":["onAttached","onDetached"]},"tabCapture":{"functions":[{"name":"capture","successCallbackIndex":1},{"name":"getCapturedTabs","successCallbackIndex":0}],"events":["onStatusChanged"]},"tabs":{"functions":[{"name":"get","successCallbackIndex":1},{"name":"getCurrent","successCallbackIndex":0},{"name":"connect"},{"name":"sendRequest"},{"name":"sendMessage"},{"name":"getSelected","successCallbackIndex":1},{"name":"getAllInWindow","successCallbackIndex":1},{"name":"create","successCallbackIndex":1},{"name":"duplicate","successCallbackIndex":1},{"name":"query","successCallbackIndex":1},{"name":"highlight","successCallbackIndex":1},{"name":"update","successCallbackIndex":2},{"name":"move","successCallbackIndex":2},{"name":"reload","successCallbackIndex":2},{"name":"remove","successCallbackIndex":1},{"name":"detectLanguage","successCallbackIndex":1},{"name":"captureVisibleTab","successCallbackIndex":2},{"name":"executeScript","successCallbackIndex":2},{"name":"insertCSS","successCallbackIndex":2},{"name":"setZoom","successCallbackIndex":2},{"name":"getZoom","successCallbackIndex":1},{"name":"setZoomSettings","successCallbackIndex":2},{"name":"getZoomSettings","successCallbackIndex":1}],"events":["onCreated","onUpdated","onMoved","onSelectionChanged","onActiveChanged","onActivated","onHighlightChanged","onHighlighted","onDetached","onAttached","onRemoved","onReplaced","onZoomChange"]},"topSites":{"functions":[{"name":"get","successCallbackIndex":0}],"events":[]},"tts":{"functions":[{"name":"speak","successCallbackIndex":2},{"name":"stop"},{"name":"pause"},{"name":"resume"},{"name":"isSpeaking","successCallbackIndex":0},{"name":"getVoices","successCallbackIndex":0}],"events":["onEvent"]},"ttsEngine":{"functions":[{"name":"sendTtsEvent"}],"events":["onSpeak","onStop","onPause","onResume"]},"types":{"functions":[],"events":[],"types":{"ChromeSetting":{"functions":[{"name":"get","successCallbackIndex":1},{"name":"set","successCallbackIndex":1},{"name":"clear","successCallbackIndex":1}],"events":["onChange"]}}},"wallpaper":{"functions":[{"name":"setWallpaper","successCallbackIndex":1}],"events":[]},"webNavigation":{"functions":[{"name":"getFrame","successCallbackIndex":1},{"name":"getAllFrames","successCallbackIndex":1}],"events":["onBeforeNavigate","onCommitted","onDOMContentLoaded","onCompleted","onErrorOccurred","onCreatedNavigationTarget","onReferenceFragmentUpdated","onTabReplaced","onHistoryStateUpdated"]},"webRequest":{"functions":[{"name":"handlerBehaviorChanged","successCallbackIndex":0}],"events":["onBeforeRequest","onBeforeSendHeaders","onSendHeaders","onHeadersReceived","onAuthRequired","onResponseStarted","onBeforeRedirect","onCompleted","onErrorOccurred"],"properties":{"MAX_HANDLER_BEHAVIOR_CHANGED_CALLS_PER_10_MINUTES":{"value":20}}},"webstore":{"functions":[{"name":"install","successCallbackIndex":1,"failureCallbackIndex":2}],"events":["onInstallStageChanged","onDownloadProgress"]},"windows":{"functions":[{"name":"get","successCallbackIndex":2},{"name":"getCurrent","successCallbackIndex":1},{"name":"getLastFocused","successCallbackIndex":1},{"name":"getAll","successCallbackIndex":1},{"name":"create","successCallbackIndex":1},{"name":"update","successCallbackIndex":2},{"name":"remove","successCallbackIndex":1}],"events":["onCreated","onRemoved","onFocusChanged"],"properties":{"WINDOW_ID_NONE":{"value":-1},"WINDOW_ID_CURRENT":{"value":-2}}}} 179 | /** 180 | * The last piece injected into the content, takes the definition stub 181 | * from ./definitions/stub.json (stored at self.JETPACK.API_DEFINITIONS) 182 | * and creates the API objects and function/event hooks in the proper scope. 183 | */ 184 | let definitions = JETPACK.API_DEFINITIONS; 185 | let chrome = unsafeWindow.chrome = createObjectIn(unsafeWindow); 186 | let apiTypes = {}; 187 | 188 | Object.keys(definitions).map(namespace => { 189 | // First, register all the types upfront. 190 | bindTypes(namespace, definitions[namespace].types); 191 | return namespace; 192 | }).forEach(namespace => { 193 | // Then bind everything else 194 | let def = definitions[namespace]; 195 | bindDefinition(namespace, def); 196 | }); 197 | 198 | function bindDefinition (namespace, def) { 199 | bindFunctions(namespace, def.functions); 200 | bindEvents(namespace, def.events); 201 | bindProperties(namespace, def.properties); 202 | } 203 | 204 | /** 205 | * Returns the corresponding object related to a string name of 206 | * a namespace. Converts "devtools.inspectedWindow", and returns 207 | * the object found at `unsafeWindow.chrome.devtools.inspectedWindow`, creating 208 | * objects along the path if needed in the unsafeWindow scope. 209 | */ 210 | function getNamespace (namespace) { 211 | let namespaces = namespace.split("."); 212 | let parent = chrome; 213 | for (let name of namespaces) { 214 | parent[name] = parent[name] || createObjectIn(unsafeWindow); 215 | parent = parent[name]; 216 | } 217 | return parent; 218 | } 219 | 220 | /** 221 | * Binds functions to their parent namespace, creating the namespace 222 | * itself if it does not exist. 223 | */ 224 | function bindFunctions (namespace, functions=[]) { 225 | let ns = getNamespace(namespace); 226 | functions.forEach(fnDef => { 227 | let { name: method, successCallbackIndex: success, failureCallbackIndex: failure } = fnDef; 228 | let name = `${namespace}.${method}`; 229 | exportFunction(JETPACK.RPC.bind(null, { name, success, failure }), ns, { defineAs: method }); 230 | }); 231 | } 232 | 233 | /** 234 | * Stores type information if used elsewhere. Only used for 235 | * contentSettings.ContentSetting, storage.StorageArea, and types.ChromeSetting. 236 | * `bindProperties` ends up using this data. 237 | */ 238 | function bindTypes (namespace, types={}) { 239 | Object.keys(types).forEach(type=> { 240 | let typeName = `${namespace}.${type}`; 241 | apiTypes[typeName] = types[type]; 242 | }); 243 | } 244 | 245 | function bindEvents (namespace, events=[]) { 246 | let ns = getNamespace(namespace); 247 | events.forEach(name => { 248 | // EventManager.getEvent caches the event instance, 249 | // so we can lazily load event objects 250 | Object.defineProperty(ns, name, { 251 | get: exportFunction(() => JETPACK.EventManager.getEvent(`${namespace}.${name}`).getShadow(), ns) 252 | }); 253 | }); 254 | } 255 | 256 | function bindProperties (namespace, properties={}) { 257 | Object.keys(properties).forEach(prop => { 258 | let def = properties[prop]; 259 | if (def.getter) { 260 | // Not yet implemented 261 | } 262 | // For containers, recursively bind its children functions/events/props, etc 263 | else if (def.container) { 264 | bindDefinition(`${namespace}.${prop}`, def); 265 | } 266 | // If this object a class, (storage.StorageArea, etc), implement its props and functions 267 | // as well 268 | else if (def.class) { 269 | // If type isn't scoped, it means it's part of the same namespace. 270 | let typeName = def.class.indexOf(".") === -1 ? `${namespace}.${def.class}` : def.class; 271 | let typeDef = apiTypes[typeName]; 272 | if (!typeDef) { 273 | throw new Error(`No type definition found for ${def.class} in ${namespace}.`); 274 | } 275 | bindDefinition(`${namespace}.${prop}`, typeDef); 276 | 277 | // Also bind the original definition, as it can contain additional 278 | // properties 279 | bindDefinition(`${namespace}.${prop}`, def); 280 | } 281 | // If this is just a value, probably an enum or constant -- just set 282 | else if (def.value != null) { 283 | getNamespace(namespace)[prop] = def.value; 284 | } 285 | }); 286 | } 287 | -------------------------------------------------------------------------------- /data/default-background.html: -------------------------------------------------------------------------------- 1 | 3 | 4 |
5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /definitions/stubs.json: -------------------------------------------------------------------------------- 1 | { 2 | "alarms": { 3 | "functions": [ 4 | { 5 | "name": "create" 6 | }, 7 | { 8 | "name": "get", 9 | "successCallbackIndex": 1 10 | }, 11 | { 12 | "name": "getAll", 13 | "successCallbackIndex": 0 14 | }, 15 | { 16 | "name": "clear", 17 | "successCallbackIndex": 1 18 | }, 19 | { 20 | "name": "clearAll", 21 | "successCallbackIndex": 0 22 | } 23 | ], 24 | "events": [ 25 | "onAlarm" 26 | ] 27 | }, 28 | "bookmarks": { 29 | "functions": [ 30 | { 31 | "name": "get", 32 | "successCallbackIndex": 1 33 | }, 34 | { 35 | "name": "getChildren", 36 | "successCallbackIndex": 1 37 | }, 38 | { 39 | "name": "getRecent", 40 | "successCallbackIndex": 1 41 | }, 42 | { 43 | "name": "getTree", 44 | "successCallbackIndex": 0 45 | }, 46 | { 47 | "name": "getSubTree", 48 | "successCallbackIndex": 1 49 | }, 50 | { 51 | "name": "search", 52 | "successCallbackIndex": 1 53 | }, 54 | { 55 | "name": "create", 56 | "successCallbackIndex": 1 57 | }, 58 | { 59 | "name": "move", 60 | "successCallbackIndex": 2 61 | }, 62 | { 63 | "name": "update", 64 | "successCallbackIndex": 2 65 | }, 66 | { 67 | "name": "remove", 68 | "successCallbackIndex": 1 69 | }, 70 | { 71 | "name": "removeTree", 72 | "successCallbackIndex": 1 73 | }, 74 | { 75 | "name": "import", 76 | "successCallbackIndex": 0 77 | }, 78 | { 79 | "name": "export", 80 | "successCallbackIndex": 0 81 | } 82 | ], 83 | "events": [ 84 | "onCreated", 85 | "onRemoved", 86 | "onChanged", 87 | "onMoved", 88 | "onChildrenReordered", 89 | "onImportBegan", 90 | "onImportEnded" 91 | ], 92 | "properties": { 93 | "MAX_WRITE_OPERATIONS_PER_HOUR": { 94 | "value": 1000000 95 | }, 96 | "MAX_SUSTAINED_WRITE_OPERATIONS_PER_MINUTE": { 97 | "value": 1000000 98 | } 99 | } 100 | }, 101 | "browserAction": { 102 | "functions": [ 103 | { 104 | "name": "setTitle" 105 | }, 106 | { 107 | "name": "getTitle", 108 | "successCallbackIndex": 1 109 | }, 110 | { 111 | "name": "setIcon", 112 | "successCallbackIndex": 1 113 | }, 114 | { 115 | "name": "setPopup" 116 | }, 117 | { 118 | "name": "getPopup", 119 | "successCallbackIndex": 1 120 | }, 121 | { 122 | "name": "setBadgeText" 123 | }, 124 | { 125 | "name": "getBadgeText", 126 | "successCallbackIndex": 1 127 | }, 128 | { 129 | "name": "setBadgeBackgroundColor" 130 | }, 131 | { 132 | "name": "getBadgeBackgroundColor", 133 | "successCallbackIndex": 1 134 | }, 135 | { 136 | "name": "enable" 137 | }, 138 | { 139 | "name": "disable" 140 | }, 141 | { 142 | "name": "openPopup", 143 | "successCallbackIndex": 0 144 | } 145 | ], 146 | "events": [ 147 | "onClicked" 148 | ] 149 | }, 150 | "browsingData": { 151 | "functions": [ 152 | { 153 | "name": "settings", 154 | "successCallbackIndex": 0 155 | }, 156 | { 157 | "name": "remove", 158 | "successCallbackIndex": 2 159 | }, 160 | { 161 | "name": "removeAppcache", 162 | "successCallbackIndex": 1 163 | }, 164 | { 165 | "name": "removeCache", 166 | "successCallbackIndex": 1 167 | }, 168 | { 169 | "name": "removeCookies", 170 | "successCallbackIndex": 1 171 | }, 172 | { 173 | "name": "removeDownloads", 174 | "successCallbackIndex": 1 175 | }, 176 | { 177 | "name": "removeFileSystems", 178 | "successCallbackIndex": 1 179 | }, 180 | { 181 | "name": "removeFormData", 182 | "successCallbackIndex": 1 183 | }, 184 | { 185 | "name": "removeHistory", 186 | "successCallbackIndex": 1 187 | }, 188 | { 189 | "name": "removeIndexedDB", 190 | "successCallbackIndex": 1 191 | }, 192 | { 193 | "name": "removeLocalStorage", 194 | "successCallbackIndex": 1 195 | }, 196 | { 197 | "name": "removePluginData", 198 | "successCallbackIndex": 1 199 | }, 200 | { 201 | "name": "removePasswords", 202 | "successCallbackIndex": 1 203 | }, 204 | { 205 | "name": "removeWebSQL", 206 | "successCallbackIndex": 1 207 | } 208 | ], 209 | "events": [] 210 | }, 211 | "commands": { 212 | "functions": [ 213 | { 214 | "name": "getAll", 215 | "successCallbackIndex": 0 216 | } 217 | ], 218 | "events": [ 219 | "onCommand" 220 | ] 221 | }, 222 | "contentSettings": { 223 | "functions": [], 224 | "events": [], 225 | "types": { 226 | "ContentSetting": { 227 | "functions": [ 228 | { 229 | "name": "clear", 230 | "successCallbackIndex": 1 231 | }, 232 | { 233 | "name": "get", 234 | "successCallbackIndex": 1 235 | }, 236 | { 237 | "name": "set", 238 | "successCallbackIndex": 1 239 | }, 240 | { 241 | "name": "getResourceIdentifiers", 242 | "successCallbackIndex": 0 243 | } 244 | ], 245 | "events": [] 246 | } 247 | }, 248 | "properties": { 249 | "cookies": { 250 | "class": "ContentSetting" 251 | }, 252 | "images": { 253 | "class": "ContentSetting" 254 | }, 255 | "javascript": { 256 | "class": "ContentSetting" 257 | }, 258 | "location": { 259 | "class": "ContentSetting" 260 | }, 261 | "plugins": { 262 | "class": "ContentSetting" 263 | }, 264 | "popups": { 265 | "class": "ContentSetting" 266 | }, 267 | "notifications": { 268 | "class": "ContentSetting" 269 | }, 270 | "fullscreen": { 271 | "class": "ContentSetting" 272 | }, 273 | "mouselock": { 274 | "class": "ContentSetting" 275 | }, 276 | "unsandboxedPlugins": { 277 | "class": "ContentSetting" 278 | }, 279 | "automaticDownloads": { 280 | "class": "ContentSetting" 281 | } 282 | } 283 | }, 284 | "contextMenus": { 285 | "functions": [ 286 | { 287 | "name": "create", 288 | "successCallbackIndex": 1 289 | }, 290 | { 291 | "name": "update", 292 | "successCallbackIndex": 2 293 | }, 294 | { 295 | "name": "remove", 296 | "successCallbackIndex": 1 297 | }, 298 | { 299 | "name": "removeAll", 300 | "successCallbackIndex": 0 301 | } 302 | ], 303 | "events": [ 304 | "onClicked" 305 | ], 306 | "properties": { 307 | "ACTION_MENU_TOP_LEVEL_LIMIT": { 308 | "value": 6 309 | } 310 | } 311 | }, 312 | "cookies": { 313 | "functions": [ 314 | { 315 | "name": "get", 316 | "successCallbackIndex": 1 317 | }, 318 | { 319 | "name": "getAll", 320 | "successCallbackIndex": 1 321 | }, 322 | { 323 | "name": "set", 324 | "successCallbackIndex": 1 325 | }, 326 | { 327 | "name": "remove", 328 | "successCallbackIndex": 1 329 | }, 330 | { 331 | "name": "getAllCookieStores", 332 | "successCallbackIndex": 0 333 | } 334 | ], 335 | "events": [ 336 | "onChanged" 337 | ] 338 | }, 339 | "debugger": { 340 | "functions": [ 341 | { 342 | "name": "attach", 343 | "successCallbackIndex": 2 344 | }, 345 | { 346 | "name": "detach", 347 | "successCallbackIndex": 1 348 | }, 349 | { 350 | "name": "sendCommand", 351 | "successCallbackIndex": 3 352 | }, 353 | { 354 | "name": "getTargets", 355 | "successCallbackIndex": 0 356 | } 357 | ], 358 | "events": [ 359 | "onEvent", 360 | "onDetach" 361 | ] 362 | }, 363 | "declarativeContent": { 364 | "functions": [], 365 | "events": [ 366 | "onPageChanged" 367 | ] 368 | }, 369 | "desktopCapture": { 370 | "functions": [ 371 | { 372 | "name": "chooseDesktopMedia", 373 | "successCallbackIndex": 2 374 | }, 375 | { 376 | "name": "cancelChooseDesktopMedia" 377 | } 378 | ], 379 | "events": [] 380 | }, 381 | "devtools.inspectedWindow": { 382 | "functions": [ 383 | { 384 | "name": "eval", 385 | "successCallbackIndex": 2 386 | }, 387 | { 388 | "name": "reload" 389 | }, 390 | { 391 | "name": "getResources", 392 | "successCallbackIndex": 0 393 | } 394 | ], 395 | "events": [ 396 | "onResourceAdded", 397 | "onResourceContentCommitted" 398 | ], 399 | "properties": { 400 | "tabId": { 401 | "getter": true 402 | } 403 | } 404 | }, 405 | "devtools.network": { 406 | "functions": [ 407 | { 408 | "name": "getHAR", 409 | "successCallbackIndex": 0 410 | } 411 | ], 412 | "events": [ 413 | "onRequestFinished", 414 | "onNavigated" 415 | ] 416 | }, 417 | "devtools.panels": { 418 | "functions": [ 419 | { 420 | "name": "create", 421 | "successCallbackIndex": 3 422 | }, 423 | { 424 | "name": "setOpenResourceHandler", 425 | "successCallbackIndex": 0 426 | }, 427 | { 428 | "name": "openResource", 429 | "successCallbackIndex": 2 430 | } 431 | ], 432 | "events": [], 433 | "types": { 434 | "ElementsPanel": { 435 | "functions": [ 436 | { 437 | "name": "createSidebarPane", 438 | "successCallbackIndex": 1 439 | } 440 | ], 441 | "events": [ 442 | "onSelectionChanged" 443 | ] 444 | }, 445 | "SourcesPanel": { 446 | "functions": [ 447 | { 448 | "name": "createSidebarPane", 449 | "successCallbackIndex": 1 450 | } 451 | ], 452 | "events": [ 453 | "onSelectionChanged" 454 | ] 455 | } 456 | }, 457 | "properties": { 458 | "elements": { 459 | "class": "ElementsPanel" 460 | }, 461 | "sources": { 462 | "class": "SourcesPanel" 463 | } 464 | } 465 | }, 466 | "downloads": { 467 | "functions": [ 468 | { 469 | "name": "download", 470 | "successCallbackIndex": 1 471 | }, 472 | { 473 | "name": "search", 474 | "successCallbackIndex": 1 475 | }, 476 | { 477 | "name": "pause", 478 | "successCallbackIndex": 1 479 | }, 480 | { 481 | "name": "resume", 482 | "successCallbackIndex": 1 483 | }, 484 | { 485 | "name": "cancel", 486 | "successCallbackIndex": 1 487 | }, 488 | { 489 | "name": "getFileIcon", 490 | "successCallbackIndex": 2 491 | }, 492 | { 493 | "name": "open" 494 | }, 495 | { 496 | "name": "show" 497 | }, 498 | { 499 | "name": "showDefaultFolder" 500 | }, 501 | { 502 | "name": "erase", 503 | "successCallbackIndex": 1 504 | }, 505 | { 506 | "name": "removeFile", 507 | "successCallbackIndex": 1 508 | }, 509 | { 510 | "name": "acceptDanger", 511 | "successCallbackIndex": 1 512 | }, 513 | { 514 | "name": "drag" 515 | }, 516 | { 517 | "name": "setShelfEnabled" 518 | } 519 | ], 520 | "events": [ 521 | "onCreated", 522 | "onErased", 523 | "onChanged", 524 | "onDeterminingFilename" 525 | ] 526 | }, 527 | "events": { 528 | "functions": [], 529 | "events": [] 530 | }, 531 | "extension": { 532 | "functions": [ 533 | { 534 | "name": "sendRequest" 535 | }, 536 | { 537 | "name": "getURL" 538 | }, 539 | { 540 | "name": "getViews" 541 | }, 542 | { 543 | "name": "getBackgroundPage" 544 | }, 545 | { 546 | "name": "getExtensionTabs" 547 | }, 548 | { 549 | "name": "isAllowedIncognitoAccess", 550 | "successCallbackIndex": 0 551 | }, 552 | { 553 | "name": "isAllowedFileSchemeAccess", 554 | "successCallbackIndex": 0 555 | }, 556 | { 557 | "name": "setUpdateUrlData" 558 | } 559 | ], 560 | "events": [ 561 | "onRequest", 562 | "onRequestExternal" 563 | ], 564 | "properties": { 565 | "lastError": { 566 | "getter": true, 567 | "properties": { 568 | "message": { 569 | "getter": true 570 | } 571 | } 572 | }, 573 | "inIncognitoContext": { 574 | "getter": true 575 | } 576 | } 577 | }, 578 | "extensionTypes": { 579 | "functions": [], 580 | "events": [] 581 | }, 582 | "fontSettings": { 583 | "functions": [ 584 | { 585 | "name": "clearFont", 586 | "successCallbackIndex": 1 587 | }, 588 | { 589 | "name": "getFont", 590 | "successCallbackIndex": 1 591 | }, 592 | { 593 | "name": "setFont", 594 | "successCallbackIndex": 1 595 | }, 596 | { 597 | "name": "getFontList", 598 | "successCallbackIndex": 0 599 | }, 600 | { 601 | "name": "clearDefaultFontSize", 602 | "successCallbackIndex": 1 603 | }, 604 | { 605 | "name": "getDefaultFontSize", 606 | "successCallbackIndex": 1 607 | }, 608 | { 609 | "name": "setDefaultFontSize", 610 | "successCallbackIndex": 1 611 | }, 612 | { 613 | "name": "clearDefaultFixedFontSize", 614 | "successCallbackIndex": 1 615 | }, 616 | { 617 | "name": "getDefaultFixedFontSize", 618 | "successCallbackIndex": 1 619 | }, 620 | { 621 | "name": "setDefaultFixedFontSize", 622 | "successCallbackIndex": 1 623 | }, 624 | { 625 | "name": "clearMinimumFontSize", 626 | "successCallbackIndex": 1 627 | }, 628 | { 629 | "name": "getMinimumFontSize", 630 | "successCallbackIndex": 1 631 | }, 632 | { 633 | "name": "setMinimumFontSize", 634 | "successCallbackIndex": 1 635 | } 636 | ], 637 | "events": [ 638 | "onFontChanged", 639 | "onDefaultFontSizeChanged", 640 | "onDefaultFixedFontSizeChanged", 641 | "onMinimumFontSizeChanged" 642 | ] 643 | }, 644 | "gcm": { 645 | "functions": [ 646 | { 647 | "name": "register", 648 | "successCallbackIndex": 1 649 | }, 650 | { 651 | "name": "unregister", 652 | "successCallbackIndex": 0 653 | }, 654 | { 655 | "name": "send", 656 | "successCallbackIndex": 1 657 | } 658 | ], 659 | "events": [ 660 | "onMessage", 661 | "onMessagesDeleted", 662 | "onSendError" 663 | ], 664 | "properties": { 665 | "MAX_MESSAGE_SIZE": { 666 | "value": 4096 667 | } 668 | } 669 | }, 670 | "history": { 671 | "functions": [ 672 | { 673 | "name": "search", 674 | "successCallbackIndex": 1 675 | }, 676 | { 677 | "name": "getVisits", 678 | "successCallbackIndex": 1 679 | }, 680 | { 681 | "name": "addUrl", 682 | "successCallbackIndex": 1 683 | }, 684 | { 685 | "name": "deleteUrl", 686 | "successCallbackIndex": 1 687 | }, 688 | { 689 | "name": "deleteRange", 690 | "successCallbackIndex": 1 691 | }, 692 | { 693 | "name": "deleteAll", 694 | "successCallbackIndex": 0 695 | } 696 | ], 697 | "events": [ 698 | "onVisited", 699 | "onVisitRemoved" 700 | ] 701 | }, 702 | "i18n": { 703 | "functions": [ 704 | { 705 | "name": "getAcceptLanguages", 706 | "successCallbackIndex": 0 707 | }, 708 | { 709 | "name": "getMessage" 710 | }, 711 | { 712 | "name": "getUILanguage" 713 | } 714 | ], 715 | "events": [] 716 | }, 717 | "identity": { 718 | "functions": [ 719 | { 720 | "name": "getAccounts", 721 | "successCallbackIndex": 0 722 | }, 723 | { 724 | "name": "getAuthToken", 725 | "successCallbackIndex": 1 726 | }, 727 | { 728 | "name": "getProfileUserInfo", 729 | "successCallbackIndex": 0 730 | }, 731 | { 732 | "name": "removeCachedAuthToken", 733 | "successCallbackIndex": 1 734 | }, 735 | { 736 | "name": "launchWebAuthFlow", 737 | "successCallbackIndex": 1 738 | }, 739 | { 740 | "name": "getRedirectURL" 741 | } 742 | ], 743 | "events": [ 744 | "onSignInChanged" 745 | ] 746 | }, 747 | "idle": { 748 | "functions": [ 749 | { 750 | "name": "queryState", 751 | "successCallbackIndex": 1 752 | }, 753 | { 754 | "name": "setDetectionInterval" 755 | } 756 | ], 757 | "events": [ 758 | "onStateChanged" 759 | ] 760 | }, 761 | "management": { 762 | "functions": [ 763 | { 764 | "name": "getAll", 765 | "successCallbackIndex": 0 766 | }, 767 | { 768 | "name": "get", 769 | "successCallbackIndex": 1 770 | }, 771 | { 772 | "name": "getSelf", 773 | "successCallbackIndex": 0 774 | }, 775 | { 776 | "name": "getPermissionWarningsById", 777 | "successCallbackIndex": 1 778 | }, 779 | { 780 | "name": "getPermissionWarningsByManifest", 781 | "successCallbackIndex": 1 782 | }, 783 | { 784 | "name": "setEnabled", 785 | "successCallbackIndex": 2 786 | }, 787 | { 788 | "name": "uninstall", 789 | "successCallbackIndex": 2 790 | }, 791 | { 792 | "name": "uninstallSelf", 793 | "successCallbackIndex": 1 794 | }, 795 | { 796 | "name": "launchApp", 797 | "successCallbackIndex": 1 798 | }, 799 | { 800 | "name": "createAppShortcut", 801 | "successCallbackIndex": 1 802 | }, 803 | { 804 | "name": "setLaunchType", 805 | "successCallbackIndex": 2 806 | }, 807 | { 808 | "name": "generateAppForLink", 809 | "successCallbackIndex": 2 810 | } 811 | ], 812 | "events": [ 813 | "onInstalled", 814 | "onUninstalled", 815 | "onEnabled", 816 | "onDisabled" 817 | ] 818 | }, 819 | "notifications": { 820 | "functions": [ 821 | { 822 | "name": "create", 823 | "successCallbackIndex": 2 824 | }, 825 | { 826 | "name": "update", 827 | "successCallbackIndex": 2 828 | }, 829 | { 830 | "name": "clear", 831 | "successCallbackIndex": 1 832 | }, 833 | { 834 | "name": "getAll", 835 | "successCallbackIndex": 0 836 | }, 837 | { 838 | "name": "getPermissionLevel", 839 | "successCallbackIndex": 0 840 | } 841 | ], 842 | "events": [ 843 | "onClosed", 844 | "onClicked", 845 | "onButtonClicked", 846 | "onPermissionLevelChanged", 847 | "onShowSettings" 848 | ] 849 | }, 850 | "omnibox": { 851 | "functions": [ 852 | { 853 | "name": "sendSuggestions" 854 | }, 855 | { 856 | "name": "setDefaultSuggestion" 857 | } 858 | ], 859 | "events": [ 860 | "onInputStarted", 861 | "onInputChanged", 862 | "onInputEntered", 863 | "onInputCancelled" 864 | ] 865 | }, 866 | "pageAction": { 867 | "functions": [ 868 | { 869 | "name": "show" 870 | }, 871 | { 872 | "name": "hide" 873 | }, 874 | { 875 | "name": "setTitle" 876 | }, 877 | { 878 | "name": "getTitle", 879 | "successCallbackIndex": 1 880 | }, 881 | { 882 | "name": "setIcon", 883 | "successCallbackIndex": 1 884 | }, 885 | { 886 | "name": "setPopup" 887 | }, 888 | { 889 | "name": "getPopup", 890 | "successCallbackIndex": 1 891 | } 892 | ], 893 | "events": [ 894 | "onClicked" 895 | ] 896 | }, 897 | "pageCapture": { 898 | "functions": [ 899 | { 900 | "name": "saveAsMHTML", 901 | "successCallbackIndex": 1 902 | } 903 | ], 904 | "events": [] 905 | }, 906 | "permissions": { 907 | "functions": [ 908 | { 909 | "name": "getAll", 910 | "successCallbackIndex": 0 911 | }, 912 | { 913 | "name": "contains", 914 | "successCallbackIndex": 1 915 | }, 916 | { 917 | "name": "request", 918 | "successCallbackIndex": 1 919 | }, 920 | { 921 | "name": "remove", 922 | "successCallbackIndex": 1 923 | } 924 | ], 925 | "events": [ 926 | "onAdded", 927 | "onRemoved" 928 | ] 929 | }, 930 | "power": { 931 | "functions": [ 932 | { 933 | "name": "requestKeepAwake" 934 | }, 935 | { 936 | "name": "releaseKeepAwake" 937 | } 938 | ], 939 | "events": [] 940 | }, 941 | "privacy": { 942 | "functions": [], 943 | "events": [], 944 | "properties": { 945 | "network": { 946 | "container": true, 947 | "properties": { 948 | "networkPredictionEnabled": { 949 | "class": "types.ChromeSetting" 950 | }, 951 | "webRTCMultipleRoutesEnabled": { 952 | "class": "types.ChromeSetting" 953 | } 954 | } 955 | }, 956 | "services": { 957 | "container": true, 958 | "properties": { 959 | "alternateErrorPagesEnabled": { 960 | "class": "types.ChromeSetting" 961 | }, 962 | "autofillEnabled": { 963 | "class": "types.ChromeSetting" 964 | }, 965 | "hotwordSearchEnabled": { 966 | "class": "types.ChromeSetting" 967 | }, 968 | "passwordSavingEnabled": { 969 | "class": "types.ChromeSetting" 970 | }, 971 | "safeBrowsingEnabled": { 972 | "class": "types.ChromeSetting" 973 | }, 974 | "safeBrowsingExtendedReportingEnabled": { 975 | "class": "types.ChromeSetting" 976 | }, 977 | "searchSuggestEnabled": { 978 | "class": "types.ChromeSetting" 979 | }, 980 | "spellingServiceEnabled": { 981 | "class": "types.ChromeSetting" 982 | }, 983 | "translationServiceEnabled": { 984 | "class": "types.ChromeSetting" 985 | } 986 | } 987 | }, 988 | "websites": { 989 | "container": true, 990 | "properties": { 991 | "thirdPartyCookiesAllowed": { 992 | "class": "types.ChromeSetting" 993 | }, 994 | "hyperlinkAuditingEnabled": { 995 | "class": "types.ChromeSetting" 996 | }, 997 | "referrersEnabled": { 998 | "class": "types.ChromeSetting" 999 | }, 1000 | "protectedContentEnabled": { 1001 | "class": "types.ChromeSetting" 1002 | } 1003 | } 1004 | } 1005 | } 1006 | }, 1007 | "proxy": { 1008 | "functions": [], 1009 | "events": [ 1010 | "onProxyError" 1011 | ], 1012 | "properties": { 1013 | "settings": { 1014 | "class": "types.ChromeSetting" 1015 | } 1016 | } 1017 | }, 1018 | "runtime": { 1019 | "functions": [ 1020 | { 1021 | "name": "getBackgroundPage", 1022 | "successCallbackIndex": 0 1023 | }, 1024 | { 1025 | "name": "openOptionsPage", 1026 | "successCallbackIndex": 0 1027 | }, 1028 | { 1029 | "name": "getManifest" 1030 | }, 1031 | { 1032 | "name": "getURL" 1033 | }, 1034 | { 1035 | "name": "setUninstallURL" 1036 | }, 1037 | { 1038 | "name": "reload" 1039 | }, 1040 | { 1041 | "name": "requestUpdateCheck", 1042 | "successCallbackIndex": 0 1043 | }, 1044 | { 1045 | "name": "restart" 1046 | }, 1047 | { 1048 | "name": "connect" 1049 | }, 1050 | { 1051 | "name": "connectNative" 1052 | }, 1053 | { 1054 | "name": "sendMessage" 1055 | }, 1056 | { 1057 | "name": "sendNativeMessage" 1058 | }, 1059 | { 1060 | "name": "getPlatformInfo", 1061 | "successCallbackIndex": 0 1062 | }, 1063 | { 1064 | "name": "getPackageDirectoryEntry", 1065 | "successCallbackIndex": 0 1066 | } 1067 | ], 1068 | "events": [ 1069 | "onStartup", 1070 | "onInstalled", 1071 | "onSuspend", 1072 | "onSuspendCanceled", 1073 | "onUpdateAvailable", 1074 | "onBrowserUpdateAvailable", 1075 | "onConnect", 1076 | "onConnectExternal", 1077 | "onMessage", 1078 | "onMessageExternal", 1079 | "onRestartRequired" 1080 | ], 1081 | "properties": { 1082 | "lastError": { 1083 | "getter": true, 1084 | "properties": { 1085 | "message": { 1086 | "getter": true 1087 | } 1088 | } 1089 | }, 1090 | "id": { 1091 | "getter": true 1092 | } 1093 | } 1094 | }, 1095 | "sessions": { 1096 | "functions": [ 1097 | { 1098 | "name": "getRecentlyClosed", 1099 | "successCallbackIndex": 1 1100 | }, 1101 | { 1102 | "name": "getDevices", 1103 | "successCallbackIndex": 1 1104 | }, 1105 | { 1106 | "name": "restore", 1107 | "successCallbackIndex": 1 1108 | } 1109 | ], 1110 | "events": [ 1111 | "onChanged" 1112 | ], 1113 | "properties": { 1114 | "MAX_SESSION_RESULTS": { 1115 | "value": 25 1116 | } 1117 | } 1118 | }, 1119 | "storage": { 1120 | "functions": [], 1121 | "events": [ 1122 | "onChanged" 1123 | ], 1124 | "types": { 1125 | "StorageArea": { 1126 | "functions": [ 1127 | { 1128 | "name": "get", 1129 | "successCallbackIndex": 1 1130 | }, 1131 | { 1132 | "name": "getBytesInUse", 1133 | "successCallbackIndex": 1 1134 | }, 1135 | { 1136 | "name": "set", 1137 | "successCallbackIndex": 1 1138 | }, 1139 | { 1140 | "name": "remove", 1141 | "successCallbackIndex": 1 1142 | }, 1143 | { 1144 | "name": "clear", 1145 | "successCallbackIndex": 0 1146 | } 1147 | ], 1148 | "events": [] 1149 | } 1150 | }, 1151 | "properties": { 1152 | "sync": { 1153 | "class": "StorageArea", 1154 | "properties": { 1155 | "QUOTA_BYTES": { 1156 | "value": 102400 1157 | }, 1158 | "QUOTA_BYTES_PER_ITEM": { 1159 | "value": 8192 1160 | }, 1161 | "MAX_ITEMS": { 1162 | "value": 512 1163 | }, 1164 | "MAX_WRITE_OPERATIONS_PER_HOUR": { 1165 | "value": 1800 1166 | }, 1167 | "MAX_WRITE_OPERATIONS_PER_MINUTE": { 1168 | "value": 120 1169 | }, 1170 | "MAX_SUSTAINED_WRITE_OPERATIONS_PER_MINUTE": { 1171 | "value": 1000000 1172 | } 1173 | } 1174 | }, 1175 | "local": { 1176 | "class": "StorageArea", 1177 | "properties": { 1178 | "QUOTA_BYTES": { 1179 | "value": 5242880 1180 | } 1181 | } 1182 | }, 1183 | "managed": { 1184 | "class": "StorageArea" 1185 | } 1186 | } 1187 | }, 1188 | "system.cpu": { 1189 | "functions": [ 1190 | { 1191 | "name": "getInfo", 1192 | "successCallbackIndex": 0 1193 | } 1194 | ], 1195 | "events": [] 1196 | }, 1197 | "system.memory": { 1198 | "functions": [ 1199 | { 1200 | "name": "getInfo", 1201 | "successCallbackIndex": 0 1202 | } 1203 | ], 1204 | "events": [] 1205 | }, 1206 | "system.storage": { 1207 | "functions": [ 1208 | { 1209 | "name": "getInfo", 1210 | "successCallbackIndex": 0 1211 | }, 1212 | { 1213 | "name": "ejectDevice", 1214 | "successCallbackIndex": 1 1215 | }, 1216 | { 1217 | "name": "getAvailableCapacity", 1218 | "successCallbackIndex": 1 1219 | } 1220 | ], 1221 | "events": [ 1222 | "onAttached", 1223 | "onDetached" 1224 | ] 1225 | }, 1226 | "tabCapture": { 1227 | "functions": [ 1228 | { 1229 | "name": "capture", 1230 | "successCallbackIndex": 1 1231 | }, 1232 | { 1233 | "name": "getCapturedTabs", 1234 | "successCallbackIndex": 0 1235 | } 1236 | ], 1237 | "events": [ 1238 | "onStatusChanged" 1239 | ] 1240 | }, 1241 | "tabs": { 1242 | "functions": [ 1243 | { 1244 | "name": "get", 1245 | "successCallbackIndex": 1 1246 | }, 1247 | { 1248 | "name": "getCurrent", 1249 | "successCallbackIndex": 0 1250 | }, 1251 | { 1252 | "name": "connect" 1253 | }, 1254 | { 1255 | "name": "sendRequest" 1256 | }, 1257 | { 1258 | "name": "sendMessage" 1259 | }, 1260 | { 1261 | "name": "getSelected", 1262 | "successCallbackIndex": 1 1263 | }, 1264 | { 1265 | "name": "getAllInWindow", 1266 | "successCallbackIndex": 1 1267 | }, 1268 | { 1269 | "name": "create", 1270 | "successCallbackIndex": 1 1271 | }, 1272 | { 1273 | "name": "duplicate", 1274 | "successCallbackIndex": 1 1275 | }, 1276 | { 1277 | "name": "query", 1278 | "successCallbackIndex": 1 1279 | }, 1280 | { 1281 | "name": "highlight", 1282 | "successCallbackIndex": 1 1283 | }, 1284 | { 1285 | "name": "update", 1286 | "successCallbackIndex": 2 1287 | }, 1288 | { 1289 | "name": "move", 1290 | "successCallbackIndex": 2 1291 | }, 1292 | { 1293 | "name": "reload", 1294 | "successCallbackIndex": 2 1295 | }, 1296 | { 1297 | "name": "remove", 1298 | "successCallbackIndex": 1 1299 | }, 1300 | { 1301 | "name": "detectLanguage", 1302 | "successCallbackIndex": 1 1303 | }, 1304 | { 1305 | "name": "captureVisibleTab", 1306 | "successCallbackIndex": 2 1307 | }, 1308 | { 1309 | "name": "executeScript", 1310 | "successCallbackIndex": 2 1311 | }, 1312 | { 1313 | "name": "insertCSS", 1314 | "successCallbackIndex": 2 1315 | }, 1316 | { 1317 | "name": "setZoom", 1318 | "successCallbackIndex": 2 1319 | }, 1320 | { 1321 | "name": "getZoom", 1322 | "successCallbackIndex": 1 1323 | }, 1324 | { 1325 | "name": "setZoomSettings", 1326 | "successCallbackIndex": 2 1327 | }, 1328 | { 1329 | "name": "getZoomSettings", 1330 | "successCallbackIndex": 1 1331 | } 1332 | ], 1333 | "events": [ 1334 | "onCreated", 1335 | "onUpdated", 1336 | "onMoved", 1337 | "onSelectionChanged", 1338 | "onActiveChanged", 1339 | "onActivated", 1340 | "onHighlightChanged", 1341 | "onHighlighted", 1342 | "onDetached", 1343 | "onAttached", 1344 | "onRemoved", 1345 | "onReplaced", 1346 | "onZoomChange" 1347 | ] 1348 | }, 1349 | "topSites": { 1350 | "functions": [ 1351 | { 1352 | "name": "get", 1353 | "successCallbackIndex": 0 1354 | } 1355 | ], 1356 | "events": [] 1357 | }, 1358 | "tts": { 1359 | "functions": [ 1360 | { 1361 | "name": "speak", 1362 | "successCallbackIndex": 2 1363 | }, 1364 | { 1365 | "name": "stop" 1366 | }, 1367 | { 1368 | "name": "pause" 1369 | }, 1370 | { 1371 | "name": "resume" 1372 | }, 1373 | { 1374 | "name": "isSpeaking", 1375 | "successCallbackIndex": 0 1376 | }, 1377 | { 1378 | "name": "getVoices", 1379 | "successCallbackIndex": 0 1380 | } 1381 | ], 1382 | "events": [ 1383 | "onEvent" 1384 | ] 1385 | }, 1386 | "ttsEngine": { 1387 | "functions": [ 1388 | { 1389 | "name": "sendTtsEvent" 1390 | } 1391 | ], 1392 | "events": [ 1393 | "onSpeak", 1394 | "onStop", 1395 | "onPause", 1396 | "onResume" 1397 | ] 1398 | }, 1399 | "types": { 1400 | "functions": [], 1401 | "events": [], 1402 | "types": { 1403 | "ChromeSetting": { 1404 | "functions": [ 1405 | { 1406 | "name": "get", 1407 | "successCallbackIndex": 1 1408 | }, 1409 | { 1410 | "name": "set", 1411 | "successCallbackIndex": 1 1412 | }, 1413 | { 1414 | "name": "clear", 1415 | "successCallbackIndex": 1 1416 | } 1417 | ], 1418 | "events": [ 1419 | "onChange" 1420 | ] 1421 | } 1422 | } 1423 | }, 1424 | "wallpaper": { 1425 | "functions": [ 1426 | { 1427 | "name": "setWallpaper", 1428 | "successCallbackIndex": 1 1429 | } 1430 | ], 1431 | "events": [] 1432 | }, 1433 | "webNavigation": { 1434 | "functions": [ 1435 | { 1436 | "name": "getFrame", 1437 | "successCallbackIndex": 1 1438 | }, 1439 | { 1440 | "name": "getAllFrames", 1441 | "successCallbackIndex": 1 1442 | } 1443 | ], 1444 | "events": [ 1445 | "onBeforeNavigate", 1446 | "onCommitted", 1447 | "onDOMContentLoaded", 1448 | "onCompleted", 1449 | "onErrorOccurred", 1450 | "onCreatedNavigationTarget", 1451 | "onReferenceFragmentUpdated", 1452 | "onTabReplaced", 1453 | "onHistoryStateUpdated" 1454 | ] 1455 | }, 1456 | "webRequest": { 1457 | "functions": [ 1458 | { 1459 | "name": "handlerBehaviorChanged", 1460 | "successCallbackIndex": 0 1461 | } 1462 | ], 1463 | "events": [ 1464 | "onBeforeRequest", 1465 | "onBeforeSendHeaders", 1466 | "onSendHeaders", 1467 | "onHeadersReceived", 1468 | "onAuthRequired", 1469 | "onResponseStarted", 1470 | "onBeforeRedirect", 1471 | "onCompleted", 1472 | "onErrorOccurred" 1473 | ], 1474 | "properties": { 1475 | "MAX_HANDLER_BEHAVIOR_CHANGED_CALLS_PER_10_MINUTES": { 1476 | "value": 20 1477 | } 1478 | } 1479 | }, 1480 | "webstore": { 1481 | "functions": [ 1482 | { 1483 | "name": "install", 1484 | "successCallbackIndex": 1, 1485 | "failureCallbackIndex": 2 1486 | } 1487 | ], 1488 | "events": [ 1489 | "onInstallStageChanged", 1490 | "onDownloadProgress" 1491 | ] 1492 | }, 1493 | "windows": { 1494 | "functions": [ 1495 | { 1496 | "name": "get", 1497 | "successCallbackIndex": 2 1498 | }, 1499 | { 1500 | "name": "getCurrent", 1501 | "successCallbackIndex": 1 1502 | }, 1503 | { 1504 | "name": "getLastFocused", 1505 | "successCallbackIndex": 1 1506 | }, 1507 | { 1508 | "name": "getAll", 1509 | "successCallbackIndex": 1 1510 | }, 1511 | { 1512 | "name": "create", 1513 | "successCallbackIndex": 1 1514 | }, 1515 | { 1516 | "name": "update", 1517 | "successCallbackIndex": 2 1518 | }, 1519 | { 1520 | "name": "remove", 1521 | "successCallbackIndex": 1 1522 | } 1523 | ], 1524 | "events": [ 1525 | "onCreated", 1526 | "onRemoved", 1527 | "onFocusChanged" 1528 | ], 1529 | "properties": { 1530 | "WINDOW_ID_NONE": { 1531 | "value": -1 1532 | }, 1533 | "WINDOW_ID_CURRENT": { 1534 | "value": -2 1535 | } 1536 | } 1537 | } 1538 | } -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | "use strict"; 5 | 6 | const self = require("sdk/self"); 7 | const { load } = require("./crx"); 8 | 9 | var rootURI = self.data.url("crx/"); 10 | var crx = load({ 11 | rootURI: rootURI 12 | }); 13 | -------------------------------------------------------------------------------- /lib/api/storage.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | "use strict"; 5 | 6 | const ss = require("sdk/simple-storage"); 7 | const QUOTA_BYTES = 5242880; // 5 MB 8 | const { registerChromeFunction } = require("../chrome-api-controller"); 9 | 10 | /** 11 | * All `storage.*` APIs (sync, local, managed) all have the same 12 | * functions defined as StorageArea. 13 | * https://developer.chrome.com/extensions/storage#type-StorageArea 14 | */ 15 | 16 | registerChromeFunction("storage.local.getBytesInUse", function (keys, callback) { 17 | if (typeof keys == "string") { 18 | keys = [ keys ]; 19 | } 20 | 21 | // handle case where a blank list is provided 22 | if (Array.isArray(keys)) { 23 | if (keys.length == 0) { 24 | return callback(0); 25 | } 26 | } 27 | 28 | // TODO: if keys are provided, then only get the usage of those 29 | // handle case where total usage is desired 30 | callback(QUOTA_BYTES * ss.quotaUsage); 31 | }, "Does not scope usage to keys, or handle case where total usage is desired."); 32 | 33 | registerChromeFunction("storage.local.get", function (keys, callback) { 34 | let defaults = {}; 35 | 36 | if (typeof keys == "object") { 37 | defaults = keys; 38 | keys = Object.keys(keys); 39 | } 40 | else if (typeof keys == "string") { 41 | keys = [ keys ]; 42 | } 43 | 44 | let items = {}; 45 | keys.forEach((key) => { 46 | items[key] = ss.storage[key] || defaults[key]; 47 | }); 48 | 49 | callback(items); 50 | }); 51 | 52 | registerChromeFunction("storage.local.set", function (items, callback) { 53 | Object.keys(items).forEach((key) => { 54 | ss.storage[key] = items[key]; 55 | }); 56 | 57 | callback(); 58 | }); 59 | -------------------------------------------------------------------------------- /lib/api/tabs.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | "use strict"; 5 | 6 | const { chromeEvent, bus, registerChromeFunction } = require("../chrome-api-controller"); 7 | 8 | const tabs = require("sdk/tabs"); 9 | const self = require("sdk/self"); 10 | 11 | let tabIndex = 0; 12 | const tabsMap = new WeakMap(); 13 | 14 | /** 15 | * TODO function that takes a Firefox Tab and returns a Chrome Tab type, 16 | * many methods here return a tab object and we are not currently matching it 17 | * as best as we can. 18 | * https://developer.chrome.com/extensions/tabs#type-Tab 19 | */ 20 | 21 | registerChromeFunction("tabs.duplicate", function (tabID, callback) { 22 | var url = getTabForID(tabID).url; 23 | tabs.open({ 24 | url: url, 25 | onLoad: tab => { 26 | let chromeTab = { 27 | id: getTabID(tab), 28 | url: url, 29 | title: tab.title, 30 | // TODO: implement this! 31 | favIconUrl: undefined 32 | }; 33 | callback(chromeTab); 34 | } 35 | }); 36 | }); 37 | 38 | registerChromeFunction("tabs.remove", function (tabs, callback) { 39 | var tabIDs = (Array.isArray(tabs) ? tabs : [ tabs ]).sort(); 40 | for (let i = tabIDs.length - 1; i >= 0; i--) { 41 | let tab = getTabForID(tabIDs[i]); 42 | tab && tab.close(); 43 | } 44 | callback(); 45 | }); 46 | 47 | registerChromeFunction("tabs.query", function (data, callback) { 48 | var result = []; 49 | for (let tab of tabs) { 50 | result.push({ 51 | url: tab.url 52 | }); 53 | } 54 | callback(result); 55 | }, "Does not support query data."); 56 | 57 | registerChromeFunction("tabs.getCurrent", function (callback) { 58 | var activeTab = tabs.activeTab; 59 | callback({ 60 | id: getTabID(activeTab), 61 | url: activeTab.url, 62 | title: activeTab.title 63 | }); 64 | }); 65 | 66 | registerChromeFunction("tabs.executeScript", function (tabID, data, callback) { 67 | let tab = (!tabID) ? tabs.activeTab : tabs[tabID]; 68 | let runAt = data.runAt ? data.runAt.replace(/^document_/i, "") : "ready"; 69 | 70 | if (runAt == "idle") { 71 | runAt = "ready"; 72 | } 73 | 74 | if (data.code) { 75 | tab.attach({ 76 | contentScriptWhen: runAt, 77 | contentScript: data.code, 78 | onAttach: () => callback() 79 | }); 80 | } 81 | else { 82 | tab.attach({ 83 | contentScriptWhen: runAt, 84 | contentScriptFile: getURL(data.file), 85 | onAttach: () => callback() 86 | }); 87 | } 88 | }, "Does not return evaluated value of script."); 89 | 90 | registerChromeFunction("tabs.create", function (options, callback) { 91 | var url = options.url; 92 | tabs.open({ 93 | url: url, 94 | onLoad: tab => { 95 | let chromeTab = { 96 | id: getTabID(tab), 97 | url: url, 98 | title: tab.title, 99 | // TODO: implement this! 100 | favIconUrl: undefined 101 | }; 102 | callback(chromeTab); 103 | chromeEvent("tabs.onCreated", chromeTab); 104 | } 105 | }); 106 | }, "Many creation options are not supported."); 107 | 108 | 109 | registerChromeFunction("tabs.sendMessage", function (tabID, message, options, callback) { 110 | 111 | }, "runtime.onMessage event is not fired."); 112 | 113 | /* 114 | target.port.on("tabs:send:message", function(data) { 115 | emit(emitter, "tabs:send:message", data); 116 | }); 117 | 118 | target.port.on("tabs:message:response", function(data) { 119 | emit(emitter, "tabs:got:message", data); 120 | }); 121 | 122 | function tabsGotMessage(data) { 123 | target.port.emit("tabs:got:message", data); 124 | } 125 | on(emitter, "tabs:got:message", tabsGotMessage); 126 | target.once("detach", () => { 127 | off(emitter, "tabs:got:message", tabsGotMessage); 128 | }) 129 | 130 | */ 131 | 132 | function getURL(path) { 133 | return self.data.url("./crx/" + path); 134 | } 135 | 136 | function getTabID(tab) { 137 | if (!tabsMap.has(tab)) { 138 | tabsMap.set(tab, tabIndex++); 139 | } 140 | return tabsMap.get(tab); 141 | } 142 | 143 | function getTabForID(id) { 144 | for (let i = tabs.length -1; i >= 0; i--) { 145 | let tab = tabs[i]; 146 | let tabID = getTabID(tab); 147 | if (tabID == id) { 148 | return tab; 149 | } 150 | } 151 | return null; 152 | } 153 | -------------------------------------------------------------------------------- /lib/chrome-api-controller.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | "use strict"; 5 | 6 | const { EventTarget } = require("sdk/event/target"); 7 | const utils = require("./utils"); 8 | 9 | let FunctionMap = new Map(); 10 | let TargetHandlerMap = new Map(); 11 | 12 | exports.attach = function attach ({ target }) { 13 | let handler = handleChromeRequest.bind(null, target); 14 | TargetHandlerMap.set(target, handler); 15 | target.port.on("chrome-api:request", handler); 16 | }; 17 | 18 | exports.detach = function detach (target) { 19 | let handler = TargetHandlerMap.get(target); 20 | target.port.removeListener("chrome-api:request", handler); 21 | }; 22 | 23 | /** 24 | * Adds a function to the Chrome API wrapper. Takes a name, which includes 25 | * the namespace as well as function name ("chrome.tabs.duplicate"), and a 26 | * function to be executed when called by the Chrome extension. 27 | * 28 | * @param {string} name 29 | * @param {function} fn 30 | */ 31 | exports.registerChromeFunction = function registerChromeFunction (name, fn) { 32 | FunctionMap.set(name, fn); 33 | }; 34 | 35 | /** 36 | * Sends an event back to the Chrome content. Pass in the namespace with 37 | * event name, like "tabs.onCreated", and any additional arguments. 38 | * 39 | * @param {string} name 40 | * @param {Mixed} ...args 41 | */ 42 | exports.chromeEvent = function chromeEvent (name, ...args) { 43 | console.log("emitting",name,args); 44 | for (let [target] of TargetHandlerMap) { 45 | target.port.emit("chrome-api:event", { args, name }); 46 | } 47 | }; 48 | 49 | exports.bus = new EventTarget(); 50 | 51 | function handleChromeRequest (target, { name, args, id, success, failure }) { 52 | let fn = FunctionMap.get(name); 53 | let ns; 54 | 55 | // If not found, try fetching implementation for lazy loading. 56 | if (!fn) { 57 | try { 58 | ns = (name || "").split(".")[0]; 59 | require(`./api/${ns}`); 60 | fn = FunctionMap.get(name); 61 | } catch (e) { 62 | } 63 | } 64 | 65 | // If still not found, API probably isn't implemented, send an alert 66 | // and respond to the request with empty data so it's not hanging. 67 | if (!fn) { 68 | utils.alertUnsupportedAPI(name); 69 | return; 70 | } 71 | 72 | // If the function has a success or failure callback, bind that so 73 | // we can respond back to the content script 74 | if (success != void 0) { 75 | args[success] = successCallback; 76 | } 77 | if (failure != void 0) { 78 | args[failure] = failureCallback; 79 | } 80 | 81 | // Call the implementation with the arguments received 82 | fn.apply(null, args); 83 | 84 | function successCallback () { 85 | target.port.emit("chrome-api:response", { id, res: Array.prototype.slice.call(arguments) }); 86 | } 87 | 88 | function failureCallback () { 89 | target.port.emit("chrome-api:response", { 90 | id, res: Array.prototype.slice.call(arguments), error: true 91 | }); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /lib/content-script-utils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Converts Chrome Match Patterns to Firefox Match Patterns. 3 | * Used for host permissions and content script matching. 4 | * 5 | * https://developer.mozilla.org/en-US/Add-ons/SDK/Low-Level_APIs/util_match-pattern 6 | * https://developer.chrome.com/extensions/match_patterns 7 | */ 8 | 9 | function convertPattern (chromePatterns) { 10 | chromePatterns = [].concat(chromePatterns); 11 | var starUsed = false; 12 | 13 | let result = chromePatterns.map(pattern => { 14 | console.log(":",pattern); 15 | // exact match "http://google.com" 16 | // wildcard "*" 17 | // domain prefix "*.example.com" 18 | // url plus wild: "http://blah.com/*" 19 | // scheme plus wild: "https://*" 20 | 21 | // Match all 22 | if (pattern === "