tor-browser

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

editing-session.js (6131B)


      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 /**
      8 * An instance of EditingSession tracks changes that have been made during the
      9 * modification of box model values. All of these changes can be reverted by
     10 * calling revert.
     11 */
     12 class EditingSession {
     13  /**
     14   * @param {object} options
     15   * @param  {InspectorPanel} options.inspector
     16   *         The inspector panel.
     17   * @param  {Document} options.doc
     18   *         A DOM document that can be used to test style rules.
     19   * @param  {Array} options.elementRules
     20   *         An array of the style rules defined for the node being
     21   *         edited. These should be in order of priority, least
     22   *         important first.
     23   */
     24  constructor({ inspector, doc, elementRules }) {
     25    this._doc = doc;
     26    this._inspector = inspector;
     27    this._rules = elementRules;
     28    this._modifications = new Map();
     29  }
     30  /**
     31   * Gets the value of a single property from the CSS rule.
     32   *
     33   * @param  {StyleRuleFront} rule
     34   *         The CSS rule.
     35   * @param  {string} property
     36   *         The name of the property.
     37   * @return {string} the value.
     38   */
     39  getPropertyFromRule(rule, property) {
     40    // Use the parsed declarations in the StyleRuleFront object if available.
     41    const index = this.getPropertyIndex(property, rule);
     42    if (index !== -1) {
     43      return rule.declarations[index].value;
     44    }
     45 
     46    // Fallback to parsing the cssText locally otherwise.
     47    const dummyStyle = this._element.style;
     48    dummyStyle.cssText = rule.cssText;
     49    return dummyStyle.getPropertyValue(property);
     50  }
     51 
     52  /**
     53   * Returns the current value for a property as a string or the empty string if
     54   * no style rules affect the property.
     55   *
     56   * @param  {string} property
     57   *         The name of the property as a string
     58   */
     59  getProperty(property) {
     60    // Create a hidden element for getPropertyFromRule to use
     61    const div = this._doc.createElement("div");
     62    div.setAttribute("style", "display: none");
     63    this._doc.getElementById("inspector-main-content").appendChild(div);
     64    this._element = this._doc.createElement("p");
     65    div.appendChild(this._element);
     66 
     67    // As the rules are in order of priority we can just iterate until we find
     68    // the first that defines a value for the property and return that.
     69    for (const rule of this._rules) {
     70      const value = this.getPropertyFromRule(rule, property);
     71      if (value !== "") {
     72        div.remove();
     73        return value;
     74      }
     75    }
     76    div.remove();
     77    return "";
     78  }
     79 
     80  /**
     81   * Get the index of a given css property name in a CSS rule.
     82   * Or -1, if there are no properties in the rule yet.
     83   *
     84   * @param  {string} name
     85   *         The property name.
     86   * @param  {StyleRuleFront} rule
     87   *         Optional, defaults to the element style rule.
     88   * @return {number} The property index in the rule.
     89   */
     90  getPropertyIndex(name, rule = this._rules[0]) {
     91    if (!rule.declarations.length) {
     92      return -1;
     93    }
     94 
     95    return rule.declarations.findIndex(p => p.name === name);
     96  }
     97 
     98  /**
     99   * Sets a number of properties on the node.
    100   *
    101   * @param  {Array} properties
    102   *         An array of properties, each is an object with name and
    103   *         value properties. If the value is "" then the property
    104   *         is removed.
    105   * @return {Promise} Resolves when the modifications are complete.
    106   */
    107  async setProperties(properties) {
    108    for (const property of properties) {
    109      // Get a RuleModificationList or RuleRewriter helper object from the
    110      // StyleRuleActor to make changes to CSS properties.
    111      // Note that RuleRewriter doesn't support modifying several properties at
    112      // once, so we do this in a sequence here.
    113      const modifications = this._rules[0].startModifyingProperties(
    114        this._inspector.panelWin,
    115        this._inspector.cssProperties
    116      );
    117 
    118      // Remember the property so it can be reverted.
    119      if (!this._modifications.has(property.name)) {
    120        this._modifications.set(
    121          property.name,
    122          this.getPropertyFromRule(this._rules[0], property.name)
    123        );
    124      }
    125 
    126      // Find the index of the property to be changed, or get the next index to
    127      // insert the new property at.
    128      let index = this.getPropertyIndex(property.name);
    129      if (index === -1) {
    130        index = this._rules[0].declarations.length;
    131      }
    132 
    133      if (property.value == "") {
    134        modifications.removeProperty(index, property.name);
    135      } else {
    136        modifications.setProperty(index, property.name, property.value, "");
    137      }
    138 
    139      await modifications.apply();
    140    }
    141  }
    142 
    143  /**
    144   * Reverts all of the property changes made by this instance.
    145   *
    146   * @return {Promise} Resolves when all properties have been reverted.
    147   */
    148  async revert() {
    149    // Revert each property that we modified previously, one by one. See
    150    // setProperties for information about why.
    151    for (const [property, value] of this._modifications) {
    152      const modifications = this._rules[0].startModifyingProperties(
    153        this._inspector.panelWin,
    154        this._inspector.cssProperties
    155      );
    156 
    157      // Find the index of the property to be reverted.
    158      let index = this.getPropertyIndex(property);
    159 
    160      if (value != "") {
    161        // If the property doesn't exist anymore, insert at the beginning of the
    162        // rule.
    163        if (index === -1) {
    164          index = 0;
    165        }
    166        modifications.setProperty(index, property, value, "");
    167      } else {
    168        // If the property doesn't exist anymore, no need to remove it. It had
    169        // not been added after all.
    170        if (index === -1) {
    171          continue;
    172        }
    173        modifications.removeProperty(index, property);
    174      }
    175 
    176      await modifications.apply();
    177    }
    178  }
    179 
    180  destroy() {
    181    this._modifications.clear();
    182 
    183    this._cssProperties = null;
    184    this._doc = null;
    185    this._inspector = null;
    186    this._modifications = null;
    187    this._rules = null;
    188  }
    189 }
    190 
    191 module.exports = EditingSession;