no bug - Import translations from android-l10n r=release a=l10n CLOSED TREE
[gecko.git] / browser / actors / AboutNewTabParent.sys.mjs
blobc2ee068b04b05a47477d835aab54975f7cdf5ddd
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/. */
5 const lazy = {};
7 ChromeUtils.defineESModuleGetters(lazy, {
8   AboutNewTab: "resource:///modules/AboutNewTab.sys.mjs",
9   ASRouter: "resource:///modules/asrouter/ASRouter.sys.mjs",
10 });
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();
16 export class AboutNewTabParent extends JSWindowActorParent {
17   static get loadedTabs() {
18     return gLoadedTabs;
19   }
21   getTabDetails() {
22     let browser = this.browsingContext.top.embedderElement;
23     return browser ? gLoadedTabs.get(browser) : null;
24   }
26   handleEvent(event) {
27     if (event.type == "SwapDocShells") {
28       let oldBrowser = this.browsingContext.top.embedderElement;
29       let newBrowser = event.detail;
31       let tabDetails = gLoadedTabs.get(oldBrowser);
32       if (tabDetails) {
33         tabDetails.browser = newBrowser;
34         gLoadedTabs.delete(oldBrowser);
35         gLoadedTabs.set(newBrowser, tabDetails);
37         oldBrowser.removeEventListener("SwapDocShells", this);
38         newBrowser.addEventListener("SwapDocShells", this);
39       }
40     }
41   }
43   async receiveMessage(message) {
44     switch (message.name) {
45       case "AboutNewTabVisible":
46         await lazy.ASRouter.waitForInitialized;
47         lazy.ASRouter.sendTriggerMessage({
48           browser: this.browsingContext.top.embedderElement,
49           // triggerId and triggerContext
50           id: "defaultBrowserCheck",
51           context: { source: "newtab" },
52         });
53         break;
55       case "Init": {
56         let browsingContext = this.browsingContext;
57         let browser = browsingContext.top.embedderElement;
58         if (!browser) {
59           return;
60         }
62         let tabDetails = {
63           actor: this,
64           browser,
65           browsingContext,
66           portID: message.data.portID,
67           url: message.data.url,
68         };
69         gLoadedTabs.set(browser, tabDetails);
71         browser.addEventListener("SwapDocShells", this);
72         browser.addEventListener("EndSwapDocShells", this);
74         this.notifyActivityStreamChannel("onNewTabInit", message, tabDetails);
75         break;
76       }
78       case "Load":
79         this.notifyActivityStreamChannel("onNewTabLoad", message);
80         break;
82       case "Unload": {
83         let tabDetails = this.getTabDetails();
84         if (!tabDetails) {
85           // When closing a tab, the embedderElement can already be disconnected, so
86           // as a backup, look up the tab details by browsing context.
87           tabDetails = this.getByBrowsingContext(this.browsingContext);
88         }
90         if (!tabDetails) {
91           return;
92         }
94         tabDetails.browser.removeEventListener("EndSwapDocShells", this);
96         gLoadedTabs.delete(tabDetails.browser);
98         this.notifyActivityStreamChannel("onNewTabUnload", message, tabDetails);
99         break;
100       }
102       case "ActivityStream:ContentToMain":
103         this.notifyActivityStreamChannel("onMessage", message);
104         break;
105     }
106   }
108   notifyActivityStreamChannel(name, message, tabDetails) {
109     if (!tabDetails) {
110       tabDetails = this.getTabDetails();
111       if (!tabDetails) {
112         return;
113       }
114     }
116     let channel = this.getChannel();
117     if (!channel) {
118       // We're not yet ready to deal with these messages. We'll queue
119       // them for now, and then dispatch them once the channel has finished
120       // being set up.
121       AboutNewTabParent.#queuedMessages.push({
122         actor: this,
123         name,
124         message,
125         tabDetails,
126       });
127       return;
128     }
130     let messageToSend = {
131       target: this,
132       data: message.data || {},
133     };
135     channel[name](messageToSend, tabDetails);
136   }
138   getByBrowsingContext(expectedBrowsingContext) {
139     for (let tabDetails of AboutNewTabParent.loadedTabs.values()) {
140       if (tabDetails.browsingContext === expectedBrowsingContext) {
141         return tabDetails;
142       }
143     }
145     return null;
146   }
148   getChannel() {
149     return lazy.AboutNewTab.activityStream?.store?.getMessageChannel();
150   }
152   // Queued messages sent from the content process. These are only queued
153   // if an AboutNewTabParent receives them before the
154   // ActivityStreamMessageChannel exists.
155   static #queuedMessages = [];
157   /**
158    * If there were any messages sent from content before the
159    * ActivityStreamMessageChannel was set up, dispatch them now.
160    */
161   static flushQueuedMessagesFromContent() {
162     for (let messageData of AboutNewTabParent.#queuedMessages) {
163       let { actor, name, message, tabDetails } = messageData;
164       actor.notifyActivityStreamChannel(name, message, tabDetails);
165     }
166     AboutNewTabParent.#queuedMessages = [];
167   }