addon-sdk/source/lib/sdk/deprecated/symbiont.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4 "use strict";
michael@0 5
michael@0 6 module.metadata = {
michael@0 7 "stability": "deprecated"
michael@0 8 };
michael@0 9
michael@0 10 const { Worker } = require('./traits-worker');
michael@0 11 const { Loader } = require('../content/loader');
michael@0 12 const hiddenFrames = require('../frame/hidden-frame');
michael@0 13 const { on, off } = require('../system/events');
michael@0 14 const unload = require('../system/unload');
michael@0 15 const { getDocShell } = require("../frame/utils");
michael@0 16 const { ignoreWindow } = require('../private-browsing/utils');
michael@0 17
michael@0 18 // Everything coming from add-on's xpi considered an asset.
michael@0 19 const assetsURI = require('../self').data.url().replace(/data\/$/, "");
michael@0 20
michael@0 21 /**
michael@0 22 * This trait is layered on top of `Worker` and in contrast to symbiont
michael@0 23 * Worker constructor requires `content` option that represents content
michael@0 24 * that will be loaded in the provided frame, if frame is not provided
michael@0 25 * Worker will create hidden one.
michael@0 26 */
michael@0 27 const Symbiont = Worker.resolve({
michael@0 28 constructor: '_initWorker',
michael@0 29 destroy: '_workerDestroy'
michael@0 30 }).compose(Loader, {
michael@0 31
michael@0 32 /**
michael@0 33 * The constructor requires all the options that are required by
michael@0 34 * `require('content').Worker` with the difference that the `frame` option
michael@0 35 * is optional. If `frame` is not provided, `contentURL` is expected.
michael@0 36 * @param {Object} options
michael@0 37 * @param {String} options.contentURL
michael@0 38 * URL of a content to load into `this._frame` and create worker for.
michael@0 39 * @param {Element} [options.frame]
michael@0 40 * iframe element that is used to load `options.contentURL` into.
michael@0 41 * if frame is not provided hidden iframe will be created.
michael@0 42 */
michael@0 43 constructor: function Symbiont(options) {
michael@0 44 options = options || {};
michael@0 45
michael@0 46 if ('contentURL' in options)
michael@0 47 this.contentURL = options.contentURL;
michael@0 48 if ('contentScriptWhen' in options)
michael@0 49 this.contentScriptWhen = options.contentScriptWhen;
michael@0 50 if ('contentScriptOptions' in options)
michael@0 51 this.contentScriptOptions = options.contentScriptOptions;
michael@0 52 if ('contentScriptFile' in options)
michael@0 53 this.contentScriptFile = options.contentScriptFile;
michael@0 54 if ('contentScript' in options)
michael@0 55 this.contentScript = options.contentScript;
michael@0 56 if ('allow' in options)
michael@0 57 this.allow = options.allow;
michael@0 58 if ('onError' in options)
michael@0 59 this.on('error', options.onError);
michael@0 60 if ('onMessage' in options)
michael@0 61 this.on('message', options.onMessage);
michael@0 62 if ('frame' in options) {
michael@0 63 this._initFrame(options.frame);
michael@0 64 }
michael@0 65 else {
michael@0 66 let self = this;
michael@0 67 this._hiddenFrame = hiddenFrames.HiddenFrame({
michael@0 68 onReady: function onFrame() {
michael@0 69 self._initFrame(this.element);
michael@0 70 },
michael@0 71 onUnload: function onUnload() {
michael@0 72 // Bug 751211: Remove reference to _frame when hidden frame is
michael@0 73 // automatically removed on unload, otherwise we are going to face
michael@0 74 // "dead object" exception
michael@0 75 self.destroy();
michael@0 76 }
michael@0 77 });
michael@0 78 hiddenFrames.add(this._hiddenFrame);
michael@0 79 }
michael@0 80
michael@0 81 unload.ensure(this._public, "destroy");
michael@0 82 },
michael@0 83
michael@0 84 destroy: function destroy() {
michael@0 85 this._workerDestroy();
michael@0 86 this._unregisterListener();
michael@0 87 this._frame = null;
michael@0 88 if (this._hiddenFrame) {
michael@0 89 hiddenFrames.remove(this._hiddenFrame);
michael@0 90 this._hiddenFrame = null;
michael@0 91 }
michael@0 92 },
michael@0 93
michael@0 94 /**
michael@0 95 * XUL iframe or browser elements with attribute `type` being `content`.
michael@0 96 * Used to create `ContentSymbiont` from.
michael@0 97 * @type {nsIFrame|nsIBrowser}
michael@0 98 */
michael@0 99 _frame: null,
michael@0 100
michael@0 101 /**
michael@0 102 * Listener to the `'frameReady"` event (emitted when `iframe` is ready).
michael@0 103 * Removes listener, sets right permissions to the frame and loads content.
michael@0 104 */
michael@0 105 _initFrame: function _initFrame(frame) {
michael@0 106 if (this._loadListener)
michael@0 107 this._unregisterListener();
michael@0 108
michael@0 109 this._frame = frame;
michael@0 110
michael@0 111 if (getDocShell(frame)) {
michael@0 112 this._reallyInitFrame(frame);
michael@0 113 }
michael@0 114 else {
michael@0 115 if (this._waitForFrame) {
michael@0 116 off('content-document-global-created', this._waitForFrame);
michael@0 117 }
michael@0 118 this._waitForFrame = this.__waitForFrame.bind(this, frame);
michael@0 119 on('content-document-global-created', this._waitForFrame);
michael@0 120 }
michael@0 121 },
michael@0 122
michael@0 123 __waitForFrame: function _waitForFrame(frame, { subject: win }) {
michael@0 124 if (frame.contentWindow == win) {
michael@0 125 off('content-document-global-created', this._waitForFrame);
michael@0 126 delete this._waitForFrame;
michael@0 127 this._reallyInitFrame(frame);
michael@0 128 }
michael@0 129 },
michael@0 130
michael@0 131 _reallyInitFrame: function _reallyInitFrame(frame) {
michael@0 132 getDocShell(frame).allowJavascript = this.allow.script;
michael@0 133 frame.setAttribute("src", this._contentURL);
michael@0 134
michael@0 135 // Inject `addon` object in document if we load a document from
michael@0 136 // one of our addon folder and if no content script are defined. bug 612726
michael@0 137 let isDataResource =
michael@0 138 typeof this._contentURL == "string" &&
michael@0 139 this._contentURL.indexOf(assetsURI) == 0;
michael@0 140 let hasContentScript =
michael@0 141 (Array.isArray(this.contentScript) ? this.contentScript.length > 0
michael@0 142 : !!this.contentScript) ||
michael@0 143 (Array.isArray(this.contentScriptFile) ? this.contentScriptFile.length > 0
michael@0 144 : !!this.contentScriptFile);
michael@0 145 // If we have to inject `addon` we have to do it before document
michael@0 146 // script execution, so during `start`:
michael@0 147 this._injectInDocument = isDataResource && !hasContentScript;
michael@0 148 if (this._injectInDocument)
michael@0 149 this.contentScriptWhen = "start";
michael@0 150
michael@0 151 if ((frame.contentDocument.readyState == "complete" ||
michael@0 152 (frame.contentDocument.readyState == "interactive" &&
michael@0 153 this.contentScriptWhen != 'end' )) &&
michael@0 154 frame.contentDocument.location == this._contentURL) {
michael@0 155 // In some cases src doesn't change and document is already ready
michael@0 156 // (for ex: when the user moves a widget while customizing toolbars.)
michael@0 157 this._onInit();
michael@0 158 return;
michael@0 159 }
michael@0 160
michael@0 161 let self = this;
michael@0 162
michael@0 163 if ('start' == this.contentScriptWhen) {
michael@0 164 this._loadEvent = 'start';
michael@0 165 on('document-element-inserted',
michael@0 166 this._loadListener = function onStart({ subject: doc }) {
michael@0 167 let window = doc.defaultView;
michael@0 168
michael@0 169 if (ignoreWindow(window)) {
michael@0 170 return;
michael@0 171 }
michael@0 172
michael@0 173 if (window && window == frame.contentWindow) {
michael@0 174 self._unregisterListener();
michael@0 175 self._onInit();
michael@0 176 }
michael@0 177
michael@0 178 });
michael@0 179 return;
michael@0 180 }
michael@0 181
michael@0 182 let eventName = 'end' == this.contentScriptWhen ? 'load' : 'DOMContentLoaded';
michael@0 183 let self = this;
michael@0 184 this._loadEvent = eventName;
michael@0 185 frame.addEventListener(eventName,
michael@0 186 this._loadListener = function _onReady(event) {
michael@0 187
michael@0 188 if (event.target != frame.contentDocument)
michael@0 189 return;
michael@0 190 self._unregisterListener();
michael@0 191
michael@0 192 self._onInit();
michael@0 193
michael@0 194 }, true);
michael@0 195
michael@0 196 },
michael@0 197
michael@0 198 /**
michael@0 199 * Unregister listener that watchs for document being ready to be injected.
michael@0 200 * This listener is registered in `Symbiont._initFrame`.
michael@0 201 */
michael@0 202 _unregisterListener: function _unregisterListener() {
michael@0 203 if (this._waitForFrame) {
michael@0 204 off('content-document-global-created', this._waitForFrame);
michael@0 205 delete this._waitForFrame;
michael@0 206 }
michael@0 207
michael@0 208 if (!this._loadListener)
michael@0 209 return;
michael@0 210 if (this._loadEvent == "start") {
michael@0 211 off('document-element-inserted', this._loadListener);
michael@0 212 }
michael@0 213 else {
michael@0 214 this._frame.removeEventListener(this._loadEvent, this._loadListener,
michael@0 215 true);
michael@0 216 }
michael@0 217 this._loadListener = null;
michael@0 218 },
michael@0 219
michael@0 220 /**
michael@0 221 * Called by Symbiont itself when the frame is ready to load
michael@0 222 * content scripts according to contentScriptWhen. Overloaded by Panel.
michael@0 223 */
michael@0 224 _onInit: function () {
michael@0 225 this._initWorker({ window: this._frame.contentWindow });
michael@0 226 }
michael@0 227
michael@0 228 });
michael@0 229 exports.Symbiont = Symbiont;

mercurial