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