1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/identity/Sandbox.jsm Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,153 @@ 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 file, 1.6 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +"use strict"; 1.9 + 1.10 +this.EXPORTED_SYMBOLS = ["Sandbox"]; 1.11 + 1.12 +const {classes: Cc, interfaces: Ci, utils: Cu} = Components; 1.13 + 1.14 +const XHTML_NS = "http://www.w3.org/1999/xhtml"; 1.15 + 1.16 +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); 1.17 +Cu.import("resource://gre/modules/Services.jsm"); 1.18 + 1.19 +XPCOMUtils.defineLazyModuleGetter(this, 1.20 + "Logger", 1.21 + "resource://gre/modules/identity/LogUtils.jsm"); 1.22 + 1.23 +/** 1.24 + * An object that represents a sandbox in an iframe loaded with aURL. The 1.25 + * callback provided to the constructor will be invoked when the sandbox is 1.26 + * ready to be used. The callback will receive this object as its only argument. 1.27 + * 1.28 + * You must call free() when you are finished with the sandbox to explicitly 1.29 + * free up all associated resources. 1.30 + * 1.31 + * @param aURL 1.32 + * (string) URL to load in the sandbox. 1.33 + * 1.34 + * @param aCallback 1.35 + * (function) Callback to be invoked with a Sandbox, when ready. 1.36 + */ 1.37 +this.Sandbox = function Sandbox(aURL, aCallback) { 1.38 + // Normalize the URL so the comparison in _makeSandboxContentLoaded works 1.39 + this._url = Services.io.newURI(aURL, null, null).spec; 1.40 + this._log("Creating sandbox for:", this._url); 1.41 + this._createFrame(); 1.42 + this._createSandbox(aCallback); 1.43 +}; 1.44 + 1.45 +this.Sandbox.prototype = { 1.46 + 1.47 + /** 1.48 + * Use the outer window ID as the identifier of the sandbox. 1.49 + */ 1.50 + get id() { 1.51 + return this._frame.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor) 1.52 + .getInterface(Ci.nsIDOMWindowUtils).outerWindowID; 1.53 + }, 1.54 + 1.55 + /** 1.56 + * Reload the URL in the sandbox. This is useful to reuse a Sandbox (same 1.57 + * id and URL). 1.58 + */ 1.59 + reload: function Sandbox_reload(aCallback) { 1.60 + this._log("reload:", this.id, ":", this._url); 1.61 + this._createSandbox(function createdSandbox(aSandbox) { 1.62 + this._log("reloaded sandbox id:", aSandbox.id); 1.63 + aCallback(aSandbox); 1.64 + }.bind(this)); 1.65 + }, 1.66 + 1.67 + /** 1.68 + * Frees the sandbox and releases the iframe created to host it. 1.69 + */ 1.70 + free: function Sandbox_free() { 1.71 + this._log("free:", this.id); 1.72 + this._container.removeChild(this._frame); 1.73 + this._frame = null; 1.74 + this._container = null; 1.75 + this._url = null; 1.76 + }, 1.77 + 1.78 + /** 1.79 + * Creates an empty, hidden iframe and sets it to the _frame 1.80 + * property of this object. 1.81 + */ 1.82 + _createFrame: function Sandbox__createFrame() { 1.83 + let hiddenWindow = Services.appShell.hiddenDOMWindow; 1.84 + let doc = hiddenWindow.document; 1.85 + 1.86 + // Insert iframe in to create docshell. 1.87 + let frame = doc.createElementNS(XHTML_NS, "iframe"); 1.88 + frame.setAttribute("mozframetype", "content"); 1.89 + frame.sandbox = "allow-forms allow-scripts allow-same-origin"; 1.90 + frame.style.visibility = "collapse"; 1.91 + doc.documentElement.appendChild(frame); 1.92 + 1.93 + let docShell = frame.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor) 1.94 + .getInterface(Ci.nsIWebNavigation) 1.95 + .QueryInterface(Ci.nsIInterfaceRequestor) 1.96 + .getInterface(Ci.nsIDocShell); 1.97 + 1.98 + // Stop about:blank from being loaded. 1.99 + docShell.stop(Ci.nsIWebNavigation.STOP_NETWORK); 1.100 + 1.101 + // Disable some types of content 1.102 + docShell.allowAuth = false; 1.103 + docShell.allowPlugins = false; 1.104 + docShell.allowImages = false; 1.105 + docShell.allowMedia = false; 1.106 + docShell.allowWindowControl = false; 1.107 + 1.108 + // Disable stylesheet loading since the document is not visible. 1.109 + let markupDocViewer = docShell.contentViewer 1.110 + .QueryInterface(Ci.nsIMarkupDocumentViewer); 1.111 + markupDocViewer.authorStyleDisabled = true; 1.112 + 1.113 + // Set instance properties. 1.114 + this._frame = frame; 1.115 + this._container = doc.documentElement; 1.116 + }, 1.117 + 1.118 + _createSandbox: function Sandbox__createSandbox(aCallback) { 1.119 + let self = this; 1.120 + function _makeSandboxContentLoaded(event) { 1.121 + self._log("_makeSandboxContentLoaded:", self.id, 1.122 + event.target.location.toString()); 1.123 + if (event.target != self._frame.contentDocument) { 1.124 + return; 1.125 + } 1.126 + self._frame.removeEventListener( 1.127 + "DOMWindowCreated", _makeSandboxContentLoaded, true 1.128 + ); 1.129 + 1.130 + aCallback(self); 1.131 + }; 1.132 + 1.133 + this._frame.addEventListener("DOMWindowCreated", 1.134 + _makeSandboxContentLoaded, 1.135 + true); 1.136 + 1.137 + // Load the iframe. 1.138 + let webNav = this._frame.contentWindow 1.139 + .QueryInterface(Ci.nsIInterfaceRequestor) 1.140 + .getInterface(Ci.nsIWebNavigation); 1.141 + 1.142 + webNav.loadURI( 1.143 + this._url, 1.144 + Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE, 1.145 + null, // referrer 1.146 + null, // postData 1.147 + null // headers 1.148 + ); 1.149 + 1.150 + }, 1.151 + 1.152 + _log: function Sandbox__log(...aMessageArgs) { 1.153 + Logger.log.apply(Logger, ["sandbox"].concat(aMessageArgs)); 1.154 + }, 1.155 + 1.156 +};