|
1 <?xml version="1.0"?> |
|
2 <?xml-stylesheet type="text/css" href="chrome://global/skin"?> |
|
3 <?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?> |
|
4 <!-- |
|
5 https://bugzilla.mozilla.org/show_bug.cgi?id=840488 |
|
6 --> |
|
7 <window title="Mozilla Bug 840488" |
|
8 xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> |
|
9 <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> |
|
10 |
|
11 <!-- test results are displayed in the html:body --> |
|
12 <body xmlns="http://www.w3.org/1999/xhtml"> |
|
13 <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=840488" |
|
14 target="_blank">Mozilla Bug 840488</a> |
|
15 </body> |
|
16 |
|
17 <iframe id="root" name="root" type="content"/> |
|
18 <iframe id="chromeFrame" name="chromeFrame" type="content"/> |
|
19 |
|
20 <!-- test code goes here --> |
|
21 <script type="application/javascript"> |
|
22 <![CDATA[ |
|
23 |
|
24 /** Test for all the different ways that script can be disabled for a given global. **/ |
|
25 |
|
26 SimpleTest.waitForExplicitFinish(); |
|
27 const Cu = Components.utils; |
|
28 const Ci = Components.interfaces; |
|
29 Cu.import("resource://gre/modules/Promise.jsm"); |
|
30 Cu.import("resource://gre/modules/Services.jsm"); |
|
31 const ssm = Services.scriptSecurityManager; |
|
32 function makeURI(uri) { return Services.io.newURI(uri, null, null); } |
|
33 const path = "/tests/caps/tests/mochitest/file_disableScript.html"; |
|
34 const uri = "http://www.example.com" + path; |
|
35 var rootFrame = document.getElementById('root'); |
|
36 var chromeFrame = document.getElementById('chromeFrame'); |
|
37 navigateFrame(rootFrame, uri + "?name=rootframe").then(function() { |
|
38 navigateFrame(chromeFrame, "file_disableScript.html").then(go); |
|
39 }); |
|
40 |
|
41 function navigateFrame(ifr, src) { |
|
42 let deferred = Promise.defer(); |
|
43 function onload() { |
|
44 ifr.removeEventListener('load', onload); |
|
45 deferred.resolve(); |
|
46 } |
|
47 ifr.addEventListener('load', onload, false); |
|
48 ifr.setAttribute('src', src); |
|
49 return deferred.promise; |
|
50 } |
|
51 |
|
52 function navigateBack(ifr) { |
|
53 let deferred = Promise.defer(); |
|
54 |
|
55 // pageshow events don't fire on the iframe element, so we need to use the |
|
56 // chrome event handler for the docshell. |
|
57 var browser = ifr.contentWindow |
|
58 .QueryInterface(Ci.nsIInterfaceRequestor) |
|
59 .getInterface(Ci.nsIWebNavigation) |
|
60 .QueryInterface(Ci.nsIDocShell) |
|
61 .chromeEventHandler; |
|
62 function onpageshow(evt) { |
|
63 info("Navigated back. Persisted: " + evt.persisted); |
|
64 browser.removeEventListener('pageshow', onpageshow); |
|
65 deferred.resolve(); |
|
66 } |
|
67 browser.addEventListener('pageshow', onpageshow, false); |
|
68 ifr.contentWindow.history.back(); |
|
69 return deferred.promise; |
|
70 } |
|
71 |
|
72 function addFrame(parentWin, name, expectOnload) { |
|
73 let ifr = parentWin.document.createElement('iframe'); |
|
74 parentWin.document.body.appendChild(ifr); |
|
75 ifr.setAttribute('name', name); |
|
76 let deferred = Promise.defer(); |
|
77 // We need to append 'name' to avoid running afoul of recursive frame detection. |
|
78 let frameURI = uri + "?name=" + name; |
|
79 navigateFrame(ifr, frameURI).then(function() { |
|
80 is(ifr.contentWindow.location, frameURI, "Successful load"); |
|
81 is(!!ifr.contentWindow.wrappedJSObject.gFiredOnload, expectOnload, |
|
82 "onload should only fire when scripts are enabled"); |
|
83 deferred.resolve(); |
|
84 }); |
|
85 return deferred.promise; |
|
86 } |
|
87 |
|
88 function checkScriptEnabled(win, expectEnabled) { |
|
89 win.wrappedJSObject.gFiredOnclick = false; |
|
90 win.document.body.dispatchEvent(new win.Event('click')); |
|
91 is(win.wrappedJSObject.gFiredOnclick, expectEnabled, "Checking script-enabled for " + win.name + " (" + win.location + ")"); |
|
92 } |
|
93 |
|
94 function setScriptEnabledForDocShell(win, enabled) { |
|
95 win.QueryInterface(Ci.nsIInterfaceRequestor) |
|
96 .getInterface(Ci.nsIDocShell) |
|
97 .allowJavascript = enabled; |
|
98 } |
|
99 |
|
100 function testList(expectEnabled, win, list, idx) { |
|
101 let idx = idx || 0; |
|
102 let deferred = Promise.defer(); |
|
103 let target = list[idx] + path; |
|
104 info("Testing scriptability for: " + target + ". expecting " + expectEnabled); |
|
105 navigateFrame(win.frameElement, target).then(function() { |
|
106 checkScriptEnabled(win, expectEnabled); |
|
107 if (idx == list.length - 1) |
|
108 deferred.resolve(); |
|
109 else |
|
110 testList(expectEnabled, win, list, idx + 1).then(function() { deferred.resolve(); }); |
|
111 }); |
|
112 return deferred.promise; |
|
113 } |
|
114 |
|
115 function testDomainPolicy(defaultScriptability, exceptions, superExceptions, |
|
116 exempt, notExempt, set, superSet, win) { |
|
117 // Populate our sets. |
|
118 for (var e of exceptions) |
|
119 set.add(makeURI(e)); |
|
120 for (var e of superExceptions) |
|
121 superSet.add(makeURI(e)); |
|
122 |
|
123 return testList(defaultScriptability, win, notExempt).then(function() { |
|
124 return testList(!defaultScriptability, win, exempt); |
|
125 }); |
|
126 } |
|
127 |
|
128 function setScriptEnabledForBrowser(enabled) { |
|
129 var prefname = "javascript.enabled"; |
|
130 Services.prefs.setBoolPref(prefname, enabled); |
|
131 } |
|
132 |
|
133 function reloadFrame(frame) { |
|
134 let deferred = Promise.defer(); |
|
135 frame.addEventListener('load', function onload() { |
|
136 deferred.resolve(); |
|
137 frame.removeEventListener('load', onload); |
|
138 }, false); |
|
139 frame.contentWindow.location.reload(true); |
|
140 return deferred.promise; |
|
141 } |
|
142 |
|
143 function go() { |
|
144 var rootWin = rootFrame.contentWindow; |
|
145 var chromeWin = chromeFrame.contentWindow; |
|
146 |
|
147 // Test simple docshell enable/disable. |
|
148 checkScriptEnabled(rootWin, true); |
|
149 setScriptEnabledForDocShell(rootWin, false); |
|
150 checkScriptEnabled(rootWin, false); |
|
151 setScriptEnabledForDocShell(rootWin, true); |
|
152 checkScriptEnabled(rootWin, true); |
|
153 |
|
154 // Privileged frames are immune to docshell flags. |
|
155 ok(ssm.isSystemPrincipal(chromeWin.document.nodePrincipal), "Sanity check for System Principal"); |
|
156 setScriptEnabledForDocShell(chromeWin, false); |
|
157 checkScriptEnabled(chromeWin, true); |
|
158 setScriptEnabledForDocShell(chromeWin, true); |
|
159 |
|
160 // Play around with the docshell tree and make sure everything works as |
|
161 // we expect. |
|
162 addFrame(rootWin, 'parent', true).then(function() { |
|
163 checkScriptEnabled(rootWin[0], true); |
|
164 return addFrame(rootWin[0], 'childA', true); |
|
165 }).then(function() { |
|
166 checkScriptEnabled(rootWin[0][0], true); |
|
167 setScriptEnabledForDocShell(rootWin[0], false); |
|
168 checkScriptEnabled(rootWin, true); |
|
169 checkScriptEnabled(rootWin[0], false); |
|
170 checkScriptEnabled(rootWin[0][0], false); |
|
171 return addFrame(rootWin[0], 'childB', false); |
|
172 }).then(function() { |
|
173 checkScriptEnabled(rootWin[0][1], false); |
|
174 setScriptEnabledForDocShell(rootWin[0][0], false); |
|
175 setScriptEnabledForDocShell(rootWin[0], true); |
|
176 checkScriptEnabled(rootWin[0], true); |
|
177 checkScriptEnabled(rootWin[0][0], false); |
|
178 setScriptEnabledForDocShell(rootWin[0][0], true); |
|
179 |
|
180 // Flags are inherited from the parent docshell at attach time. Note that |
|
181 // the flag itself is inherited, regardless of whether or not scripts are |
|
182 // currently allowed on the parent (which could depend on the parent's |
|
183 // parent). Check that. |
|
184 checkScriptEnabled(rootWin[0][1], false); |
|
185 setScriptEnabledForDocShell(rootWin[0], false); |
|
186 setScriptEnabledForDocShell(rootWin[0][1], true); |
|
187 return addFrame(rootWin[0][1], 'grandchild', false); |
|
188 }).then(function() { |
|
189 checkScriptEnabled(rootWin[0], false); |
|
190 checkScriptEnabled(rootWin[0][1], false); |
|
191 checkScriptEnabled(rootWin[0][1][0], false); |
|
192 setScriptEnabledForDocShell(rootWin[0], true); |
|
193 checkScriptEnabled(rootWin[0], true); |
|
194 checkScriptEnabled(rootWin[0][1], true); |
|
195 checkScriptEnabled(rootWin[0][1][0], true); |
|
196 |
|
197 // Try navigating two frames, then munging docshell scriptability, then |
|
198 // pulling the frames out of the bfcache to make sure that flags are |
|
199 // properly propagated to inactive inner windows. We do this both for an |
|
200 // 'own' docshell, as well as for an ancestor docshell. |
|
201 return navigateFrame(rootWin[0][0].frameElement, rootWin[0][0].location + '-navigated'); |
|
202 }).then(function() { return navigateFrame(rootWin[0][1][0].frameElement, rootWin[0][1][0].location + '-navigated'); }) |
|
203 .then(function() { |
|
204 checkScriptEnabled(rootWin[0][0], true); |
|
205 checkScriptEnabled(rootWin[0][1][0], true); |
|
206 setScriptEnabledForDocShell(rootWin[0][0], false); |
|
207 setScriptEnabledForDocShell(rootWin[0][1], false); |
|
208 checkScriptEnabled(rootWin[0][0], false); |
|
209 checkScriptEnabled(rootWin[0][1][0], false); |
|
210 return navigateBack(rootWin[0][0].frameElement); |
|
211 }).then(function() { return navigateBack(rootWin[0][1][0].frameElement); }) |
|
212 .then(function() { |
|
213 checkScriptEnabled(rootWin[0][0], false); |
|
214 checkScriptEnabled(rootWin[0][1][0], false); |
|
215 |
|
216 // Disable JS via the global pref pref. This is only guaranteed to have an effect |
|
217 // for subsequent loads. |
|
218 setScriptEnabledForBrowser(false); |
|
219 return reloadFrame(rootFrame); |
|
220 }).then(function() { |
|
221 checkScriptEnabled(rootWin, false); |
|
222 checkScriptEnabled(chromeWin, true); |
|
223 setScriptEnabledForBrowser(true); |
|
224 return reloadFrame(rootFrame); |
|
225 }).then(function() { |
|
226 checkScriptEnabled(rootWin, true); |
|
227 |
|
228 // Play around with dynamically blocking script for a given global. |
|
229 // This takes effect immediately. |
|
230 Cu.blockScriptForGlobal(rootWin); |
|
231 Cu.blockScriptForGlobal(rootWin); |
|
232 Cu.unblockScriptForGlobal(rootWin); |
|
233 checkScriptEnabled(rootWin, false); |
|
234 Cu.unblockScriptForGlobal(rootWin); |
|
235 checkScriptEnabled(rootWin, true); |
|
236 Cu.blockScriptForGlobal(rootWin); |
|
237 try { |
|
238 Cu.blockScriptForGlobal(chromeWin); |
|
239 ok(false, "Should have thrown"); |
|
240 } catch (e) { |
|
241 ok(/may not be disabled/.test(e), |
|
242 "Shouldn't be able to programmatically block script for system globals"); |
|
243 } |
|
244 return reloadFrame(rootFrame); |
|
245 }).then(function() { |
|
246 checkScriptEnabled(rootWin, true); |
|
247 |
|
248 // Test system-wide domain policy. This only takes effect for subsequently- |
|
249 // loaded globals. |
|
250 |
|
251 // Check the basic semantics of the sets. |
|
252 is(ssm.domainPolicyActive, false, "not enabled"); |
|
253 window.policy = ssm.activateDomainPolicy(); |
|
254 ok(policy instanceof Ci.nsIDomainPolicy, "Got a policy"); |
|
255 try { |
|
256 ssm.activateDomainPolicy(); |
|
257 ok(false, "Should have thrown"); |
|
258 } catch (e) { |
|
259 ok(true, "can't have two live domain policies"); |
|
260 } |
|
261 var sbRef = policy.superBlacklist; |
|
262 isnot(sbRef, null, "superBlacklist non-null"); |
|
263 ok(!sbRef.contains(makeURI('http://www.example.com'))); |
|
264 sbRef.add(makeURI('http://www.example.com/foopy')); |
|
265 ok(sbRef.contains(makeURI('http://www.example.com'))); |
|
266 sbRef.remove(makeURI('http://www.example.com')); |
|
267 ok(!sbRef.contains(makeURI('http://www.example.com'))); |
|
268 sbRef.add(makeURI('http://www.example.com/foopy/this.that/')); |
|
269 ok(sbRef.contains(makeURI('http://www.example.com/baz'))); |
|
270 ok(!sbRef.contains(makeURI('https://www.example.com'))); |
|
271 ok(!sbRef.contains(makeURI('https://www.example.com:88'))); |
|
272 ok(!sbRef.contains(makeURI('http://foo.www.example.com'))); |
|
273 ok(sbRef.containsSuperDomain(makeURI('http://foo.www.example.com'))); |
|
274 ok(sbRef.containsSuperDomain(makeURI('http://foo.bar.www.example.com'))); |
|
275 ok(!sbRef.containsSuperDomain(makeURI('http://foo.bar.www.exxample.com'))); |
|
276 ok(!sbRef.containsSuperDomain(makeURI('http://example.com'))); |
|
277 ok(!sbRef.containsSuperDomain(makeURI('http://com/this.that/'))); |
|
278 ok(!sbRef.containsSuperDomain(makeURI('https://foo.www.example.com'))); |
|
279 ok(sbRef.contains(makeURI('http://www.example.com'))); |
|
280 policy.deactivate(); |
|
281 is(ssm.domainPolicyActive, false, "back to inactive"); |
|
282 ok(!sbRef.contains(makeURI('http://www.example.com')), |
|
283 "Disabling domain policy clears the set"); |
|
284 policy = ssm.activateDomainPolicy(); |
|
285 ok(policy.superBlacklist); |
|
286 isnot(sbRef, policy.superBlacklist, "Mint new sets each time!"); |
|
287 policy.deactivate(); |
|
288 is(policy.blacklist, null, "blacklist nulled out"); |
|
289 policy = ssm.activateDomainPolicy(); |
|
290 isnot(policy.blacklist, null, "non-null again"); |
|
291 isnot(policy.blacklist, sbRef, "freshly minted"); |
|
292 policy.deactivate(); |
|
293 |
|
294 // |
|
295 // Now, create and apply a mock-policy. We check the same policy both as |
|
296 // a blacklist and as a whitelist. |
|
297 // |
|
298 |
|
299 window.testPolicy = { |
|
300 // The policy. |
|
301 exceptions: ['http://test1.example.com', 'http://example.com'], |
|
302 superExceptions: ['http://test2.example.org', 'https://test1.example.com'], |
|
303 |
|
304 // The testcases. |
|
305 exempt: ['http://test1.example.com', 'http://example.com', |
|
306 'http://test2.example.org', 'http://sub1.test2.example.org', |
|
307 'https://sub1.test1.example.com'], |
|
308 |
|
309 notExempt: ['http://test2.example.com', 'http://sub1.test1.example.com', |
|
310 'http://www.example.com', 'https://test2.example.com', |
|
311 'https://example.com', 'http://test1.example.org'], |
|
312 }; |
|
313 |
|
314 policy = ssm.activateDomainPolicy(); |
|
315 info("Testing Blacklist-style Domain Policy"); |
|
316 return testDomainPolicy(true, testPolicy.exceptions, |
|
317 testPolicy.superExceptions, testPolicy.exempt, |
|
318 testPolicy.notExempt, policy.blacklist, |
|
319 policy.superBlacklist, rootWin); |
|
320 }).then(function() { |
|
321 policy.deactivate(); |
|
322 policy = ssm.activateDomainPolicy(); |
|
323 info("Testing Whitelist-style Domain Policy"); |
|
324 setScriptEnabledForBrowser(false); |
|
325 return testDomainPolicy(false, testPolicy.exceptions, |
|
326 testPolicy.superExceptions, testPolicy.exempt, |
|
327 testPolicy.notExempt, policy.whitelist, |
|
328 policy.superWhitelist, rootWin); |
|
329 }).then(function() { |
|
330 setScriptEnabledForBrowser(true); |
|
331 policy.deactivate(); |
|
332 |
|
333 SimpleTest.finish(); |
|
334 }); |
|
335 } |
|
336 |
|
337 ]]> |
|
338 </script> |
|
339 </window> |