Bug 1265798 - Implement CSS database to query css properties r=pbro

MozReview-Commit-ID: CAUh2GyeA2o

--HG--
extra : rebase_source : fc8e97f7bfc92c709014e58bd7f1670bc859460e
This commit is contained in:
Greg Tatum 2016-05-26 08:48:00 -07:00
Родитель a56fcbf802
Коммит 262a831a1f
26 изменённых файлов: 872 добавлений и 97 удалений

Просмотреть файл

@ -17,6 +17,7 @@ var clipboard = require("sdk/clipboard");
const {executeSoon} = require("devtools/shared/DevToolsUtils");
var {KeyShortcuts} = require("devtools/client/shared/key-shortcuts");
var {Task} = require("devtools/shared/task");
const {initCssProperties} = require("devtools/shared/fronts/css-properties");
loader.lazyRequireGetter(this, "CSS", "CSS");
@ -108,15 +109,14 @@ InspectorPanel.prototype = {
/**
* open is effectively an asynchronous constructor
*/
open: function () {
return this.target.makeRemote().then(() => {
return this._getPageStyle();
}).then(() => {
return this._getDefaultNodeForSelection();
}).then(defaultSelection => {
return this._deferredOpen(defaultSelection);
}).then(null, console.error);
},
open: Task.async(function* () {
this._cssPropertiesLoaded = initCssProperties(this.toolbox);
yield this._cssPropertiesLoaded;
yield this.target.makeRemote();
yield this._getPageStyle();
let defaultSelection = yield this._getDefaultNodeForSelection();
return yield this._deferredOpen(defaultSelection);
}),
get toolbox() {
return this._toolbox;
@ -649,6 +649,12 @@ InspectorPanel.prototype = {
this.layoutview.destroy();
}
let cssPropertiesDestroyer = this._cssPropertiesLoaded.then(({front}) => {
if (front) {
front.destroy();
}
});
this.sidebar.off("select", this._setDefaultSidebar);
let sidebarDestroyer = this.sidebar.destroy();
this.sidebar = null;
@ -678,7 +684,8 @@ InspectorPanel.prototype = {
this._panelDestroyer = promise.all([
sidebarDestroyer,
markupDestroyer
markupDestroyer,
cssPropertiesDestroyer
]);
return this._panelDestroyer;

Просмотреть файл

@ -12,6 +12,7 @@ const {InplaceEditor, editableItem} =
require("devtools/client/shared/inplace-editor");
const {ReflowFront} = require("devtools/server/actors/layout");
const {LocalizationHelper} = require("devtools/client/shared/l10n");
const {getCssProperties} = require("devtools/shared/fronts/css-properties");
const STRINGS_URI = "chrome://devtools/locale/shared.properties";
const SHARED_L10N = new LocalizationHelper(STRINGS_URI);
@ -21,16 +22,19 @@ const LONG_TEXT_ROTATE_LIMIT = 3;
/**
* An instance of EditingSession tracks changes that have been made during the
* modification of box model values. All of these changes can be reverted by
* calling revert.
* calling revert. The main parameter is the LayoutView that created it.
*
* @param doc A DOM document that can be used to test style rules.
* @param rules An array of the style rules defined for the node being edited.
* These should be in order of priority, least important first.
* @param inspector The inspector panel.
* @param doc A DOM document that can be used to test style rules.
* @param rules An array of the style rules defined for the node being
* edited. These should be in order of priority, least
* important first.
*/
function EditingSession(doc, rules) {
function EditingSession({inspector, doc, elementRules}) {
this._doc = doc;
this._rules = rules;
this._rules = elementRules;
this._modifications = new Map();
this._cssProperties = getCssProperties(inspector.toolbox);
}
EditingSession.prototype = {
@ -110,7 +114,8 @@ EditingSession.prototype = {
// StyleRuleActor to make changes to CSS properties.
// Note that RuleRewriter doesn't support modifying several properties at
// once, so we do this in a sequence here.
let modifications = this._rules[0].startModifyingProperties();
let modifications = this._rules[0].startModifyingProperties(
this._cssProperties);
// Remember the property so it can be reverted.
if (!this._modifications.has(property.name)) {
@ -143,7 +148,8 @@ EditingSession.prototype = {
// Revert each property that we modified previously, one by one. See
// setProperties for information about why.
for (let [property, value] of this._modifications) {
let modifications = this._rules[0].startModifyingProperties();
let modifications = this._rules[0].startModifyingProperties(
this._cssProperties);
// Find the index of the property to be reverted.
let index = this.getPropertyIndex(property);
@ -358,7 +364,7 @@ LayoutView.prototype = {
*/
initEditor: function (element, event, dimension) {
let { property } = dimension;
let session = new EditingSession(this.doc, this.elementRules);
let session = new EditingSession(this);
let initialValue = session.getProperty(property);
let editor = new InplaceEditor({

Просмотреть файл

@ -14,6 +14,7 @@ const {TextProperty} =
require("devtools/client/inspector/rules/models/text-property");
const {promiseWarn} = require("devtools/client/inspector/shared/utils");
const {parseDeclarations} = require("devtools/shared/css-parsing-utils");
const {getCssProperties} = require("devtools/shared/fronts/css-properties");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
@ -59,6 +60,9 @@ function Rule(elementStyle, options) {
this.mediaText = this.domRule.mediaText;
}
const toolbox = this.elementStyle.ruleView.inspector.toolbox;
this.cssProperties = getCssProperties(toolbox);
// Populate the text properties with the style's current authoredText
// value, and add in any disabled properties from the store.
this.textProps = this._getTextProperties();
@ -248,7 +252,8 @@ Rule.prototype = {
// Note that even though StyleRuleActors normally provide parsed
// declarations already, _applyPropertiesNoAuthored is only used when
// connected to older backend that do not provide them. So parse here.
for (let cssProp of parseDeclarations(this.style.authoredText)) {
for (let cssProp of parseDeclarations(this.cssProperties.isKnown,
this.style.authoredText)) {
cssProps[cssProp.name] = cssProp;
}
@ -312,7 +317,8 @@ Rule.prototype = {
// until it settles before applying the next modification.
let resultPromise =
promise.resolve(this._applyingModifications).then(() => {
let modifications = this.style.startModifyingProperties();
let modifications = this.style.startModifyingProperties(
this.cssProperties);
modifier(modifications);
if (this.style.canSetRuleText) {
return this._applyPropertiesAuthored(modifications);
@ -388,7 +394,7 @@ Rule.prototype = {
* The property's priority (either "important" or an empty string).
*/
previewPropertyValue: function (property, value, priority) {
let modifications = this.style.startModifyingProperties();
let modifications = this.style.startModifyingProperties(this.cssProperties);
modifications.setProperty(this.textProps.indexOf(property),
property.name, value, priority);
modifications.apply().then(() => {
@ -444,7 +450,8 @@ Rule.prototype = {
// Starting with FF49, StyleRuleActors provide parsed declarations.
let props = this.style.declarations;
if (!props) {
props = parseDeclarations(this.style.authoredText, true);
props = parseDeclarations(this.cssProperties.isKnown,
this.style.authoredText, true);
}
for (let prop of props) {

Просмотреть файл

@ -8,6 +8,7 @@
const {Cc, Ci, Cu} = require("chrome");
const {escapeCSSComment} = require("devtools/shared/css-parsing-utils");
const {getCssProperties} = require("devtools/shared/fronts/css-properties");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
@ -49,6 +50,9 @@ function TextProperty(rule, name, value, priority, enabled = true,
this.enabled = !!enabled;
this.invisible = invisible;
this.updateComputed();
const toolbox = this.rule.elementStyle.ruleView.inspector.toolbox;
this.cssProperties = getCssProperties(toolbox);
}
TextProperty.prototype = {
@ -186,15 +190,7 @@ TextProperty.prototype = {
* @return {Boolean} true if the property name is known, false otherwise.
*/
isKnownProperty: function () {
try {
// If the property name is invalid, the cssPropertyIsShorthand
// will throw an exception. But if it is valid, no exception will
// be thrown; so we just ignore the return value.
domUtils.cssPropertyIsShorthand(this.name);
return true;
} catch (e) {
return false;
}
return this.cssProperties.isKnown(this.name);
},
/**

Просмотреть файл

@ -442,7 +442,7 @@ RuleEditor.prototype = {
// Auto-close the input if multiple rules get pasted into new property.
this.editor.input.addEventListener("paste",
blurOnMultipleProperties, false);
blurOnMultipleProperties(this.rule.cssProperties), false);
},
/**
@ -462,7 +462,8 @@ RuleEditor.prototype = {
// case, we're creating a new declaration, it doesn't make sense to accept
// these entries
this.multipleAddedProperties =
parseDeclarations(value, true).filter(d => d.name);
parseDeclarations(this.rule.cssProperties.isKnown, value, true)
.filter(d => d.name);
// Blur the editor field now and deal with adding declarations later when
// the field gets destroyed (see _newPropertyDestroy)

Просмотреть файл

@ -6,6 +6,7 @@
const {Ci} = require("chrome");
const {CssLogic} = require("devtools/shared/inspector/css-logic");
const {getCssProperties} = require("devtools/shared/fronts/css-properties");
const {InplaceEditor, editableField} =
require("devtools/client/shared/inplace-editor");
const {
@ -44,6 +45,9 @@ function TextPropertyEditor(ruleEditor, property) {
this.browserWindow = this.doc.defaultView.top;
this._populatedComputed = false;
const toolbox = this.ruleView.inspector.toolbox;
this.cssProperties = getCssProperties(toolbox);
this._onEnableClicked = this._onEnableClicked.bind(this);
this._onExpandClicked = this._onExpandClicked.bind(this);
this._onStartEditing = this._onStartEditing.bind(this);
@ -199,7 +203,7 @@ TextPropertyEditor.prototype = {
// Auto blur name field on multiple CSS rules get pasted in.
this.nameContainer.addEventListener("paste",
blurOnMultipleProperties, false);
blurOnMultipleProperties(this.cssProperties), false);
this.valueContainer.addEventListener("click", (event) => {
// Clicks within the value shouldn't propagate any further.
@ -559,7 +563,7 @@ TextPropertyEditor.prototype = {
// Adding multiple rules inside of name field overwrites the current
// property with the first, then adds any more onto the property list.
let properties = parseDeclarations(value);
let properties = parseDeclarations(this.cssProperties.isKnown, value);
if (properties.length) {
this.prop.setName(properties[0].name);
@ -618,7 +622,8 @@ TextPropertyEditor.prototype = {
*/
_onValueDone: function (value = "", commit, direction) {
let parsedProperties = this._getValueAndExtraProperties(value);
let val = parseSingleValue(parsedProperties.firstValue);
let val = parseSingleValue(this.cssProperties.isKnown,
parsedProperties.firstValue);
let isValueUnchanged = (!commit && !this.ruleEditor.isEditing) ||
!parsedProperties.propertiesToAdd.length &&
this.committed.value === val.value &&
@ -706,7 +711,7 @@ TextPropertyEditor.prototype = {
let firstValue = value;
let propertiesToAdd = [];
let properties = parseDeclarations(value);
let properties = parseDeclarations(this.cssProperties.isKnown, value);
// Check to see if the input string can be parsed as multiple properties
if (properties.length) {
@ -745,7 +750,7 @@ TextPropertyEditor.prototype = {
return;
}
let val = parseSingleValue(value);
let val = parseSingleValue(this.cssProperties.isKnown, value);
this.ruleEditor.rule.previewPropertyValue(this.prop, val.value,
val.priority);
},

Просмотреть файл

@ -132,13 +132,15 @@ 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);
function blurOnMultipleProperties(cssProperties) {
return (e) => {
setTimeout(() => {
let props = parseDeclarations(cssProperties.isKnown, e.target.value);
if (props.length > 1) {
e.target.blur();
}
}, 0);
};
}
exports.blurOnMultipleProperties = blurOnMultipleProperties;

Просмотреть файл

@ -0,0 +1,422 @@
/* 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";
/**
* This list is generated from the output of the CssPropertiesActor. If a server
* does not support the actor, this is loaded as a backup. This list does not
* guarantee that the server actually supports these CSS properties.
*/
exports.propertiesList = [
"align-content",
"align-items",
"align-self",
"animation-delay",
"animation-direction",
"animation-duration",
"animation-fill-mode",
"animation-iteration-count",
"animation-name",
"animation-play-state",
"animation-timing-function",
"-moz-appearance",
"backface-visibility",
"background-attachment",
"background-blend-mode",
"background-clip",
"background-color",
"background-image",
"background-origin",
"background-position-x",
"background-position-y",
"background-repeat",
"background-size",
"-moz-binding",
"block-size",
"border-block-end-color",
"border-block-end-style",
"border-block-end-width",
"border-block-start-color",
"border-block-start-style",
"border-block-start-width",
"border-bottom-color",
"-moz-border-bottom-colors",
"border-bottom-left-radius",
"border-bottom-right-radius",
"border-bottom-style",
"border-bottom-width",
"border-collapse",
"border-image-outset",
"border-image-repeat",
"border-image-slice",
"border-image-source",
"border-image-width",
"border-inline-end-color",
"border-inline-end-style",
"border-inline-end-width",
"border-inline-start-color",
"border-inline-start-style",
"border-inline-start-width",
"border-left-color",
"-moz-border-left-colors",
"border-left-style",
"border-left-width",
"border-right-color",
"-moz-border-right-colors",
"border-right-style",
"border-right-width",
"border-spacing",
"border-top-color",
"-moz-border-top-colors",
"border-top-left-radius",
"border-top-right-radius",
"border-top-style",
"border-top-width",
"bottom",
"-moz-box-align",
"box-decoration-break",
"-moz-box-direction",
"-moz-box-flex",
"-moz-box-ordinal-group",
"-moz-box-orient",
"-moz-box-pack",
"box-shadow",
"box-sizing",
"caption-side",
"clear",
"clip",
"clip-path",
"clip-rule",
"color",
"color-adjust",
"color-interpolation",
"color-interpolation-filters",
"-moz-column-count",
"-moz-column-fill",
"-moz-column-gap",
"-moz-column-rule-color",
"-moz-column-rule-style",
"-moz-column-rule-width",
"-moz-column-width",
"content",
"-moz-control-character-visibility",
"counter-increment",
"counter-reset",
"cursor",
"direction",
"display",
"dominant-baseline",
"empty-cells",
"fill",
"fill-opacity",
"fill-rule",
"filter",
"flex-basis",
"flex-direction",
"flex-grow",
"flex-shrink",
"flex-wrap",
"float",
"-moz-float-edge",
"flood-color",
"flood-opacity",
"font-family",
"font-feature-settings",
"font-kerning",
"font-language-override",
"font-size",
"font-size-adjust",
"font-stretch",
"font-style",
"font-synthesis",
"font-variant-alternates",
"font-variant-caps",
"font-variant-east-asian",
"font-variant-ligatures",
"font-variant-numeric",
"font-variant-position",
"font-weight",
"-moz-force-broken-image-icon",
"grid-auto-columns",
"grid-auto-flow",
"grid-auto-rows",
"grid-column-end",
"grid-column-gap",
"grid-column-start",
"grid-row-end",
"grid-row-gap",
"grid-row-start",
"grid-template-areas",
"grid-template-columns",
"grid-template-rows",
"height",
"hyphens",
"image-orientation",
"-moz-image-region",
"image-rendering",
"ime-mode",
"inline-size",
"isolation",
"justify-content",
"justify-items",
"justify-self",
"left",
"letter-spacing",
"lighting-color",
"line-height",
"list-style-image",
"list-style-position",
"list-style-type",
"margin-block-end",
"margin-block-start",
"margin-bottom",
"margin-inline-end",
"margin-inline-start",
"margin-left",
"margin-right",
"margin-top",
"marker-end",
"marker-mid",
"marker-offset",
"marker-start",
"mask",
"mask-type",
"max-block-size",
"max-height",
"max-inline-size",
"max-width",
"min-block-size",
"min-height",
"min-inline-size",
"min-width",
"mix-blend-mode",
"object-fit",
"object-position",
"offset-block-end",
"offset-block-start",
"offset-inline-end",
"offset-inline-start",
"opacity",
"order",
"-moz-orient",
"-moz-osx-font-smoothing",
"outline-color",
"outline-offset",
"-moz-outline-radius-bottomleft",
"-moz-outline-radius-bottomright",
"-moz-outline-radius-topleft",
"-moz-outline-radius-topright",
"outline-style",
"outline-width",
"overflow-x",
"overflow-y",
"padding-block-end",
"padding-block-start",
"padding-bottom",
"padding-inline-end",
"padding-inline-start",
"padding-left",
"padding-right",
"padding-top",
"page-break-after",
"page-break-before",
"page-break-inside",
"paint-order",
"perspective",
"perspective-origin",
"pointer-events",
"position",
"quotes",
"resize",
"right",
"ruby-align",
"ruby-position",
"scroll-behavior",
"scroll-snap-coordinate",
"scroll-snap-destination",
"scroll-snap-points-x",
"scroll-snap-points-y",
"scroll-snap-type-x",
"scroll-snap-type-y",
"shape-rendering",
"-moz-stack-sizing",
"stop-color",
"stop-opacity",
"stroke",
"stroke-dasharray",
"stroke-dashoffset",
"stroke-linecap",
"stroke-linejoin",
"stroke-miterlimit",
"stroke-opacity",
"stroke-width",
"-moz-tab-size",
"table-layout",
"text-align",
"-moz-text-align-last",
"text-anchor",
"text-combine-upright",
"text-decoration-color",
"text-decoration-line",
"text-decoration-style",
"text-emphasis-color",
"text-emphasis-position",
"text-emphasis-style",
"-webkit-text-fill-color",
"text-indent",
"text-orientation",
"text-overflow",
"text-rendering",
"text-shadow",
"-moz-text-size-adjust",
"-webkit-text-stroke-color",
"-webkit-text-stroke-width",
"text-transform",
"top",
"transform",
"transform-box",
"transform-origin",
"transform-style",
"transition-delay",
"transition-duration",
"transition-property",
"transition-timing-function",
"unicode-bidi",
"-moz-user-focus",
"-moz-user-input",
"-moz-user-modify",
"-moz-user-select",
"vector-effect",
"vertical-align",
"visibility",
"white-space",
"width",
"will-change",
"-moz-window-dragging",
"word-break",
"word-spacing",
"word-wrap",
"writing-mode",
"z-index",
"all",
"animation",
"background",
"background-position",
"border",
"border-block-end",
"border-block-start",
"border-bottom",
"border-color",
"border-image",
"border-inline-end",
"border-inline-start",
"border-left",
"border-radius",
"border-right",
"border-style",
"border-top",
"border-width",
"-moz-column-rule",
"-moz-columns",
"flex",
"flex-flow",
"font",
"font-variant",
"grid",
"grid-area",
"grid-column",
"grid-gap",
"grid-row",
"grid-template",
"list-style",
"margin",
"marker",
"outline",
"-moz-outline-radius",
"overflow",
"padding",
"scroll-snap-type",
"text-decoration",
"text-emphasis",
"-webkit-text-stroke",
"-moz-transform",
"transition",
"-moz-transform-origin",
"-moz-perspective-origin",
"-moz-perspective",
"-moz-transform-style",
"-moz-backface-visibility",
"-moz-border-image",
"-moz-transition",
"-moz-transition-delay",
"-moz-transition-duration",
"-moz-transition-property",
"-moz-transition-timing-function",
"-moz-animation",
"-moz-animation-delay",
"-moz-animation-direction",
"-moz-animation-duration",
"-moz-animation-fill-mode",
"-moz-animation-iteration-count",
"-moz-animation-name",
"-moz-animation-play-state",
"-moz-animation-timing-function",
"-moz-box-sizing",
"-moz-font-feature-settings",
"-moz-font-language-override",
"-moz-padding-end",
"-moz-padding-start",
"-moz-margin-end",
"-moz-margin-start",
"-moz-border-end",
"-moz-border-end-color",
"-moz-border-end-style",
"-moz-border-end-width",
"-moz-border-start",
"-moz-border-start-color",
"-moz-border-start-style",
"-moz-border-start-width",
"-moz-hyphens",
"-webkit-animation",
"-webkit-animation-delay",
"-webkit-animation-direction",
"-webkit-animation-duration",
"-webkit-animation-fill-mode",
"-webkit-animation-iteration-count",
"-webkit-animation-name",
"-webkit-animation-play-state",
"-webkit-animation-timing-function",
"-webkit-filter",
"-webkit-text-size-adjust",
"-webkit-transform",
"-webkit-transform-origin",
"-webkit-transform-style",
"-webkit-backface-visibility",
"-webkit-perspective",
"-webkit-perspective-origin",
"-webkit-transition",
"-webkit-transition-delay",
"-webkit-transition-duration",
"-webkit-transition-property",
"-webkit-transition-timing-function",
"-webkit-border-radius",
"-webkit-border-top-left-radius",
"-webkit-border-top-right-radius",
"-webkit-border-bottom-left-radius",
"-webkit-border-bottom-right-radius",
"-webkit-background-clip",
"-webkit-background-origin",
"-webkit-background-size",
"-webkit-border-image",
"-webkit-box-shadow",
"-webkit-box-sizing",
"-webkit-box-flex",
"-webkit-box-ordinal-group",
"-webkit-box-orient",
"-webkit-box-direction",
"-webkit-box-align",
"-webkit-box-pack",
"-webkit-user-select"
];

Просмотреть файл

@ -21,6 +21,7 @@ DevToolsModules(
'css-angle.js',
'css-color-db.js',
'css-color.js',
'css-properties-db.js',
'css-reload.js',
'Curl.jsm',
'demangle.js',

Просмотреть файл

@ -8,6 +8,7 @@
var Cu = Components.utils;
const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
const {parseDeclarations, _parseCommentDeclarations} = require("devtools/shared/css-parsing-utils");
const {isCssPropertyKnown} = require("devtools/server/actors/css-properties");
const TEST_DATA = [
// Simple test
@ -359,7 +360,8 @@ function run_basic_tests() {
do_print("Test input string " + test.input);
let output;
try {
output = parseDeclarations(test.input, test.parseComments);
output = parseDeclarations(isCssPropertyKnown, test.input,
test.parseComments);
} catch (e) {
do_print("parseDeclarations threw an exception with the given input " +
"string");
@ -394,7 +396,7 @@ const COMMENT_DATA = [
function run_comment_tests() {
for (let test of COMMENT_DATA) {
do_print("Test input string " + test.input);
let output = _parseCommentDeclarations(test.input, 0,
let output = _parseCommentDeclarations(isCssPropertyKnown, test.input, 0,
test.input.length + 4);
deepEqual(output, test.expected);
}

Просмотреть файл

@ -8,6 +8,7 @@
var Cu = Components.utils;
const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
const {parseSingleValue} = require("devtools/shared/css-parsing-utils");
const {isCssPropertyKnown} = require("devtools/server/actors/css-properties");
const TEST_DATA = [
{input: null, throws: true},
@ -69,7 +70,7 @@ function run_test() {
for (let test of TEST_DATA) {
do_print("Test input value " + test.input);
try {
let output = parseSingleValue(test.input);
let output = parseSingleValue(isCssPropertyKnown, test.input);
assertOutput(output, test.expected);
} catch (e) {
do_print("parseSingleValue threw an exception with the given input " +

Просмотреть файл

@ -8,6 +8,7 @@
var Cu = Components.utils;
Cu.import("resource://devtools/shared/Loader.jsm");
const {RuleRewriter} = devtools.require("devtools/shared/css-parsing-utils");
const {isCssPropertyKnown} = require("devtools/server/actors/css-properties");
const TEST_DATA = [
{
@ -443,7 +444,7 @@ const TEST_DATA = [
];
function rewriteDeclarations(inputString, instruction, defaultIndentation) {
let rewriter = new RuleRewriter(null, inputString);
let rewriter = new RuleRewriter(isCssPropertyKnown, null, inputString);
rewriter.defaultIndentation = defaultIndentation;
switch (instruction.type) {

Просмотреть файл

@ -0,0 +1,53 @@
/* 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, Cu } = require("chrome");
loader.lazyGetter(this, "DOMUtils", () => {
return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
});
const protocol = require("devtools/shared/protocol");
const { ActorClassWithSpec, Actor } = protocol;
const { cssPropertiesSpec } = require("devtools/shared/specs/css-properties");
var CssPropertiesActor = exports.CssPropertiesActor = ActorClassWithSpec(cssPropertiesSpec, {
typeName: "cssProperties",
initialize: function(conn, parent) {
Actor.prototype.initialize.call(this, conn);
this.parent = parent;
},
destroy: function() {
Actor.prototype.destroy.call(this);
},
getCSSDatabase: function() {
const propertiesList = DOMUtils.getCSSPropertyNames(DOMUtils.INCLUDE_ALIASES);
return { propertiesList };
}
});
/**
* Test if a CSS is property is known using server-code.
*
* @param {string} name
* @return {Boolean}
*/
function isCssPropertyKnown(name) {
try {
// If the property name is unknown, the cssPropertyIsShorthand
// will throw an exception. But if it is known, no exception will
// be thrown; so we just ignore the return value.
DOMUtils.cssPropertyIsShorthand(name);
return true;
} catch (e) {
return false;
}
}
exports.isCssPropertyKnown = isCssPropertyKnown

Просмотреть файл

@ -21,6 +21,7 @@ DevToolsModules(
'childtab.js',
'chrome.js',
'common.js',
'css-properties.js',
'csscoverage.js',
'device.js',
'director-manager.js',

Просмотреть файл

@ -10,6 +10,7 @@ const protocol = require("devtools/shared/protocol");
const {LongStringActor} = require("devtools/server/actors/string");
const {getDefinedGeometryProperties} = require("devtools/server/actors/highlighters/geometry-editor");
const {parseDeclarations} = require("devtools/shared/css-parsing-utils");
const {isCssPropertyKnown} = require("devtools/server/actors/css-properties");
const {Task} = require("devtools/shared/task");
const events = require("sdk/event/core");
@ -1093,8 +1094,9 @@ var StyleRuleActor = protocol.ActorClassWithSpec(styleRuleSpec, {
// and so that we can safely determine if a declaration is valid rather than
// have the client guess it.
if (form.authoredText || form.cssText) {
let declarations = parseDeclarations(form.authoredText ||
form.cssText, true);
let declarations = parseDeclarations(isCssPropertyKnown,
form.authoredText || form.cssText,
true);
form.declarations = declarations.map(decl => {
decl.isValid = DOMUtils.cssPropertyIsValid(decl.name, decl.value);
return decl;

Просмотреть файл

@ -525,6 +525,11 @@ var DebuggerServer = {
constructor: "ReflowActor",
type: { tab: true }
});
this.registerModule("devtools/server/actors/css-properties", {
prefix: "cssProperties",
constructor: "CssPropertiesActor",
type: { tab: true }
});
this.registerModule("devtools/server/actors/csscoverage", {
prefix: "cssUsage",
constructor: "CSSUsageActor",

Просмотреть файл

@ -8,6 +8,7 @@ support-files =
Debugger.Source.prototype.element.html
director-helpers.js
hello-actor.js
inspector_css-properties.html
inspector_getImageData.html
inspector-delay-image-response.sjs
inspector-helpers.js
@ -32,6 +33,7 @@ skip-if = buildapp == 'mulet'
[test_css-logic-inheritance.html]
[test_css-logic-media-queries.html]
[test_css-logic-specificity.html]
[test_css-properties.html]
[test_Debugger.Source.prototype.introductionScript.html]
[test_Debugger.Source.prototype.introductionType.html]
[test_Debugger.Source.prototype.element.html]

Просмотреть файл

@ -0,0 +1,10 @@
<html>
<head>
<body>
<script type="text/javascript">
window.onload = function() {
window.opener.postMessage('ready', '*');
};
</script>
</body>
</html>

Просмотреть файл

@ -0,0 +1,95 @@
<!DOCTYPE HTML>
<html>
<!--
Bug 1265798 - Replace inIDOMUtils.cssPropertyIsShorthand
-->
<head>
<meta charset="utf-8">
<title>Test CSS Properties Actor</title>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
<script type="application/javascript;version=1.8" src="inspector-helpers.js"></script>
<script type="application/javascript;version=1.8">
window.onload = function() {
const { initCssProperties, getCssProperties } =
require("devtools/shared/fronts/css-properties");
function promiseAttachUrl (url) {
return new Promise((resolve, reject) => {
attachURL(url, function(err, client, tab, doc) {
if (err) {
return reject(err);
}
resolve({client, tab, doc});
});
})
}
const runCssPropertiesTests = Task.async(function* (url, useActor) {
info("Opening two tabs.");
let attachmentA = yield promiseAttachUrl(url);
let attachmentB = yield promiseAttachUrl(url);
const toolboxMockA = {
target: {
hasActor: () => useActor,
client: attachmentA.client,
form: attachmentA.tab
}
};
const toolboxMockB = {
target: {
hasActor: () => useActor,
client: attachmentB.client,
form: attachmentB.tab
}
};
yield initCssProperties(toolboxMockA);
yield initCssProperties(toolboxMockB);
const cssProperties = getCssProperties(toolboxMockA);
const cssPropertiesA = getCssProperties(toolboxMockA);
const cssPropertiesB = getCssProperties(toolboxMockB);
is(cssProperties, cssPropertiesA,
"Multiple calls with the same toolbox returns the same object.");
isnot(cssProperties, cssPropertiesB,
"Multiple calls with the different toolboxes return different "+
" objects.");
ok(cssProperties.isKnown("border"),
"The `border` shorthand property is known.");
ok(cssProperties.isKnown("display"),
"The `display` property is known.");
ok(!cssProperties.isKnown("foobar"),
"A fake property is not known.");
ok(cssProperties.isKnown("--foobar"),
"A CSS variable properly evaluates.");
ok(cssProperties.isKnown("--foob\\{ar"),
"A CSS variable with escaped character properly evaluates.");
ok(cssProperties.isKnown("--fübar"),
"A CSS variable unicode properly evaluates.");
ok(!cssProperties.isKnown("--foo bar"),
"A CSS variable with spaces fails");
});
addAsyncTest(function* setup() {
let url = document.getElementById("cssProperties").href;
yield runCssPropertiesTests(url, true);
yield runCssPropertiesTests(url, false);
runNextTest();
});
SimpleTest.waitForExplicitFinish();
runNextTest();
}
</script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1265798">Mozilla Bug 1265798</a>
<a id="cssProperties" target="_blank" href="inspector_css-properties.html">Test Document</a>
</body>
</html>

Просмотреть файл

@ -12,6 +12,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=
<script type="application/javascript;version=1.8" src="inspector-helpers.js"></script>
<script type="application/javascript;version=1.8">
const inspector = require("devtools/server/actors/inspector");
const {isCssPropertyKnown} = require("devtools/server/actors/css-properties");
window.onload = function() {
SimpleTest.waitForExplicitFinish();
@ -82,13 +83,13 @@ addAsyncTest(function* modifyProperties() {
});
function* setProperty(rule, index, name, value) {
let changes = rule.startModifyingProperties();
let changes = rule.startModifyingProperties(isCssPropertyKnown);
changes.setProperty(index, name, value);
yield changes.apply();
}
function* removeProperty(rule, index, name) {
let changes = rule.startModifyingProperties();
let changes = rule.startModifyingProperties(isCssPropertyKnown);
changes.removeProperty(index, name);
yield changes.apply();
}

Просмотреть файл

@ -14,14 +14,10 @@
"use strict";
const {Cc, Ci} = require("chrome");
loader.lazyRequireGetter(this, "CSS", "CSS");
const promise = require("promise");
const {getCSSLexer} = require("devtools/shared/css-lexer");
const {Task} = require("devtools/shared/task");
loader.lazyGetter(this, "DOMUtils", () => {
return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
});
const SELECTOR_ATTRIBUTE = exports.SELECTOR_ATTRIBUTE = 1;
const SELECTOR_ELEMENT = exports.SELECTOR_ELEMENT = 2;
@ -150,33 +146,15 @@ function unescapeCSSComment(inputString) {
return result.replace(/\*\\(\\*)\//g, "*$1/");
}
/**
* A helper function for parseDeclarations that implements a heuristic
* to decide whether a given bit of comment text should be parsed as a
* declaration.
*
* @param {String} name the property name that has been parsed
* @return {Boolean} true if the property should be parsed, false if
* the remainder of the comment should be skipped
*/
function shouldParsePropertyInComment(name) {
try {
// If the property name is invalid, the cssPropertyIsShorthand
// will throw an exception. But if it is valid, no exception will
// be thrown; so we just ignore the return value.
DOMUtils.cssPropertyIsShorthand(name);
return true;
} catch (e) {
return false;
}
}
/**
* A helper function for @see parseDeclarations that handles parsing
* of comment text. This wraps a recursive call to parseDeclarations
* with the processing needed to ensure that offsets in the result
* refer back to the original, unescaped, input string.
*
* @param {Function} isCssPropertyKnown
* A function to check if the CSS property is known. This is either an
* internal server function or from the CssPropertiesFront.
* @param {String} commentText The text of the comment, without the
* delimiters.
* @param {Number} startOffset The offset of the comment opener
@ -186,7 +164,8 @@ function shouldParsePropertyInComment(name) {
* @return {array} Array of declarations of the same form as returned
* by parseDeclarations.
*/
function parseCommentDeclarations(commentText, startOffset, endOffset) {
function parseCommentDeclarations(isCssPropertyKnown, commentText, startOffset,
endOffset) {
let commentOverride = false;
if (commentText === "") {
return [];
@ -242,8 +221,8 @@ function parseCommentDeclarations(commentText, startOffset, endOffset) {
// seem worthwhile to support declarations in comments-in-comments
// here, as there's no way to generate those using the tools, and
// users would be crazy to write such things.
let newDecls = parseDeclarationsInternal(rewrittenText, false,
true, commentOverride);
let newDecls = parseDeclarationsInternal(isCssPropertyKnown, rewrittenText,
false, true, commentOverride);
for (let decl of newDecls) {
decl.offsets[0] = rewrites[decl.offsets[0]];
decl.offsets[1] = rewrites[decl.offsets[1]];
@ -276,6 +255,8 @@ function getEmptyDeclaration() {
* The return value and arguments are like parseDeclarations, with
* these additional arguments.
*
* @param {Function} isCssPropertyKnown
* Function to check if the CSS property is known.
* @param {Boolean} inComment
* If true, assume that this call is parsing some text
* which came from a comment in another declaration.
@ -287,8 +268,8 @@ function getEmptyDeclaration() {
* rewriteDeclarations, and skip the usual name-checking
* heuristic.
*/
function parseDeclarationsInternal(inputString, parseComments,
inComment, commentOverride) {
function parseDeclarationsInternal(isCssPropertyKnown, inputString,
parseComments, inComment, commentOverride) {
if (inputString === null || inputString === undefined) {
throw new Error("empty input string");
}
@ -335,7 +316,7 @@ function parseDeclarationsInternal(inputString, parseComments,
// When parsing a comment body, if the left-hand-side is not a
// valid property name, then drop it and stop parsing.
if (inComment && !commentOverride &&
!shouldParsePropertyInComment(lastProp.name)) {
!isCssPropertyKnown(lastProp.name)) {
lastProp.name = null;
break;
}
@ -378,7 +359,8 @@ function parseDeclarationsInternal(inputString, parseComments,
if (parseComments && !lastProp.name && !lastProp.value) {
let commentText = inputString.substring(token.startOffset + 2,
token.endOffset - 2);
let newDecls = parseCommentDeclarations(commentText, token.startOffset,
let newDecls = parseCommentDeclarations(isCssPropertyKnown, commentText,
token.startOffset,
token.endOffset);
// Insert the new declarations just before the final element.
@ -420,13 +402,18 @@ function parseDeclarationsInternal(inputString, parseComments,
/**
* Returns an array of CSS declarations given a string.
* For example, parseDeclarations("width: 1px; height: 1px") would return
* For example, parseDeclarations(isCssPropertyKnown, "width: 1px; height: 1px")
* would return:
* [{name:"width", value: "1px"}, {name: "height", "value": "1px"}]
*
* The input string is assumed to only contain declarations so { and }
* characters will be treated as part of either the property or value,
* depending where it's found.
*
* @param {Function} isCssPropertyKnown
* A function to check if the CSS property is known. This is either an
* internal server function or from the CssPropertiesFront.
* that are supported by the server.
* @param {String} inputString
* An input string of CSS
* @param {Boolean} parseComments
@ -450,8 +437,10 @@ function parseDeclarationsInternal(inputString, parseComments,
* on the object, which will hold the offsets of the start
* and end of the enclosing comment.
*/
function parseDeclarations(inputString, parseComments = false) {
return parseDeclarationsInternal(inputString, parseComments, false, false);
function parseDeclarations(isCssPropertyKnown, inputString,
parseComments = false) {
return parseDeclarationsInternal(isCssPropertyKnown, inputString,
parseComments, false, false);
}
/**
@ -464,7 +453,8 @@ function parseDeclarations(inputString, parseComments = false) {
*
* An example showing how to disable the 3rd property in a rule:
*
* let rewriter = new RuleRewriter(ruleActor, ruleActor.authoredText);
* let rewriter = new RuleRewriter(isCssPropertyKnown, ruleActor,
* ruleActor.authoredText);
* rewriter.setPropertyEnabled(3, "color", false);
* rewriter.apply().then(() => { ... the change is made ... });
*
@ -478,13 +468,20 @@ function parseDeclarations(inputString, parseComments = false) {
* on this object. This property has the same form as the |changed|
* property of the object returned by |getResult|.
*
* @param {Function} isCssPropertyKnown
* A function to check if the CSS property is known. This is either an
* internal server function or from the CssPropertiesFront.
* that are supported by the server. Note that if Bug 1222047
* is completed then isCssPropertyKnown will not need to be passed in.
* The CssProperty front will be able to obtained directly from the
* RuleRewriter.
* @param {StyleRuleFront} rule The style rule to use. Note that this
* is only needed by the |apply| and |getDefaultIndentation| methods;
* and in particular for testing it can be |null|.
* @param {String} inputString The CSS source text to parse and modify.
* @return {Object} an object that can be used to rewrite the input text.
*/
function RuleRewriter(rule, inputString) {
function RuleRewriter(isCssPropertyKnown, rule, inputString) {
this.rule = rule;
this.inputString = inputString;
// Whether there are any newlines in the input text.
@ -493,7 +490,8 @@ function RuleRewriter(rule, inputString) {
// performing the requested action.
this.changedDeclarations = {};
// The declarations.
this.declarations = parseDeclarations(this.inputString, true);
this.declarations = parseDeclarations(isCssPropertyKnown, this.inputString,
true);
this.decl = null;
this.result = null;
@ -1082,12 +1080,17 @@ function parsePseudoClassesAndAttributes(value) {
* Expects a single CSS value to be passed as the input and parses the value
* and priority.
*
* @param {Function} isCssPropertyKnown
* A function to check if the CSS property is known. This is either an
* internal server function or from the CssPropertiesFront.
* that are supported by the server.
* @param {String} value
* The value from the text editor.
* @return {Object} an object with 'value' and 'priority' properties.
*/
function parseSingleValue(value) {
let declaration = parseDeclarations("a: " + value + ";")[0];
function parseSingleValue(isCssPropertyKnown, value) {
let declaration = parseDeclarations(isCssPropertyKnown,
"a: " + value + ";")[0];
return {
value: declaration ? declaration.value : "",
priority: declaration ? declaration.priority : ""

Просмотреть файл

@ -0,0 +1,128 @@
/* 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 { FrontClassWithSpec, Front } = require("devtools/shared/protocol");
const { cssPropertiesSpec } = require("devtools/shared/specs/css-properties");
const { Task } = require("devtools/shared/task");
/**
* 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
*/
var NON_ASCII = "[^\\x00-\\x7F]";
var ESCAPE = "\\\\[^\n\r]";
var FIRST_CHAR = ["[_a-z]", NON_ASCII, ESCAPE].join("|");
var TRAILING_CHAR = ["[_a-z0-9-]", NON_ASCII, ESCAPE].join("|");
var IS_VARIABLE_TOKEN = new RegExp(`^--(${FIRST_CHAR})(${TRAILING_CHAR})*$`,
"i");
/**
* Check that this is a CSS variable.
*
* @param {String} input
* @return {Boolean}
*/
function isCssVariable(input) {
return !!input.match(IS_VARIABLE_TOKEN);
}
var cachedCssProperties = new WeakMap();
/**
* The CssProperties front provides a mechanism to have a one-time asynchronous
* load of a CSS properties database. This is then fed into the CssProperties
* interface that provides synchronous methods for finding out what CSS
* properties the current server supports.
*/
const CssPropertiesFront = FrontClassWithSpec(cssPropertiesSpec, {
initialize: function (client, { cssPropertiesActor }) {
Front.prototype.initialize.call(this, client, {actor: cssPropertiesActor});
this.manage(this);
}
});
exports.CssPropertiesFront = CssPropertiesFront;
/**
* Ask questions to a CSS database. This class does not care how the database
* gets loaded in, only the questions that you can ask to it.
*
* @param {array} properties
* A list of all supported CSS properties.
*/
function CssProperties(properties) {
this.properties = properties;
// Bind isKnown so it can be passed around to helper functions.
this.isKnown = this.isKnown.bind(this);
}
CssProperties.prototype = {
/**
* Checks to see if the property is known by the browser. This function has
* `this` already bound so that it can be passed around by reference.
*
* @param {String} property
* The property name to be checked.
* @return {Boolean}
*/
isKnown(property) {
return this.properties.includes(property) || isCssVariable(property);
}
};
exports.CssProperties = CssProperties;
/**
* Create a CssProperties object with a fully loaded CSS database. The
* CssProperties interface can be queried synchronously, but the initialization
* is potentially async and should be handled up-front when the tool is created.
*
* The front is returned only with this function so that it can be destroyed
* once the toolbox is destroyed.
*
* @param {Toolbox} The current toolbox.
* @returns {Promise} Resolves to {cssProperties, cssPropertiesFront}.
*/
exports.initCssProperties = Task.async(function* (toolbox) {
let client = toolbox.target.client;
if (cachedCssProperties.has(client)) {
return cachedCssProperties.get(client);
}
let propertiesList, front;
// Get the list dynamically if the cssProperties exists.
if (toolbox.target.hasActor("cssProperties")) {
front = CssPropertiesFront(client, toolbox.target.form);
const db = yield front.getCSSDatabase();
propertiesList = db.propertiesList;
} else {
// The target does not support this actor, so require a static list of
// supported properties.
const db = require("devtools/client/shared/css-properties-db");
propertiesList = db.propertiesList;
}
const cssProperties = new CssProperties(propertiesList);
cachedCssProperties.set(client, {cssProperties, front});
return {cssProperties, front};
});
/**
* Synchronously get a cached and initialized CssProperties.
*
* @param {Toolbox} The current toolbox.
* @returns {CssProperties}
*/
exports.getCssProperties = function (toolbox) {
if (!cachedCssProperties.has(toolbox.target.client)) {
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;
};
exports.CssPropertiesFront = CssPropertiesFront;

Просмотреть файл

@ -7,6 +7,7 @@
DevToolsModules(
'addons.js',
'animation.js',
'css-properties.js',
'highlighters.js',
'inspector.js',
'storage.js',

Просмотреть файл

@ -128,10 +128,14 @@ const StyleRuleFront = FrontClassWithSpec(styleRuleSpec, {
* A RuleRewriter will be returned when the rule's canSetRuleText
* trait is true; otherwise a RuleModificationList will be
* returned.
*
* @param {CssPropertiesFront} cssProperties
* This is needed by the RuleRewriter.
* @return {RuleModificationList}
*/
startModifyingProperties: function () {
startModifyingProperties: function (cssProperties) {
if (this.canSetRuleText) {
return new RuleRewriter(this, this.authoredText);
return new RuleRewriter(cssProperties.isKnown, this, this.authoredText);
}
return new RuleModificationList(this);
},
@ -416,4 +420,3 @@ var RuleModificationList = Class({
// Nothing.
},
});

Просмотреть файл

@ -0,0 +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 { RetVal, generateActorSpec } = require("devtools/shared/protocol");
const cssPropertiesSpec = generateActorSpec({
typeName: "cssProperties",
methods: {
getCSSDatabase: {
request: {},
response: RetVal("json"),
}
}
});
exports.cssPropertiesSpec = cssPropertiesSpec;

Просмотреть файл

@ -7,6 +7,7 @@
DevToolsModules(
'addons.js',
'animation.js',
'css-properties.js',
'highlighters.js',
'inspector.js',
'storage.js',