From 262a831a1f019ff8ef66e3ba4d6a9d31092e8264 Mon Sep 17 00:00:00 2001 From: Greg Tatum Date: Thu, 26 May 2016 08:48:00 -0700 Subject: [PATCH] Bug 1265798 - Implement CSS database to query css properties r=pbro MozReview-Commit-ID: CAUh2GyeA2o --HG-- extra : rebase_source : fc8e97f7bfc92c709014e58bd7f1670bc859460e --- devtools/client/inspector/inspector-panel.js | 27 +- devtools/client/inspector/layout/layout.js | 24 +- .../client/inspector/rules/models/rule.js | 15 +- .../inspector/rules/models/text-property.js | 14 +- .../inspector/rules/views/rule-editor.js | 5 +- .../rules/views/text-property-editor.js | 15 +- devtools/client/inspector/shared/utils.js | 16 +- devtools/client/shared/css-properties-db.js | 422 ++++++++++++++++++ devtools/client/shared/moz.build | 1 + .../test/unit/test_parseDeclarations.js | 6 +- .../shared/test/unit/test_parseSingleValue.js | 3 +- .../test/unit/test_rewriteDeclarations.js | 3 +- devtools/server/actors/css-properties.js | 53 +++ devtools/server/actors/moz.build | 1 + devtools/server/actors/styles.js | 6 +- devtools/server/main.js | 5 + devtools/server/tests/mochitest/chrome.ini | 2 + .../mochitest/inspector_css-properties.html | 10 + .../tests/mochitest/test_css-properties.html | 95 ++++ .../tests/mochitest/test_styles-modify.html | 5 +- devtools/shared/css-parsing-utils.js | 83 ++-- devtools/shared/fronts/css-properties.js | 128 ++++++ devtools/shared/fronts/moz.build | 1 + devtools/shared/fronts/styles.js | 9 +- devtools/shared/specs/css-properties.js | 19 + devtools/shared/specs/moz.build | 1 + 26 files changed, 872 insertions(+), 97 deletions(-) create mode 100644 devtools/client/shared/css-properties-db.js create mode 100644 devtools/server/actors/css-properties.js create mode 100644 devtools/server/tests/mochitest/inspector_css-properties.html create mode 100644 devtools/server/tests/mochitest/test_css-properties.html create mode 100644 devtools/shared/fronts/css-properties.js create mode 100644 devtools/shared/specs/css-properties.js diff --git a/devtools/client/inspector/inspector-panel.js b/devtools/client/inspector/inspector-panel.js index 34eeada58509..672bfec11745 100644 --- a/devtools/client/inspector/inspector-panel.js +++ b/devtools/client/inspector/inspector-panel.js @@ -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; diff --git a/devtools/client/inspector/layout/layout.js b/devtools/client/inspector/layout/layout.js index 842d538af444..3ea62f795cc5 100644 --- a/devtools/client/inspector/layout/layout.js +++ b/devtools/client/inspector/layout/layout.js @@ -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({ diff --git a/devtools/client/inspector/rules/models/rule.js b/devtools/client/inspector/rules/models/rule.js index ef72e226d970..4420e91eefab 100644 --- a/devtools/client/inspector/rules/models/rule.js +++ b/devtools/client/inspector/rules/models/rule.js @@ -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) { diff --git a/devtools/client/inspector/rules/models/text-property.js b/devtools/client/inspector/rules/models/text-property.js index 1d344a72d033..87adc8da9844 100644 --- a/devtools/client/inspector/rules/models/text-property.js +++ b/devtools/client/inspector/rules/models/text-property.js @@ -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); }, /** diff --git a/devtools/client/inspector/rules/views/rule-editor.js b/devtools/client/inspector/rules/views/rule-editor.js index f7ed7ea3d762..47d595b67f8b 100644 --- a/devtools/client/inspector/rules/views/rule-editor.js +++ b/devtools/client/inspector/rules/views/rule-editor.js @@ -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) diff --git a/devtools/client/inspector/rules/views/text-property-editor.js b/devtools/client/inspector/rules/views/text-property-editor.js index c690a8ee87b9..392d3a0a579a 100644 --- a/devtools/client/inspector/rules/views/text-property-editor.js +++ b/devtools/client/inspector/rules/views/text-property-editor.js @@ -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); }, diff --git a/devtools/client/inspector/shared/utils.js b/devtools/client/inspector/shared/utils.js index 4587278048a9..7bf2d87cdeb4 100644 --- a/devtools/client/inspector/shared/utils.js +++ b/devtools/client/inspector/shared/utils.js @@ -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; diff --git a/devtools/client/shared/css-properties-db.js b/devtools/client/shared/css-properties-db.js new file mode 100644 index 000000000000..75e3152d7bcc --- /dev/null +++ b/devtools/client/shared/css-properties-db.js @@ -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" +]; diff --git a/devtools/client/shared/moz.build b/devtools/client/shared/moz.build index 94f739f91014..575026e5438a 100644 --- a/devtools/client/shared/moz.build +++ b/devtools/client/shared/moz.build @@ -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', diff --git a/devtools/client/shared/test/unit/test_parseDeclarations.js b/devtools/client/shared/test/unit/test_parseDeclarations.js index 9b060bd52ac3..e11dbbe468d9 100644 --- a/devtools/client/shared/test/unit/test_parseDeclarations.js +++ b/devtools/client/shared/test/unit/test_parseDeclarations.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); } diff --git a/devtools/client/shared/test/unit/test_parseSingleValue.js b/devtools/client/shared/test/unit/test_parseSingleValue.js index 0d1b45ed6cc6..cebbbb6f5c55 100644 --- a/devtools/client/shared/test/unit/test_parseSingleValue.js +++ b/devtools/client/shared/test/unit/test_parseSingleValue.js @@ -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 " + diff --git a/devtools/client/shared/test/unit/test_rewriteDeclarations.js b/devtools/client/shared/test/unit/test_rewriteDeclarations.js index 804d8e2b46a2..27e1e98068f5 100644 --- a/devtools/client/shared/test/unit/test_rewriteDeclarations.js +++ b/devtools/client/shared/test/unit/test_rewriteDeclarations.js @@ -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) { diff --git a/devtools/server/actors/css-properties.js b/devtools/server/actors/css-properties.js new file mode 100644 index 000000000000..aeeb833c3110 --- /dev/null +++ b/devtools/server/actors/css-properties.js @@ -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 diff --git a/devtools/server/actors/moz.build b/devtools/server/actors/moz.build index 8f54a95e05c7..4ee9abd8410a 100644 --- a/devtools/server/actors/moz.build +++ b/devtools/server/actors/moz.build @@ -21,6 +21,7 @@ DevToolsModules( 'childtab.js', 'chrome.js', 'common.js', + 'css-properties.js', 'csscoverage.js', 'device.js', 'director-manager.js', diff --git a/devtools/server/actors/styles.js b/devtools/server/actors/styles.js index 7eadd7208a28..eb32b79f4b57 100644 --- a/devtools/server/actors/styles.js +++ b/devtools/server/actors/styles.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; diff --git a/devtools/server/main.js b/devtools/server/main.js index 187930e12055..2441ccafcc37 100644 --- a/devtools/server/main.js +++ b/devtools/server/main.js @@ -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", diff --git a/devtools/server/tests/mochitest/chrome.ini b/devtools/server/tests/mochitest/chrome.ini index 30b278890272..77f567667c0b 100644 --- a/devtools/server/tests/mochitest/chrome.ini +++ b/devtools/server/tests/mochitest/chrome.ini @@ -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] diff --git a/devtools/server/tests/mochitest/inspector_css-properties.html b/devtools/server/tests/mochitest/inspector_css-properties.html new file mode 100644 index 000000000000..2c160c92803f --- /dev/null +++ b/devtools/server/tests/mochitest/inspector_css-properties.html @@ -0,0 +1,10 @@ + + + + + + diff --git a/devtools/server/tests/mochitest/test_css-properties.html b/devtools/server/tests/mochitest/test_css-properties.html new file mode 100644 index 000000000000..1fc933c99095 --- /dev/null +++ b/devtools/server/tests/mochitest/test_css-properties.html @@ -0,0 +1,95 @@ + + + + + + Test CSS Properties Actor + + + + + + + Mozilla Bug 1265798 + Test Document + + diff --git a/devtools/server/tests/mochitest/test_styles-modify.html b/devtools/server/tests/mochitest/test_styles-modify.html index 91368421f9d3..5a8e20bc32d5 100644 --- a/devtools/server/tests/mochitest/test_styles-modify.html +++ b/devtools/server/tests/mochitest/test_styles-modify.html @@ -12,6 +12,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=