зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
a56fcbf802
Коммит
262a831a1f
|
@ -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',
|
||||
|
|
Загрузка…
Ссылка в новой задаче