1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/addon-sdk/source/test/test-page-worker.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,468 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 +"use strict"; 1.8 + 1.9 +const { Loader } = require('sdk/test/loader'); 1.10 +const { Page } = require("sdk/page-worker"); 1.11 +const { URL } = require("sdk/url"); 1.12 +const fixtures = require("./fixtures"); 1.13 +const testURI = fixtures.url("test.html"); 1.14 + 1.15 +const ERR_DESTROYED = 1.16 + "Couldn't find the worker to receive this message. " + 1.17 + "The script may not be initialized yet, or may already have been unloaded."; 1.18 + 1.19 +exports.testSimplePageCreation = function(assert, done) { 1.20 + let page = new Page({ 1.21 + contentScript: "self.postMessage(window.location.href)", 1.22 + contentScriptWhen: "end", 1.23 + onMessage: function (message) { 1.24 + assert.equal(message, "about:blank", 1.25 + "Page Worker should start with a blank page by default"); 1.26 + assert.equal(this, page, "The 'this' object is the page itself."); 1.27 + done(); 1.28 + } 1.29 + }); 1.30 +} 1.31 + 1.32 +/* 1.33 + * Tests that we can't be tricked by document overloads as we have access 1.34 + * to wrapped nodes 1.35 + */ 1.36 +exports.testWrappedDOM = function(assert, done) { 1.37 + let page = Page({ 1.38 + allow: { script: true }, 1.39 + contentURL: "data:text/html;charset=utf-8,<script>document.getElementById=3;window.scrollTo=3;</script>", 1.40 + contentScript: "window.addEventListener('load', function () " + 1.41 + "self.postMessage([typeof(document.getElementById), " + 1.42 + "typeof(window.scrollTo)]), true)", 1.43 + onMessage: function (message) { 1.44 + assert.equal(message[0], 1.45 + "function", 1.46 + "getElementById from content script is the native one"); 1.47 + 1.48 + assert.equal(message[1], 1.49 + "function", 1.50 + "scrollTo from content script is the native one"); 1.51 + 1.52 + done(); 1.53 + } 1.54 + }); 1.55 +} 1.56 + 1.57 +/* 1.58 +// We do not offer unwrapped access to DOM since bug 601295 landed 1.59 +// See 660780 to track progress of unwrap feature 1.60 +exports.testUnwrappedDOM = function(assert, done) { 1.61 + let page = Page({ 1.62 + allow: { script: true }, 1.63 + contentURL: "data:text/html;charset=utf-8,<script>document.getElementById=3;window.scrollTo=3;</script>", 1.64 + contentScript: "window.addEventListener('load', function () " + 1.65 + "self.postMessage([typeof(unsafeWindow.document.getElementById), " + 1.66 + "typeof(unsafeWindow.scrollTo)]), true)", 1.67 + onMessage: function (message) { 1.68 + assert.equal(message[0], 1.69 + "number", 1.70 + "document inside page is free to be changed"); 1.71 + 1.72 + assert.equal(message[1], 1.73 + "number", 1.74 + "window inside page is free to be changed"); 1.75 + 1.76 + done(); 1.77 + } 1.78 + }); 1.79 +} 1.80 +*/ 1.81 + 1.82 +exports.testPageProperties = function(assert) { 1.83 + let page = new Page(); 1.84 + 1.85 + for each (let prop in ['contentURL', 'allow', 'contentScriptFile', 1.86 + 'contentScript', 'contentScriptWhen', 'on', 1.87 + 'postMessage', 'removeListener']) { 1.88 + assert.ok(prop in page, prop + " property is defined on page."); 1.89 + } 1.90 + 1.91 + assert.ok(function () page.postMessage("foo") || true, 1.92 + "postMessage doesn't throw exception on page."); 1.93 +} 1.94 + 1.95 +exports.testConstructorAndDestructor = function(assert, done) { 1.96 + let loader = Loader(module); 1.97 + let { Page } = loader.require("sdk/page-worker"); 1.98 + let global = loader.sandbox("sdk/page-worker"); 1.99 + 1.100 + let pagesReady = 0; 1.101 + 1.102 + let page1 = Page({ 1.103 + contentScript: "self.postMessage('')", 1.104 + contentScriptWhen: "end", 1.105 + onMessage: pageReady 1.106 + }); 1.107 + let page2 = Page({ 1.108 + contentScript: "self.postMessage('')", 1.109 + contentScriptWhen: "end", 1.110 + onMessage: pageReady 1.111 + }); 1.112 + 1.113 + assert.notEqual(page1, page2, 1.114 + "Page 1 and page 2 should be different objects."); 1.115 + 1.116 + function pageReady() { 1.117 + if (++pagesReady == 2) { 1.118 + page1.destroy(); 1.119 + page2.destroy(); 1.120 + 1.121 + assert.ok(isDestroyed(page1), "page1 correctly unloaded."); 1.122 + assert.ok(isDestroyed(page2), "page2 correctly unloaded."); 1.123 + 1.124 + loader.unload(); 1.125 + done(); 1.126 + } 1.127 + } 1.128 +} 1.129 + 1.130 +exports.testAutoDestructor = function(assert, done) { 1.131 + let loader = Loader(module); 1.132 + let { Page } = loader.require("sdk/page-worker"); 1.133 + 1.134 + let page = Page({ 1.135 + contentScript: "self.postMessage('')", 1.136 + contentScriptWhen: "end", 1.137 + onMessage: function() { 1.138 + loader.unload(); 1.139 + assert.ok(isDestroyed(page), "Page correctly unloaded."); 1.140 + done(); 1.141 + } 1.142 + }); 1.143 +} 1.144 + 1.145 +exports.testValidateOptions = function(assert) { 1.146 + assert.throws( 1.147 + function () Page({ contentURL: 'home' }), 1.148 + /The `contentURL` option must be a valid URL\./, 1.149 + "Validation correctly denied a non-URL contentURL" 1.150 + ); 1.151 + 1.152 + assert.throws( 1.153 + function () Page({ onMessage: "This is not a function."}), 1.154 + /The option "onMessage" must be one of the following types: function/, 1.155 + "Validation correctly denied a non-function onMessage." 1.156 + ); 1.157 + 1.158 + assert.pass("Options validation is working."); 1.159 +} 1.160 + 1.161 +exports.testContentAndAllowGettersAndSetters = function(assert, done) { 1.162 + let content = "data:text/html;charset=utf-8,<script>window.localStorage.allowScript=3;</script>"; 1.163 + 1.164 + // Load up the page with testURI initially for the resource:// principal, 1.165 + // then load the actual data:* content, as data:* URIs no longer 1.166 + // have localStorage 1.167 + let page = Page({ 1.168 + contentURL: testURI, 1.169 + contentScript: "if (window.location.href==='"+testURI+"')" + 1.170 + " self.postMessage('reload');" + 1.171 + "else " + 1.172 + " self.postMessage(window.localStorage.allowScript)", 1.173 + contentScriptWhen: "end", 1.174 + onMessage: step0 1.175 + }); 1.176 + 1.177 + function step0(message) { 1.178 + if (message === 'reload') 1.179 + return page.contentURL = content; 1.180 + assert.equal(message, "3", 1.181 + "Correct value expected for allowScript - 3"); 1.182 + assert.equal(page.contentURL, content, 1.183 + "Correct content expected"); 1.184 + page.removeListener('message', step0); 1.185 + page.on('message', step1); 1.186 + page.allow = { script: false }; 1.187 + page.contentURL = content = 1.188 + "data:text/html;charset=utf-8,<script>window.localStorage.allowScript='f'</script>"; 1.189 + } 1.190 + 1.191 + function step1(message) { 1.192 + assert.equal(message, "3", 1.193 + "Correct value expected for allowScript - 3"); 1.194 + assert.equal(page.contentURL, content, "Correct content expected"); 1.195 + page.removeListener('message', step1); 1.196 + page.on('message', step2); 1.197 + page.allow = { script: true }; 1.198 + page.contentURL = content = 1.199 + "data:text/html;charset=utf-8,<script>window.localStorage.allowScript='g'</script>"; 1.200 + } 1.201 + 1.202 + function step2(message) { 1.203 + assert.equal(message, "g", 1.204 + "Correct value expected for allowScript - g"); 1.205 + assert.equal(page.contentURL, content, "Correct content expected"); 1.206 + page.removeListener('message', step2); 1.207 + page.on('message', step3); 1.208 + page.allow.script = false; 1.209 + page.contentURL = content = 1.210 + "data:text/html;charset=utf-8,<script>window.localStorage.allowScript=3</script>"; 1.211 + } 1.212 + 1.213 + function step3(message) { 1.214 + assert.equal(message, "g", 1.215 + "Correct value expected for allowScript - g"); 1.216 + assert.equal(page.contentURL, content, "Correct content expected"); 1.217 + page.removeListener('message', step3); 1.218 + page.on('message', step4); 1.219 + page.allow.script = true; 1.220 + page.contentURL = content = 1.221 + "data:text/html;charset=utf-8,<script>window.localStorage.allowScript=4</script>"; 1.222 + } 1.223 + 1.224 + function step4(message) { 1.225 + assert.equal(message, "4", 1.226 + "Correct value expected for allowScript - 4"); 1.227 + assert.equal(page.contentURL, content, "Correct content expected"); 1.228 + done(); 1.229 + } 1.230 + 1.231 +} 1.232 + 1.233 +exports.testOnMessageCallback = function(assert, done) { 1.234 + Page({ 1.235 + contentScript: "self.postMessage('')", 1.236 + contentScriptWhen: "end", 1.237 + onMessage: function() { 1.238 + assert.pass("onMessage callback called"); 1.239 + done(); 1.240 + } 1.241 + }); 1.242 +} 1.243 + 1.244 +exports.testMultipleOnMessageCallbacks = function(assert, done) { 1.245 + let count = 0; 1.246 + let page = Page({ 1.247 + contentScript: "self.postMessage('')", 1.248 + contentScriptWhen: "end", 1.249 + onMessage: () => count += 1 1.250 + }); 1.251 + page.on('message', () => count += 2); 1.252 + page.on('message', () => count *= 3); 1.253 + page.on('message', () => 1.254 + assert.equal(count, 9, "All callbacks were called, in order.")); 1.255 + page.on('message', done); 1.256 +}; 1.257 + 1.258 +exports.testLoadContentPage = function(assert, done) { 1.259 + let page = Page({ 1.260 + onMessage: function(message) { 1.261 + // The message is an array whose first item is the test method to call 1.262 + // and the rest of whose items are arguments to pass it. 1.263 + let msg = message.shift(); 1.264 + if (msg == "done") 1.265 + return done(); 1.266 + assert[msg].apply(assert, message); 1.267 + }, 1.268 + contentURL: fixtures.url("test-page-worker.html"), 1.269 + contentScriptFile: fixtures.url("test-page-worker.js"), 1.270 + contentScriptWhen: "ready" 1.271 + }); 1.272 +} 1.273 + 1.274 +exports.testAllowScriptDefault = function(assert, done) { 1.275 + let page = Page({ 1.276 + onMessage: function(message) { 1.277 + assert.ok(message, "Script is allowed to run by default."); 1.278 + done(); 1.279 + }, 1.280 + contentURL: "data:text/html;charset=utf-8,<script>document.documentElement.setAttribute('foo', 3);</script>", 1.281 + contentScript: "self.postMessage(document.documentElement.getAttribute('foo'))", 1.282 + contentScriptWhen: "ready" 1.283 + }); 1.284 +} 1.285 + 1.286 +exports.testAllowScript = function(assert, done) { 1.287 + let page = Page({ 1.288 + onMessage: function(message) { 1.289 + assert.ok(message, "Script runs when allowed to do so."); 1.290 + done(); 1.291 + }, 1.292 + allow: { script: true }, 1.293 + contentURL: "data:text/html;charset=utf-8,<script>document.documentElement.setAttribute('foo', 3);</script>", 1.294 + contentScript: "self.postMessage(document.documentElement.hasAttribute('foo') && " + 1.295 + " document.documentElement.getAttribute('foo') == 3)", 1.296 + contentScriptWhen: "ready" 1.297 + }); 1.298 +} 1.299 + 1.300 +exports.testPingPong = function(assert, done) { 1.301 + let page = Page({ 1.302 + contentURL: 'data:text/html;charset=utf-8,ping-pong', 1.303 + contentScript: 'self.on("message", function(message) self.postMessage("pong"));' 1.304 + + 'self.postMessage("ready");', 1.305 + onMessage: function(message) { 1.306 + if ('ready' == message) { 1.307 + page.postMessage('ping'); 1.308 + } 1.309 + else { 1.310 + assert.ok(message, 'pong', 'Callback from contentScript'); 1.311 + done(); 1.312 + } 1.313 + } 1.314 + }); 1.315 +}; 1.316 + 1.317 +exports.testRedirect = function (assert, done) { 1.318 + let page = Page({ 1.319 + contentURL: 'data:text/html;charset=utf-8,first-page', 1.320 + contentScriptWhen: "end", 1.321 + contentScript: '' + 1.322 + 'if (/first-page/.test(document.location.href)) ' + 1.323 + ' document.location.href = "data:text/html;charset=utf-8,redirect";' + 1.324 + 'else ' + 1.325 + ' self.port.emit("redirect", document.location.href);' 1.326 + }); 1.327 + 1.328 + page.port.on('redirect', function (url) { 1.329 + assert.equal(url, 'data:text/html;charset=utf-8,redirect', 'Reinjects contentScript on reload'); 1.330 + done(); 1.331 + }); 1.332 +}; 1.333 + 1.334 +exports.testRedirectIncludeArrays = function (assert, done) { 1.335 + let firstURL = 'data:text/html;charset=utf-8,first-page'; 1.336 + let page = Page({ 1.337 + contentURL: firstURL, 1.338 + contentScript: '(function () {' + 1.339 + 'self.port.emit("load", document.location.href);' + 1.340 + ' self.port.on("redirect", function (url) {' + 1.341 + ' document.location.href = url;' + 1.342 + ' })' + 1.343 + '})();', 1.344 + include: ['about:blank', 'data:*'] 1.345 + }); 1.346 + 1.347 + page.port.on('load', function (url) { 1.348 + if (url === firstURL) { 1.349 + page.port.emit('redirect', 'about:blank'); 1.350 + } else if (url === 'about:blank') { 1.351 + page.port.emit('redirect', 'about:mozilla'); 1.352 + assert.ok('`include` property handles arrays'); 1.353 + assert.equal(url, 'about:blank', 'Redirects work with accepted domains'); 1.354 + done(); 1.355 + } else if (url === 'about:mozilla') { 1.356 + assert.fail('Should not redirect to restricted domain'); 1.357 + } 1.358 + }); 1.359 +}; 1.360 + 1.361 +exports.testRedirectFromWorker = function (assert, done) { 1.362 + let firstURL = 'data:text/html;charset=utf-8,first-page'; 1.363 + let secondURL = 'data:text/html;charset=utf-8,second-page'; 1.364 + let thirdURL = 'data:text/html;charset=utf-8,third-page'; 1.365 + let page = Page({ 1.366 + contentURL: firstURL, 1.367 + contentScript: '(function () {' + 1.368 + 'self.port.emit("load", document.location.href);' + 1.369 + ' self.port.on("redirect", function (url) {' + 1.370 + ' document.location.href = url;' + 1.371 + ' })' + 1.372 + '})();', 1.373 + include: 'data:*' 1.374 + }); 1.375 + 1.376 + page.port.on('load', function (url) { 1.377 + if (url === firstURL) { 1.378 + page.port.emit('redirect', secondURL); 1.379 + } else if (url === secondURL) { 1.380 + page.port.emit('redirect', thirdURL); 1.381 + } else if (url === thirdURL) { 1.382 + page.port.emit('redirect', 'about:mozilla'); 1.383 + assert.equal(url, thirdURL, 'Redirects work with accepted domains on include strings'); 1.384 + done(); 1.385 + } else { 1.386 + assert.fail('Should not redirect to unauthorized domains'); 1.387 + } 1.388 + }); 1.389 +}; 1.390 + 1.391 +exports.testRedirectWithContentURL = function (assert, done) { 1.392 + let firstURL = 'data:text/html;charset=utf-8,first-page'; 1.393 + let secondURL = 'data:text/html;charset=utf-8,second-page'; 1.394 + let thirdURL = 'data:text/html;charset=utf-8,third-page'; 1.395 + let page = Page({ 1.396 + contentURL: firstURL, 1.397 + contentScript: '(function () {' + 1.398 + 'self.port.emit("load", document.location.href);' + 1.399 + '})();', 1.400 + include: 'data:*' 1.401 + }); 1.402 + 1.403 + page.port.on('load', function (url) { 1.404 + if (url === firstURL) { 1.405 + page.contentURL = secondURL; 1.406 + } else if (url === secondURL) { 1.407 + page.contentURL = thirdURL; 1.408 + } else if (url === thirdURL) { 1.409 + page.contentURL = 'about:mozilla'; 1.410 + assert.equal(url, thirdURL, 'Redirects work with accepted domains on include strings'); 1.411 + done(); 1.412 + } else { 1.413 + assert.fail('Should not redirect to unauthorized domains'); 1.414 + } 1.415 + }); 1.416 +}; 1.417 + 1.418 + 1.419 +exports.testMultipleDestroys = function(assert) { 1.420 + let page = Page(); 1.421 + page.destroy(); 1.422 + page.destroy(); 1.423 + assert.pass("Multiple destroys should not cause an error"); 1.424 +}; 1.425 + 1.426 +exports.testContentScriptOptionsOption = function(assert, done) { 1.427 + let page = new Page({ 1.428 + contentScript: "self.postMessage( [typeof self.options.d, self.options] );", 1.429 + contentScriptWhen: "end", 1.430 + contentScriptOptions: {a: true, b: [1,2,3], c: "string", d: function(){ return 'test'}}, 1.431 + onMessage: function(msg) { 1.432 + assert.equal(msg[0], 'undefined', 'functions are stripped from contentScriptOptions'); 1.433 + assert.equal(typeof msg[1], 'object', 'object as contentScriptOptions'); 1.434 + assert.equal(msg[1].a, true, 'boolean in contentScriptOptions'); 1.435 + assert.equal(msg[1].b.join(), '1,2,3', 'array and numbers in contentScriptOptions'); 1.436 + assert.equal(msg[1].c, 'string', 'string in contentScriptOptions'); 1.437 + done(); 1.438 + } 1.439 + }); 1.440 +}; 1.441 + 1.442 +exports.testMessageQueue = function (assert, done) { 1.443 + let page = new Page({ 1.444 + contentScript: 'self.on("message", function (m) {' + 1.445 + 'self.postMessage(m);' + 1.446 + '});', 1.447 + contentURL: 'data:text/html;charset=utf-8,', 1.448 + }); 1.449 + page.postMessage('ping'); 1.450 + page.on('message', function (m) { 1.451 + assert.equal(m, 'ping', 'postMessage should queue messages'); 1.452 + done(); 1.453 + }); 1.454 +}; 1.455 + 1.456 +function isDestroyed(page) { 1.457 + try { 1.458 + page.postMessage("foo"); 1.459 + } 1.460 + catch (err) { 1.461 + if (err.message == ERR_DESTROYED) { 1.462 + return true; 1.463 + } 1.464 + else { 1.465 + throw err; 1.466 + } 1.467 + } 1.468 + return false; 1.469 +} 1.470 + 1.471 +require("test").run(exports);