michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: "use strict"; michael@0: michael@0: this.EXPORTED_SYMBOLS = ["Sandbox"]; michael@0: michael@0: const {classes: Cc, interfaces: Ci, utils: Cu} = Components; michael@0: michael@0: const XHTML_NS = "http://www.w3.org/1999/xhtml"; michael@0: michael@0: Cu.import("resource://gre/modules/XPCOMUtils.jsm"); michael@0: Cu.import("resource://gre/modules/Services.jsm"); michael@0: michael@0: XPCOMUtils.defineLazyModuleGetter(this, michael@0: "Logger", michael@0: "resource://gre/modules/identity/LogUtils.jsm"); michael@0: michael@0: /** michael@0: * An object that represents a sandbox in an iframe loaded with aURL. The michael@0: * callback provided to the constructor will be invoked when the sandbox is michael@0: * ready to be used. The callback will receive this object as its only argument. michael@0: * michael@0: * You must call free() when you are finished with the sandbox to explicitly michael@0: * free up all associated resources. michael@0: * michael@0: * @param aURL michael@0: * (string) URL to load in the sandbox. michael@0: * michael@0: * @param aCallback michael@0: * (function) Callback to be invoked with a Sandbox, when ready. michael@0: */ michael@0: this.Sandbox = function Sandbox(aURL, aCallback) { michael@0: // Normalize the URL so the comparison in _makeSandboxContentLoaded works michael@0: this._url = Services.io.newURI(aURL, null, null).spec; michael@0: this._log("Creating sandbox for:", this._url); michael@0: this._createFrame(); michael@0: this._createSandbox(aCallback); michael@0: }; michael@0: michael@0: this.Sandbox.prototype = { michael@0: michael@0: /** michael@0: * Use the outer window ID as the identifier of the sandbox. michael@0: */ michael@0: get id() { michael@0: return this._frame.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor) michael@0: .getInterface(Ci.nsIDOMWindowUtils).outerWindowID; michael@0: }, michael@0: michael@0: /** michael@0: * Reload the URL in the sandbox. This is useful to reuse a Sandbox (same michael@0: * id and URL). michael@0: */ michael@0: reload: function Sandbox_reload(aCallback) { michael@0: this._log("reload:", this.id, ":", this._url); michael@0: this._createSandbox(function createdSandbox(aSandbox) { michael@0: this._log("reloaded sandbox id:", aSandbox.id); michael@0: aCallback(aSandbox); michael@0: }.bind(this)); michael@0: }, michael@0: michael@0: /** michael@0: * Frees the sandbox and releases the iframe created to host it. michael@0: */ michael@0: free: function Sandbox_free() { michael@0: this._log("free:", this.id); michael@0: this._container.removeChild(this._frame); michael@0: this._frame = null; michael@0: this._container = null; michael@0: this._url = null; michael@0: }, michael@0: michael@0: /** michael@0: * Creates an empty, hidden iframe and sets it to the _frame michael@0: * property of this object. michael@0: */ michael@0: _createFrame: function Sandbox__createFrame() { michael@0: let hiddenWindow = Services.appShell.hiddenDOMWindow; michael@0: let doc = hiddenWindow.document; michael@0: michael@0: // Insert iframe in to create docshell. michael@0: let frame = doc.createElementNS(XHTML_NS, "iframe"); michael@0: frame.setAttribute("mozframetype", "content"); michael@0: frame.sandbox = "allow-forms allow-scripts allow-same-origin"; michael@0: frame.style.visibility = "collapse"; michael@0: doc.documentElement.appendChild(frame); michael@0: michael@0: let docShell = frame.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor) michael@0: .getInterface(Ci.nsIWebNavigation) michael@0: .QueryInterface(Ci.nsIInterfaceRequestor) michael@0: .getInterface(Ci.nsIDocShell); michael@0: michael@0: // Stop about:blank from being loaded. michael@0: docShell.stop(Ci.nsIWebNavigation.STOP_NETWORK); michael@0: michael@0: // Disable some types of content michael@0: docShell.allowAuth = false; michael@0: docShell.allowPlugins = false; michael@0: docShell.allowImages = false; michael@0: docShell.allowMedia = false; michael@0: docShell.allowWindowControl = false; michael@0: michael@0: // Disable stylesheet loading since the document is not visible. michael@0: let markupDocViewer = docShell.contentViewer michael@0: .QueryInterface(Ci.nsIMarkupDocumentViewer); michael@0: markupDocViewer.authorStyleDisabled = true; michael@0: michael@0: // Set instance properties. michael@0: this._frame = frame; michael@0: this._container = doc.documentElement; michael@0: }, michael@0: michael@0: _createSandbox: function Sandbox__createSandbox(aCallback) { michael@0: let self = this; michael@0: function _makeSandboxContentLoaded(event) { michael@0: self._log("_makeSandboxContentLoaded:", self.id, michael@0: event.target.location.toString()); michael@0: if (event.target != self._frame.contentDocument) { michael@0: return; michael@0: } michael@0: self._frame.removeEventListener( michael@0: "DOMWindowCreated", _makeSandboxContentLoaded, true michael@0: ); michael@0: michael@0: aCallback(self); michael@0: }; michael@0: michael@0: this._frame.addEventListener("DOMWindowCreated", michael@0: _makeSandboxContentLoaded, michael@0: true); michael@0: michael@0: // Load the iframe. michael@0: let webNav = this._frame.contentWindow michael@0: .QueryInterface(Ci.nsIInterfaceRequestor) michael@0: .getInterface(Ci.nsIWebNavigation); michael@0: michael@0: webNav.loadURI( michael@0: this._url, michael@0: Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE, michael@0: null, // referrer michael@0: null, // postData michael@0: null // headers michael@0: ); michael@0: michael@0: }, michael@0: michael@0: _log: function Sandbox__log(...aMessageArgs) { michael@0: Logger.log.apply(Logger, ["sandbox"].concat(aMessageArgs)); michael@0: }, michael@0: michael@0: };