|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
|
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 "use strict"; |
|
6 |
|
7 this.EXPORTED_SYMBOLS = ["Sandbox"]; |
|
8 |
|
9 const {classes: Cc, interfaces: Ci, utils: Cu} = Components; |
|
10 |
|
11 const XHTML_NS = "http://www.w3.org/1999/xhtml"; |
|
12 |
|
13 Cu.import("resource://gre/modules/XPCOMUtils.jsm"); |
|
14 Cu.import("resource://gre/modules/Services.jsm"); |
|
15 |
|
16 XPCOMUtils.defineLazyModuleGetter(this, |
|
17 "Logger", |
|
18 "resource://gre/modules/identity/LogUtils.jsm"); |
|
19 |
|
20 /** |
|
21 * An object that represents a sandbox in an iframe loaded with aURL. The |
|
22 * callback provided to the constructor will be invoked when the sandbox is |
|
23 * ready to be used. The callback will receive this object as its only argument. |
|
24 * |
|
25 * You must call free() when you are finished with the sandbox to explicitly |
|
26 * free up all associated resources. |
|
27 * |
|
28 * @param aURL |
|
29 * (string) URL to load in the sandbox. |
|
30 * |
|
31 * @param aCallback |
|
32 * (function) Callback to be invoked with a Sandbox, when ready. |
|
33 */ |
|
34 this.Sandbox = function Sandbox(aURL, aCallback) { |
|
35 // Normalize the URL so the comparison in _makeSandboxContentLoaded works |
|
36 this._url = Services.io.newURI(aURL, null, null).spec; |
|
37 this._log("Creating sandbox for:", this._url); |
|
38 this._createFrame(); |
|
39 this._createSandbox(aCallback); |
|
40 }; |
|
41 |
|
42 this.Sandbox.prototype = { |
|
43 |
|
44 /** |
|
45 * Use the outer window ID as the identifier of the sandbox. |
|
46 */ |
|
47 get id() { |
|
48 return this._frame.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor) |
|
49 .getInterface(Ci.nsIDOMWindowUtils).outerWindowID; |
|
50 }, |
|
51 |
|
52 /** |
|
53 * Reload the URL in the sandbox. This is useful to reuse a Sandbox (same |
|
54 * id and URL). |
|
55 */ |
|
56 reload: function Sandbox_reload(aCallback) { |
|
57 this._log("reload:", this.id, ":", this._url); |
|
58 this._createSandbox(function createdSandbox(aSandbox) { |
|
59 this._log("reloaded sandbox id:", aSandbox.id); |
|
60 aCallback(aSandbox); |
|
61 }.bind(this)); |
|
62 }, |
|
63 |
|
64 /** |
|
65 * Frees the sandbox and releases the iframe created to host it. |
|
66 */ |
|
67 free: function Sandbox_free() { |
|
68 this._log("free:", this.id); |
|
69 this._container.removeChild(this._frame); |
|
70 this._frame = null; |
|
71 this._container = null; |
|
72 this._url = null; |
|
73 }, |
|
74 |
|
75 /** |
|
76 * Creates an empty, hidden iframe and sets it to the _frame |
|
77 * property of this object. |
|
78 */ |
|
79 _createFrame: function Sandbox__createFrame() { |
|
80 let hiddenWindow = Services.appShell.hiddenDOMWindow; |
|
81 let doc = hiddenWindow.document; |
|
82 |
|
83 // Insert iframe in to create docshell. |
|
84 let frame = doc.createElementNS(XHTML_NS, "iframe"); |
|
85 frame.setAttribute("mozframetype", "content"); |
|
86 frame.sandbox = "allow-forms allow-scripts allow-same-origin"; |
|
87 frame.style.visibility = "collapse"; |
|
88 doc.documentElement.appendChild(frame); |
|
89 |
|
90 let docShell = frame.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor) |
|
91 .getInterface(Ci.nsIWebNavigation) |
|
92 .QueryInterface(Ci.nsIInterfaceRequestor) |
|
93 .getInterface(Ci.nsIDocShell); |
|
94 |
|
95 // Stop about:blank from being loaded. |
|
96 docShell.stop(Ci.nsIWebNavigation.STOP_NETWORK); |
|
97 |
|
98 // Disable some types of content |
|
99 docShell.allowAuth = false; |
|
100 docShell.allowPlugins = false; |
|
101 docShell.allowImages = false; |
|
102 docShell.allowMedia = false; |
|
103 docShell.allowWindowControl = false; |
|
104 |
|
105 // Disable stylesheet loading since the document is not visible. |
|
106 let markupDocViewer = docShell.contentViewer |
|
107 .QueryInterface(Ci.nsIMarkupDocumentViewer); |
|
108 markupDocViewer.authorStyleDisabled = true; |
|
109 |
|
110 // Set instance properties. |
|
111 this._frame = frame; |
|
112 this._container = doc.documentElement; |
|
113 }, |
|
114 |
|
115 _createSandbox: function Sandbox__createSandbox(aCallback) { |
|
116 let self = this; |
|
117 function _makeSandboxContentLoaded(event) { |
|
118 self._log("_makeSandboxContentLoaded:", self.id, |
|
119 event.target.location.toString()); |
|
120 if (event.target != self._frame.contentDocument) { |
|
121 return; |
|
122 } |
|
123 self._frame.removeEventListener( |
|
124 "DOMWindowCreated", _makeSandboxContentLoaded, true |
|
125 ); |
|
126 |
|
127 aCallback(self); |
|
128 }; |
|
129 |
|
130 this._frame.addEventListener("DOMWindowCreated", |
|
131 _makeSandboxContentLoaded, |
|
132 true); |
|
133 |
|
134 // Load the iframe. |
|
135 let webNav = this._frame.contentWindow |
|
136 .QueryInterface(Ci.nsIInterfaceRequestor) |
|
137 .getInterface(Ci.nsIWebNavigation); |
|
138 |
|
139 webNav.loadURI( |
|
140 this._url, |
|
141 Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE, |
|
142 null, // referrer |
|
143 null, // postData |
|
144 null // headers |
|
145 ); |
|
146 |
|
147 }, |
|
148 |
|
149 _log: function Sandbox__log(...aMessageArgs) { |
|
150 Logger.log.apply(Logger, ["sandbox"].concat(aMessageArgs)); |
|
151 }, |
|
152 |
|
153 }; |