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

branch
TOR_BUG_3246
changeset 7
129ffea94266
equal deleted inserted replaced
-1:000000000000 0:55e20cb51f39
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
5 "use strict";
6
7 // Skipping due to window creation being unsupported in Fennec
8 module.metadata = {
9 engines: {
10 'Firefox': '*'
11 }
12 };
13
14 const { Cc, Ci } = require("chrome");
15 const { on } = require("sdk/event/core");
16 const { setTimeout } = require("sdk/timers");
17 const { LoaderWithHookedConsole } = require("sdk/test/loader");
18 const { Worker } = require("sdk/content/worker");
19 const { close } = require("sdk/window/helpers");
20 const { set: setPref } = require("sdk/preferences/service");
21 const { isArray } = require("sdk/lang/type");
22 const { URL } = require('sdk/url');
23 const fixtures = require("./fixtures");
24
25 const DEPRECATE_PREF = "devtools.errorconsole.deprecation_warnings";
26
27 const DEFAULT_CONTENT_URL = "data:text/html;charset=utf-8,foo";
28
29 const WINDOW_SCRIPT_URL = "data:text/html;charset=utf-8," +
30 "<script>window.addEventListener('message', function (e) {" +
31 " if (e.data === 'from -> content-script')" +
32 " window.postMessage('from -> window', '*');" +
33 "});</script>";
34
35 function makeWindow() {
36 let content =
37 "<?xml version=\"1.0\"?>" +
38 "<window " +
39 "xmlns=\"http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul\">" +
40 "<script>var documentValue=true;</script>" +
41 "</window>";
42 var url = "data:application/vnd.mozilla.xul+xml;charset=utf-8," +
43 encodeURIComponent(content);
44 var features = ["chrome", "width=10", "height=10"];
45
46 return Cc["@mozilla.org/embedcomp/window-watcher;1"].
47 getService(Ci.nsIWindowWatcher).
48 openWindow(null, url, null, features.join(","), null);
49 }
50
51 // Listen for only first one occurence of DOM event
52 function listenOnce(node, eventName, callback) {
53 node.addEventListener(eventName, function onevent(event) {
54 node.removeEventListener(eventName, onevent, true);
55 callback(node);
56 }, true);
57 }
58
59 // Load a given url in a given browser and fires the callback when it is loaded
60 function loadAndWait(browser, url, callback) {
61 listenOnce(browser, "load", callback);
62 // We have to wait before calling `loadURI` otherwise, if we call
63 // `loadAndWait` during browser load event, the history will be broken
64 setTimeout(function () {
65 browser.loadURI(url);
66 }, 0);
67 }
68
69 // Returns a test function that will automatically open a new chrome window
70 // with a <browser> element loaded on a given content URL
71 // The callback receive 3 arguments:
72 // - test: reference to the jetpack test object
73 // - browser: a reference to the <browser> xul node
74 // - done: a callback to call when test is over
75 function WorkerTest(url, callback) {
76 return function testFunction(assert, done) {
77 let chromeWindow = makeWindow();
78 chromeWindow.addEventListener("load", function onload() {
79 chromeWindow.removeEventListener("load", onload, true);
80 let browser = chromeWindow.document.createElement("browser");
81 browser.setAttribute("type", "content");
82 chromeWindow.document.documentElement.appendChild(browser);
83 // Wait for about:blank load event ...
84 listenOnce(browser, "load", function onAboutBlankLoad() {
85 // ... before loading the expected doc and waiting for its load event
86 loadAndWait(browser, url, function onDocumentLoaded() {
87 callback(assert, browser, function onTestDone() {
88
89 close(chromeWindow).then(done);
90 });
91 });
92 });
93 }, true);
94 };
95 }
96
97 exports["test:sample"] = WorkerTest(
98 DEFAULT_CONTENT_URL,
99 function(assert, browser, done) {
100
101 assert.notEqual(browser.contentWindow.location.href, "about:blank",
102 "window is now on the right document");
103
104 let window = browser.contentWindow
105 let worker = Worker({
106 window: window,
107 contentScript: "new " + function WorkerScope() {
108 // window is accessible
109 let myLocation = window.location.toString();
110 self.on("message", function(data) {
111 if (data == "hi!")
112 self.postMessage("bye!");
113 });
114 },
115 contentScriptWhen: "ready",
116 onMessage: function(msg) {
117 assert.equal("bye!", msg);
118 assert.equal(worker.url, window.location.href,
119 "worker.url still works");
120 done();
121 }
122 });
123
124 assert.equal(worker.url, window.location.href,
125 "worker.url works");
126 assert.equal(worker.contentURL, window.location.href,
127 "worker.contentURL works");
128 worker.postMessage("hi!");
129 }
130 );
131
132 exports["test:emit"] = WorkerTest(
133 DEFAULT_CONTENT_URL,
134 function(assert, browser, done) {
135
136 let worker = Worker({
137 window: browser.contentWindow,
138 contentScript: "new " + function WorkerScope() {
139 // Validate self.on and self.emit
140 self.port.on("addon-to-content", function (data) {
141 self.port.emit("content-to-addon", data);
142 });
143
144 // Check for global pollution
145 //if (typeof on != "undefined")
146 // self.postMessage("`on` is in globals");
147 if (typeof once != "undefined")
148 self.postMessage("`once` is in globals");
149 if (typeof emit != "undefined")
150 self.postMessage("`emit` is in globals");
151
152 },
153 onMessage: function(msg) {
154 assert.fail("Got an unexpected message : "+msg);
155 }
156 });
157
158 // Validate worker.port
159 worker.port.on("content-to-addon", function (data) {
160 assert.equal(data, "event data");
161 done();
162 });
163 worker.port.emit("addon-to-content", "event data");
164 }
165 );
166
167 exports["test:emit hack message"] = WorkerTest(
168 DEFAULT_CONTENT_URL,
169 function(assert, browser, done) {
170 let worker = Worker({
171 window: browser.contentWindow,
172 contentScript: "new " + function WorkerScope() {
173 // Validate self.port
174 self.port.on("message", function (data) {
175 self.port.emit("message", data);
176 });
177 // We should not receive message on self, but only on self.port
178 self.on("message", function (data) {
179 self.postMessage("message", data);
180 });
181 },
182 onError: function(e) {
183 assert.fail("Got exception: "+e);
184 }
185 });
186
187 worker.port.on("message", function (data) {
188 assert.equal(data, "event data");
189 done();
190 });
191 worker.on("message", function (data) {
192 assert.fail("Got an unexpected message : "+msg);
193 });
194 worker.port.emit("message", "event data");
195 }
196 );
197
198 exports["test:n-arguments emit"] = WorkerTest(
199 DEFAULT_CONTENT_URL,
200 function(assert, browser, done) {
201 let repeat = 0;
202 let worker = Worker({
203 window: browser.contentWindow,
204 contentScript: "new " + function WorkerScope() {
205 // Validate self.on and self.emit
206 self.port.on("addon-to-content", function (a1, a2, a3) {
207 self.port.emit("content-to-addon", a1, a2, a3);
208 });
209 }
210 });
211
212 // Validate worker.port
213 worker.port.on("content-to-addon", function (arg1, arg2, arg3) {
214 if (!repeat++) {
215 this.emit("addon-to-content", "first argument", "second", "third");
216 } else {
217 assert.equal(arg1, "first argument");
218 assert.equal(arg2, "second");
219 assert.equal(arg3, "third");
220 done();
221 }
222 });
223 worker.port.emit("addon-to-content", "first argument", "second", "third");
224 }
225 );
226
227 exports["test:post-json-values-only"] = WorkerTest(
228 DEFAULT_CONTENT_URL,
229 function(assert, browser, done) {
230
231 let worker = Worker({
232 window: browser.contentWindow,
233 contentScript: "new " + function WorkerScope() {
234 self.on("message", function (message) {
235 self.postMessage([ message.fun === undefined,
236 typeof message.w,
237 message.w && "port" in message.w,
238 message.w._url,
239 Array.isArray(message.array),
240 JSON.stringify(message.array)]);
241 });
242 }
243 });
244
245 // Validate worker.onMessage
246 let array = [1, 2, 3];
247 worker.on("message", function (message) {
248 assert.ok(message[0], "function becomes undefined");
249 assert.equal(message[1], "object", "object stays object");
250 assert.ok(message[2], "object's attributes are enumerable");
251 assert.equal(message[3], DEFAULT_CONTENT_URL,
252 "jsonable attributes are accessible");
253 // See bug 714891, Arrays may be broken over compartements:
254 assert.ok(message[4], "Array keeps being an array");
255 assert.equal(message[5], JSON.stringify(array),
256 "Array is correctly serialized");
257 done();
258 });
259 // Add a new url property sa the Class function used by
260 // Worker doesn't set enumerables to true for non-functions
261 worker._url = DEFAULT_CONTENT_URL;
262
263 worker.postMessage({ fun: function () {}, w: worker, array: array });
264 }
265 );
266
267 exports["test:emit-json-values-only"] = WorkerTest(
268 DEFAULT_CONTENT_URL,
269 function(assert, browser, done) {
270
271 let worker = Worker({
272 window: browser.contentWindow,
273 contentScript: "new " + function WorkerScope() {
274 // Validate self.on and self.emit
275 self.port.on("addon-to-content", function (fun, w, obj, array) {
276 self.port.emit("content-to-addon", [
277 fun === null,
278 typeof w,
279 "port" in w,
280 w._url,
281 "fun" in obj,
282 Object.keys(obj.dom).length,
283 Array.isArray(array),
284 JSON.stringify(array)
285 ]);
286 });
287 }
288 });
289
290 // Validate worker.port
291 let array = [1, 2, 3];
292 worker.port.on("content-to-addon", function (result) {
293 assert.ok(result[0], "functions become null");
294 assert.equal(result[1], "object", "objects stay objects");
295 assert.ok(result[2], "object's attributes are enumerable");
296 assert.equal(result[3], DEFAULT_CONTENT_URL,
297 "json attribute is accessible");
298 assert.ok(!result[4], "function as object attribute is removed");
299 assert.equal(result[5], 0, "DOM nodes are converted into empty object");
300 // See bug 714891, Arrays may be broken over compartments:
301 assert.ok(result[6], "Array keeps being an array");
302 assert.equal(result[7], JSON.stringify(array),
303 "Array is correctly serialized");
304 done();
305 });
306
307 let obj = {
308 fun: function () {},
309 dom: browser.contentWindow.document.createElement("div")
310 };
311 // Add a new url property sa the Class function used by
312 // Worker doesn't set enumerables to true for non-functions
313 worker._url = DEFAULT_CONTENT_URL;
314 worker.port.emit("addon-to-content", function () {}, worker, obj, array);
315 }
316 );
317
318 exports["test:content is wrapped"] = WorkerTest(
319 "data:text/html;charset=utf-8,<script>var documentValue=true;</script>",
320 function(assert, browser, done) {
321
322 let worker = Worker({
323 window: browser.contentWindow,
324 contentScript: "new " + function WorkerScope() {
325 self.postMessage(!window.documentValue);
326 },
327 contentScriptWhen: "ready",
328 onMessage: function(msg) {
329 assert.ok(msg,
330 "content script has a wrapped access to content document");
331 done();
332 }
333 });
334 }
335 );
336
337 exports["test:chrome is unwrapped"] = function(assert, done) {
338 let window = makeWindow();
339
340 listenOnce(window, "load", function onload() {
341
342 let worker = Worker({
343 window: window,
344 contentScript: "new " + function WorkerScope() {
345 self.postMessage(window.documentValue);
346 },
347 contentScriptWhen: "ready",
348 onMessage: function(msg) {
349 assert.ok(msg,
350 "content script has an unwrapped access to chrome document");
351 close(window).then(done);
352 }
353 });
354
355 });
356 }
357
358 exports["test:nothing is leaked to content script"] = WorkerTest(
359 DEFAULT_CONTENT_URL,
360 function(assert, browser, done) {
361
362 let worker = Worker({
363 window: browser.contentWindow,
364 contentScript: "new " + function WorkerScope() {
365 self.postMessage([
366 "ContentWorker" in window,
367 "UNWRAP_ACCESS_KEY" in window,
368 "getProxyForObject" in window
369 ]);
370 },
371 contentScriptWhen: "ready",
372 onMessage: function(list) {
373 assert.ok(!list[0], "worker API contrustor isn't leaked");
374 assert.ok(!list[1], "Proxy API stuff isn't leaked 1/2");
375 assert.ok(!list[2], "Proxy API stuff isn't leaked 2/2");
376 done();
377 }
378 });
379 }
380 );
381
382 exports["test:ensure console.xxx works in cs"] = WorkerTest(
383 DEFAULT_CONTENT_URL,
384 function(assert, browser, done) {
385 let { loader } = LoaderWithHookedConsole(module, onMessage);
386
387 // Intercept all console method calls
388 let calls = [];
389 function onMessage(type, msg) {
390 assert.equal(type, msg,
391 "console.xxx(\"xxx\"), i.e. message is equal to the " +
392 "console method name we are calling");
393 calls.push(msg);
394 }
395
396 // Finally, create a worker that will call all console methods
397 let worker = loader.require("sdk/content/worker").Worker({
398 window: browser.contentWindow,
399 contentScript: "new " + function WorkerScope() {
400 console.time("time");
401 console.log("log");
402 console.info("info");
403 console.warn("warn");
404 console.error("error");
405 console.debug("debug");
406 console.exception("exception");
407 console.timeEnd("timeEnd");
408 self.postMessage();
409 },
410 onMessage: function() {
411 // Ensure that console methods are called in the same execution order
412 const EXPECTED_CALLS = ["time", "log", "info", "warn", "error",
413 "debug", "exception", "timeEnd"];
414 assert.equal(JSON.stringify(calls),
415 JSON.stringify(EXPECTED_CALLS),
416 "console methods have been called successfully, in expected order");
417 done();
418 }
419 });
420 }
421 );
422
423 exports["test:setTimeout works with string argument"] = WorkerTest(
424 "data:text/html;charset=utf-8,<script>var docVal=5;</script>",
425 function(assert, browser, done) {
426 let worker = Worker({
427 window: browser.contentWindow,
428 contentScript: "new " + function ContentScriptScope() {
429 // must use "window.scVal" instead of "var csVal"
430 // since we are inside ContentScriptScope function.
431 // i'm NOT putting code-in-string inside code-in-string </YO DAWG>
432 window.csVal = 13;
433 setTimeout("self.postMessage([" +
434 "csVal, " +
435 "window.docVal, " +
436 "'ContentWorker' in window, " +
437 "'UNWRAP_ACCESS_KEY' in window, " +
438 "'getProxyForObject' in window, " +
439 "])", 1);
440 },
441 contentScriptWhen: "ready",
442 onMessage: function([csVal, docVal, chrome1, chrome2, chrome3]) {
443 // test timer code is executed in the correct context
444 assert.equal(csVal, 13, "accessing content-script values");
445 assert.notEqual(docVal, 5, "can't access document values (directly)");
446 assert.ok(!chrome1 && !chrome2 && !chrome3, "nothing is leaked from chrome");
447 done();
448 }
449 });
450 }
451 );
452
453 exports["test:setInterval works with string argument"] = WorkerTest(
454 DEFAULT_CONTENT_URL,
455 function(assert, browser, done) {
456 let count = 0;
457 let worker = Worker({
458 window: browser.contentWindow,
459 contentScript: "setInterval('self.postMessage(1)', 50)",
460 contentScriptWhen: "ready",
461 onMessage: function(one) {
462 count++;
463 assert.equal(one, 1, "got " + count + " message(s) from setInterval");
464 if (count >= 3) done();
465 }
466 });
467 }
468 );
469
470 exports["test:setInterval async Errors passed to .onError"] = WorkerTest(
471 DEFAULT_CONTENT_URL,
472 function(assert, browser, done) {
473 let count = 0;
474 let worker = Worker({
475 window: browser.contentWindow,
476 contentScript: "setInterval(() => { throw Error('ubik') }, 50)",
477 contentScriptWhen: "ready",
478 onError: function(err) {
479 count++;
480 assert.equal(err.message, "ubik",
481 "error (corectly) propagated " + count + " time(s)");
482 if (count >= 3) done();
483 }
484 });
485 }
486 );
487
488 exports["test:setTimeout throws array, passed to .onError"] = WorkerTest(
489 DEFAULT_CONTENT_URL,
490 function(assert, browser, done) {
491 let worker = Worker({
492 window: browser.contentWindow,
493 contentScript: "setTimeout(function() { throw ['array', 42] }, 1)",
494 contentScriptWhen: "ready",
495 onError: function(arr) {
496 assert.ok(isArray(arr),
497 "the type of thrown/propagated object is array");
498 assert.ok(arr.length==2,
499 "the propagated thrown array is the right length");
500 assert.equal(arr[1], 42,
501 "element inside the thrown array correctly propagated");
502 done();
503 }
504 });
505 }
506 );
507
508 exports["test:setTimeout string arg with SyntaxError to .onError"] = WorkerTest(
509 DEFAULT_CONTENT_URL,
510 function(assert, browser, done) {
511 let worker = Worker({
512 window: browser.contentWindow,
513 contentScript: "setTimeout('syntax 123 error', 1)",
514 contentScriptWhen: "ready",
515 onError: function(err) {
516 assert.equal(err.name, "SyntaxError",
517 "received SyntaxError thrown from bad code in string argument to setTimeout");
518 assert.ok('fileName' in err,
519 "propagated SyntaxError contains a fileName property");
520 assert.ok('stack' in err,
521 "propagated SyntaxError contains a stack property");
522 assert.equal(err.message, "missing ; before statement",
523 "propagated SyntaxError has the correct (helpful) message");
524 assert.equal(err.lineNumber, 1,
525 "propagated SyntaxError was thrown on the right lineNumber");
526 done();
527 }
528 });
529 }
530 );
531
532 exports["test:setTimeout can't be cancelled by content"] = WorkerTest(
533 "data:text/html;charset=utf-8,<script>var documentValue=true;</script>",
534 function(assert, browser, done) {
535
536 let worker = Worker({
537 window: browser.contentWindow,
538 contentScript: "new " + function WorkerScope() {
539 let id = setTimeout(function () {
540 self.postMessage("timeout");
541 }, 100);
542 unsafeWindow.eval("clearTimeout("+id+");");
543 },
544 contentScriptWhen: "ready",
545 onMessage: function(msg) {
546 assert.ok(msg,
547 "content didn't managed to cancel our setTimeout");
548 done();
549 }
550 });
551 }
552 );
553
554 exports["test:clearTimeout"] = WorkerTest(
555 "data:text/html;charset=utf-8,clear timeout",
556 function(assert, browser, done) {
557 let worker = Worker({
558 window: browser.contentWindow,
559 contentScript: "new " + function WorkerScope() {
560 let id1 = setTimeout(function() {
561 self.postMessage("failed");
562 }, 10);
563 let id2 = setTimeout(function() {
564 self.postMessage("done");
565 }, 100);
566 clearTimeout(id1);
567 },
568 contentScriptWhen: "ready",
569 onMessage: function(msg) {
570 if (msg === "failed") {
571 assert.fail("failed to cancel timer");
572 } else {
573 assert.pass("timer cancelled");
574 done();
575 }
576 }
577 });
578 }
579 );
580
581 exports["test:clearInterval"] = WorkerTest(
582 "data:text/html;charset=utf-8,clear timeout",
583 function(assert, browser, done) {
584 let called = 0;
585 let worker = Worker({
586 window: browser.contentWindow,
587 contentScript: "new " + function WorkerScope() {
588 let id = setInterval(function() {
589 self.postMessage("intreval")
590 clearInterval(id)
591 setTimeout(function() {
592 self.postMessage("done")
593 }, 100)
594 }, 10);
595 },
596 contentScriptWhen: "ready",
597 onMessage: function(msg) {
598 if (msg === "intreval") {
599 called = called + 1;
600 if (called > 1) assert.fail("failed to cancel timer");
601 } else {
602 assert.pass("interval cancelled");
603 done();
604 }
605 }
606 });
607 }
608 )
609
610 exports["test:setTimeout are unregistered on content unload"] = WorkerTest(
611 DEFAULT_CONTENT_URL,
612 function(assert, browser, done) {
613
614 let originalWindow = browser.contentWindow;
615 let worker = Worker({
616 window: browser.contentWindow,
617 contentScript: "new " + function WorkerScope() {
618 document.title = "ok";
619 let i = 0;
620 setInterval(function () {
621 document.title = i++;
622 }, 10);
623 },
624 contentScriptWhen: "ready"
625 });
626
627 // Change location so that content script is destroyed,
628 // and all setTimeout/setInterval should be unregistered.
629 // Wait some cycles in order to execute some intervals.
630 setTimeout(function () {
631 // Bug 689621: Wait for the new document load so that we are sure that
632 // previous document cancelled its intervals
633 let url2 = "data:text/html;charset=utf-8,<title>final</title>";
634 loadAndWait(browser, url2, function onload() {
635 let titleAfterLoad = originalWindow.document.title;
636 // Wait additional cycles to verify that intervals are really cancelled
637 setTimeout(function () {
638 assert.equal(browser.contentDocument.title, "final",
639 "New document has not been modified");
640 assert.equal(originalWindow.document.title, titleAfterLoad,
641 "Nor previous one");
642
643 done();
644 }, 100);
645 });
646 }, 100);
647 }
648 );
649
650 exports['test:check window attribute in iframes'] = WorkerTest(
651 DEFAULT_CONTENT_URL,
652 function(assert, browser, done) {
653
654 // Create a first iframe and wait for its loading
655 let contentWin = browser.contentWindow;
656 let contentDoc = contentWin.document;
657 let iframe = contentDoc.createElement("iframe");
658 contentDoc.body.appendChild(iframe);
659
660 listenOnce(iframe, "load", function onload() {
661
662 // Create a second iframe inside the first one and wait for its loading
663 let iframeDoc = iframe.contentWindow.document;
664 let subIframe = iframeDoc.createElement("iframe");
665 iframeDoc.body.appendChild(subIframe);
666
667 listenOnce(subIframe, "load", function onload() {
668 subIframe.removeEventListener("load", onload, true);
669
670 // And finally create a worker against this second iframe
671 let worker = Worker({
672 window: subIframe.contentWindow,
673 contentScript: 'new ' + function WorkerScope() {
674 self.postMessage([
675 window.top !== window,
676 frameElement,
677 window.parent !== window,
678 top.location.href,
679 parent.location.href,
680 ]);
681 },
682 onMessage: function(msg) {
683 assert.ok(msg[0], "window.top != window");
684 assert.ok(msg[1], "window.frameElement is defined");
685 assert.ok(msg[2], "window.parent != window");
686 assert.equal(msg[3], contentWin.location.href,
687 "top.location refers to the toplevel content doc");
688 assert.equal(msg[4], iframe.contentWindow.location.href,
689 "parent.location refers to the first iframe doc");
690 done();
691 }
692 });
693
694 });
695 subIframe.setAttribute("src", "data:text/html;charset=utf-8,bar");
696
697 });
698 iframe.setAttribute("src", "data:text/html;charset=utf-8,foo");
699 }
700 );
701
702 exports['test:check window attribute in toplevel documents'] = WorkerTest(
703 DEFAULT_CONTENT_URL,
704 function(assert, browser, done) {
705
706 let worker = Worker({
707 window: browser.contentWindow,
708 contentScript: 'new ' + function WorkerScope() {
709 self.postMessage([
710 window.top === window,
711 frameElement,
712 window.parent === window
713 ]);
714 },
715 onMessage: function(msg) {
716 assert.ok(msg[0], "window.top == window");
717 assert.ok(!msg[1], "window.frameElement is null");
718 assert.ok(msg[2], "window.parent == window");
719 done();
720 }
721 });
722 }
723 );
724
725 exports["test:check worker API with page history"] = WorkerTest(
726 DEFAULT_CONTENT_URL,
727 function(assert, browser, done) {
728 let url2 = "data:text/html;charset=utf-8,bar";
729
730 loadAndWait(browser, url2, function () {
731 let worker = Worker({
732 window: browser.contentWindow,
733 contentScript: "new " + function WorkerScope() {
734 // Just before the content script is disable, we register a timeout
735 // that will be disable until the page gets visible again
736 self.on("pagehide", function () {
737 setTimeout(function () {
738 self.postMessage("timeout restored");
739 }, 0);
740 });
741 },
742 contentScriptWhen: "start"
743 });
744
745 // postMessage works correctly when the page is visible
746 worker.postMessage("ok");
747
748 // We have to wait before going back into history,
749 // otherwise `goBack` won't do anything.
750 setTimeout(function () {
751 browser.goBack();
752 }, 0);
753
754 // Wait for the document to be hidden
755 browser.addEventListener("pagehide", function onpagehide() {
756 browser.removeEventListener("pagehide", onpagehide, false);
757 // Now any event sent to this worker should throw
758
759 assert.throws(
760 function () { worker.postMessage("data"); },
761 /The page is currently hidden and can no longer be used/,
762 "postMessage should throw when the page is hidden in history"
763 );
764
765 assert.throws(
766 function () { worker.port.emit("event"); },
767 /The page is currently hidden and can no longer be used/,
768 "port.emit should throw when the page is hidden in history"
769 );
770
771 // Display the page with attached content script back in order to resume
772 // its timeout and receive the expected message.
773 // We have to delay this in order to not break the history.
774 // We delay for a non-zero amount of time in order to ensure that we
775 // do not receive the message immediatly, so that the timeout is
776 // actually disabled
777 setTimeout(function () {
778 worker.on("message", function (data) {
779 assert.ok(data, "timeout restored");
780 done();
781 });
782 browser.goForward();
783 }, 500);
784
785 }, false);
786 });
787
788 }
789 );
790
791 exports['test:conentScriptFile as URL instance'] = WorkerTest(
792 DEFAULT_CONTENT_URL,
793 function(assert, browser, done) {
794
795 let url = new URL(fixtures.url("test-contentScriptFile.js"));
796 let worker = Worker({
797 window: browser.contentWindow,
798 contentScriptFile: url,
799 onMessage: function(msg) {
800 assert.equal(msg, "msg from contentScriptFile",
801 "received a wrong message from contentScriptFile");
802 done();
803 }
804 });
805 }
806 );
807
808 exports["test:worker events"] = WorkerTest(
809 DEFAULT_CONTENT_URL,
810 function (assert, browser, done) {
811 let window = browser.contentWindow;
812 let events = [];
813 let worker = Worker({
814 window: window,
815 contentScript: 'new ' + function WorkerScope() {
816 self.postMessage('start');
817 },
818 onAttach: win => {
819 events.push('attach');
820 assert.pass('attach event called when attached');
821 assert.equal(window, win, 'attach event passes in attached window');
822 },
823 onError: err => {
824 assert.equal(err.message, 'Custom',
825 'Error passed into error event');
826 worker.detach();
827 },
828 onMessage: msg => {
829 assert.pass('`onMessage` handles postMessage')
830 throw new Error('Custom');
831 },
832 onDetach: _ => {
833 assert.pass('`onDetach` called when worker detached');
834 done();
835 }
836 });
837 // `attach` event is called synchronously during instantiation,
838 // so we can't listen to that, TODO FIX?
839 // worker.on('attach', obj => console.log('attach', obj));
840 }
841 );
842
843 exports["test:onDetach in contentScript on destroy"] = WorkerTest(
844 "data:text/html;charset=utf-8,foo#detach",
845 function(assert, browser, done) {
846 let worker = Worker({
847 window: browser.contentWindow,
848 contentScript: 'new ' + function WorkerScope() {
849 self.port.on('detach', function(reason) {
850 window.location.hash += '!' + reason;
851 })
852 },
853 });
854 browser.contentWindow.addEventListener('hashchange', _ => {
855 assert.equal(browser.contentWindow.location.hash, '#detach!',
856 "location.href is as expected");
857 done();
858 })
859 worker.destroy();
860 }
861 );
862
863 exports["test:onDetach in contentScript on unload"] = WorkerTest(
864 "data:text/html;charset=utf-8,foo#detach",
865 function(assert, browser, done) {
866 let { loader } = LoaderWithHookedConsole(module);
867 let worker = loader.require("sdk/content/worker").Worker({
868 window: browser.contentWindow,
869 contentScript: 'new ' + function WorkerScope() {
870 self.port.on('detach', function(reason) {
871 window.location.hash += '!' + reason;
872 })
873 },
874 });
875 browser.contentWindow.addEventListener('hashchange', _ => {
876 assert.equal(browser.contentWindow.location.hash, '#detach!shutdown',
877 "location.href is as expected");
878 done();
879 })
880 loader.unload('shutdown');
881 }
882 );
883
884 exports["test:console method log functions properly"] = WorkerTest(
885 DEFAULT_CONTENT_URL,
886 function(assert, browser, done) {
887 let logs = [];
888
889 let clean = message =>
890 message.trim().
891 replace(/[\r\n]/g, " ").
892 replace(/ +/g, " ");
893
894 let onMessage = (type, message) => logs.push(clean(message));
895 let { loader } = LoaderWithHookedConsole(module, onMessage);
896
897 let worker = loader.require("sdk/content/worker").Worker({
898 window: browser.contentWindow,
899 contentScript: "new " + function WorkerScope() {
900 console.log(Function);
901 console.log((foo) => foo * foo);
902 console.log(function foo(bar) { return bar + bar });
903
904 self.postMessage();
905 },
906 onMessage: () => {
907 assert.deepEqual(logs, [
908 "function Function() { [native code] }",
909 "(foo) => foo * foo",
910 "function foo(bar) { \"use strict\"; return bar + bar }"
911 ]);
912
913 done();
914 }
915 });
916 }
917 );
918
919 exports["test:global postMessage"] = WorkerTest(
920 WINDOW_SCRIPT_URL,
921 function(assert, browser, done) {
922 let contentScript = "window.addEventListener('message', function (e) {" +
923 " if (e.data === 'from -> window')" +
924 " self.port.emit('response', e.data, e.origin);" +
925 "});" +
926 "postMessage('from -> content-script', '*');";
927 let { loader } = LoaderWithHookedConsole(module);
928 let worker = loader.require("sdk/content/worker").Worker({
929 window: browser.contentWindow,
930 contentScriptWhen: "ready",
931 contentScript: contentScript
932 });
933
934 worker.port.on("response", (data, origin) => {
935 assert.equal(data, "from -> window", "Communication from content-script to window completed");
936 done();
937 });
938 });
939
940 require("test").run(exports);

mercurial