tor-browser

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

AboutMessagePreviewParent.sys.mjs (6345B)


      1 /* vim: set ts=2 sw=2 sts=2 et tw=80: */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 import { ASRouter } from "resource:///modules/asrouter/ASRouter.sys.mjs";
      7 import { JsonSchema } from "resource://gre/modules/JsonSchema.sys.mjs";
      8 
      9 const lazy = {};
     10 
     11 ChromeUtils.defineESModuleGetters(lazy, {
     12  AddonManager: "resource://gre/modules/AddonManager.sys.mjs",
     13  BookmarksBarButton: "resource:///modules/asrouter/BookmarksBarButton.sys.mjs",
     14  CFRPageActions: "resource:///modules/asrouter/CFRPageActions.sys.mjs",
     15  CustomizableUI:
     16    "moz-src:///browser/components/customizableui/CustomizableUI.sys.mjs",
     17  FeatureCalloutBroker:
     18    "resource:///modules/asrouter/FeatureCalloutBroker.sys.mjs",
     19  InfoBar: "resource:///modules/asrouter/InfoBar.sys.mjs",
     20  SpecialMessageActions:
     21    "resource://messaging-system/lib/SpecialMessageActions.sys.mjs",
     22  Spotlight: "resource:///modules/asrouter/Spotlight.sys.mjs",
     23 });
     24 
     25 const SWITCH_THEMES = {
     26  DARK: "firefox-compact-dark@mozilla.org",
     27  LIGHT: "firefox-compact-light@mozilla.org",
     28 };
     29 
     30 function dispatchCFRAction({ type, data }, browser) {
     31  if (type === "USER_ACTION") {
     32    lazy.SpecialMessageActions.handleAction(data, browser);
     33  }
     34 }
     35 
     36 export class AboutMessagePreviewParent extends JSWindowActorParent {
     37  constructor() {
     38    super();
     39 
     40    const EXISTING_THEME = Services.prefs.getStringPref(
     41      "extensions.activeThemeID"
     42    );
     43 
     44    this._onUnload = () => {
     45      lazy.AddonManager.getAddonByID(EXISTING_THEME).then(addon =>
     46        addon.enable()
     47      );
     48    };
     49  }
     50 
     51  didDestroy() {
     52    this._onUnload();
     53  }
     54 
     55  showInfoBar(message, browser) {
     56    lazy.InfoBar.showInfoBarMessage(browser, message, dispatchCFRAction);
     57  }
     58 
     59  showSpotlight(message, browser) {
     60    lazy.Spotlight.showSpotlightDialog(browser, message, () => {});
     61  }
     62 
     63  showBookmarksBarButton(message, browser) {
     64    //ensure the bookmarks bar is open
     65    lazy.CustomizableUI.setToolbarVisibility(
     66      lazy.CustomizableUI.AREA_BOOKMARKS,
     67      true
     68    );
     69    //and then send the message
     70    lazy.BookmarksBarButton.showBookmarksBarButton(browser, message);
     71  }
     72 
     73  showCFR(message, browser) {
     74    lazy.CFRPageActions.forceRecommendation(
     75      browser,
     76      message,
     77      dispatchCFRAction
     78    );
     79  }
     80 
     81  showPrivateBrowsingMessage(message, browser) {
     82    ASRouter.forcePBWindow(browser, message);
     83  }
     84 
     85  async showFeatureCallout(message, browser) {
     86    // Clear the Feature Tour prefs used by some callouts, to ensure
     87    // the behaviour of the message is correct
     88    let tourPref = message.content.tour_pref_name;
     89    if (tourPref) {
     90      Services.prefs.clearUserPref(tourPref);
     91    }
     92    // For messagePreview, force the trigger && targeting to be something we can show.
     93    message.trigger = { id: "nthTabClosed" };
     94    message.targeting = "true";
     95    // Check whether or not the callout is showing already, then
     96    // modify the anchor property of the feature callout to
     97    // ensure it's something we can show.
     98    let showing = await lazy.FeatureCalloutBroker.showFeatureCallout(
     99      browser,
    100      message
    101    );
    102    if (!showing) {
    103      for (const screen of message.content.screens) {
    104        let existingAnchors = screen.anchors;
    105        let fallbackAnchor = { selector: "#star-button-box" };
    106 
    107        if (existingAnchors[0].hasOwnProperty("arrow_position")) {
    108          fallbackAnchor.arrow_position = "top-center-arrow-end";
    109        } else {
    110          fallbackAnchor.panel_position = {
    111            anchor_attachment: "bottomcenter",
    112            callout_attachment: "topright",
    113          };
    114        }
    115 
    116        screen.anchors = [...existingAnchors, fallbackAnchor];
    117        console.log("ANCHORS: ", screen.anchors);
    118      }
    119      // Try showing again
    120      await lazy.FeatureCalloutBroker.showFeatureCallout(browser, message);
    121    }
    122  }
    123 
    124  /**
    125   * Chooses the appropriate messaging system function for showing
    126   * the message, based on the template passed in data
    127   *
    128   * @param {string} data - a string containing the message JSON
    129   * @param {boolean} validationEnabled - whether or not to run
    130   * schema validation on the message JSON. Should be false in
    131   * tests so that we don't have to pass real messages or call
    132   * the validation function.
    133   */
    134 
    135  async showMessage(data, validationEnabled = true) {
    136    let message;
    137    try {
    138      message = JSON.parse(data);
    139    } catch (e) {
    140      console.error("Could not parse message", e);
    141      return;
    142    }
    143 
    144    if (validationEnabled) {
    145      const schema = await fetch(
    146        "chrome://browser/content/asrouter/schemas/MessagingExperiment.schema.json",
    147        { credentials: "omit" }
    148      ).then(rsp => rsp.json());
    149      const result = JsonSchema.validate(message, schema);
    150      if (!result.valid) {
    151        console.error(
    152          `Invalid message: ${JSON.stringify(result.errors, undefined, 2)}`
    153        );
    154      }
    155    }
    156 
    157    const browser =
    158      this.browsingContext.topChromeWindow.gBrowser.selectedBrowser;
    159    switch (message.template) {
    160      case "infobar":
    161        this.showInfoBar(message, browser);
    162        return;
    163      case "spotlight":
    164        this.showSpotlight(message, browser);
    165        return;
    166      case "cfr_doorhanger":
    167        this.showCFR(message, browser);
    168        return;
    169      case "feature_callout":
    170        this.showFeatureCallout(message, browser);
    171        return;
    172      case "bookmarks_bar_button":
    173        this.showBookmarksBarButton(message, browser);
    174        return;
    175      case "pb_newtab":
    176        this.showPrivateBrowsingMessage(message, browser);
    177        return;
    178      default:
    179        console.error(`Unsupported message template ${message.template}`);
    180    }
    181  }
    182 
    183  receiveMessage(message) {
    184    // validationEnabled is used for testing
    185    const { name, data, validationEnabled } = message;
    186 
    187    switch (name) {
    188      case "MessagePreview:SHOW_MESSAGE":
    189        this.showMessage(data, validationEnabled);
    190        return;
    191      case "MessagePreview:CHANGE_THEME": {
    192        const theme = data.isDark ? SWITCH_THEMES.LIGHT : SWITCH_THEMES.DARK;
    193        lazy.AddonManager.getAddonByID(theme).then(addon => addon.enable());
    194        return;
    195      }
    196      default:
    197        console.log(`Unexpected event ${name} was not handled.`);
    198    }
    199  }
    200 }