1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/addon-sdk/source/test/tabs/test-firefox-tabs.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1049 @@ 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 { Cc, Ci } = require('chrome'); 1.10 +const { Loader } = require('sdk/test/loader'); 1.11 +const { setTimeout } = require('sdk/timers'); 1.12 +const { getOwnerWindow } = require('sdk/private-browsing/window/utils'); 1.13 +const { windows, onFocus, getMostRecentBrowserWindow } = require('sdk/window/utils'); 1.14 +const { open, focus, close } = require('sdk/window/helpers'); 1.15 +const tabs = require('sdk/tabs'); 1.16 +const { browserWindows } = require('sdk/windows'); 1.17 +const { set: setPref } = require("sdk/preferences/service"); 1.18 +const DEPRECATE_PREF = "devtools.errorconsole.deprecation_warnings"; 1.19 +const fixtures = require("../fixtures"); 1.20 + 1.21 +// Bug 682681 - tab.title should never be empty 1.22 +exports.testBug682681_aboutURI = function(assert, done) { 1.23 + let url = 'chrome://browser/locale/tabbrowser.properties'; 1.24 + let stringBundle = Cc["@mozilla.org/intl/stringbundle;1"]. 1.25 + getService(Ci.nsIStringBundleService). 1.26 + createBundle(url); 1.27 + let emptyTabTitle = stringBundle.GetStringFromName('tabs.emptyTabTitle'); 1.28 + 1.29 + tabs.on('ready', function onReady(tab) { 1.30 + tabs.removeListener('ready', onReady); 1.31 + 1.32 + assert.equal(tab.title, 1.33 + emptyTabTitle, 1.34 + "title of about: tab is not blank"); 1.35 + 1.36 + tab.close(done); 1.37 + }); 1.38 + 1.39 + // open a about: url 1.40 + tabs.open({ 1.41 + url: "about:blank", 1.42 + inBackground: true 1.43 + }); 1.44 +}; 1.45 + 1.46 +// related to Bug 682681 1.47 +exports.testTitleForDataURI = function(assert, done) { 1.48 + tabs.open({ 1.49 + url: "data:text/html;charset=utf-8,<title>tab</title>", 1.50 + inBackground: true, 1.51 + onReady: function(tab) { 1.52 + assert.equal(tab.title, "tab", "data: title is not Connecting..."); 1.53 + tab.close(done); 1.54 + } 1.55 + }); 1.56 +}; 1.57 + 1.58 +// TEST: 'BrowserWindow' instance creation on tab 'activate' event 1.59 +// See bug 648244: there was a infinite loop. 1.60 +exports.testBrowserWindowCreationOnActivate = function(assert, done) { 1.61 + let windows = require("sdk/windows").browserWindows; 1.62 + let gotActivate = false; 1.63 + 1.64 + tabs.once('activate', function onActivate(eventTab) { 1.65 + assert.ok(windows.activeWindow, "Is able to fetch activeWindow"); 1.66 + gotActivate = true; 1.67 + }); 1.68 + 1.69 + open().then(function(window) { 1.70 + assert.ok(gotActivate, "Received activate event"); 1.71 + return close(window); 1.72 + }).then(done).then(null, assert.fail); 1.73 +} 1.74 + 1.75 +// TEST: tab unloader 1.76 +exports.testAutomaticDestroyEventOpen = function(assert, done) { 1.77 + let called = false; 1.78 + let loader = Loader(module); 1.79 + let tabs2 = loader.require("sdk/tabs"); 1.80 + tabs2.on('open', _ => called = true); 1.81 + 1.82 + // Fire a tab event and ensure that the destroyed tab is inactive 1.83 + tabs.once('open', tab => { 1.84 + setTimeout(_ => { 1.85 + assert.ok(!called, "Unloaded tab module is destroyed and inactive"); 1.86 + tab.close(done); 1.87 + }); 1.88 + }); 1.89 + 1.90 + loader.unload(); 1.91 + tabs.open("data:text/html;charset=utf-8,testAutomaticDestroyEventOpen"); 1.92 +}; 1.93 + 1.94 +exports.testAutomaticDestroyEventActivate = function(assert, done) { 1.95 + let called = false; 1.96 + let loader = Loader(module); 1.97 + let tabs2 = loader.require("sdk/tabs"); 1.98 + tabs2.on('activate', _ => called = true); 1.99 + 1.100 + // Fire a tab event and ensure that the destroyed tab is inactive 1.101 + tabs.once('activate', tab => { 1.102 + setTimeout(_ => { 1.103 + assert.ok(!called, "Unloaded tab module is destroyed and inactive"); 1.104 + tab.close(done); 1.105 + }); 1.106 + }); 1.107 + 1.108 + loader.unload(); 1.109 + tabs.open("data:text/html;charset=utf-8,testAutomaticDestroyEventActivate"); 1.110 +}; 1.111 + 1.112 +exports.testAutomaticDestroyEventDeactivate = function(assert, done) { 1.113 + let called = false; 1.114 + let currentTab = tabs.activeTab; 1.115 + let loader = Loader(module); 1.116 + let tabs2 = loader.require("sdk/tabs"); 1.117 + 1.118 + tabs.open({ 1.119 + url: "data:text/html;charset=utf-8,testAutomaticDestroyEventDeactivate", 1.120 + onActivate: _ => setTimeout(_ => { 1.121 + tabs2.on('deactivate', _ => called = true); 1.122 + 1.123 + // Fire a tab event and ensure that the destroyed tab is inactive 1.124 + tabs.once('deactivate', tab => { 1.125 + setTimeout(_ => { 1.126 + assert.ok(!called, "Unloaded tab module is destroyed and inactive"); 1.127 + tab.close(done); 1.128 + }); 1.129 + }); 1.130 + 1.131 + loader.unload(); 1.132 + currentTab.activate(); 1.133 + }) 1.134 + }); 1.135 +}; 1.136 + 1.137 +exports.testAutomaticDestroyEventClose = function(assert, done) { 1.138 + let called = false; 1.139 + let loader = Loader(module); 1.140 + let tabs2 = loader.require("sdk/tabs"); 1.141 + 1.142 + tabs.open({ 1.143 + url: "data:text/html;charset=utf-8,testAutomaticDestroyEventClose", 1.144 + onReady: tab => { 1.145 + tabs2.on('close', _ => called = true); 1.146 + 1.147 + // Fire a tab event and ensure that the destroyed tab is inactive 1.148 + tabs.once('close', tab => { 1.149 + setTimeout(_ => { 1.150 + assert.ok(!called, "Unloaded tab module is destroyed and inactive"); 1.151 + done(); 1.152 + }); 1.153 + }); 1.154 + 1.155 + loader.unload(); 1.156 + tab.close(); 1.157 + } 1.158 + }); 1.159 +}; 1.160 + 1.161 +exports.testTabPropertiesInNewWindow = function(assert, done) { 1.162 + let warning = "DEPRECATED: tab.favicon is deprecated, please use require(\"sdk/places/favicon\").getFavicon instead.\n" 1.163 + const { LoaderWithFilteredConsole } = require("sdk/test/loader"); 1.164 + let loader = LoaderWithFilteredConsole(module, function(type, message) { 1.165 + if (type == "error" && message.substring(0, warning.length) == warning) 1.166 + return false; 1.167 + return true; 1.168 + }); 1.169 + 1.170 + let tabs = loader.require('sdk/tabs'); 1.171 + let { getOwnerWindow } = loader.require('sdk/private-browsing/window/utils'); 1.172 + 1.173 + let count = 0; 1.174 + function onReadyOrLoad (tab) { 1.175 + if (count++) { 1.176 + close(getOwnerWindow(tab)).then(done).then(null, assert.fail); 1.177 + } 1.178 + } 1.179 + 1.180 + let url = "data:text/html;charset=utf-8,<html><head><title>foo</title></head><body>foo</body></html>"; 1.181 + tabs.open({ 1.182 + inNewWindow: true, 1.183 + url: url, 1.184 + onReady: function(tab) { 1.185 + assert.equal(tab.title, "foo", "title of the new tab matches"); 1.186 + assert.equal(tab.url, url, "URL of the new tab matches"); 1.187 + assert.ok(tab.favicon, "favicon of the new tab is not empty"); 1.188 + assert.equal(tab.style, null, "style of the new tab matches"); 1.189 + assert.equal(tab.index, 0, "index of the new tab matches"); 1.190 + assert.notEqual(tab.getThumbnail(), null, "thumbnail of the new tab matches"); 1.191 + assert.notEqual(tab.id, null, "a tab object always has an id property."); 1.192 + 1.193 + onReadyOrLoad(tab); 1.194 + }, 1.195 + onLoad: function(tab) { 1.196 + assert.equal(tab.title, "foo", "title of the new tab matches"); 1.197 + assert.equal(tab.url, url, "URL of the new tab matches"); 1.198 + assert.ok(tab.favicon, "favicon of the new tab is not empty"); 1.199 + assert.equal(tab.style, null, "style of the new tab matches"); 1.200 + assert.equal(tab.index, 0, "index of the new tab matches"); 1.201 + assert.notEqual(tab.getThumbnail(), null, "thumbnail of the new tab matches"); 1.202 + assert.notEqual(tab.id, null, "a tab object always has an id property."); 1.203 + 1.204 + onReadyOrLoad(tab); 1.205 + } 1.206 + }); 1.207 +}; 1.208 + 1.209 +exports.testTabPropertiesInSameWindow = function(assert, done) { 1.210 + let warning = "DEPRECATED: tab.favicon is deprecated, please use require(\"sdk/places/favicon\").getFavicon instead.\n" 1.211 + const { LoaderWithFilteredConsole } = require("sdk/test/loader"); 1.212 + let loader = LoaderWithFilteredConsole(module, function(type, message) { 1.213 + if (type == "error" && message.substring(0, warning.length) == warning) 1.214 + return false; 1.215 + return true; 1.216 + }); 1.217 + 1.218 + let tabs = loader.require('sdk/tabs'); 1.219 + 1.220 + // Get current count of tabs so we know the index of the 1.221 + // new tab, bug 893846 1.222 + let tabCount = tabs.length; 1.223 + let count = 0; 1.224 + function onReadyOrLoad (tab) { 1.225 + if (count++) { 1.226 + tab.close(done); 1.227 + } 1.228 + } 1.229 + 1.230 + let url = "data:text/html;charset=utf-8,<html><head><title>foo</title></head><body>foo</body></html>"; 1.231 + tabs.open({ 1.232 + url: url, 1.233 + onReady: function(tab) { 1.234 + assert.equal(tab.title, "foo", "title of the new tab matches"); 1.235 + assert.equal(tab.url, url, "URL of the new tab matches"); 1.236 + assert.ok(tab.favicon, "favicon of the new tab is not empty"); 1.237 + assert.equal(tab.style, null, "style of the new tab matches"); 1.238 + assert.equal(tab.index, tabCount, "index of the new tab matches"); 1.239 + assert.notEqual(tab.getThumbnail(), null, "thumbnail of the new tab matches"); 1.240 + assert.notEqual(tab.id, null, "a tab object always has an id property."); 1.241 + 1.242 + onReadyOrLoad(tab); 1.243 + }, 1.244 + onLoad: function(tab) { 1.245 + assert.equal(tab.title, "foo", "title of the new tab matches"); 1.246 + assert.equal(tab.url, url, "URL of the new tab matches"); 1.247 + assert.ok(tab.favicon, "favicon of the new tab is not empty"); 1.248 + assert.equal(tab.style, null, "style of the new tab matches"); 1.249 + assert.equal(tab.index, tabCount, "index of the new tab matches"); 1.250 + assert.notEqual(tab.getThumbnail(), null, "thumbnail of the new tab matches"); 1.251 + assert.notEqual(tab.id, null, "a tab object always has an id property."); 1.252 + 1.253 + onReadyOrLoad(tab); 1.254 + } 1.255 + }); 1.256 +}; 1.257 + 1.258 +// TEST: tab properties 1.259 +exports.testTabContentTypeAndReload = function(assert, done) { 1.260 + open().then(focus).then(function(window) { 1.261 + let url = "data:text/html;charset=utf-8,<html><head><title>foo</title></head><body>foo</body></html>"; 1.262 + let urlXML = "data:text/xml;charset=utf-8,<foo>bar</foo>"; 1.263 + tabs.open({ 1.264 + url: url, 1.265 + onReady: function(tab) { 1.266 + if (tab.url === url) { 1.267 + assert.equal(tab.contentType, "text/html"); 1.268 + tab.url = urlXML; 1.269 + } 1.270 + else { 1.271 + assert.equal(tab.contentType, "text/xml"); 1.272 + close(window).then(done).then(null, assert.fail); 1.273 + } 1.274 + } 1.275 + }); 1.276 + }); 1.277 +}; 1.278 + 1.279 +// TEST: tabs iterator and length property 1.280 +exports.testTabsIteratorAndLength = function(assert, done) { 1.281 + open(null, { features: { chrome: true, toolbar: true } }).then(focus).then(function(window) { 1.282 + let startCount = 0; 1.283 + for each (let t in tabs) startCount++; 1.284 + assert.equal(startCount, tabs.length, "length property is correct"); 1.285 + let url = "data:text/html;charset=utf-8,default"; 1.286 + 1.287 + tabs.open(url); 1.288 + tabs.open(url); 1.289 + tabs.open({ 1.290 + url: url, 1.291 + onOpen: function(tab) { 1.292 + let count = 0; 1.293 + for each (let t in tabs) count++; 1.294 + assert.equal(count, startCount + 3, "iterated tab count matches"); 1.295 + assert.equal(startCount + 3, tabs.length, "iterated tab count matches length property"); 1.296 + 1.297 + close(window).then(done).then(null, assert.fail); 1.298 + } 1.299 + }); 1.300 + }); 1.301 +}; 1.302 + 1.303 +// TEST: tab.url setter 1.304 +exports.testTabLocation = function(assert, done) { 1.305 + open().then(focus).then(function(window) { 1.306 + let url1 = "data:text/html;charset=utf-8,foo"; 1.307 + let url2 = "data:text/html;charset=utf-8,bar"; 1.308 + 1.309 + tabs.on('ready', function onReady(tab) { 1.310 + if (tab.url != url2) 1.311 + return; 1.312 + tabs.removeListener('ready', onReady); 1.313 + assert.pass("tab.load() loaded the correct url"); 1.314 + close(window).then(done).then(null, assert.fail); 1.315 + }); 1.316 + 1.317 + tabs.open({ 1.318 + url: url1, 1.319 + onOpen: function(tab) { 1.320 + tab.url = url2 1.321 + } 1.322 + }); 1.323 + }); 1.324 +}; 1.325 + 1.326 +// TEST: tab.close() 1.327 +exports.testTabClose = function(assert, done) { 1.328 + let testName = "testTabClose"; 1.329 + let url = "data:text/html;charset=utf-8," + testName; 1.330 + 1.331 + assert.notEqual(tabs.activeTab.url, url, "tab is not the active tab"); 1.332 + tabs.once('ready', function onReady(tab) { 1.333 + assert.equal(tabs.activeTab.url, tab.url, "tab is now the active tab"); 1.334 + assert.equal(url, tab.url, "tab url is the test url"); 1.335 + let secondOnCloseCalled = false; 1.336 + 1.337 + // Bug 699450: Multiple calls to tab.close should not throw 1.338 + tab.close(function() secondOnCloseCalled = true); 1.339 + try { 1.340 + tab.close(function () { 1.341 + assert.notEqual(tabs.activeTab.url, url, "tab is no longer the active tab"); 1.342 + assert.ok(secondOnCloseCalled, 1.343 + "The immediate second call to tab.close happened"); 1.344 + assert.notEqual(tabs.activeTab.url, url, "tab is no longer the active tab"); 1.345 + 1.346 + done(); 1.347 + }); 1.348 + } 1.349 + catch(e) { 1.350 + assert.fail("second call to tab.close() thrown an exception: " + e); 1.351 + } 1.352 + }); 1.353 + 1.354 + tabs.open(url); 1.355 +}; 1.356 + 1.357 +// TEST: tab.move() 1.358 +exports.testTabMove = function(assert, done) { 1.359 + open().then(focus).then(function(window) { 1.360 + let url = "data:text/html;charset=utf-8,foo"; 1.361 + 1.362 + tabs.open({ 1.363 + url: url, 1.364 + onOpen: function(tab) { 1.365 + assert.equal(tab.index, 1, "tab index before move matches"); 1.366 + tab.index = 0; 1.367 + assert.equal(tab.index, 0, "tab index after move matches"); 1.368 + close(window).then(done).then(null, assert.fail); 1.369 + } 1.370 + }); 1.371 + }).then(null, assert.fail); 1.372 +}; 1.373 + 1.374 +// TEST: open tab with default options 1.375 +exports.testOpen = function(assert, done) { 1.376 + let url = "data:text/html;charset=utf-8,default"; 1.377 + tabs.open({ 1.378 + url: url, 1.379 + onReady: function(tab) { 1.380 + assert.equal(tab.url, url, "URL of the new tab matches"); 1.381 + assert.equal(tab.isPinned, false, "The new tab is not pinned"); 1.382 + 1.383 + tab.close(done); 1.384 + } 1.385 + }); 1.386 +}; 1.387 + 1.388 +// TEST: opening a pinned tab 1.389 +exports.testOpenPinned = function(assert, done) { 1.390 + let url = "data:text/html;charset=utf-8,default"; 1.391 + tabs.open({ 1.392 + url: url, 1.393 + isPinned: true, 1.394 + onOpen: function(tab) { 1.395 + assert.equal(tab.isPinned, true, "The new tab is pinned"); 1.396 + tab.close(done); 1.397 + } 1.398 + }); 1.399 +}; 1.400 + 1.401 +// TEST: pin/unpin opened tab 1.402 +exports.testPinUnpin = function(assert, done) { 1.403 + let url = "data:text/html;charset=utf-8,default"; 1.404 + tabs.open({ 1.405 + url: url, 1.406 + inBackground: true, 1.407 + onOpen: function(tab) { 1.408 + tab.pin(); 1.409 + assert.equal(tab.isPinned, true, "The tab was pinned correctly"); 1.410 + tab.unpin(); 1.411 + assert.equal(tab.isPinned, false, "The tab was unpinned correctly"); 1.412 + tab.close(done); 1.413 + } 1.414 + }); 1.415 +} 1.416 + 1.417 +// TEST: open tab in background 1.418 +exports.testInBackground = function(assert, done) { 1.419 + let window = getMostRecentBrowserWindow(); 1.420 + let activeUrl = tabs.activeTab.url; 1.421 + let url = "data:text/html;charset=utf-8,background"; 1.422 + assert.equal(getMostRecentBrowserWindow(), window, "getMostRecentBrowserWindow() matches this window"); 1.423 + tabs.on('ready', function onReady(tab) { 1.424 + tabs.removeListener('ready', onReady); 1.425 + assert.equal(tabs.activeTab.url, activeUrl, "URL of active tab has not changed"); 1.426 + assert.equal(tab.url, url, "URL of the new background tab matches"); 1.427 + assert.equal(getMostRecentBrowserWindow(), window, "a new window was not opened"); 1.428 + assert.notEqual(tabs.activeTab.url, url, "URL of active tab is not the new URL"); 1.429 + tab.close(done); 1.430 + }); 1.431 + 1.432 + tabs.open({ 1.433 + url: url, 1.434 + inBackground: true 1.435 + }); 1.436 +} 1.437 + 1.438 +// TEST: open tab in new window 1.439 +exports.testOpenInNewWindow = function(assert, done) { 1.440 + let startWindowCount = windows().length; 1.441 + 1.442 + let url = "data:text/html;charset=utf-8,testOpenInNewWindow"; 1.443 + tabs.open({ 1.444 + url: url, 1.445 + inNewWindow: true, 1.446 + onReady: function(tab) { 1.447 + let newWindow = getOwnerWindow(tab); 1.448 + assert.equal(windows().length, startWindowCount + 1, "a new window was opened"); 1.449 + 1.450 + onFocus(newWindow).then(function() { 1.451 + assert.equal(getMostRecentBrowserWindow(), newWindow, "new window is active"); 1.452 + assert.equal(tab.url, url, "URL of the new tab matches"); 1.453 + assert.equal(newWindow.content.location, url, "URL of new tab in new window matches"); 1.454 + assert.equal(tabs.activeTab.url, url, "URL of activeTab matches"); 1.455 + 1.456 + return close(newWindow).then(done); 1.457 + }).then(null, assert.fail); 1.458 + } 1.459 + }); 1.460 + 1.461 +} 1.462 + 1.463 +// Test tab.open inNewWindow + onOpen combination 1.464 +exports.testOpenInNewWindowOnOpen = function(assert, done) { 1.465 + let startWindowCount = windows().length; 1.466 + 1.467 + let url = "data:text/html;charset=utf-8,newwindow"; 1.468 + tabs.open({ 1.469 + url: url, 1.470 + inNewWindow: true, 1.471 + onOpen: function(tab) { 1.472 + let newWindow = getOwnerWindow(tab); 1.473 + 1.474 + onFocus(newWindow).then(function() { 1.475 + assert.equal(windows().length, startWindowCount + 1, "a new window was opened"); 1.476 + assert.equal(getMostRecentBrowserWindow(), newWindow, "new window is active"); 1.477 + 1.478 + close(newWindow).then(done).then(null, assert.fail); 1.479 + }); 1.480 + } 1.481 + }); 1.482 +}; 1.483 + 1.484 +// TEST: onOpen event handler 1.485 +exports.testTabsEvent_onOpen = function(assert, done) { 1.486 + open().then(focus).then(window => { 1.487 + let url = "data:text/html;charset=utf-8,1"; 1.488 + let eventCount = 0; 1.489 + 1.490 + // add listener via property assignment 1.491 + function listener1(tab) { 1.492 + eventCount++; 1.493 + }; 1.494 + tabs.on('open', listener1); 1.495 + 1.496 + // add listener via collection add 1.497 + tabs.on('open', function listener2(tab) { 1.498 + assert.equal(++eventCount, 2, "both listeners notified"); 1.499 + tabs.removeListener('open', listener1); 1.500 + tabs.removeListener('open', listener2); 1.501 + close(window).then(done).then(null, assert.fail); 1.502 + }); 1.503 + 1.504 + tabs.open(url); 1.505 + }).then(null, assert.fail); 1.506 +}; 1.507 + 1.508 +// TEST: onClose event handler 1.509 +exports.testTabsEvent_onClose = function(assert, done) { 1.510 + open().then(focus).then(window => { 1.511 + let url = "data:text/html;charset=utf-8,onclose"; 1.512 + let eventCount = 0; 1.513 + 1.514 + // add listener via property assignment 1.515 + function listener1(tab) { 1.516 + eventCount++; 1.517 + } 1.518 + tabs.on('close', listener1); 1.519 + 1.520 + // add listener via collection add 1.521 + tabs.on('close', function listener2(tab) { 1.522 + assert.equal(++eventCount, 2, "both listeners notified"); 1.523 + tabs.removeListener('close', listener1); 1.524 + tabs.removeListener('close', listener2); 1.525 + close(window).then(done).then(null, assert.fail); 1.526 + }); 1.527 + 1.528 + tabs.on('ready', function onReady(tab) { 1.529 + tabs.removeListener('ready', onReady); 1.530 + tab.close(); 1.531 + }); 1.532 + 1.533 + tabs.open(url); 1.534 + }).then(null, assert.fail); 1.535 +}; 1.536 + 1.537 +// TEST: onClose event handler when a window is closed 1.538 +exports.testTabsEvent_onCloseWindow = function(assert, done) { 1.539 + let closeCount = 0; 1.540 + let individualCloseCount = 0; 1.541 + 1.542 + open().then(focus).then(window => { 1.543 + assert.pass('opened a new window'); 1.544 + 1.545 + tabs.on("close", function listener() { 1.546 + if (++closeCount == 4) { 1.547 + tabs.removeListener("close", listener); 1.548 + } 1.549 + }); 1.550 + 1.551 + function endTest() { 1.552 + if (++individualCloseCount < 3) { 1.553 + assert.pass('tab closed ' + individualCloseCount); 1.554 + return; 1.555 + } 1.556 + 1.557 + assert.equal(closeCount, 4, "Correct number of close events received"); 1.558 + assert.equal(individualCloseCount, 3, 1.559 + "Each tab with an attached onClose listener received a close " + 1.560 + "event when the window was closed"); 1.561 + 1.562 + done(); 1.563 + } 1.564 + 1.565 + // One tab is already open with the window 1.566 + let openTabs = 1; 1.567 + function testCasePossiblyLoaded() { 1.568 + if (++openTabs == 4) { 1.569 + window.close(); 1.570 + } 1.571 + assert.pass('tab opened ' + openTabs); 1.572 + } 1.573 + 1.574 + tabs.open({ 1.575 + url: "data:text/html;charset=utf-8,tab2", 1.576 + onOpen: testCasePossiblyLoaded, 1.577 + onClose: endTest 1.578 + }); 1.579 + 1.580 + tabs.open({ 1.581 + url: "data:text/html;charset=utf-8,tab3", 1.582 + onOpen: testCasePossiblyLoaded, 1.583 + onClose: endTest 1.584 + }); 1.585 + 1.586 + tabs.open({ 1.587 + url: "data:text/html;charset=utf-8,tab4", 1.588 + onOpen: testCasePossiblyLoaded, 1.589 + onClose: endTest 1.590 + }); 1.591 + }).then(null, assert.fail); 1.592 +} 1.593 + 1.594 +// TEST: onReady event handler 1.595 +exports.testTabsEvent_onReady = function(assert, done) { 1.596 + open().then(focus).then(window => { 1.597 + let url = "data:text/html;charset=utf-8,onready"; 1.598 + let eventCount = 0; 1.599 + 1.600 + // add listener via property assignment 1.601 + function listener1(tab) { 1.602 + eventCount++; 1.603 + }; 1.604 + tabs.on('ready', listener1); 1.605 + 1.606 + // add listener via collection add 1.607 + tabs.on('ready', function listener2(tab) { 1.608 + assert.equal(++eventCount, 2, "both listeners notified"); 1.609 + tabs.removeListener('ready', listener1); 1.610 + tabs.removeListener('ready', listener2); 1.611 + close(window).then(done); 1.612 + }); 1.613 + 1.614 + tabs.open(url); 1.615 + }).then(null, assert.fail); 1.616 +}; 1.617 + 1.618 +// TEST: onActivate event handler 1.619 +exports.testTabsEvent_onActivate = function(assert, done) { 1.620 + open().then(focus).then(window => { 1.621 + let url = "data:text/html;charset=utf-8,onactivate"; 1.622 + let eventCount = 0; 1.623 + 1.624 + // add listener via property assignment 1.625 + function listener1(tab) { 1.626 + eventCount++; 1.627 + }; 1.628 + tabs.on('activate', listener1); 1.629 + 1.630 + // add listener via collection add 1.631 + tabs.on('activate', function listener2(tab) { 1.632 + assert.equal(++eventCount, 2, "both listeners notified"); 1.633 + tabs.removeListener('activate', listener1); 1.634 + tabs.removeListener('activate', listener2); 1.635 + close(window).then(done).then(null, assert.fail); 1.636 + }); 1.637 + 1.638 + tabs.open(url); 1.639 + }).then(null, assert.fail); 1.640 +}; 1.641 + 1.642 +// onDeactivate event handler 1.643 +exports.testTabsEvent_onDeactivate = function(assert, done) { 1.644 + open().then(focus).then(window => { 1.645 + let url = "data:text/html;charset=utf-8,ondeactivate"; 1.646 + let eventCount = 0; 1.647 + 1.648 + // add listener via property assignment 1.649 + function listener1(tab) { 1.650 + eventCount++; 1.651 + }; 1.652 + tabs.on('deactivate', listener1); 1.653 + 1.654 + // add listener via collection add 1.655 + tabs.on('deactivate', function listener2(tab) { 1.656 + assert.equal(++eventCount, 2, "both listeners notified"); 1.657 + tabs.removeListener('deactivate', listener1); 1.658 + tabs.removeListener('deactivate', listener2); 1.659 + close(window).then(done).then(null, assert.fail); 1.660 + }); 1.661 + 1.662 + tabs.on('open', function onOpen(tab) { 1.663 + tabs.removeListener('open', onOpen); 1.664 + tabs.open("data:text/html;charset=utf-8,foo"); 1.665 + }); 1.666 + 1.667 + tabs.open(url); 1.668 + }).then(null, assert.fail); 1.669 +}; 1.670 + 1.671 +// pinning 1.672 +exports.testTabsEvent_pinning = function(assert, done) { 1.673 + open().then(focus).then(window => { 1.674 + let url = "data:text/html;charset=utf-8,1"; 1.675 + 1.676 + tabs.on('open', function onOpen(tab) { 1.677 + tabs.removeListener('open', onOpen); 1.678 + tab.pin(); 1.679 + }); 1.680 + 1.681 + tabs.on('pinned', function onPinned(tab) { 1.682 + tabs.removeListener('pinned', onPinned); 1.683 + assert.ok(tab.isPinned, "notified tab is pinned"); 1.684 + tab.unpin(); 1.685 + }); 1.686 + 1.687 + tabs.on('unpinned', function onUnpinned(tab) { 1.688 + tabs.removeListener('unpinned', onUnpinned); 1.689 + assert.ok(!tab.isPinned, "notified tab is not pinned"); 1.690 + close(window).then(done).then(null, assert.fail); 1.691 + }); 1.692 + 1.693 + tabs.open(url); 1.694 + }).then(null, assert.fail); 1.695 +}; 1.696 + 1.697 +// TEST: per-tab event handlers 1.698 +exports.testPerTabEvents = function(assert, done) { 1.699 + open().then(focus).then(window => { 1.700 + let eventCount = 0; 1.701 + 1.702 + tabs.open({ 1.703 + url: "data:text/html;charset=utf-8,foo", 1.704 + onOpen: function(tab) { 1.705 + // add listener via property assignment 1.706 + function listener1() { 1.707 + eventCount++; 1.708 + }; 1.709 + tab.on('ready', listener1); 1.710 + 1.711 + // add listener via collection add 1.712 + tab.on('ready', function listener2() { 1.713 + assert.equal(eventCount, 1, "both listeners notified"); 1.714 + tab.removeListener('ready', listener1); 1.715 + tab.removeListener('ready', listener2); 1.716 + close(window).then(done).then(null, assert.fail); 1.717 + }); 1.718 + } 1.719 + }); 1.720 + }).then(null, assert.fail); 1.721 +}; 1.722 + 1.723 +exports.testAttachOnOpen = function (assert, done) { 1.724 + // Take care that attach has to be called on tab ready and not on tab open. 1.725 + open().then(focus).then(window => { 1.726 + tabs.open({ 1.727 + url: "data:text/html;charset=utf-8,foobar", 1.728 + onOpen: function (tab) { 1.729 + let worker = tab.attach({ 1.730 + contentScript: 'self.postMessage(document.location.href); ', 1.731 + onMessage: function (msg) { 1.732 + assert.equal(msg, "about:blank", 1.733 + "Worker document url is about:blank on open"); 1.734 + worker.destroy(); 1.735 + close(window).then(done).then(null, assert.fail); 1.736 + } 1.737 + }); 1.738 + } 1.739 + }); 1.740 + }).then(null, assert.fail); 1.741 +} 1.742 + 1.743 +exports.testAttachOnMultipleDocuments = function (assert, done) { 1.744 + // Example of attach that process multiple tab documents 1.745 + open().then(focus).then(window => { 1.746 + let firstLocation = "data:text/html;charset=utf-8,foobar"; 1.747 + let secondLocation = "data:text/html;charset=utf-8,bar"; 1.748 + let thirdLocation = "data:text/html;charset=utf-8,fox"; 1.749 + let onReadyCount = 0; 1.750 + let worker1 = null; 1.751 + let worker2 = null; 1.752 + let detachEventCount = 0; 1.753 + 1.754 + tabs.open({ 1.755 + url: firstLocation, 1.756 + onReady: function (tab) { 1.757 + onReadyCount++; 1.758 + if (onReadyCount == 1) { 1.759 + worker1 = tab.attach({ 1.760 + contentScript: 'self.on("message", ' + 1.761 + ' function () self.postMessage(document.location.href)' + 1.762 + ');', 1.763 + onMessage: function (msg) { 1.764 + assert.equal(msg, firstLocation, 1.765 + "Worker url is equal to the 1st document"); 1.766 + tab.url = secondLocation; 1.767 + }, 1.768 + onDetach: function () { 1.769 + detachEventCount++; 1.770 + assert.pass("Got worker1 detach event"); 1.771 + assert.throws(function () { 1.772 + worker1.postMessage("ex-1"); 1.773 + }, 1.774 + /Couldn't find the worker/, 1.775 + "postMessage throw because worker1 is destroyed"); 1.776 + checkEnd(); 1.777 + } 1.778 + }); 1.779 + worker1.postMessage("new-doc-1"); 1.780 + } 1.781 + else if (onReadyCount == 2) { 1.782 + 1.783 + worker2 = tab.attach({ 1.784 + contentScript: 'self.on("message", ' + 1.785 + ' function () self.postMessage(document.location.href)' + 1.786 + ');', 1.787 + onMessage: function (msg) { 1.788 + assert.equal(msg, secondLocation, 1.789 + "Worker url is equal to the 2nd document"); 1.790 + tab.url = thirdLocation; 1.791 + }, 1.792 + onDetach: function () { 1.793 + detachEventCount++; 1.794 + assert.pass("Got worker2 detach event"); 1.795 + assert.throws(function () { 1.796 + worker2.postMessage("ex-2"); 1.797 + }, 1.798 + /Couldn't find the worker/, 1.799 + "postMessage throw because worker2 is destroyed"); 1.800 + checkEnd(); 1.801 + } 1.802 + }); 1.803 + worker2.postMessage("new-doc-2"); 1.804 + } 1.805 + else if (onReadyCount == 3) { 1.806 + tab.close(); 1.807 + } 1.808 + } 1.809 + }); 1.810 + 1.811 + function checkEnd() { 1.812 + if (detachEventCount != 2) 1.813 + return; 1.814 + 1.815 + assert.pass("Got all detach events"); 1.816 + 1.817 + close(window).then(done).then(null, assert.fail); 1.818 + } 1.819 + }).then(null, assert.fail); 1.820 +} 1.821 + 1.822 + 1.823 +exports.testAttachWrappers = function (assert, done) { 1.824 + // Check that content script has access to wrapped values by default 1.825 + open().then(focus).then(window => { 1.826 + let document = "data:text/html;charset=utf-8,<script>var globalJSVar = true; " + 1.827 + " document.getElementById = 3;</script>"; 1.828 + let count = 0; 1.829 + 1.830 + tabs.open({ 1.831 + url: document, 1.832 + onReady: function (tab) { 1.833 + let worker = tab.attach({ 1.834 + contentScript: 'try {' + 1.835 + ' self.postMessage(!("globalJSVar" in window));' + 1.836 + ' self.postMessage(typeof window.globalJSVar == "undefined");' + 1.837 + '} catch(e) {' + 1.838 + ' self.postMessage(e.message);' + 1.839 + '}', 1.840 + onMessage: function (msg) { 1.841 + assert.equal(msg, true, "Worker has wrapped objects ("+count+")"); 1.842 + if (count++ == 1) 1.843 + close(window).then(done).then(null, assert.fail); 1.844 + } 1.845 + }); 1.846 + } 1.847 + }); 1.848 + }).then(null, assert.fail); 1.849 +} 1.850 + 1.851 +/* 1.852 +// We do not offer unwrapped access to DOM since bug 601295 landed 1.853 +// See 660780 to track progress of unwrap feature 1.854 +exports.testAttachUnwrapped = function (assert, done) { 1.855 + // Check that content script has access to unwrapped values through unsafeWindow 1.856 + openBrowserWindow(function(window, browser) { 1.857 + let document = "data:text/html;charset=utf-8,<script>var globalJSVar=true;</script>"; 1.858 + let count = 0; 1.859 + 1.860 + tabs.open({ 1.861 + url: document, 1.862 + onReady: function (tab) { 1.863 + let worker = tab.attach({ 1.864 + contentScript: 'try {' + 1.865 + ' self.postMessage(unsafeWindow.globalJSVar);' + 1.866 + '} catch(e) {' + 1.867 + ' self.postMessage(e.message);' + 1.868 + '}', 1.869 + onMessage: function (msg) { 1.870 + assert.equal(msg, true, "Worker has access to javascript content globals ("+count+")"); 1.871 + close(window).then(done); 1.872 + } 1.873 + }); 1.874 + } 1.875 + }); 1.876 + 1.877 + }); 1.878 +} 1.879 +*/ 1.880 + 1.881 +exports['test window focus changes active tab'] = function(assert, done) { 1.882 + let url1 = "data:text/html;charset=utf-8," + encodeURIComponent("test window focus changes active tab</br><h1>Window #1"); 1.883 + 1.884 + let win1 = openBrowserWindow(function() { 1.885 + assert.pass("window 1 is open"); 1.886 + 1.887 + let win2 = openBrowserWindow(function() { 1.888 + assert.pass("window 2 is open"); 1.889 + 1.890 + focus(win2).then(function() { 1.891 + tabs.on("activate", function onActivate(tab) { 1.892 + tabs.removeListener("activate", onActivate); 1.893 + assert.pass("activate was called on windows focus change."); 1.894 + assert.equal(tab.url, url1, 'the activated tab url is correct'); 1.895 + 1.896 + return close(win2).then(function() { 1.897 + assert.pass('window 2 was closed'); 1.898 + return close(win1); 1.899 + }).then(done).then(null, assert.fail); 1.900 + }); 1.901 + 1.902 + win1.focus(); 1.903 + }); 1.904 + }, "data:text/html;charset=utf-8,test window focus changes active tab</br><h1>Window #2"); 1.905 + }, url1); 1.906 +}; 1.907 + 1.908 +exports['test ready event on new window tab'] = function(assert, done) { 1.909 + let uri = encodeURI("data:text/html;charset=utf-8,Waiting for ready event!"); 1.910 + 1.911 + require("sdk/tabs").on("ready", function onReady(tab) { 1.912 + if (tab.url === uri) { 1.913 + require("sdk/tabs").removeListener("ready", onReady); 1.914 + assert.pass("ready event was emitted"); 1.915 + close(window).then(done).then(null, assert.fail); 1.916 + } 1.917 + }); 1.918 + 1.919 + let window = openBrowserWindow(function(){}, uri); 1.920 +}; 1.921 + 1.922 +exports['test unique tab ids'] = function(assert, done) { 1.923 + var windows = require('sdk/windows').browserWindows; 1.924 + var { all, defer } = require('sdk/core/promise'); 1.925 + 1.926 + function openWindow() { 1.927 + let deferred = defer(); 1.928 + let win = windows.open({ 1.929 + url: "data:text/html;charset=utf-8,<html>foo</html>", 1.930 + }); 1.931 + 1.932 + win.on('open', function(window) { 1.933 + assert.ok(window.tabs.length); 1.934 + assert.ok(window.tabs.activeTab); 1.935 + assert.ok(window.tabs.activeTab.id); 1.936 + deferred.resolve({ 1.937 + id: window.tabs.activeTab.id, 1.938 + win: win 1.939 + }); 1.940 + }); 1.941 + 1.942 + return deferred.promise; 1.943 + } 1.944 + 1.945 + var one = openWindow(), two = openWindow(); 1.946 + all([one, two]).then(function(results) { 1.947 + assert.notEqual(results[0].id, results[1].id, "tab Ids should not be equal."); 1.948 + results[0].win.close(); 1.949 + results[1].win.close(); 1.950 + done(); 1.951 + }); 1.952 +} 1.953 + 1.954 +// related to Bug 671305 1.955 +exports.testOnLoadEventWithDOM = function(assert, done) { 1.956 + let count = 0; 1.957 + let title = 'testOnLoadEventWithDOM'; 1.958 + 1.959 + // open a about: url 1.960 + tabs.open({ 1.961 + url: 'data:text/html;charset=utf-8,<title>' + title + '</title>', 1.962 + inBackground: true, 1.963 + onLoad: function(tab) { 1.964 + assert.equal(tab.title, title, 'tab passed in as arg, load called'); 1.965 + 1.966 + if (++count > 1) { 1.967 + assert.pass('onLoad event called on reload'); 1.968 + tab.close(done); 1.969 + } 1.970 + else { 1.971 + assert.pass('first onLoad event occured'); 1.972 + tab.reload(); 1.973 + } 1.974 + } 1.975 + }); 1.976 +}; 1.977 + 1.978 +// related to Bug 671305 1.979 +exports.testOnLoadEventWithImage = function(assert, done) { 1.980 + let count = 0; 1.981 + 1.982 + tabs.open({ 1.983 + url: fixtures.url('Firefox.jpg'), 1.984 + inBackground: true, 1.985 + onLoad: function(tab) { 1.986 + if (++count > 1) { 1.987 + assert.pass('onLoad event called on reload with image'); 1.988 + tab.close(done); 1.989 + } 1.990 + else { 1.991 + assert.pass('first onLoad event occured'); 1.992 + tab.reload(); 1.993 + } 1.994 + } 1.995 + }); 1.996 +}; 1.997 + 1.998 +exports.testFaviconGetterDeprecation = function (assert, done) { 1.999 + setPref(DEPRECATE_PREF, true); 1.1000 + const { LoaderWithHookedConsole } = require("sdk/test/loader"); 1.1001 + let { loader, messages } = LoaderWithHookedConsole(module); 1.1002 + let tabs = loader.require('sdk/tabs'); 1.1003 + 1.1004 + tabs.open({ 1.1005 + url: 'data:text/html;charset=utf-8,', 1.1006 + onOpen: function (tab) { 1.1007 + let favicon = tab.favicon; 1.1008 + assert.ok(messages.length === 1, 'only one error is dispatched'); 1.1009 + assert.ok(messages[0].type, 'error', 'the console message is an error'); 1.1010 + 1.1011 + let msg = messages[0].msg; 1.1012 + assert.ok(msg.indexOf('tab.favicon is deprecated') !== -1, 1.1013 + 'message contains the given message'); 1.1014 + tab.close(done); 1.1015 + loader.unload(); 1.1016 + } 1.1017 + }); 1.1018 +} 1.1019 + 1.1020 +/******************* helpers *********************/ 1.1021 + 1.1022 +// Utility function to open a new browser window. 1.1023 +function openBrowserWindow(callback, url) { 1.1024 + let ww = Cc["@mozilla.org/embedcomp/window-watcher;1"]. 1.1025 + getService(Ci.nsIWindowWatcher); 1.1026 + let urlString = Cc["@mozilla.org/supports-string;1"]. 1.1027 + createInstance(Ci.nsISupportsString); 1.1028 + urlString.data = url; 1.1029 + let window = ww.openWindow(null, "chrome://browser/content/browser.xul", 1.1030 + "_blank", "chrome,all,dialog=no", urlString); 1.1031 + 1.1032 + if (callback) { 1.1033 + window.addEventListener("load", function onLoad(event) { 1.1034 + if (event.target && event.target.defaultView == window) { 1.1035 + window.removeEventListener("load", onLoad, true); 1.1036 + let browsers = window.document.getElementsByTagName("tabbrowser"); 1.1037 + try { 1.1038 + setTimeout(function () { 1.1039 + callback(window, browsers[0]); 1.1040 + }, 10); 1.1041 + } 1.1042 + catch (e) { 1.1043 + console.exception(e); 1.1044 + } 1.1045 + } 1.1046 + }, true); 1.1047 + } 1.1048 + 1.1049 + return window; 1.1050 +} 1.1051 + 1.1052 +require('sdk/test').run(exports);