diff options
author | Matt A. Tobin <email@mattatobin.com> | 2018-02-09 06:46:43 -0500 |
---|---|---|
committer | Matt A. Tobin <email@mattatobin.com> | 2018-02-09 06:46:43 -0500 |
commit | ac46df8daea09899ce30dc8fd70986e258c746bf (patch) | |
tree | 2750d3125fc253fd5b0671e4bd268eff1fd97296 /addon-sdk/source/lib/sdk/remote | |
parent | 8cecf8d5208f3945b35f879bba3015bb1a11bec6 (diff) | |
download | UXP-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/remote')
-rw-r--r-- | addon-sdk/source/lib/sdk/remote/child.js | 284 | ||||
-rw-r--r-- | addon-sdk/source/lib/sdk/remote/core.js | 8 | ||||
-rw-r--r-- | addon-sdk/source/lib/sdk/remote/parent.js | 338 | ||||
-rw-r--r-- | addon-sdk/source/lib/sdk/remote/utils.js | 39 |
4 files changed, 0 insertions, 669 deletions
diff --git a/addon-sdk/source/lib/sdk/remote/child.js b/addon-sdk/source/lib/sdk/remote/child.js deleted file mode 100644 index 4ccfa661a..000000000 --- a/addon-sdk/source/lib/sdk/remote/child.js +++ /dev/null @@ -1,284 +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 { isChildLoader } = require('./core'); -if (!isChildLoader) - throw new Error("Cannot load sdk/remote/child in a main process loader."); - -const { Ci, Cc, Cu } = require('chrome'); -const runtime = require('../system/runtime'); -const { Class } = require('../core/heritage'); -const { Namespace } = require('../core/namespace'); -const { omit } = require('../util/object'); -const { when } = require('../system/unload'); -const { EventTarget } = require('../event/target'); -const { emit } = require('../event/core'); -const { Disposable } = require('../core/disposable'); -const { EventParent } = require('./utils'); -const { addListItem, removeListItem } = require('../util/list'); - -const loaderID = require('@loader/options').loaderID; - -const MAIN_PROCESS = Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT; - -const mm = Cc['@mozilla.org/childprocessmessagemanager;1']. - getService(Ci.nsISyncMessageSender); - -const ns = Namespace(); - -const process = { - port: new EventTarget(), - get id() { - return runtime.processID; - }, - get isRemote() { - return runtime.processType != MAIN_PROCESS; - } -}; -exports.process = process; - -function definePort(obj, name) { - obj.port.emit = (event, ...args) => { - let manager = ns(obj).messageManager; - if (!manager) - return; - - manager.sendAsyncMessage(name, { loaderID, event, args }); - }; -} - -function messageReceived({ data, objects }) { - // Ignore messages from other loaders - if (data.loaderID != loaderID) - return; - - let keys = Object.keys(objects); - if (keys.length) { - // If any objects are CPOWs then ignore this message. We don't want child - // processes interracting with CPOWs - if (!keys.every(name => !Cu.isCrossProcessWrapper(objects[name]))) - return; - - data.args.push(objects); - } - - emit(this.port, data.event, this, ...data.args); -} - -ns(process).messageManager = mm; -definePort(process, 'sdk/remote/process/message'); -let processMessageReceived = messageReceived.bind(process); -mm.addMessageListener('sdk/remote/process/message', processMessageReceived); - -when(() => { - mm.removeMessageListener('sdk/remote/process/message', processMessageReceived); - frames = null; -}); - -process.port.on('sdk/remote/require', (process, uri) => { - require(uri); -}); - -function listenerEquals(a, b) { - for (let prop of ["type", "callback", "isCapturing"]) { - if (a[prop] != b[prop]) - return false; - } - return true; -} - -function listenerFor(type, callback, isCapturing = false) { - return { - type, - callback, - isCapturing, - registeredCallback: undefined, - get args() { - return [ - this.type, - this.registeredCallback ? this.registeredCallback : this.callback, - this.isCapturing - ]; - } - }; -} - -function removeListenerFromArray(array, listener) { - let index = array.findIndex(l => listenerEquals(l, listener)); - if (index < 0) - return; - array.splice(index, 1); -} - -function getListenerFromArray(array, listener) { - return array.find(l => listenerEquals(l, listener)); -} - -function arrayContainsListener(array, listener) { - return !!getListenerFromArray(array, listener); -} - -function makeFrameEventListener(frame, callback) { - return callback.bind(frame); -} - -var FRAME_ID = 0; -var tabMap = new Map(); - -const Frame = Class({ - implements: [ Disposable ], - extends: EventTarget, - setup: function(contentFrame) { - // This ID should be unique for this loader across all processes - let priv = ns(this); - - priv.id = runtime.processID + ":" + FRAME_ID++; - - priv.contentFrame = contentFrame; - priv.messageManager = contentFrame; - priv.domListeners = []; - - tabMap.set(contentFrame.docShell, this); - - priv.messageReceived = messageReceived.bind(this); - priv.messageManager.addMessageListener('sdk/remote/frame/message', priv.messageReceived); - - this.port = new EventTarget(); - definePort(this, 'sdk/remote/frame/message'); - - priv.messageManager.sendAsyncMessage('sdk/remote/frame/attach', { - loaderID, - frameID: priv.id, - processID: runtime.processID - }); - - frames.attachItem(this); - }, - - dispose: function() { - let priv = ns(this); - - emit(this, 'detach', this); - - for (let listener of priv.domListeners) - priv.contentFrame.removeEventListener(...listener.args); - - priv.messageManager.removeMessageListener('sdk/remote/frame/message', priv.messageReceived); - tabMap.delete(priv.contentFrame.docShell); - priv.contentFrame = null; - }, - - get content() { - return ns(this).contentFrame.content; - }, - - get isTab() { - let docShell = ns(this).contentFrame.docShell; - if (process.isRemote) { - // We don't want to roundtrip to the main process to get this property. - // This hack relies on the host app having defined webBrowserChrome only - // in frames that are part of the tabs. Since only Firefox has remote - // processes right now and does this this works. - let tabchild = docShell.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsITabChild); - return !!tabchild.webBrowserChrome; - } - else { - // This is running in the main process so we can break out to the browser - // And check we can find a tab for the browser element directly. - let browser = docShell.chromeEventHandler; - let tab = require('../tabs/utils').getTabForBrowser(browser); - return !!tab; - } - }, - - addEventListener: function(...args) { - let priv = ns(this); - - let listener = listenerFor(...args); - if (arrayContainsListener(priv.domListeners, listener)) - return; - - listener.registeredCallback = makeFrameEventListener(this, listener.callback); - - priv.domListeners.push(listener); - priv.contentFrame.addEventListener(...listener.args); - }, - - removeEventListener: function(...args) { - let priv = ns(this); - - let listener = getListenerFromArray(priv.domListeners, listenerFor(...args)); - if (!listener) - return; - - removeListenerFromArray(priv.domListeners, listener); - priv.contentFrame.removeEventListener(...listener.args); - } -}); - -const FrameList = Class({ - implements: [ EventParent, Disposable ], - extends: EventTarget, - setup: function() { - EventParent.prototype.initialize.call(this); - - this.port = new EventTarget(); - ns(this).domListeners = []; - - this.on('attach', frame => { - for (let listener of ns(this).domListeners) - frame.addEventListener(...listener.args); - }); - }, - - dispose: function() { - // The only case where we get destroyed is when the loader is unloaded in - // which case each frame will clean up its own event listeners. - ns(this).domListeners = null; - }, - - getFrameForWindow: function(window) { - let docShell = window.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDocShell); - - return tabMap.get(docShell) || null; - }, - - addEventListener: function(...args) { - let listener = listenerFor(...args); - if (arrayContainsListener(ns(this).domListeners, listener)) - return; - - ns(this).domListeners.push(listener); - for (let frame of this) - frame.addEventListener(...listener.args); - }, - - removeEventListener: function(...args) { - let listener = listenerFor(...args); - if (!arrayContainsListener(ns(this).domListeners, listener)) - return; - - removeListenerFromArray(ns(this).domListeners, listener); - for (let frame of this) - frame.removeEventListener(...listener.args); - } -}); -var frames = exports.frames = new FrameList(); - -function registerContentFrame(contentFrame) { - let frame = new Frame(contentFrame); -} -exports.registerContentFrame = registerContentFrame; - -function unregisterContentFrame(contentFrame) { - let frame = tabMap.get(contentFrame.docShell); - if (!frame) - return; - - frame.destroy(); -} -exports.unregisterContentFrame = unregisterContentFrame; diff --git a/addon-sdk/source/lib/sdk/remote/core.js b/addon-sdk/source/lib/sdk/remote/core.js deleted file mode 100644 index 78bb673fd..000000000 --- a/addon-sdk/source/lib/sdk/remote/core.js +++ /dev/null @@ -1,8 +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 options = require("@loader/options"); - -exports.isChildLoader = options.childLoader; diff --git a/addon-sdk/source/lib/sdk/remote/parent.js b/addon-sdk/source/lib/sdk/remote/parent.js deleted file mode 100644 index f110fe3f6..000000000 --- a/addon-sdk/source/lib/sdk/remote/parent.js +++ /dev/null @@ -1,338 +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 { isChildLoader } = require('./core'); -if (isChildLoader) - throw new Error("Cannot load sdk/remote/parent in a child loader."); - -const { Cu, Ci, Cc } = require('chrome'); -const runtime = require('../system/runtime'); - -const MAIN_PROCESS = Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT; - -if (runtime.processType != MAIN_PROCESS) { - throw new Error('Cannot use sdk/remote/parent in a child process.'); -} - -const { Class } = require('../core/heritage'); -const { Namespace } = require('../core/namespace'); -const { Disposable } = require('../core/disposable'); -const { omit } = require('../util/object'); -const { when } = require('../system/unload'); -const { EventTarget } = require('../event/target'); -const { emit } = require('../event/core'); -const system = require('../system/events'); -const { EventParent } = require('./utils'); -const options = require('@loader/options'); -const loaderModule = require('toolkit/loader'); -const { getTabForBrowser } = require('../tabs/utils'); - -const appInfo = Cc["@mozilla.org/xre/app-info;1"]. - getService(Ci.nsIXULRuntime); - -exports.useRemoteProcesses = appInfo.browserTabsRemoteAutostart; - -// Chose the right function for resolving relative a module id -var moduleResolve; -if (options.isNative) { - moduleResolve = (id, requirer) => loaderModule.nodeResolve(id, requirer, { rootURI: options.rootURI }); -} -else { - moduleResolve = loaderModule.resolve; -} -// Build the sorted path mapping structure that resolveURI requires -var pathMapping = Object.keys(options.paths) - .sort((a, b) => b.length - a.length) - .map(p => [p, options.paths[p]]); - -// Load the scripts in the child processes -var { getNewLoaderID } = require('../../framescript/FrameScriptManager.jsm'); -var PATH = options.paths['']; - -const childOptions = omit(options, ['modules', 'globals', 'resolve', 'load']); -childOptions.modules = {}; -// @l10n/data is just JSON data and can be safely sent across to the child loader -try { - childOptions.modules["@l10n/data"] = require("@l10n/data"); -} -catch (e) { - // There may be no l10n data -} -const loaderID = getNewLoaderID(); -childOptions.loaderID = loaderID; -childOptions.childLoader = true; - -const ppmm = Cc['@mozilla.org/parentprocessmessagemanager;1']. - getService(Ci.nsIMessageBroadcaster); -const gmm = Cc['@mozilla.org/globalmessagemanager;1']. - getService(Ci.nsIMessageBroadcaster); - -const ns = Namespace(); - -var processMap = new Map(); - -function definePort(obj, name) { - obj.port.emitCPOW = (event, args, cpows = {}) => { - let manager = ns(obj).messageManager; - if (!manager) - return; - - let method = manager instanceof Ci.nsIMessageBroadcaster ? - "broadcastAsyncMessage" : "sendAsyncMessage"; - - manager[method](name, { loaderID, event, args }, cpows); - }; - - obj.port.emit = (event, ...args) => obj.port.emitCPOW(event, args); -} - -function messageReceived({ target, data }) { - // Ignore messages from other loaders - if (data.loaderID != loaderID) - return; - - emit(this.port, data.event, this, ...data.args); -} - -// Process represents a gecko process that can load webpages. Each process -// contains a number of Frames. This class is used to send and receive messages -// from a single process. -const Process = Class({ - implements: [ Disposable ], - extends: EventTarget, - setup: function(id, messageManager, isRemote) { - ns(this).id = id; - ns(this).isRemote = isRemote; - ns(this).messageManager = messageManager; - ns(this).messageReceived = messageReceived.bind(this); - this.destroy = this.destroy.bind(this); - ns(this).messageManager.addMessageListener('sdk/remote/process/message', ns(this).messageReceived); - ns(this).messageManager.addMessageListener('child-process-shutdown', this.destroy); - - this.port = new EventTarget(); - definePort(this, 'sdk/remote/process/message'); - - // Load any remote modules - for (let module of remoteModules.values()) - this.port.emit('sdk/remote/require', module); - - processMap.set(ns(this).id, this); - processes.attachItem(this); - }, - - dispose: function() { - emit(this, 'detach', this); - processMap.delete(ns(this).id); - ns(this).messageManager.removeMessageListener('sdk/remote/process/message', ns(this).messageReceived); - ns(this).messageManager.removeMessageListener('child-process-shutdown', this.destroy); - ns(this).messageManager = null; - }, - - // Returns true if this process is a child process - get isRemote() { - return ns(this).isRemote; - } -}); - -// Processes gives an API for enumerating an sending and receiving messages from -// all processes as well as detecting when a new process starts. -const Processes = Class({ - implements: [ EventParent ], - extends: EventTarget, - initialize: function() { - EventParent.prototype.initialize.call(this); - ns(this).messageManager = ppmm; - - this.port = new EventTarget(); - definePort(this, 'sdk/remote/process/message'); - }, - - getById: function(id) { - return processMap.get(id); - } -}); -var processes = exports.processes = new Processes(); - -var frameMap = new Map(); - -function setFrameProcess(frame, process) { - ns(frame).process = process; - frames.attachItem(frame); -} - -// Frames display webpages in a process. In the main process every Frame is -// linked with a <browser> or <iframe> element. -const Frame = Class({ - implements: [ Disposable ], - extends: EventTarget, - setup: function(id, node) { - ns(this).id = id; - ns(this).node = node; - - let frameLoader = node.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader; - ns(this).messageManager = frameLoader.messageManager; - - ns(this).messageReceived = messageReceived.bind(this); - ns(this).messageManager.addMessageListener('sdk/remote/frame/message', ns(this).messageReceived); - - this.port = new EventTarget(); - definePort(this, 'sdk/remote/frame/message'); - - frameMap.set(ns(this).messageManager, this); - }, - - dispose: function() { - emit(this, 'detach', this); - ns(this).messageManager.removeMessageListener('sdk/remote/frame/message', ns(this).messageReceived); - - frameMap.delete(ns(this).messageManager); - ns(this).messageManager = null; - }, - - // Returns the browser or iframe element this frame displays in - get frameElement() { - return ns(this).node; - }, - - // Returns the process that this frame loads in - get process() { - return ns(this).process; - }, - - // Returns true if this frame is a tab in a main browser window - get isTab() { - let tab = getTabForBrowser(ns(this).node); - return !!tab; - } -}); - -function managerDisconnected({ subject: manager }) { - let frame = frameMap.get(manager); - if (frame) - frame.destroy(); -} -system.on('message-manager-disconnect', managerDisconnected); - -// Provides an API for enumerating and sending and receiving messages from all -// Frames -const FrameList = Class({ - implements: [ EventParent ], - extends: EventTarget, - initialize: function() { - EventParent.prototype.initialize.call(this); - ns(this).messageManager = gmm; - - this.port = new EventTarget(); - definePort(this, 'sdk/remote/frame/message'); - }, - - // Returns the frame for a browser element - getFrameForBrowser: function(browser) { - for (let frame of this) { - if (frame.frameElement == browser) - return frame; - } - return null; - }, -}); -var frames = exports.frames = new FrameList(); - -// Create the module loader in any existing processes -ppmm.broadcastAsyncMessage('sdk/remote/process/load', { - modulePath: PATH, - loaderID, - options: childOptions, - reason: "broadcast" -}); - -// A loader has started in a remote process -function processLoaderStarted({ target, data }) { - if (data.loaderID != loaderID) - return; - - if (processMap.has(data.processID)) { - console.error("Saw the same process load the same loader twice. This is a bug in the SDK."); - return; - } - - let process = new Process(data.processID, target, data.isRemote); - - if (pendingFrames.has(data.processID)) { - for (let frame of pendingFrames.get(data.processID)) - setFrameProcess(frame, process); - pendingFrames.delete(data.processID); - } -} - -// A new process has started -function processStarted({ target, data: { modulePath } }) { - if (modulePath != PATH) - return; - - // Have it load a loader if it hasn't already - target.sendAsyncMessage('sdk/remote/process/load', { - modulePath, - loaderID, - options: childOptions, - reason: "response" - }); -} - -var pendingFrames = new Map(); - -// A new frame has been created in the remote process -function frameAttached({ target, data }) { - if (data.loaderID != loaderID) - return; - - let frame = new Frame(data.frameID, target); - - let process = processMap.get(data.processID); - if (process) { - setFrameProcess(frame, process); - return; - } - - // In some cases frame messages can arrive earlier than process messages - // causing us to see a new frame appear before its process. In this case - // cache the frame data until we see the process. See bug 1131375. - if (!pendingFrames.has(data.processID)) - pendingFrames.set(data.processID, [frame]); - else - pendingFrames.get(data.processID).push(frame); -} - -// Wait for new processes and frames -ppmm.addMessageListener('sdk/remote/process/attach', processLoaderStarted); -ppmm.addMessageListener('sdk/remote/process/start', processStarted); -gmm.addMessageListener('sdk/remote/frame/attach', frameAttached); - -when(reason => { - ppmm.removeMessageListener('sdk/remote/process/attach', processLoaderStarted); - ppmm.removeMessageListener('sdk/remote/process/start', processStarted); - gmm.removeMessageListener('sdk/remote/frame/attach', frameAttached); - - ppmm.broadcastAsyncMessage('sdk/remote/process/unload', { loaderID, reason }); -}); - -var remoteModules = new Set(); - -// Ensures a module is loaded in every child process. It is safe to send -// messages to this module immediately after calling this. -// Pass a module to resolve the id relatively. -function remoteRequire(id, module = null) { - // Resolve relative to calling module if passed - if (module) - id = moduleResolve(id, module.id); - let uri = loaderModule.resolveURI(id, pathMapping); - - // Don't reload the same module - if (remoteModules.has(uri)) - return; - - remoteModules.add(uri); - processes.port.emit('sdk/remote/require', uri); -} -exports.remoteRequire = remoteRequire; diff --git a/addon-sdk/source/lib/sdk/remote/utils.js b/addon-sdk/source/lib/sdk/remote/utils.js deleted file mode 100644 index 5a5e39198..000000000 --- a/addon-sdk/source/lib/sdk/remote/utils.js +++ /dev/null @@ -1,39 +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 { List, addListItem, removeListItem } = require('../util/list'); -const { emit } = require('../event/core'); -const { pipe } = require('../event/utils'); - -// A helper class that maintains a list of EventTargets. Any events emitted -// to an EventTarget are also emitted by the EventParent. Likewise for an -// EventTarget's port property. -const EventParent = Class({ - implements: [ List ], - - attachItem: function(item) { - addListItem(this, item); - - pipe(item.port, this.port); - pipe(item, this); - - item.once('detach', () => { - removeListItem(this, item); - }) - - emit(this, 'attach', item); - }, - - // Calls listener for every object already in the list and every object - // subsequently added to the list. - forEvery: function(listener) { - for (let item of this) - listener(item); - - this.on('attach', listener); - } -}); -exports.EventParent = EventParent; |