browser_events_module.js (9598B)
1 /* Any copyright is dedicated to the Public Domain. 2 * http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 const { RootMessageHandlerRegistry } = ChromeUtils.importESModule( 7 "chrome://remote/content/shared/messagehandler/RootMessageHandlerRegistry.sys.mjs" 8 ); 9 const { RootMessageHandler } = ChromeUtils.importESModule( 10 "chrome://remote/content/shared/messagehandler/RootMessageHandler.sys.mjs" 11 ); 12 13 /** 14 * Emit an event from a WindowGlobal module triggered by a specific command. 15 * Check that the event is emitted on the RootMessageHandler as well as on 16 * the parent process MessageHandlerRegistry. 17 */ 18 add_task(async function test_event() { 19 const tab = BrowserTestUtils.addTab( 20 gBrowser, 21 "https://example.com/document-builder.sjs?html=tab" 22 ); 23 await BrowserTestUtils.browserLoaded(tab.linkedBrowser); 24 const browsingContext = tab.linkedBrowser.browsingContext; 25 26 const rootMessageHandler = createRootMessageHandler("session-id-event"); 27 let messageHandlerEvent; 28 let registryEvent; 29 30 // Events are emitted both as generic message-handler-event events as well 31 // as under their own name. We expect to receive the event for both. 32 const _onMessageHandlerEvent = (eventName, eventData) => { 33 if (eventData.name === "event-from-window-global") { 34 messageHandlerEvent = eventData; 35 } 36 }; 37 rootMessageHandler.on("message-handler-event", _onMessageHandlerEvent); 38 const onNamedEvent = rootMessageHandler.once("event-from-window-global"); 39 // MessageHandlerRegistry should forward all the message-handler-events. 40 const _onMessageHandlerRegistryEvent = (eventName, eventData) => { 41 if (eventData.name === "event-from-window-global") { 42 registryEvent = eventData; 43 } 44 }; 45 RootMessageHandlerRegistry.on( 46 "message-handler-registry-event", 47 _onMessageHandlerRegistryEvent 48 ); 49 50 callTestEmitEvent(rootMessageHandler, browsingContext.id); 51 52 const namedEvent = await onNamedEvent; 53 is( 54 namedEvent.text, 55 `event from ${browsingContext.id}`, 56 "Received the expected payload" 57 ); 58 59 is( 60 messageHandlerEvent.name, 61 "event-from-window-global", 62 "Received event on the ROOT MessageHandler" 63 ); 64 is( 65 messageHandlerEvent.data.text, 66 `event from ${browsingContext.id}`, 67 "Received the expected payload" 68 ); 69 70 is( 71 registryEvent, 72 messageHandlerEvent, 73 "The event forwarded by the MessageHandlerRegistry is identical to the MessageHandler event" 74 ); 75 rootMessageHandler.off("message-handler-event", _onMessageHandlerEvent); 76 RootMessageHandlerRegistry.off( 77 "message-handler-registry-event", 78 _onMessageHandlerRegistryEvent 79 ); 80 rootMessageHandler.destroy(); 81 gBrowser.removeTab(tab); 82 }); 83 84 /** 85 * Emit an event from a Root module triggered by a specific command. 86 * Check that the event is emitted on the RootMessageHandler. 87 */ 88 add_task(async function test_root_event() { 89 const rootMessageHandler = createRootMessageHandler("session-id-root_event"); 90 91 // events are emitted both as generic message-handler-event events as 92 // well as under their own name. We expect to receive the event for both. 93 const onHandlerEvent = rootMessageHandler.once("message-handler-event"); 94 const onNamedEvent = rootMessageHandler.once("event-from-root"); 95 96 rootMessageHandler.handleCommand({ 97 moduleName: "event", 98 commandName: "testEmitRootEvent", 99 destination: { 100 type: RootMessageHandler.type, 101 }, 102 }); 103 104 const { name, data } = await onHandlerEvent; 105 is(name, "event-from-root", "Received event on the ROOT MessageHandler"); 106 is(data.text, "event from root", "Received the expected payload"); 107 108 const namedEvent = await onNamedEvent; 109 is(namedEvent.text, "event from root", "Received the expected payload"); 110 111 rootMessageHandler.destroy(); 112 }); 113 114 /** 115 * Emit an event from a windowglobal-in-root module triggered by a specific command. 116 * Check that the event is emitted on the RootMessageHandler. 117 */ 118 add_task(async function test_windowglobal_in_root_event() { 119 const tab = BrowserTestUtils.addTab( 120 gBrowser, 121 "https://example.com/document-builder.sjs?html=tab" 122 ); 123 await BrowserTestUtils.browserLoaded(tab.linkedBrowser); 124 const browsingContext = tab.linkedBrowser.browsingContext; 125 126 const rootMessageHandler = createRootMessageHandler( 127 "session-id-windowglobal_in_root_event" 128 ); 129 130 // events are emitted both as generic message-handler-event events as 131 // well as under their own name. We expect to receive the event for both. 132 const onHandlerEvent = rootMessageHandler.once("message-handler-event"); 133 const onNamedEvent = rootMessageHandler.once( 134 "event-from-window-global-in-root" 135 ); 136 rootMessageHandler.handleCommand({ 137 moduleName: "event", 138 commandName: "testEmitWindowGlobalInRootEvent", 139 destination: { 140 type: WindowGlobalMessageHandler.type, 141 id: browsingContext.id, 142 }, 143 }); 144 145 const { name, data } = await onHandlerEvent; 146 is( 147 name, 148 "event-from-window-global-in-root", 149 "Received event on the ROOT MessageHandler" 150 ); 151 is( 152 data.text, 153 `windowglobal-in-root event for ${browsingContext.id}`, 154 "Received the expected payload" 155 ); 156 157 const namedEvent = await onNamedEvent; 158 is( 159 namedEvent.text, 160 `windowglobal-in-root event for ${browsingContext.id}`, 161 "Received the expected payload" 162 ); 163 164 rootMessageHandler.destroy(); 165 gBrowser.removeTab(tab); 166 }); 167 168 /** 169 * Emit an event from a windowglobal module, but from 2 different sessions. 170 * Check that the event is emitted by the corresponding RootMessageHandler as 171 * well as by the parent process MessageHandlerRegistry. 172 */ 173 add_task(async function test_event_multisession() { 174 const tab = BrowserTestUtils.addTab( 175 gBrowser, 176 "https://example.com/document-builder.sjs?html=tab" 177 ); 178 await BrowserTestUtils.browserLoaded(tab.linkedBrowser); 179 const browsingContextId = tab.linkedBrowser.browsingContext.id; 180 181 const root1 = createRootMessageHandler("session-id-event_multisession-1"); 182 let root1Events = 0; 183 const onRoot1Event = function (evtName, wrappedEvt) { 184 if (wrappedEvt.name === "event-from-window-global") { 185 root1Events++; 186 } 187 }; 188 root1.on("message-handler-event", onRoot1Event); 189 190 const root2 = createRootMessageHandler("session-id-event_multisession-2"); 191 let root2Events = 0; 192 const onRoot2Event = function (evtName, wrappedEvt) { 193 if (wrappedEvt.name === "event-from-window-global") { 194 root2Events++; 195 } 196 }; 197 root2.on("message-handler-event", onRoot2Event); 198 199 let registryEvents = 0; 200 const onRegistryEvent = function (evtName, wrappedEvt) { 201 if (wrappedEvt.name === "event-from-window-global") { 202 registryEvents++; 203 } 204 }; 205 RootMessageHandlerRegistry.on( 206 "message-handler-registry-event", 207 onRegistryEvent 208 ); 209 210 callTestEmitEvent(root1, browsingContextId); 211 callTestEmitEvent(root2, browsingContextId); 212 213 info("Wait for root1 event to be received"); 214 await TestUtils.waitForCondition(() => root1Events === 1); 215 info("Wait for root2 event to be received"); 216 await TestUtils.waitForCondition(() => root2Events === 1); 217 218 await TestUtils.waitForTick(); 219 is(root1Events, 1, "Session 1 only received 1 event"); 220 is(root2Events, 1, "Session 2 only received 1 event"); 221 is( 222 registryEvents, 223 2, 224 "MessageHandlerRegistry forwarded events from both sessions" 225 ); 226 227 root1.off("message-handler-event", onRoot1Event); 228 root2.off("message-handler-event", onRoot2Event); 229 RootMessageHandlerRegistry.off( 230 "message-handler-registry-event", 231 onRegistryEvent 232 ); 233 root1.destroy(); 234 root2.destroy(); 235 gBrowser.removeTab(tab); 236 }); 237 238 /** 239 * Test that events can be emitted from individual frame contexts and that 240 * events going through a shared content process MessageHandlerRegistry are not 241 * duplicated. 242 */ 243 add_task(async function test_event_with_frames() { 244 info("Navigate the initial tab to the test URL"); 245 const tab = gBrowser.selectedTab; 246 await loadURL(tab.linkedBrowser, createTestMarkupWithFrames()); 247 248 const contexts = 249 tab.linkedBrowser.browsingContext.getAllBrowsingContextsInSubtree(); 250 is(contexts.length, 4, "Test tab has 3 children contexts (4 in total)"); 251 252 const rootMessageHandler = createRootMessageHandler( 253 "session-id-event_with_frames" 254 ); 255 256 const rootEvents = []; 257 const onRootEvent = function (evtName, wrappedEvt) { 258 if (wrappedEvt.name === "event-from-window-global") { 259 rootEvents.push(wrappedEvt.data.text); 260 } 261 }; 262 rootMessageHandler.on("message-handler-event", onRootEvent); 263 264 const namedEvents = []; 265 const onNamedEvent = (name, event) => namedEvents.push(event.text); 266 rootMessageHandler.on("event-from-window-global", onNamedEvent); 267 268 for (const context of contexts) { 269 callTestEmitEvent(rootMessageHandler, context.id); 270 info("Wait for root event to be received in both event arrays"); 271 await TestUtils.waitForCondition(() => 272 [namedEvents, rootEvents].every(events => 273 events.includes(`event from ${context.id}`) 274 ) 275 ); 276 } 277 278 info("Wait for a bit and check that we did not receive duplicated events"); 279 await TestUtils.waitForTick(); 280 is(rootEvents.length, 4, "Only received 4 events"); 281 282 rootMessageHandler.off("message-handler-event", onRootEvent); 283 rootMessageHandler.off("event-from-window-global", onNamedEvent); 284 rootMessageHandler.destroy(); 285 }); 286 287 function callTestEmitEvent(rootMessageHandler, browsingContextId) { 288 rootMessageHandler.handleCommand({ 289 moduleName: "event", 290 commandName: "testEmitEvent", 291 destination: { 292 type: WindowGlobalMessageHandler.type, 293 id: browsingContextId, 294 }, 295 }); 296 }