Bug 1152721 - Extract styleinspector common methods to utils.js, r=mratcliffe
☠☠ backed out by 4d3ef8a98608 ☠ ☠
authorJulian Descottes <julian.descottes@gmail.com>
Tue, 04 Aug 2015 23:26:29 -0700
changeset 287985 de6f45e30226195589e8ab173fcb4781ce50a28a
parent 287984 5b3ba9f596e376f3110077c5140dff51275976ee
child 287986 7f106466e9ad617f292a64ead3062a84e3da4690
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmratcliffe
bugs1152721
milestone42.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 1152721 - Extract styleinspector common methods to utils.js, r=mratcliffe
browser/devtools/shared/test/unit/test_advanceValidate.js
browser/devtools/styleinspector/computed-view.js
browser/devtools/styleinspector/moz.build
browser/devtools/styleinspector/rule-view.js
browser/devtools/styleinspector/utils.js
--- a/browser/devtools/shared/test/unit/test_advanceValidate.js
+++ b/browser/devtools/shared/test/unit/test_advanceValidate.js
@@ -6,25 +6,25 @@
 "use strict";
 
 // Tests the advanceValidate function from rule-view.js.
 
 const Cu = Components.utils;
 const Ci = Components.interfaces;
 let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 let require = devtools.require;
-let {_advanceValidate} = require("devtools/styleinspector/rule-view");
+let {advanceValidate} = require("devtools/styleinspector/rule-view");
 
 //                            1         2         3
 //                  0123456789012345678901234567890
 let sampleInput = '\\symbol "string" url(somewhere)';
 
 function testInsertion(where, result, testName) {
   do_print(testName);
-  equal(_advanceValidate(Ci.nsIDOMKeyEvent.DOM_VK_SEMICOLON, sampleInput, where),
+  equal(advanceValidate(Ci.nsIDOMKeyEvent.DOM_VK_SEMICOLON, sampleInput, where),
         result, "testing _advanceValidate at " + where);
 }
 
 function run_test() {
   testInsertion(4, true, "inside a symbol");
   testInsertion(1, false, "after a backslash");
   testInsertion(8, true, "after whitespace");
   testInsertion(11, false, "inside a string");
--- a/browser/devtools/styleinspector/computed-view.js
+++ b/browser/devtools/styleinspector/computed-view.js
@@ -10,18 +10,20 @@
 "use strict";
 
 const {Cc, Ci, Cu} = require("chrome");
 
 const ToolDefinitions = require("main").Tools;
 const {CssLogic} = require("devtools/styleinspector/css-logic");
 const {ELEMENT_STYLE} = require("devtools/server/actors/styles");
 const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
+const {setTimeout, clearTimeout} = Cu.import("resource://gre/modules/Timer.jsm", {});
 const {OutputParser} = require("devtools/output-parser");
 const {PrefObserver, PREF_ORIG_SOURCES} = require("devtools/styleeditor/utils");
+const {createChild} = require("devtools/styleinspector/utils");
 const {gDevTools} = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
 
 loader.lazyRequireGetter(this, "overlays", "devtools/styleinspector/style-inspector-overlays");
 loader.lazyRequireGetter(this, "StyleInspectorMenu", "devtools/styleinspector/style-inspector-menu");
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
@@ -65,26 +67,26 @@ function UpdateProcess(aWin, aGenerator,
 UpdateProcess.prototype = {
   /**
    * Schedule a new batch on the main loop.
    */
   schedule: function() {
     if (this.canceled) {
       return;
     }
-    this._timeout = this.win.setTimeout(this._timeoutHandler.bind(this), 0);
+    this._timeout = setTimeout(this._timeoutHandler.bind(this), 0);
   },
 
   /**
    * Cancel the running process.  onItem will not be called again,
    * and onCancel will be called.
    */
   cancel: function() {
     if (this._timeout) {
-      this.win.clearTimeout(this._timeout);
+      clearTimeout(this._timeout);
       this._timeout = 0;
     }
     this.canceled = true;
     this.onCancel();
   },
 
   _timeoutHandler: function() {
     this._timeout = null;
@@ -503,27 +505,25 @@ CssComputedView.prototype = {
   },
 
   /**
    * Called when the user enters a search term in the filter style search box.
    *
    * @param {Event} aEvent the DOM Event object.
    */
   _onFilterStyles: function(aEvent) {
-    let win = this.styleWindow;
-
     if (this._filterChangedTimeout) {
-      win.clearTimeout(this._filterChangedTimeout);
+      clearTimeout(this._filterChangedTimeout);
     }
 
     let filterTimeout = (this.searchField.value.length > 0)
       ? FILTER_CHANGED_TIMEOUT : 0;
     this.searchClearButton.hidden = this.searchField.value.length === 0;
 
-    this._filterChangedTimeout = win.setTimeout(() => {
+    this._filterChangedTimeout = setTimeout(() => {
       if (this.searchField.value.length > 0) {
         this.searchField.setAttribute("filled", true);
       } else {
         this.searchField.removeAttribute("filled");
       }
 
       this.refreshPanel();
       this._filterChangeTimeout = null;
@@ -1381,37 +1381,10 @@ SelectorView.prototype = {
           let sheet = source || href;
           toolbox.getCurrentPanel().selectStyleSheet(sheet, line, column);
         });
       }
     });
   }
 };
 
-/**
- * Create a child element with a set of attributes.
- *
- * @param {Element} aParent
- *        The parent node.
- * @param {string} aTag
- *        The tag name.
- * @param {object} aAttributes
- *        A set of attributes to set on the node.
- */
-function createChild(aParent, aTag, aAttributes={}) {
-  let elt = aParent.ownerDocument.createElementNS(HTML_NS, aTag);
-  for (let attr in aAttributes) {
-    if (aAttributes.hasOwnProperty(attr)) {
-      if (attr === "textContent") {
-        elt.textContent = aAttributes[attr];
-      } else if (attr === "child") {
-        elt.appendChild(aAttributes[attr]);
-      } else {
-        elt.setAttribute(attr, aAttributes[attr]);
-      }
-    }
-  }
-  aParent.appendChild(elt);
-  return elt;
-}
-
 exports.CssComputedView = CssComputedView;
 exports.PropertyView = PropertyView;
--- a/browser/devtools/styleinspector/moz.build
+++ b/browser/devtools/styleinspector/moz.build
@@ -9,9 +9,10 @@ XPCSHELL_TESTS_MANIFESTS += ['test/unit/
 
 EXTRA_JS_MODULES.devtools.styleinspector += [
     'computed-view.js',
     'css-parsing-utils.js',
     'rule-view.js',
     'style-inspector-menu.js',
     'style-inspector-overlays.js',
     'style-inspector.js',
+    'utils.js',
 ]
--- a/browser/devtools/styleinspector/rule-view.js
+++ b/browser/devtools/styleinspector/rule-view.js
@@ -7,32 +7,41 @@
 /* globals overlays, Services, EventEmitter, StyleInspectorMenu,
    clipboardHelper, _strings, domUtils, AutocompletePopup, loader,
    osString */
 
 "use strict";
 
 const {Cc, Ci, Cu} = require("chrome");
 const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
+const {setTimeout, clearTimeout} =
+      Cu.import("resource://gre/modules/Timer.jsm", {});
 const {CssLogic} = require("devtools/styleinspector/css-logic");
 const {InplaceEditor, editableField, editableItem} =
       require("devtools/shared/inplace-editor");
 const {ELEMENT_STYLE, PSEUDO_ELEMENTS} =
       require("devtools/server/actors/styles");
 const {OutputParser} = require("devtools/output-parser");
 const {PrefObserver, PREF_ORIG_SOURCES} = require("devtools/styleeditor/utils");
 const {
+  createChild,
+  appendText,
+  advanceValidate,
+  blurOnMultipleProperties,
+  promiseWarn,
+  throttle
+} = require("devtools/styleinspector/utils");
+const {
   parseDeclarations,
   parseSingleValue,
   parsePseudoClassesAndAttributes,
   SELECTOR_ATTRIBUTE,
   SELECTOR_ELEMENT,
   SELECTOR_PSEUDO_CLASS
 } = require("devtools/styleinspector/css-parsing-utils");
-
 loader.lazyRequireGetter(this, "overlays", "devtools/styleinspector/style-inspector-overlays");
 loader.lazyRequireGetter(this, "EventEmitter", "devtools/toolkit/event-emitter");
 loader.lazyRequireGetter(this, "StyleInspectorMenu", "devtools/styleinspector/style-inspector-menu");
 loader.lazyImporter(this, "Services", "resource://gre/modules/Services.jsm");
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 const HTML_NS = "http://www.w3.org/1999/xhtml";
@@ -45,21 +54,16 @@ const PROPERTY_NAME_CLASS = "ruleview-pr
 const FILTER_CHANGED_TIMEOUT = 150;
 
 // This is used to parse user input when filtering.
 const FILTER_PROP_RE = /\s*([^:\s]*)\s*:\s*(.*?)\s*;?$/;
 
 const IOService = Cc["@mozilla.org/network/io-service;1"]
                   .getService(Ci.nsIIOService);
 
-function promiseWarn(err) {
-  console.error(err);
-  return promise.reject(err);
-}
-
 /**
  * To figure out how shorthand properties are interpreted by the
  * engine, we will set properties on a dummy element and observe
  * how their .style attribute reflects them as computed values.
  * This function creates the document in which those dummy elements
  * will be created.
  */
 let gDummyPromise;
@@ -3604,87 +3608,16 @@ UserProperties.prototype = {
   }
 };
 
 /**
  * Helper functions
  */
 
 /**
- * Create a child element with a set of attributes.
- *
- * @param {Element} aParent
- *        The parent node.
- * @param {string} aTag
- *        The tag name.
- * @param {object} aAttributes
- *        A set of attributes to set on the node.
- */
-function createChild(aParent, aTag, aAttributes) {
-  let elt = aParent.ownerDocument.createElementNS(HTML_NS, aTag);
-  for (let attr in aAttributes) {
-    if (aAttributes.hasOwnProperty(attr)) {
-      if (attr === "textContent") {
-        elt.textContent = aAttributes[attr];
-      } else if (attr === "child") {
-        elt.appendChild(aAttributes[attr]);
-      } else {
-        elt.setAttribute(attr, aAttributes[attr]);
-      }
-    }
-  }
-  aParent.appendChild(elt);
-  return elt;
-}
-
-function setTimeout() {
-  let window = Services.appShell.hiddenDOMWindow;
-  return window.setTimeout.apply(window, arguments);
-}
-
-function clearTimeout() {
-  let window = Services.appShell.hiddenDOMWindow;
-  return window.clearTimeout.apply(window, arguments);
-}
-
-function throttle(func, wait, scope) {
-  let timer = null;
-  return function() {
-    if (timer) {
-      clearTimeout(timer);
-    }
-    let args = arguments;
-    timer = setTimeout(function() {
-      timer = null;
-      func.apply(scope, args);
-    }, wait);
-  };
-}
-
-/**
- * Event handler that causes a blur on the target if the input has
- * multiple CSS properties as the value.
- */
-function blurOnMultipleProperties(e) {
-  setTimeout(() => {
-    let props = parseDeclarations(e.target.value);
-    if (props.length > 1) {
-      e.target.blur();
-    }
-  }, 0);
-}
-
-/**
- * Append a text node to an element.
- */
-function appendText(aParent, aText) {
-  aParent.appendChild(aParent.ownerDocument.createTextNode(aText));
-}
-
-/**
  * Walk up the DOM from a given node until a parent property holder is found.
  * For elements inside the computed property list, the non-computed parent
  * property holder will be returned
  * @param {DOMNode} node The node to start from
  * @return {DOMNode} The parent property holder node, or null if not found
  */
 function getParentTextPropertyHolder(node) {
   while (true) {
@@ -3737,58 +3670,16 @@ function getPropertyNameAndValue(node) {
         name: node.querySelector(".ruleview-propertyname").textContent,
         value: node.querySelector(".ruleview-propertyvalue").textContent
       };
     }
     node = node.parentNode;
   }
 }
 
-/**
- * Called when a character is typed in a value editor.  This decides
- * whether to advance or not, first by checking to see if ";" was
- * typed, and then by lexing the input and seeing whether the ";"
- * would be a terminator at this point.
- *
- * @param {number} aKeyCode Key code to be checked.
- * @param {String} aValue   Current text editor value.
- * @param {number} aInsertionPoint The index of the insertion point.
- * @return {Boolean} True if the focus should advance; false if
- *        the character should be inserted.
- */
-function advanceValidate(aKeyCode, aValue, aInsertionPoint) {
-  // Only ";" has special handling here.
-  if (aKeyCode !== Ci.nsIDOMKeyEvent.DOM_VK_SEMICOLON) {
-    return false;
-  }
-
-  // Insert the character provisionally and see what happens.  If we
-  // end up with a ";" symbol token, then the semicolon terminates the
-  // value.  Otherwise it's been inserted in some spot where it has a
-  // valid meaning, like a comment or string.
-  aValue = aValue.slice(0, aInsertionPoint) + ";" +
-    aValue.slice(aInsertionPoint);
-  let lexer = domUtils.getCSSLexer(aValue);
-  while (true) {
-    let token = lexer.nextToken();
-    if (token.endOffset > aInsertionPoint) {
-      if (token.tokenType === "symbol" && token.text === ";") {
-        // The ";" is a terminator.
-        return true;
-      }
-      // The ";" is not a terminator in this context.
-      break;
-    }
-  }
-  return false;
-}
-
-// We're exporting _advanceValidate for unit tests.
-exports._advanceValidate = advanceValidate;
-
 XPCOMUtils.defineLazyGetter(this, "clipboardHelper", function() {
   return Cc["@mozilla.org/widget/clipboardhelper;1"]
     .getService(Ci.nsIClipboardHelper);
 });
 
 XPCOMUtils.defineLazyGetter(this, "osString", function() {
   return Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS;
 });
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleinspector/utils.js
@@ -0,0 +1,166 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript 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/. */
+
+/* global domUtils */
+
+"use strict";
+
+const {Cc, Ci, Cu} = require("chrome");
+const {setTimeout, clearTimeout} =
+      Cu.import("resource://gre/modules/Timer.jsm", {});
+const {parseDeclarations} =
+      require("devtools/styleinspector/css-parsing-utils");
+const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
+
+loader.lazyServiceGetter(this, "domUtils",
+  "@mozilla.org/inspector/dom-utils;1", "inIDOMUtils");
+
+const HTML_NS = "http://www.w3.org/1999/xhtml";
+
+/**
+ * Create a child element with a set of attributes.
+ *
+ * @param {Element} parent
+ *        The parent node.
+ * @param {string} tagName
+ *        The tag name.
+ * @param {object} attributes
+ *        A set of attributes to set on the node.
+ */
+function createChild(parent, tagName, attributes={}) {
+  let elt = parent.ownerDocument.createElementNS(HTML_NS, tagName);
+  for (let attr in attributes) {
+    if (attributes.hasOwnProperty(attr)) {
+      if (attr === "textContent") {
+        elt.textContent = attributes[attr];
+      } else if (attr === "child") {
+        elt.appendChild(attributes[attr]);
+      } else {
+        elt.setAttribute(attr, attributes[attr]);
+      }
+    }
+  }
+  parent.appendChild(elt);
+  return elt;
+}
+
+exports.createChild = createChild;
+
+/**
+ * Append a text node to an element.
+ *
+ * @param {Element} parent
+ *        The parent node.
+ * @param {string} text
+ *        The text content for the text node.
+ */
+function appendText(parent, text) {
+  parent.appendChild(parent.ownerDocument.createTextNode(text));
+}
+
+exports.appendText = appendText;
+
+/**
+ * Called when a character is typed in a value editor.  This decides
+ * whether to advance or not, first by checking to see if ";" was
+ * typed, and then by lexing the input and seeing whether the ";"
+ * would be a terminator at this point.
+ *
+ * @param {number} keyCode
+ *        Key code to be checked.
+ * @param {string} aValue
+ *        Current text editor value.
+ * @param {number} insertionPoint
+ *        The index of the insertion point.
+ * @return {Boolean} True if the focus should advance; false if
+ *        the character should be inserted.
+ */
+function advanceValidate(keyCode, value, insertionPoint) {
+  // Only ";" has special handling here.
+  if (keyCode !== Ci.nsIDOMKeyEvent.DOM_VK_SEMICOLON) {
+    return false;
+  }
+
+  // Insert the character provisionally and see what happens.  If we
+  // end up with a ";" symbol token, then the semicolon terminates the
+  // value.  Otherwise it's been inserted in some spot where it has a
+  // valid meaning, like a comment or string.
+  value = value.slice(0, insertionPoint) + ";" + value.slice(insertionPoint);
+  let lexer = domUtils.getCSSLexer(value);
+  while (true) {
+    let token = lexer.nextToken();
+    if (token.endOffset > insertionPoint) {
+      if (token.tokenType === "symbol" && token.text === ";") {
+        // The ";" is a terminator.
+        return true;
+      }
+      // The ";" is not a terminator in this context.
+      break;
+    }
+  }
+  return false;
+}
+
+exports.advanceValidate = advanceValidate;
+
+/**
+ * Create a throttling function wrapper to regulate its frequency.
+ *
+ * @param {Function} func
+ *         The function to throttle
+ * @param {number} wait
+ *         The throttling period
+ * @param {Object} scope
+ *         The scope to use for func
+ * @return {Function} The throttled function
+ */
+function throttle(func, wait, scope) {
+  let timer = null;
+
+  return function() {
+    if (timer) {
+      clearTimeout(timer);
+    }
+
+    let args = arguments;
+    timer = setTimeout(function() {
+      timer = null;
+      func.apply(scope, args);
+    }, wait);
+  };
+}
+
+exports.throttle = throttle;
+
+/**
+ * Event handler that causes a blur on the target if the input has
+ * multiple CSS properties as the value.
+ */
+function blurOnMultipleProperties(e) {
+  setTimeout(() => {
+    let props = parseDeclarations(e.target.value);
+    if (props.length > 1) {
+      e.target.blur();
+    }
+  }, 0);
+}
+
+exports.blurOnMultipleProperties = blurOnMultipleProperties;
+
+/**
+ * Log the provided error to the console and return a rejected Promise for
+ * this error.
+ *
+ * @param {Error} error
+ *         The error to log
+ * @return {Promise} A rejected promise
+ */
+function promiseWarn(error) {
+  console.error(error);
+  return promise.reject(error);
+}
+
+exports.promiseWarn = promiseWarn;