tor-browser

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

registered-property-editor.js (6050B)


      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 EventEmitter = require("resource://devtools/shared/event-emitter.js");
      8 const {
      9  appendText,
     10  createChild,
     11 } = require("resource://devtools/client/inspector/shared/utils.js");
     12 
     13 const INDENT_SIZE = 2;
     14 const INDENT_STR = " ".repeat(INDENT_SIZE);
     15 
     16 /**
     17 * RegisteredPropertyEditor creates a list of TextPropertyEditors for a given
     18 * CSS registered property propertyDefinition that can be rendered in the Rules view.
     19 *
     20 * @param {CssRuleView} ruleView
     21 *        The CssRuleView containg the document holding this rule editor.
     22 * @param {Rule} rule
     23 *        The Rule object we're editing.
     24 */
     25 class RegisteredPropertyEditor extends EventEmitter {
     26  /**
     27   * @param {CssRuleView} ruleView
     28   *        The CssRuleView containing the document holding this rule editor.
     29   * @param {object} propertyDefinition
     30   *        The property definition data as returned by PageStyleActor's getRegisteredProperties
     31   */
     32  constructor(ruleView, propertyDefinition) {
     33    super();
     34 
     35    this.#doc = ruleView.styleDocument;
     36    this.#propertyDefinition = propertyDefinition;
     37    this.#createElement();
     38  }
     39 
     40  #doc;
     41  #propertyDefinition;
     42  // The HTMLElement that will represent the registered property. Populated in #createElement.
     43  element = null;
     44 
     45  #createElement() {
     46    this.element = this.#doc.createElement("div");
     47    this.element.className = "ruleview-rule devtools-monospace";
     48    this.element.setAttribute("uneditable", true);
     49    this.element.setAttribute("unmatched", false);
     50    this.element.setAttribute("data-name", this.#propertyDefinition.name);
     51 
     52    // Give a relative position for the inplace editor's measurement
     53    // span to be placed absolutely against.
     54    this.element.style.position = "relative";
     55 
     56    const code = createChild(this.element, "code", {
     57      class: "ruleview-code",
     58    });
     59 
     60    const header = createChild(code, "header", {});
     61 
     62    this.propertyName = createChild(header, "span", {
     63      class: "ruleview-registered-property-name",
     64      textContent: this.#propertyDefinition.name,
     65    });
     66 
     67    this.openBrace = createChild(header, "span", {
     68      class: "ruleview-ruleopen",
     69      textContent: " {",
     70    });
     71 
     72    // We can't use a proper "ol" as it will mess with selection copy text,
     73    // adding spaces on list item instead of the one we craft (.ruleview-rule-indent)
     74    this.propertyList = createChild(code, "div", {
     75      class: "ruleview-propertylist",
     76      role: "list",
     77    });
     78 
     79    this.#populateProperties();
     80 
     81    this.closeBrace = createChild(code, "div", {
     82      class: "ruleview-ruleclose",
     83      textContent: "}",
     84    });
     85  }
     86 
     87  /**
     88   * Sets the content of this.#propertyList with the contents of the registered property .
     89   */
     90  #populateProperties() {
     91    const properties = [
     92      {
     93        name: "syntax",
     94        value: `"${this.#propertyDefinition.syntax}"`,
     95      },
     96      {
     97        name: "inherits",
     98        value: this.#propertyDefinition.inherits,
     99      },
    100    ];
    101 
    102    // The initial value may not be set, when syntax is "*", so let's only display
    103    // it when it is actually set.
    104    if (this.#propertyDefinition.initialValue !== null) {
    105      // For JS-defined properties, we want to display them in the same syntax that
    106      // was used in CSS.registerProperty (so we'll show `initialValue` and not `initial-value`).
    107      properties.push({
    108        name: this.#propertyDefinition.fromJS
    109          ? "initialValue"
    110          : "initial-value",
    111        value: this.#propertyDefinition.fromJS
    112          ? `"${this.#propertyDefinition.initialValue}"`
    113          : this.#propertyDefinition.initialValue,
    114      });
    115    }
    116 
    117    // When the property is registered with CSS.registerProperty, we want to match the
    118    // object shape of the parameter, so include the "name" property.
    119    if (this.#propertyDefinition.fromJS) {
    120      properties.unshift({
    121        name: "name",
    122        value: `"${this.#propertyDefinition.name}"`,
    123      });
    124    }
    125 
    126    for (const { name, value } of properties) {
    127      // XXX: We could use the TextPropertyEditor here.
    128      // Pros:
    129      // - we'd get the similar markup, so styling would be easier
    130      // - the value would be properly parsed so our various swatches and popups would work
    131      //   out of the box
    132      // - rule view filtering would also work out of the box
    133      // Cons:
    134      // - it is quite tied with the Rules view regular rule, which mean we'd have
    135      //   to modify it to accept registered properties.
    136 
    137      const element = createChild(this.propertyList, "div", {
    138        role: "listitem",
    139      });
    140      const container = createChild(element, "div", {
    141        class: "ruleview-propertycontainer",
    142      });
    143 
    144      createChild(container, "span", {
    145        class: "ruleview-rule-indent clipboard-only",
    146        textContent: INDENT_STR,
    147      });
    148 
    149      const nameContainer = createChild(container, "span", {
    150        class: "ruleview-namecontainer",
    151      });
    152 
    153      createChild(nameContainer, "span", {
    154        class: "ruleview-propertyname theme-fg-color3",
    155        textContent: name,
    156      });
    157 
    158      appendText(nameContainer, ": ");
    159 
    160      // Create a span that will hold the property and semicolon.
    161      // Use this span to create a slightly larger click target
    162      // for the value.
    163      const valueContainer = createChild(container, "span", {
    164        class: "ruleview-propertyvaluecontainer",
    165      });
    166 
    167      // Property value, editable when focused.  Changes to the
    168      // property value are applied as they are typed, and reverted
    169      // if the user presses escape.
    170      createChild(valueContainer, "span", {
    171        class: "ruleview-propertyvalue theme-fg-color1",
    172        textContent: value,
    173      });
    174 
    175      appendText(valueContainer, this.#propertyDefinition.fromJS ? "," : ";");
    176 
    177      this.propertyList.appendChild(element);
    178    }
    179  }
    180 }
    181 
    182 module.exports = RegisteredPropertyEditor;