tor-browser

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

NewTabMessaging.sys.mjs (4617B)


      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 https://mozilla.org/MPL/2.0/. */
      4 
      5 import {
      6  actionTypes as at,
      7  actionCreators as ac,
      8 } from "resource://newtab/common/Actions.mjs";
      9 
     10 export class NewTabMessaging {
     11  constructor() {
     12    this.initialized = false;
     13    this.ASRouterDispatch = null;
     14    this.browserSet = new WeakSet();
     15  }
     16 
     17  init() {
     18    if (!this.initialized) {
     19      this.initialized = true;
     20      Services.obs.addObserver(this, "newtab-message");
     21      Services.obs.addObserver(this, "newtab-message-query");
     22    }
     23  }
     24 
     25  uninit() {
     26    Services.obs.removeObserver(this, "newtab-message-query");
     27    Services.obs.removeObserver(this, "newtab-message");
     28  }
     29 
     30  observe(subject, topic, _data) {
     31    if (topic === "newtab-message") {
     32      let { targetBrowser, message, dispatch } = subject.wrappedJSObject;
     33      this.ASRouterDispatch = dispatch;
     34      this.showMessage(targetBrowser, message);
     35    } else if (topic === "newtab-message-query") {
     36      let { browser } = subject.wrappedJSObject;
     37      if (this.browserSet.has(browser.selectedBrowser)) {
     38        subject.wrappedJSObject.activeNewtabMessage = true;
     39      }
     40    }
     41  }
     42 
     43  async showMessage(targetBrowser, message) {
     44    if (targetBrowser) {
     45      let actor =
     46        targetBrowser.browsingContext.currentWindowGlobal.getActor(
     47          "AboutNewTab"
     48        );
     49      if (actor) {
     50        let tabDetails = actor.getTabDetails();
     51        if (tabDetails) {
     52          // Only send the message for the tab that triggered the message
     53          this.store.dispatch(
     54            ac.OnlyToOneContent(
     55              {
     56                type: at.MESSAGE_SET,
     57                // send along portID as well so that the child process can easily just update the single tab
     58                data: {
     59                  message,
     60                  portID: tabDetails.portID,
     61                },
     62              },
     63              tabDetails.portID
     64            )
     65          );
     66        }
     67      }
     68    } else {
     69      // if targetBrowser is null, send to the preloaded tab / main process
     70      // This should only run if message is triggered from asrouter during dev
     71      this.store.dispatch(
     72        ac.AlsoToPreloaded({
     73          type: at.MESSAGE_SET,
     74          data: {
     75            message,
     76          },
     77        })
     78      );
     79      // Also force visibility for messages sent from about:asrouter during dev
     80      this.store.dispatch(
     81        ac.AlsoToPreloaded({
     82          type: at.MESSAGE_TOGGLE_VISIBILITY,
     83          data: true,
     84        })
     85      );
     86    }
     87  }
     88 
     89  /**
     90   *
     91   * @param {string} id ID of message to be blocked
     92   */
     93  blockMessage(id) {
     94    if (id) {
     95      this.ASRouterDispatch?.({
     96        type: "BLOCK_MESSAGE_BY_ID",
     97        data: {
     98          id,
     99        },
    100      });
    101    }
    102  }
    103 
    104  /**
    105   * Send impression to ASRouter
    106   *
    107   * @param {object} message
    108   */
    109  handleImpression(message) {
    110    this.sendTelemetry("IMPRESSION", message);
    111    this.ASRouterDispatch?.({
    112      type: "IMPRESSION",
    113      data: message,
    114    });
    115  }
    116 
    117  /**
    118   * Sends telemetry data through ASRouter to
    119   * match pattern with ASRouterTelemetry
    120   */
    121  sendTelemetry(event, message, source = "newtab") {
    122    const data = {
    123      action: "newtab_message_user_event",
    124      event,
    125      event_context: { source, page: "about:newtab" },
    126      message_id: message.id,
    127    };
    128    this.ASRouterDispatch?.({
    129      type: "NEWTAB_MESSAGE_TELEMETRY",
    130      data,
    131    });
    132  }
    133 
    134  notifyVisiblity(action) {
    135    const { browser } = action._target;
    136    if (browser) {
    137      // isVisible
    138      if (action.data) {
    139        // we dont want to add the browser if it is already part of browserSet
    140        if (!this.browserSet.has(browser)) {
    141          this.browserSet.add(browser);
    142        }
    143      } else if (this.browserSet.has(browser)) {
    144        this.browserSet.delete(browser);
    145      }
    146    }
    147  }
    148 
    149  onAction(action) {
    150    switch (action.type) {
    151      case at.INIT:
    152        this.init();
    153        break;
    154      case at.UNINIT:
    155        this.uninit();
    156        break;
    157      case at.MESSAGE_IMPRESSION:
    158        this.handleImpression(action.data);
    159        break;
    160      case at.MESSAGE_DISMISS:
    161        this.sendTelemetry("DISMISS", action.data.message);
    162        break;
    163      case at.MESSAGE_CLICK:
    164        this.sendTelemetry("CLICK", action.data.message, action.data.source);
    165        break;
    166      case at.MESSAGE_BLOCK:
    167        this.blockMessage(action.data);
    168        break;
    169      case at.MESSAGE_NOTIFY_VISIBILITY:
    170        this.notifyVisiblity(action);
    171        break;
    172    }
    173  }
    174 }