michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: 'use strict'; michael@0: michael@0: const { Cc, Ci } = require('chrome'); michael@0: const { Loader } = require('sdk/test/loader'); michael@0: const { setTimeout } = require('sdk/timers'); michael@0: const { getOwnerWindow } = require('sdk/private-browsing/window/utils'); michael@0: const { windows, onFocus, getMostRecentBrowserWindow } = require('sdk/window/utils'); michael@0: const { open, focus, close } = require('sdk/window/helpers'); michael@0: const tabs = require('sdk/tabs'); michael@0: const { browserWindows } = require('sdk/windows'); michael@0: const { set: setPref } = require("sdk/preferences/service"); michael@0: const DEPRECATE_PREF = "devtools.errorconsole.deprecation_warnings"; michael@0: const fixtures = require("../fixtures"); michael@0: michael@0: // Bug 682681 - tab.title should never be empty michael@0: exports.testBug682681_aboutURI = function(assert, done) { michael@0: let url = 'chrome://browser/locale/tabbrowser.properties'; michael@0: let stringBundle = Cc["@mozilla.org/intl/stringbundle;1"]. michael@0: getService(Ci.nsIStringBundleService). michael@0: createBundle(url); michael@0: let emptyTabTitle = stringBundle.GetStringFromName('tabs.emptyTabTitle'); michael@0: michael@0: tabs.on('ready', function onReady(tab) { michael@0: tabs.removeListener('ready', onReady); michael@0: michael@0: assert.equal(tab.title, michael@0: emptyTabTitle, michael@0: "title of about: tab is not blank"); michael@0: michael@0: tab.close(done); michael@0: }); michael@0: michael@0: // open a about: url michael@0: tabs.open({ michael@0: url: "about:blank", michael@0: inBackground: true michael@0: }); michael@0: }; michael@0: michael@0: // related to Bug 682681 michael@0: exports.testTitleForDataURI = function(assert, done) { michael@0: tabs.open({ michael@0: url: "data:text/html;charset=utf-8,tab", michael@0: inBackground: true, michael@0: onReady: function(tab) { michael@0: assert.equal(tab.title, "tab", "data: title is not Connecting..."); michael@0: tab.close(done); michael@0: } michael@0: }); michael@0: }; michael@0: michael@0: // TEST: 'BrowserWindow' instance creation on tab 'activate' event michael@0: // See bug 648244: there was a infinite loop. michael@0: exports.testBrowserWindowCreationOnActivate = function(assert, done) { michael@0: let windows = require("sdk/windows").browserWindows; michael@0: let gotActivate = false; michael@0: michael@0: tabs.once('activate', function onActivate(eventTab) { michael@0: assert.ok(windows.activeWindow, "Is able to fetch activeWindow"); michael@0: gotActivate = true; michael@0: }); michael@0: michael@0: open().then(function(window) { michael@0: assert.ok(gotActivate, "Received activate event"); michael@0: return close(window); michael@0: }).then(done).then(null, assert.fail); michael@0: } michael@0: michael@0: // TEST: tab unloader michael@0: exports.testAutomaticDestroyEventOpen = function(assert, done) { michael@0: let called = false; michael@0: let loader = Loader(module); michael@0: let tabs2 = loader.require("sdk/tabs"); michael@0: tabs2.on('open', _ => called = true); michael@0: michael@0: // Fire a tab event and ensure that the destroyed tab is inactive michael@0: tabs.once('open', tab => { michael@0: setTimeout(_ => { michael@0: assert.ok(!called, "Unloaded tab module is destroyed and inactive"); michael@0: tab.close(done); michael@0: }); michael@0: }); michael@0: michael@0: loader.unload(); michael@0: tabs.open("data:text/html;charset=utf-8,testAutomaticDestroyEventOpen"); michael@0: }; michael@0: michael@0: exports.testAutomaticDestroyEventActivate = function(assert, done) { michael@0: let called = false; michael@0: let loader = Loader(module); michael@0: let tabs2 = loader.require("sdk/tabs"); michael@0: tabs2.on('activate', _ => called = true); michael@0: michael@0: // Fire a tab event and ensure that the destroyed tab is inactive michael@0: tabs.once('activate', tab => { michael@0: setTimeout(_ => { michael@0: assert.ok(!called, "Unloaded tab module is destroyed and inactive"); michael@0: tab.close(done); michael@0: }); michael@0: }); michael@0: michael@0: loader.unload(); michael@0: tabs.open("data:text/html;charset=utf-8,testAutomaticDestroyEventActivate"); michael@0: }; michael@0: michael@0: exports.testAutomaticDestroyEventDeactivate = function(assert, done) { michael@0: let called = false; michael@0: let currentTab = tabs.activeTab; michael@0: let loader = Loader(module); michael@0: let tabs2 = loader.require("sdk/tabs"); michael@0: michael@0: tabs.open({ michael@0: url: "data:text/html;charset=utf-8,testAutomaticDestroyEventDeactivate", michael@0: onActivate: _ => setTimeout(_ => { michael@0: tabs2.on('deactivate', _ => called = true); michael@0: michael@0: // Fire a tab event and ensure that the destroyed tab is inactive michael@0: tabs.once('deactivate', tab => { michael@0: setTimeout(_ => { michael@0: assert.ok(!called, "Unloaded tab module is destroyed and inactive"); michael@0: tab.close(done); michael@0: }); michael@0: }); michael@0: michael@0: loader.unload(); michael@0: currentTab.activate(); michael@0: }) michael@0: }); michael@0: }; michael@0: michael@0: exports.testAutomaticDestroyEventClose = function(assert, done) { michael@0: let called = false; michael@0: let loader = Loader(module); michael@0: let tabs2 = loader.require("sdk/tabs"); michael@0: michael@0: tabs.open({ michael@0: url: "data:text/html;charset=utf-8,testAutomaticDestroyEventClose", michael@0: onReady: tab => { michael@0: tabs2.on('close', _ => called = true); michael@0: michael@0: // Fire a tab event and ensure that the destroyed tab is inactive michael@0: tabs.once('close', tab => { michael@0: setTimeout(_ => { michael@0: assert.ok(!called, "Unloaded tab module is destroyed and inactive"); michael@0: done(); michael@0: }); michael@0: }); michael@0: michael@0: loader.unload(); michael@0: tab.close(); michael@0: } michael@0: }); michael@0: }; michael@0: michael@0: exports.testTabPropertiesInNewWindow = function(assert, done) { michael@0: let warning = "DEPRECATED: tab.favicon is deprecated, please use require(\"sdk/places/favicon\").getFavicon instead.\n" michael@0: const { LoaderWithFilteredConsole } = require("sdk/test/loader"); michael@0: let loader = LoaderWithFilteredConsole(module, function(type, message) { michael@0: if (type == "error" && message.substring(0, warning.length) == warning) michael@0: return false; michael@0: return true; michael@0: }); michael@0: michael@0: let tabs = loader.require('sdk/tabs'); michael@0: let { getOwnerWindow } = loader.require('sdk/private-browsing/window/utils'); michael@0: michael@0: let count = 0; michael@0: function onReadyOrLoad (tab) { michael@0: if (count++) { michael@0: close(getOwnerWindow(tab)).then(done).then(null, assert.fail); michael@0: } michael@0: } michael@0: michael@0: let url = "data:text/html;charset=utf-8,foofoo"; michael@0: tabs.open({ michael@0: inNewWindow: true, michael@0: url: url, michael@0: onReady: function(tab) { michael@0: assert.equal(tab.title, "foo", "title of the new tab matches"); michael@0: assert.equal(tab.url, url, "URL of the new tab matches"); michael@0: assert.ok(tab.favicon, "favicon of the new tab is not empty"); michael@0: assert.equal(tab.style, null, "style of the new tab matches"); michael@0: assert.equal(tab.index, 0, "index of the new tab matches"); michael@0: assert.notEqual(tab.getThumbnail(), null, "thumbnail of the new tab matches"); michael@0: assert.notEqual(tab.id, null, "a tab object always has an id property."); michael@0: michael@0: onReadyOrLoad(tab); michael@0: }, michael@0: onLoad: function(tab) { michael@0: assert.equal(tab.title, "foo", "title of the new tab matches"); michael@0: assert.equal(tab.url, url, "URL of the new tab matches"); michael@0: assert.ok(tab.favicon, "favicon of the new tab is not empty"); michael@0: assert.equal(tab.style, null, "style of the new tab matches"); michael@0: assert.equal(tab.index, 0, "index of the new tab matches"); michael@0: assert.notEqual(tab.getThumbnail(), null, "thumbnail of the new tab matches"); michael@0: assert.notEqual(tab.id, null, "a tab object always has an id property."); michael@0: michael@0: onReadyOrLoad(tab); michael@0: } michael@0: }); michael@0: }; michael@0: michael@0: exports.testTabPropertiesInSameWindow = function(assert, done) { michael@0: let warning = "DEPRECATED: tab.favicon is deprecated, please use require(\"sdk/places/favicon\").getFavicon instead.\n" michael@0: const { LoaderWithFilteredConsole } = require("sdk/test/loader"); michael@0: let loader = LoaderWithFilteredConsole(module, function(type, message) { michael@0: if (type == "error" && message.substring(0, warning.length) == warning) michael@0: return false; michael@0: return true; michael@0: }); michael@0: michael@0: let tabs = loader.require('sdk/tabs'); michael@0: michael@0: // Get current count of tabs so we know the index of the michael@0: // new tab, bug 893846 michael@0: let tabCount = tabs.length; michael@0: let count = 0; michael@0: function onReadyOrLoad (tab) { michael@0: if (count++) { michael@0: tab.close(done); michael@0: } michael@0: } michael@0: michael@0: let url = "data:text/html;charset=utf-8,foofoo"; michael@0: tabs.open({ michael@0: url: url, michael@0: onReady: function(tab) { michael@0: assert.equal(tab.title, "foo", "title of the new tab matches"); michael@0: assert.equal(tab.url, url, "URL of the new tab matches"); michael@0: assert.ok(tab.favicon, "favicon of the new tab is not empty"); michael@0: assert.equal(tab.style, null, "style of the new tab matches"); michael@0: assert.equal(tab.index, tabCount, "index of the new tab matches"); michael@0: assert.notEqual(tab.getThumbnail(), null, "thumbnail of the new tab matches"); michael@0: assert.notEqual(tab.id, null, "a tab object always has an id property."); michael@0: michael@0: onReadyOrLoad(tab); michael@0: }, michael@0: onLoad: function(tab) { michael@0: assert.equal(tab.title, "foo", "title of the new tab matches"); michael@0: assert.equal(tab.url, url, "URL of the new tab matches"); michael@0: assert.ok(tab.favicon, "favicon of the new tab is not empty"); michael@0: assert.equal(tab.style, null, "style of the new tab matches"); michael@0: assert.equal(tab.index, tabCount, "index of the new tab matches"); michael@0: assert.notEqual(tab.getThumbnail(), null, "thumbnail of the new tab matches"); michael@0: assert.notEqual(tab.id, null, "a tab object always has an id property."); michael@0: michael@0: onReadyOrLoad(tab); michael@0: } michael@0: }); michael@0: }; michael@0: michael@0: // TEST: tab properties michael@0: exports.testTabContentTypeAndReload = function(assert, done) { michael@0: open().then(focus).then(function(window) { michael@0: let url = "data:text/html;charset=utf-8,foofoo"; michael@0: let urlXML = "data:text/xml;charset=utf-8,bar"; michael@0: tabs.open({ michael@0: url: url, michael@0: onReady: function(tab) { michael@0: if (tab.url === url) { michael@0: assert.equal(tab.contentType, "text/html"); michael@0: tab.url = urlXML; michael@0: } michael@0: else { michael@0: assert.equal(tab.contentType, "text/xml"); michael@0: close(window).then(done).then(null, assert.fail); michael@0: } michael@0: } michael@0: }); michael@0: }); michael@0: }; michael@0: michael@0: // TEST: tabs iterator and length property michael@0: exports.testTabsIteratorAndLength = function(assert, done) { michael@0: open(null, { features: { chrome: true, toolbar: true } }).then(focus).then(function(window) { michael@0: let startCount = 0; michael@0: for each (let t in tabs) startCount++; michael@0: assert.equal(startCount, tabs.length, "length property is correct"); michael@0: let url = "data:text/html;charset=utf-8,default"; michael@0: michael@0: tabs.open(url); michael@0: tabs.open(url); michael@0: tabs.open({ michael@0: url: url, michael@0: onOpen: function(tab) { michael@0: let count = 0; michael@0: for each (let t in tabs) count++; michael@0: assert.equal(count, startCount + 3, "iterated tab count matches"); michael@0: assert.equal(startCount + 3, tabs.length, "iterated tab count matches length property"); michael@0: michael@0: close(window).then(done).then(null, assert.fail); michael@0: } michael@0: }); michael@0: }); michael@0: }; michael@0: michael@0: // TEST: tab.url setter michael@0: exports.testTabLocation = function(assert, done) { michael@0: open().then(focus).then(function(window) { michael@0: let url1 = "data:text/html;charset=utf-8,foo"; michael@0: let url2 = "data:text/html;charset=utf-8,bar"; michael@0: michael@0: tabs.on('ready', function onReady(tab) { michael@0: if (tab.url != url2) michael@0: return; michael@0: tabs.removeListener('ready', onReady); michael@0: assert.pass("tab.load() loaded the correct url"); michael@0: close(window).then(done).then(null, assert.fail); michael@0: }); michael@0: michael@0: tabs.open({ michael@0: url: url1, michael@0: onOpen: function(tab) { michael@0: tab.url = url2 michael@0: } michael@0: }); michael@0: }); michael@0: }; michael@0: michael@0: // TEST: tab.close() michael@0: exports.testTabClose = function(assert, done) { michael@0: let testName = "testTabClose"; michael@0: let url = "data:text/html;charset=utf-8," + testName; michael@0: michael@0: assert.notEqual(tabs.activeTab.url, url, "tab is not the active tab"); michael@0: tabs.once('ready', function onReady(tab) { michael@0: assert.equal(tabs.activeTab.url, tab.url, "tab is now the active tab"); michael@0: assert.equal(url, tab.url, "tab url is the test url"); michael@0: let secondOnCloseCalled = false; michael@0: michael@0: // Bug 699450: Multiple calls to tab.close should not throw michael@0: tab.close(function() secondOnCloseCalled = true); michael@0: try { michael@0: tab.close(function () { michael@0: assert.notEqual(tabs.activeTab.url, url, "tab is no longer the active tab"); michael@0: assert.ok(secondOnCloseCalled, michael@0: "The immediate second call to tab.close happened"); michael@0: assert.notEqual(tabs.activeTab.url, url, "tab is no longer the active tab"); michael@0: michael@0: done(); michael@0: }); michael@0: } michael@0: catch(e) { michael@0: assert.fail("second call to tab.close() thrown an exception: " + e); michael@0: } michael@0: }); michael@0: michael@0: tabs.open(url); michael@0: }; michael@0: michael@0: // TEST: tab.move() michael@0: exports.testTabMove = function(assert, done) { michael@0: open().then(focus).then(function(window) { michael@0: let url = "data:text/html;charset=utf-8,foo"; michael@0: michael@0: tabs.open({ michael@0: url: url, michael@0: onOpen: function(tab) { michael@0: assert.equal(tab.index, 1, "tab index before move matches"); michael@0: tab.index = 0; michael@0: assert.equal(tab.index, 0, "tab index after move matches"); michael@0: close(window).then(done).then(null, assert.fail); michael@0: } michael@0: }); michael@0: }).then(null, assert.fail); michael@0: }; michael@0: michael@0: // TEST: open tab with default options michael@0: exports.testOpen = function(assert, done) { michael@0: let url = "data:text/html;charset=utf-8,default"; michael@0: tabs.open({ michael@0: url: url, michael@0: onReady: function(tab) { michael@0: assert.equal(tab.url, url, "URL of the new tab matches"); michael@0: assert.equal(tab.isPinned, false, "The new tab is not pinned"); michael@0: michael@0: tab.close(done); michael@0: } michael@0: }); michael@0: }; michael@0: michael@0: // TEST: opening a pinned tab michael@0: exports.testOpenPinned = function(assert, done) { michael@0: let url = "data:text/html;charset=utf-8,default"; michael@0: tabs.open({ michael@0: url: url, michael@0: isPinned: true, michael@0: onOpen: function(tab) { michael@0: assert.equal(tab.isPinned, true, "The new tab is pinned"); michael@0: tab.close(done); michael@0: } michael@0: }); michael@0: }; michael@0: michael@0: // TEST: pin/unpin opened tab michael@0: exports.testPinUnpin = function(assert, done) { michael@0: let url = "data:text/html;charset=utf-8,default"; michael@0: tabs.open({ michael@0: url: url, michael@0: inBackground: true, michael@0: onOpen: function(tab) { michael@0: tab.pin(); michael@0: assert.equal(tab.isPinned, true, "The tab was pinned correctly"); michael@0: tab.unpin(); michael@0: assert.equal(tab.isPinned, false, "The tab was unpinned correctly"); michael@0: tab.close(done); michael@0: } michael@0: }); michael@0: } michael@0: michael@0: // TEST: open tab in background michael@0: exports.testInBackground = function(assert, done) { michael@0: let window = getMostRecentBrowserWindow(); michael@0: let activeUrl = tabs.activeTab.url; michael@0: let url = "data:text/html;charset=utf-8,background"; michael@0: assert.equal(getMostRecentBrowserWindow(), window, "getMostRecentBrowserWindow() matches this window"); michael@0: tabs.on('ready', function onReady(tab) { michael@0: tabs.removeListener('ready', onReady); michael@0: assert.equal(tabs.activeTab.url, activeUrl, "URL of active tab has not changed"); michael@0: assert.equal(tab.url, url, "URL of the new background tab matches"); michael@0: assert.equal(getMostRecentBrowserWindow(), window, "a new window was not opened"); michael@0: assert.notEqual(tabs.activeTab.url, url, "URL of active tab is not the new URL"); michael@0: tab.close(done); michael@0: }); michael@0: michael@0: tabs.open({ michael@0: url: url, michael@0: inBackground: true michael@0: }); michael@0: } michael@0: michael@0: // TEST: open tab in new window michael@0: exports.testOpenInNewWindow = function(assert, done) { michael@0: let startWindowCount = windows().length; michael@0: michael@0: let url = "data:text/html;charset=utf-8,testOpenInNewWindow"; michael@0: tabs.open({ michael@0: url: url, michael@0: inNewWindow: true, michael@0: onReady: function(tab) { michael@0: let newWindow = getOwnerWindow(tab); michael@0: assert.equal(windows().length, startWindowCount + 1, "a new window was opened"); michael@0: michael@0: onFocus(newWindow).then(function() { michael@0: assert.equal(getMostRecentBrowserWindow(), newWindow, "new window is active"); michael@0: assert.equal(tab.url, url, "URL of the new tab matches"); michael@0: assert.equal(newWindow.content.location, url, "URL of new tab in new window matches"); michael@0: assert.equal(tabs.activeTab.url, url, "URL of activeTab matches"); michael@0: michael@0: return close(newWindow).then(done); michael@0: }).then(null, assert.fail); michael@0: } michael@0: }); michael@0: michael@0: } michael@0: michael@0: // Test tab.open inNewWindow + onOpen combination michael@0: exports.testOpenInNewWindowOnOpen = function(assert, done) { michael@0: let startWindowCount = windows().length; michael@0: michael@0: let url = "data:text/html;charset=utf-8,newwindow"; michael@0: tabs.open({ michael@0: url: url, michael@0: inNewWindow: true, michael@0: onOpen: function(tab) { michael@0: let newWindow = getOwnerWindow(tab); michael@0: michael@0: onFocus(newWindow).then(function() { michael@0: assert.equal(windows().length, startWindowCount + 1, "a new window was opened"); michael@0: assert.equal(getMostRecentBrowserWindow(), newWindow, "new window is active"); michael@0: michael@0: close(newWindow).then(done).then(null, assert.fail); michael@0: }); michael@0: } michael@0: }); michael@0: }; michael@0: michael@0: // TEST: onOpen event handler michael@0: exports.testTabsEvent_onOpen = function(assert, done) { michael@0: open().then(focus).then(window => { michael@0: let url = "data:text/html;charset=utf-8,1"; michael@0: let eventCount = 0; michael@0: michael@0: // add listener via property assignment michael@0: function listener1(tab) { michael@0: eventCount++; michael@0: }; michael@0: tabs.on('open', listener1); michael@0: michael@0: // add listener via collection add michael@0: tabs.on('open', function listener2(tab) { michael@0: assert.equal(++eventCount, 2, "both listeners notified"); michael@0: tabs.removeListener('open', listener1); michael@0: tabs.removeListener('open', listener2); michael@0: close(window).then(done).then(null, assert.fail); michael@0: }); michael@0: michael@0: tabs.open(url); michael@0: }).then(null, assert.fail); michael@0: }; michael@0: michael@0: // TEST: onClose event handler michael@0: exports.testTabsEvent_onClose = function(assert, done) { michael@0: open().then(focus).then(window => { michael@0: let url = "data:text/html;charset=utf-8,onclose"; michael@0: let eventCount = 0; michael@0: michael@0: // add listener via property assignment michael@0: function listener1(tab) { michael@0: eventCount++; michael@0: } michael@0: tabs.on('close', listener1); michael@0: michael@0: // add listener via collection add michael@0: tabs.on('close', function listener2(tab) { michael@0: assert.equal(++eventCount, 2, "both listeners notified"); michael@0: tabs.removeListener('close', listener1); michael@0: tabs.removeListener('close', listener2); michael@0: close(window).then(done).then(null, assert.fail); michael@0: }); michael@0: michael@0: tabs.on('ready', function onReady(tab) { michael@0: tabs.removeListener('ready', onReady); michael@0: tab.close(); michael@0: }); michael@0: michael@0: tabs.open(url); michael@0: }).then(null, assert.fail); michael@0: }; michael@0: michael@0: // TEST: onClose event handler when a window is closed michael@0: exports.testTabsEvent_onCloseWindow = function(assert, done) { michael@0: let closeCount = 0; michael@0: let individualCloseCount = 0; michael@0: michael@0: open().then(focus).then(window => { michael@0: assert.pass('opened a new window'); michael@0: michael@0: tabs.on("close", function listener() { michael@0: if (++closeCount == 4) { michael@0: tabs.removeListener("close", listener); michael@0: } michael@0: }); michael@0: michael@0: function endTest() { michael@0: if (++individualCloseCount < 3) { michael@0: assert.pass('tab closed ' + individualCloseCount); michael@0: return; michael@0: } michael@0: michael@0: assert.equal(closeCount, 4, "Correct number of close events received"); michael@0: assert.equal(individualCloseCount, 3, michael@0: "Each tab with an attached onClose listener received a close " + michael@0: "event when the window was closed"); michael@0: michael@0: done(); michael@0: } michael@0: michael@0: // One tab is already open with the window michael@0: let openTabs = 1; michael@0: function testCasePossiblyLoaded() { michael@0: if (++openTabs == 4) { michael@0: window.close(); michael@0: } michael@0: assert.pass('tab opened ' + openTabs); michael@0: } michael@0: michael@0: tabs.open({ michael@0: url: "data:text/html;charset=utf-8,tab2", michael@0: onOpen: testCasePossiblyLoaded, michael@0: onClose: endTest michael@0: }); michael@0: michael@0: tabs.open({ michael@0: url: "data:text/html;charset=utf-8,tab3", michael@0: onOpen: testCasePossiblyLoaded, michael@0: onClose: endTest michael@0: }); michael@0: michael@0: tabs.open({ michael@0: url: "data:text/html;charset=utf-8,tab4", michael@0: onOpen: testCasePossiblyLoaded, michael@0: onClose: endTest michael@0: }); michael@0: }).then(null, assert.fail); michael@0: } michael@0: michael@0: // TEST: onReady event handler michael@0: exports.testTabsEvent_onReady = function(assert, done) { michael@0: open().then(focus).then(window => { michael@0: let url = "data:text/html;charset=utf-8,onready"; michael@0: let eventCount = 0; michael@0: michael@0: // add listener via property assignment michael@0: function listener1(tab) { michael@0: eventCount++; michael@0: }; michael@0: tabs.on('ready', listener1); michael@0: michael@0: // add listener via collection add michael@0: tabs.on('ready', function listener2(tab) { michael@0: assert.equal(++eventCount, 2, "both listeners notified"); michael@0: tabs.removeListener('ready', listener1); michael@0: tabs.removeListener('ready', listener2); michael@0: close(window).then(done); michael@0: }); michael@0: michael@0: tabs.open(url); michael@0: }).then(null, assert.fail); michael@0: }; michael@0: michael@0: // TEST: onActivate event handler michael@0: exports.testTabsEvent_onActivate = function(assert, done) { michael@0: open().then(focus).then(window => { michael@0: let url = "data:text/html;charset=utf-8,onactivate"; michael@0: let eventCount = 0; michael@0: michael@0: // add listener via property assignment michael@0: function listener1(tab) { michael@0: eventCount++; michael@0: }; michael@0: tabs.on('activate', listener1); michael@0: michael@0: // add listener via collection add michael@0: tabs.on('activate', function listener2(tab) { michael@0: assert.equal(++eventCount, 2, "both listeners notified"); michael@0: tabs.removeListener('activate', listener1); michael@0: tabs.removeListener('activate', listener2); michael@0: close(window).then(done).then(null, assert.fail); michael@0: }); michael@0: michael@0: tabs.open(url); michael@0: }).then(null, assert.fail); michael@0: }; michael@0: michael@0: // onDeactivate event handler michael@0: exports.testTabsEvent_onDeactivate = function(assert, done) { michael@0: open().then(focus).then(window => { michael@0: let url = "data:text/html;charset=utf-8,ondeactivate"; michael@0: let eventCount = 0; michael@0: michael@0: // add listener via property assignment michael@0: function listener1(tab) { michael@0: eventCount++; michael@0: }; michael@0: tabs.on('deactivate', listener1); michael@0: michael@0: // add listener via collection add michael@0: tabs.on('deactivate', function listener2(tab) { michael@0: assert.equal(++eventCount, 2, "both listeners notified"); michael@0: tabs.removeListener('deactivate', listener1); michael@0: tabs.removeListener('deactivate', listener2); michael@0: close(window).then(done).then(null, assert.fail); michael@0: }); michael@0: michael@0: tabs.on('open', function onOpen(tab) { michael@0: tabs.removeListener('open', onOpen); michael@0: tabs.open("data:text/html;charset=utf-8,foo"); michael@0: }); michael@0: michael@0: tabs.open(url); michael@0: }).then(null, assert.fail); michael@0: }; michael@0: michael@0: // pinning michael@0: exports.testTabsEvent_pinning = function(assert, done) { michael@0: open().then(focus).then(window => { michael@0: let url = "data:text/html;charset=utf-8,1"; michael@0: michael@0: tabs.on('open', function onOpen(tab) { michael@0: tabs.removeListener('open', onOpen); michael@0: tab.pin(); michael@0: }); michael@0: michael@0: tabs.on('pinned', function onPinned(tab) { michael@0: tabs.removeListener('pinned', onPinned); michael@0: assert.ok(tab.isPinned, "notified tab is pinned"); michael@0: tab.unpin(); michael@0: }); michael@0: michael@0: tabs.on('unpinned', function onUnpinned(tab) { michael@0: tabs.removeListener('unpinned', onUnpinned); michael@0: assert.ok(!tab.isPinned, "notified tab is not pinned"); michael@0: close(window).then(done).then(null, assert.fail); michael@0: }); michael@0: michael@0: tabs.open(url); michael@0: }).then(null, assert.fail); michael@0: }; michael@0: michael@0: // TEST: per-tab event handlers michael@0: exports.testPerTabEvents = function(assert, done) { michael@0: open().then(focus).then(window => { michael@0: let eventCount = 0; michael@0: michael@0: tabs.open({ michael@0: url: "data:text/html;charset=utf-8,foo", michael@0: onOpen: function(tab) { michael@0: // add listener via property assignment michael@0: function listener1() { michael@0: eventCount++; michael@0: }; michael@0: tab.on('ready', listener1); michael@0: michael@0: // add listener via collection add michael@0: tab.on('ready', function listener2() { michael@0: assert.equal(eventCount, 1, "both listeners notified"); michael@0: tab.removeListener('ready', listener1); michael@0: tab.removeListener('ready', listener2); michael@0: close(window).then(done).then(null, assert.fail); michael@0: }); michael@0: } michael@0: }); michael@0: }).then(null, assert.fail); michael@0: }; michael@0: michael@0: exports.testAttachOnOpen = function (assert, done) { michael@0: // Take care that attach has to be called on tab ready and not on tab open. michael@0: open().then(focus).then(window => { michael@0: tabs.open({ michael@0: url: "data:text/html;charset=utf-8,foobar", michael@0: onOpen: function (tab) { michael@0: let worker = tab.attach({ michael@0: contentScript: 'self.postMessage(document.location.href); ', michael@0: onMessage: function (msg) { michael@0: assert.equal(msg, "about:blank", michael@0: "Worker document url is about:blank on open"); michael@0: worker.destroy(); michael@0: close(window).then(done).then(null, assert.fail); michael@0: } michael@0: }); michael@0: } michael@0: }); michael@0: }).then(null, assert.fail); michael@0: } michael@0: michael@0: exports.testAttachOnMultipleDocuments = function (assert, done) { michael@0: // Example of attach that process multiple tab documents michael@0: open().then(focus).then(window => { michael@0: let firstLocation = "data:text/html;charset=utf-8,foobar"; michael@0: let secondLocation = "data:text/html;charset=utf-8,bar"; michael@0: let thirdLocation = "data:text/html;charset=utf-8,fox"; michael@0: let onReadyCount = 0; michael@0: let worker1 = null; michael@0: let worker2 = null; michael@0: let detachEventCount = 0; michael@0: michael@0: tabs.open({ michael@0: url: firstLocation, michael@0: onReady: function (tab) { michael@0: onReadyCount++; michael@0: if (onReadyCount == 1) { michael@0: worker1 = tab.attach({ michael@0: contentScript: 'self.on("message", ' + michael@0: ' function () self.postMessage(document.location.href)' + michael@0: ');', michael@0: onMessage: function (msg) { michael@0: assert.equal(msg, firstLocation, michael@0: "Worker url is equal to the 1st document"); michael@0: tab.url = secondLocation; michael@0: }, michael@0: onDetach: function () { michael@0: detachEventCount++; michael@0: assert.pass("Got worker1 detach event"); michael@0: assert.throws(function () { michael@0: worker1.postMessage("ex-1"); michael@0: }, michael@0: /Couldn't find the worker/, michael@0: "postMessage throw because worker1 is destroyed"); michael@0: checkEnd(); michael@0: } michael@0: }); michael@0: worker1.postMessage("new-doc-1"); michael@0: } michael@0: else if (onReadyCount == 2) { michael@0: michael@0: worker2 = tab.attach({ michael@0: contentScript: 'self.on("message", ' + michael@0: ' function () self.postMessage(document.location.href)' + michael@0: ');', michael@0: onMessage: function (msg) { michael@0: assert.equal(msg, secondLocation, michael@0: "Worker url is equal to the 2nd document"); michael@0: tab.url = thirdLocation; michael@0: }, michael@0: onDetach: function () { michael@0: detachEventCount++; michael@0: assert.pass("Got worker2 detach event"); michael@0: assert.throws(function () { michael@0: worker2.postMessage("ex-2"); michael@0: }, michael@0: /Couldn't find the worker/, michael@0: "postMessage throw because worker2 is destroyed"); michael@0: checkEnd(); michael@0: } michael@0: }); michael@0: worker2.postMessage("new-doc-2"); michael@0: } michael@0: else if (onReadyCount == 3) { michael@0: tab.close(); michael@0: } michael@0: } michael@0: }); michael@0: michael@0: function checkEnd() { michael@0: if (detachEventCount != 2) michael@0: return; michael@0: michael@0: assert.pass("Got all detach events"); michael@0: michael@0: close(window).then(done).then(null, assert.fail); michael@0: } michael@0: }).then(null, assert.fail); michael@0: } michael@0: michael@0: michael@0: exports.testAttachWrappers = function (assert, done) { michael@0: // Check that content script has access to wrapped values by default michael@0: open().then(focus).then(window => { michael@0: let document = "data:text/html;charset=utf-8,"; michael@0: let count = 0; michael@0: michael@0: tabs.open({ michael@0: url: document, michael@0: onReady: function (tab) { michael@0: let worker = tab.attach({ michael@0: contentScript: 'try {' + michael@0: ' self.postMessage(!("globalJSVar" in window));' + michael@0: ' self.postMessage(typeof window.globalJSVar == "undefined");' + michael@0: '} catch(e) {' + michael@0: ' self.postMessage(e.message);' + michael@0: '}', michael@0: onMessage: function (msg) { michael@0: assert.equal(msg, true, "Worker has wrapped objects ("+count+")"); michael@0: if (count++ == 1) michael@0: close(window).then(done).then(null, assert.fail); michael@0: } michael@0: }); michael@0: } michael@0: }); michael@0: }).then(null, assert.fail); michael@0: } michael@0: michael@0: /* michael@0: // We do not offer unwrapped access to DOM since bug 601295 landed michael@0: // See 660780 to track progress of unwrap feature michael@0: exports.testAttachUnwrapped = function (assert, done) { michael@0: // Check that content script has access to unwrapped values through unsafeWindow michael@0: openBrowserWindow(function(window, browser) { michael@0: let document = "data:text/html;charset=utf-8,"; michael@0: let count = 0; michael@0: michael@0: tabs.open({ michael@0: url: document, michael@0: onReady: function (tab) { michael@0: let worker = tab.attach({ michael@0: contentScript: 'try {' + michael@0: ' self.postMessage(unsafeWindow.globalJSVar);' + michael@0: '} catch(e) {' + michael@0: ' self.postMessage(e.message);' + michael@0: '}', michael@0: onMessage: function (msg) { michael@0: assert.equal(msg, true, "Worker has access to javascript content globals ("+count+")"); michael@0: close(window).then(done); michael@0: } michael@0: }); michael@0: } michael@0: }); michael@0: michael@0: }); michael@0: } michael@0: */ michael@0: michael@0: exports['test window focus changes active tab'] = function(assert, done) { michael@0: let url1 = "data:text/html;charset=utf-8," + encodeURIComponent("test window focus changes active tab

Window #1"); michael@0: michael@0: let win1 = openBrowserWindow(function() { michael@0: assert.pass("window 1 is open"); michael@0: michael@0: let win2 = openBrowserWindow(function() { michael@0: assert.pass("window 2 is open"); michael@0: michael@0: focus(win2).then(function() { michael@0: tabs.on("activate", function onActivate(tab) { michael@0: tabs.removeListener("activate", onActivate); michael@0: assert.pass("activate was called on windows focus change."); michael@0: assert.equal(tab.url, url1, 'the activated tab url is correct'); michael@0: michael@0: return close(win2).then(function() { michael@0: assert.pass('window 2 was closed'); michael@0: return close(win1); michael@0: }).then(done).then(null, assert.fail); michael@0: }); michael@0: michael@0: win1.focus(); michael@0: }); michael@0: }, "data:text/html;charset=utf-8,test window focus changes active tab

Window #2"); michael@0: }, url1); michael@0: }; michael@0: michael@0: exports['test ready event on new window tab'] = function(assert, done) { michael@0: let uri = encodeURI("data:text/html;charset=utf-8,Waiting for ready event!"); michael@0: michael@0: require("sdk/tabs").on("ready", function onReady(tab) { michael@0: if (tab.url === uri) { michael@0: require("sdk/tabs").removeListener("ready", onReady); michael@0: assert.pass("ready event was emitted"); michael@0: close(window).then(done).then(null, assert.fail); michael@0: } michael@0: }); michael@0: michael@0: let window = openBrowserWindow(function(){}, uri); michael@0: }; michael@0: michael@0: exports['test unique tab ids'] = function(assert, done) { michael@0: var windows = require('sdk/windows').browserWindows; michael@0: var { all, defer } = require('sdk/core/promise'); michael@0: michael@0: function openWindow() { michael@0: let deferred = defer(); michael@0: let win = windows.open({ michael@0: url: "data:text/html;charset=utf-8,foo", michael@0: }); michael@0: michael@0: win.on('open', function(window) { michael@0: assert.ok(window.tabs.length); michael@0: assert.ok(window.tabs.activeTab); michael@0: assert.ok(window.tabs.activeTab.id); michael@0: deferred.resolve({ michael@0: id: window.tabs.activeTab.id, michael@0: win: win michael@0: }); michael@0: }); michael@0: michael@0: return deferred.promise; michael@0: } michael@0: michael@0: var one = openWindow(), two = openWindow(); michael@0: all([one, two]).then(function(results) { michael@0: assert.notEqual(results[0].id, results[1].id, "tab Ids should not be equal."); michael@0: results[0].win.close(); michael@0: results[1].win.close(); michael@0: done(); michael@0: }); michael@0: } michael@0: michael@0: // related to Bug 671305 michael@0: exports.testOnLoadEventWithDOM = function(assert, done) { michael@0: let count = 0; michael@0: let title = 'testOnLoadEventWithDOM'; michael@0: michael@0: // open a about: url michael@0: tabs.open({ michael@0: url: 'data:text/html;charset=utf-8,' + title + '', michael@0: inBackground: true, michael@0: onLoad: function(tab) { michael@0: assert.equal(tab.title, title, 'tab passed in as arg, load called'); michael@0: michael@0: if (++count > 1) { michael@0: assert.pass('onLoad event called on reload'); michael@0: tab.close(done); michael@0: } michael@0: else { michael@0: assert.pass('first onLoad event occured'); michael@0: tab.reload(); michael@0: } michael@0: } michael@0: }); michael@0: }; michael@0: michael@0: // related to Bug 671305 michael@0: exports.testOnLoadEventWithImage = function(assert, done) { michael@0: let count = 0; michael@0: michael@0: tabs.open({ michael@0: url: fixtures.url('Firefox.jpg'), michael@0: inBackground: true, michael@0: onLoad: function(tab) { michael@0: if (++count > 1) { michael@0: assert.pass('onLoad event called on reload with image'); michael@0: tab.close(done); michael@0: } michael@0: else { michael@0: assert.pass('first onLoad event occured'); michael@0: tab.reload(); michael@0: } michael@0: } michael@0: }); michael@0: }; michael@0: michael@0: exports.testFaviconGetterDeprecation = function (assert, done) { michael@0: setPref(DEPRECATE_PREF, true); michael@0: const { LoaderWithHookedConsole } = require("sdk/test/loader"); michael@0: let { loader, messages } = LoaderWithHookedConsole(module); michael@0: let tabs = loader.require('sdk/tabs'); michael@0: michael@0: tabs.open({ michael@0: url: 'data:text/html;charset=utf-8,', michael@0: onOpen: function (tab) { michael@0: let favicon = tab.favicon; michael@0: assert.ok(messages.length === 1, 'only one error is dispatched'); michael@0: assert.ok(messages[0].type, 'error', 'the console message is an error'); michael@0: michael@0: let msg = messages[0].msg; michael@0: assert.ok(msg.indexOf('tab.favicon is deprecated') !== -1, michael@0: 'message contains the given message'); michael@0: tab.close(done); michael@0: loader.unload(); michael@0: } michael@0: }); michael@0: } michael@0: michael@0: /******************* helpers *********************/ michael@0: michael@0: // Utility function to open a new browser window. michael@0: function openBrowserWindow(callback, url) { michael@0: let ww = Cc["@mozilla.org/embedcomp/window-watcher;1"]. michael@0: getService(Ci.nsIWindowWatcher); michael@0: let urlString = Cc["@mozilla.org/supports-string;1"]. michael@0: createInstance(Ci.nsISupportsString); michael@0: urlString.data = url; michael@0: let window = ww.openWindow(null, "chrome://browser/content/browser.xul", michael@0: "_blank", "chrome,all,dialog=no", urlString); michael@0: michael@0: if (callback) { michael@0: window.addEventListener("load", function onLoad(event) { michael@0: if (event.target && event.target.defaultView == window) { michael@0: window.removeEventListener("load", onLoad, true); michael@0: let browsers = window.document.getElementsByTagName("tabbrowser"); michael@0: try { michael@0: setTimeout(function () { michael@0: callback(window, browsers[0]); michael@0: }, 10); michael@0: } michael@0: catch (e) { michael@0: console.exception(e); michael@0: } michael@0: } michael@0: }, true); michael@0: } michael@0: michael@0: return window; michael@0: } michael@0: michael@0: require('sdk/test').run(exports);