1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/addon-sdk/source/lib/sdk/deprecated/symbiont.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,229 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 +"use strict"; 1.8 + 1.9 +module.metadata = { 1.10 + "stability": "deprecated" 1.11 +}; 1.12 + 1.13 +const { Worker } = require('./traits-worker'); 1.14 +const { Loader } = require('../content/loader'); 1.15 +const hiddenFrames = require('../frame/hidden-frame'); 1.16 +const { on, off } = require('../system/events'); 1.17 +const unload = require('../system/unload'); 1.18 +const { getDocShell } = require("../frame/utils"); 1.19 +const { ignoreWindow } = require('../private-browsing/utils'); 1.20 + 1.21 +// Everything coming from add-on's xpi considered an asset. 1.22 +const assetsURI = require('../self').data.url().replace(/data\/$/, ""); 1.23 + 1.24 +/** 1.25 + * This trait is layered on top of `Worker` and in contrast to symbiont 1.26 + * Worker constructor requires `content` option that represents content 1.27 + * that will be loaded in the provided frame, if frame is not provided 1.28 + * Worker will create hidden one. 1.29 + */ 1.30 +const Symbiont = Worker.resolve({ 1.31 + constructor: '_initWorker', 1.32 + destroy: '_workerDestroy' 1.33 + }).compose(Loader, { 1.34 + 1.35 + /** 1.36 + * The constructor requires all the options that are required by 1.37 + * `require('content').Worker` with the difference that the `frame` option 1.38 + * is optional. If `frame` is not provided, `contentURL` is expected. 1.39 + * @param {Object} options 1.40 + * @param {String} options.contentURL 1.41 + * URL of a content to load into `this._frame` and create worker for. 1.42 + * @param {Element} [options.frame] 1.43 + * iframe element that is used to load `options.contentURL` into. 1.44 + * if frame is not provided hidden iframe will be created. 1.45 + */ 1.46 + constructor: function Symbiont(options) { 1.47 + options = options || {}; 1.48 + 1.49 + if ('contentURL' in options) 1.50 + this.contentURL = options.contentURL; 1.51 + if ('contentScriptWhen' in options) 1.52 + this.contentScriptWhen = options.contentScriptWhen; 1.53 + if ('contentScriptOptions' in options) 1.54 + this.contentScriptOptions = options.contentScriptOptions; 1.55 + if ('contentScriptFile' in options) 1.56 + this.contentScriptFile = options.contentScriptFile; 1.57 + if ('contentScript' in options) 1.58 + this.contentScript = options.contentScript; 1.59 + if ('allow' in options) 1.60 + this.allow = options.allow; 1.61 + if ('onError' in options) 1.62 + this.on('error', options.onError); 1.63 + if ('onMessage' in options) 1.64 + this.on('message', options.onMessage); 1.65 + if ('frame' in options) { 1.66 + this._initFrame(options.frame); 1.67 + } 1.68 + else { 1.69 + let self = this; 1.70 + this._hiddenFrame = hiddenFrames.HiddenFrame({ 1.71 + onReady: function onFrame() { 1.72 + self._initFrame(this.element); 1.73 + }, 1.74 + onUnload: function onUnload() { 1.75 + // Bug 751211: Remove reference to _frame when hidden frame is 1.76 + // automatically removed on unload, otherwise we are going to face 1.77 + // "dead object" exception 1.78 + self.destroy(); 1.79 + } 1.80 + }); 1.81 + hiddenFrames.add(this._hiddenFrame); 1.82 + } 1.83 + 1.84 + unload.ensure(this._public, "destroy"); 1.85 + }, 1.86 + 1.87 + destroy: function destroy() { 1.88 + this._workerDestroy(); 1.89 + this._unregisterListener(); 1.90 + this._frame = null; 1.91 + if (this._hiddenFrame) { 1.92 + hiddenFrames.remove(this._hiddenFrame); 1.93 + this._hiddenFrame = null; 1.94 + } 1.95 + }, 1.96 + 1.97 + /** 1.98 + * XUL iframe or browser elements with attribute `type` being `content`. 1.99 + * Used to create `ContentSymbiont` from. 1.100 + * @type {nsIFrame|nsIBrowser} 1.101 + */ 1.102 + _frame: null, 1.103 + 1.104 + /** 1.105 + * Listener to the `'frameReady"` event (emitted when `iframe` is ready). 1.106 + * Removes listener, sets right permissions to the frame and loads content. 1.107 + */ 1.108 + _initFrame: function _initFrame(frame) { 1.109 + if (this._loadListener) 1.110 + this._unregisterListener(); 1.111 + 1.112 + this._frame = frame; 1.113 + 1.114 + if (getDocShell(frame)) { 1.115 + this._reallyInitFrame(frame); 1.116 + } 1.117 + else { 1.118 + if (this._waitForFrame) { 1.119 + off('content-document-global-created', this._waitForFrame); 1.120 + } 1.121 + this._waitForFrame = this.__waitForFrame.bind(this, frame); 1.122 + on('content-document-global-created', this._waitForFrame); 1.123 + } 1.124 + }, 1.125 + 1.126 + __waitForFrame: function _waitForFrame(frame, { subject: win }) { 1.127 + if (frame.contentWindow == win) { 1.128 + off('content-document-global-created', this._waitForFrame); 1.129 + delete this._waitForFrame; 1.130 + this._reallyInitFrame(frame); 1.131 + } 1.132 + }, 1.133 + 1.134 + _reallyInitFrame: function _reallyInitFrame(frame) { 1.135 + getDocShell(frame).allowJavascript = this.allow.script; 1.136 + frame.setAttribute("src", this._contentURL); 1.137 + 1.138 + // Inject `addon` object in document if we load a document from 1.139 + // one of our addon folder and if no content script are defined. bug 612726 1.140 + let isDataResource = 1.141 + typeof this._contentURL == "string" && 1.142 + this._contentURL.indexOf(assetsURI) == 0; 1.143 + let hasContentScript = 1.144 + (Array.isArray(this.contentScript) ? this.contentScript.length > 0 1.145 + : !!this.contentScript) || 1.146 + (Array.isArray(this.contentScriptFile) ? this.contentScriptFile.length > 0 1.147 + : !!this.contentScriptFile); 1.148 + // If we have to inject `addon` we have to do it before document 1.149 + // script execution, so during `start`: 1.150 + this._injectInDocument = isDataResource && !hasContentScript; 1.151 + if (this._injectInDocument) 1.152 + this.contentScriptWhen = "start"; 1.153 + 1.154 + if ((frame.contentDocument.readyState == "complete" || 1.155 + (frame.contentDocument.readyState == "interactive" && 1.156 + this.contentScriptWhen != 'end' )) && 1.157 + frame.contentDocument.location == this._contentURL) { 1.158 + // In some cases src doesn't change and document is already ready 1.159 + // (for ex: when the user moves a widget while customizing toolbars.) 1.160 + this._onInit(); 1.161 + return; 1.162 + } 1.163 + 1.164 + let self = this; 1.165 + 1.166 + if ('start' == this.contentScriptWhen) { 1.167 + this._loadEvent = 'start'; 1.168 + on('document-element-inserted', 1.169 + this._loadListener = function onStart({ subject: doc }) { 1.170 + let window = doc.defaultView; 1.171 + 1.172 + if (ignoreWindow(window)) { 1.173 + return; 1.174 + } 1.175 + 1.176 + if (window && window == frame.contentWindow) { 1.177 + self._unregisterListener(); 1.178 + self._onInit(); 1.179 + } 1.180 + 1.181 + }); 1.182 + return; 1.183 + } 1.184 + 1.185 + let eventName = 'end' == this.contentScriptWhen ? 'load' : 'DOMContentLoaded'; 1.186 + let self = this; 1.187 + this._loadEvent = eventName; 1.188 + frame.addEventListener(eventName, 1.189 + this._loadListener = function _onReady(event) { 1.190 + 1.191 + if (event.target != frame.contentDocument) 1.192 + return; 1.193 + self._unregisterListener(); 1.194 + 1.195 + self._onInit(); 1.196 + 1.197 + }, true); 1.198 + 1.199 + }, 1.200 + 1.201 + /** 1.202 + * Unregister listener that watchs for document being ready to be injected. 1.203 + * This listener is registered in `Symbiont._initFrame`. 1.204 + */ 1.205 + _unregisterListener: function _unregisterListener() { 1.206 + if (this._waitForFrame) { 1.207 + off('content-document-global-created', this._waitForFrame); 1.208 + delete this._waitForFrame; 1.209 + } 1.210 + 1.211 + if (!this._loadListener) 1.212 + return; 1.213 + if (this._loadEvent == "start") { 1.214 + off('document-element-inserted', this._loadListener); 1.215 + } 1.216 + else { 1.217 + this._frame.removeEventListener(this._loadEvent, this._loadListener, 1.218 + true); 1.219 + } 1.220 + this._loadListener = null; 1.221 + }, 1.222 + 1.223 + /** 1.224 + * Called by Symbiont itself when the frame is ready to load 1.225 + * content scripts according to contentScriptWhen. Overloaded by Panel. 1.226 + */ 1.227 + _onInit: function () { 1.228 + this._initWorker({ window: this._frame.contentWindow }); 1.229 + } 1.230 + 1.231 +}); 1.232 +exports.Symbiont = Symbiont;