Bug 1265785 - replace uses of inIDOMUtils.getCSSPseudoElementNames r=pbro
authorGreg Tatum <tatum.creative@gmail.com>
Tue, 12 Jul 2016 06:45:00 +0200
changeset 330035 c194fb332a11d0266ec34dbf466d1e0ed58f7456
parent 330034 6b143301ec2fe573f505a28ea7fb61daf31763f3
child 330036 6648e2942cf97d43f1706e38fb4e5aa61ebf70a3
push id9858
push userjlund@mozilla.com
push dateMon, 01 Aug 2016 14:37:10 +0000
treeherdermozilla-aurora@203106ef6cb6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspbro
bugs1265785
milestone50.0a1
Bug 1265785 - replace uses of inIDOMUtils.getCSSPseudoElementNames r=pbro
devtools/client/inspector/rules/models/element-style.js
devtools/server/actors/css-properties.js
devtools/shared/css-properties-db.js
devtools/shared/fronts/css-properties.js
devtools/shared/tests/unit/test_css-properties-db.js
devtools/shared/tests/unit/xpcshell.ini
--- a/devtools/client/inspector/rules/models/element-style.js
+++ b/devtools/client/inspector/rules/models/element-style.js
@@ -1,30 +1,21 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* 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";
 
-const {Cc, Ci} = require("chrome");
 const promise = require("promise");
 const {Rule} = require("devtools/client/inspector/rules/models/rule");
 const {promiseWarn} = require("devtools/client/inspector/shared/utils");
 const {ELEMENT_STYLE} = require("devtools/shared/specs/styles");
-const {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm");
-
-loader.lazyGetter(this, "PSEUDO_ELEMENTS", () => {
-  return domUtils.getCSSPseudoElementNames();
-});
-
-XPCOMUtils.defineLazyGetter(this, "domUtils", function () {
-  return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
-});
+const {getCssProperties} = require("devtools/shared/fronts/css-properties");
 
 /**
  * ElementStyle is responsible for the following:
  *   Keeps track of which properties are overridden.
  *   Maintains a list of Rule objects for a given element.
  *
  * @param {Element} element
  *        The element whose style we are viewing.
@@ -43,16 +34,17 @@ XPCOMUtils.defineLazyGetter(this, "domUt
 function ElementStyle(element, ruleView, store, pageStyle,
     showUserAgentStyles) {
   this.element = element;
   this.ruleView = ruleView;
   this.store = store || {};
   this.pageStyle = pageStyle;
   this.showUserAgentStyles = showUserAgentStyles;
   this.rules = [];
+  this.cssProperties = getCssProperties(this.ruleView.inspector.toolbox);
 
   // We don't want to overwrite this.store.userProperties so we only create it
   // if it doesn't already exist.
   if (!("userProperties" in this.store)) {
     this.store.userProperties = new UserProperties();
   }
 
   if (!("disabled" in this.store)) {
@@ -203,17 +195,17 @@ ElementStyle.prototype = {
     return true;
   },
 
   /**
    * Calls markOverridden with all supported pseudo elements
    */
   markOverriddenAll: function () {
     this.markOverridden();
-    for (let pseudo of PSEUDO_ELEMENTS) {
+    for (let pseudo of this.cssProperties.pseudoElements) {
       this.markOverridden(pseudo);
     }
   },
 
   /**
    * Mark the properties listed in this.rules for a given pseudo element
    * with an overridden flag if an earlier property overrides it.
    *
--- a/devtools/server/actors/css-properties.js
+++ b/devtools/server/actors/css-properties.js
@@ -23,44 +23,57 @@ exports.CssPropertiesActor = ActorClassW
     this.parent = parent;
   },
 
   destroy() {
     Actor.prototype.destroy.call(this);
   },
 
   getCSSDatabase() {
-    const db = {};
-    const properties = DOMUtils.getCSSPropertyNames(DOMUtils.INCLUDE_ALIASES);
-
-    properties.forEach(name => {
-      // Get the list of CSS types this property supports.
-      let supports = [];
-      for (let type in CSS_TYPES) {
-        if (safeCssPropertySupportsType(name, DOMUtils["TYPE_" + type])) {
-          supports.push(CSS_TYPES[type]);
-        }
-      }
+    const properties = generateCssProperties();
+    const pseudoElements = DOMUtils.getCSSPseudoElementNames();
 
-      // In order to maintain any backwards compatible changes when debugging older
-      // clients, take the definition from the static CSS properties database, and fill it
-      // in with the most recent property definition from the server.
-      const clientDefinition = CSS_PROPERTIES[name] || {};
-      const serverDefinition = {
-        isInherited: DOMUtils.isInheritedProperty(name),
-        supports
-      };
-      db[name] = Object.assign(clientDefinition, serverDefinition);
-    });
-
-    return db;
+    return { properties, pseudoElements };
   }
 });
 
 /**
+ * Generate the CSS properties object. Every key is the property name, while
+ * the values are objects that contain information about that property.
+ *
+ * @return {Object}
+ */
+function generateCssProperties() {
+  const properties = {};
+  const propertyNames = DOMUtils.getCSSPropertyNames(DOMUtils.INCLUDE_ALIASES);
+
+  propertyNames.forEach(name => {
+    // Get the list of CSS types this property supports.
+    let supports = [];
+    for (let type in CSS_TYPES) {
+      if (safeCssPropertySupportsType(name, DOMUtils["TYPE_" + type])) {
+        supports.push(CSS_TYPES[type]);
+      }
+    }
+
+    // In order to maintain any backwards compatible changes when debugging older
+    // clients, take the definition from the static CSS properties database, and fill it
+    // in with the most recent property definition from the server.
+    const clientDefinition = CSS_PROPERTIES[name] || {};
+    const serverDefinition = {
+      isInherited: DOMUtils.isInheritedProperty(name),
+      supports
+    };
+    properties[name] = Object.assign(clientDefinition, serverDefinition);
+  });
+
+  return properties;
+}
+
+/**
  * Test if a CSS is property is known using server-code.
  *
  * @param {string} name
  * @return {Boolean}
  */
 function isCssPropertyKnown(name) {
   try {
     // If the property name is unknown, the cssPropertyIsShorthand
--- a/devtools/shared/css-properties-db.js
+++ b/devtools/shared/css-properties-db.js
@@ -40,16 +40,31 @@ exports.COLOR_TAKING_FUNCTIONS = ["linea
  */
 exports.ANGLE_TAKING_FUNCTIONS = ["linear-gradient", "-moz-linear-gradient",
                                   "repeating-linear-gradient",
                                   "-moz-repeating-linear-gradient", "rotate", "rotateX",
                                   "rotateY", "rotateZ", "rotate3d", "skew", "skewX",
                                   "skewY", "hue-rotate"];
 
 /**
+ * The list of all CSS Pseudo Elements. This list can be generated from:
+ *
+ * let domUtils = Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
+ * domUtils.getCSSPseudoElementNames();
+ */
+exports.PSEUDO_ELEMENTS = [":after", ":before", ":backdrop", ":first-letter",
+                           ":first-line", ":-moz-selection", ":-moz-focus-inner",
+                           ":-moz-focus-outer", ":-moz-list-bullet",
+                           ":-moz-list-number", ":-moz-math-anonymous",
+                           ":-moz-progress-bar", ":-moz-range-track",
+                           ":-moz-range-progress", ":-moz-range-thumb",
+                           ":-moz-meter-bar", ":-moz-placeholder",
+                           ":-moz-color-swatch"];
+
+/**
  * This list is generated from the output of the CssPropertiesActor. If a server
  * does not support the actor, this is loaded as a backup. This list does not
  * guarantee that the server actually supports these CSS properties.
  */
 exports.CSS_PROPERTIES = {
   "align-content": {
     isInherited: false,
     supports: []
@@ -1738,8 +1753,13 @@ exports.CSS_PROPERTIES = {
     isInherited: false,
     supports: []
   },
   "-webkit-user-select": {
     isInherited: false,
     supports: []
   }
 };
+
+exports.CSS_PROPERTIES_DB = {
+  properties: exports.CSS_PROPERTIES,
+  pseudoElements: exports.PSEUDO_ELEMENTS
+};
--- a/devtools/shared/fronts/css-properties.js
+++ b/devtools/shared/fronts/css-properties.js
@@ -1,17 +1,17 @@
 /* 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";
 
 const { FrontClassWithSpec, Front } = require("devtools/shared/protocol");
 const { cssPropertiesSpec } = require("devtools/shared/specs/css-properties");
 const { Task } = require("devtools/shared/task");
-const { CSS_PROPERTIES } = require("devtools/shared/css-properties-db");
+const { CSS_PROPERTIES_DB } = require("devtools/shared/css-properties-db");
 
 /**
  * Build up a regular expression that matches a CSS variable token. This is an
  * ident token that starts with two dashes "--".
  *
  * https://www.w3.org/TR/css-syntax-3/#ident-token-diagram
  */
 var NON_ASCII = "[^\\x00-\\x7F]";
@@ -48,24 +48,25 @@ const CssPropertiesFront = FrontClassWit
 exports.CssPropertiesFront = CssPropertiesFront;
 
 /**
  * Ask questions to a CSS database. This class does not care how the database
  * gets loaded in, only the questions that you can ask to it.
  * Prototype functions are bound to 'this' so they can be passed around as helper
  * functions.
  *
- * @param {Array}  propertiesList
- *                 A list of known properties.
+ * @param {Object} db
+ *                 A database of CSS properties
  * @param {Object} inheritedList
  *                 The key is the property name, the value is whether or not
  *                 that property is inherited.
  */
-function CssProperties(properties) {
-  this.properties = properties;
+function CssProperties(db) {
+  this.properties = db.properties;
+  this.pseudoElements = db.pseudoElements;
 
   this.isKnown = this.isKnown.bind(this);
   this.isInherited = this.isInherited.bind(this);
   this.supportsType = this.supportsType.bind(this);
 }
 
 CssProperties.prototype = {
   /**
@@ -123,31 +124,41 @@ exports.initCssProperties = Task.async(f
 
   let db, front;
 
   // Get the list dynamically if the cssProperties actor exists.
   if (toolbox.target.hasActor("cssProperties")) {
     front = CssPropertiesFront(client, toolbox.target.form);
     db = yield front.getCSSDatabase();
 
-    // Even if the target has the cssProperties actor, it may not be the latest version.
-    // So, the "supports" data may be missing.
-    // Start with the server's list (because that's the correct one), and add the supports
-    // information if required.
-    if (!db.color.supports) {
-      for (let name in db) {
-        if (typeof CSS_PROPERTIES[name] === "object") {
-          db[name].supports = CSS_PROPERTIES[name].supports;
+    // Even if the target has the cssProperties actor, the returned data may
+    // not be in the same shape or have all of the data we need. The following
+    // code normalizes this data.
+
+    // Firefox 49's getCSSDatabase() just returned the properties object, but
+    // now it returns an object with multiple types of CSS information.
+    if (!db.properties) {
+      db = { properties: db };
+    }
+
+    // Fill in any missing DB information from the static database.
+    db = Object.assign({}, CSS_PROPERTIES_DB, db);
+
+    // Add "supports" information to the css properties if it's missing.
+    if (!db.properties.color.supports) {
+      for (let name in db.properties) {
+        if (typeof CSS_PROPERTIES_DB.properties[name] === "object") {
+          db.properties[name].supports = CSS_PROPERTIES_DB.properties[name].supports;
         }
       }
     }
   } else {
     // The target does not support this actor, so require a static list of supported
     // properties.
-    db = CSS_PROPERTIES;
+    db = CSS_PROPERTIES_DB;
   }
 
   const cssProperties = new CssProperties(db);
   cachedCssProperties.set(client, {cssProperties, front});
   return {cssProperties, front};
 });
 
 /**
new file mode 100644
--- /dev/null
+++ b/devtools/shared/tests/unit/test_css-properties-db.js
@@ -0,0 +1,36 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that css-properties-db matches platform.
+
+"use strict";
+
+const DOMUtils = Components.classes["@mozilla.org/inspector/dom-utils;1"]
+                           .getService(Components.interfaces.inIDOMUtils);
+
+const {PSEUDO_ELEMENTS} = require("devtools/shared/css-properties-db");
+
+function run_test() {
+  // Check that the platform and client match for pseudo elements.
+  let foundPseudoElements = 0;
+  const platformPseudoElements = DOMUtils.getCSSPseudoElementNames();
+  const instructions = "If this assertion fails then it means that pseudo elements " +
+                       "have been added, removed, or changed on the platform and need " +
+                       "to be updated on the static client-side list of pseudo " +
+                       "elements within the devtools. " +
+                       "See devtools/shared/css-properties-db.js and " +
+                       "exports.PSEUDO_ELEMENTS for information on how to update the " +
+                       "list of pseudo elements to fix this test.";
+
+  for (let element of PSEUDO_ELEMENTS) {
+    const hasElement = platformPseudoElements.includes(element);
+    ok(hasElement,
+       `"${element}" pseudo element from the client-side CSS properties database was ` +
+       `found on the platform. ${instructions}`);
+    foundPseudoElements += hasElement ? 1 : 0;
+  }
+
+  equal(foundPseudoElements, platformPseudoElements.length,
+        `The client side CSS properties database of psuedo element names should match ` +
+        `those found on the platform. ${instructions}`);
+}
--- a/devtools/shared/tests/unit/xpcshell.ini
+++ b/devtools/shared/tests/unit/xpcshell.ini
@@ -4,16 +4,17 @@ head = head_devtools.js
 tail =
 firefox-appdir = browser
 skip-if = toolkit == 'android' || toolkit == 'gonk'
 support-files =
   exposeLoader.js
 
 [test_assert.js]
 [test_csslexer.js]
+[test_css-properties-db.js]
 [test_fetch-chrome.js]
 [test_fetch-file.js]
 [test_fetch-http.js]
 [test_fetch-resource.js]
 [test_flatten.js]
 [test_indentation.js]
 [test_independent_loaders.js]
 [test_invisible_loader.js]