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

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

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

mercurial