1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/addon-sdk/source/test/test-tabs-common.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,521 @@ 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, LoaderWithHookedConsole } = require("sdk/test/loader"); 1.10 +const { browserWindows } = require('sdk/windows'); 1.11 +const tabs = require('sdk/tabs'); 1.12 +const { isPrivate } = require('sdk/private-browsing'); 1.13 +const { openDialog } = require('sdk/window/utils'); 1.14 +const { isWindowPrivate } = require('sdk/window/utils'); 1.15 +const { setTimeout } = require('sdk/timers'); 1.16 +const { openWebpage } = require('./private-browsing/helper'); 1.17 +const { isTabPBSupported, isWindowPBSupported } = require('sdk/private-browsing/utils'); 1.18 +const app = require("sdk/system/xul-app"); 1.19 + 1.20 +const URL = 'data:text/html;charset=utf-8,<html><head><title>#title#</title></head></html>'; 1.21 + 1.22 +// TEST: tab count 1.23 +exports.testTabCounts = function(assert, done) { 1.24 + tabs.open({ 1.25 + url: 'about:blank', 1.26 + onReady: function(tab) { 1.27 + let count1 = 0, 1.28 + count2 = 0; 1.29 + for each(let window in browserWindows) { 1.30 + count1 += window.tabs.length; 1.31 + for each(let tab in window.tabs) { 1.32 + count2 += 1; 1.33 + } 1.34 + } 1.35 + 1.36 + assert.ok(tabs.length > 1, 'tab count is > 1'); 1.37 + assert.equal(count1, tabs.length, 'tab count by length is correct'); 1.38 + assert.equal(count2, tabs.length, 'tab count by iteration is correct'); 1.39 + 1.40 + // end test 1.41 + tab.close(done); 1.42 + } 1.43 + }); 1.44 +}; 1.45 + 1.46 + 1.47 +// TEST: tabs.activeTab getter 1.48 +exports.testActiveTab_getter = function(assert, done) { 1.49 + let evtCount = 0; 1.50 + let activeTab = null; 1.51 + 1.52 + function endTest(type, tab) { 1.53 + if (type == 'activate') { 1.54 + assert.strictEqual(tabs.activeTab, tab, 'the active tab is the opened tab'); 1.55 + activeTab = tabs.activeTab; 1.56 + } 1.57 + else { 1.58 + assert.equal(tab.url, url, 'the opened tab has the correct url'); 1.59 + } 1.60 + 1.61 + if (++evtCount != 2) 1.62 + return; 1.63 + 1.64 + assert.strictEqual(activeTab, tab, 'the active tab is the ready tab'); 1.65 + assert.strictEqual(tabs.activeTab, tab, 'the active tab is the ready tab'); 1.66 + 1.67 + tab.close(done); 1.68 + } 1.69 + 1.70 + let url = URL.replace("#title#", "testActiveTab_getter"); 1.71 + tabs.open({ 1.72 + url: url, 1.73 + onReady: endTest.bind(null, 'ready'), 1.74 + onActivate: endTest.bind(null, 'activate') 1.75 + }); 1.76 +}; 1.77 + 1.78 +// TEST: tab.activate() 1.79 +exports.testActiveTab_setter = function(assert, done) { 1.80 + let url = URL.replace("#title#", "testActiveTab_setter"); 1.81 + let tab1URL = URL.replace("#title#", "tab1"); 1.82 + 1.83 + tabs.open({ 1.84 + url: tab1URL, 1.85 + onReady: function(activeTab) { 1.86 + let activeTabURL = tabs.activeTab.url; 1.87 + 1.88 + tabs.open({ 1.89 + url: url, 1.90 + inBackground: true, 1.91 + onReady: function onReady(tab) { 1.92 + assert.equal(tabs.activeTab.url, activeTabURL, "activeTab url has not changed"); 1.93 + assert.equal(tab.url, url, "url of new background tab matches"); 1.94 + 1.95 + tab.once('activate', function onActivate(eventTab) { 1.96 + assert.equal(tabs.activeTab.url, url, "url after activeTab setter matches"); 1.97 + assert.equal(eventTab, tab, "event argument is the activated tab"); 1.98 + assert.equal(eventTab, tabs.activeTab, "the tab is the active one"); 1.99 + 1.100 + activeTab.close(function() { 1.101 + tab.close(done); 1.102 + }); 1.103 + }); 1.104 + 1.105 + tab.activate(); 1.106 + } 1.107 + }); 1.108 + } 1.109 + }); 1.110 +}; 1.111 + 1.112 +// TEST: tab.close() 1.113 +exports.testTabClose_alt = function(assert, done) { 1.114 + let url = URL.replace('#title#', 'TabClose_alt'); 1.115 + let tab1URL = URL.replace('#title#', 'tab1'); 1.116 + 1.117 + tabs.open({ 1.118 + url: tab1URL, 1.119 + onReady: function(tab1) { 1.120 + // make sure that our tab is not active first 1.121 + assert.notEqual(tabs.activeTab.url, url, "tab is not the active tab"); 1.122 + 1.123 + tabs.open({ 1.124 + url: url, 1.125 + onReady: function(tab) { 1.126 + assert.equal(tab.url, url, "tab is now the active tab"); 1.127 + assert.equal(tabs.activeTab.url, url, "tab is now the active tab"); 1.128 + 1.129 + // another tab should be activated on close 1.130 + tabs.once('activate', function() { 1.131 + assert.notEqual(tabs.activeTab.url, url, "tab is no longer the active tab"); 1.132 + 1.133 + // end test 1.134 + tab1.close(done); 1.135 + }); 1.136 + 1.137 + tab.close(); 1.138 + } 1.139 + }); 1.140 + } 1.141 + }); 1.142 +}; 1.143 + 1.144 +exports.testAttachOnOpen_alt = function (assert, done) { 1.145 + // Take care that attach has to be called on tab ready and not on tab open. 1.146 + tabs.open({ 1.147 + url: "data:text/html;charset=utf-8,foobar", 1.148 + onOpen: function (tab) { 1.149 + let worker = tab.attach({ 1.150 + contentScript: 'self.postMessage(document.location.href); ', 1.151 + onMessage: function (msg) { 1.152 + assert.equal(msg, "about:blank", 1.153 + "Worker document url is about:blank on open"); 1.154 + worker.destroy(); 1.155 + tab.close(done); 1.156 + } 1.157 + }); 1.158 + } 1.159 + }); 1.160 +}; 1.161 + 1.162 +exports.testAttachOnMultipleDocuments_alt = function (assert, done) { 1.163 + // Example of attach that process multiple tab documents 1.164 + let firstLocation = "data:text/html;charset=utf-8,foobar"; 1.165 + let secondLocation = "data:text/html;charset=utf-8,bar"; 1.166 + let thirdLocation = "data:text/html;charset=utf-8,fox"; 1.167 + let onReadyCount = 0; 1.168 + let worker1 = null; 1.169 + let worker2 = null; 1.170 + let detachEventCount = 0; 1.171 + 1.172 + tabs.open({ 1.173 + url: firstLocation, 1.174 + onReady: function (tab) { 1.175 + onReadyCount++; 1.176 + if (onReadyCount == 1) { 1.177 + worker1 = tab.attach({ 1.178 + contentScript: 'self.on("message", ' + 1.179 + ' function () self.postMessage(document.location.href)' + 1.180 + ');', 1.181 + onMessage: function (msg) { 1.182 + assert.equal(msg, firstLocation, 1.183 + "Worker url is equal to the 1st document"); 1.184 + tab.url = secondLocation; 1.185 + }, 1.186 + onDetach: function () { 1.187 + detachEventCount++; 1.188 + assert.pass("Got worker1 detach event"); 1.189 + assert.throws(function () { 1.190 + worker1.postMessage("ex-1"); 1.191 + }, 1.192 + /Couldn't find the worker/, 1.193 + "postMessage throw because worker1 is destroyed"); 1.194 + checkEnd(); 1.195 + } 1.196 + }); 1.197 + worker1.postMessage("new-doc-1"); 1.198 + } 1.199 + else if (onReadyCount == 2) { 1.200 + worker2 = tab.attach({ 1.201 + contentScript: 'self.on("message", ' + 1.202 + ' function () self.postMessage(document.location.href)' + 1.203 + ');', 1.204 + onMessage: function (msg) { 1.205 + assert.equal(msg, secondLocation, 1.206 + "Worker url is equal to the 2nd document"); 1.207 + tab.url = thirdLocation; 1.208 + }, 1.209 + onDetach: function () { 1.210 + detachEventCount++; 1.211 + assert.pass("Got worker2 detach event"); 1.212 + assert.throws(function () { 1.213 + worker2.postMessage("ex-2"); 1.214 + }, 1.215 + /Couldn't find the worker/, 1.216 + "postMessage throw because worker2 is destroyed"); 1.217 + checkEnd(tab); 1.218 + } 1.219 + }); 1.220 + worker2.postMessage("new-doc-2"); 1.221 + } 1.222 + else if (onReadyCount == 3) { 1.223 + tab.close(); 1.224 + } 1.225 + } 1.226 + }); 1.227 + 1.228 + function checkEnd(tab) { 1.229 + if (detachEventCount != 2) 1.230 + return; 1.231 + 1.232 + assert.pass("Got all detach events"); 1.233 + 1.234 + done(); 1.235 + } 1.236 +}; 1.237 + 1.238 +exports.testAttachWrappers_alt = function (assert, done) { 1.239 + // Check that content script has access to wrapped values by default 1.240 + 1.241 + let document = "data:text/html;charset=utf-8,<script>var globalJSVar = true; " + 1.242 + " document.getElementById = 3;</script>"; 1.243 + let count = 0; 1.244 + 1.245 + tabs.open({ 1.246 + url: document, 1.247 + onReady: function (tab) { 1.248 + let worker = tab.attach({ 1.249 + contentScript: 'try {' + 1.250 + ' self.postMessage(!("globalJSVar" in window));' + 1.251 + ' self.postMessage(typeof window.globalJSVar == "undefined");' + 1.252 + '} catch(e) {' + 1.253 + ' self.postMessage(e.message);' + 1.254 + '}', 1.255 + onMessage: function (msg) { 1.256 + assert.equal(msg, true, "Worker has wrapped objects ("+count+")"); 1.257 + if (count++ == 1) 1.258 + tab.close(function() done()); 1.259 + } 1.260 + }); 1.261 + } 1.262 + }); 1.263 +}; 1.264 + 1.265 +// TEST: activeWindow getter and activeTab getter on tab 'activate' event 1.266 +exports.testActiveWindowActiveTabOnActivate_alt = function(assert, done) { 1.267 + 1.268 + let activateCount = 0; 1.269 + let newTabs = []; 1.270 + let tabs = browserWindows.activeWindow.tabs; 1.271 + 1.272 + tabs.on('activate', function onActivate(tab) { 1.273 + assert.equal(tabs.activeTab, tab, 1.274 + "the active window's active tab is the tab provided"); 1.275 + 1.276 + if (++activateCount == 2) { 1.277 + tabs.removeListener('activate', onActivate); 1.278 + 1.279 + newTabs.forEach(function(tab) { 1.280 + tab.close(function() { 1.281 + if (--activateCount == 0) { 1.282 + done(); 1.283 + } 1.284 + }); 1.285 + }); 1.286 + } 1.287 + else if (activateCount > 2) { 1.288 + assert.fail("activateCount is greater than 2 for some reason.."); 1.289 + } 1.290 + }); 1.291 + 1.292 + tabs.open({ 1.293 + url: URL.replace("#title#", "tabs.open1"), 1.294 + onOpen: function(tab) newTabs.push(tab) 1.295 + }); 1.296 + tabs.open({ 1.297 + url: URL.replace("#title#", "tabs.open2"), 1.298 + onOpen: function(tab) newTabs.push(tab) 1.299 + }); 1.300 +}; 1.301 + 1.302 +// TEST: tab properties 1.303 +exports.testTabContentTypeAndReload = function(assert, done) { 1.304 + 1.305 + let url = "data:text/html;charset=utf-8,<html><head><title>foo</title></head><body>foo</body></html>"; 1.306 + let urlXML = "data:text/xml;charset=utf-8,<foo>bar</foo>"; 1.307 + tabs.open({ 1.308 + url: url, 1.309 + onReady: function(tab) { 1.310 + if (tab.url === url) { 1.311 + assert.equal(tab.contentType, "text/html"); 1.312 + tab.url = urlXML; 1.313 + } 1.314 + else { 1.315 + assert.equal(tab.contentType, "text/xml"); 1.316 + tab.close(done); 1.317 + } 1.318 + } 1.319 + }); 1.320 +}; 1.321 + 1.322 +// test that it isn't possible to open a private tab without the private permission 1.323 +exports.testTabOpenPrivate = function(assert, done) { 1.324 + 1.325 + let url = 'about:blank'; 1.326 + tabs.open({ 1.327 + url: url, 1.328 + isPrivate: true, 1.329 + onReady: function(tab) { 1.330 + assert.equal(tab.url, url, 'opened correct tab'); 1.331 + assert.equal(isPrivate(tab), false, 'private tabs are not supported by default'); 1.332 + 1.333 + tab.close(done); 1.334 + } 1.335 + }); 1.336 +} 1.337 + 1.338 +// We need permission flag in order to see private window's tabs 1.339 +exports.testPrivateAreNotListed = function (assert, done) { 1.340 + let originalTabCount = tabs.length; 1.341 + 1.342 + let page = openWebpage("about:blank", true); 1.343 + if (!page) { 1.344 + assert.pass("Private browsing isn't supported in this release"); 1.345 + return; 1.346 + } 1.347 + 1.348 + page.ready.then(function (win) { 1.349 + if (isTabPBSupported || isWindowPBSupported) { 1.350 + assert.ok(isWindowPrivate(win), "the window is private"); 1.351 + assert.equal(tabs.length, originalTabCount, 1.352 + 'but the tab is *not* visible in tabs list'); 1.353 + } 1.354 + else { 1.355 + assert.ok(!isWindowPrivate(win), "the window isn't private"); 1.356 + assert.equal(tabs.length, originalTabCount + 1, 1.357 + 'so that the tab is visible is tabs list'); 1.358 + } 1.359 + page.close().then(done); 1.360 + }); 1.361 +} 1.362 + 1.363 +// If we close the tab while being in `onOpen` listener, 1.364 +// we end up synchronously consuming TabOpen, closing the tab and still 1.365 +// synchronously consuming the related TabClose event before the second 1.366 +// loader have a change to process the first TabOpen event! 1.367 +exports.testImmediateClosing = function (assert, done) { 1.368 + let tabURL = 'data:text/html,foo'; 1.369 + 1.370 + let { loader, messages } = LoaderWithHookedConsole(module, onMessage); 1.371 + let concurrentTabs = loader.require("sdk/tabs"); 1.372 + concurrentTabs.on("open", function (tab) { 1.373 + // On Firefox, It shouldn't receive such event as the other loader will just 1.374 + // open and destroy the tab without giving a chance to other loader to even 1.375 + // know about the existance of this tab. 1.376 + if (app.is("Firefox")) { 1.377 + assert.fail("Concurrent loader received a tabs `open` event"); 1.378 + } 1.379 + else { 1.380 + // On mobile, we can still receive an open event, 1.381 + // but not the related ready event 1.382 + tab.on("ready", function () { 1.383 + assert.fail("Concurrent loader received a tabs `ready` event"); 1.384 + }); 1.385 + } 1.386 + }); 1.387 + function onMessage(type, msg) { 1.388 + assert.fail("Unexpected mesage on concurrent loader: " + msg); 1.389 + } 1.390 + 1.391 + tabs.open({ 1.392 + url: tabURL, 1.393 + onOpen: function(tab) { 1.394 + tab.close(function () { 1.395 + assert.pass("Tab succesfully removed"); 1.396 + // Let a chance to the concurrent loader to receive a TabOpen event 1.397 + // on the next event loop turn 1.398 + setTimeout(function () { 1.399 + loader.unload(); 1.400 + done(); 1.401 + }, 0); 1.402 + }); 1.403 + } 1.404 + }); 1.405 +} 1.406 + 1.407 +// TEST: tab.reload() 1.408 +exports.testTabReload = function(assert, done) { 1.409 + 1.410 + let url = "data:text/html;charset=utf-8,<!doctype%20html><title></title>"; 1.411 + 1.412 + tabs.open({ 1.413 + url: url, 1.414 + onReady: function onReady(tab) { 1.415 + tab.removeListener('ready', onReady); 1.416 + 1.417 + tab.once( 1.418 + 'ready', 1.419 + function onReload() { 1.420 + assert.pass("the tab was loaded again"); 1.421 + assert.equal(tab.url, url, "the tab has the same URL"); 1.422 + 1.423 + tab.close(function() done()); 1.424 + } 1.425 + ); 1.426 + 1.427 + tab.reload(); 1.428 + } 1.429 + }); 1.430 +}; 1.431 + 1.432 +exports.testOnPageShowEvent = function (assert, done) { 1.433 + let events = []; 1.434 + let firstUrl = 'data:text/html;charset=utf-8,First'; 1.435 + let secondUrl = 'data:text/html;charset=utf-8,Second'; 1.436 + 1.437 + let counter = 0; 1.438 + function onPageShow (tab, persisted) { 1.439 + events.push('pageshow'); 1.440 + counter++; 1.441 + if (counter === 1) { 1.442 + assert.equal(persisted, false, 'page should not be cached on initial load'); 1.443 + tab.url = secondUrl; 1.444 + } 1.445 + else if (counter === 2) { 1.446 + assert.equal(persisted, false, 'second test page should not be cached either'); 1.447 + tab.attach({ 1.448 + contentScript: 'setTimeout(function () { window.history.back(); }, 0)' 1.449 + }); 1.450 + } 1.451 + else { 1.452 + assert.equal(persisted, true, 'when we get back to the fist page, it has to' + 1.453 + 'come from cache'); 1.454 + tabs.removeListener('pageshow', onPageShow); 1.455 + tabs.removeListener('open', onOpen); 1.456 + tabs.removeListener('ready', onReady); 1.457 + tab.close(() => { 1.458 + ['open', 'ready', 'pageshow', 'ready', 1.459 + 'pageshow', 'pageshow'].map((type, i) => { 1.460 + assert.equal(type, events[i], 'correct ordering of events'); 1.461 + }); 1.462 + done() 1.463 + }); 1.464 + } 1.465 + } 1.466 + 1.467 + function onOpen () events.push('open'); 1.468 + function onReady () events.push('ready'); 1.469 + 1.470 + tabs.on('pageshow', onPageShow); 1.471 + tabs.on('open', onOpen); 1.472 + tabs.on('ready', onReady); 1.473 + tabs.open({ 1.474 + url: firstUrl 1.475 + }); 1.476 +}; 1.477 + 1.478 +exports.testOnPageShowEventDeclarative = function (assert, done) { 1.479 + let events = []; 1.480 + let firstUrl = 'data:text/html;charset=utf-8,First'; 1.481 + let secondUrl = 'data:text/html;charset=utf-8,Second'; 1.482 + 1.483 + let counter = 0; 1.484 + function onPageShow (tab, persisted) { 1.485 + events.push('pageshow'); 1.486 + counter++; 1.487 + if (counter === 1) { 1.488 + assert.equal(persisted, false, 'page should not be cached on initial load'); 1.489 + tab.url = secondUrl; 1.490 + } 1.491 + else if (counter === 2) { 1.492 + assert.equal(persisted, false, 'second test page should not be cached either'); 1.493 + tab.attach({ 1.494 + contentScript: 'setTimeout(function () { window.history.back(); }, 0)' 1.495 + }); 1.496 + } 1.497 + else { 1.498 + assert.equal(persisted, true, 'when we get back to the fist page, it has to' + 1.499 + 'come from cache'); 1.500 + tabs.removeListener('pageshow', onPageShow); 1.501 + tabs.removeListener('open', onOpen); 1.502 + tabs.removeListener('ready', onReady); 1.503 + tab.close(() => { 1.504 + ['open', 'ready', 'pageshow', 'ready', 1.505 + 'pageshow', 'pageshow'].map((type, i) => { 1.506 + assert.equal(type, events[i], 'correct ordering of events'); 1.507 + }); 1.508 + done() 1.509 + }); 1.510 + } 1.511 + } 1.512 + 1.513 + function onOpen () events.push('open'); 1.514 + function onReady () events.push('ready'); 1.515 + 1.516 + tabs.open({ 1.517 + url: firstUrl, 1.518 + onPageShow: onPageShow, 1.519 + onOpen: onOpen, 1.520 + onReady: onReady 1.521 + }); 1.522 +}; 1.523 + 1.524 +require('sdk/test').run(exports);