Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Tabs Events Test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/ExtensionTestUtils.js"></script>
<script type="text/javascript" src="head.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<script type="text/javascript">
"use strict";
add_task(async function testTabEvents() {
async function background() {
const events = [];
let eventPromise;
const 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 (const [i, name] of names.entries()) {
browser.test.assertEq(name, i in events && events[i].type,
`Got expected ${name} event`);
}
return events.splice(0);
}
try {
browser.test.log("Create tab");
const tab = await browser.tabs.create({url: "about:blank"});
const oldIndex = tab.index;
const [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("Remove tab");
await browser.tabs.remove(tab.id);
const [removed] = await expectEvents(["onRemoved"]);
browser.test.assertEq(tab.id, removed.tabId, "Expected removed tab ID");
browser.test.assertEq(tab.windowId, 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.notifyPass("tabs-events");
} catch (e) {
browser.test.fail(`${e} :: ${e.stack}`);
browser.test.notifyFail("tabs-events");
}
}
const extension = ExtensionTestUtils.loadExtension({
manifest: {
"permissions": ["tabs"],
},
background,
});
await extension.startup();
await extension.awaitFinish("tabs-events");
await extension.unload();
});
add_task(async function testTabRemovalEvent() {
async function background() {
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.log("Make sure the removed tab is not available in the tabs.query callback.");
chrome.tabs.query({}, tabs => {
for (const tab of tabs) {
browser.test.assertTrue(tab.id != tabId, "Tab query should not include removed tabId");
}
browser.test.notifyPass("tabs-events");
});
});
try {
const tab = await browser.tabs.create({url: url});
await awaitLoad(tab.id);
await browser.tabs.remove(tab.id);
} catch (e) {
browser.test.fail(`${e} :: ${e.stack}`);
browser.test.notifyFail("tabs-events");
}
}
const extension = ExtensionTestUtils.loadExtension({
manifest: {
"permissions": ["tabs"],
},
background,
});
await extension.startup();
await extension.awaitFinish("tabs-events");
await extension.unload();
});
add_task(async function testTabActivationEvent() {
// TODO bug 1565536: tabs.onActivated is not supported in GeckoView.
if (true) {
todo(false, "skipping testTabActivationEvent");
return;
}
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 [tab0] = await browser.tabs.query({active: true});
const [, tab1] = await Promise.all([
listener.expect(info => {
browser.test.assertEq(tab0.id, info.previousTabId, "Got expected previousTabId");
}),
browser.tabs.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),
]);
browser.tabs.onActivated.removeListener(listener);
await browser.tabs.remove(tab2.id);
browser.test.notifyPass("tabs-events");
} catch (e) {
browser.test.fail(`${e} :: ${e.stack}`);
browser.test.notifyFail("tabs-events");
}
}
const 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() {
function background() {
const EVENTS = [
"onActivated",
"onRemoved",
"onUpdated",
];
browser.tabs.onCreated.addListener(() => {
browser.test.sendMessage("onCreated");
});
for (const event of EVENTS) {
browser.tabs[event].addListener(() => {
});
}
browser.test.onMessage.addListener(async msg => {
if (msg === "createTab") {
await browser.tabs.create({url: "about:blank"});
}
});
browser.test.sendMessage("ready");
}
const apiEvents = ["onActivated", "onCreated", "onRemoved", "onUpdated"];
const apiNs = "tabs";
const extension = ExtensionTestUtils.loadExtension({
manifest: {
browser_specific_settings: { gecko: { id: "eventpage@tabs" } },
"permissions": ["tabs"],
background: { persistent: false },
},
background,
});
await extension.startup();
info("Wait for event page to be started");
await extension.awaitMessage("ready");
// Sanity check
info("Wait for tabs.onCreated listener call");
extension.sendMessage("createTab");
await extension.awaitMessage("onCreated");
// on startup, all event listeners should not be primed
await assertPersistentListeners(extension, apiNs, apiEvents, { primed: false });
// when the extension is killed, all event listeners should be primed
info("Terminate event page");
await extension.terminateBackground({ disableResetIdleForTest: true });
await assertPersistentListeners(extension, apiNs, apiEvents, { primed: true });
// on start up again, all event listeners should not be primed
info("Wake up event page on a new tabs.onCreated event");
const newWin = window.open();
info("Wait for event page to be restarted");
await extension.awaitMessage("ready");
info("Wait for the primed tabs.onCreated to be received by the event page");
await extension.awaitMessage("onCreated");
await assertPersistentListeners(extension, apiNs, apiEvents, { primed: false });
await extension.unload();
newWin.close();
});
</script>
</body>
</html>