tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }