tor-browser

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

css-transform.js (6838B)


      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  AutoRefreshHighlighter,
      9 } = require("resource://devtools/server/actors/highlighters/auto-refresh.js");
     10 const {
     11  CanvasFrameAnonymousContentHelper,
     12  getComputedStyle,
     13 } = require("resource://devtools/server/actors/highlighters/utils/markup.js");
     14 const {
     15  setIgnoreLayoutChanges,
     16  getNodeBounds,
     17 } = require("resource://devtools/shared/layout/utils.js");
     18 
     19 // The minimum distance a line should be before it has an arrow marker-end
     20 const ARROW_LINE_MIN_DISTANCE = 10;
     21 
     22 var MARKER_COUNTER = 1;
     23 
     24 /**
     25 * The CssTransformHighlighter is the class that draws an outline around a
     26 * transformed element and an outline around where it would be if untransformed
     27 * as well as arrows connecting the 2 outlines' corners.
     28 */
     29 class CssTransformHighlighter extends AutoRefreshHighlighter {
     30  constructor(highlighterEnv) {
     31    super(highlighterEnv);
     32 
     33    this.markup = new CanvasFrameAnonymousContentHelper(
     34      this.highlighterEnv,
     35      this._buildMarkup.bind(this),
     36      {
     37        contentRootHostClassName: "devtools-highlighter-css-transform",
     38      }
     39    );
     40    this.isReady = this.markup.initialize();
     41  }
     42 
     43  _buildMarkup() {
     44    const container = this.markup.createNode({
     45      attributes: {
     46        class: "highlighter-container",
     47      },
     48    });
     49 
     50    // The root wrapper is used to unzoom the highlighter when needed.
     51    this.rootEl = this.markup.createNode({
     52      parent: container,
     53      attributes: {
     54        id: "css-transform-root",
     55        class: "css-transform-root",
     56      },
     57    });
     58 
     59    const svg = this.markup.createSVGNode({
     60      nodeType: "svg",
     61      parent: this.rootEl,
     62      attributes: {
     63        id: "css-transform-elements",
     64        hidden: "true",
     65        width: "100%",
     66        height: "100%",
     67      },
     68    });
     69 
     70    // Add a marker tag to the svg root for the arrow tip
     71    this.markerId = "css-transform-arrow-marker-" + MARKER_COUNTER;
     72    MARKER_COUNTER++;
     73    const marker = this.markup.createSVGNode({
     74      nodeType: "marker",
     75      parent: svg,
     76      attributes: {
     77        id: this.markerId,
     78        markerWidth: "10",
     79        markerHeight: "5",
     80        orient: "auto",
     81        markerUnits: "strokeWidth",
     82        refX: "10",
     83        refY: "5",
     84        viewBox: "0 0 10 10",
     85      },
     86    });
     87    this.markup.createSVGNode({
     88      nodeType: "path",
     89      parent: marker,
     90      attributes: {
     91        d: "M 0 0 L 10 5 L 0 10 z",
     92        fill: "#08C",
     93      },
     94    });
     95 
     96    const shapesGroup = this.markup.createSVGNode({
     97      nodeType: "g",
     98      parent: svg,
     99    });
    100 
    101    // Create the 2 polygons (transformed and untransformed)
    102    this.markup.createSVGNode({
    103      nodeType: "polygon",
    104      parent: shapesGroup,
    105      attributes: {
    106        id: "css-transform-untransformed",
    107        class: "css-transform-untransformed",
    108      },
    109    });
    110    this.markup.createSVGNode({
    111      nodeType: "polygon",
    112      parent: shapesGroup,
    113      attributes: {
    114        id: "css-transform-transformed",
    115        class: "css-transform-transformed",
    116      },
    117    });
    118 
    119    // Create the arrows
    120    for (const nb of ["1", "2", "3", "4"]) {
    121      this.markup.createSVGNode({
    122        nodeType: "line",
    123        parent: shapesGroup,
    124        attributes: {
    125          id: "css-transform-line" + nb,
    126          class: "css-transform-line",
    127          "marker-end": "url(#" + this.markerId + ")",
    128        },
    129      });
    130    }
    131 
    132    return container;
    133  }
    134 
    135  /**
    136   * Destroy the nodes. Remove listeners.
    137   */
    138  destroy() {
    139    AutoRefreshHighlighter.prototype.destroy.call(this);
    140    this.markup.destroy();
    141    this.rootEl = null;
    142  }
    143 
    144  getElement(id) {
    145    return this.markup.getElement(id);
    146  }
    147 
    148  /**
    149   * Show the highlighter on a given node
    150   */
    151  _show() {
    152    if (!this._isTransformed(this.currentNode)) {
    153      this.hide();
    154      return false;
    155    }
    156 
    157    return this._update();
    158  }
    159 
    160  /**
    161   * Checks if the supplied node is transformed and not inline
    162   */
    163  _isTransformed(node) {
    164    const style = getComputedStyle(node);
    165    return style && style.transform !== "none" && style.display !== "inline";
    166  }
    167 
    168  _setPolygonPoints(quad, id) {
    169    const points = [];
    170    for (const point of ["p1", "p2", "p3", "p4"]) {
    171      points.push(quad[point].x + "," + quad[point].y);
    172    }
    173    this.getElement(id).setAttribute("points", points.join(" "));
    174  }
    175 
    176  _setLinePoints(p1, p2, id) {
    177    const line = this.getElement(id);
    178    line.setAttribute("x1", p1.x);
    179    line.setAttribute("y1", p1.y);
    180    line.setAttribute("x2", p2.x);
    181    line.setAttribute("y2", p2.y);
    182 
    183    const dist = Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));
    184    if (dist < ARROW_LINE_MIN_DISTANCE) {
    185      line.removeAttribute("marker-end");
    186    } else {
    187      line.setAttribute("marker-end", "url(#" + this.markerId + ")");
    188    }
    189  }
    190 
    191  /**
    192   * Update the highlighter on the current highlighted node (the one that was
    193   * passed as an argument to show(node)).
    194   * Should be called whenever node size or attributes change
    195   */
    196  _update() {
    197    setIgnoreLayoutChanges(true);
    198 
    199    // Getting the points for the transformed shape
    200    const quads = this.currentQuads.border;
    201    if (
    202      !quads.length ||
    203      quads[0].bounds.width <= 0 ||
    204      quads[0].bounds.height <= 0
    205    ) {
    206      this._hideShapes();
    207      return false;
    208    }
    209 
    210    const [quad] = quads;
    211 
    212    // Getting the points for the untransformed shape
    213    const untransformedQuad = getNodeBounds(this.win, this.currentNode);
    214 
    215    this._setPolygonPoints(quad, "css-transform-transformed");
    216    this._setPolygonPoints(untransformedQuad, "css-transform-untransformed");
    217    this._setLinePoints(untransformedQuad.p1, quad.p1, "css-transform-line1");
    218    this._setLinePoints(untransformedQuad.p2, quad.p2, "css-transform-line2");
    219    this._setLinePoints(untransformedQuad.p3, quad.p3, "css-transform-line3");
    220    this._setLinePoints(untransformedQuad.p4, quad.p4, "css-transform-line4");
    221 
    222    // Adapt to the current zoom
    223    this.markup.scaleRootElement(this.currentNode, "css-transform-root");
    224 
    225    this._showShapes();
    226 
    227    setIgnoreLayoutChanges(
    228      false,
    229      this.highlighterEnv.window.document.documentElement
    230    );
    231    return true;
    232  }
    233 
    234  /**
    235   * Hide the highlighter, the outline and the infobar.
    236   */
    237  _hide() {
    238    setIgnoreLayoutChanges(true);
    239    this._hideShapes();
    240    setIgnoreLayoutChanges(
    241      false,
    242      this.highlighterEnv.window.document.documentElement
    243    );
    244  }
    245 
    246  _hideShapes() {
    247    this.getElement("css-transform-elements").setAttribute("hidden", "true");
    248  }
    249 
    250  _showShapes() {
    251    this.getElement("css-transform-elements").removeAttribute("hidden");
    252  }
    253 }
    254 
    255 exports.CssTransformHighlighter = CssTransformHighlighter;