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

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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 const hiddenFrames = require("sdk/frame/hidden-frame");
michael@0 6 const { create: makeFrame } = require("sdk/frame/utils");
michael@0 7 const { window } = require("sdk/addon/window");
michael@0 8 const { Loader } = require('sdk/test/loader');
michael@0 9 const { URL } = require("sdk/url");
michael@0 10 const testURI = require("./fixtures").url("test.html");
michael@0 11 const testHost = URL(testURI).scheme + '://' + URL(testURI).host;
michael@0 12
michael@0 13 /*
michael@0 14 * Utility function that allow to easily run a proxy test with a clean
michael@0 15 * new HTML document. See first unit test for usage.
michael@0 16 */
michael@0 17 function createProxyTest(html, callback) {
michael@0 18 return function (assert, done) {
michael@0 19 let url = 'data:text/html;charset=utf-8,' + encodeURIComponent(html);
michael@0 20 let principalLoaded = false;
michael@0 21
michael@0 22 let element = makeFrame(window.document, {
michael@0 23 nodeName: "iframe",
michael@0 24 type: "content",
michael@0 25 allowJavascript: true,
michael@0 26 allowPlugins: true,
michael@0 27 allowAuth: true,
michael@0 28 uri: testURI
michael@0 29 });
michael@0 30
michael@0 31 element.addEventListener("DOMContentLoaded", onDOMReady, false);
michael@0 32
michael@0 33 function onDOMReady() {
michael@0 34 // Reload frame after getting principal from `testURI`
michael@0 35 if (!principalLoaded) {
michael@0 36 element.setAttribute("src", url);
michael@0 37 principalLoaded = true;
michael@0 38 return;
michael@0 39 }
michael@0 40
michael@0 41 assert.equal(element.getAttribute("src"), url, "correct URL loaded");
michael@0 42 element.removeEventListener("DOMContentLoaded", onDOMReady,
michael@0 43 false);
michael@0 44 let xrayWindow = element.contentWindow;
michael@0 45 let rawWindow = xrayWindow.wrappedJSObject;
michael@0 46
michael@0 47 let isDone = false;
michael@0 48 let helper = {
michael@0 49 xrayWindow: xrayWindow,
michael@0 50 rawWindow: rawWindow,
michael@0 51 createWorker: function (contentScript) {
michael@0 52 return createWorker(assert, xrayWindow, contentScript, helper.done);
michael@0 53 },
michael@0 54 done: function () {
michael@0 55 if (isDone)
michael@0 56 return;
michael@0 57 isDone = true;
michael@0 58 element.parentNode.removeChild(element);
michael@0 59 done();
michael@0 60 }
michael@0 61 };
michael@0 62 callback(helper, assert);
michael@0 63 }
michael@0 64 };
michael@0 65 }
michael@0 66
michael@0 67 function createWorker(assert, xrayWindow, contentScript, done) {
michael@0 68 let loader = Loader(module);
michael@0 69 let Worker = loader.require("sdk/content/worker").Worker;
michael@0 70 let worker = Worker({
michael@0 71 window: xrayWindow,
michael@0 72 contentScript: [
michael@0 73 'new ' + function () {
michael@0 74 assert = function assert(v, msg) {
michael@0 75 self.port.emit("assert", {assertion:v, msg:msg});
michael@0 76 }
michael@0 77 done = function done() {
michael@0 78 self.port.emit("done");
michael@0 79 }
michael@0 80 },
michael@0 81 contentScript
michael@0 82 ]
michael@0 83 });
michael@0 84
michael@0 85 worker.port.on("done", done);
michael@0 86 worker.port.on("assert", function (data) {
michael@0 87 assert.ok(data.assertion, data.msg);
michael@0 88 });
michael@0 89
michael@0 90 return worker;
michael@0 91 }
michael@0 92
michael@0 93 /* Examples for the `createProxyTest` uses */
michael@0 94
michael@0 95 let html = "<script>var documentGlobal = true</script>";
michael@0 96
michael@0 97 exports["test Create Proxy Test"] = createProxyTest(html, function (helper, assert) {
michael@0 98 // You can get access to regular `test` object in second argument of
michael@0 99 // `createProxyTest` method:
michael@0 100 assert.ok(helper.rawWindow.documentGlobal,
michael@0 101 "You have access to a raw window reference via `helper.rawWindow`");
michael@0 102 assert.ok(!("documentGlobal" in helper.xrayWindow),
michael@0 103 "You have access to an XrayWrapper reference via `helper.xrayWindow`");
michael@0 104
michael@0 105 // If you do not create a Worker, you have to call helper.done(),
michael@0 106 // in order to say when your test is finished
michael@0 107 helper.done();
michael@0 108 });
michael@0 109
michael@0 110 exports["test Create Proxy Test With Worker"] = createProxyTest("", function (helper) {
michael@0 111
michael@0 112 helper.createWorker(
michael@0 113 "new " + function WorkerScope() {
michael@0 114 assert(true, "You can do assertions in your content script");
michael@0 115 // And if you create a worker, you either have to call `done`
michael@0 116 // from content script or helper.done()
michael@0 117 done();
michael@0 118 }
michael@0 119 );
michael@0 120
michael@0 121 });
michael@0 122
michael@0 123 exports["test Create Proxy Test With Events"] = createProxyTest("", function (helper, assert) {
michael@0 124
michael@0 125 let worker = helper.createWorker(
michael@0 126 "new " + function WorkerScope() {
michael@0 127 self.port.emit("foo");
michael@0 128 }
michael@0 129 );
michael@0 130
michael@0 131 worker.port.on("foo", function () {
michael@0 132 assert.pass("You can use events");
michael@0 133 // And terminate your test with helper.done:
michael@0 134 helper.done();
michael@0 135 });
michael@0 136
michael@0 137 });
michael@0 138
michael@0 139 // Bug 714778: There was some issue around `toString` functions
michael@0 140 // that ended up being shared between content scripts
michael@0 141 exports["test Shared To String Proxies"] = createProxyTest("", function(helper) {
michael@0 142
michael@0 143 let worker = helper.createWorker(
michael@0 144 'new ' + function ContentScriptScope() {
michael@0 145 // We ensure that `toString` can't be modified so that nothing could
michael@0 146 // leak to/from the document and between content scripts
michael@0 147 // It only applies to JS proxies, there isn't any such issue with xrays.
michael@0 148 //document.location.toString = function foo() {};
michael@0 149 document.location.toString.foo = "bar";
michael@0 150 assert("foo" in document.location.toString, "document.location.toString can be modified");
michael@0 151 assert(document.location.toString() == "data:text/html;charset=utf-8,",
michael@0 152 "First document.location.toString()");
michael@0 153 self.postMessage("next");
michael@0 154 }
michael@0 155 );
michael@0 156 worker.on("message", function () {
michael@0 157 helper.createWorker(
michael@0 158 'new ' + function ContentScriptScope2() {
michael@0 159 assert(!("foo" in document.location.toString),
michael@0 160 "document.location.toString is different for each content script");
michael@0 161 assert(document.location.toString() == "data:text/html;charset=utf-8,",
michael@0 162 "Second document.location.toString()");
michael@0 163 done();
michael@0 164 }
michael@0 165 );
michael@0 166 });
michael@0 167 });
michael@0 168
michael@0 169
michael@0 170 // Ensure that postMessage is working correctly across documents with an iframe
michael@0 171 let html = '<iframe id="iframe" name="test" src="data:text/html;charset=utf-8," />';
michael@0 172 exports["test postMessage"] = createProxyTest(html, function (helper, assert) {
michael@0 173 let ifWindow = helper.xrayWindow.document.getElementById("iframe").contentWindow;
michael@0 174 // Listen without proxies, to check that it will work in regular case
michael@0 175 // simulate listening from a web document.
michael@0 176 ifWindow.addEventListener("message", function listener(event) {
michael@0 177 ifWindow.removeEventListener("message", listener, false);
michael@0 178 // As we are in system principal, event is an XrayWrapper
michael@0 179 // xrays use current compartments when calling postMessage method.
michael@0 180 // Whereas js proxies was using postMessage method compartment,
michael@0 181 // not the caller one.
michael@0 182 assert.strictEqual(event.source, helper.xrayWindow,
michael@0 183 "event.source is the top window");
michael@0 184 assert.equal(event.origin, testHost, "origin matches testHost");
michael@0 185
michael@0 186 assert.equal(event.data, "{\"foo\":\"bar\\n \\\"escaped\\\".\"}",
michael@0 187 "message data is correct");
michael@0 188
michael@0 189 helper.done();
michael@0 190 }, false);
michael@0 191
michael@0 192 helper.createWorker(
michael@0 193 'new ' + function ContentScriptScope() {
michael@0 194 var json = JSON.stringify({foo : "bar\n \"escaped\"."});
michael@0 195
michael@0 196 document.getElementById("iframe").contentWindow.postMessage(json, "*");
michael@0 197 }
michael@0 198 );
michael@0 199 });
michael@0 200
michael@0 201 let html = '<input id="input2" type="checkbox" />';
michael@0 202 exports["test Object Listener"] = createProxyTest(html, function (helper) {
michael@0 203
michael@0 204 helper.createWorker(
michael@0 205 'new ' + function ContentScriptScope() {
michael@0 206 // Test objects being given as event listener
michael@0 207 let input = document.getElementById("input2");
michael@0 208 let myClickListener = {
michael@0 209 called: false,
michael@0 210 handleEvent: function(event) {
michael@0 211 assert(this === myClickListener, "`this` is the original object");
michael@0 212 assert(!this.called, "called only once");
michael@0 213 this.called = true;
michael@0 214 assert(event.target, input, "event.target is the wrapped window");
michael@0 215 done();
michael@0 216 }
michael@0 217 };
michael@0 218
michael@0 219 window.addEventListener("click", myClickListener, true);
michael@0 220 input.click();
michael@0 221 window.removeEventListener("click", myClickListener, true);
michael@0 222 }
michael@0 223 );
michael@0 224
michael@0 225 });
michael@0 226
michael@0 227 exports["test Object Listener 2"] = createProxyTest("", function (helper) {
michael@0 228
michael@0 229 helper.createWorker(
michael@0 230 ('new ' + function ContentScriptScope() {
michael@0 231 // variable replaced with `testHost`
michael@0 232 let testHost = "TOKEN";
michael@0 233 // Verify object as DOM event listener
michael@0 234 let myMessageListener = {
michael@0 235 called: false,
michael@0 236 handleEvent: function(event) {
michael@0 237 window.removeEventListener("message", myMessageListener, true);
michael@0 238
michael@0 239 assert(this == myMessageListener, "`this` is the original object");
michael@0 240 assert(!this.called, "called only once");
michael@0 241 this.called = true;
michael@0 242 assert(event.target == document.defaultView, "event.target is the wrapped window");
michael@0 243 assert(event.source == document.defaultView, "event.source is the wrapped window");
michael@0 244 assert(event.origin == testHost, "origin matches testHost");
michael@0 245 assert(event.data == "ok", "message data is correct");
michael@0 246 done();
michael@0 247 }
michael@0 248 };
michael@0 249
michael@0 250 window.addEventListener("message", myMessageListener, true);
michael@0 251 document.defaultView.postMessage("ok", '*');
michael@0 252 }
michael@0 253 ).replace("TOKEN", testHost));
michael@0 254
michael@0 255 });
michael@0 256
michael@0 257 let html = '<input id="input" type="text" /><input id="input3" type="checkbox" />' +
michael@0 258 '<input id="input2" type="checkbox" />';
michael@0 259
michael@0 260 exports.testStringOverload = createProxyTest(html, function (helper, assert) {
michael@0 261 // Proxy - toString error
michael@0 262 let originalString = "string";
michael@0 263 let p = Proxy.create({
michael@0 264 get: function(receiver, name) {
michael@0 265 if (name == "binded")
michael@0 266 return originalString.toString.bind(originalString);
michael@0 267 return originalString[name];
michael@0 268 }
michael@0 269 });
michael@0 270 assert.throws(function () {
michael@0 271 p.toString();
michael@0 272 },
michael@0 273 /toString method called on incompatible Proxy/,
michael@0 274 "toString can't be called with this being the proxy");
michael@0 275 assert.equal(p.binded(), "string", "but it works if we bind this to the original string");
michael@0 276
michael@0 277 helper.createWorker(
michael@0 278 'new ' + function ContentScriptScope() {
michael@0 279 // RightJS is hacking around String.prototype, and do similar thing:
michael@0 280 // Pass `this` from a String prototype method.
michael@0 281 // It is funny because typeof this == "object"!
michael@0 282 // So that when we pass `this` to a native method,
michael@0 283 // our proxy code can fail on another even more crazy thing.
michael@0 284 // See following test to see what fails around proxies.
michael@0 285 String.prototype.update = function () {
michael@0 286 assert(typeof this == "object", "in update, `this` is an object");
michael@0 287 assert(this.toString() == "input", "in update, `this.toString works");
michael@0 288 return document.querySelectorAll(this);
michael@0 289 };
michael@0 290 assert("input".update().length == 3, "String.prototype overload works");
michael@0 291 done();
michael@0 292 }
michael@0 293 );
michael@0 294 });
michael@0 295
michael@0 296 exports["test MozMatchedSelector"] = createProxyTest("", function (helper) {
michael@0 297 helper.createWorker(
michael@0 298 'new ' + function ContentScriptScope() {
michael@0 299 // Check mozMatchesSelector XrayWrappers bug:
michael@0 300 // mozMatchesSelector returns bad results when we are not calling it from the node itself
michael@0 301 // SEE BUG 658909: mozMatchesSelector returns incorrect results with XrayWrappers
michael@0 302 assert(document.createElement( "div" ).mozMatchesSelector("div"),
michael@0 303 "mozMatchesSelector works while being called from the node");
michael@0 304 assert(document.documentElement.mozMatchesSelector.call(
michael@0 305 document.createElement( "div" ),
michael@0 306 "div"
michael@0 307 ),
michael@0 308 "mozMatchesSelector works while being called from a " +
michael@0 309 "function reference to " +
michael@0 310 "document.documentElement.mozMatchesSelector.call");
michael@0 311 done();
michael@0 312 }
michael@0 313 );
michael@0 314 });
michael@0 315
michael@0 316 exports["test Events Overload"] = createProxyTest("", function (helper) {
michael@0 317
michael@0 318 helper.createWorker(
michael@0 319 'new ' + function ContentScriptScope() {
michael@0 320 // If we add a "____proxy" attribute on XrayWrappers in order to store
michael@0 321 // the related proxy to create an unique proxy for each wrapper;
michael@0 322 // we end up setting this attribute to prototype objects :x
michael@0 323 // And so, instances created with such prototype will be considered
michael@0 324 // as equal to the prototype ...
michael@0 325 // // Internal method that return the proxy for a given XrayWrapper
michael@0 326 // function proxify(obj) {
michael@0 327 // if (obj._proxy) return obj._proxy;
michael@0 328 // return obj._proxy = Proxy.create(...);
michael@0 329 // }
michael@0 330 //
michael@0 331 // // Get a proxy of an XrayWrapper prototype object
michael@0 332 // let proto = proxify(xpcProto);
michael@0 333 //
michael@0 334 // // Use this proxy as a prototype
michael@0 335 // function Constr() {}
michael@0 336 // Constr.proto = proto;
michael@0 337 //
michael@0 338 // // Try to create an instance using this prototype
michael@0 339 // let xpcInstance = new Constr();
michael@0 340 // let wrapper = proxify(xpcInstance)
michael@0 341 //
michael@0 342 // xpcProto._proxy = proto and as xpcInstance.__proto__ = xpcProto,
michael@0 343 // xpcInstance._proxy = proto ... and profixy(xpcInstance) = proto :(
michael@0 344 //
michael@0 345 let proto = window.document.createEvent('HTMLEvents').__proto__;
michael@0 346 window.Event.prototype = proto;
michael@0 347 let event = document.createEvent('HTMLEvents');
michael@0 348 assert(event !== proto, "Event should not be equal to its prototype");
michael@0 349 event.initEvent('dataavailable', true, true);
michael@0 350 assert(event.type === 'dataavailable', "Events are working fine");
michael@0 351 done();
michael@0 352 }
michael@0 353 );
michael@0 354
michael@0 355 });
michael@0 356
michael@0 357 exports["test Nested Attributes"] = createProxyTest("", function (helper) {
michael@0 358
michael@0 359 helper.createWorker(
michael@0 360 'new ' + function ContentScriptScope() {
michael@0 361 // XrayWrappers has a bug when you set an attribute on it,
michael@0 362 // in some cases, it creates an unnecessary wrapper that introduces
michael@0 363 // a different object that refers to the same original object
michael@0 364 // Check that our wrappers don't reproduce this bug
michael@0 365 // SEE BUG 658560: Fix identity problem with CrossOriginWrappers
michael@0 366 let o = {sandboxObject:true};
michael@0 367 window.nested = o;
michael@0 368 o.foo = true;
michael@0 369 assert(o === window.nested, "Nested attribute to sandbox object should not be proxified");
michael@0 370 window.nested = document;
michael@0 371 assert(window.nested === document, "Nested attribute to proxy should not be double proxified");
michael@0 372 done();
michael@0 373 }
michael@0 374 );
michael@0 375
michael@0 376 });
michael@0 377
michael@0 378 exports["test Form nodeName"] = createProxyTest("", function (helper) {
michael@0 379
michael@0 380 helper.createWorker(
michael@0 381 'new ' + function ContentScriptScope() {
michael@0 382 let body = document.body;
michael@0 383 // Check form[nodeName]
michael@0 384 let form = document.createElement("form");
michael@0 385 let input = document.createElement("input");
michael@0 386 input.setAttribute("name", "test");
michael@0 387 form.appendChild(input);
michael@0 388 body.appendChild(form);
michael@0 389 assert(form.test == input, "form[nodeName] is valid");
michael@0 390 body.removeChild(form);
michael@0 391 done();
michael@0 392 }
michael@0 393 );
michael@0 394
michael@0 395 });
michael@0 396
michael@0 397 exports["test localStorage"] = createProxyTest("", function (helper, assert) {
michael@0 398
michael@0 399 let worker = helper.createWorker(
michael@0 400 'new ' + function ContentScriptScope() {
michael@0 401 // Check localStorage:
michael@0 402 assert(window.localStorage, "has access to localStorage");
michael@0 403 window.localStorage.name = 1;
michael@0 404 assert(window.localStorage.name == 1, "localStorage appears to work");
michael@0 405
michael@0 406 self.port.on("step2", function () {
michael@0 407 window.localStorage.clear();
michael@0 408 assert(window.localStorage.name == undefined, "localStorage really, really works");
michael@0 409 done();
michael@0 410 });
michael@0 411 self.port.emit("step1");
michael@0 412 }
michael@0 413 );
michael@0 414
michael@0 415 worker.port.on("step1", function () {
michael@0 416 assert.equal(helper.rawWindow.localStorage.name, 1, "localStorage really works");
michael@0 417 worker.port.emit("step2");
michael@0 418 });
michael@0 419
michael@0 420 });
michael@0 421
michael@0 422 exports["test Auto Unwrap Custom Attributes"] = createProxyTest("", function (helper) {
michael@0 423
michael@0 424 helper.createWorker(
michael@0 425 'new ' + function ContentScriptScope() {
michael@0 426 let body = document.body;
michael@0 427 // Setting a custom object to a proxy attribute is not wrapped when we get it afterward
michael@0 428 let object = {custom: true, enumerable: false};
michael@0 429 body.customAttribute = object;
michael@0 430 assert(object === body.customAttribute, "custom JS attributes are not wrapped");
michael@0 431 done();
michael@0 432 }
michael@0 433 );
michael@0 434
michael@0 435 });
michael@0 436
michael@0 437 exports["test Object Tag"] = createProxyTest("", function (helper) {
michael@0 438
michael@0 439 helper.createWorker(
michael@0 440 'new ' + function ContentScriptScope() {
michael@0 441 // <object>, <embed> and other tags return typeof 'function'
michael@0 442 let flash = document.createElement("object");
michael@0 443 assert(typeof flash == "function", "<object> is typeof 'function'");
michael@0 444 assert(flash.toString().match(/\[object HTMLObjectElement.*\]/), "<object> is HTMLObjectElement");
michael@0 445 assert("setAttribute" in flash, "<object> has a setAttribute method");
michael@0 446 done();
michael@0 447 }
michael@0 448 );
michael@0 449
michael@0 450 });
michael@0 451
michael@0 452 exports["test Highlight toString Behavior"] = createProxyTest("", function (helper, assert) {
michael@0 453 // We do not have any workaround this particular use of toString
michael@0 454 // applied on <object> elements. So disable this test until we found one!
michael@0 455 //assert.equal(helper.rawWindow.Object.prototype.toString.call(flash), "[object HTMLObjectElement]", "<object> is HTMLObjectElement");
michael@0 456 function f() {};
michael@0 457 let funToString = Object.prototype.toString.call(f);
michael@0 458 assert.ok(/\[object Function.*\]/.test(funToString), "functions are functions 1");
michael@0 459
michael@0 460 // This is how jquery call toString:
michael@0 461 let strToString = helper.rawWindow.Object.prototype.toString.call("");
michael@0 462 assert.ok(/\[object String.*\]/.test(strToString), "strings are strings");
michael@0 463
michael@0 464 let o = {__exposedProps__:{}};
michael@0 465 let objToString = helper.rawWindow.Object.prototype.toString.call(o);
michael@0 466 assert.ok(/\[object Object.*\]/.test(objToString), "objects are objects");
michael@0 467
michael@0 468 // Make sure to pass a function from the same compartments
michael@0 469 // or toString will return [object Object] on FF8+
michael@0 470 let f2 = helper.rawWindow.eval("(function () {})");
michael@0 471 let funToString2 = helper.rawWindow.Object.prototype.toString.call(f2);
michael@0 472 assert.ok(/\[object Function.*\]/.test(funToString2), "functions are functions 2");
michael@0 473
michael@0 474 helper.done();
michael@0 475 });
michael@0 476
michael@0 477 exports["test Document TagName"] = createProxyTest("", function (helper) {
michael@0 478
michael@0 479 helper.createWorker(
michael@0 480 'new ' + function ContentScriptScope() {
michael@0 481 let body = document.body;
michael@0 482 // Check document[tagName]
michael@0 483 let div = document.createElement("div");
michael@0 484 div.setAttribute("name", "test");
michael@0 485 body.appendChild(div);
michael@0 486 assert(!document.test, "document[divName] is undefined");
michael@0 487 body.removeChild(div);
michael@0 488
michael@0 489 let form = document.createElement("form");
michael@0 490 form.setAttribute("name", "test");
michael@0 491 body.appendChild(form);
michael@0 492 assert(document.test == form, "document[formName] is valid");
michael@0 493 body.removeChild(form);
michael@0 494
michael@0 495 let img = document.createElement("img");
michael@0 496 img.setAttribute("name", "test");
michael@0 497 body.appendChild(img);
michael@0 498 assert(document.test == img, "document[imgName] is valid");
michael@0 499 body.removeChild(img);
michael@0 500 done();
michael@0 501 }
michael@0 502 );
michael@0 503
michael@0 504 });
michael@0 505
michael@0 506 let html = '<iframe id="iframe" name="test" src="data:text/html;charset=utf-8," />';
michael@0 507 exports["test Window Frames"] = createProxyTest(html, function (helper) {
michael@0 508
michael@0 509 helper.createWorker(
michael@0 510 'let glob = this; new ' + function ContentScriptScope() {
michael@0 511 // Check window[frameName] and window.frames[i]
michael@0 512 let iframe = document.getElementById("iframe");
michael@0 513 //assert(window.frames.length == 1, "The iframe is reported in window.frames check1");
michael@0 514 //assert(window.frames[0] == iframe.contentWindow, "The iframe is reported in window.frames check2");
michael@0 515 assert(window.test == iframe.contentWindow, "window[frameName] is valid");
michael@0 516 done();
michael@0 517 }
michael@0 518 );
michael@0 519
michael@0 520 });
michael@0 521
michael@0 522 exports["test Collections"] = createProxyTest("", function (helper) {
michael@0 523
michael@0 524 helper.createWorker(
michael@0 525 'new ' + function ContentScriptScope() {
michael@0 526 // Highlight XPCNativeWrapper bug with HTMLCollection
michael@0 527 // tds[0] is only defined on first access :o
michael@0 528 let body = document.body;
michael@0 529 let div = document.createElement("div");
michael@0 530 body.appendChild(div);
michael@0 531 div.innerHTML = "<table><tr><td style='padding:0;border:0;display:none'></td><td>t</td></tr></table>";
michael@0 532 let tds = div.getElementsByTagName("td");
michael@0 533 assert(tds[0] == tds[0], "We can get array element multiple times");
michael@0 534 body.removeChild(div);
michael@0 535 done();
michael@0 536 }
michael@0 537 );
michael@0 538
michael@0 539 });
michael@0 540
michael@0 541 let html = '<input id="input" type="text" /><input id="input3" type="checkbox" />' +
michael@0 542 '<input id="input2" type="checkbox" />';
michael@0 543 exports["test Collections 2"] = createProxyTest(html, function (helper) {
michael@0 544
michael@0 545 helper.createWorker(
michael@0 546 'new ' + function ContentScriptScope() {
michael@0 547 // Verify that NodeList/HTMLCollection are working fine
michael@0 548 let body = document.body;
michael@0 549 let inputs = body.getElementsByTagName("input");
michael@0 550 assert(body.childNodes.length == 3, "body.childNodes length is correct");
michael@0 551 assert(inputs.length == 3, "inputs.length is correct");
michael@0 552 assert(body.childNodes[0] == inputs[0], "body.childNodes[0] is correct");
michael@0 553 assert(body.childNodes[1] == inputs[1], "body.childNodes[1] is correct");
michael@0 554 assert(body.childNodes[2] == inputs[2], "body.childNodes[2] is correct");
michael@0 555 let count = 0;
michael@0 556 for(let i in body.childNodes) {
michael@0 557 count++;
michael@0 558 }
michael@0 559 assert(count == 6, "body.childNodes is iterable");
michael@0 560 done();
michael@0 561 }
michael@0 562 );
michael@0 563
michael@0 564 });
michael@0 565
michael@0 566 exports["test valueOf"] = createProxyTest("", function (helper) {
michael@0 567
michael@0 568 helper.createWorker(
michael@0 569 'new ' + function ContentScriptScope() {
michael@0 570 // Bug 787013: Until this bug is fixed, we are missing some methods
michael@0 571 // on JS objects that comes from global `Object` object
michael@0 572 assert(!('valueOf' in window), "valueOf is missing");
michael@0 573 assert(!('toLocateString' in window), "toLocaleString is missing");
michael@0 574 done();
michael@0 575 }
michael@0 576 );
michael@0 577
michael@0 578 });
michael@0 579
michael@0 580 exports["test XMLHttpRequest"] = createProxyTest("", function (helper) {
michael@0 581
michael@0 582 helper.createWorker(
michael@0 583 'new ' + function ContentScriptScope() {
michael@0 584 // XMLHttpRequest doesn't support XMLHttpRequest.apply,
michael@0 585 // that may break our proxy code
michael@0 586 assert(new window.XMLHttpRequest(), "we are able to instantiate XMLHttpRequest object");
michael@0 587 done();
michael@0 588 }
michael@0 589 );
michael@0 590
michael@0 591 });
michael@0 592
michael@0 593 exports["test XPathResult"] = createProxyTest("", function (helper, assert) {
michael@0 594 const XPathResultTypes = ["ANY_TYPE",
michael@0 595 "NUMBER_TYPE", "STRING_TYPE", "BOOLEAN_TYPE",
michael@0 596 "UNORDERED_NODE_ITERATOR_TYPE",
michael@0 597 "ORDERED_NODE_ITERATOR_TYPE",
michael@0 598 "UNORDERED_NODE_SNAPSHOT_TYPE",
michael@0 599 "ORDERED_NODE_SNAPSHOT_TYPE",
michael@0 600 "ANY_UNORDERED_NODE_TYPE",
michael@0 601 "FIRST_ORDERED_NODE_TYPE"];
michael@0 602
michael@0 603 // Check XPathResult bug with constants being undefined on XPCNativeWrapper
michael@0 604 let xpcXPathResult = helper.xrayWindow.XPathResult;
michael@0 605
michael@0 606 XPathResultTypes.forEach(function(type, i) {
michael@0 607 assert.equal(xpcXPathResult.wrappedJSObject[type],
michael@0 608 helper.rawWindow.XPathResult[type],
michael@0 609 "XPathResult's constants are valid on unwrapped node");
michael@0 610
michael@0 611 assert.equal(xpcXPathResult[type], i,
michael@0 612 "XPathResult's constants are defined on " +
michael@0 613 "XPCNativeWrapper (platform bug #)");
michael@0 614 });
michael@0 615
michael@0 616 let value = helper.rawWindow.XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE;
michael@0 617 let worker = helper.createWorker(
michael@0 618 'new ' + function ContentScriptScope() {
michael@0 619 self.port.on("value", function (value) {
michael@0 620 // Check that our work around is working:
michael@0 621 assert(window.XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE === value,
michael@0 622 "XPathResult works correctly on Proxies");
michael@0 623 done();
michael@0 624 });
michael@0 625 }
michael@0 626 );
michael@0 627 worker.port.emit("value", value);
michael@0 628 });
michael@0 629
michael@0 630 exports["test Prototype Inheritance"] = createProxyTest("", function (helper) {
michael@0 631
michael@0 632 helper.createWorker(
michael@0 633 'new ' + function ContentScriptScope() {
michael@0 634 // Verify that inherited prototype function like initEvent
michael@0 635 // are handled correctly. (e2.type will return an error if it's not the case)
michael@0 636 let event1 = document.createEvent( 'MouseEvents' );
michael@0 637 event1.initEvent( "click", true, true );
michael@0 638 let event2 = document.createEvent( 'MouseEvents' );
michael@0 639 event2.initEvent( "click", true, true );
michael@0 640 assert(event2.type == "click", "We are able to create an event");
michael@0 641 done();
michael@0 642 }
michael@0 643 );
michael@0 644
michael@0 645 });
michael@0 646
michael@0 647 exports["test Functions"] = createProxyTest("", function (helper) {
michael@0 648
michael@0 649 helper.rawWindow.callFunction = function callFunction(f) f();
michael@0 650 helper.rawWindow.isEqual = function isEqual(a, b) a == b;
michael@0 651 // bug 784116: workaround in order to allow proxy code to cache proxies on
michael@0 652 // these functions:
michael@0 653 helper.rawWindow.callFunction.__exposedProps__ = {__proxy: 'rw'};
michael@0 654 helper.rawWindow.isEqual.__exposedProps__ = {__proxy: 'rw'};
michael@0 655
michael@0 656 helper.createWorker(
michael@0 657 'new ' + function ContentScriptScope() {
michael@0 658 // Check basic usage of functions
michael@0 659 let closure2 = function () {return "ok";};
michael@0 660 assert(window.wrappedJSObject.callFunction(closure2) == "ok", "Function references work");
michael@0 661
michael@0 662 // Ensure that functions are cached when being wrapped to native code
michael@0 663 let closure = function () {};
michael@0 664 assert(window.wrappedJSObject.isEqual(closure, closure), "Function references are cached before being wrapped to native");
michael@0 665 done();
michael@0 666 }
michael@0 667 );
michael@0 668
michael@0 669 });
michael@0 670
michael@0 671 let html = '<input id="input2" type="checkbox" />';
michael@0 672 exports["test Listeners"] = createProxyTest(html, function (helper) {
michael@0 673
michael@0 674 helper.createWorker(
michael@0 675 'new ' + function ContentScriptScope() {
michael@0 676 // Verify listeners:
michael@0 677 let input = document.getElementById("input2");
michael@0 678 assert(input, "proxy.getElementById works");
michael@0 679
michael@0 680 function onclick() {};
michael@0 681 input.onclick = onclick;
michael@0 682 assert(input.onclick === onclick, "on* attributes are equal to original function set");
michael@0 683
michael@0 684 let addEventListenerCalled = false;
michael@0 685 let expandoCalled = false;
michael@0 686 input.addEventListener("click", function onclick(event) {
michael@0 687 input.removeEventListener("click", onclick, true);
michael@0 688
michael@0 689 assert(!addEventListenerCalled, "closure given to addEventListener is called once");
michael@0 690 if (addEventListenerCalled)
michael@0 691 return;
michael@0 692 addEventListenerCalled = true;
michael@0 693
michael@0 694 assert(!event.target.ownerDocument.defaultView.documentGlobal, "event object is still wrapped and doesn't expose document globals");
michael@0 695
michael@0 696 let input2 = document.getElementById("input2");
michael@0 697
michael@0 698 input.onclick = function (event) {
michael@0 699 input.onclick = null;
michael@0 700 assert(!expandoCalled, "closure set to expando is called once");
michael@0 701 if (expandoCalled) return;
michael@0 702 expandoCalled = true;
michael@0 703
michael@0 704 assert(!event.target.ownerDocument.defaultView.documentGlobal, "event object is still wrapped and doesn't expose document globals");
michael@0 705
michael@0 706 setTimeout(function () {
michael@0 707 input.click();
michael@0 708 done();
michael@0 709 }, 0);
michael@0 710
michael@0 711 }
michael@0 712
michael@0 713 setTimeout(function () {
michael@0 714 input.click();
michael@0 715 }, 0);
michael@0 716
michael@0 717 }, true);
michael@0 718
michael@0 719 input.click();
michael@0 720 }
michael@0 721 );
michael@0 722
michael@0 723 });
michael@0 724
michael@0 725 exports["test MozRequestAnimationFrame"] = createProxyTest("", function (helper) {
michael@0 726
michael@0 727 helper.createWorker(
michael@0 728 'new ' + function ContentScriptScope() {
michael@0 729 window.mozRequestAnimationFrame(function callback() {
michael@0 730 assert(callback == this, "callback is equal to `this`");
michael@0 731 done();
michael@0 732 });
michael@0 733 }
michael@0 734 );
michael@0 735
michael@0 736 });
michael@0 737
michael@0 738 exports["testGlobalScope"] = createProxyTest("", function (helper) {
michael@0 739
michael@0 740 helper.createWorker(
michael@0 741 'let toplevelScope = true;' +
michael@0 742 'assert(window.toplevelScope, "variables in toplevel scope are set to `window` object");' +
michael@0 743 'assert(this.toplevelScope, "variables in toplevel scope are set to `this` object");' +
michael@0 744 'done();'
michael@0 745 );
michael@0 746
michael@0 747 });
michael@0 748
michael@0 749 // Bug 715755: proxy code throw an exception on COW
michael@0 750 // Create an http server in order to simulate real cross domain documents
michael@0 751 exports["test Cross Domain Iframe"] = createProxyTest("", function (helper) {
michael@0 752 let serverPort = 8099;
michael@0 753 let server = require("sdk/test/httpd").startServerAsync(serverPort);
michael@0 754 server.registerPathHandler("/", function handle(request, response) {
michael@0 755 // Returns the webpage that receive a message and forward it back to its
michael@0 756 // parent document by appending ' world'.
michael@0 757 let content = "<html><head><meta charset='utf-8'></head>\n";
michael@0 758 content += "<script>\n";
michael@0 759 content += " window.addEventListener('message', function (event) {\n";
michael@0 760 content += " parent.postMessage(event.data + ' world', '*');\n";
michael@0 761 content += " }, true);\n";
michael@0 762 content += "</script>\n";
michael@0 763 content += "<body></body>\n";
michael@0 764 content += "</html>\n";
michael@0 765 response.write(content);
michael@0 766 });
michael@0 767
michael@0 768 let worker = helper.createWorker(
michael@0 769 'new ' + function ContentScriptScope() {
michael@0 770 // Waits for the server page url
michael@0 771 self.on("message", function (url) {
michael@0 772 // Creates an iframe with this page
michael@0 773 let iframe = document.createElement("iframe");
michael@0 774 iframe.addEventListener("load", function onload() {
michael@0 775 iframe.removeEventListener("load", onload, true);
michael@0 776 try {
michael@0 777 // Try to communicate with iframe's content
michael@0 778 window.addEventListener("message", function onmessage(event) {
michael@0 779 window.removeEventListener("message", onmessage, true);
michael@0 780
michael@0 781 assert(event.data == "hello world", "COW works properly");
michael@0 782 self.port.emit("end");
michael@0 783 }, true);
michael@0 784 iframe.contentWindow.postMessage("hello", "*");
michael@0 785 } catch(e) {
michael@0 786 assert(false, "COW fails : "+e.message);
michael@0 787 }
michael@0 788 }, true);
michael@0 789 iframe.setAttribute("src", url);
michael@0 790 document.body.appendChild(iframe);
michael@0 791 });
michael@0 792 }
michael@0 793 );
michael@0 794
michael@0 795 worker.port.on("end", function () {
michael@0 796 server.stop(helper.done);
michael@0 797 });
michael@0 798
michael@0 799 worker.postMessage("http://localhost:" + serverPort + "/");
michael@0 800
michael@0 801 });
michael@0 802
michael@0 803 // Bug 769006: Ensure that MutationObserver works fine with proxies
michael@0 804 let html = '<a href="foo">link</a>';
michael@0 805 exports["test MutationObvserver"] = createProxyTest(html, function (helper) {
michael@0 806
michael@0 807 helper.createWorker(
michael@0 808 'new ' + function ContentScriptScope() {
michael@0 809 if (typeof MutationObserver == "undefined") {
michael@0 810 assert(true, "No MutationObserver for this FF version");
michael@0 811 done();
michael@0 812 return;
michael@0 813 }
michael@0 814 let link = document.getElementsByTagName("a")[0];
michael@0 815
michael@0 816 // Register a Mutation observer
michael@0 817 let obs = new MutationObserver(function(mutations){
michael@0 818 // Ensure that mutation data are valid
michael@0 819 assert(mutations.length == 1, "only one attribute mutation");
michael@0 820 let mutation = mutations[0];
michael@0 821 assert(mutation.type == "attributes", "check `type`");
michael@0 822 assert(mutation.target == link, "check `target`");
michael@0 823 assert(mutation.attributeName == "href", "check `attributeName`");
michael@0 824 assert(mutation.oldValue == "foo", "check `oldValue`");
michael@0 825 obs.disconnect();
michael@0 826 done();
michael@0 827 });
michael@0 828 obs.observe(document, {
michael@0 829 subtree: true,
michael@0 830 attributes: true,
michael@0 831 attributeOldValue: true,
michael@0 832 attributeFilter: ["href"]
michael@0 833 });
michael@0 834
michael@0 835 // Modify the DOM
michael@0 836 link.setAttribute("href", "bar");
michael@0 837 }
michael@0 838 );
michael@0 839
michael@0 840 });
michael@0 841
michael@0 842 let html = '<script>' +
michael@0 843 'var accessCheck = function() {' +
michael@0 844 ' assert(true, "exporting function works");' +
michael@0 845 ' try{' +
michael@0 846 ' exportedObj.prop;' +
michael@0 847 ' assert(false, "content should not have access to content-script");' +
michael@0 848 ' } catch(e) {' +
michael@0 849 ' assert(e.toString().indexOf("Permission denied") != -1,' +
michael@0 850 ' "content should not have access to content-script");' +
michael@0 851 ' }' +
michael@0 852 '}</script>';
michael@0 853 exports["test nsEp for content-script"] = createProxyTest(html, function (helper) {
michael@0 854
michael@0 855 helper.createWorker(
michael@0 856 'let glob = this; new ' + function ContentScriptScope() {
michael@0 857
michael@0 858 exportFunction(assert, unsafeWindow, { defineAs: "assert" });
michael@0 859 window.wrappedJSObject.assert(true, "assert exported");
michael@0 860 window.wrappedJSObject.exportedObj = { prop: 42 };
michael@0 861 window.wrappedJSObject.accessCheck();
michael@0 862 done();
michael@0 863 }
michael@0 864 );
michael@0 865
michael@0 866 });
michael@0 867
michael@0 868 require("test").run(exports);

mercurial