diff -r 000000000000 -r 6474c204b198 addon-sdk/source/test/test-content-script.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/addon-sdk/source/test/test-content-script.js Wed Dec 31 06:09:35 2014 +0100
@@ -0,0 +1,868 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const hiddenFrames = require("sdk/frame/hidden-frame");
+const { create: makeFrame } = require("sdk/frame/utils");
+const { window } = require("sdk/addon/window");
+const { Loader } = require('sdk/test/loader');
+const { URL } = require("sdk/url");
+const testURI = require("./fixtures").url("test.html");
+const testHost = URL(testURI).scheme + '://' + URL(testURI).host;
+
+/*
+ * Utility function that allow to easily run a proxy test with a clean
+ * new HTML document. See first unit test for usage.
+ */
+function createProxyTest(html, callback) {
+ return function (assert, done) {
+ let url = 'data:text/html;charset=utf-8,' + encodeURIComponent(html);
+ let principalLoaded = false;
+
+ let element = makeFrame(window.document, {
+ nodeName: "iframe",
+ type: "content",
+ allowJavascript: true,
+ allowPlugins: true,
+ allowAuth: true,
+ uri: testURI
+ });
+
+ element.addEventListener("DOMContentLoaded", onDOMReady, false);
+
+ function onDOMReady() {
+ // Reload frame after getting principal from `testURI`
+ if (!principalLoaded) {
+ element.setAttribute("src", url);
+ principalLoaded = true;
+ return;
+ }
+
+ assert.equal(element.getAttribute("src"), url, "correct URL loaded");
+ element.removeEventListener("DOMContentLoaded", onDOMReady,
+ false);
+ let xrayWindow = element.contentWindow;
+ let rawWindow = xrayWindow.wrappedJSObject;
+
+ let isDone = false;
+ let helper = {
+ xrayWindow: xrayWindow,
+ rawWindow: rawWindow,
+ createWorker: function (contentScript) {
+ return createWorker(assert, xrayWindow, contentScript, helper.done);
+ },
+ done: function () {
+ if (isDone)
+ return;
+ isDone = true;
+ element.parentNode.removeChild(element);
+ done();
+ }
+ };
+ callback(helper, assert);
+ }
+ };
+}
+
+function createWorker(assert, xrayWindow, contentScript, done) {
+ let loader = Loader(module);
+ let Worker = loader.require("sdk/content/worker").Worker;
+ let worker = Worker({
+ window: xrayWindow,
+ contentScript: [
+ 'new ' + function () {
+ assert = function assert(v, msg) {
+ self.port.emit("assert", {assertion:v, msg:msg});
+ }
+ done = function done() {
+ self.port.emit("done");
+ }
+ },
+ contentScript
+ ]
+ });
+
+ worker.port.on("done", done);
+ worker.port.on("assert", function (data) {
+ assert.ok(data.assertion, data.msg);
+ });
+
+ return worker;
+}
+
+/* Examples for the `createProxyTest` uses */
+
+let html = "";
+
+exports["test Create Proxy Test"] = createProxyTest(html, function (helper, assert) {
+ // You can get access to regular `test` object in second argument of
+ // `createProxyTest` method:
+ assert.ok(helper.rawWindow.documentGlobal,
+ "You have access to a raw window reference via `helper.rawWindow`");
+ assert.ok(!("documentGlobal" in helper.xrayWindow),
+ "You have access to an XrayWrapper reference via `helper.xrayWindow`");
+
+ // If you do not create a Worker, you have to call helper.done(),
+ // in order to say when your test is finished
+ helper.done();
+});
+
+exports["test Create Proxy Test With Worker"] = createProxyTest("", function (helper) {
+
+ helper.createWorker(
+ "new " + function WorkerScope() {
+ assert(true, "You can do assertions in your content script");
+ // And if you create a worker, you either have to call `done`
+ // from content script or helper.done()
+ done();
+ }
+ );
+
+});
+
+exports["test Create Proxy Test With Events"] = createProxyTest("", function (helper, assert) {
+
+ let worker = helper.createWorker(
+ "new " + function WorkerScope() {
+ self.port.emit("foo");
+ }
+ );
+
+ worker.port.on("foo", function () {
+ assert.pass("You can use events");
+ // And terminate your test with helper.done:
+ helper.done();
+ });
+
+});
+
+// Bug 714778: There was some issue around `toString` functions
+// that ended up being shared between content scripts
+exports["test Shared To String Proxies"] = createProxyTest("", function(helper) {
+
+ let worker = helper.createWorker(
+ 'new ' + function ContentScriptScope() {
+ // We ensure that `toString` can't be modified so that nothing could
+ // leak to/from the document and between content scripts
+ // It only applies to JS proxies, there isn't any such issue with xrays.
+ //document.location.toString = function foo() {};
+ document.location.toString.foo = "bar";
+ assert("foo" in document.location.toString, "document.location.toString can be modified");
+ assert(document.location.toString() == "data:text/html;charset=utf-8,",
+ "First document.location.toString()");
+ self.postMessage("next");
+ }
+ );
+ worker.on("message", function () {
+ helper.createWorker(
+ 'new ' + function ContentScriptScope2() {
+ assert(!("foo" in document.location.toString),
+ "document.location.toString is different for each content script");
+ assert(document.location.toString() == "data:text/html;charset=utf-8,",
+ "Second document.location.toString()");
+ done();
+ }
+ );
+ });
+});
+
+
+// Ensure that postMessage is working correctly across documents with an iframe
+let html = '';
+exports["test postMessage"] = createProxyTest(html, function (helper, assert) {
+ let ifWindow = helper.xrayWindow.document.getElementById("iframe").contentWindow;
+ // Listen without proxies, to check that it will work in regular case
+ // simulate listening from a web document.
+ ifWindow.addEventListener("message", function listener(event) {
+ ifWindow.removeEventListener("message", listener, false);
+ // As we are in system principal, event is an XrayWrapper
+ // xrays use current compartments when calling postMessage method.
+ // Whereas js proxies was using postMessage method compartment,
+ // not the caller one.
+ assert.strictEqual(event.source, helper.xrayWindow,
+ "event.source is the top window");
+ assert.equal(event.origin, testHost, "origin matches testHost");
+
+ assert.equal(event.data, "{\"foo\":\"bar\\n \\\"escaped\\\".\"}",
+ "message data is correct");
+
+ helper.done();
+ }, false);
+
+ helper.createWorker(
+ 'new ' + function ContentScriptScope() {
+ var json = JSON.stringify({foo : "bar\n \"escaped\"."});
+
+ document.getElementById("iframe").contentWindow.postMessage(json, "*");
+ }
+ );
+});
+
+let html = '';
+exports["test Object Listener"] = createProxyTest(html, function (helper) {
+
+ helper.createWorker(
+ 'new ' + function ContentScriptScope() {
+ // Test objects being given as event listener
+ let input = document.getElementById("input2");
+ let myClickListener = {
+ called: false,
+ handleEvent: function(event) {
+ assert(this === myClickListener, "`this` is the original object");
+ assert(!this.called, "called only once");
+ this.called = true;
+ assert(event.target, input, "event.target is the wrapped window");
+ done();
+ }
+ };
+
+ window.addEventListener("click", myClickListener, true);
+ input.click();
+ window.removeEventListener("click", myClickListener, true);
+ }
+ );
+
+});
+
+exports["test Object Listener 2"] = createProxyTest("", function (helper) {
+
+ helper.createWorker(
+ ('new ' + function ContentScriptScope() {
+ // variable replaced with `testHost`
+ let testHost = "TOKEN";
+ // Verify object as DOM event listener
+ let myMessageListener = {
+ called: false,
+ handleEvent: function(event) {
+ window.removeEventListener("message", myMessageListener, true);
+
+ assert(this == myMessageListener, "`this` is the original object");
+ assert(!this.called, "called only once");
+ this.called = true;
+ assert(event.target == document.defaultView, "event.target is the wrapped window");
+ assert(event.source == document.defaultView, "event.source is the wrapped window");
+ assert(event.origin == testHost, "origin matches testHost");
+ assert(event.data == "ok", "message data is correct");
+ done();
+ }
+ };
+
+ window.addEventListener("message", myMessageListener, true);
+ document.defaultView.postMessage("ok", '*');
+ }
+ ).replace("TOKEN", testHost));
+
+});
+
+let html = '' +
+ '';
+
+exports.testStringOverload = createProxyTest(html, function (helper, assert) {
+ // Proxy - toString error
+ let originalString = "string";
+ let p = Proxy.create({
+ get: function(receiver, name) {
+ if (name == "binded")
+ return originalString.toString.bind(originalString);
+ return originalString[name];
+ }
+ });
+ assert.throws(function () {
+ p.toString();
+ },
+ /toString method called on incompatible Proxy/,
+ "toString can't be called with this being the proxy");
+ assert.equal(p.binded(), "string", "but it works if we bind this to the original string");
+
+ helper.createWorker(
+ 'new ' + function ContentScriptScope() {
+ // RightJS is hacking around String.prototype, and do similar thing:
+ // Pass `this` from a String prototype method.
+ // It is funny because typeof this == "object"!
+ // So that when we pass `this` to a native method,
+ // our proxy code can fail on another even more crazy thing.
+ // See following test to see what fails around proxies.
+ String.prototype.update = function () {
+ assert(typeof this == "object", "in update, `this` is an object");
+ assert(this.toString() == "input", "in update, `this.toString works");
+ return document.querySelectorAll(this);
+ };
+ assert("input".update().length == 3, "String.prototype overload works");
+ done();
+ }
+ );
+});
+
+exports["test MozMatchedSelector"] = createProxyTest("", function (helper) {
+ helper.createWorker(
+ 'new ' + function ContentScriptScope() {
+ // Check mozMatchesSelector XrayWrappers bug:
+ // mozMatchesSelector returns bad results when we are not calling it from the node itself
+ // SEE BUG 658909: mozMatchesSelector returns incorrect results with XrayWrappers
+ assert(document.createElement( "div" ).mozMatchesSelector("div"),
+ "mozMatchesSelector works while being called from the node");
+ assert(document.documentElement.mozMatchesSelector.call(
+ document.createElement( "div" ),
+ "div"
+ ),
+ "mozMatchesSelector works while being called from a " +
+ "function reference to " +
+ "document.documentElement.mozMatchesSelector.call");
+ done();
+ }
+ );
+});
+
+exports["test Events Overload"] = createProxyTest("", function (helper) {
+
+ helper.createWorker(
+ 'new ' + function ContentScriptScope() {
+ // If we add a "____proxy" attribute on XrayWrappers in order to store
+ // the related proxy to create an unique proxy for each wrapper;
+ // we end up setting this attribute to prototype objects :x
+ // And so, instances created with such prototype will be considered
+ // as equal to the prototype ...
+ // // Internal method that return the proxy for a given XrayWrapper
+ // function proxify(obj) {
+ // if (obj._proxy) return obj._proxy;
+ // return obj._proxy = Proxy.create(...);
+ // }
+ //
+ // // Get a proxy of an XrayWrapper prototype object
+ // let proto = proxify(xpcProto);
+ //
+ // // Use this proxy as a prototype
+ // function Constr() {}
+ // Constr.proto = proto;
+ //
+ // // Try to create an instance using this prototype
+ // let xpcInstance = new Constr();
+ // let wrapper = proxify(xpcInstance)
+ //
+ // xpcProto._proxy = proto and as xpcInstance.__proto__ = xpcProto,
+ // xpcInstance._proxy = proto ... and profixy(xpcInstance) = proto :(
+ //
+ let proto = window.document.createEvent('HTMLEvents').__proto__;
+ window.Event.prototype = proto;
+ let event = document.createEvent('HTMLEvents');
+ assert(event !== proto, "Event should not be equal to its prototype");
+ event.initEvent('dataavailable', true, true);
+ assert(event.type === 'dataavailable', "Events are working fine");
+ done();
+ }
+ );
+
+});
+
+exports["test Nested Attributes"] = createProxyTest("", function (helper) {
+
+ helper.createWorker(
+ 'new ' + function ContentScriptScope() {
+ // XrayWrappers has a bug when you set an attribute on it,
+ // in some cases, it creates an unnecessary wrapper that introduces
+ // a different object that refers to the same original object
+ // Check that our wrappers don't reproduce this bug
+ // SEE BUG 658560: Fix identity problem with CrossOriginWrappers
+ let o = {sandboxObject:true};
+ window.nested = o;
+ o.foo = true;
+ assert(o === window.nested, "Nested attribute to sandbox object should not be proxified");
+ window.nested = document;
+ assert(window.nested === document, "Nested attribute to proxy should not be double proxified");
+ done();
+ }
+ );
+
+});
+
+exports["test Form nodeName"] = createProxyTest("", function (helper) {
+
+ helper.createWorker(
+ 'new ' + function ContentScriptScope() {
+ let body = document.body;
+ // Check form[nodeName]
+ let form = document.createElement("form");
+ let input = document.createElement("input");
+ input.setAttribute("name", "test");
+ form.appendChild(input);
+ body.appendChild(form);
+ assert(form.test == input, "form[nodeName] is valid");
+ body.removeChild(form);
+ done();
+ }
+ );
+
+});
+
+exports["test localStorage"] = createProxyTest("", function (helper, assert) {
+
+ let worker = helper.createWorker(
+ 'new ' + function ContentScriptScope() {
+ // Check localStorage:
+ assert(window.localStorage, "has access to localStorage");
+ window.localStorage.name = 1;
+ assert(window.localStorage.name == 1, "localStorage appears to work");
+
+ self.port.on("step2", function () {
+ window.localStorage.clear();
+ assert(window.localStorage.name == undefined, "localStorage really, really works");
+ done();
+ });
+ self.port.emit("step1");
+ }
+ );
+
+ worker.port.on("step1", function () {
+ assert.equal(helper.rawWindow.localStorage.name, 1, "localStorage really works");
+ worker.port.emit("step2");
+ });
+
+});
+
+exports["test Auto Unwrap Custom Attributes"] = createProxyTest("", function (helper) {
+
+ helper.createWorker(
+ 'new ' + function ContentScriptScope() {
+ let body = document.body;
+ // Setting a custom object to a proxy attribute is not wrapped when we get it afterward
+ let object = {custom: true, enumerable: false};
+ body.customAttribute = object;
+ assert(object === body.customAttribute, "custom JS attributes are not wrapped");
+ done();
+ }
+ );
+
+});
+
+exports["test Object Tag"] = createProxyTest("", function (helper) {
+
+ helper.createWorker(
+ 'new ' + function ContentScriptScope() {
+ //