Bug 1289425 - Allow sourceeditor to fallback to client-side css properties; r?tromey
MozReview-Commit-ID: Khak8Av1v67
--- 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
};