summaryrefslogtreecommitdiffstats
path: root/addon-sdk/source/test/addons/e10s-content/lib/test-page-worker.js
diff options
context:
space:
mode:
Diffstat (limited to 'addon-sdk/source/test/addons/e10s-content/lib/test-page-worker.js')
-rw-r--r--addon-sdk/source/test/addons/e10s-content/lib/test-page-worker.js524
1 files changed, 524 insertions, 0 deletions
diff --git a/addon-sdk/source/test/addons/e10s-content/lib/test-page-worker.js b/addon-sdk/source/test/addons/e10s-content/lib/test-page-worker.js
new file mode 100644
index 000000000..3a9106ec0
--- /dev/null
+++ b/addon-sdk/source/test/addons/e10s-content/lib/test-page-worker.js
@@ -0,0 +1,524 @@
+/* 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/. */
+"use strict";
+
+const { Loader } = require('sdk/test/loader');
+const { Page } = require("sdk/page-worker");
+const { URL } = require("sdk/url");
+const fixtures = require("./fixtures");
+const testURI = fixtures.url("test.html");
+
+const ERR_DESTROYED =
+ "Couldn't find the worker to receive this message. " +
+ "The script may not be initialized yet, or may already have been unloaded.";
+
+const Isolate = fn => "(" + fn + ")()";
+
+exports.testSimplePageCreation = function(assert, done) {
+ let page = new Page({
+ contentScript: "self.postMessage(window.location.href)",
+ contentScriptWhen: "end",
+ onMessage: function (message) {
+ assert.equal(message, "about:blank",
+ "Page Worker should start with a blank page by default");
+ assert.equal(this, page, "The 'this' object is the page itself.");
+ done();
+ }
+ });
+}
+
+/*
+ * Tests that we can't be tricked by document overloads as we have access
+ * to wrapped nodes
+ */
+exports.testWrappedDOM = function(assert, done) {
+ let page = Page({
+ allow: { script: true },
+ contentURL: "data:text/html;charset=utf-8,<script>document.getElementById=3;window.scrollTo=3;</script>",
+ contentScript: 'new ' + function() {
+ function send() {
+ self.postMessage([typeof(document.getElementById), typeof(window.scrollTo)]);
+ }
+ if (document.readyState !== 'complete')
+ window.addEventListener('load', send, true)
+ else
+ send();
+ },
+ onMessage: function (message) {
+ assert.equal(message[0],
+ "function",
+ "getElementById from content script is the native one");
+
+ assert.equal(message[1],
+ "function",
+ "scrollTo from content script is the native one");
+
+ done();
+ }
+ });
+}
+
+/*
+// We do not offer unwrapped access to DOM since bug 601295 landed
+// See 660780 to track progress of unwrap feature
+exports.testUnwrappedDOM = function(assert, done) {
+ let page = Page({
+ allow: { script: true },
+ contentURL: "data:text/html;charset=utf-8,<script>document.getElementById=3;window.scrollTo=3;</script>",
+ contentScript: "window.addEventListener('load', function () { " +
+ "return self.postMessage([typeof(unsafeWindow.document.getElementById), " +
+ "typeof(unsafeWindow.scrollTo)]); }, true)",
+ onMessage: function (message) {
+ assert.equal(message[0],
+ "number",
+ "document inside page is free to be changed");
+
+ assert.equal(message[1],
+ "number",
+ "window inside page is free to be changed");
+
+ done();
+ }
+ });
+}
+*/
+
+exports.testPageProperties = function(assert) {
+ let page = new Page();
+
+ for (let prop of ['contentURL', 'allow', 'contentScriptFile',
+ 'contentScript', 'contentScriptWhen', 'on',
+ 'postMessage', 'removeListener']) {
+ assert.ok(prop in page, prop + " property is defined on page.");
+ }
+
+ assert.ok(() => page.postMessage("foo") || true,
+ "postMessage doesn't throw exception on page.");
+}
+
+exports.testConstructorAndDestructor = function(assert, done) {
+ let loader = Loader(module);
+ let { Page } = loader.require("sdk/page-worker");
+ let global = loader.sandbox("sdk/page-worker");
+
+ let pagesReady = 0;
+
+ let page1 = Page({
+ contentScript: "self.postMessage('')",
+ contentScriptWhen: "end",
+ onMessage: pageReady
+ });
+ let page2 = Page({
+ contentScript: "self.postMessage('')",
+ contentScriptWhen: "end",
+ onMessage: pageReady
+ });
+
+ assert.notEqual(page1, page2,
+ "Page 1 and page 2 should be different objects.");
+
+ function pageReady() {
+ if (++pagesReady == 2) {
+ page1.destroy();
+ page2.destroy();
+
+ assert.ok(isDestroyed(page1), "page1 correctly unloaded.");
+ assert.ok(isDestroyed(page2), "page2 correctly unloaded.");
+
+ loader.unload();
+ done();
+ }
+ }
+}
+
+exports.testAutoDestructor = function(assert, done) {
+ let loader = Loader(module);
+ let { Page } = loader.require("sdk/page-worker");
+
+ let page = Page({
+ contentScript: "self.postMessage('')",
+ contentScriptWhen: "end",
+ onMessage: function() {
+ loader.unload();
+ assert.ok(isDestroyed(page), "Page correctly unloaded.");
+ done();
+ }
+ });
+}
+
+exports.testValidateOptions = function(assert) {
+ assert.throws(
+ () => Page({ contentURL: 'home' }),
+ /The `contentURL` option must be a valid URL\./,
+ "Validation correctly denied a non-URL contentURL"
+ );
+
+ assert.throws(
+ () => Page({ onMessage: "This is not a function."}),
+ /The option "onMessage" must be one of the following types: function/,
+ "Validation correctly denied a non-function onMessage."
+ );
+
+ assert.pass("Options validation is working.");
+}
+
+exports.testContentAndAllowGettersAndSetters = function(assert, done) {
+ let content = "data:text/html;charset=utf-8,<script>window.localStorage.allowScript=3;</script>";
+
+ // Load up the page with testURI initially for the resource:// principal,
+ // then load the actual data:* content, as data:* URIs no longer
+ // have localStorage
+ let page = Page({
+ contentURL: testURI,
+ contentScript: "if (window.location.href==='"+testURI+"')" +
+ " self.postMessage('reload');" +
+ "else " +
+ " self.postMessage(window.localStorage.allowScript)",
+ contentScriptWhen: "end",
+ onMessage: step0
+ });
+
+ function step0(message) {
+ if (message === 'reload')
+ return page.contentURL = content;
+ assert.equal(message, "3",
+ "Correct value expected for allowScript - 3");
+ assert.equal(page.contentURL, content,
+ "Correct content expected");
+ page.removeListener('message', step0);
+ page.on('message', step1);
+ page.allow = { script: false };
+ page.contentURL = content =
+ "data:text/html;charset=utf-8,<script>window.localStorage.allowScript='f'</script>";
+ }
+
+ function step1(message) {
+ assert.equal(message, "3",
+ "Correct value expected for allowScript - 3");
+ assert.equal(page.contentURL, content, "Correct content expected");
+ page.removeListener('message', step1);
+ page.on('message', step2);
+ page.allow = { script: true };
+ page.contentURL = content =
+ "data:text/html;charset=utf-8,<script>window.localStorage.allowScript='g'</script>";
+ }
+
+ function step2(message) {
+ assert.equal(message, "g",
+ "Correct value expected for allowScript - g");
+ assert.equal(page.contentURL, content, "Correct content expected");
+ page.removeListener('message', step2);
+ page.on('message', step3);
+ page.allow.script = false;
+ page.contentURL = content =
+ "data:text/html;charset=utf-8,<script>window.localStorage.allowScript=3</script>";
+ }
+
+ function step3(message) {
+ assert.equal(message, "g",
+ "Correct value expected for allowScript - g");
+ assert.equal(page.contentURL, content, "Correct content expected");
+ page.removeListener('message', step3);
+ page.on('message', step4);
+ page.allow.script = true;
+ page.contentURL = content =
+ "data:text/html;charset=utf-8,<script>window.localStorage.allowScript=4</script>";
+ }
+
+ function step4(message) {
+ assert.equal(message, "4",
+ "Correct value expected for allowScript - 4");
+ assert.equal(page.contentURL, content, "Correct content expected");
+ done();
+ }
+
+}
+
+exports.testOnMessageCallback = function(assert, done) {
+ Page({
+ contentScript: "self.postMessage('')",
+ contentScriptWhen: "end",
+ onMessage: function() {
+ assert.pass("onMessage callback called");
+ done();
+ }
+ });
+}
+
+exports.testMultipleOnMessageCallbacks = function(assert, done) {
+ let count = 0;
+ let page = Page({
+ contentScript: "self.postMessage('')",
+ contentScriptWhen: "end",
+ onMessage: () => count += 1
+ });
+ page.on('message', () => count += 2);
+ page.on('message', () => count *= 3);
+ page.on('message', () =>
+ assert.equal(count, 9, "All callbacks were called, in order."));
+ page.on('message', done);
+};
+
+exports.testLoadContentPage = function(assert, done) {
+ let page = Page({
+ onMessage: function(message) {
+ // The message is an array whose first item is the test method to call
+ // and the rest of whose items are arguments to pass it.
+ let msg = message.shift();
+ if (msg == "done")
+ return done();
+ assert[msg].apply(assert, message);
+ },
+ contentURL: fixtures.url("test-page-worker.html"),
+ contentScriptFile: fixtures.url("test-page-worker.js"),
+ contentScriptWhen: "ready"
+ });
+}
+
+exports.testLoadContentPageRelativePath = function(assert, done) {
+ let page = require("sdk/page-worker").Page({
+ onMessage: function(message) {
+ // The message is an array whose first item is the test method to call
+ // and the rest of whose items are arguments to pass it.
+ let msg = message.shift();
+ if (msg == "done")
+ return done();
+ assert[msg].apply(assert, message);
+ },
+ contentURL: "./test-page-worker.html",
+ contentScriptFile: "./test-page-worker.js",
+ contentScriptWhen: "ready"
+ });
+}
+
+exports.testAllowScriptDefault = function(assert, done) {
+ let page = Page({
+ onMessage: function(message) {
+ assert.ok(message, "Script is allowed to run by default.");
+ done();
+ },
+ contentURL: "data:text/html;charset=utf-8,<script>document.documentElement.setAttribute('foo', 3);</script>",
+ contentScript: "self.postMessage(document.documentElement.getAttribute('foo'))",
+ contentScriptWhen: "ready"
+ });
+}
+
+exports.testAllowScript = function(assert, done) {
+ let page = Page({
+ onMessage: function(message) {
+ assert.ok(message, "Script runs when allowed to do so.");
+ done();
+ },
+ allow: { script: true },
+ contentURL: "data:text/html;charset=utf-8,<script>document.documentElement.setAttribute('foo', 3);</script>",
+ contentScript: "self.postMessage(document.documentElement.hasAttribute('foo') && " +
+ " document.documentElement.getAttribute('foo') == 3)",
+ contentScriptWhen: "ready"
+ });
+}
+
+exports.testPingPong = function(assert, done) {
+ let page = Page({
+ contentURL: 'data:text/html;charset=utf-8,ping-pong',
+ contentScript: 'self.on("message", function(message) { return self.postMessage("pong"); });'
+ + 'self.postMessage("ready");',
+ onMessage: function(message) {
+ if ('ready' == message) {
+ page.postMessage('ping');
+ }
+ else {
+ assert.ok(message, 'pong', 'Callback from contentScript');
+ done();
+ }
+ }
+ });
+};
+
+exports.testRedirect = function (assert, done) {
+ let page = Page({
+ contentURL: 'data:text/html;charset=utf-8,first-page',
+ contentScriptWhen: "end",
+ contentScript: '' +
+ 'if (/first-page/.test(document.location.href)) ' +
+ ' document.location.href = "data:text/html;charset=utf-8,redirect";' +
+ 'else ' +
+ ' self.port.emit("redirect", document.location.href);'
+ });
+
+ page.port.on('redirect', function (url) {
+ assert.equal(url, 'data:text/html;charset=utf-8,redirect', 'Reinjects contentScript on reload');
+ done();
+ });
+};
+
+exports.testRedirectIncludeArrays = function (assert, done) {
+ let firstURL = 'data:text/html;charset=utf-8,first-page';
+ let page = Page({
+ contentURL: firstURL,
+ contentScript: '(function () {' +
+ 'self.port.emit("load", document.location.href);' +
+ ' self.port.on("redirect", function (url) {' +
+ ' document.location.href = url;' +
+ ' })' +
+ '})();',
+ include: ['about:blank', 'data:*']
+ });
+
+ page.port.on('load', function (url) {
+ if (url === firstURL) {
+ page.port.emit('redirect', 'about:blank');
+ } else if (url === 'about:blank') {
+ page.port.emit('redirect', 'about:mozilla');
+ assert.ok('`include` property handles arrays');
+ assert.equal(url, 'about:blank', 'Redirects work with accepted domains');
+ done();
+ } else if (url === 'about:mozilla') {
+ assert.fail('Should not redirect to restricted domain');
+ }
+ });
+};
+
+exports.testRedirectFromWorker = function (assert, done) {
+ let firstURL = 'data:text/html;charset=utf-8,first-page';
+ let secondURL = 'data:text/html;charset=utf-8,second-page';
+ let thirdURL = 'data:text/html;charset=utf-8,third-page';
+ let page = Page({
+ contentURL: firstURL,
+ contentScript: '(function () {' +
+ 'self.port.emit("load", document.location.href);' +
+ ' self.port.on("redirect", function (url) {' +
+ ' document.location.href = url;' +
+ ' })' +
+ '})();',
+ include: 'data:*'
+ });
+
+ page.port.on('load', function (url) {
+ if (url === firstURL) {
+ page.port.emit('redirect', secondURL);
+ } else if (url === secondURL) {
+ page.port.emit('redirect', thirdURL);
+ } else if (url === thirdURL) {
+ page.port.emit('redirect', 'about:mozilla');
+ assert.equal(url, thirdURL, 'Redirects work with accepted domains on include strings');
+ done();
+ } else {
+ assert.fail('Should not redirect to unauthorized domains');
+ }
+ });
+};
+
+exports.testRedirectWithContentURL = function (assert, done) {
+ let firstURL = 'data:text/html;charset=utf-8,first-page';
+ let secondURL = 'data:text/html;charset=utf-8,second-page';
+ let thirdURL = 'data:text/html;charset=utf-8,third-page';
+ let page = Page({
+ contentURL: firstURL,
+ contentScript: '(function () {' +
+ 'self.port.emit("load", document.location.href);' +
+ '})();',
+ include: 'data:*'
+ });
+
+ page.port.on('load', function (url) {
+ if (url === firstURL) {
+ page.contentURL = secondURL;
+ } else if (url === secondURL) {
+ page.contentURL = thirdURL;
+ } else if (url === thirdURL) {
+ page.contentURL = 'about:mozilla';
+ assert.equal(url, thirdURL, 'Redirects work with accepted domains on include strings');
+ done();
+ } else {
+ assert.fail('Should not redirect to unauthorized domains');
+ }
+ });
+};
+
+
+exports.testMultipleDestroys = function(assert) {
+ let page = Page();
+ page.destroy();
+ page.destroy();
+ assert.pass("Multiple destroys should not cause an error");
+};
+
+exports.testContentScriptOptionsOption = function(assert, done) {
+ let page = new Page({
+ contentScript: "self.postMessage( [typeof self.options.d, self.options] );",
+ contentScriptWhen: "end",
+ contentScriptOptions: {a: true, b: [1,2,3], c: "string", d: function(){ return 'test'}},
+ onMessage: function(msg) {
+ assert.equal(msg[0], 'undefined', 'functions are stripped from contentScriptOptions');
+ assert.equal(typeof msg[1], 'object', 'object as contentScriptOptions');
+ assert.equal(msg[1].a, true, 'boolean in contentScriptOptions');
+ assert.equal(msg[1].b.join(), '1,2,3', 'array and numbers in contentScriptOptions');
+ assert.equal(msg[1].c, 'string', 'string in contentScriptOptions');
+ done();
+ }
+ });
+};
+
+exports.testMessageQueue = function (assert, done) {
+ let page = new Page({
+ contentScript: 'self.on("message", function (m) {' +
+ 'self.postMessage(m);' +
+ '});',
+ contentURL: 'data:text/html;charset=utf-8,',
+ });
+ page.postMessage('ping');
+ page.on('message', function (m) {
+ assert.equal(m, 'ping', 'postMessage should queue messages');
+ done();
+ });
+};
+
+exports.testWindowStopDontBreak = function (assert, done) {
+ const { Ci, Cc } = require('chrome');
+ const consoleService = Cc['@mozilla.org/consoleservice;1'].
+ getService(Ci.nsIConsoleService);
+ const listener = {
+ observe: ({message}) => {
+ if (message.includes('contentWorker is null'))
+ assert.fail('contentWorker is null');
+ }
+ };
+ consoleService.registerListener(listener)
+
+ let page = new Page({
+ contentURL: 'data:text/html;charset=utf-8,testWindowStopDontBreak',
+ contentScriptWhen: 'ready',
+ contentScript: Isolate(() => {
+ window.stop();
+ self.port.on('ping', () => self.port.emit('pong'));
+ })
+ });
+
+ page.port.on('pong', () => {
+ assert.pass('page-worker works after window.stop');
+ page.destroy();
+ consoleService.unregisterListener(listener);
+ done();
+ });
+
+ page.port.emit("ping");
+};
+
+
+function isDestroyed(page) {
+ try {
+ page.postMessage("foo");
+ }
+ catch (err) {
+ if (err.message == ERR_DESTROYED) {
+ return true;
+ }
+ else {
+ throw err;
+ }
+ }
+ return false;
+}
+
+// require("sdk/test").run(exports);