summaryrefslogtreecommitdiffstats
path: root/addon-sdk/source/lib/sdk/content
diff options
context:
space:
mode:
authorMatt A. Tobin <email@mattatobin.com>2018-02-09 06:46:43 -0500
committerMatt A. Tobin <email@mattatobin.com>2018-02-09 06:46:43 -0500
commitac46df8daea09899ce30dc8fd70986e258c746bf (patch)
tree2750d3125fc253fd5b0671e4bd268eff1fd97296 /addon-sdk/source/lib/sdk/content
parent8cecf8d5208f3945b35f879bba3015bb1a11bec6 (diff)
downloadUXP-ac46df8daea09899ce30dc8fd70986e258c746bf.tar
UXP-ac46df8daea09899ce30dc8fd70986e258c746bf.tar.gz
UXP-ac46df8daea09899ce30dc8fd70986e258c746bf.tar.lz
UXP-ac46df8daea09899ce30dc8fd70986e258c746bf.tar.xz
UXP-ac46df8daea09899ce30dc8fd70986e258c746bf.zip
Move Add-on SDK source to toolkit/jetpack
Diffstat (limited to 'addon-sdk/source/lib/sdk/content')
-rw-r--r--addon-sdk/source/lib/sdk/content/content-worker.js305
-rw-r--r--addon-sdk/source/lib/sdk/content/content.js17
-rw-r--r--addon-sdk/source/lib/sdk/content/context-menu.js408
-rw-r--r--addon-sdk/source/lib/sdk/content/events.js57
-rw-r--r--addon-sdk/source/lib/sdk/content/l10n-html.js133
-rw-r--r--addon-sdk/source/lib/sdk/content/loader.js74
-rw-r--r--addon-sdk/source/lib/sdk/content/mod.js68
-rw-r--r--addon-sdk/source/lib/sdk/content/page-mod.js236
-rw-r--r--addon-sdk/source/lib/sdk/content/page-worker.js154
-rw-r--r--addon-sdk/source/lib/sdk/content/sandbox.js426
-rw-r--r--addon-sdk/source/lib/sdk/content/sandbox/events.js12
-rw-r--r--addon-sdk/source/lib/sdk/content/tab-events.js58
-rw-r--r--addon-sdk/source/lib/sdk/content/thumbnail.js51
-rw-r--r--addon-sdk/source/lib/sdk/content/utils.js105
-rw-r--r--addon-sdk/source/lib/sdk/content/worker-child.js158
-rw-r--r--addon-sdk/source/lib/sdk/content/worker.js180
16 files changed, 0 insertions, 2442 deletions
diff --git a/addon-sdk/source/lib/sdk/content/content-worker.js b/addon-sdk/source/lib/sdk/content/content-worker.js
deleted file mode 100644
index 0a8225733..000000000
--- a/addon-sdk/source/lib/sdk/content/content-worker.js
+++ /dev/null
@@ -1,305 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-Object.freeze({
- // TODO: Bug 727854 Use same implementation than common JS modules,
- // i.e. EventEmitter module
-
- /**
- * Create an EventEmitter instance.
- */
- createEventEmitter: function createEventEmitter(emit) {
- let listeners = Object.create(null);
- let eventEmitter = Object.freeze({
- emit: emit,
- on: function on(name, callback) {
- if (typeof callback !== "function")
- return this;
- if (!(name in listeners))
- listeners[name] = [];
- listeners[name].push(callback);
- return this;
- },
- once: function once(name, callback) {
- eventEmitter.on(name, function onceCallback() {
- eventEmitter.removeListener(name, onceCallback);
- callback.apply(callback, arguments);
- });
- },
- removeListener: function removeListener(name, callback) {
- if (!(name in listeners))
- return;
- let index = listeners[name].indexOf(callback);
- if (index == -1)
- return;
- listeners[name].splice(index, 1);
- }
- });
- function onEvent(name) {
- if (!(name in listeners))
- return [];
- let args = Array.slice(arguments, 1);
- let results = [];
- for (let callback of listeners[name]) {
- results.push(callback.apply(null, args));
- }
- return results;
- }
- return {
- eventEmitter: eventEmitter,
- emit: onEvent
- };
- },
-
- /**
- * Create an EventEmitter instance to communicate with chrome module
- * by passing only strings between compartments.
- * This function expects `emitToChrome` function, that allows to send
- * events to the chrome module. It returns the EventEmitter as `pipe`
- * attribute, and, `onChromeEvent` a function that allows chrome module
- * to send event into the EventEmitter.
- *
- * pipe.emit --> emitToChrome
- * onChromeEvent --> callback registered through pipe.on
- */
- createPipe: function createPipe(emitToChrome) {
- let ContentWorker = this;
- function onEvent(type, ...args) {
- // JSON.stringify is buggy with cross-sandbox values,
- // it may return "{}" on functions. Use a replacer to match them correctly.
- let replacer = (k, v) =>
- typeof(v) === "function"
- ? (type === "console" ? Function.toString.call(v) : void(0))
- : v;
-
- let str = JSON.stringify([type, ...args], replacer);
- emitToChrome(str);
- }
-
- let { eventEmitter, emit } =
- ContentWorker.createEventEmitter(onEvent);
-
- return {
- pipe: eventEmitter,
- onChromeEvent: function onChromeEvent(array) {
- // We either receive a stringified array, or a real array.
- // We still allow to pass an array of objects, in WorkerSandbox.emitSync
- // in order to allow sending DOM node reference between content script
- // and modules (only used for context-menu API)
- let args = typeof array == "string" ? JSON.parse(array) : array;
- return emit.apply(null, args);
- }
- };
- },
-
- injectConsole: function injectConsole(exports, pipe) {
- exports.console = Object.freeze({
- log: pipe.emit.bind(null, "console", "log"),
- info: pipe.emit.bind(null, "console", "info"),
- warn: pipe.emit.bind(null, "console", "warn"),
- error: pipe.emit.bind(null, "console", "error"),
- debug: pipe.emit.bind(null, "console", "debug"),
- exception: pipe.emit.bind(null, "console", "exception"),
- trace: pipe.emit.bind(null, "console", "trace"),
- time: pipe.emit.bind(null, "console", "time"),
- timeEnd: pipe.emit.bind(null, "console", "timeEnd")
- });
- },
-
- injectTimers: function injectTimers(exports, chromeAPI, pipe, console) {
- // wrapped functions from `'timer'` module.
- // Wrapper adds `try catch` blocks to the callbacks in order to
- // emit `error` event if exception is thrown in
- // the Worker global scope.
- // @see http://www.w3.org/TR/workers/#workerutils
-
- // List of all living timeouts/intervals
- let _timers = Object.create(null);
-
- // Keep a reference to original timeout functions
- let {
- setTimeout: chromeSetTimeout,
- setInterval: chromeSetInterval,
- clearTimeout: chromeClearTimeout,
- clearInterval: chromeClearInterval
- } = chromeAPI.timers;
-
- function registerTimer(timer) {
- let registerMethod = null;
- if (timer.kind == "timeout")
- registerMethod = chromeSetTimeout;
- else if (timer.kind == "interval")
- registerMethod = chromeSetInterval;
- else
- throw new Error("Unknown timer kind: " + timer.kind);
-
- if (typeof timer.fun == 'string') {
- let code = timer.fun;
- timer.fun = () => chromeAPI.sandbox.evaluate(exports, code);
- } else if (typeof timer.fun != 'function') {
- throw new Error('Unsupported callback type' + typeof timer.fun);
- }
-
- let id = registerMethod(onFire, timer.delay);
- function onFire() {
- try {
- if (timer.kind == "timeout")
- delete _timers[id];
- timer.fun.apply(null, timer.args);
- } catch(e) {
- console.exception(e);
- let wrapper = {
- instanceOfError: instanceOf(e, Error),
- value: e,
- };
- if (wrapper.instanceOfError) {
- wrapper.value = {
- message: e.message,
- fileName: e.fileName,
- lineNumber: e.lineNumber,
- stack: e.stack,
- name: e.name,
- };
- }
- pipe.emit('error', wrapper);
- }
- }
- _timers[id] = timer;
- return id;
- }
-
- // copied from sdk/lang/type.js since modules are not available here
- function instanceOf(value, Type) {
- var isConstructorNameSame;
- var isConstructorSourceSame;
-
- // If `instanceof` returned `true` we know result right away.
- var isInstanceOf = value instanceof Type;
-
- // If `instanceof` returned `false` we do ducktype check since `Type` may be
- // from a different sandbox. If a constructor of the `value` or a constructor
- // of the value's prototype has same name and source we assume that it's an
- // instance of the Type.
- if (!isInstanceOf && value) {
- isConstructorNameSame = value.constructor.name === Type.name;
- isConstructorSourceSame = String(value.constructor) == String(Type);
- isInstanceOf = (isConstructorNameSame && isConstructorSourceSame) ||
- instanceOf(Object.getPrototypeOf(value), Type);
- }
- return isInstanceOf;
- }
-
- function unregisterTimer(id) {
- if (!(id in _timers))
- return;
- let { kind } = _timers[id];
- delete _timers[id];
- if (kind == "timeout")
- chromeClearTimeout(id);
- else if (kind == "interval")
- chromeClearInterval(id);
- else
- throw new Error("Unknown timer kind: " + kind);
- }
-
- function disableAllTimers() {
- Object.keys(_timers).forEach(unregisterTimer);
- }
-
- exports.setTimeout = function ContentScriptSetTimeout(callback, delay) {
- return registerTimer({
- kind: "timeout",
- fun: callback,
- delay: delay,
- args: Array.slice(arguments, 2)
- });
- };
- exports.clearTimeout = function ContentScriptClearTimeout(id) {
- unregisterTimer(id);
- };
-
- exports.setInterval = function ContentScriptSetInterval(callback, delay) {
- return registerTimer({
- kind: "interval",
- fun: callback,
- delay: delay,
- args: Array.slice(arguments, 2)
- });
- };
- exports.clearInterval = function ContentScriptClearInterval(id) {
- unregisterTimer(id);
- };
-
- // On page-hide, save a list of all existing timers before disabling them,
- // in order to be able to restore them on page-show.
- // These events are fired when the page goes in/out of bfcache.
- // https://developer.mozilla.org/En/Working_with_BFCache
- let frozenTimers = [];
- pipe.on("pageshow", function onPageShow() {
- frozenTimers.forEach(registerTimer);
- });
- pipe.on("pagehide", function onPageHide() {
- frozenTimers = [];
- for (let id in _timers)
- frozenTimers.push(_timers[id]);
- disableAllTimers();
- // Some other pagehide listeners may register some timers that won't be
- // frozen as this particular pagehide listener is called first.
- // So freeze these timers on next cycle.
- chromeSetTimeout(function () {
- for (let id in _timers)
- frozenTimers.push(_timers[id]);
- disableAllTimers();
- }, 0);
- });
-
- // Unregister all timers when the page is destroyed
- // (i.e. when it is removed from bfcache)
- pipe.on("detach", function clearTimeouts() {
- disableAllTimers();
- _timers = {};
- frozenTimers = [];
- });
- },
-
- injectMessageAPI: function injectMessageAPI(exports, pipe, console) {
-
- let ContentWorker = this;
- let { eventEmitter: port, emit : portEmit } =
- ContentWorker.createEventEmitter(pipe.emit.bind(null, "event"));
- pipe.on("event", portEmit);
-
- let self = {
- port: port,
- postMessage: pipe.emit.bind(null, "message"),
- on: pipe.on.bind(null),
- once: pipe.once.bind(null),
- removeListener: pipe.removeListener.bind(null),
- };
- Object.defineProperty(exports, "self", {
- value: self
- });
- },
-
- injectOptions: function (exports, options) {
- Object.defineProperty( exports.self, "options", { value: JSON.parse( options ) });
- },
-
- inject: function (exports, chromeAPI, emitToChrome, options) {
- let ContentWorker = this;
- let { pipe, onChromeEvent } =
- ContentWorker.createPipe(emitToChrome);
-
- ContentWorker.injectConsole(exports, pipe);
- ContentWorker.injectTimers(exports, chromeAPI, pipe, exports.console);
- ContentWorker.injectMessageAPI(exports, pipe, exports.console);
- if ( options !== undefined ) {
- ContentWorker.injectOptions(exports, options);
- }
-
- Object.freeze( exports.self );
-
- return onChromeEvent;
- }
-});
diff --git a/addon-sdk/source/lib/sdk/content/content.js b/addon-sdk/source/lib/sdk/content/content.js
deleted file mode 100644
index 9655223a3..000000000
--- a/addon-sdk/source/lib/sdk/content/content.js
+++ /dev/null
@@ -1,17 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-"use strict";
-
-module.metadata = {
- "stability": "deprecated"
-};
-
-const { deprecateUsage } = require('../util/deprecate');
-
-Object.defineProperty(exports, "Worker", {
- get: function() {
- deprecateUsage('`sdk/content/content` is deprecated. Please use `sdk/content/worker` directly.');
- return require('./worker').Worker;
- }
-});
diff --git a/addon-sdk/source/lib/sdk/content/context-menu.js b/addon-sdk/source/lib/sdk/content/context-menu.js
deleted file mode 100644
index 2955e2f09..000000000
--- a/addon-sdk/source/lib/sdk/content/context-menu.js
+++ /dev/null
@@ -1,408 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-"use strict";
-
-const { Class } = require("../core/heritage");
-const self = require("../self");
-const { WorkerChild } = require("./worker-child");
-const { getInnerId } = require("../window/utils");
-const { Ci } = require("chrome");
-const { Services } = require("resource://gre/modules/Services.jsm");
-const system = require('../system/events');
-const { process } = require('../remote/child');
-
-// These functions are roughly copied from sdk/selection which doesn't work
-// in the content process
-function getElementWithSelection(window) {
- let element = Services.focus.getFocusedElementForWindow(window, false, {});
- if (!element)
- return null;
-
- try {
- // Accessing selectionStart and selectionEnd on e.g. a button
- // results in an exception thrown as per the HTML5 spec. See
- // http://www.whatwg.org/specs/web-apps/current-work/multipage/association-of-controls-and-forms.html#textFieldSelection
-
- let { value, selectionStart, selectionEnd } = element;
-
- let hasSelection = typeof value === "string" &&
- !isNaN(selectionStart) &&
- !isNaN(selectionEnd) &&
- selectionStart !== selectionEnd;
-
- return hasSelection ? element : null;
- }
- catch (err) {
- console.exception(err);
- return null;
- }
-}
-
-function safeGetRange(selection, rangeNumber) {
- try {
- let { rangeCount } = selection;
- let range = null;
-
- for (let rangeNumber = 0; rangeNumber < rangeCount; rangeNumber++ ) {
- range = selection.getRangeAt(rangeNumber);
-
- if (range && range.toString())
- break;
-
- range = null;
- }
-
- return range;
- }
- catch (e) {
- return null;
- }
-}
-
-function getSelection(window) {
- let selection = window.getSelection();
- let range = safeGetRange(selection);
- if (range)
- return range.toString();
-
- let node = getElementWithSelection(window);
- if (!node)
- return null;
-
- return node.value.substring(node.selectionStart, node.selectionEnd);
-}
-
-//These are used by PageContext.isCurrent below. If the popupNode or any of
-//its ancestors is one of these, Firefox uses a tailored context menu, and so
-//the page context doesn't apply.
-const NON_PAGE_CONTEXT_ELTS = [
- Ci.nsIDOMHTMLAnchorElement,
- Ci.nsIDOMHTMLAppletElement,
- Ci.nsIDOMHTMLAreaElement,
- Ci.nsIDOMHTMLButtonElement,
- Ci.nsIDOMHTMLCanvasElement,
- Ci.nsIDOMHTMLEmbedElement,
- Ci.nsIDOMHTMLImageElement,
- Ci.nsIDOMHTMLInputElement,
- Ci.nsIDOMHTMLMapElement,
- Ci.nsIDOMHTMLMediaElement,
- Ci.nsIDOMHTMLMenuElement,
- Ci.nsIDOMHTMLObjectElement,
- Ci.nsIDOMHTMLOptionElement,
- Ci.nsIDOMHTMLSelectElement,
- Ci.nsIDOMHTMLTextAreaElement,
-];
-
-// List all editable types of inputs. Or is it better to have a list
-// of non-editable inputs?
-var editableInputs = {
- email: true,
- number: true,
- password: true,
- search: true,
- tel: true,
- text: true,
- textarea: true,
- url: true
-};
-
-var CONTEXTS = {};
-
-var Context = Class({
- initialize: function(id) {
- this.id = id;
- },
-
- adjustPopupNode: function adjustPopupNode(popupNode) {
- return popupNode;
- },
-
- // Gets state to pass through to the parent process for the node the user
- // clicked on
- getState: function(popupNode) {
- return false;
- }
-});
-
-// Matches when the context-clicked node doesn't have any of
-// NON_PAGE_CONTEXT_ELTS in its ancestors
-CONTEXTS.PageContext = Class({
- extends: Context,
-
- getState: function(popupNode) {
- // If there is a selection in the window then this context does not match
- if (!popupNode.ownerDocument.defaultView.getSelection().isCollapsed)
- return false;
-
- // If the clicked node or any of its ancestors is one of the blocked
- // NON_PAGE_CONTEXT_ELTS then this context does not match
- while (!(popupNode instanceof Ci.nsIDOMDocument)) {
- if (NON_PAGE_CONTEXT_ELTS.some(type => popupNode instanceof type))
- return false;
-
- popupNode = popupNode.parentNode;
- }
-
- return true;
- }
-});
-
-// Matches when there is an active selection in the window
-CONTEXTS.SelectionContext = Class({
- extends: Context,
-
- getState: function(popupNode) {
- if (!popupNode.ownerDocument.defaultView.getSelection().isCollapsed)
- return true;
-
- try {
- // The node may be a text box which has selectionStart and selectionEnd
- // properties. If not this will throw.
- let { selectionStart, selectionEnd } = popupNode;
- return !isNaN(selectionStart) && !isNaN(selectionEnd) &&
- selectionStart !== selectionEnd;
- }
- catch (e) {
- return false;
- }
- }
-});
-
-// Matches when the context-clicked node or any of its ancestors matches the
-// selector given
-CONTEXTS.SelectorContext = Class({
- extends: Context,
-
- initialize: function initialize(id, selector) {
- Context.prototype.initialize.call(this, id);
- this.selector = selector;
- },
-
- adjustPopupNode: function adjustPopupNode(popupNode) {
- let selector = this.selector;
-
- while (!(popupNode instanceof Ci.nsIDOMDocument)) {
- if (popupNode.matches(selector))
- return popupNode;
-
- popupNode = popupNode.parentNode;
- }
-
- return null;
- },
-
- getState: function(popupNode) {
- return !!this.adjustPopupNode(popupNode);
- }
-});
-
-// Matches when the page url matches any of the patterns given
-CONTEXTS.URLContext = Class({
- extends: Context,
-
- getState: function(popupNode) {
- return popupNode.ownerDocument.URL;
- }
-});
-
-// Matches when the user-supplied predicate returns true
-CONTEXTS.PredicateContext = Class({
- extends: Context,
-
- getState: function(node) {
- let window = node.ownerDocument.defaultView;
- let data = {};
-
- data.documentType = node.ownerDocument.contentType;
-
- data.documentURL = node.ownerDocument.location.href;
- data.targetName = node.nodeName.toLowerCase();
- data.targetID = node.id || null ;
-
- if ((data.targetName === 'input' && editableInputs[node.type]) ||
- data.targetName === 'textarea') {
- data.isEditable = !node.readOnly && !node.disabled;
- }
- else {
- data.isEditable = node.isContentEditable;
- }
-
- data.selectionText = getSelection(window, "TEXT");
-
- data.srcURL = node.src || null;
- data.value = node.value || null;
-
- while (!data.linkURL && node) {
- data.linkURL = node.href || null;
- node = node.parentNode;
- }
-
- return data;
- },
-});
-
-function instantiateContext({ id, type, args }) {
- if (!(type in CONTEXTS)) {
- console.error("Attempt to use unknown context " + type);
- return;
- }
- return new CONTEXTS[type](id, ...args);
-}
-
-var ContextWorker = Class({
- implements: [ WorkerChild ],
-
- // Calls the context workers context listeners and returns the first result
- // that is either a string or a value that evaluates to true. If all of the
- // listeners returned false then returns false. If there are no listeners,
- // returns true (show the menu item by default).
- getMatchedContext: function getCurrentContexts(popupNode) {
- let results = this.sandbox.emitSync("context", popupNode);
- if (!results.length)
- return true;
- return results.reduce((val, result) => val || result);
- },
-
- // Emits a click event in the worker's port. popupNode is the node that was
- // context-clicked, and clickedItemData is the data of the item that was
- // clicked.
- fireClick: function fireClick(popupNode, clickedItemData) {
- this.sandbox.emitSync("click", popupNode, clickedItemData);
- }
-});
-
-// Gets the item's content script worker for a window, creating one if necessary
-// Once created it will be automatically destroyed when the window unloads.
-// If there is not content scripts for the item then null will be returned.
-function getItemWorkerForWindow(item, window) {
- if (!item.contentScript && !item.contentScriptFile)
- return null;
-
- let id = getInnerId(window);
- let worker = item.workerMap.get(id);
-
- if (worker)
- return worker;
-
- worker = ContextWorker({
- id: item.id,
- window,
- manager: item.manager,
- contentScript: item.contentScript,
- contentScriptFile: item.contentScriptFile,
- onDetach: function() {
- item.workerMap.delete(id);
- }
- });
-
- item.workerMap.set(id, worker);
-
- return worker;
-}
-
-// A very simple remote proxy for every item. It's job is to provide data for
-// the main process to use to determine visibility state and to call into
-// content scripts when clicked.
-var RemoteItem = Class({
- initialize: function(options, manager) {
- this.id = options.id;
- this.contexts = options.contexts.map(instantiateContext);
- this.contentScript = options.contentScript;
- this.contentScriptFile = options.contentScriptFile;
-
- this.manager = manager;
-
- this.workerMap = new Map();
- keepAlive.set(this.id, this);
- },
-
- destroy: function() {
- for (let worker of this.workerMap.values()) {
- worker.destroy();
- }
- keepAlive.delete(this.id);
- },
-
- activate: function(popupNode, data) {
- let worker = getItemWorkerForWindow(this, popupNode.ownerDocument.defaultView);
- if (!worker)
- return;
-
- for (let context of this.contexts)
- popupNode = context.adjustPopupNode(popupNode);
-
- worker.fireClick(popupNode, data);
- },
-
- // Fills addonInfo with state data to send through to the main process
- getContextState: function(popupNode, addonInfo) {
- if (!(self.id in addonInfo)) {
- addonInfo[self.id] = {
- processID: process.id,
- items: {}
- };
- }
-
- let worker = getItemWorkerForWindow(this, popupNode.ownerDocument.defaultView);
- let contextStates = {};
- for (let context of this.contexts)
- contextStates[context.id] = context.getState(popupNode);
-
- addonInfo[self.id].items[this.id] = {
- // It isn't ideal to create a PageContext for every item but there isn't
- // a good shared place to do it.
- pageContext: (new CONTEXTS.PageContext()).getState(popupNode),
- contextStates,
- hasWorker: !!worker,
- workerContext: worker ? worker.getMatchedContext(popupNode) : true
- }
- }
-});
-exports.RemoteItem = RemoteItem;
-
-// Holds remote items for this frame.
-var keepAlive = new Map();
-
-// Called to create remote proxies for items. If they already exist we destroy
-// and recreate. This can happen if the item changes in some way or in odd
-// timing cases where the frame script is create around the same time as the
-// item is created in the main process
-process.port.on('sdk/contextmenu/createitems', (process, items) => {
- for (let itemoptions of items) {
- let oldItem = keepAlive.get(itemoptions.id);
- if (oldItem) {
- oldItem.destroy();
- }
-
- let item = new RemoteItem(itemoptions, this);
- }
-});
-
-process.port.on('sdk/contextmenu/destroyitems', (process, items) => {
- for (let id of items) {
- let item = keepAlive.get(id);
- item.destroy();
- }
-});
-
-var lastPopupNode = null;
-
-system.on('content-contextmenu', ({ subject }) => {
- let { event: { target: popupNode }, addonInfo } = subject.wrappedJSObject;
- lastPopupNode = popupNode;
-
- for (let item of keepAlive.values()) {
- item.getContextState(popupNode, addonInfo);
- }
-}, true);
-
-process.port.on('sdk/contextmenu/activateitems', (process, items, data) => {
- for (let id of items) {
- let item = keepAlive.get(id);
- if (!item)
- continue;
-
- item.activate(lastPopupNode, data);
- }
-});
diff --git a/addon-sdk/source/lib/sdk/content/events.js b/addon-sdk/source/lib/sdk/content/events.js
deleted file mode 100644
index c085b6179..000000000
--- a/addon-sdk/source/lib/sdk/content/events.js
+++ /dev/null
@@ -1,57 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-module.metadata = {
- "stability": "experimental"
-};
-
-const { Ci } = require("chrome");
-const { open } = require("../event/dom");
-const { observe } = require("../event/chrome");
-const { filter, merge, map, expand } = require("../event/utils");
-const { windows } = require("../window/utils");
-const { events: windowEvents } = require("sdk/window/events");
-
-// Note: Please note that even though pagehide event is included
-// it's not observable reliably since it's not always triggered
-// when closing tabs. Implementation can be imrpoved once that
-// event will be necessary.
-var TYPES = ["DOMContentLoaded", "load", "pageshow", "pagehide"];
-
-var insert = observe("document-element-inserted");
-var windowCreate = merge([
- observe("content-document-global-created"),
- observe("chrome-document-global-created")
-]);
-var create = map(windowCreate, function({target, data, type}) {
- return { target: target.document, type: type, data: data }
-});
-
-function streamEventsFrom({document}) {
- // Map supported event types to a streams of those events on the given
- // `window` for the inserted document and than merge these streams into
- // single form stream off all window state change events.
- let stateChanges = TYPES.map(function(type) {
- return open(document, type, { capture: true });
- });
-
- // Since load events on document occur for every loded resource
- return filter(merge(stateChanges), function({target}) {
- return target instanceof Ci.nsIDOMDocument
- })
-}
-exports.streamEventsFrom = streamEventsFrom;
-
-var opened = windows(null, { includePrivate: true });
-var state = merge(opened.map(streamEventsFrom));
-
-
-var futureReady = filter(windowEvents, ({type}) =>
- type === "DOMContentLoaded");
-var futureWindows = map(futureReady, ({target}) => target);
-var futureState = expand(futureWindows, streamEventsFrom);
-
-exports.events = merge([insert, create, state, futureState]);
diff --git a/addon-sdk/source/lib/sdk/content/l10n-html.js b/addon-sdk/source/lib/sdk/content/l10n-html.js
deleted file mode 100644
index f324623dc..000000000
--- a/addon-sdk/source/lib/sdk/content/l10n-html.js
+++ /dev/null
@@ -1,133 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-"use strict";
-
-module.metadata = {
- "stability": "unstable"
-};
-
-const { Ci, Cc, Cu } = require("chrome");
-const core = require("../l10n/core");
-const { loadSheet, removeSheet } = require("../stylesheet/utils");
-const { process, frames } = require("../remote/child");
-var observerService = Cc["@mozilla.org/observer-service;1"]
- .getService(Ci.nsIObserverService);
-const { ShimWaiver } = Cu.import("resource://gre/modules/ShimWaiver.jsm");
-const addObserver = ShimWaiver.getProperty(observerService, "addObserver");
-const removeObserver = ShimWaiver.getProperty(observerService, "removeObserver");
-
-const assetsURI = require('../self').data.url();
-
-const hideSheetUri = "data:text/css,:root {visibility: hidden !important;}";
-
-function translateElementAttributes(element) {
- // Translateable attributes
- const attrList = ['title', 'accesskey', 'alt', 'label', 'placeholder'];
- const ariaAttrMap = {
- 'ariaLabel': 'aria-label',
- 'ariaValueText': 'aria-valuetext',
- 'ariaMozHint': 'aria-moz-hint'
- };
- const attrSeparator = '.';
-
- // Try to translate each of the attributes
- for (let attribute of attrList) {
- const data = core.get(element.dataset.l10nId + attrSeparator + attribute);
- if (data)
- element.setAttribute(attribute, data);
- }
-
- // Look for the aria attribute translations that match fxOS's aliases
- for (let attrAlias in ariaAttrMap) {
- const data = core.get(element.dataset.l10nId + attrSeparator + attrAlias);
- if (data)
- element.setAttribute(ariaAttrMap[attrAlias], data);
- }
-}
-
-// Taken from Gaia:
-// https://github.com/andreasgal/gaia/blob/04fde2640a7f40314643016a5a6c98bf3755f5fd/webapi.js#L1470
-function translateElement(element) {
- element = element || document;
-
- // check all translatable children (= w/ a `data-l10n-id' attribute)
- var children = element.querySelectorAll('*[data-l10n-id]');
- var elementCount = children.length;
- for (var i = 0; i < elementCount; i++) {
- var child = children[i];
-
- // translate the child
- var key = child.dataset.l10nId;
- var data = core.get(key);
- if (data)
- child.textContent = data;
-
- translateElementAttributes(child);
- }
-}
-exports.translateElement = translateElement;
-
-function onDocumentReady2Translate(event) {
- let document = event.target;
- document.removeEventListener("DOMContentLoaded", onDocumentReady2Translate,
- false);
-
- translateElement(document);
-
- try {
- // Finally display document when we finished replacing all text content
- if (document.defaultView)
- removeSheet(document.defaultView, hideSheetUri, 'user');
- }
- catch(e) {
- console.exception(e);
- }
-}
-
-function onContentWindow(document) {
- // Accept only HTML documents
- if (!(document instanceof Ci.nsIDOMHTMLDocument))
- return;
-
- // Bug 769483: data:URI documents instanciated with nsIDOMParser
- // have a null `location` attribute at this time
- if (!document.location)
- return;
-
- // Accept only document from this addon
- if (document.location.href.indexOf(assetsURI) !== 0)
- return;
-
- try {
- // First hide content of the document in order to have content blinking
- // between untranslated and translated states
- loadSheet(document.defaultView, hideSheetUri, 'user');
- }
- catch(e) {
- console.exception(e);
- }
- // Wait for DOM tree to be built before applying localization
- document.addEventListener("DOMContentLoaded", onDocumentReady2Translate,
- false);
-}
-
-// Listen to creation of content documents in order to translate them as soon
-// as possible in their loading process
-const ON_CONTENT = "document-element-inserted";
-let enabled = false;
-function enable() {
- if (enabled)
- return;
- addObserver(onContentWindow, ON_CONTENT, false);
- enabled = true;
-}
-process.port.on("sdk/l10n/html/enable", enable);
-
-function disable() {
- if (!enabled)
- return;
- removeObserver(onContentWindow, ON_CONTENT);
- enabled = false;
-}
-process.port.on("sdk/l10n/html/disable", disable);
diff --git a/addon-sdk/source/lib/sdk/content/loader.js b/addon-sdk/source/lib/sdk/content/loader.js
deleted file mode 100644
index e4f0dd2aa..000000000
--- a/addon-sdk/source/lib/sdk/content/loader.js
+++ /dev/null
@@ -1,74 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-"use strict";
-
-module.metadata = {
- "stability": "unstable"
-};
-
-const { isValidURI, isLocalURL, URL } = require('../url');
-const { contract } = require('../util/contract');
-const { isString, isNil, instanceOf, isJSONable } = require('../lang/type');
-const { validateOptions,
- string, array, object, either, required } = require('../deprecated/api-utils');
-
-const isValidScriptFile = (value) =>
- (isString(value) || instanceOf(value, URL)) && isLocalURL(value);
-
-// map of property validations
-const valid = {
- contentURL: {
- is: either(string, object),
- ok: url => isNil(url) || isLocalURL(url) || isValidURI(url),
- msg: 'The `contentURL` option must be a valid URL.'
- },
- contentScriptFile: {
- is: either(string, object, array),
- ok: value => isNil(value) || [].concat(value).every(isValidScriptFile),
- msg: 'The `contentScriptFile` option must be a local URL or an array of URLs.'
- },
- contentScript: {
- is: either(string, array),
- ok: value => isNil(value) || [].concat(value).every(isString),
- msg: 'The `contentScript` option must be a string or an array of strings.'
- },
- contentScriptWhen: {
- is: required(string),
- map: value => value || 'end',
- ok: value => ~['start', 'ready', 'end'].indexOf(value),
- msg: 'The `contentScriptWhen` option must be either "start", "ready" or "end".'
- },
- contentScriptOptions: {
- ok: value => isNil(value) || isJSONable(value),
- msg: 'The contentScriptOptions should be a jsonable value.'
- }
-};
-exports.validationAttributes = valid;
-
-/**
- * Shortcut function to validate property with validation.
- * @param {Object|Number|String} suspect
- * value to validate
- * @param {Object} validation
- * validation rule passed to `api-utils`
- */
-function validate(suspect, validation) {
- return validateOptions(
- { $: suspect },
- { $: validation }
- ).$;
-}
-
-function Allow(script) {
- return {
- get script() {
- return script;
- },
- set script(value) {
- script = !!value;
- }
- };
-}
-
-exports.contract = contract(valid);
diff --git a/addon-sdk/source/lib/sdk/content/mod.js b/addon-sdk/source/lib/sdk/content/mod.js
deleted file mode 100644
index 81fe9ee42..000000000
--- a/addon-sdk/source/lib/sdk/content/mod.js
+++ /dev/null
@@ -1,68 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-"use strict";
-
-module.metadata = {
- "stability": "experimental"
-};
-
-const { Ci } = require("chrome");
-const { dispatcher } = require("../util/dispatcher");
-const { add, remove, iterator } = require("../lang/weak-set");
-
-var getTargetWindow = dispatcher("getTargetWindow");
-
-getTargetWindow.define(function (target) {
- if (target instanceof Ci.nsIDOMWindow)
- return target;
- if (target instanceof Ci.nsIDOMDocument)
- return target.defaultView || null;
-
- return null;
-});
-
-exports.getTargetWindow = getTargetWindow;
-
-var attachTo = dispatcher("attachTo");
-exports.attachTo = attachTo;
-
-var detachFrom = dispatcher("detatchFrom");
-exports.detachFrom = detachFrom;
-
-function attach(modification, target) {
- if (!modification)
- return;
-
- let window = getTargetWindow(target);
-
- attachTo(modification, window);
-
- // modification are stored per content; `window` reference can still be the
- // same even if the content is changed, therefore `document` is used instead.
- add(modification, window.document);
-}
-exports.attach = attach;
-
-function detach(modification, target) {
- if (!modification)
- return;
-
- if (target) {
- let window = getTargetWindow(target);
- detachFrom(modification, window);
- remove(modification, window.document);
- }
- else {
- let documents = iterator(modification);
- for (let document of documents) {
- let window = document.defaultView;
- // The window might have already gone away
- if (!window)
- continue;
- detachFrom(modification, document.defaultView);
- remove(modification, document);
- }
- }
-}
-exports.detach = detach;
diff --git a/addon-sdk/source/lib/sdk/content/page-mod.js b/addon-sdk/source/lib/sdk/content/page-mod.js
deleted file mode 100644
index 8ff9b1e7b..000000000
--- a/addon-sdk/source/lib/sdk/content/page-mod.js
+++ /dev/null
@@ -1,236 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-"use strict";
-
-module.metadata = {
- "stability": "stable"
-};
-
-const { getAttachEventType } = require('../content/utils');
-const { Class } = require('../core/heritage');
-const { Disposable } = require('../core/disposable');
-const { WeakReference } = require('../core/reference');
-const { WorkerChild } = require('./worker-child');
-const { EventTarget } = require('../event/target');
-const { on, emit, once, setListeners } = require('../event/core');
-const { on: domOn, removeListener: domOff } = require('../dom/events');
-const { isRegExp, isUndefined } = require('../lang/type');
-const { merge } = require('../util/object');
-const { isBrowser, getFrames } = require('../window/utils');
-const { getTabs, getURI: getTabURI } = require('../tabs/utils');
-const { ignoreWindow } = require('../private-browsing/utils');
-const { Style } = require("../stylesheet/style");
-const { attach, detach } = require("../content/mod");
-const { has, hasAny } = require("../util/array");
-const { Rules } = require("../util/rules");
-const { List, addListItem, removeListItem } = require('../util/list');
-const { when } = require("../system/unload");
-const { uuid } = require('../util/uuid');
-const { frames, process } = require('../remote/child');
-
-const pagemods = new Map();
-const styles = new WeakMap();
-var styleFor = (mod) => styles.get(mod);
-
-// Helper functions
-var modMatchesURI = (mod, uri) => mod.include.matchesAny(uri) && !mod.exclude.matchesAny(uri);
-
-/**
- * PageMod constructor (exported below).
- * @constructor
- */
-const ChildPageMod = Class({
- implements: [
- EventTarget,
- Disposable,
- ],
- setup: function PageMod(model) {
- merge(this, model);
-
- // Set listeners on {PageMod} itself, not the underlying worker,
- // like `onMessage`, as it'll get piped.
- setListeners(this, model);
-
- function deserializeRules(rules) {
- for (let rule of rules) {
- yield rule.type == "string" ? rule.value
- : new RegExp(rule.pattern, rule.flags);
- }
- }
-
- let include = [...deserializeRules(this.include)];
- this.include = Rules();
- this.include.add.apply(this.include, include);
-
- let exclude = [...deserializeRules(this.exclude)];
- this.exclude = Rules();
- this.exclude.add.apply(this.exclude, exclude);
-
- if (this.contentStyle || this.contentStyleFile) {
- styles.set(this, Style({
- uri: this.contentStyleFile,
- source: this.contentStyle
- }));
- }
-
- pagemods.set(this.id, this);
- this.seenDocuments = new WeakMap();
-
- // `applyOnExistingDocuments` has to be called after `pagemods.add()`
- // otherwise its calls to `onContent` method won't do anything.
- if (has(this.attachTo, 'existing'))
- applyOnExistingDocuments(this);
- },
-
- dispose: function() {
- let style = styleFor(this);
- if (style)
- detach(style);
-
- for (let i in this.include)
- this.include.remove(this.include[i]);
-
- pagemods.delete(this.id);
- }
-});
-
-function onContentWindow({ target: document }) {
- // Return if we have no pagemods
- if (pagemods.size === 0)
- return;
-
- let window = document.defaultView;
- // XML documents don't have windows, and we don't yet support them.
- if (!window)
- return;
-
- // Frame event listeners are bound to the frame the event came from by default
- let frame = this;
- // We apply only on documents in tabs of Firefox
- if (!frame.isTab)
- return;
-
- // When the tab is private, only addons with 'private-browsing' flag in
- // their package.json can apply content script to private documents
- if (ignoreWindow(window))
- return;
-
- for (let pagemod of pagemods.values()) {
- if (modMatchesURI(pagemod, window.location.href))
- onContent(pagemod, window);
- }
-}
-frames.addEventListener("DOMDocElementInserted", onContentWindow, true);
-
-function applyOnExistingDocuments (mod) {
- for (let frame of frames) {
- // Fake a newly created document
- let window = frame.content;
- // on startup with e10s, contentWindow might not exist yet,
- // in which case we will get notified by "document-element-inserted".
- if (!window || !window.frames)
- return;
- let uri = window.location.href;
- if (has(mod.attachTo, "top") && modMatchesURI(mod, uri))
- onContent(mod, window);
- if (has(mod.attachTo, "frame"))
- getFrames(window).
- filter(iframe => modMatchesURI(mod, iframe.location.href)).
- forEach(frame => onContent(mod, frame));
- }
-}
-
-function createWorker(mod, window) {
- let workerId = String(uuid());
-
- // Instruct the parent to connect to this worker. Do this first so the parent
- // side is connected before the worker attempts to send any messages there
- let frame = frames.getFrameForWindow(window.top);
- frame.port.emit('sdk/page-mod/worker-create', mod.id, {
- id: workerId,
- url: window.location.href
- });
-
- // Create a child worker and notify the parent
- let worker = WorkerChild({
- id: workerId,
- window: window,
- contentScript: mod.contentScript,
- contentScriptFile: mod.contentScriptFile,
- contentScriptOptions: mod.contentScriptOptions
- });
-
- once(worker, 'detach', () => worker.destroy());
-}
-
-function onContent (mod, window) {
- let isTopDocument = window.top === window;
- // Is a top level document and `top` is not set, ignore
- if (isTopDocument && !has(mod.attachTo, "top"))
- return;
- // Is a frame document and `frame` is not set, ignore
- if (!isTopDocument && !has(mod.attachTo, "frame"))
- return;
-
- // ensure we attach only once per document
- let seen = mod.seenDocuments;
- if (seen.has(window.document))
- return;
- seen.set(window.document, true);
-
- let style = styleFor(mod);
- if (style)
- attach(style, window);
-
- // Immediately evaluate content script if the document state is already
- // matching contentScriptWhen expectations
- if (isMatchingAttachState(mod, window)) {
- createWorker(mod, window);
- return;
- }
-
- let eventName = getAttachEventType(mod) || 'load';
- domOn(window, eventName, function onReady (e) {
- if (e.target.defaultView !== window)
- return;
- domOff(window, eventName, onReady, true);
- createWorker(mod, window);
-
- // Attaching is asynchronous so if the document is already loaded we will
- // miss the pageshow event so send a synthetic one.
- if (window.document.readyState == "complete") {
- mod.on('attach', worker => {
- try {
- worker.send('pageshow');
- emit(worker, 'pageshow');
- }
- catch (e) {
- // This can fail if an earlier attach listener destroyed the worker
- }
- });
- }
- }, true);
-}
-
-function isMatchingAttachState (mod, window) {
- let state = window.document.readyState;
- return 'start' === mod.contentScriptWhen ||
- // Is `load` event already dispatched?
- 'complete' === state ||
- // Is DOMContentLoaded already dispatched and waiting for it?
- ('ready' === mod.contentScriptWhen && state === 'interactive')
-}
-
-process.port.on('sdk/page-mod/create', (process, model) => {
- if (pagemods.has(model.id))
- return;
-
- new ChildPageMod(model);
-});
-
-process.port.on('sdk/page-mod/destroy', (process, id) => {
- let mod = pagemods.get(id);
- if (mod)
- mod.destroy();
-});
diff --git a/addon-sdk/source/lib/sdk/content/page-worker.js b/addon-sdk/source/lib/sdk/content/page-worker.js
deleted file mode 100644
index e9e741120..000000000
--- a/addon-sdk/source/lib/sdk/content/page-worker.js
+++ /dev/null
@@ -1,154 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-"use strict";
-
-const { frames } = require("../remote/child");
-const { Class } = require("../core/heritage");
-const { Disposable } = require('../core/disposable');
-const { data } = require("../self");
-const { once } = require("../dom/events");
-const { getAttachEventType } = require("./utils");
-const { Rules } = require('../util/rules');
-const { uuid } = require('../util/uuid');
-const { WorkerChild } = require("./worker-child");
-const { Cc, Ci, Cu } = require("chrome");
-const { observe } = require("../event/chrome");
-const { on } = require("../event/core");
-
-const appShell = Cc["@mozilla.org/appshell/appShellService;1"].getService(Ci.nsIAppShellService);
-
-const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
-
-const pages = new Map();
-
-const DOC_INSERTED = "document-element-inserted";
-
-function isValidURL(page, url) {
- return !page.rules || page.rules.matchesAny(url);
-}
-
-const ChildPage = Class({
- implements: [ Disposable ],
- setup: function(frame, id, options) {
- this.id = id;
- this.frame = frame;
- this.options = options;
-
- this.webNav = appShell.createWindowlessBrowser(false);
- this.docShell.allowJavascript = this.options.allow.script;
-
- // Accessing the browser's window forces the initial about:blank document to
- // be created before we start listening for notifications
- this.contentWindow;
-
- this.webProgress.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_LOCATION);
-
- pages.set(this.id, this);
-
- this.contentURL = options.contentURL;
-
- if (options.include) {
- this.rules = Rules();
- this.rules.add.apply(this.rules, [].concat(options.include));
- }
- },
-
- dispose: function() {
- pages.delete(this.id);
- this.webProgress.removeProgressListener(this);
- this.webNav.close();
- this.webNav = null;
- },
-
- attachWorker: function() {
- if (!isValidURL(this, this.contentWindow.location.href))
- return;
-
- this.options.id = uuid().toString();
- this.options.window = this.contentWindow;
- this.frame.port.emit("sdk/frame/connect", this.id, {
- id: this.options.id,
- url: this.contentWindow.document.documentURIObject.spec
- });
- new WorkerChild(this.options);
- },
-
- get docShell() {
- return this.webNav.QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIDocShell);
- },
-
- get webProgress() {
- return this.docShell.QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIWebProgress);
- },
-
- get contentWindow() {
- return this.docShell.QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIDOMWindow);
- },
-
- get contentURL() {
- return this.options.contentURL;
- },
- set contentURL(url) {
- this.options.contentURL = url;
-
- url = this.options.contentURL ? data.url(this.options.contentURL) : "about:blank";
- this.webNav.loadURI(url, Ci.nsIWebNavigation.LOAD_FLAGS_NONE, null, null, null);
- },
-
- onLocationChange: function(progress, request, location, flags) {
- // Ignore inner-frame events
- if (progress != this.webProgress)
- return;
- // Ignore events that don't change the document
- if (flags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT)
- return;
-
- let event = getAttachEventType(this.options);
- // Attaching at the start of the load is handled by the
- // document-element-inserted listener.
- if (event == DOC_INSERTED)
- return;
-
- once(this.contentWindow, event, () => {
- this.attachWorker();
- }, false);
- },
-
- QueryInterface: XPCOMUtils.generateQI(["nsIWebProgressListener", "nsISupportsWeakReference"])
-});
-
-on(observe(DOC_INSERTED), "data", ({ target }) => {
- let page = Array.from(pages.values()).find(p => p.contentWindow.document === target);
- if (!page)
- return;
-
- if (getAttachEventType(page.options) == DOC_INSERTED)
- page.attachWorker();
-});
-
-frames.port.on("sdk/frame/create", (frame, id, options) => {
- new ChildPage(frame, id, options);
-});
-
-frames.port.on("sdk/frame/set", (frame, id, params) => {
- let page = pages.get(id);
- if (!page)
- return;
-
- if ("allowScript" in params)
- page.docShell.allowJavascript = params.allowScript;
- if ("contentURL" in params)
- page.contentURL = params.contentURL;
-});
-
-frames.port.on("sdk/frame/destroy", (frame, id) => {
- let page = pages.get(id);
- if (!page)
- return;
-
- page.destroy();
-});
diff --git a/addon-sdk/source/lib/sdk/content/sandbox.js b/addon-sdk/source/lib/sdk/content/sandbox.js
deleted file mode 100644
index 096ba5c87..000000000
--- a/addon-sdk/source/lib/sdk/content/sandbox.js
+++ /dev/null
@@ -1,426 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-'use strict';
-
-module.metadata = {
- 'stability': 'unstable'
-};
-
-const { Class } = require('../core/heritage');
-const { EventTarget } = require('../event/target');
-const { on, off, emit } = require('../event/core');
-const { events } = require('./sandbox/events');
-const { requiresAddonGlobal } = require('./utils');
-const { delay: async } = require('../lang/functional');
-const { Ci, Cu, Cc } = require('chrome');
-const timer = require('../timers');
-const { URL } = require('../url');
-const { sandbox, evaluate, load } = require('../loader/sandbox');
-const { merge } = require('../util/object');
-const { getTabForContentWindowNoShim } = require('../tabs/utils');
-const { getInnerId } = require('../window/utils');
-const { PlainTextConsole } = require('../console/plain-text');
-const { data } = require('../self');const { isChildLoader } = require('../remote/core');
-// WeakMap of sandboxes so we can access private values
-const sandboxes = new WeakMap();
-
-/* Trick the linker in order to ensure shipping these files in the XPI.
- require('./content-worker.js');
- Then, retrieve URL of these files in the XPI:
-*/
-var prefix = module.uri.split('sandbox.js')[0];
-const CONTENT_WORKER_URL = prefix + 'content-worker.js';
-const metadata = require('@loader/options').metadata;
-
-// Fetch additional list of domains to authorize access to for each content
-// script. It is stored in manifest `metadata` field which contains
-// package.json data. This list is originaly defined by authors in
-// `permissions` attribute of their package.json addon file.
-const permissions = (metadata && metadata['permissions']) || {};
-const EXPANDED_PRINCIPALS = permissions['cross-domain-content'] || [];
-
-const waiveSecurityMembrane = !!permissions['unsafe-content-script'];
-
-const nsIScriptSecurityManager = Ci.nsIScriptSecurityManager;
-const secMan = Cc["@mozilla.org/scriptsecuritymanager;1"].
- getService(Ci.nsIScriptSecurityManager);
-
-const JS_VERSION = '1.8';
-
-// Tests whether this window is loaded in a tab
-function isWindowInTab(window) {
- if (isChildLoader) {
- let { frames } = require('../remote/child');
- let frame = frames.getFrameForWindow(window.top);
- return frame && frame.isTab;
- }
- else {
- // The deprecated sync worker API still does everything in the main process
- return getTabForContentWindowNoShim(window);
- }
-}
-
-const WorkerSandbox = Class({
- implements: [ EventTarget ],
-
- /**
- * Emit a message to the worker content sandbox
- */
- emit: function emit(type, ...args) {
- // JSON.stringify is buggy with cross-sandbox values,
- // it may return "{}" on functions. Use a replacer to match them correctly.
- let replacer = (k, v) =>
- typeof(v) === "function"
- ? (type === "console" ? Function.toString.call(v) : void(0))
- : v;
-
- // Ensure having an asynchronous behavior
- async(() =>
- emitToContent(this, JSON.stringify([type, ...args], replacer))
- );
- },
-
- /**
- * Synchronous version of `emit`.
- * /!\ Should only be used when it is strictly mandatory /!\
- * Doesn't ensure passing only JSON values.
- * Mainly used by context-menu in order to avoid breaking it.
- */
- emitSync: function emitSync(...args) {
- // because the arguments could be also non JSONable values,
- // we need to ensure the array instance is created from
- // the content's sandbox
- return emitToContent(this, new modelFor(this).sandbox.Array(...args));
- },
-
- /**
- * Configures sandbox and loads content scripts into it.
- * @param {Worker} worker
- * content worker
- */
- initialize: function WorkerSandbox(worker, window) {
- let model = {};
- sandboxes.set(this, model);
- model.worker = worker;
- // We receive a wrapped window, that may be an xraywrapper if it's content
- let proto = window;
-
- // TODO necessary?
- // Ensure that `emit` has always the right `this`
- this.emit = this.emit.bind(this);
- this.emitSync = this.emitSync.bind(this);
-
- // Use expanded principal for content-script if the content is a
- // regular web content for better isolation.
- // (This behavior can be turned off for now with the unsafe-content-script
- // flag to give addon developers time for making the necessary changes)
- // But prevent it when the Worker isn't used for a content script but for
- // injecting `addon` object into a Panel scope, for example.
- // That's because:
- // 1/ It is useless to use multiple domains as the worker is only used
- // to communicate with the addon,
- // 2/ By using it it would prevent the document to have access to any JS
- // value of the worker. As JS values coming from multiple domain principals
- // can't be accessed by 'mono-principals' (principal with only one domain).
- // Even if this principal is for a domain that is specified in the multiple
- // domain principal.
- let principals = window;
- let wantGlobalProperties = [];
- let isSystemPrincipal = secMan.isSystemPrincipal(
- window.document.nodePrincipal);
- if (!isSystemPrincipal && !requiresAddonGlobal(worker)) {
- if (EXPANDED_PRINCIPALS.length > 0) {
- // We have to replace XHR constructor of the content document
- // with a custom cross origin one, automagically added by platform code:
- delete proto.XMLHttpRequest;
- wantGlobalProperties.push('XMLHttpRequest');
- }
- if (!waiveSecurityMembrane)
- principals = EXPANDED_PRINCIPALS.concat(window);
- }
-
- // Create the sandbox and bind it to window in order for content scripts to
- // have access to all standard globals (window, document, ...)
- let content = sandbox(principals, {
- sandboxPrototype: proto,
- wantXrays: !requiresAddonGlobal(worker),
- wantGlobalProperties: wantGlobalProperties,
- wantExportHelpers: true,
- sameZoneAs: window,
- metadata: {
- SDKContentScript: true,
- 'inner-window-id': getInnerId(window)
- }
- });
- model.sandbox = content;
-
- // We have to ensure that window.top and window.parent are the exact same
- // object than window object, i.e. the sandbox global object. But not
- // always, in case of iframes, top and parent are another window object.
- let top = window.top === window ? content : content.top;
- let parent = window.parent === window ? content : content.parent;
- merge(content, {
- // We need 'this === window === top' to be true in toplevel scope:
- get window() {
- return content;
- },
- get top() {
- return top;
- },
- get parent() {
- return parent;
- }
- });
-
- // Use the Greasemonkey naming convention to provide access to the
- // unwrapped window object so the content script can access document
- // JavaScript values.
- // NOTE: this functionality is experimental and may change or go away
- // at any time!
- //
- // Note that because waivers aren't propagated between origins, we
- // need the unsafeWindow getter to live in the sandbox.
- var unsafeWindowGetter =
- new content.Function('return window.wrappedJSObject || window;');
- Object.defineProperty(content, 'unsafeWindow', {get: unsafeWindowGetter});
-
- // Load trusted code that will inject content script API.
- let ContentWorker = load(content, CONTENT_WORKER_URL);
-
- // prepare a clean `self.options`
- let options = 'contentScriptOptions' in worker ?
- JSON.stringify(worker.contentScriptOptions) :
- undefined;
-
- // Then call `inject` method and communicate with this script
- // by trading two methods that allow to send events to the other side:
- // - `onEvent` called by content script
- // - `result.emitToContent` called by addon script
- let onEvent = Cu.exportFunction(onContentEvent.bind(null, this), ContentWorker);
- let chromeAPI = createChromeAPI(ContentWorker);
- let result = Cu.waiveXrays(ContentWorker).inject(content, chromeAPI, onEvent, options);
-
- // Merge `emitToContent` into our private model of the
- // WorkerSandbox so we can communicate with content script
- model.emitToContent = result;
-
- let console = new PlainTextConsole(null, getInnerId(window));
-
- // Handle messages send by this script:
- setListeners(this, console);
-
- // Inject `addon` global into target document if document is trusted,
- // `addon` in document is equivalent to `self` in content script.
- if (requiresAddonGlobal(worker)) {
- Object.defineProperty(getUnsafeWindow(window), 'addon', {
- value: content.self,
- configurable: true
- }
- );
- }
-
- // Inject our `console` into target document if worker doesn't have a tab
- // (e.g Panel, PageWorker).
- // `worker.tab` can't be used because bug 804935.
- if (!isWindowInTab(window)) {
- let win = getUnsafeWindow(window);
-
- // export our chrome console to content window, as described here:
- // https://developer.mozilla.org/en-US/docs/Components.utils.createObjectIn
- let con = Cu.createObjectIn(win);
-
- let genPropDesc = function genPropDesc(fun) {
- return { enumerable: true, configurable: true, writable: true,
- value: console[fun] };
- }
-
- const properties = {
- log: genPropDesc('log'),
- info: genPropDesc('info'),
- warn: genPropDesc('warn'),
- error: genPropDesc('error'),
- debug: genPropDesc('debug'),
- trace: genPropDesc('trace'),
- dir: genPropDesc('dir'),
- group: genPropDesc('group'),
- groupCollapsed: genPropDesc('groupCollapsed'),
- groupEnd: genPropDesc('groupEnd'),
- time: genPropDesc('time'),
- timeEnd: genPropDesc('timeEnd'),
- profile: genPropDesc('profile'),
- profileEnd: genPropDesc('profileEnd'),
- exception: genPropDesc('exception'),
- assert: genPropDesc('assert'),
- count: genPropDesc('count'),
- table: genPropDesc('table'),
- clear: genPropDesc('clear'),
- dirxml: genPropDesc('dirxml'),
- markTimeline: genPropDesc('markTimeline'),
- timeline: genPropDesc('timeline'),
- timelineEnd: genPropDesc('timelineEnd'),
- timeStamp: genPropDesc('timeStamp'),
- };
-
- Object.defineProperties(con, properties);
- Cu.makeObjectPropsNormal(con);
-
- win.console = con;
- };
-
- emit(events, "content-script-before-inserted", {
- window: window,
- worker: worker
- });
-
- // The order of `contentScriptFile` and `contentScript` evaluation is
- // intentional, so programs can load libraries like jQuery from script URLs
- // and use them in scripts.
- let contentScriptFile = ('contentScriptFile' in worker)
- ? worker.contentScriptFile
- : null,
- contentScript = ('contentScript' in worker)
- ? worker.contentScript
- : null;
-
- if (contentScriptFile)
- importScripts.apply(null, [this].concat(contentScriptFile));
-
- if (contentScript) {
- evaluateIn(
- this,
- Array.isArray(contentScript) ? contentScript.join(';\n') : contentScript
- );
- }
- },
- destroy: function destroy(reason) {
- if (typeof reason != 'string')
- reason = '';
- this.emitSync('event', 'detach', reason);
- let model = modelFor(this);
- model.sandbox = null
- model.worker = null;
- },
-
-});
-
-exports.WorkerSandbox = WorkerSandbox;
-
-/**
- * Imports scripts to the sandbox by reading files under urls and
- * evaluating its source. If exception occurs during evaluation
- * `'error'` event is emitted on the worker.
- * This is actually an analog to the `importScript` method in web
- * workers but in our case it's not exposed even though content
- * scripts may be able to do it synchronously since IO operation
- * takes place in the UI process.
- */
-function importScripts (workerSandbox, ...urls) {
- let { worker, sandbox } = modelFor(workerSandbox);
- for (let i in urls) {
- let contentScriptFile = data.url(urls[i]);
-
- try {
- let uri = URL(contentScriptFile);
- if (uri.scheme === 'resource')
- load(sandbox, String(uri));
- else
- throw Error('Unsupported `contentScriptFile` url: ' + String(uri));
- }
- catch(e) {
- emit(worker, 'error', e);
- }
- }
-}
-
-function setListeners (workerSandbox, console) {
- let { worker } = modelFor(workerSandbox);
- // console.xxx calls
- workerSandbox.on('console', function consoleListener (kind, ...args) {
- console[kind].apply(console, args);
- });
-
- // self.postMessage calls
- workerSandbox.on('message', function postMessage(data) {
- // destroyed?
- if (worker)
- emit(worker, 'message', data);
- });
-
- // self.port.emit calls
- workerSandbox.on('event', function portEmit (...eventArgs) {
- // If not destroyed, emit event information to worker
- // `eventArgs` has the event name as first element,
- // and remaining elements are additional arguments to pass
- if (worker)
- emit.apply(null, [worker.port].concat(eventArgs));
- });
-
- // unwrap, recreate and propagate async Errors thrown from content-script
- workerSandbox.on('error', function onError({instanceOfError, value}) {
- if (worker) {
- let error = value;
- if (instanceOfError) {
- error = new Error(value.message, value.fileName, value.lineNumber);
- error.stack = value.stack;
- error.name = value.name;
- }
- emit(worker, 'error', error);
- }
- });
-}
-
-/**
- * Evaluates code in the sandbox.
- * @param {String} code
- * JavaScript source to evaluate.
- * @param {String} [filename='javascript:' + code]
- * Name of the file
- */
-function evaluateIn (workerSandbox, code, filename) {
- let { worker, sandbox } = modelFor(workerSandbox);
- try {
- evaluate(sandbox, code, filename || 'javascript:' + code);
- }
- catch(e) {
- emit(worker, 'error', e);
- }
-}
-
-/**
- * Method called by the worker sandbox when it needs to send a message
- */
-function onContentEvent (workerSandbox, args) {
- // As `emit`, we ensure having an asynchronous behavior
- async(function () {
- // We emit event to chrome/addon listeners
- emit.apply(null, [workerSandbox].concat(JSON.parse(args)));
- });
-}
-
-
-function modelFor (workerSandbox) {
- return sandboxes.get(workerSandbox);
-}
-
-function getUnsafeWindow (win) {
- return win.wrappedJSObject || win;
-}
-
-function emitToContent (workerSandbox, args) {
- return modelFor(workerSandbox).emitToContent(args);
-}
-
-function createChromeAPI (scope) {
- return Cu.cloneInto({
- timers: {
- setTimeout: timer.setTimeout.bind(timer),
- setInterval: timer.setInterval.bind(timer),
- clearTimeout: timer.clearTimeout.bind(timer),
- clearInterval: timer.clearInterval.bind(timer),
- },
- sandbox: {
- evaluate: evaluate,
- },
- }, scope, {cloneFunctions: true});
-}
diff --git a/addon-sdk/source/lib/sdk/content/sandbox/events.js b/addon-sdk/source/lib/sdk/content/sandbox/events.js
deleted file mode 100644
index d6f7eb004..000000000
--- a/addon-sdk/source/lib/sdk/content/sandbox/events.js
+++ /dev/null
@@ -1,12 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-module.metadata = {
- "stability": "experimental"
-};
-
-const events = {};
-exports.events = events;
diff --git a/addon-sdk/source/lib/sdk/content/tab-events.js b/addon-sdk/source/lib/sdk/content/tab-events.js
deleted file mode 100644
index 9e244a853..000000000
--- a/addon-sdk/source/lib/sdk/content/tab-events.js
+++ /dev/null
@@ -1,58 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-"use strict";
-
-const { Ci } = require('chrome');
-const system = require('sdk/system/events');
-const { frames } = require('sdk/remote/child');
-const { WorkerChild } = require('sdk/content/worker-child');
-
-// map observer topics to tab event names
-const EVENTS = {
- 'content-document-global-created': 'create',
- 'chrome-document-global-created': 'create',
- 'content-document-interactive': 'ready',
- 'chrome-document-interactive': 'ready',
- 'content-document-loaded': 'load',
- 'chrome-document-loaded': 'load',
-// 'content-page-shown': 'pageshow', // bug 1024105
-}
-
-function topicListener({ subject, type }) {
- // NOTE detect the window from the subject:
- // - on *-global-created the subject is the window
- // - in the other cases it is the document object
- let window = subject instanceof Ci.nsIDOMWindow ? subject : subject.defaultView;
- if (!window){
- return;
- }
- let frame = frames.getFrameForWindow(window);
- if (frame) {
- let readyState = frame.content.document.readyState;
- frame.port.emit('sdk/tab/event', EVENTS[type], { readyState });
- }
-}
-
-for (let topic in EVENTS)
- system.on(topic, topicListener, true);
-
-// bug 1024105 - content-page-shown notification doesn't pass persisted param
-function eventListener({target, type, persisted}) {
- let frame = this;
- if (target === frame.content.document) {
- frame.port.emit('sdk/tab/event', type, persisted);
- }
-}
-frames.addEventListener('pageshow', eventListener, true);
-
-frames.port.on('sdk/tab/attach', (frame, options) => {
- options.window = frame.content;
- new WorkerChild(options);
-});
-
-// Forward the existent frames's readyState.
-for (let frame of frames) {
- let readyState = frame.content.document.readyState;
- frame.port.emit('sdk/tab/event', 'init', { readyState });
-}
diff --git a/addon-sdk/source/lib/sdk/content/thumbnail.js b/addon-sdk/source/lib/sdk/content/thumbnail.js
deleted file mode 100644
index 783615fc6..000000000
--- a/addon-sdk/source/lib/sdk/content/thumbnail.js
+++ /dev/null
@@ -1,51 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-'use strict';
-
-module.metadata = {
- 'stability': 'unstable'
-};
-
-const { Cc, Ci, Cu } = require('chrome');
-const AppShellService = Cc['@mozilla.org/appshell/appShellService;1'].
- getService(Ci.nsIAppShellService);
-
-const NS = 'http://www.w3.org/1999/xhtml';
-const COLOR = 'rgb(255,255,255)';
-
-/**
- * Creates canvas element with a thumbnail of the passed window.
- * @param {Window} window
- * @returns {Element}
- */
-function getThumbnailCanvasForWindow(window) {
- let aspectRatio = 0.5625; // 16:9
- let thumbnail = AppShellService.hiddenDOMWindow.document
- .createElementNS(NS, 'canvas');
- thumbnail.mozOpaque = true;
- thumbnail.width = Math.ceil(window.screen.availWidth / 5.75);
- thumbnail.height = Math.round(thumbnail.width * aspectRatio);
- let ctx = thumbnail.getContext('2d');
- let snippetWidth = window.innerWidth * .6;
- let scale = thumbnail.width / snippetWidth;
- ctx.scale(scale, scale);
- ctx.drawWindow(window, window.scrollX, window.scrollY, snippetWidth,
- snippetWidth * aspectRatio, COLOR);
- return thumbnail;
-}
-exports.getThumbnailCanvasForWindow = getThumbnailCanvasForWindow;
-
-/**
- * Creates Base64 encoded data URI of the thumbnail for the passed window.
- * @param {Window} window
- * @returns {String}
- */
-exports.getThumbnailURIForWindow = function getThumbnailURIForWindow(window) {
- return getThumbnailCanvasForWindow(window).toDataURL()
-};
-
-// default 80x45 blank when not available
-exports.BLANK = 'data:image/png;base64,' +
- 'iVBORw0KGgoAAAANSUhEUgAAAFAAAAAtCAYAAAA5reyyAAAAJElEQVRoge3BAQ'+
- 'EAAACCIP+vbkhAAQAAAAAAAAAAAAAAAADXBjhtAAGQ0AF/AAAAAElFTkSuQmCC';
diff --git a/addon-sdk/source/lib/sdk/content/utils.js b/addon-sdk/source/lib/sdk/content/utils.js
deleted file mode 100644
index 90995a614..000000000
--- a/addon-sdk/source/lib/sdk/content/utils.js
+++ /dev/null
@@ -1,105 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-'use strict';
-
-module.metadata = {
- 'stability': 'unstable'
-};
-
-var { merge } = require('../util/object');
-var { data } = require('../self');
-var assetsURI = data.url();
-var isArray = Array.isArray;
-var method = require('../../method/core');
-var { uuid } = require('../util/uuid');
-
-const isAddonContent = ({ contentURL }) =>
- contentURL && data.url(contentURL).startsWith(assetsURI);
-
-exports.isAddonContent = isAddonContent;
-
-function hasContentScript({ contentScript, contentScriptFile }) {
- return (isArray(contentScript) ? contentScript.length > 0 :
- !!contentScript) ||
- (isArray(contentScriptFile) ? contentScriptFile.length > 0 :
- !!contentScriptFile);
-}
-exports.hasContentScript = hasContentScript;
-
-function requiresAddonGlobal(model) {
- return model.injectInDocument || (isAddonContent(model) && !hasContentScript(model));
-}
-exports.requiresAddonGlobal = requiresAddonGlobal;
-
-function getAttachEventType(model) {
- if (!model) return null;
- let when = model.contentScriptWhen;
- return requiresAddonGlobal(model) ? 'document-element-inserted' :
- when === 'start' ? 'document-element-inserted' :
- when === 'ready' ? 'DOMContentLoaded' :
- when === 'end' ? 'load' :
- null;
-}
-exports.getAttachEventType = getAttachEventType;
-
-var attach = method('worker-attach');
-exports.attach = attach;
-
-var connect = method('worker-connect');
-exports.connect = connect;
-
-var detach = method('worker-detach');
-exports.detach = detach;
-
-var destroy = method('worker-destroy');
-exports.destroy = destroy;
-
-function WorkerHost (workerFor) {
- // Define worker properties that just proxy to underlying worker
- return ['postMessage', 'port', 'url', 'tab'].reduce(function(proto, name) {
- // Use descriptor properties instead so we can call
- // the worker function in the context of the worker so we
- // don't have to create new functions with `fn.bind(worker)`
- let descriptorProp = {
- value: function (...args) {
- let worker = workerFor(this);
- return worker[name].apply(worker, args);
- }
- };
-
- let accessorProp = {
- get: function () { return workerFor(this)[name]; },
- set: function (value) { workerFor(this)[name] = value; }
- };
-
- Object.defineProperty(proto, name, merge({
- enumerable: true,
- configurable: false,
- }, isDescriptor(name) ? descriptorProp : accessorProp));
- return proto;
- }, {});
-
- function isDescriptor (prop) {
- return ~['postMessage'].indexOf(prop);
- }
-}
-exports.WorkerHost = WorkerHost;
-
-function makeChildOptions(options) {
- function makeStringArray(arrayOrValue) {
- if (!arrayOrValue)
- return [];
- return [].concat(arrayOrValue).map(String);
- }
-
- return {
- id: String(uuid()),
- contentScript: makeStringArray(options.contentScript),
- contentScriptFile: makeStringArray(options.contentScriptFile),
- contentScriptOptions: options.contentScriptOptions ?
- JSON.stringify(options.contentScriptOptions) :
- null,
- }
-}
-exports.makeChildOptions = makeChildOptions;
diff --git a/addon-sdk/source/lib/sdk/content/worker-child.js b/addon-sdk/source/lib/sdk/content/worker-child.js
deleted file mode 100644
index dbf65a933..000000000
--- a/addon-sdk/source/lib/sdk/content/worker-child.js
+++ /dev/null
@@ -1,158 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-'use strict';
-
-const { merge } = require('../util/object');
-const { Class } = require('../core/heritage');
-const { emit } = require('../event/core');
-const { EventTarget } = require('../event/target');
-const { getInnerId, getByInnerId } = require('../window/utils');
-const { instanceOf, isObject } = require('../lang/type');
-const system = require('../system/events');
-const { when } = require('../system/unload');
-const { WorkerSandbox } = require('./sandbox');
-const { Ci } = require('chrome');
-const { process, frames } = require('../remote/child');
-
-const EVENTS = {
- 'chrome-page-shown': 'pageshow',
- 'content-page-shown': 'pageshow',
- 'chrome-page-hidden': 'pagehide',
- 'content-page-hidden': 'pagehide',
- 'inner-window-destroyed': 'detach',
-}
-
-// The parent Worker must have been created (or an async message sent to spawn
-// its creation) before creating the WorkerChild or messages from the content
-// script to the parent will get lost.
-const WorkerChild = Class({
- implements: [EventTarget],
-
- initialize(options) {
- merge(this, options);
- keepAlive.set(this.id, this);
-
- this.windowId = getInnerId(this.window);
- if (this.contentScriptOptions)
- this.contentScriptOptions = JSON.parse(this.contentScriptOptions);
-
- this.port = EventTarget();
- this.port.on('*', this.send.bind(this, 'event'));
- this.on('*', this.send.bind(this));
-
- this.observe = this.observe.bind(this);
-
- for (let topic in EVENTS)
- system.on(topic, this.observe);
-
- this.receive = this.receive.bind(this);
- process.port.on('sdk/worker/message', this.receive);
-
- this.sandbox = WorkerSandbox(this, this.window);
-
- // If the document has an unexpected readyState, its worker-child instance is initialized
- // as frozen until one of the known readyState is reached.
- let initialDocumentReadyState = this.window.document.readyState;
- this.frozen = [
- "loading", "interactive", "complete"
- ].includes(initialDocumentReadyState) ? false : true;
-
- if (this.frozen) {
- console.warn("SDK worker-child started as frozen on unexpected initial document.readyState", {
- initialDocumentReadyState, windowLocation: this.window.location.href,
- });
- }
-
- this.frozenMessages = [];
- this.on('pageshow', () => {
- this.frozen = false;
- this.frozenMessages.forEach(args => this.sandbox.emit(...args));
- this.frozenMessages = [];
- });
- this.on('pagehide', () => {
- this.frozen = true;
- });
- },
-
- // messages
- receive(process, id, args) {
- if (id !== this.id)
- return;
- args = JSON.parse(args);
-
- if (this.frozen)
- this.frozenMessages.push(args);
- else
- this.sandbox.emit(...args);
-
- if (args[0] === 'detach')
- this.destroy(args[1]);
- },
-
- send(...args) {
- process.port.emit('sdk/worker/event', this.id, JSON.stringify(args, exceptions));
- },
-
- // notifications
- observe({ type, subject }) {
- if (!this.sandbox)
- return;
-
- if (subject.defaultView && getInnerId(subject.defaultView) === this.windowId) {
- this.sandbox.emitSync(EVENTS[type]);
- emit(this, EVENTS[type]);
- }
-
- if (type === 'inner-window-destroyed' &&
- subject.QueryInterface(Ci.nsISupportsPRUint64).data === this.windowId) {
- this.destroy();
- }
- },
-
- get frame() {
- return frames.getFrameForWindow(this.window.top);
- },
-
- // detach/destroy: unload and release the sandbox
- destroy(reason) {
- if (!this.sandbox)
- return;
-
- for (let topic in EVENTS)
- system.off(topic, this.observe);
- process.port.off('sdk/worker/message', this.receive);
-
- this.sandbox.destroy(reason);
- this.sandbox = null;
- keepAlive.delete(this.id);
-
- this.send('detach');
- }
-})
-exports.WorkerChild = WorkerChild;
-
-// Error instances JSON poorly
-function exceptions(key, value) {
- if (!isObject(value) || !instanceOf(value, Error))
- return value;
- let _errorType = value.constructor.name;
- let { message, fileName, lineNumber, stack, name } = value;
- return { _errorType, message, fileName, lineNumber, stack, name };
-}
-
-// workers for windows in this tab
-var keepAlive = new Map();
-
-process.port.on('sdk/worker/create', (process, options, cpows) => {
- options.window = cpows.window;
- let worker = new WorkerChild(options);
-
- let frame = frames.getFrameForWindow(options.window.top);
- frame.port.emit('sdk/worker/connect', options.id, options.window.location.href);
-});
-
-when(reason => {
- for (let worker of keepAlive.values())
- worker.destroy(reason);
-});
diff --git a/addon-sdk/source/lib/sdk/content/worker.js b/addon-sdk/source/lib/sdk/content/worker.js
deleted file mode 100644
index 39b940a88..000000000
--- a/addon-sdk/source/lib/sdk/content/worker.js
+++ /dev/null
@@ -1,180 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-"use strict";
-
-module.metadata = {
- "stability": "unstable"
-};
-
-const { emit } = require('../event/core');
-const { omit, merge } = require('../util/object');
-const { Class } = require('../core/heritage');
-const { method } = require('../lang/functional');
-const { getInnerId } = require('../window/utils');
-const { EventTarget } = require('../event/target');
-const { isPrivate } = require('../private-browsing/utils');
-const { getTabForBrowser, getTabForContentWindowNoShim, getBrowserForTab } = require('../tabs/utils');
-const { attach, connect, detach, destroy, makeChildOptions } = require('./utils');
-const { ensure } = require('../system/unload');
-const { on: observe } = require('../system/events');
-const { Ci, Cu } = require('chrome');
-const { modelFor: tabFor } = require('sdk/model/core');
-const { remoteRequire, processes, frames } = require('../remote/parent');
-remoteRequire('sdk/content/worker-child');
-
-const workers = new WeakMap();
-var modelFor = (worker) => workers.get(worker);
-
-const ERR_DESTROYED = "Couldn't find the worker to receive this message. " +
- "The script may not be initialized yet, or may already have been unloaded.";
-
-// a handle for communication between content script and addon code
-const Worker = Class({
- implements: [EventTarget],
-
- initialize(options = {}) {
- ensure(this, 'detach');
-
- let model = {
- attached: false,
- destroyed: false,
- earlyEvents: [], // fired before worker was attached
- frozen: true, // document is not yet active
- options,
- };
- workers.set(this, model);
-
- this.on('detach', this.detach);
- EventTarget.prototype.initialize.call(this, options);
-
- this.receive = this.receive.bind(this);
-
- this.port = EventTarget();
- this.port.emit = this.send.bind(this, 'event');
- this.postMessage = this.send.bind(this, 'message');
-
- if ('window' in options) {
- let window = options.window;
- delete options.window;
- attach(this, window);
- }
- },
-
- // messages
- receive(process, id, args) {
- let model = modelFor(this);
- if (id !== model.id || !model.attached)
- return;
- args = JSON.parse(args);
- if (model.destroyed && args[0] != 'detach')
- return;
-
- if (args[0] === 'event')
- emit(this.port, ...args.slice(1))
- else
- emit(this, ...args);
- },
-
- send(...args) {
- let model = modelFor(this);
- if (model.destroyed && args[0] !== 'detach')
- throw new Error(ERR_DESTROYED);
-
- if (!model.attached) {
- model.earlyEvents.push(args);
- return;
- }
-
- processes.port.emit('sdk/worker/message', model.id, JSON.stringify(args));
- },
-
- // properties
- get url() {
- let { url } = modelFor(this);
- return url;
- },
-
- get contentURL() {
- return this.url;
- },
-
- get tab() {
- require('sdk/tabs');
- let { frame } = modelFor(this);
- if (!frame)
- return null;
- let rawTab = getTabForBrowser(frame.frameElement);
- return rawTab && tabFor(rawTab);
- },
-
- toString: () => '[object Worker]',
-
- detach: method(detach),
- destroy: method(destroy),
-})
-exports.Worker = Worker;
-
-attach.define(Worker, function(worker, window) {
- let model = modelFor(worker);
- if (model.attached)
- detach(worker);
-
- let childOptions = makeChildOptions(model.options);
- processes.port.emitCPOW('sdk/worker/create', [childOptions], { window });
-
- let listener = (frame, id, url) => {
- if (id != childOptions.id)
- return;
- frames.port.off('sdk/worker/connect', listener);
- connect(worker, frame, { id, url });
- };
- frames.port.on('sdk/worker/connect', listener);
-});
-
-connect.define(Worker, function(worker, frame, { id, url }) {
- let model = modelFor(worker);
- if (model.attached)
- detach(worker);
-
- model.id = id;
- model.frame = frame;
- model.url = url;
-
- // Messages from content -> chrome come through the process message manager
- // since that lives longer than the frame message manager
- processes.port.on('sdk/worker/event', worker.receive);
-
- model.attached = true;
- model.destroyed = false;
- model.frozen = false;
-
- model.earlyEvents.forEach(args => worker.send(...args));
- model.earlyEvents = [];
- emit(worker, 'attach');
-});
-
-// unload and release the child worker, release window reference
-detach.define(Worker, function(worker) {
- let model = modelFor(worker);
- if (!model.attached)
- return;
-
- processes.port.off('sdk/worker/event', worker.receive);
- model.attached = false;
- model.destroyed = true;
- emit(worker, 'detach');
-});
-
-isPrivate.define(Worker, ({ tab }) => isPrivate(tab));
-
-// Something in the parent side has destroyed the worker, tell the child to
-// detach, the child will respond when it has detached
-destroy.define(Worker, function(worker, reason) {
- let model = modelFor(worker);
- model.destroyed = true;
- if (!model.attached)
- return;
-
- worker.send('detach', reason);
-});