tor-browser

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

remote-node-picker-notice.js (5187B)


      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 "use strict";
      6 
      7 const {
      8  CanvasFrameAnonymousContentHelper,
      9 } = require("resource://devtools/server/actors/highlighters/utils/markup.js");
     10 
     11 loader.lazyGetter(this, "HighlightersBundle", () => {
     12  return new Localization(["devtools/shared/highlighters.ftl"], true);
     13 });
     14 
     15 loader.lazyGetter(this, "isAndroid", () => {
     16  return Services.appinfo.OS === "Android";
     17 });
     18 
     19 /**
     20 * The RemoteNodePickerNotice is a class that displays a notice in a remote debugged page.
     21 * This is used to signal to users they can click/tap an element to select it in the
     22 * about:devtools-toolbox toolbox inspector.
     23 */
     24 class RemoteNodePickerNotice {
     25  #highlighterEnvironment;
     26  #previousHoveredElement;
     27 
     28  rootElementId = "node-picker-notice-root";
     29  hideButtonId = "node-picker-notice-hide-button";
     30  infoNoticeElementId = "node-picker-notice-info";
     31 
     32  /**
     33   * @param {highlighterEnvironment} highlighterEnvironment
     34   */
     35  constructor(highlighterEnvironment) {
     36    this.#highlighterEnvironment = highlighterEnvironment;
     37 
     38    this.markup = new CanvasFrameAnonymousContentHelper(
     39      this.#highlighterEnvironment,
     40      this.#buildMarkup,
     41      {
     42        contentRootHostClassName:
     43          "devtools-highlighter-remote-node-picker-notice",
     44      }
     45    );
     46    this.isReady = this.markup.initialize();
     47  }
     48 
     49  #buildMarkup = () => {
     50    const container = this.markup.createNode({
     51      attributes: { class: "highlighter-container" },
     52    });
     53 
     54    // Wrapper element.
     55    const wrapper = this.markup.createNode({
     56      parent: container,
     57      attributes: {
     58        id: this.rootElementId,
     59        hidden: "true",
     60        overlay: "true",
     61      },
     62    });
     63 
     64    const toolbar = this.markup.createNode({
     65      parent: wrapper,
     66      attributes: {
     67        id: "node-picker-notice-toolbar",
     68        class: "toolbar",
     69      },
     70    });
     71 
     72    this.markup.createNode({
     73      parent: toolbar,
     74      attributes: {
     75        id: "node-picker-notice-icon",
     76        class: isAndroid ? "touch" : "",
     77      },
     78    });
     79 
     80    const actionStr = HighlightersBundle.formatValueSync(
     81      isAndroid
     82        ? "remote-node-picker-notice-action-touch"
     83        : "remote-node-picker-notice-action-desktop"
     84    );
     85 
     86    this.markup.createNode({
     87      nodeType: "span",
     88      parent: toolbar,
     89      text: HighlightersBundle.formatValueSync("remote-node-picker-notice", {
     90        action: actionStr,
     91      }),
     92      attributes: {
     93        id: this.infoNoticeElementId,
     94      },
     95    });
     96 
     97    this.markup.createNode({
     98      nodeType: "button",
     99      parent: toolbar,
    100      text: HighlightersBundle.formatValueSync(
    101        "remote-node-picker-notice-hide-button"
    102      ),
    103      attributes: {
    104        id: this.hideButtonId,
    105      },
    106    });
    107 
    108    return container;
    109  };
    110 
    111  destroy() {
    112    // hide will nullify take care of this.#abortController.
    113    this.hide();
    114    this.markup.destroy();
    115    this.#highlighterEnvironment = null;
    116    this.#previousHoveredElement = null;
    117  }
    118 
    119  /**
    120   * We can't use event listener directly on the anonymous content because they aren't
    121   * working while the page is paused.
    122   * This is called from the NodePicker instance for easier events management.
    123   *
    124   * @param {ClickEvent}
    125   */
    126  onClick(e) {
    127    const target = e.originalTarget || e.target;
    128    const targetId = target?.id;
    129 
    130    if (targetId === this.hideButtonId) {
    131      this.hide();
    132    }
    133  }
    134 
    135  /**
    136   * Since we can't use :hover in the CSS for the anonymous content as it wouldn't work
    137   * when the page is paused, we have to roll our own implementation, adding a `.hover`
    138   * class for the element we want to style on hover (e.g. the close button).
    139   * This is called from the NodePicker instance for easier events management.
    140   *
    141   * @param {MouseMoveEvent}
    142   */
    143  handleHoveredElement(e) {
    144    const hideButton = this.markup.getElement(this.hideButtonId);
    145 
    146    const target = e.originalTarget || e.target;
    147    const targetId = target?.id;
    148 
    149    // If the user didn't change targets, do nothing
    150    if (this.#previousHoveredElement?.id === targetId) {
    151      return;
    152    }
    153 
    154    if (targetId === this.hideButtonId) {
    155      hideButton.classList?.add("hover");
    156    } else {
    157      hideButton.classList?.remove("hover");
    158    }
    159    this.#previousHoveredElement = target;
    160  }
    161 
    162  getMarkupRootElement() {
    163    return this.markup.getElement(this.rootElementId);
    164  }
    165 
    166  async show() {
    167    if (this.#highlighterEnvironment.isXUL) {
    168      return false;
    169    }
    170    await this.isReady;
    171 
    172    // Show the highlighter's root element.
    173    const root = this.getMarkupRootElement();
    174    root.removeAttribute("hidden");
    175    root.setAttribute("overlay", "true");
    176 
    177    return true;
    178  }
    179 
    180  hide() {
    181    if (this.#highlighterEnvironment.isXUL) {
    182      return;
    183    }
    184 
    185    // Hide the overlay.
    186    this.getMarkupRootElement().setAttribute("hidden", "true");
    187    // Reset the hover state
    188    this.markup.getElement(this.hideButtonId).classList.remove("hover");
    189    this.#previousHoveredElement = null;
    190  }
    191 }
    192 exports.RemoteNodePickerNotice = RemoteNodePickerNotice;