Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Errors

/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
// A single monitor for the tests. If it receives any
// incognito data in event listeners it will fail.
let monitor;
add_task(async function startup() {
monitor = await startIncognitoMonitorExtension();
});
registerCleanupFunction(async function finish() {
await monitor.unload();
});
// Test tab events from private windows, the monitor above will fail
// if it receives any.
add_task(async function test_tab_events_incognito_monitored() {
async function background() {
let incognito = true;
let events = [];
let eventPromise;
let checkEvents = () => {
if (eventPromise && events.length >= eventPromise.names.length) {
eventPromise.resolve();
}
};
browser.tabs.onCreated.addListener(tab => {
events.push({ type: "onCreated", tab });
checkEvents();
});
browser.tabs.onAttached.addListener((tabId, info) => {
events.push(Object.assign({ type: "onAttached", tabId }, info));
checkEvents();
});
browser.tabs.onDetached.addListener((tabId, info) => {
events.push(Object.assign({ type: "onDetached", tabId }, info));
checkEvents();
});
browser.tabs.onRemoved.addListener((tabId, info) => {
events.push(Object.assign({ type: "onRemoved", tabId }, info));
checkEvents();
});
browser.tabs.onMoved.addListener((tabId, info) => {
events.push(Object.assign({ type: "onMoved", tabId }, info));
checkEvents();
});
async function expectEvents(names) {
browser.test.log(`Expecting events: ${names.join(", ")}`);
await new Promise(resolve => {
eventPromise = { names, resolve };
checkEvents();
});
browser.test.assertEq(
names.length,
events.length,
"Got expected number of events"
);
for (let [i, name] of names.entries()) {
browser.test.assertEq(
name,
i in events && events[i].type,
`Got expected ${name} event`
);
}
return events.splice(0);
}
try {
let firstWindow = await browser.windows.create({
url: "about:blank",
incognito,
});
let otherWindow = await browser.windows.create({
url: "about:blank",
incognito,
});
let windowId = firstWindow.id;
let otherWindowId = otherWindow.id;
// Wait for a tab in each window
await expectEvents(["onCreated", "onCreated"]);
let initialTab = (
await browser.tabs.query({
active: true,
windowId: otherWindowId,
})
)[0];
browser.test.log("Create tab in window 1");
let tab = await browser.tabs.create({
windowId,
index: 0,
url: "about:blank",
});
let oldIndex = tab.index;
browser.test.assertEq(0, oldIndex, "Tab has the expected index");
browser.test.assertEq(tab.incognito, incognito, "Tab is incognito");
let [created] = await expectEvents(["onCreated"]);
browser.test.assertEq(tab.id, created.tab.id, "Got expected tab ID");
browser.test.assertEq(
oldIndex,
created.tab.index,
"Got expected tab index"
);
browser.test.log("Move tab to window 2");
await browser.tabs.move([tab.id], { windowId: otherWindowId, index: 0 });
let [detached, attached] = await expectEvents([
"onDetached",
"onAttached",
]);
browser.test.assertEq(
tab.id,
detached.tabId,
"Expected onDetached tab ID"
);
browser.test.assertEq(
oldIndex,
detached.oldPosition,
"Expected old index"
);
browser.test.assertEq(
windowId,
detached.oldWindowId,
"Expected old window ID"
);
browser.test.assertEq(
tab.id,
attached.tabId,
"Expected onAttached tab ID"
);
browser.test.assertEq(0, attached.newPosition, "Expected new index");
browser.test.assertEq(
otherWindowId,
attached.newWindowId,
"Expected new window ID"
);
browser.test.log("Move tab within the same window");
let [moved] = await browser.tabs.move([tab.id], { index: 1 });
browser.test.assertEq(1, moved.index, "Expected new index");
[moved] = await expectEvents(["onMoved"]);
browser.test.assertEq(tab.id, moved.tabId, "Expected tab ID");
browser.test.assertEq(0, moved.fromIndex, "Expected old index");
browser.test.assertEq(1, moved.toIndex, "Expected new index");
browser.test.assertEq(
otherWindowId,
moved.windowId,
"Expected window ID"
);
browser.test.log("Remove tab");
await browser.tabs.remove(tab.id);
let [removed] = await expectEvents(["onRemoved"]);
browser.test.assertEq(
tab.id,
removed.tabId,
"Expected removed tab ID for tabs.remove"
);
browser.test.assertEq(
otherWindowId,
removed.windowId,
"Expected removed tab window ID"
);
// Note: We want to test for the actual boolean value false here.
browser.test.assertEq(
false,
removed.isWindowClosing,
"Expected isWindowClosing value"
);
browser.test.log("Close second window");
await browser.windows.remove(otherWindowId);
[removed] = await expectEvents(["onRemoved"]);
browser.test.assertEq(
initialTab.id,
removed.tabId,
"Expected removed tab ID for windows.remove"
);
browser.test.assertEq(
otherWindowId,
removed.windowId,
"Expected removed tab window ID"
);
browser.test.assertEq(
true,
removed.isWindowClosing,
"Expected isWindowClosing value"
);
browser.test.log("Create additional tab in window 1");
tab = await browser.tabs.create({ windowId, url: "about:blank" });
await expectEvents(["onCreated"]);
browser.test.assertEq(tab.incognito, incognito, "Tab is incognito");
browser.test.log("Create a new window, adopting the new tab");
// We have to explicitly wait for the event here, since its timing is
// not predictable.
let promiseAttached = new Promise(resolve => {
browser.tabs.onAttached.addListener(function listener() {
browser.tabs.onAttached.removeListener(listener);
resolve();
});
});
let [window] = await Promise.all([
browser.windows.create({ tabId: tab.id, incognito }),
promiseAttached,
]);
[detached, attached] = await expectEvents(["onDetached", "onAttached"]);
browser.test.assertEq(
tab.id,
detached.tabId,
"Expected onDetached tab ID"
);
browser.test.assertEq(
1,
detached.oldPosition,
"Expected onDetached old index"
);
browser.test.assertEq(
windowId,
detached.oldWindowId,
"Expected onDetached old window ID"
);
browser.test.assertEq(
tab.id,
attached.tabId,
"Expected onAttached tab ID"
);
browser.test.assertEq(
0,
attached.newPosition,
"Expected onAttached new index"
);
browser.test.assertEq(
window.id,
attached.newWindowId,
"Expected onAttached new window id"
);
browser.test.log(
"Close the new window by moving the tab into former window"
);
await browser.tabs.move(tab.id, { index: 1, windowId });
[detached, attached] = await expectEvents(["onDetached", "onAttached"]);
browser.test.assertEq(
tab.id,
detached.tabId,
"Expected onDetached tab ID"
);
browser.test.assertEq(
0,
detached.oldPosition,
"Expected onDetached old index"
);
browser.test.assertEq(
window.id,
detached.oldWindowId,
"Expected onDetached old window ID"
);
browser.test.assertEq(
tab.id,
attached.tabId,
"Expected onAttached tab ID"
);
browser.test.assertEq(
1,
attached.newPosition,
"Expected onAttached new index"
);
browser.test.assertEq(
windowId,
attached.newWindowId,
"Expected onAttached new window id"
);
browser.test.assertEq(tab.incognito, incognito, "Tab is incognito");
browser.test.log("Remove the tab");
await browser.tabs.remove(tab.id);
browser.windows.remove(windowId);
browser.test.notifyPass("tabs-events");
} catch (e) {
browser.test.fail(`${e} :: ${e.stack}`);
browser.test.notifyFail("tabs-events");
}
}
let extension = ExtensionTestUtils.loadExtension({
manifest: {
permissions: ["tabs"],
},
background,
incognitoOverride: "spanning",
});
await extension.startup();
await extension.awaitFinish("tabs-events");
await extension.unload();
});
add_task(async function testTabEventsSize() {
function background() {
function sendSizeMessages(tab, type) {
browser.test.sendMessage(`${type}-dims`, {
width: tab.width,
height: tab.height,
});
}
browser.tabs.onCreated.addListener(tab => {
sendSizeMessages(tab, "on-created");
});
browser.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
if (changeInfo.status == "complete") {
sendSizeMessages(tab, "on-updated");
}
});
browser.test.onMessage.addListener(async (msg, arg) => {
if (msg === "create-tab") {
let tab = await browser.tabs.create({ url: "https://example.com/" });
sendSizeMessages(tab, "create");
browser.test.sendMessage("created-tab-id", tab.id);
} else if (msg === "update-tab") {
let tab = await browser.tabs.update(arg, {
});
sendSizeMessages(tab, "update");
} else if (msg === "remove-tab") {
browser.tabs.remove(arg);
browser.test.sendMessage("tab-removed");
}
});
browser.test.sendMessage("ready");
}
let extension = ExtensionTestUtils.loadExtension({
manifest: {
permissions: ["tabs"],
},
background,
});
const RESOLUTION_PREF = "layout.css.devPixelsPerPx";
registerCleanupFunction(() => {
SpecialPowers.clearUserPref(RESOLUTION_PREF);
});
function checkDimensions(dims, type) {
is(
dims.width,
gBrowser.selectedBrowser.clientWidth,
`tab from ${type} reports expected width`
);
is(
dims.height,
gBrowser.selectedBrowser.clientHeight,
`tab from ${type} reports expected height`
);
}
await Promise.all([extension.startup(), extension.awaitMessage("ready")]);
for (let resolution of [2, 1]) {
SpecialPowers.setCharPref(RESOLUTION_PREF, String(resolution));
is(
window.devicePixelRatio,
resolution,
"window has the required resolution"
);
extension.sendMessage("create-tab");
let tabId = await extension.awaitMessage("created-tab-id");
checkDimensions(await extension.awaitMessage("create-dims"), "create");
checkDimensions(
await extension.awaitMessage("on-created-dims"),
"onCreated"
);
checkDimensions(
await extension.awaitMessage("on-updated-dims"),
"onUpdated"
);
extension.sendMessage("update-tab", tabId);
checkDimensions(await extension.awaitMessage("update-dims"), "update");
checkDimensions(
await extension.awaitMessage("on-updated-dims"),
"onUpdated"
);
extension.sendMessage("remove-tab", tabId);
await extension.awaitMessage("tab-removed");
}
await extension.unload();
SpecialPowers.clearUserPref(RESOLUTION_PREF);
}).skip(); // Bug 1614075 perma-fail comparing devicePixelRatio
add_task(async function testTabRemovalEvent() {
async function background() {
let events = [];
function awaitLoad(tabId) {
return new Promise(resolve => {
browser.tabs.onUpdated.addListener(function listener(tabId_, changed) {
if (tabId == tabId_ && changed.status == "complete") {
browser.tabs.onUpdated.removeListener(listener);
resolve();
}
});
});
}
chrome.tabs.onRemoved.addListener(tabId => {
browser.test.assertEq(
0,
events.length,
"No events recorded before onRemoved."
);
events.push("onRemoved");
browser.test.log(
"Make sure the removed tab is not available in the tabs.query callback."
);
chrome.tabs.query({}, tabs => {
for (let tab of tabs) {
browser.test.assertTrue(
tab.id != tabId,
"Tab query should not include removed tabId"
);
}
});
});
try {
let url =
let tab = await browser.tabs.create({ url: url });
await awaitLoad(tab.id);
chrome.tabs.onActivated.addListener(() => {
browser.test.assertEq(
1,
events.length,
"One event recorded before onActivated."
);
events.push("onActivated");
browser.test.assertEq(
"onRemoved",
events[0],
"onRemoved fired before onActivated."
);
browser.test.notifyPass("tabs-events");
});
await browser.tabs.remove(tab.id);
} catch (e) {
browser.test.fail(`${e} :: ${e.stack}`);
browser.test.notifyFail("tabs-events");
}
}
let extension = ExtensionTestUtils.loadExtension({
manifest: {
permissions: ["tabs"],
},
background,
});
await extension.startup();
await extension.awaitFinish("tabs-events");
await extension.unload();
});
add_task(async function testTabCreateRelated() {
await SpecialPowers.pushPrefEnv({
set: [
["browser.tabs.opentabfor.middleclick", true],
["browser.tabs.insertRelatedAfterCurrent", true],
],
});
async function background() {
let created;
browser.tabs.onCreated.addListener(tab => {
browser.test.log(`tabs.onCreated, index=${tab.index}`);
browser.test.assertEq(1, tab.index, "expecting tab index of 1");
created = tab.id;
});
browser.tabs.onMoved.addListener((id, info) => {
browser.test.log(
`tabs.onMoved, from ${info.fromIndex} to ${info.toIndex}`
);
browser.test.fail("tabMoved was received");
});
browser.tabs.onRemoved.addListener(tabId => {
browser.test.assertEq(created, tabId, "removed id same as created");
browser.test.sendMessage("tabRemoved");
});
}
let extension = ExtensionTestUtils.loadExtension({
manifest: {
permissions: ["tabs"],
},
background,
});
// Create a *opener* tab page which has a link to "example.com".
let pageURL =
let openerTab = await BrowserTestUtils.openNewForegroundTab(
gBrowser,
pageURL
);
gBrowser.moveTabTo(openerTab, 0);
await extension.startup();
let newTabPromise = BrowserTestUtils.waitForNewTab(
gBrowser,
true
);
await BrowserTestUtils.synthesizeMouseAtCenter(
"#link_to_example_com",
{ button: 1 },
gBrowser.selectedBrowser
);
let openTab = await newTabPromise;
is(
openTab.linkedBrowser.currentURI.spec,
"Middle click should open site to correct url."
);
BrowserTestUtils.removeTab(openTab);
await extension.awaitMessage("tabRemoved");
await extension.unload();
BrowserTestUtils.removeTab(openerTab);
});
add_task(async function testLastTabRemoval() {
await SpecialPowers.pushPrefEnv({
set: [["browser.tabs.closeWindowWithLastTab", false]],
});
async function background() {
let windowId;
browser.tabs.onCreated.addListener(tab => {
browser.test.assertEq(
windowId,
tab.windowId,
"expecting onCreated after onRemoved on the same window"
);
browser.test.sendMessage("tabCreated", `${tab.width}x${tab.height}`);
});
browser.tabs.onRemoved.addListener((tabId, info) => {
windowId = info.windowId;
});
}
let extension = ExtensionTestUtils.loadExtension({
manifest: {
permissions: ["tabs"],
},
background,
});
let newWin = await BrowserTestUtils.openNewBrowserWindow();
await extension.startup();
const oldBrowser = newWin.gBrowser.selectedBrowser;
const expectedDims = `${oldBrowser.clientWidth}x${oldBrowser.clientHeight}`;
BrowserTestUtils.removeTab(newWin.gBrowser.selectedTab);
const actualDims = await extension.awaitMessage("tabCreated");
is(
actualDims,
expectedDims,
"created tab reports a size same to the removed last tab"
);
await extension.unload();
await BrowserTestUtils.closeWindow(newWin);
await SpecialPowers.popPrefEnv();
});
add_task(async function testTabActivationEvent() {
async function background() {
function makeExpectable() {
let expectation = null,
resolver = null;
const expectable = param => {
if (expectation === null) {
browser.test.fail("unexpected call to expectable");
} else {
try {
resolver(expectation(param));
} catch (e) {
resolver(Promise.reject(e));
} finally {
expectation = null;
}
}
};
expectable.expect = e => {
expectation = e;
return new Promise(r => {
resolver = r;
});
};
return expectable;
}
try {
const listener = makeExpectable();
browser.tabs.onActivated.addListener(listener);
const [
,
{
tabs: [tab1],
},
] = await Promise.all([
listener.expect(info => {
browser.test.assertEq(
undefined,
info.previousTabId,
"previousTabId should not be defined when window is first opened"
);
}),
browser.windows.create({ url: "about:blank" }),
]);
const [, tab2] = await Promise.all([
listener.expect(info => {
browser.test.assertEq(
tab1.id,
info.previousTabId,
"Got expected previousTabId"
);
}),
browser.tabs.create({ url: "about:blank" }),
]);
await Promise.all([
listener.expect(info => {
browser.test.assertEq(tab1.id, info.tabId, "Got expected tabId");
browser.test.assertEq(
tab2.id,
info.previousTabId,
"Got expected previousTabId"
);
}),
browser.tabs.update(tab1.id, { active: true }),
]);
await Promise.all([
listener.expect(info => {
browser.test.assertEq(tab2.id, info.tabId, "Got expected tabId");
browser.test.assertEq(
undefined,
info.previousTabId,
"previousTabId should not be defined when previous tab was closed"
);
}),
browser.tabs.remove(tab1.id),
]);
await browser.tabs.remove(tab2.id);
browser.test.notifyPass("tabs-events");
} catch (e) {
browser.test.fail(`${e} :: ${e.stack}`);
browser.test.notifyFail("tabs-events");
}
}
let extension = ExtensionTestUtils.loadExtension({
manifest: {
permissions: ["tabs"],
},
background,
});
await extension.startup();
await extension.awaitFinish("tabs-events");
await extension.unload();
});
add_task(async function test_tabs_event_page() {
await SpecialPowers.pushPrefEnv({
set: [["extensions.eventPages.enabled", true]],
});
let extension = ExtensionTestUtils.loadExtension({
useAddonManager: "permanent",
manifest: {
browser_specific_settings: { gecko: { id: "eventpage@tabs" } },
permissions: ["tabs"],
background: { persistent: false },
},
background() {
const EVENTS = [
"onActivated",
"onAttached",
"onDetached",
"onRemoved",
"onMoved",
"onHighlighted",
"onUpdated",
];
browser.tabs.onCreated.addListener(() => {
browser.test.sendMessage("onCreated");
});
for (let event of EVENTS) {
browser.tabs[event].addListener(() => {});
}
browser.test.sendMessage("ready");
},
});
const EVENTS = [
"onActivated",
"onAttached",
"onCreated",
"onDetached",
"onRemoved",
"onMoved",
"onHighlighted",
"onUpdated",
];
await extension.startup();
await extension.awaitMessage("ready");
for (let event of EVENTS) {
assertPersistentListeners(extension, "tabs", event, {
primed: false,
});
}
// test events waken background
await extension.terminateBackground();
for (let event of EVENTS) {
assertPersistentListeners(extension, "tabs", event, {
primed: true,
});
}
let win = await BrowserTestUtils.openNewBrowserWindow();
await extension.awaitMessage("ready");
await extension.awaitMessage("onCreated");
ok(true, "persistent event woke background");
for (let event of EVENTS) {
assertPersistentListeners(extension, "tabs", event, {
primed: false,
});
}
await BrowserTestUtils.closeWindow(win);
await extension.unload();
await SpecialPowers.popPrefEnv();
});