addon-sdk/source/test/test-page-worker.js

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:8e9b8c1fd3df
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
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 "use strict";
5
6 const { Loader } = require('sdk/test/loader');
7 const { Page } = require("sdk/page-worker");
8 const { URL } = require("sdk/url");
9 const fixtures = require("./fixtures");
10 const testURI = fixtures.url("test.html");
11
12 const ERR_DESTROYED =
13 "Couldn't find the worker to receive this message. " +
14 "The script may not be initialized yet, or may already have been unloaded.";
15
16 exports.testSimplePageCreation = function(assert, done) {
17 let page = new Page({
18 contentScript: "self.postMessage(window.location.href)",
19 contentScriptWhen: "end",
20 onMessage: function (message) {
21 assert.equal(message, "about:blank",
22 "Page Worker should start with a blank page by default");
23 assert.equal(this, page, "The 'this' object is the page itself.");
24 done();
25 }
26 });
27 }
28
29 /*
30 * Tests that we can't be tricked by document overloads as we have access
31 * to wrapped nodes
32 */
33 exports.testWrappedDOM = function(assert, done) {
34 let page = Page({
35 allow: { script: true },
36 contentURL: "data:text/html;charset=utf-8,<script>document.getElementById=3;window.scrollTo=3;</script>",
37 contentScript: "window.addEventListener('load', function () " +
38 "self.postMessage([typeof(document.getElementById), " +
39 "typeof(window.scrollTo)]), true)",
40 onMessage: function (message) {
41 assert.equal(message[0],
42 "function",
43 "getElementById from content script is the native one");
44
45 assert.equal(message[1],
46 "function",
47 "scrollTo from content script is the native one");
48
49 done();
50 }
51 });
52 }
53
54 /*
55 // We do not offer unwrapped access to DOM since bug 601295 landed
56 // See 660780 to track progress of unwrap feature
57 exports.testUnwrappedDOM = function(assert, done) {
58 let page = Page({
59 allow: { script: true },
60 contentURL: "data:text/html;charset=utf-8,<script>document.getElementById=3;window.scrollTo=3;</script>",
61 contentScript: "window.addEventListener('load', function () " +
62 "self.postMessage([typeof(unsafeWindow.document.getElementById), " +
63 "typeof(unsafeWindow.scrollTo)]), true)",
64 onMessage: function (message) {
65 assert.equal(message[0],
66 "number",
67 "document inside page is free to be changed");
68
69 assert.equal(message[1],
70 "number",
71 "window inside page is free to be changed");
72
73 done();
74 }
75 });
76 }
77 */
78
79 exports.testPageProperties = function(assert) {
80 let page = new Page();
81
82 for each (let prop in ['contentURL', 'allow', 'contentScriptFile',
83 'contentScript', 'contentScriptWhen', 'on',
84 'postMessage', 'removeListener']) {
85 assert.ok(prop in page, prop + " property is defined on page.");
86 }
87
88 assert.ok(function () page.postMessage("foo") || true,
89 "postMessage doesn't throw exception on page.");
90 }
91
92 exports.testConstructorAndDestructor = function(assert, done) {
93 let loader = Loader(module);
94 let { Page } = loader.require("sdk/page-worker");
95 let global = loader.sandbox("sdk/page-worker");
96
97 let pagesReady = 0;
98
99 let page1 = Page({
100 contentScript: "self.postMessage('')",
101 contentScriptWhen: "end",
102 onMessage: pageReady
103 });
104 let page2 = Page({
105 contentScript: "self.postMessage('')",
106 contentScriptWhen: "end",
107 onMessage: pageReady
108 });
109
110 assert.notEqual(page1, page2,
111 "Page 1 and page 2 should be different objects.");
112
113 function pageReady() {
114 if (++pagesReady == 2) {
115 page1.destroy();
116 page2.destroy();
117
118 assert.ok(isDestroyed(page1), "page1 correctly unloaded.");
119 assert.ok(isDestroyed(page2), "page2 correctly unloaded.");
120
121 loader.unload();
122 done();
123 }
124 }
125 }
126
127 exports.testAutoDestructor = function(assert, done) {
128 let loader = Loader(module);
129 let { Page } = loader.require("sdk/page-worker");
130
131 let page = Page({
132 contentScript: "self.postMessage('')",
133 contentScriptWhen: "end",
134 onMessage: function() {
135 loader.unload();
136 assert.ok(isDestroyed(page), "Page correctly unloaded.");
137 done();
138 }
139 });
140 }
141
142 exports.testValidateOptions = function(assert) {
143 assert.throws(
144 function () Page({ contentURL: 'home' }),
145 /The `contentURL` option must be a valid URL\./,
146 "Validation correctly denied a non-URL contentURL"
147 );
148
149 assert.throws(
150 function () Page({ onMessage: "This is not a function."}),
151 /The option "onMessage" must be one of the following types: function/,
152 "Validation correctly denied a non-function onMessage."
153 );
154
155 assert.pass("Options validation is working.");
156 }
157
158 exports.testContentAndAllowGettersAndSetters = function(assert, done) {
159 let content = "data:text/html;charset=utf-8,<script>window.localStorage.allowScript=3;</script>";
160
161 // Load up the page with testURI initially for the resource:// principal,
162 // then load the actual data:* content, as data:* URIs no longer
163 // have localStorage
164 let page = Page({
165 contentURL: testURI,
166 contentScript: "if (window.location.href==='"+testURI+"')" +
167 " self.postMessage('reload');" +
168 "else " +
169 " self.postMessage(window.localStorage.allowScript)",
170 contentScriptWhen: "end",
171 onMessage: step0
172 });
173
174 function step0(message) {
175 if (message === 'reload')
176 return page.contentURL = content;
177 assert.equal(message, "3",
178 "Correct value expected for allowScript - 3");
179 assert.equal(page.contentURL, content,
180 "Correct content expected");
181 page.removeListener('message', step0);
182 page.on('message', step1);
183 page.allow = { script: false };
184 page.contentURL = content =
185 "data:text/html;charset=utf-8,<script>window.localStorage.allowScript='f'</script>";
186 }
187
188 function step1(message) {
189 assert.equal(message, "3",
190 "Correct value expected for allowScript - 3");
191 assert.equal(page.contentURL, content, "Correct content expected");
192 page.removeListener('message', step1);
193 page.on('message', step2);
194 page.allow = { script: true };
195 page.contentURL = content =
196 "data:text/html;charset=utf-8,<script>window.localStorage.allowScript='g'</script>";
197 }
198
199 function step2(message) {
200 assert.equal(message, "g",
201 "Correct value expected for allowScript - g");
202 assert.equal(page.contentURL, content, "Correct content expected");
203 page.removeListener('message', step2);
204 page.on('message', step3);
205 page.allow.script = false;
206 page.contentURL = content =
207 "data:text/html;charset=utf-8,<script>window.localStorage.allowScript=3</script>";
208 }
209
210 function step3(message) {
211 assert.equal(message, "g",
212 "Correct value expected for allowScript - g");
213 assert.equal(page.contentURL, content, "Correct content expected");
214 page.removeListener('message', step3);
215 page.on('message', step4);
216 page.allow.script = true;
217 page.contentURL = content =
218 "data:text/html;charset=utf-8,<script>window.localStorage.allowScript=4</script>";
219 }
220
221 function step4(message) {
222 assert.equal(message, "4",
223 "Correct value expected for allowScript - 4");
224 assert.equal(page.contentURL, content, "Correct content expected");
225 done();
226 }
227
228 }
229
230 exports.testOnMessageCallback = function(assert, done) {
231 Page({
232 contentScript: "self.postMessage('')",
233 contentScriptWhen: "end",
234 onMessage: function() {
235 assert.pass("onMessage callback called");
236 done();
237 }
238 });
239 }
240
241 exports.testMultipleOnMessageCallbacks = function(assert, done) {
242 let count = 0;
243 let page = Page({
244 contentScript: "self.postMessage('')",
245 contentScriptWhen: "end",
246 onMessage: () => count += 1
247 });
248 page.on('message', () => count += 2);
249 page.on('message', () => count *= 3);
250 page.on('message', () =>
251 assert.equal(count, 9, "All callbacks were called, in order."));
252 page.on('message', done);
253 };
254
255 exports.testLoadContentPage = function(assert, done) {
256 let page = Page({
257 onMessage: function(message) {
258 // The message is an array whose first item is the test method to call
259 // and the rest of whose items are arguments to pass it.
260 let msg = message.shift();
261 if (msg == "done")
262 return done();
263 assert[msg].apply(assert, message);
264 },
265 contentURL: fixtures.url("test-page-worker.html"),
266 contentScriptFile: fixtures.url("test-page-worker.js"),
267 contentScriptWhen: "ready"
268 });
269 }
270
271 exports.testAllowScriptDefault = function(assert, done) {
272 let page = Page({
273 onMessage: function(message) {
274 assert.ok(message, "Script is allowed to run by default.");
275 done();
276 },
277 contentURL: "data:text/html;charset=utf-8,<script>document.documentElement.setAttribute('foo', 3);</script>",
278 contentScript: "self.postMessage(document.documentElement.getAttribute('foo'))",
279 contentScriptWhen: "ready"
280 });
281 }
282
283 exports.testAllowScript = function(assert, done) {
284 let page = Page({
285 onMessage: function(message) {
286 assert.ok(message, "Script runs when allowed to do so.");
287 done();
288 },
289 allow: { script: true },
290 contentURL: "data:text/html;charset=utf-8,<script>document.documentElement.setAttribute('foo', 3);</script>",
291 contentScript: "self.postMessage(document.documentElement.hasAttribute('foo') && " +
292 " document.documentElement.getAttribute('foo') == 3)",
293 contentScriptWhen: "ready"
294 });
295 }
296
297 exports.testPingPong = function(assert, done) {
298 let page = Page({
299 contentURL: 'data:text/html;charset=utf-8,ping-pong',
300 contentScript: 'self.on("message", function(message) self.postMessage("pong"));'
301 + 'self.postMessage("ready");',
302 onMessage: function(message) {
303 if ('ready' == message) {
304 page.postMessage('ping');
305 }
306 else {
307 assert.ok(message, 'pong', 'Callback from contentScript');
308 done();
309 }
310 }
311 });
312 };
313
314 exports.testRedirect = function (assert, done) {
315 let page = Page({
316 contentURL: 'data:text/html;charset=utf-8,first-page',
317 contentScriptWhen: "end",
318 contentScript: '' +
319 'if (/first-page/.test(document.location.href)) ' +
320 ' document.location.href = "data:text/html;charset=utf-8,redirect";' +
321 'else ' +
322 ' self.port.emit("redirect", document.location.href);'
323 });
324
325 page.port.on('redirect', function (url) {
326 assert.equal(url, 'data:text/html;charset=utf-8,redirect', 'Reinjects contentScript on reload');
327 done();
328 });
329 };
330
331 exports.testRedirectIncludeArrays = function (assert, done) {
332 let firstURL = 'data:text/html;charset=utf-8,first-page';
333 let page = Page({
334 contentURL: firstURL,
335 contentScript: '(function () {' +
336 'self.port.emit("load", document.location.href);' +
337 ' self.port.on("redirect", function (url) {' +
338 ' document.location.href = url;' +
339 ' })' +
340 '})();',
341 include: ['about:blank', 'data:*']
342 });
343
344 page.port.on('load', function (url) {
345 if (url === firstURL) {
346 page.port.emit('redirect', 'about:blank');
347 } else if (url === 'about:blank') {
348 page.port.emit('redirect', 'about:mozilla');
349 assert.ok('`include` property handles arrays');
350 assert.equal(url, 'about:blank', 'Redirects work with accepted domains');
351 done();
352 } else if (url === 'about:mozilla') {
353 assert.fail('Should not redirect to restricted domain');
354 }
355 });
356 };
357
358 exports.testRedirectFromWorker = function (assert, done) {
359 let firstURL = 'data:text/html;charset=utf-8,first-page';
360 let secondURL = 'data:text/html;charset=utf-8,second-page';
361 let thirdURL = 'data:text/html;charset=utf-8,third-page';
362 let page = Page({
363 contentURL: firstURL,
364 contentScript: '(function () {' +
365 'self.port.emit("load", document.location.href);' +
366 ' self.port.on("redirect", function (url) {' +
367 ' document.location.href = url;' +
368 ' })' +
369 '})();',
370 include: 'data:*'
371 });
372
373 page.port.on('load', function (url) {
374 if (url === firstURL) {
375 page.port.emit('redirect', secondURL);
376 } else if (url === secondURL) {
377 page.port.emit('redirect', thirdURL);
378 } else if (url === thirdURL) {
379 page.port.emit('redirect', 'about:mozilla');
380 assert.equal(url, thirdURL, 'Redirects work with accepted domains on include strings');
381 done();
382 } else {
383 assert.fail('Should not redirect to unauthorized domains');
384 }
385 });
386 };
387
388 exports.testRedirectWithContentURL = function (assert, done) {
389 let firstURL = 'data:text/html;charset=utf-8,first-page';
390 let secondURL = 'data:text/html;charset=utf-8,second-page';
391 let thirdURL = 'data:text/html;charset=utf-8,third-page';
392 let page = Page({
393 contentURL: firstURL,
394 contentScript: '(function () {' +
395 'self.port.emit("load", document.location.href);' +
396 '})();',
397 include: 'data:*'
398 });
399
400 page.port.on('load', function (url) {
401 if (url === firstURL) {
402 page.contentURL = secondURL;
403 } else if (url === secondURL) {
404 page.contentURL = thirdURL;
405 } else if (url === thirdURL) {
406 page.contentURL = 'about:mozilla';
407 assert.equal(url, thirdURL, 'Redirects work with accepted domains on include strings');
408 done();
409 } else {
410 assert.fail('Should not redirect to unauthorized domains');
411 }
412 });
413 };
414
415
416 exports.testMultipleDestroys = function(assert) {
417 let page = Page();
418 page.destroy();
419 page.destroy();
420 assert.pass("Multiple destroys should not cause an error");
421 };
422
423 exports.testContentScriptOptionsOption = function(assert, done) {
424 let page = new Page({
425 contentScript: "self.postMessage( [typeof self.options.d, self.options] );",
426 contentScriptWhen: "end",
427 contentScriptOptions: {a: true, b: [1,2,3], c: "string", d: function(){ return 'test'}},
428 onMessage: function(msg) {
429 assert.equal(msg[0], 'undefined', 'functions are stripped from contentScriptOptions');
430 assert.equal(typeof msg[1], 'object', 'object as contentScriptOptions');
431 assert.equal(msg[1].a, true, 'boolean in contentScriptOptions');
432 assert.equal(msg[1].b.join(), '1,2,3', 'array and numbers in contentScriptOptions');
433 assert.equal(msg[1].c, 'string', 'string in contentScriptOptions');
434 done();
435 }
436 });
437 };
438
439 exports.testMessageQueue = function (assert, done) {
440 let page = new Page({
441 contentScript: 'self.on("message", function (m) {' +
442 'self.postMessage(m);' +
443 '});',
444 contentURL: 'data:text/html;charset=utf-8,',
445 });
446 page.postMessage('ping');
447 page.on('message', function (m) {
448 assert.equal(m, 'ping', 'postMessage should queue messages');
449 done();
450 });
451 };
452
453 function isDestroyed(page) {
454 try {
455 page.postMessage("foo");
456 }
457 catch (err) {
458 if (err.message == ERR_DESTROYED) {
459 return true;
460 }
461 else {
462 throw err;
463 }
464 }
465 return false;
466 }
467
468 require("test").run(exports);

mercurial