Bug 1289425 - Allow sourceeditor to fallback to client-side css properties; r?tromey draft
authorGreg Tatum <tatum.creative@gmail.com>
Fri, 12 Aug 2016 15:01:42 -0500
changeset 407406 542eb4f35df8742a1d71d6ce4d446d60766b64c0
parent 407405 dd33e917ed113dcb191be4f1ac5a1ca19c50d5c9
child 529877 f255162c454c33e92a5d2f8b28fcf3e6aa845362
push id27965
push userbmo:gtatum@mozilla.com
push dateTue, 30 Aug 2016 12:52:36 +0000
reviewerstromey
bugs1289425
milestone51.0a1
Bug 1289425 - Allow sourceeditor to fallback to client-side css properties; r?tromey MozReview-Commit-ID: Khak8Av1v67
devtools/client/shared/inplace-editor.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/test/browser_styleeditor_autocomplete.js
devtools/shared/fronts/css-properties.js
--- a/devtools/client/shared/inplace-editor.js
+++ b/devtools/client/shared/inplace-editor.js
@@ -18,17 +18,16 @@
  *   trigger: "dblclick"
  * });
  *
  * See editableField() for more options.
  */
 
 "use strict";
 
-const {Ci} = require("chrome");
 const Services = require("Services");
 const focusManager = Services.focus;
 const {KeyCodes} = require("devtools/client/shared/keycodes");
 
 const HTML_NS = "http://www.w3.org/1999/xhtml";
 const CONTENT_TYPES = {
   PLAIN_TEXT: 0,
   CSS_VALUE: 1,
--- a/devtools/client/sourceeditor/css-autocompleter.js
+++ b/devtools/client/sourceeditor/css-autocompleter.js
@@ -1,16 +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";
 
 /* eslint-disable complexity */
 const {cssTokenizer, cssTokenizerWithLineColumn} = require("devtools/shared/css-parsing-utils");
+const {getClientCssProperties} = require("devtools/shared/fronts/css-properties");
 
 /**
  * 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
  *  - CSS property values
@@ -81,17 +82,18 @@ const SELECTOR_STATES = {
  *        - 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;
+  // If no css properties database is passed in, default to the client list.
+  this.cssProperties = options.cssProperties || getClientCssProperties();
 
   this.propertyNames = this.cssProperties.getNames().sort();
 
   // Array containing the [line, ch, scopeStack] for the locations where the
   // CSS state is "null"
   this.nullStates = [];
 }
 
--- a/devtools/client/sourceeditor/editor.js
+++ b/devtools/client/sourceeditor/editor.js
@@ -28,16 +28,17 @@ const MAX_VERTICAL_OFFSET = 3;
 // line in text selection.
 const RE_SCRATCHPAD_ERROR = /(?:@Scratchpad\/\d+:|\()(\d+):?(\d+)?(?:\)|\n)/;
 const RE_JUMP_TO_LINE = /^(\d+):?(\d+)?/;
 
 const Services = require("Services");
 const promise = require("promise");
 const events = require("devtools/shared/event-emitter");
 const { PrefObserver } = require("devtools/client/styleeditor/utils");
+const { getClientCssProperties } = require("devtools/shared/fronts/css-properties");
 
 const {LocalizationHelper} = require("devtools/shared/l10n");
 const L10N = new LocalizationHelper("devtools/locale/sourceeditor.properties");
 
 const { OS } = Services.appinfo;
 
 // CM_STYLES, CM_SCRIPTS and CM_IFRAME represent the HTML,
 // JavaScript and CSS that is injected into an iframe in
@@ -229,19 +230,22 @@ 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) {
+    // Ensure that autocompletion has cssProperties if it's passed in via the options.
     this.config.autocompleteOpts.cssProperties = this.config.cssProperties;
+  } else {
+    // Use a static client-side database of CSS values if none is provided.
+    this.config.cssProperties = getClientCssProperties();
   }
 
   events.decorate(this);
 }
 
 Editor.prototype = {
   container: null,
   version: null,
@@ -282,45 +286,39 @@ Editor.prototype = {
       }
 
       let scriptsToInject = CM_SCRIPTS.concat(this.config.externalScripts);
       scriptsToInject.forEach(url => {
         if (url.startsWith("chrome://")) {
           Services.scriptloader.loadSubScript(url, win, "utf8");
         }
       });
-      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.
+      // 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);
+      const {
+        propertyKeywords,
+        colorKeywords,
+        valueKeywords
+      } = getCSSKeywords(this.config.cssProperties);
 
-        let cssSpec = win.CodeMirror.resolveMode("text/css");
-        cssSpec.propertyKeywords = propertyKeywords;
-        cssSpec.colorKeywords = colorKeywords;
-        cssSpec.valueKeywords = valueKeywords;
-        win.CodeMirror.defineMIME("text/css", cssSpec);
+      let cssSpec = win.CodeMirror.resolveMode("text/css");
+      cssSpec.propertyKeywords = propertyKeywords;
+      cssSpec.colorKeywords = colorKeywords;
+      cssSpec.valueKeywords = valueKeywords;
+      win.CodeMirror.defineMIME("text/css", cssSpec);
 
-        let scssSpec = win.CodeMirror.resolveMode("text/x-scss");
-        scssSpec.propertyKeywords = propertyKeywords;
-        scssSpec.colorKeywords = colorKeywords;
-        scssSpec.valueKeywords = valueKeywords;
-        win.CodeMirror.defineMIME("text/x-scss", scssSpec);
+      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.");
-      }
+      win.CodeMirror.commands.save = () => this.emit("saveRequested");
 
       // 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;
 
@@ -525,22 +523,16 @@ 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);
     }
--- a/devtools/client/sourceeditor/test/browser_css_autocompletion.js
+++ b/devtools/client/sourceeditor/test/browser_css_autocompletion.js
@@ -91,17 +91,17 @@ 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,
-                                    cssProperties: getClientCssPropertiesForTests()});
+                                    cssProperties: getClientCssProperties()});
       checkStateAndMoveOn();
     });
   });
 }
 
 function checkStateAndMoveOn() {
   if (index == tests.length) {
     finishUp();
--- 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({cssProperties: getClientCssPropertiesForTests()});
+  let completer = new CSSCompleter({cssProperties: getClientCssProperties()});
   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
@@ -60,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({cssProperties: getClientCssPropertiesForTests()});
+  let completer = new CSSCompleter({cssProperties: getClientCssProperties()});
   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, cssProperties: getClientCssPropertiesForTests()}
+    autocompleteOpts: {walker: walker, cssProperties: getClientCssProperties()}
   });
   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
@@ -4,17 +4,17 @@
 
 "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 flags = require("devtools/shared/flags");
-const {getClientCssPropertiesForTests} = require("devtools/shared/fronts/css-properties");
+const {getClientCssProperties} = 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
@@ -58,17 +58,17 @@ function setup(cb, additionalOpts = {}) 
     "<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"],
-    cssProperties: getClientCssPropertiesForTests()
+    cssProperties: getClientCssProperties()
   };
 
   for (let o in additionalOpts) {
     opts[o] = additionalOpts[o];
   }
 
   win.addEventListener("load", function onLoad() {
     win.removeEventListener("load", onLoad, false);
--- a/devtools/client/styleeditor/test/browser_styleeditor_autocomplete.js
+++ b/devtools/client/styleeditor/test/browser_styleeditor_autocomplete.js
@@ -3,17 +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 {getClientCssProperties} = 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),
@@ -191,17 +191,17 @@ 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 cssProperties = getClientCssPropertiesForTests();
+  let cssProperties = getClientCssProperties();
   let props = {};
   let propNames = cssProperties.getNames();
   propNames.forEach(prop => {
     props[prop] = cssProperties.getValues(prop).sort();
   });
   return {
     CSSValues: props,
     CSSProperties: propNames.sort()
--- a/devtools/shared/fronts/css-properties.js
+++ b/devtools/shared/fronts/css-properties.js
@@ -173,20 +173,22 @@ function getCssProperties(toolbox) {
     throw new Error("The CSS database has not been initialized, please make " +
                     "sure initCssDatabase was called once before for this " +
                     "toolbox.");
   }
   return cachedCssProperties.get(toolbox.target.client).cssProperties;
 }
 
 /**
- * Get a client-side CssProperties. This is useful for dependencies in tests.
+ * Get a client-side CssProperties. This is useful for dependencies in tests, or parts
+ * of the codebase that don't particularly need to match every known CSS property on
+ * the target.
  * @return {CssProperties}
  */
-function getClientCssPropertiesForTests() {
+function getClientCssProperties() {
   return new CssProperties(normalizeCssData(CSS_PROPERTIES_DB));
 }
 
 /**
  * Get the current browser version.
  * @returns {string} The browser version.
  */
 function getClientBrowserVersion(toolbox) {
@@ -257,11 +259,11 @@ function reattachCssColorValues(db) {
     }
   }
 }
 
 module.exports = {
   CssPropertiesFront,
   CssProperties,
   getCssProperties,
-  getClientCssPropertiesForTests,
+  getClientCssProperties,
   initCssProperties
 };