test_ext_tabs_events.html (9409B)
1 <!DOCTYPE HTML> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title>Tabs Events Test</title> 6 <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> 7 <script type="text/javascript" src="/tests/SimpleTest/ExtensionTestUtils.js"></script> 8 <script type="text/javascript" src="head.js"></script> 9 <link rel="stylesheet" href="/tests/SimpleTest/test.css"/> 10 </head> 11 <body> 12 13 <script type="text/javascript"> 14 "use strict"; 15 16 add_task(async function testTabEvents() { 17 async function background() { 18 const events = []; 19 let eventPromise; 20 const checkEvents = () => { 21 if (eventPromise && events.length >= eventPromise.names.length) { 22 eventPromise.resolve(); 23 } 24 }; 25 26 browser.tabs.onCreated.addListener(tab => { 27 events.push({type: "onCreated", tab}); 28 checkEvents(); 29 }); 30 31 browser.tabs.onAttached.addListener((tabId, info) => { 32 events.push(Object.assign({type: "onAttached", tabId}, info)); 33 checkEvents(); 34 }); 35 36 browser.tabs.onDetached.addListener((tabId, info) => { 37 events.push(Object.assign({type: "onDetached", tabId}, info)); 38 checkEvents(); 39 }); 40 41 browser.tabs.onRemoved.addListener((tabId, info) => { 42 events.push(Object.assign({type: "onRemoved", tabId}, info)); 43 checkEvents(); 44 }); 45 46 browser.tabs.onMoved.addListener((tabId, info) => { 47 events.push(Object.assign({type: "onMoved", tabId}, info)); 48 checkEvents(); 49 }); 50 51 async function expectEvents(names) { 52 browser.test.log(`Expecting events: ${names.join(", ")}`); 53 54 await new Promise(resolve => { 55 eventPromise = {names, resolve}; 56 checkEvents(); 57 }); 58 59 browser.test.assertEq(names.length, events.length, "Got expected number of events"); 60 for (const [i, name] of names.entries()) { 61 browser.test.assertEq(name, i in events && events[i].type, 62 `Got expected ${name} event`); 63 } 64 return events.splice(0); 65 } 66 67 try { 68 browser.test.log("Create tab"); 69 const tab = await browser.tabs.create({url: "about:blank"}); 70 const oldIndex = tab.index; 71 72 const [created] = await expectEvents(["onCreated"]); 73 browser.test.assertEq(tab.id, created.tab.id, "Got expected tab ID"); 74 browser.test.assertEq(oldIndex, created.tab.index, "Got expected tab index"); 75 76 77 browser.test.log("Remove tab"); 78 await browser.tabs.remove(tab.id); 79 const [removed] = await expectEvents(["onRemoved"]); 80 81 browser.test.assertEq(tab.id, removed.tabId, "Expected removed tab ID"); 82 browser.test.assertEq(tab.windowId, removed.windowId, "Expected removed tab window ID"); 83 // Note: We want to test for the actual boolean value false here. 84 browser.test.assertEq(false, removed.isWindowClosing, "Expected isWindowClosing value"); 85 86 browser.test.notifyPass("tabs-events"); 87 } catch (e) { 88 browser.test.fail(`${e} :: ${e.stack}`); 89 browser.test.notifyFail("tabs-events"); 90 } 91 } 92 93 const extension = ExtensionTestUtils.loadExtension({ 94 manifest: { 95 "permissions": ["tabs"], 96 }, 97 background, 98 }); 99 100 await extension.startup(); 101 await extension.awaitFinish("tabs-events"); 102 await extension.unload(); 103 }); 104 105 add_task(async function testTabRemovalEvent() { 106 async function background() { 107 function awaitLoad(tabId) { 108 return new Promise(resolve => { 109 browser.tabs.onUpdated.addListener(function listener(tabId_, changed) { 110 if (tabId == tabId_ && changed.status == "complete") { 111 browser.tabs.onUpdated.removeListener(listener); 112 resolve(); 113 } 114 }); 115 }); 116 } 117 118 chrome.tabs.onRemoved.addListener((tabId) => { 119 browser.test.log("Make sure the removed tab is not available in the tabs.query callback."); 120 chrome.tabs.query({}, tabs => { 121 for (const tab of tabs) { 122 browser.test.assertTrue(tab.id != tabId, "Tab query should not include removed tabId"); 123 } 124 browser.test.notifyPass("tabs-events"); 125 }); 126 }); 127 128 try { 129 const url = "http://example.com/mochitest/mobile/shared/components/extensions/test/mochitest/context.html"; 130 const tab = await browser.tabs.create({url: url}); 131 await awaitLoad(tab.id); 132 133 await browser.tabs.remove(tab.id); 134 } catch (e) { 135 browser.test.fail(`${e} :: ${e.stack}`); 136 browser.test.notifyFail("tabs-events"); 137 } 138 } 139 140 const extension = ExtensionTestUtils.loadExtension({ 141 manifest: { 142 "permissions": ["tabs"], 143 }, 144 background, 145 }); 146 147 await extension.startup(); 148 await extension.awaitFinish("tabs-events"); 149 await extension.unload(); 150 }); 151 152 add_task(async function testTabActivationEvent() { 153 // TODO bug 1565536: tabs.onActivated is not supported in GeckoView. 154 if (true) { 155 todo(false, "skipping testTabActivationEvent"); 156 return; 157 } 158 async function background() { 159 function makeExpectable() { 160 let expectation = null, resolver = null; 161 const expectable = param => { 162 if (expectation === null) { 163 browser.test.fail("unexpected call to expectable"); 164 } else { 165 try { 166 resolver(expectation(param)); 167 } catch (e) { 168 resolver(Promise.reject(e)); 169 } finally { 170 expectation = null; 171 } 172 } 173 }; 174 expectable.expect = e => { 175 expectation = e; 176 return new Promise(r => { resolver = r; }); 177 }; 178 return expectable; 179 } 180 try { 181 const listener = makeExpectable(); 182 browser.tabs.onActivated.addListener(listener); 183 184 const [tab0] = await browser.tabs.query({active: true}); 185 const [, tab1] = await Promise.all([ 186 listener.expect(info => { 187 browser.test.assertEq(tab0.id, info.previousTabId, "Got expected previousTabId"); 188 }), 189 browser.tabs.create({url: "about:blank"}), 190 ]); 191 const [, tab2] = await Promise.all([ 192 listener.expect(info => { 193 browser.test.assertEq(tab1.id, info.previousTabId, "Got expected previousTabId"); 194 }), 195 browser.tabs.create({url: "about:blank"}), 196 ]); 197 198 await Promise.all([ 199 listener.expect(info => { 200 browser.test.assertEq(tab1.id, info.tabId, "Got expected tabId"); 201 browser.test.assertEq(tab2.id, info.previousTabId, "Got expected previousTabId"); 202 }), 203 browser.tabs.update(tab1.id, {active: true}), 204 ]); 205 206 await Promise.all([ 207 listener.expect(info => { 208 browser.test.assertEq(tab2.id, info.tabId, "Got expected tabId"); 209 browser.test.assertEq(undefined, info.previousTabId, "previousTabId should not be defined when previous tab was closed"); 210 }), 211 browser.tabs.remove(tab1.id), 212 ]); 213 214 browser.tabs.onActivated.removeListener(listener); 215 await browser.tabs.remove(tab2.id); 216 217 browser.test.notifyPass("tabs-events"); 218 } catch (e) { 219 browser.test.fail(`${e} :: ${e.stack}`); 220 browser.test.notifyFail("tabs-events"); 221 } 222 } 223 224 const extension = ExtensionTestUtils.loadExtension({ 225 manifest: { 226 "permissions": ["tabs"], 227 }, 228 229 background, 230 }); 231 232 await extension.startup(); 233 await extension.awaitFinish("tabs-events"); 234 await extension.unload(); 235 }); 236 237 add_task(async function test_tabs_event_page() { 238 function background() { 239 const EVENTS = [ 240 "onActivated", 241 "onRemoved", 242 "onUpdated", 243 ]; 244 browser.tabs.onCreated.addListener(() => { 245 browser.test.sendMessage("onCreated"); 246 }); 247 for (const event of EVENTS) { 248 browser.tabs[event].addListener(() => { 249 }); 250 } 251 browser.test.onMessage.addListener(async msg => { 252 if (msg === "createTab") { 253 await browser.tabs.create({url: "about:blank"}); 254 } 255 }); 256 browser.test.sendMessage("ready"); 257 } 258 259 const apiEvents = ["onActivated", "onCreated", "onRemoved", "onUpdated"]; 260 const apiNs = "tabs"; 261 const extension = ExtensionTestUtils.loadExtension({ 262 manifest: { 263 browser_specific_settings: { gecko: { id: "eventpage@tabs" } }, 264 "permissions": ["tabs"], 265 background: { persistent: false }, 266 }, 267 background, 268 }); 269 270 await extension.startup(); 271 info("Wait for event page to be started"); 272 await extension.awaitMessage("ready"); 273 // Sanity check 274 info("Wait for tabs.onCreated listener call"); 275 extension.sendMessage("createTab"); 276 await extension.awaitMessage("onCreated"); 277 278 // on startup, all event listeners should not be primed 279 await assertPersistentListeners(extension, apiNs, apiEvents, { primed: false }); 280 281 // when the extension is killed, all event listeners should be primed 282 info("Terminate event page"); 283 await extension.terminateBackground({ disableResetIdleForTest: true }); 284 await assertPersistentListeners(extension, apiNs, apiEvents, { primed: true }); 285 286 // on start up again, all event listeners should not be primed 287 info("Wake up event page on a new tabs.onCreated event"); 288 const newWin = window.open(); 289 info("Wait for event page to be restarted"); 290 await extension.awaitMessage("ready"); 291 info("Wait for the primed tabs.onCreated to be received by the event page"); 292 await extension.awaitMessage("onCreated"); 293 await assertPersistentListeners(extension, apiNs, apiEvents, { primed: false }); 294 295 await extension.unload(); 296 newWin.close(); 297 }); 298 299 </script> 300 301 </body> 302 </html>