Bug 1518618 - Add custom classes to the selectors for matches, attributes and pseudoclasses. r=rcaliman
authorGabriel Luong <gabriel.luong@gmail.com>
Fri, 11 Jan 2019 14:11:32 -0500
changeset 453605 922c3f0e3d8cd9c7f8fe752ce38a5df6db19d3fe
parent 453519 27845cbdcac6f18002fd0d46021ba963487c6659
child 453606 b7e4f51b79a9c739be5e661025fd7e3534031073
push id35361
push usernbeleuzu@mozilla.com
push dateSat, 12 Jan 2019 09:41:19 +0000
treeherdermozilla-central@a44934afe25e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrcaliman
bugs1518618
milestone66.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1518618 - Add custom classes to the selectors for matches, attributes and pseudoclasses. r=rcaliman
devtools/client/inspector/rules/components/Rule.js
devtools/client/inspector/rules/components/Selector.js
devtools/client/inspector/rules/constants.js
devtools/client/inspector/rules/models/rule.js
devtools/client/inspector/rules/moz.build
--- a/devtools/client/inspector/rules/components/Rule.js
+++ b/devtools/client/inspector/rules/components/Rule.js
@@ -22,25 +22,29 @@ class Rule extends PureComponent {
   }
 
   render() {
     const { rule } = this.props;
     const {
       declarations,
       selector,
       sourceLink,
+      type,
     } = rule;
 
     return (
       dom.div(
         { className: "ruleview-rule devtools-monospace" },
         SourceLink({ sourceLink }),
         dom.div({ className: "ruleview-code" },
           dom.div({},
-            Selector({ selector }),
+            Selector({
+              selector,
+              type,
+            }),
             dom.span({ className: "ruleview-ruleopen" }, " {")
           ),
           Declarations({ declarations }),
           dom.div({ className: "ruleview-ruleclose" }, "}")
         )
       )
     );
   }
--- a/devtools/client/inspector/rules/components/Selector.js
+++ b/devtools/client/inspector/rules/components/Selector.js
@@ -3,33 +3,96 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { PureComponent } = require("devtools/client/shared/vendor/react");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 
+const {
+  parsePseudoClassesAndAttributes,
+  SELECTOR_ATTRIBUTE,
+  SELECTOR_ELEMENT,
+  SELECTOR_PSEUDO_CLASS,
+} = require("devtools/shared/css/parsing-utils");
+const {
+  ELEMENT_STYLE,
+  PSEUDO_CLASSES,
+} = require("devtools/client/inspector/rules/constants");
+
 const Types = require("../types");
 
 class Selector extends PureComponent {
   static get propTypes() {
     return {
       selector: PropTypes.shape(Types.selector).isRequired,
+      type: PropTypes.number.isRequired,
     };
   }
 
+  renderSelector() {
+    // Show the text directly for custom selector text (such as the inline "element"
+    // style and Keyframes rules).
+    if (this.props.type === ELEMENT_STYLE || this.props.type === CSSRule.KEYFRAME_RULE) {
+      return this.props.selector.selectorText;
+    }
+
+    const { matchedSelectors, selectors } = this.props.selector;
+    const output = [];
+
+    // Go through the CSS rule's selectors and highlight the selectors that actually
+    // matches.
+    for (let i = 0; i < selectors.length; i++) {
+      const selector = selectors[i];
+
+      // Parse the selector for pseudo classes and attributes, and apply different
+      // CSS classes for the parsed values.
+      // NOTE: parsePseudoClassesAndAttributes is a good candidate for memoization.
+      const parsedSelector = parsePseudoClassesAndAttributes(selector);
+      for (const parsedText of parsedSelector) {
+        let selectorSpanClass = matchedSelectors.indexOf(selector) > -1 ?
+          "ruleview-selector-matched" : "ruleview-selector-unmatched";
+
+        switch (parsedText.type) {
+          case SELECTOR_ATTRIBUTE:
+            selectorSpanClass += " ruleview-selector-attribute";
+            break;
+          case SELECTOR_ELEMENT:
+            selectorSpanClass += " ruleview-selector";
+            break;
+          case SELECTOR_PSEUDO_CLASS:
+            selectorSpanClass += PSEUDO_CLASSES.some(p => parsedText.value === p) ?
+              " ruleview-selector-pseudo-class-lock" :
+              " ruleview-selector-pseudo-class";
+            break;
+        }
+
+        output.push(
+          dom.span({ className: selectorSpanClass }, parsedText.value)
+        );
+      }
+
+      // Append a comma separator unless this is the last selector.
+      if (i < selectors.length - 1) {
+        output.push(
+          dom.span({ className: "ruleview-selector-separator" }, ", ")
+        );
+      }
+    }
+
+    return output;
+  }
+
   render() {
-    const { selectorText } = this.props.selector;
-
     return (
       dom.span(
         {
           className: "ruleview-selectorcontainer",
-          tabIndex: -1,
+          tabIndex: 0,
         },
-        selectorText
+        this.renderSelector()
       )
     );
   }
 }
 
 module.exports = Selector;
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/rules/constants.js
@@ -0,0 +1,13 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+// The PageStyle actor flattens the DOM CSS objects a little bit, merging
+// Rules and their Styles into one actor. For elements (which have a style
+// but no associated rule) we fake a rule with the following style id.
+exports.ELEMENT_STYLE = 100;
+
+// Pseudo classes in the pseudo class panel.
+exports.PSEUDO_CLASSES = [":active", ":focus", ":focus-within", ":hover"];
--- a/devtools/client/inspector/rules/models/rule.js
+++ b/devtools/client/inspector/rules/models/rule.js
@@ -76,17 +76,17 @@ Rule.prototype = {
       inheritedSource: this.inheritedSource,
     };
   },
 
   get selector() {
     return {
       matchedSelectors: this.matchedSelectors,
       selectors: this.domRule.selectors,
-      selectorText: this.selectorText,
+      selectorText: this.keyframes ? this.domRule.keyText : this.selectorText,
     };
   },
 
   get sourceLink() {
     return {
       column: this.ruleColumn,
       line: this.ruleLine,
       mediaText: this.mediaText,
--- a/devtools/client/inspector/rules/moz.build
+++ b/devtools/client/inspector/rules/moz.build
@@ -9,16 +9,17 @@ DIRS += [
     'components',
     'models',
     'reducers',
     'utils',
     'views',
 ]
 
 DevToolsModules(
+    'constants.js',
     'new-rules.js',
     'rules.js',
     'types.js',
 )
 
 BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
 
 with Files('**'):