AboutNewTabParent.sys.mjs (5416B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 const lazy = {}; 6 7 ChromeUtils.defineESModuleGetters(lazy, { 8 AboutNewTab: "resource:///modules/AboutNewTab.sys.mjs", 9 ASRouter: "resource:///modules/asrouter/ASRouter.sys.mjs", 10 }); 11 12 // A mapping of loaded new tab pages, where the mapping is: 13 // browser -> { actor, browser, browsingContext, portID, url, loaded } 14 let gLoadedTabs = new Map(); 15 16 export class AboutNewTabParent extends JSWindowActorParent { 17 static get loadedTabs() { 18 return gLoadedTabs; 19 } 20 21 getTabDetails() { 22 let browser = this.browsingContext.top.embedderElement; 23 return browser ? gLoadedTabs.get(browser) : null; 24 } 25 26 handleEvent(event) { 27 if (event.type == "SwapDocShells") { 28 let oldBrowser = this.browsingContext.top.embedderElement; 29 let newBrowser = event.detail; 30 31 let tabDetails = gLoadedTabs.get(oldBrowser); 32 if (tabDetails) { 33 tabDetails.browser = newBrowser; 34 gLoadedTabs.delete(oldBrowser); 35 gLoadedTabs.set(newBrowser, tabDetails); 36 37 oldBrowser.removeEventListener("SwapDocShells", this); 38 newBrowser.addEventListener("SwapDocShells", this); 39 } 40 } 41 } 42 43 async receiveMessage(message) { 44 switch (message.name) { 45 case "AboutNewTabVisible": 46 { 47 const browsingContext = this.browsingContext; 48 // for all of the await's within this switch 49 // check if the Parent actor is still active 50 // helps avoid test failures 51 await lazy.ASRouter.waitForInitialized; 52 if (!browsingContext.isDiscarded) { 53 await lazy.ASRouter.sendTriggerMessage({ 54 browser: browsingContext.top.embedderElement, 55 // triggerId and triggerContext 56 id: "defaultBrowserCheck", 57 context: { source: "newtab" }, 58 }); 59 } 60 if (!browsingContext.isDiscarded) { 61 await lazy.ASRouter.sendTriggerMessage({ 62 browser: browsingContext.top.embedderElement, 63 id: "newtabMessageCheck", 64 }); 65 } 66 } 67 break; 68 case "Init": { 69 let browsingContext = this.browsingContext; 70 let browser = browsingContext.top.embedderElement; 71 if (!browser) { 72 return; 73 } 74 75 let tabDetails = { 76 actor: this, 77 browser, 78 browsingContext, 79 portID: message.data.portID, 80 url: message.data.url, 81 }; 82 gLoadedTabs.set(browser, tabDetails); 83 84 browser.addEventListener("SwapDocShells", this); 85 browser.addEventListener("EndSwapDocShells", this); 86 87 this.notifyActivityStreamChannel("onNewTabInit", message, tabDetails); 88 break; 89 } 90 91 case "Load": 92 this.notifyActivityStreamChannel("onNewTabLoad", message); 93 break; 94 95 case "Unload": { 96 let tabDetails = this.getTabDetails(); 97 if (!tabDetails) { 98 // When closing a tab, the embedderElement can already be disconnected, so 99 // as a backup, look up the tab details by browsing context. 100 tabDetails = this.getByBrowsingContext(this.browsingContext); 101 } 102 103 if (!tabDetails) { 104 return; 105 } 106 107 tabDetails.browser.removeEventListener("EndSwapDocShells", this); 108 109 gLoadedTabs.delete(tabDetails.browser); 110 111 this.notifyActivityStreamChannel("onNewTabUnload", message, tabDetails); 112 break; 113 } 114 115 case "ActivityStream:ContentToMain": 116 this.notifyActivityStreamChannel("onMessage", message); 117 break; 118 } 119 } 120 121 notifyActivityStreamChannel(name, message, tabDetails) { 122 if (!tabDetails) { 123 tabDetails = this.getTabDetails(); 124 if (!tabDetails) { 125 return; 126 } 127 } 128 129 let channel = this.getChannel(); 130 if (!channel) { 131 // We're not yet ready to deal with these messages. We'll queue 132 // them for now, and then dispatch them once the channel has finished 133 // being set up. 134 AboutNewTabParent.#queuedMessages.push({ 135 actor: this, 136 name, 137 message, 138 tabDetails, 139 }); 140 return; 141 } 142 143 let messageToSend = { 144 target: this, 145 data: message.data || {}, 146 }; 147 148 channel[name](messageToSend, tabDetails); 149 } 150 151 getByBrowsingContext(expectedBrowsingContext) { 152 for (let tabDetails of AboutNewTabParent.loadedTabs.values()) { 153 if (tabDetails.browsingContext === expectedBrowsingContext) { 154 return tabDetails; 155 } 156 } 157 158 return null; 159 } 160 161 getChannel() { 162 return lazy.AboutNewTab.activityStream?.store?.getMessageChannel(); 163 } 164 165 // Queued messages sent from the content process. These are only queued 166 // if an AboutNewTabParent receives them before the 167 // ActivityStreamMessageChannel exists. 168 static #queuedMessages = []; 169 170 /** 171 * If there were any messages sent from content before the 172 * ActivityStreamMessageChannel was set up, dispatch them now. 173 */ 174 static flushQueuedMessagesFromContent() { 175 for (let messageData of AboutNewTabParent.#queuedMessages) { 176 let { actor, name, message, tabDetails } = messageData; 177 actor.notifyActivityStreamChannel(name, message, tabDetails); 178 } 179 AboutNewTabParent.#queuedMessages = []; 180 } 181 }