Bug 1396600 - Make some efforts to lazy load properties-db. r=tromey
authorAlexandre Poirot <poirot.alex@gmail.com>
Wed, 06 Sep 2017 18:54:07 +0200
changeset 660379 492ea790f0493e82e754f18eb2b8cde3ea8946f3
parent 660378 45084f5718b924ffcbd6a791cf3607546c47befb
child 660380 f9c03c26c8763e78a2c70f1afc6cfb0fc92ec493
push id78390
push userbmo:emilio@crisal.io
push dateWed, 06 Sep 2017 23:04:15 +0000
reviewerstromey
bugs1396600
milestone57.0a1
Bug 1396600 - Make some efforts to lazy load properties-db. r=tromey MozReview-Commit-ID: LGgVb5kxN7X
devtools/client/shared/css-angle.js
devtools/client/shared/output-parser.js
devtools/client/shared/test/browser_css_angle.js
devtools/server/actors/css-properties.js
devtools/shared/css/color.js
devtools/shared/css/parsing-utils.js
devtools/shared/fronts/css-properties.js
--- a/devtools/client/shared/css-angle.js
+++ b/devtools/client/shared/css-angle.js
@@ -1,15 +1,16 @@
 /* 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 {CSS_ANGLEUNIT} = require("devtools/shared/css/properties-db");
+loader.lazyRequireGetter(this, "CSS_ANGLEUNIT",
+  "devtools/shared/css/properties-db", true);
 
 const SPECIALVALUES = new Set([
   "initial",
   "inherit",
   "unset"
 ]);
 
 const {getCSSLexer} = require("devtools/shared/css/lexer");
@@ -36,19 +37,22 @@ function CssAngle(angleValue) {
   this.newAngle(angleValue);
 }
 
 module.exports.angleUtils = {
   CssAngle: CssAngle,
   classifyAngle: classifyAngle
 };
 
-CssAngle.ANGLEUNIT = CSS_ANGLEUNIT;
+CssAngle.prototype = {
+  // Still keep trying to lazy load properties-db by lazily getting ANGLEUNIT
+  get ANGLEUNIT() {
+    return CSS_ANGLEUNIT;
+  },
 
-CssAngle.prototype = {
   _angleUnit: null,
   _angleUnitUppercase: false,
 
   // The value as-authored.
   authored: null,
   // A lower-cased copy of |authored|.
   lowerCased: null,
 
@@ -64,157 +68,157 @@ CssAngle.prototype = {
   },
 
   get valid() {
     let token = getCSSLexer(this.authored).nextToken();
     if (!token) {
       return false;
     }
     return (token.tokenType === "dimension"
-      && token.text.toLowerCase() in CssAngle.ANGLEUNIT);
+      && token.text.toLowerCase() in this.ANGLEUNIT);
   },
 
   get specialValue() {
     return SPECIALVALUES.has(this.lowerCased) ? this.authored : null;
   },
 
   get deg() {
     let invalidOrSpecialValue = this._getInvalidOrSpecialValue();
     if (invalidOrSpecialValue !== false) {
       return invalidOrSpecialValue;
     }
 
     let angleUnit = classifyAngle(this.authored);
-    if (angleUnit === CssAngle.ANGLEUNIT.deg) {
+    if (angleUnit === this.ANGLEUNIT.deg) {
       // The angle is valid and is in degree.
       return this.authored;
     }
 
     let degValue;
-    if (angleUnit === CssAngle.ANGLEUNIT.rad) {
+    if (angleUnit === this.ANGLEUNIT.rad) {
       // The angle is valid and is in radian.
       degValue = this.authoredAngleValue / (Math.PI / 180);
     }
 
-    if (angleUnit === CssAngle.ANGLEUNIT.grad) {
+    if (angleUnit === this.ANGLEUNIT.grad) {
       // The angle is valid and is in gradian.
       degValue = this.authoredAngleValue * 0.9;
     }
 
-    if (angleUnit === CssAngle.ANGLEUNIT.turn) {
+    if (angleUnit === this.ANGLEUNIT.turn) {
       // The angle is valid and is in turn.
       degValue = this.authoredAngleValue * 360;
     }
 
-    let unitStr = CssAngle.ANGLEUNIT.deg;
+    let unitStr = this.ANGLEUNIT.deg;
     if (this._angleUnitUppercase === true) {
       unitStr = unitStr.toUpperCase();
     }
     return `${Math.round(degValue * 100) / 100}${unitStr}`;
   },
 
   get rad() {
     let invalidOrSpecialValue = this._getInvalidOrSpecialValue();
     if (invalidOrSpecialValue !== false) {
       return invalidOrSpecialValue;
     }
 
     let unit = classifyAngle(this.authored);
-    if (unit === CssAngle.ANGLEUNIT.rad) {
+    if (unit === this.ANGLEUNIT.rad) {
       // The angle is valid and is in radian.
       return this.authored;
     }
 
     let radValue;
-    if (unit === CssAngle.ANGLEUNIT.deg) {
+    if (unit === this.ANGLEUNIT.deg) {
       // The angle is valid and is in degree.
       radValue = this.authoredAngleValue * (Math.PI / 180);
     }
 
-    if (unit === CssAngle.ANGLEUNIT.grad) {
+    if (unit === this.ANGLEUNIT.grad) {
       // The angle is valid and is in gradian.
       radValue = this.authoredAngleValue * 0.9 * (Math.PI / 180);
     }
 
-    if (unit === CssAngle.ANGLEUNIT.turn) {
+    if (unit === this.ANGLEUNIT.turn) {
       // The angle is valid and is in turn.
       radValue = this.authoredAngleValue * 360 * (Math.PI / 180);
     }
 
-    let unitStr = CssAngle.ANGLEUNIT.rad;
+    let unitStr = this.ANGLEUNIT.rad;
     if (this._angleUnitUppercase === true) {
       unitStr = unitStr.toUpperCase();
     }
     return `${Math.round(radValue * 10000) / 10000}${unitStr}`;
   },
 
   get grad() {
     let invalidOrSpecialValue = this._getInvalidOrSpecialValue();
     if (invalidOrSpecialValue !== false) {
       return invalidOrSpecialValue;
     }
 
     let unit = classifyAngle(this.authored);
-    if (unit === CssAngle.ANGLEUNIT.grad) {
+    if (unit === this.ANGLEUNIT.grad) {
       // The angle is valid and is in gradian
       return this.authored;
     }
 
     let gradValue;
-    if (unit === CssAngle.ANGLEUNIT.deg) {
+    if (unit === this.ANGLEUNIT.deg) {
       // The angle is valid and is in degree
       gradValue = this.authoredAngleValue / 0.9;
     }
 
-    if (unit === CssAngle.ANGLEUNIT.rad) {
+    if (unit === this.ANGLEUNIT.rad) {
       // The angle is valid and is in radian
       gradValue = this.authoredAngleValue / 0.9 / (Math.PI / 180);
     }
 
-    if (unit === CssAngle.ANGLEUNIT.turn) {
+    if (unit === this.ANGLEUNIT.turn) {
       // The angle is valid and is in turn
       gradValue = this.authoredAngleValue * 400;
     }
 
-    let unitStr = CssAngle.ANGLEUNIT.grad;
+    let unitStr = this.ANGLEUNIT.grad;
     if (this._angleUnitUppercase === true) {
       unitStr = unitStr.toUpperCase();
     }
     return `${Math.round(gradValue * 100) / 100}${unitStr}`;
   },
 
   get turn() {
     let invalidOrSpecialValue = this._getInvalidOrSpecialValue();
     if (invalidOrSpecialValue !== false) {
       return invalidOrSpecialValue;
     }
 
     let unit = classifyAngle(this.authored);
-    if (unit === CssAngle.ANGLEUNIT.turn) {
+    if (unit === this.ANGLEUNIT.turn) {
       // The angle is valid and is in turn
       return this.authored;
     }
 
     let turnValue;
-    if (unit === CssAngle.ANGLEUNIT.deg) {
+    if (unit === this.ANGLEUNIT.deg) {
       // The angle is valid and is in degree
       turnValue = this.authoredAngleValue / 360;
     }
 
-    if (unit === CssAngle.ANGLEUNIT.rad) {
+    if (unit === this.ANGLEUNIT.rad) {
       // The angle is valid and is in radian
       turnValue = (this.authoredAngleValue / (Math.PI / 180)) / 360;
     }
 
-    if (unit === CssAngle.ANGLEUNIT.grad) {
+    if (unit === this.ANGLEUNIT.grad) {
       // The angle is valid and is in gradian
       turnValue = this.authoredAngleValue / 400;
     }
 
-    let unitStr = CssAngle.ANGLEUNIT.turn;
+    let unitStr = this.ANGLEUNIT.turn;
     if (this._angleUnitUppercase === true) {
       unitStr = unitStr.toUpperCase();
     }
     return `${Math.round(turnValue * 100) / 100}${unitStr}`;
   },
 
   /**
    * Check whether the angle value is in the special list e.g.
@@ -247,66 +251,66 @@ CssAngle.prototype = {
     // Store a lower-cased version of the angle to help with format
     // testing.  The original text is kept as well so it can be
     // returned when needed.
     this.lowerCased = angle.toLowerCase();
     this._angleUnitUppercase = (angle === angle.toUpperCase());
     this.authored = angle;
 
     let reg = new RegExp(
-      `(${Object.keys(CssAngle.ANGLEUNIT).join("|")})$`, "i");
+      `(${Object.keys(this.ANGLEUNIT).join("|")})$`, "i");
     let unitStartIdx = angle.search(reg);
     this.authoredAngleValue = angle.substring(0, unitStartIdx);
     this.authoredAngleUnit = angle.substring(unitStartIdx, angle.length);
 
     return this;
   },
 
   nextAngleUnit: function () {
     // Get a reordered array from the formats object
     // to have the current format at the front so we can cycle through.
-    let formats = Object.keys(CssAngle.ANGLEUNIT);
+    let formats = Object.keys(this.ANGLEUNIT);
     let putOnEnd = formats.splice(0, formats.indexOf(this.angleUnit));
     formats = formats.concat(putOnEnd);
     let currentDisplayedValue = this[formats[0]];
 
     for (let format of formats) {
       if (this[format].toLowerCase() !== currentDisplayedValue.toLowerCase()) {
-        this.angleUnit = CssAngle.ANGLEUNIT[format];
+        this.angleUnit = this.ANGLEUNIT[format];
         break;
       }
     }
     return this.toString();
   },
 
   /**
    * Return a string representing a angle
    */
   toString: function () {
     let angle;
 
     switch (this.angleUnit) {
-      case CssAngle.ANGLEUNIT.deg:
+      case this.ANGLEUNIT.deg:
         angle = this.deg;
         break;
-      case CssAngle.ANGLEUNIT.rad:
+      case this.ANGLEUNIT.rad:
         angle = this.rad;
         break;
-      case CssAngle.ANGLEUNIT.grad:
+      case this.ANGLEUNIT.grad:
         angle = this.grad;
         break;
-      case CssAngle.ANGLEUNIT.turn:
+      case this.ANGLEUNIT.turn:
         angle = this.turn;
         break;
       default:
         angle = this.deg;
     }
 
     if (this._angleUnitUppercase &&
-        this.angleUnit != CssAngle.ANGLEUNIT.authored) {
+        this.angleUnit != this.ANGLEUNIT.authored) {
       angle = angle.toUpperCase();
     }
     return angle;
   },
 
   /**
    * This method allows comparison of CssAngle objects using ===.
    */
@@ -322,24 +326,24 @@ CssAngle.prototype = {
  * @param  {String} value
  *         The angle, in any form accepted by CSS.
  * @return {String}
  *         The angle classification, one of "deg", "rad", "grad", or "turn".
  */
 function classifyAngle(value) {
   value = value.toLowerCase();
   if (value.endsWith("deg")) {
-    return CssAngle.ANGLEUNIT.deg;
+    return CSS_ANGLEUNIT.deg;
   }
 
   if (value.endsWith("grad")) {
-    return CssAngle.ANGLEUNIT.grad;
+    return CSS_ANGLEUNIT.grad;
   }
 
   if (value.endsWith("rad")) {
-    return CssAngle.ANGLEUNIT.rad;
+    return CSS_ANGLEUNIT.rad;
   }
   if (value.endsWith("turn")) {
-    return CssAngle.ANGLEUNIT.turn;
+    return CSS_ANGLEUNIT.turn;
   }
 
-  return CssAngle.ANGLEUNIT.deg;
+  return CSS_ANGLEUNIT.deg;
 }
--- a/devtools/client/shared/output-parser.js
+++ b/devtools/client/shared/output-parser.js
@@ -1,25 +1,29 @@
 /* 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";
 
+loader.lazyRequireGetter(this, "ANGLE_TAKING_FUNCTIONS",
+  "devtools/shared/css/properties-db", true);
+loader.lazyRequireGetter(this, "BASIC_SHAPE_FUNCTIONS",
+  "devtools/shared/css/properties-db", true);
+loader.lazyRequireGetter(this, "BEZIER_KEYWORDS",
+  "devtools/shared/css/properties-db", true);
+loader.lazyRequireGetter(this, "COLOR_TAKING_FUNCTIONS",
+  "devtools/shared/css/properties-db", true);
+loader.lazyRequireGetter(this, "CSS_TYPES",
+  "devtools/shared/css/properties-db", true);
+
 const {angleUtils} = require("devtools/client/shared/css-angle");
 const {colorUtils} = require("devtools/shared/css/color");
 const {getCSSLexer} = require("devtools/shared/css/lexer");
 const EventEmitter = require("devtools/shared/old-event-emitter");
-const {
-  ANGLE_TAKING_FUNCTIONS,
-  BASIC_SHAPE_FUNCTIONS,
-  BEZIER_KEYWORDS,
-  COLOR_TAKING_FUNCTIONS,
-  CSS_TYPES
-} = require("devtools/shared/css/properties-db");
 const {appendText} = require("devtools/client/inspector/shared/utils");
 const Services = require("Services");
 
 const STYLE_INSPECTOR_PROPERTIES = "devtools/shared/locales/styleinspector.properties";
 const {LocalizationHelper} = require("devtools/shared/l10n");
 const STYLE_INSPECTOR_L10N = new LocalizationHelper(STYLE_INSPECTOR_PROPERTIES);
 
 const HTML_NS = "http://www.w3.org/1999/xhtml";
--- a/devtools/client/shared/test/browser_css_angle.js
+++ b/devtools/client/shared/test/browser_css_angle.js
@@ -43,26 +43,27 @@ function testAngleValidity() {
     let validString = testAngle.valid ? " a valid" : "an invalid";
 
     is(testAngle.valid, result,
        `Testing that "${angle}" is ${validString} angle`);
   }
 }
 
 function testToString(angle, deg, rad, grad, turn) {
-  angle.angleUnit = angleUtils.CssAngle.ANGLEUNIT.deg;
+  let { ANGLEUNIT } = angleUtils.CssAngle.prototype;
+  angle.angleUnit = ANGLEUNIT.deg;
   is(angle.toString(), deg, "toString() with deg type");
 
-  angle.angleUnit = angleUtils.CssAngle.ANGLEUNIT.rad;
+  angle.angleUnit = ANGLEUNIT.rad;
   is(angle.toString(), rad, "toString() with rad type");
 
-  angle.angleUnit = angleUtils.CssAngle.ANGLEUNIT.grad;
+  angle.angleUnit = ANGLEUNIT.grad;
   is(angle.toString(), grad, "toString() with grad type");
 
-  angle.angleUnit = angleUtils.CssAngle.ANGLEUNIT.turn;
+  angle.angleUnit = ANGLEUNIT.turn;
   is(angle.toString(), turn, "toString() with turn type");
 }
 
 function getAngleValidityData() {
   return [{
     angle: "0.2turn",
     result: true
   }, {
--- a/devtools/server/actors/css-properties.js
+++ b/devtools/server/actors/css-properties.js
@@ -1,24 +1,22 @@
 /* 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");
-
-loader.lazyGetter(this, "DOMUtils", () => {
-  return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
-});
+loader.lazyServiceGetter(this, "DOMUtils",
+  "@mozilla.org/inspector/dom-utils;1", "inIDOMUtils");
+loader.lazyRequireGetter(this, "CSS_TYPES",
+  "devtools/shared/css/properties-db", true);
 
 const protocol = require("devtools/shared/protocol");
 const { ActorClassWithSpec, Actor } = protocol;
 const { cssPropertiesSpec } = require("devtools/shared/specs/css-properties");
-const { CSS_TYPES } = require("devtools/shared/css/properties-db");
 const { cssColors } = require("devtools/shared/css/color-db");
 
 exports.CssPropertiesActor = ActorClassWithSpec(cssPropertiesSpec, {
   typeName: "cssProperties",
 
   initialize(conn) {
     Actor.prototype.initialize.call(this, conn);
   },
--- a/devtools/shared/css/color.js
+++ b/devtools/shared/css/color.js
@@ -1,17 +1,19 @@
 /* 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 Services = require("Services");
 
-const {CSS_ANGLEUNIT} = require("devtools/shared/css/properties-db");
+loader.lazyRequireGetter(this, "CSS_ANGLEUNIT",
+  "devtools/shared/css/properties-db", true);
+
 const {getAngleValueInDegrees} = require("devtools/shared/css/parsing-utils");
 
 const {getCSSLexer} = require("devtools/shared/css/lexer");
 const {cssColors} = require("devtools/shared/css/color-db");
 
 const COLOR_UNIT_PREF = "devtools.defaultColorUnit";
 
 const SPECIALVALUES = new Set([
--- a/devtools/shared/css/parsing-utils.js
+++ b/devtools/shared/css/parsing-utils.js
@@ -9,17 +9,18 @@
 // parseDeclarations - parse a CSS rule into declarations
 // RuleRewriter - rewrite CSS rule text
 // parsePseudoClassesAndAttributes - parse selector and extract
 //     pseudo-classes
 // parseSingleValue - parse a single CSS property value
 
 "use strict";
 
-const {CSS_ANGLEUNIT} = require("devtools/shared/css/properties-db");
+loader.lazyRequireGetter(this, "CSS_ANGLEUNIT",
+  "devtools/shared/css/properties-db", true);
 
 const promise = require("promise");
 const {getCSSLexer} = require("devtools/shared/css/lexer");
 const {Task} = require("devtools/shared/task");
 
 const SELECTOR_ATTRIBUTE = exports.SELECTOR_ATTRIBUTE = 1;
 const SELECTOR_ELEMENT = exports.SELECTOR_ELEMENT = 2;
 const SELECTOR_PSEUDO_CLASS = exports.SELECTOR_PSEUDO_CLASS = 3;
--- a/devtools/shared/fronts/css-properties.js
+++ b/devtools/shared/fronts/css-properties.js
@@ -1,17 +1,19 @@
 /* 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";
 
+loader.lazyRequireGetter(this, "CSS_PROPERTIES_DB",
+  "devtools/shared/css/properties-db", true);
+
 const { FrontClassWithSpec, Front } = require("devtools/shared/protocol");
 const { cssPropertiesSpec } = require("devtools/shared/specs/css-properties");
 const { Task } = require("devtools/shared/task");
-const { CSS_PROPERTIES_DB } = require("devtools/shared/css/properties-db");
 const { cssColors } = require("devtools/shared/css/color-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
  */
@@ -275,49 +277,59 @@ function getClientCssProperties() {
 /**
  * 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. This normalizes the data and fills in
  * any missing information like color values.
  *
  * @return {Object} The normalized CSS database.
  */
 function normalizeCssData(db) {
-  if (db !== CSS_PROPERTIES_DB) {
+  // If there is a `from` attributes, it means that it comes from RDP
+  // and it is not the client CSS_PROPERTIES_DB object.
+  // (prevent comparing to CSS_PROPERTIES_DB to avoid loading client database)
+  if (typeof (db.from) == "string") {
     // 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 };
     }
 
     let missingSupports = !db.properties.color.supports;
     let missingValues = !db.properties.color.values;
     let missingSubproperties = !db.properties.background.subproperties;
+    let missingIsInherited = !db.properties.font.isInherited;
 
-    for (let name in db.properties) {
-      // Skip the current property if we can't find it in CSS_PROPERTIES_DB.
-      if (typeof CSS_PROPERTIES_DB.properties[name] !== "object") {
-        continue;
-      }
+    let missingSomething = missingSupports || missingValues || missingSubproperties ||
+      missingIsInherited;
+
+    if (missingSomething) {
+      for (let name in db.properties) {
+        // Skip the current property if we can't find it in CSS_PROPERTIES_DB.
+        if (typeof CSS_PROPERTIES_DB.properties[name] !== "object") {
+          continue;
+        }
 
-      // Add "supports" information to the css properties if it's missing.
-      if (missingSupports) {
-        db.properties[name].supports = CSS_PROPERTIES_DB.properties[name].supports;
-      }
-      // Add "values" information to the css properties if it's missing.
-      if (missingValues) {
-        db.properties[name].values = CSS_PROPERTIES_DB.properties[name].values;
-      }
-      // Add "subproperties" information to the css properties if it's missing.
-      if (missingSubproperties) {
-        db.properties[name].subproperties =
-          CSS_PROPERTIES_DB.properties[name].subproperties;
-      }
-      // Add "isInherited" information to the css properties if it's missing.
-      if (db.properties.font.isInherited) {
-        db.properties[name].isInherited = CSS_PROPERTIES_DB.properties[name].isInherited;
+        // Add "supports" information to the css properties if it's missing.
+        if (missingSupports) {
+          db.properties[name].supports = CSS_PROPERTIES_DB.properties[name].supports;
+        }
+        // Add "values" information to the css properties if it's missing.
+        if (missingValues) {
+          db.properties[name].values = CSS_PROPERTIES_DB.properties[name].values;
+        }
+        // Add "subproperties" information to the css properties if it's missing.
+        if (missingSubproperties) {
+          db.properties[name].subproperties =
+            CSS_PROPERTIES_DB.properties[name].subproperties;
+        }
+        // Add "isInherited" information to the css properties if it's missing.
+        if (missingIsInherited) {
+          db.properties[name].isInherited =
+            CSS_PROPERTIES_DB.properties[name].isInherited;
+        }
       }
     }
   }
 
   reattachCssColorValues(db);
 
   // If there is no supportedFeature in db, create an empty one.
   if (!db.supportedFeature) {