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

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial