Bug 1289425 - Remove domUtils from source editor and autocomplete; r?tromey draft
authorGreg Tatum <tatum.creative@gmail.com>
Fri, 05 Aug 2016 11:44:23 -0500
changeset 407405 dd33e917ed113dcb191be4f1ac5a1ca19c50d5c9
parent 407404 41e95476f434f9cccef4e0d107222b5129ceeccb
child 407406 542eb4f35df8742a1d71d6ce4d446d60766b64c0
push id27965
push userbmo:gtatum@mozilla.com
push dateTue, 30 Aug 2016 12:52:36 +0000
reviewerstromey
bugs1289425
milestone51.0a1
Bug 1289425 - Remove domUtils from source editor and autocomplete; r?tromey MozReview-Commit-ID: HIhUkuKOsjg
devtools/client/sourceeditor/autocomplete.js
devtools/client/sourceeditor/css-autocompleter.js
devtools/client/sourceeditor/editor.js
devtools/client/sourceeditor/test/browser_css_autocompletion.js
devtools/client/sourceeditor/test/browser_css_getInfo.js
devtools/client/sourceeditor/test/browser_css_statemachine.js
devtools/client/sourceeditor/test/browser_editor_autocomplete_events.js
devtools/client/sourceeditor/test/head.js
devtools/client/styleeditor/StyleEditorUI.jsm
devtools/client/styleeditor/StyleSheetEditor.jsm
devtools/client/styleeditor/styleeditor-panel.js
devtools/client/styleeditor/test/browser_styleeditor_autocomplete.js
--- a/devtools/client/sourceeditor/autocomplete.js
+++ b/devtools/client/sourceeditor/autocomplete.js
@@ -100,17 +100,18 @@ function initializeAutoCompletion(ctx, o
 
     autocompleteMap.set(ed, {
       destroy: destroyTern
     });
 
     // TODO: Integrate tern autocompletion with this autocomplete API.
     return;
   } else if (ed.config.mode == Editor.modes.css) {
-    completer = new CSSCompleter({walker: options.walker});
+    completer = new CSSCompleter({walker: options.walker,
+                                  cssProperties: options.cssProperties});
   }
 
   function insertSelectedPopupItem() {
     let autocompleteState = autocompleteMap.get(ed);
     if (!popup || !popup.isOpen || !autocompleteState) {
       return false;
     }
 
--- a/devtools/client/sourceeditor/css-autocompleter.js
+++ b/devtools/client/sourceeditor/css-autocompleter.js
@@ -1,19 +1,15 @@
 /* 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";
 
 /* eslint-disable complexity */
-
-/* eslint-disable mozilla/reject-some-requires */
-const { Cc, Ci } = require("chrome");
-/* eslint-enable mozilla/reject-some-requires */
 const {cssTokenizer, cssTokenizerWithLineColumn} = require("devtools/shared/css-parsing-utils");
 
 /**
  * Here is what this file (+ css-parsing-utils.js) do.
  *
  * The main objective here is to provide as much suggestions to the user editing
  * a stylesheet in Style Editor. The possible things that can be suggested are:
  *  - CSS property names
@@ -73,29 +69,31 @@ const SELECTOR_STATES = {
   class: "class",          // #foo.b|
   tag: "tag",              // fo|
   pseudo: "pseudo",        // foo:|
   attribute: "attribute",  // foo[b|
   value: "value",          // foo[bar=b|
 };
 /* eslint-enable no-inline-comments */
 
-const { properties, propertyNames } = getCSSKeywords();
-
 /**
  * Constructor for the autocompletion object.
  *
  * @param options {Object} An options object containing the following options:
  *        - walker {Object} The object used for query selecting from the current
  *                 target's DOM.
  *        - maxEntries {Number} Maximum selectors suggestions to display.
+ *        - cssProperties {Object} The database of CSS properties.
  */
 function CSSCompleter(options = {}) {
   this.walker = options.walker;
   this.maxEntries = options.maxEntries || 15;
+  this.cssProperties = options.cssProperties;
+
+  this.propertyNames = this.cssProperties.getNames().sort();
 
   // Array containing the [line, ch, scopeStack] for the locations where the
   // CSS state is "null"
   this.nullStates = [];
 }
 
 CSSCompleter.prototype = {
 
@@ -839,45 +837,45 @@ CSSCompleter.prototype = {
    * @param startProp {String} Initial part of the property being completed.
    */
   completeProperties: function (startProp) {
     let finalList = [];
     if (!startProp) {
       return Promise.resolve(finalList);
     }
 
-    let length = propertyNames.length;
+    let length = this.propertyNames.length;
     let i = 0, count = 0;
     for (; i < length && count < this.maxEntries; i++) {
-      if (propertyNames[i].startsWith(startProp)) {
+      if (this.propertyNames[i].startsWith(startProp)) {
         count++;
-        let propName = propertyNames[i];
+        let propName = this.propertyNames[i];
         finalList.push({
           preLabel: startProp,
           label: propName,
           text: propName + ": "
         });
-      } else if (propertyNames[i] > startProp) {
+      } else if (this.propertyNames[i] > startProp) {
         // We have crossed all possible matches alphabetically.
         break;
       }
     }
     return Promise.resolve(finalList);
   },
 
   /**
    * Returns CSS value suggestions based on the corresponding property.
    *
    * @param propName {String} The property to which the value being completed
    *        belongs.
    * @param startValue {String} Initial part of the value being completed.
    */
   completeValues: function (propName, startValue) {
     let finalList = [];
-    let list = ["!important;", ...(properties[propName] || [])];
+    let list = ["!important;", ...this.cssProperties.getValues(propName)];
     // If there is no character being completed, we are showing an initial list
     // of possible values. Skipping '!important' in this case.
     if (!startValue) {
       list.splice(0, 1);
     }
 
     let length = list.length;
     let i = 0, count = 0;
@@ -1206,34 +1204,9 @@ CSSCompleter.prototype = {
           end: end
         }
       };
     }
     return null;
   }
 };
 
-/**
- * Returns a list of all property names and a map of property name vs possible
- * CSS values provided by the Gecko engine.
- *
- * @return {Object} An object with following properties:
- *         - propertyNames {Array} Array of string containing all the possible
- *                         CSS property names.
- *         - properties {Object|Map} A map where key is the property name and
- *                      value is an array of string containing all the possible
- *                      CSS values the property can have.
- */
-function getCSSKeywords() {
-  let domUtils = Cc["@mozilla.org/inspector/dom-utils;1"]
-                   .getService(Ci.inIDOMUtils);
-  let props = {};
-  let propNames = domUtils.getCSSPropertyNames(domUtils.INCLUDE_ALIASES);
-  propNames.forEach(prop => {
-    props[prop] = domUtils.getCSSValuesForProperty(prop).sort();
-  });
-  return {
-    properties: props,
-    propertyNames: propNames.sort()
-  };
-}
-
 module.exports = CSSCompleter;
--- a/devtools/client/sourceeditor/editor.js
+++ b/devtools/client/sourceeditor/editor.js
@@ -1,20 +1,16 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2; fill-column: 80 -*- */
 /* vim:set ts=2 sw=2 sts=2 et 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";
 
-/* eslint-disable mozilla/reject-some-requires */
-const {Cc, Ci} = require("chrome");
-/* eslint-enable mozilla/reject-some-requires */
-
 const {
   EXPAND_TAB,
   TAB_SIZE,
   DETECT_INDENT,
   getIndentationFromIteration
 } = require("devtools/shared/indentation");
 
 const ENABLE_CODE_FOLDING = "devtools.editor.enableCodeFolding";
@@ -109,18 +105,16 @@ const CM_MAPPING = [
   "redo",
   "clearHistory",
   "openDialog",
   "refresh",
   "getScrollInfo",
   "getViewport"
 ];
 
-const { cssProperties, cssValues, cssColors } = getCSSKeywords();
-
 const editors = new WeakMap();
 
 Editor.modes = {
   text: { name: "text" },
   html: { name: "htmlmixed" },
   css: { name: "css" },
   wasm: { name: "wasm" },
   js: { name: "javascript" },
@@ -235,16 +229,21 @@ function Editor(config) {
     cm.replaceSelection(" ".repeat(num), "end", "+input");
   };
 
   // Allow add-ons to inject scripts for their editor instances
   if (!this.config.externalScripts) {
     this.config.externalScripts = [];
   }
 
+  // Ensure that autocompletion has cssProperties if it's passed in via the options.
+  if (this.config.cssProperties) {
+    this.config.autocompleteOpts.cssProperties = this.config.cssProperties;
+  }
+
   events.decorate(this);
 }
 
 Editor.prototype = {
   container: null,
   version: null,
   config: null,
   Doc: null,
@@ -283,31 +282,45 @@ Editor.prototype = {
       }
 
       let scriptsToInject = CM_SCRIPTS.concat(this.config.externalScripts);
       scriptsToInject.forEach(url => {
         if (url.startsWith("chrome://")) {
           Services.scriptloader.loadSubScript(url, win, "utf8");
         }
       });
-      // Replace the propertyKeywords, colorKeywords and valueKeywords
-      // properties of the CSS MIME type with the values provided by Gecko.
-      let cssSpec = win.CodeMirror.resolveMode("text/css");
-      cssSpec.propertyKeywords = cssProperties;
-      cssSpec.colorKeywords = cssColors;
-      cssSpec.valueKeywords = cssValues;
-      win.CodeMirror.defineMIME("text/css", cssSpec);
+      if (this.config.cssProperties) {
+        // Replace the propertyKeywords, colorKeywords and valueKeywords
+        // properties of the CSS MIME type with the values provided by the CSS properties
+        // database.
+
+        const {
+          propertyKeywords,
+          colorKeywords,
+          valueKeywords
+        } = getCSSKeywords(this.config.cssProperties);
 
-      let scssSpec = win.CodeMirror.resolveMode("text/x-scss");
-      scssSpec.propertyKeywords = cssProperties;
-      scssSpec.colorKeywords = cssColors;
-      scssSpec.valueKeywords = cssValues;
-      win.CodeMirror.defineMIME("text/x-scss", scssSpec);
+        let cssSpec = win.CodeMirror.resolveMode("text/css");
+        cssSpec.propertyKeywords = propertyKeywords;
+        cssSpec.colorKeywords = colorKeywords;
+        cssSpec.valueKeywords = valueKeywords;
+        win.CodeMirror.defineMIME("text/css", cssSpec);
 
-      win.CodeMirror.commands.save = () => this.emit("saveRequested");
+        let scssSpec = win.CodeMirror.resolveMode("text/x-scss");
+        scssSpec.propertyKeywords = propertyKeywords;
+        scssSpec.colorKeywords = colorKeywords;
+        scssSpec.valueKeywords = valueKeywords;
+        win.CodeMirror.defineMIME("text/x-scss", scssSpec);
+
+        win.CodeMirror.commands.save = () => this.emit("saveRequested");
+      } else if (this.config.mode === Editor.modes.css) {
+        console.warn("The CSS properties are defaulting to the those provided by " +
+                     "CodeMirror as no CSS database was provided for CSS values " +
+                     "specific to the target platform.");
+      }
 
       // Create a CodeMirror instance add support for context menus,
       // overwrite the default controller (otherwise items in the top and
       // context menus won't work).
 
       cm = win.CodeMirror(win.document.body, this.config);
       this.Doc = win.CodeMirror.Doc;
 
@@ -512,16 +525,22 @@ Editor.prototype = {
     cm.swapDoc(doc);
   },
 
   /**
    * Changes the value of a currently used highlighting mode.
    * See Editor.modes for the list of all supported modes.
    */
   setMode: function (value) {
+    if (value === Editor.modes.css && !this.config.cssProperties) {
+      console.warn("Switching to CSS mode in the editor, but no CSS properties " +
+                   "database was provided to CodeMirror. CodeMirror will default" +
+                   "to its built-in values, and not use the values specific to the " +
+                   "target platform.");
+    }
     this.setOption("mode", value);
 
     // If autocomplete was set up and the mode is changing, then
     // turn it off and back on again so the proper mode can be used.
     if (this.config.autocomplete) {
       this.setOption("autocomplete", false);
       this.setOption("autocomplete", true);
     }
@@ -1274,50 +1293,52 @@ Editor.accel = function (key, modifiers 
  * platforms unless noaccel is specified in the options. Useful when overwriting
  * or disabling default shortcuts.
  */
 Editor.keyFor = function (cmd, opts = { noaccel: false }) {
   let key = L10N.getStr(cmd + ".commandkey");
   return opts.noaccel ? key : Editor.accel(key);
 };
 
-// Since Gecko already provide complete and up to date list of CSS property
-// names, values and color names, we compute them so that they can replace
-// the ones used in CodeMirror while initiating an editor object. This is done
-// here instead of the file codemirror/css.js so as to leave that file untouched
-// and easily upgradable.
-function getCSSKeywords() {
+/**
+ * We compute the CSS property names, values, and color names to be used with
+ * CodeMirror to more closely reflect what is supported by the target platform.
+ * The database is used to replace the values used in CodeMirror while initiating
+ * an editor object. This is done here instead of the file codemirror/css.js so
+ * as to leave that file untouched and easily upgradable.
+ */
+function getCSSKeywords(cssProperties) {
   function keySet(array) {
     let keys = {};
     for (let i = 0; i < array.length; ++i) {
       keys[array[i]] = true;
     }
     return keys;
   }
 
-  let domUtils = Cc["@mozilla.org/inspector/dom-utils;1"]
-                   .getService(Ci.inIDOMUtils);
-  let properties = domUtils.getCSSPropertyNames(domUtils.INCLUDE_ALIASES);
-  let colors = {};
-  let values = {};
-  properties.forEach(property => {
+  let propertyKeywords = cssProperties.getNames();
+  let colorKeywords = {};
+  let valueKeywords = {};
+
+  propertyKeywords.forEach(property => {
     if (property.includes("color")) {
-      domUtils.getCSSValuesForProperty(property).forEach(value => {
-        colors[value] = true;
+      cssProperties.getValues(property).forEach(value => {
+        colorKeywords[value] = true;
       });
     } else {
-      domUtils.getCSSValuesForProperty(property).forEach(value => {
-        values[value] = true;
+      cssProperties.getValues(property).forEach(value => {
+        valueKeywords[value] = true;
       });
     }
   });
+
   return {
-    cssProperties: keySet(properties),
-    cssValues: values,
-    cssColors: colors
+    propertyKeywords: keySet(propertyKeywords),
+    colorKeywords: colorKeywords,
+    valueKeywords: valueKeywords
   };
 }
 
 /**
  * Returns a controller object that can be used for
  * editor-specific commands such as find, jump to line,
  * copy/paste, etc.
  */
--- a/devtools/client/sourceeditor/test/browser_css_autocompletion.js
+++ b/devtools/client/sourceeditor/test/browser_css_autocompletion.js
@@ -90,35 +90,38 @@ function test() {
 
 function runTests() {
   progress = doc.getElementById("progress");
   progressDiv = doc.querySelector("#progress > div");
   let target = TargetFactory.forTab(gBrowser.selectedTab);
   target.makeRemote().then(() => {
     inspector = InspectorFront(target.client, target.form);
     inspector.getWalker().then(walker => {
-      completer = new CSSCompleter({walker: walker});
+      completer = new CSSCompleter({walker: walker,
+                                    cssProperties: getClientCssPropertiesForTests()});
       checkStateAndMoveOn();
     });
   });
 }
 
 function checkStateAndMoveOn() {
   if (index == tests.length) {
     finishUp();
     return;
   }
 
-  let test = tests[index];
+  let [lineCh, expectedSuggestions] = tests[index];
+  let [line, ch] = lineCh;
+
   progress.dataset.progress = ++index;
   progressDiv.style.width = 100 * index / tests.length + "%";
-  completer.complete(limit(source, test[0]),
-                     {line: test[0][0], ch: test[0][1]}).then(suggestions => {
-                       checkState(test[1], suggestions);
-                     }).then(checkStateAndMoveOn);
+
+  completer.complete(limit(source, lineCh), {line, ch})
+           .then(actualSuggestions => checkState(expectedSuggestions, actualSuggestions))
+           .then(checkStateAndMoveOn);
 }
 
 function checkState(expected, actual) {
   if (expected.length != actual.length) {
     ok(false, "Number of suggestions did not match up for state " + index +
               ". Expected: " + expected.length + ", Actual: " + actual.length);
     progress.classList.add("failed");
     return;
--- a/devtools/client/sourceeditor/test/browser_css_getInfo.js
+++ b/devtools/client/sourceeditor/test/browser_css_getInfo.js
@@ -127,17 +127,17 @@ function test() {
   BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser).then(() => {
     doc = content.document;
     runTests();
   });
   gBrowser.loadURI(TEST_URI);
 }
 
 function runTests() {
-  let completer = new CSSCompleter();
+  let completer = new CSSCompleter({cssProperties: getClientCssPropertiesForTests()});
   let matches = (arr, toCheck) => !arr.some((x, i) => x != toCheck[i]);
   let checkState = (expected, actual) => {
     if (expected[0] == "null" && actual == null) {
       return true;
     } else if (expected[0] == actual.state && expected[0] == "selector" &&
                expected[1] == actual.selector) {
       return true;
     } else if (expected[0] == actual.state && expected[0] == "property" &&
--- a/devtools/client/sourceeditor/test/browser_css_statemachine.js
+++ b/devtools/client/sourceeditor/test/browser_css_statemachine.js
@@ -1,16 +1,15 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 const CSSCompleter = require("devtools/client/sourceeditor/css-autocompleter");
-const { Cc, Ci } = require("chrome");
 
 const CSS_URI = "http://mochi.test:8888/browser/devtools/client/sourceeditor" +
                 "/test/css_statemachine_testcases.css";
 const TESTS_URI = "http://mochi.test:8888/browser/devtools/client" +
                   "/sourceeditor/test/css_statemachine_tests.json";
 
 const source = read(CSS_URI);
 const tests = eval(read(TESTS_URI));
@@ -61,17 +60,17 @@ function test() {
   gBrowser.selectedTab = gBrowser.addTab(TEST_URI);
   BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser).then(() => {
     doc = content.document;
     runTests();
   });
 }
 
 function runTests() {
-  let completer = new CSSCompleter();
+  let completer = new CSSCompleter({cssProperties: getClientCssPropertiesForTests()});
   let checkState = state => {
     if (state[0] == "null" && (!completer.state || completer.state == "null")) {
       return true;
     } else if (state[0] == completer.state && state[0] == "selector" &&
                state[1] == completer.selectorState &&
                state[2] == completer.completing &&
                state[3] == completer.selector) {
       return true;
--- a/devtools/client/sourceeditor/test/browser_editor_autocomplete_events.js
+++ b/devtools/client/sourceeditor/test/browser_editor_autocomplete_events.js
@@ -20,17 +20,17 @@ add_task(function* () {
 function* runTests() {
   let target = TargetFactory.forTab(gBrowser.selectedTab);
   yield target.makeRemote();
   let inspector = InspectorFront(target.client, target.form);
   let walker = yield inspector.getWalker();
   let {ed, win, edWin} = yield setup(null, {
     autocomplete: true,
     mode: Editor.modes.css,
-    autocompleteOpts: {walker: walker}
+    autocompleteOpts: {walker: walker, cssProperties: getClientCssPropertiesForTests()}
   });
   yield testMouse(ed, edWin);
   yield testKeyboard(ed, edWin);
   yield testKeyboardCycle(ed, edWin);
   yield testKeyboardCycleForPrefixedString(ed, edWin);
   yield testKeyboardCSSComma(ed, edWin);
   teardown(ed, win);
 }
--- a/devtools/client/sourceeditor/test/head.js
+++ b/devtools/client/sourceeditor/test/head.js
@@ -3,18 +3,18 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
 const { NetUtil } = require("resource://gre/modules/NetUtil.jsm");
 const Editor = require("devtools/client/sourceeditor/editor");
 const promise = require("promise");
-const DevToolsUtils = require("devtools/shared/DevToolsUtils");
 const flags = require("devtools/shared/flags");
+const {getClientCssPropertiesForTests} = require("devtools/shared/fronts/css-properties");
 
 flags.testing = true;
 SimpleTest.registerCleanupFunction(() => {
   flags.testing = false;
 });
 
 /**
  * Open a new tab at a URL and call a callback on load
@@ -57,18 +57,20 @@ function setup(cb, additionalOpts = {}) 
     "/there.is.only.xul' title='Editor' width='600' height='500'>" +
     "<box flex='1'/></window>";
 
   let win = Services.ww.openWindow(null, url, "_blank", opt, null);
   let opts = {
     value: "Hello.",
     lineNumbers: true,
     foldGutter: true,
-    gutters: ["CodeMirror-linenumbers", "breakpoints", "CodeMirror-foldgutter"]
+    gutters: ["CodeMirror-linenumbers", "breakpoints", "CodeMirror-foldgutter"],
+    cssProperties: getClientCssPropertiesForTests()
   };
+
   for (let o in additionalOpts) {
     opts[o] = additionalOpts[o];
   }
 
   win.addEventListener("load", function onLoad() {
     win.removeEventListener("load", onLoad, false);
 
     waitForFocus(function () {
--- a/devtools/client/styleeditor/StyleEditorUI.jsm
+++ b/devtools/client/styleeditor/StyleEditorUI.jsm
@@ -53,23 +53,25 @@ const PREF_NAV_WIDTH = "devtools.styleed
  *   'error': An error occured
  *
  * @param {StyleEditorFront} debuggee
  *        Client-side front for interacting with the page's stylesheets
  * @param {Target} target
  *        Interface for the page we're debugging
  * @param {Document} panelDoc
  *        Document of the toolbox panel to populate UI in.
+ * @param {CssProperties} A css properties database.
  */
-function StyleEditorUI(debuggee, target, panelDoc) {
+function StyleEditorUI(debuggee, target, panelDoc, cssProperties) {
   EventEmitter.decorate(this);
 
   this._debuggee = debuggee;
   this._target = target;
   this._panelDoc = panelDoc;
+  this._cssProperties = cssProperties;
   this._window = this._panelDoc.defaultView;
   this._root = this._panelDoc.getElementById("style-editor-chrome");
 
   this.editors = [];
   this.selectedEditor = null;
   this.savedLocations = {};
 
   this._onOptionsPopupShowing = this._onOptionsPopupShowing.bind(this);
@@ -599,17 +601,17 @@ StyleEditorUI.prototype = {
         let showEditor = data.editor;
         this.selectedEditor = showEditor;
 
         Task.spawn(function* () {
           if (!showEditor.sourceEditor) {
             // only initialize source editor when we switch to this view
             let inputElement =
                 details.querySelector(".stylesheet-editor-input");
-            yield showEditor.load(inputElement);
+            yield showEditor.load(inputElement, this._cssProperties);
           }
 
           showEditor.onShow();
 
           this.emit("editor-selected", showEditor);
 
           // Is there any CSS coverage markup to include?
           let usage = yield csscoverage.getUsage(this._target);
--- a/devtools/client/styleeditor/StyleSheetEditor.jsm
+++ b/devtools/client/styleeditor/StyleSheetEditor.jsm
@@ -412,38 +412,41 @@ StyleSheetEditor.prototype = {
   _onError: function (event, data) {
     this.emit("error", data);
   },
 
   /**
    * Create source editor and load state into it.
    * @param  {DOMElement} inputElement
    *         Element to load source editor in
+   * @param  {CssProperties} cssProperties
+   *         A css properties database.
    *
    * @return {Promise}
    *         Promise that will resolve when the style editor is loaded.
    */
-  load: function (inputElement) {
+  load: function (inputElement, cssProperties) {
     if (this._isDestroyed) {
       return promise.reject("Won't load source editor as the style sheet has " +
                             "already been removed from Style Editor.");
     }
 
     this._inputElement = inputElement;
 
     let config = {
       value: this._state.text,
       lineNumbers: true,
       mode: Editor.modes.css,
       readOnly: false,
       autoCloseBrackets: "{}()",
       extraKeys: this._getKeyBindings(),
       contextMenu: "sourceEditorContextMenu",
       autocomplete: Services.prefs.getBoolPref(AUTOCOMPLETION_PREF),
-      autocompleteOpts: { walker: this.walker }
+      autocompleteOpts: { walker: this.walker, cssProperties },
+      cssProperties
     };
     let sourceEditor = this._sourceEditor = new Editor(config);
 
     sourceEditor.on("dirty-change", this._onPropertyChange);
 
     return sourceEditor.appendTo(inputElement).then(() => {
       sourceEditor.on("saveRequested", this.saveToFile);
 
--- a/devtools/client/styleeditor/styleeditor-panel.js
+++ b/devtools/client/styleeditor/styleeditor-panel.js
@@ -7,16 +7,17 @@
 var Services = require("Services");
 var promise = require("promise");
 var {Task} = require("devtools/shared/task");
 var {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm");
 var EventEmitter = require("devtools/shared/event-emitter");
 
 var {StyleEditorUI} = require("resource://devtools/client/styleeditor/StyleEditorUI.jsm");
 var {getString} = require("resource://devtools/client/styleeditor/StyleEditorUtil.jsm");
+var {initCssProperties} = require("devtools/shared/fronts/css-properties");
 
 loader.lazyGetter(this, "StyleSheetsFront",
   () => require("devtools/shared/fronts/stylesheets").StyleSheetsFront);
 
 loader.lazyGetter(this, "StyleEditorFront",
   () => require("devtools/shared/fronts/styleeditor").StyleEditorFront);
 
 var StyleEditorPanel = function StyleEditorPanel(panelWin, toolbox) {
@@ -55,18 +56,22 @@ StyleEditorPanel.prototype = {
 
     if (this.target.form.styleSheetsActor) {
       this._debuggee = StyleSheetsFront(this.target.client, this.target.form);
     } else {
       /* We're talking to a pre-Firefox 29 server-side */
       this._debuggee = StyleEditorFront(this.target.client, this.target.form);
     }
 
+    // Initialize the CSS properties database.
+    const {cssProperties} = yield initCssProperties(this._toolbox);
+
     // Initialize the UI
-    this.UI = new StyleEditorUI(this._debuggee, this.target, this._panelDoc);
+    this.UI = new StyleEditorUI(this._debuggee, this.target, this._panelDoc,
+                                cssProperties);
     this.UI.on("error", this._showError);
     yield this.UI.initialize();
 
     this.isReady = true;
 
     return this;
   }),
 
--- a/devtools/client/styleeditor/test/browser_styleeditor_autocomplete.js
+++ b/devtools/client/styleeditor/test/browser_styleeditor_autocomplete.js
@@ -3,16 +3,17 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
 // Test that autocompletion works as expected.
 
 const TESTCASE_URI = TEST_BASE_HTTP + "autocomplete.html";
 const MAX_SUGGESTIONS = 15;
 
+const {getClientCssPropertiesForTests} = require("devtools/shared/fronts/css-properties");
 const {CSSProperties, CSSValues} = getCSSKeywords();
 
 // Test cases to test that autocompletion works correctly when enabled.
 // Format:
 // [
 //   key,
 //   {
 //     total: Number of suggestions in the popup (-1 if popup is closed),
@@ -190,22 +191,21 @@ function checkState(index, sourceEditor,
  * @return {Object} An object with following properties:
  *         - CSSProperties {Array} Array of string containing all the possible
  *                         CSS property names.
  *         - CSSValues {Object|Map} A map where key is the property name and
  *                     value is an array of string containing all the possible
  *                     CSS values the property can have.
  */
 function getCSSKeywords() {
-  let domUtils = Cc["@mozilla.org/inspector/dom-utils;1"]
-                   .getService(Ci.inIDOMUtils);
+  let cssProperties = getClientCssPropertiesForTests();
   let props = {};
-  let propNames = domUtils.getCSSPropertyNames(domUtils.INCLUDE_ALIASES);
+  let propNames = cssProperties.getNames();
   propNames.forEach(prop => {
-    props[prop] = domUtils.getCSSValuesForProperty(prop).sort();
+    props[prop] = cssProperties.getValues(prop).sort();
   });
   return {
     CSSValues: props,
     CSSProperties: propNames.sort()
   };
 }
 
 /**