Source code

Revision control

Copy as Markdown

Other Tools

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
});
// A mapping of loaded new tab pages, where the mapping is:
// browser -> { actor, browser, browsingContext, portID, url, loaded }
let gLoadedTabs = new Map();
export class AboutNewTabParent extends JSWindowActorParent {
static get loadedTabs() {
return gLoadedTabs;
}
getTabDetails() {
let browser = this.browsingContext.top.embedderElement;
return browser ? gLoadedTabs.get(browser) : null;
}
handleEvent(event) {
if (event.type == "SwapDocShells") {
let oldBrowser = this.browsingContext.top.embedderElement;
let newBrowser = event.detail;
let tabDetails = gLoadedTabs.get(oldBrowser);
if (tabDetails) {
tabDetails.browser = newBrowser;
gLoadedTabs.delete(oldBrowser);
gLoadedTabs.set(newBrowser, tabDetails);
oldBrowser.removeEventListener("SwapDocShells", this);
newBrowser.addEventListener("SwapDocShells", this);
}
}
}
async receiveMessage(message) {
switch (message.name) {
case "AboutNewTabVisible":
await lazy.ASRouter.waitForInitialized;
lazy.ASRouter.sendTriggerMessage({
browser: this.browsingContext.top.embedderElement,
// triggerId and triggerContext
id: "defaultBrowserCheck",
context: { source: "newtab" },
});
break;
case "Init": {
let browsingContext = this.browsingContext;
let browser = browsingContext.top.embedderElement;
if (!browser) {
return;
}
let tabDetails = {
actor: this,
browser,
browsingContext,
portID: message.data.portID,
url: message.data.url,
};
gLoadedTabs.set(browser, tabDetails);
browser.addEventListener("SwapDocShells", this);
browser.addEventListener("EndSwapDocShells", this);
this.notifyActivityStreamChannel("onNewTabInit", message, tabDetails);
break;
}
case "Load":
this.notifyActivityStreamChannel("onNewTabLoad", message);
break;
case "Unload": {
let tabDetails = this.getTabDetails();
if (!tabDetails) {
// When closing a tab, the embedderElement can already be disconnected, so
// as a backup, look up the tab details by browsing context.
tabDetails = this.getByBrowsingContext(this.browsingContext);
}
if (!tabDetails) {
return;
}
tabDetails.browser.removeEventListener("EndSwapDocShells", this);
gLoadedTabs.delete(tabDetails.browser);
this.notifyActivityStreamChannel("onNewTabUnload", message, tabDetails);
break;
}
case "ActivityStream:ContentToMain":
this.notifyActivityStreamChannel("onMessage", message);
break;
}
}
notifyActivityStreamChannel(name, message, tabDetails) {
if (!tabDetails) {
tabDetails = this.getTabDetails();
if (!tabDetails) {
return;
}
}
let channel = this.getChannel();
if (!channel) {
// We're not yet ready to deal with these messages. We'll queue
// them for now, and then dispatch them once the channel has finished
// being set up.
AboutNewTabParent.#queuedMessages.push({
actor: this,
name,
message,
tabDetails,
});
return;
}
let messageToSend = {
target: this,
data: message.data || {},
};
channel[name](messageToSend, tabDetails);
}
getByBrowsingContext(expectedBrowsingContext) {
for (let tabDetails of AboutNewTabParent.loadedTabs.values()) {
if (tabDetails.browsingContext === expectedBrowsingContext) {
return tabDetails;
}
}
return null;
}
getChannel() {
return lazy.AboutNewTab.activityStream?.store?.getMessageChannel();
}
// Queued messages sent from the content process. These are only queued
// if an AboutNewTabParent receives them before the
// ActivityStreamMessageChannel exists.
static #queuedMessages = [];
/**
* If there were any messages sent from content before the
* ActivityStreamMessageChannel was set up, dispatch them now.
*/
static flushQueuedMessagesFromContent() {
for (let messageData of AboutNewTabParent.#queuedMessages) {
let { actor, name, message, tabDetails } = messageData;
actor.notifyActivityStreamChannel(name, message, tabDetails);
}
AboutNewTabParent.#queuedMessages = [];
}
}