forked from mirrors/gecko-dev
215 lines
6.5 KiB
JavaScript
215 lines
6.5 KiB
JavaScript
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
|
/* vim: set ts=2 et sw=2 tw=80: */
|
|
/* 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 {escapeCSSComment} = require("devtools/shared/css/parsing-utils");
|
|
const {getCssProperties} = require("devtools/shared/fronts/css-properties");
|
|
|
|
/**
|
|
* TextProperty is responsible for the following:
|
|
* Manages a single property from the authoredText attribute of the
|
|
* relevant declaration.
|
|
* Maintains a list of computed properties that come from this
|
|
* property declaration.
|
|
* Changes to the TextProperty are sent to its related Rule for
|
|
* application.
|
|
*
|
|
* @param {Rule} rule
|
|
* The rule this TextProperty came from.
|
|
* @param {String} name
|
|
* The text property name (such as "background" or "border-top").
|
|
* @param {String} value
|
|
* The property's value (not including priority).
|
|
* @param {String} priority
|
|
* The property's priority (either "important" or an empty string).
|
|
* @param {Boolean} enabled
|
|
* Whether the property is enabled.
|
|
* @param {Boolean} invisible
|
|
* Whether the property is invisible. An invisible property
|
|
* does not show up in the UI; these are needed so that the
|
|
* index of a property in Rule.textProps is the same as the index
|
|
* coming from parseDeclarations.
|
|
*/
|
|
function TextProperty(rule, name, value, priority, enabled = true,
|
|
invisible = false) {
|
|
this.rule = rule;
|
|
this.name = name;
|
|
this.value = value;
|
|
this.priority = priority;
|
|
this.enabled = !!enabled;
|
|
this.invisible = invisible;
|
|
this.panelDoc = this.rule.elementStyle.ruleView.inspector.panelDoc;
|
|
|
|
const toolbox = this.rule.elementStyle.ruleView.inspector.toolbox;
|
|
this.cssProperties = getCssProperties(toolbox);
|
|
|
|
this.updateComputed();
|
|
}
|
|
|
|
TextProperty.prototype = {
|
|
/**
|
|
* Update the editor associated with this text property,
|
|
* if any.
|
|
*/
|
|
updateEditor: function () {
|
|
if (this.editor) {
|
|
this.editor.update();
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Update the list of computed properties for this text property.
|
|
*/
|
|
updateComputed: function () {
|
|
if (!this.name) {
|
|
return;
|
|
}
|
|
|
|
// This is a bit funky. To get the list of computed properties
|
|
// for this text property, we'll set the property on a dummy element
|
|
// and see what the computed style looks like.
|
|
let dummyElement = this.rule.elementStyle.ruleView.dummyElement;
|
|
let dummyStyle = dummyElement.style;
|
|
dummyStyle.cssText = "";
|
|
dummyStyle.setProperty(this.name, this.value, this.priority);
|
|
|
|
this.computed = [];
|
|
|
|
// Manually get all the properties that are set when setting a value on
|
|
// this.name and check the computed style on dummyElement for each one.
|
|
// If we just read dummyStyle, it would skip properties when value === "".
|
|
let subProps = this.cssProperties.getSubproperties(this.name);
|
|
|
|
for (let prop of subProps) {
|
|
this.computed.push({
|
|
textProp: this,
|
|
name: prop,
|
|
value: dummyStyle.getPropertyValue(prop),
|
|
priority: dummyStyle.getPropertyPriority(prop),
|
|
});
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Set all the values from another TextProperty instance into
|
|
* this TextProperty instance.
|
|
*
|
|
* @param {TextProperty} prop
|
|
* The other TextProperty instance.
|
|
*/
|
|
set: function (prop) {
|
|
let changed = false;
|
|
for (let item of ["name", "value", "priority", "enabled"]) {
|
|
if (this[item] !== prop[item]) {
|
|
this[item] = prop[item];
|
|
changed = true;
|
|
}
|
|
}
|
|
|
|
if (changed) {
|
|
this.updateEditor();
|
|
}
|
|
},
|
|
|
|
setValue: function (value, priority, force = false) {
|
|
let store = this.rule.elementStyle.store;
|
|
|
|
if (this.editor && value !== this.editor.committed.value || force) {
|
|
store.userProperties.setProperty(this.rule.style, this.name, value);
|
|
}
|
|
|
|
this.rule.setPropertyValue(this, value, priority);
|
|
this.updateEditor();
|
|
},
|
|
|
|
/**
|
|
* Called when the property's value has been updated externally, and
|
|
* the property and editor should update.
|
|
*/
|
|
noticeNewValue: function (value) {
|
|
if (value !== this.value) {
|
|
this.value = value;
|
|
this.updateEditor();
|
|
}
|
|
},
|
|
|
|
setName: function (name) {
|
|
let store = this.rule.elementStyle.store;
|
|
|
|
if (name !== this.name) {
|
|
store.userProperties.setProperty(this.rule.style, name,
|
|
this.editor.committed.value);
|
|
}
|
|
|
|
this.rule.setPropertyName(this, name);
|
|
this.updateEditor();
|
|
},
|
|
|
|
setEnabled: function (value) {
|
|
this.rule.setPropertyEnabled(this, value);
|
|
this.updateEditor();
|
|
},
|
|
|
|
remove: function () {
|
|
this.rule.removeProperty(this);
|
|
},
|
|
|
|
/**
|
|
* Return a string representation of the rule property.
|
|
*/
|
|
stringifyProperty: function () {
|
|
// Get the displayed property value
|
|
let declaration = this.name + ": " + this.editor.valueSpan.textContent +
|
|
";";
|
|
|
|
// Comment out property declarations that are not enabled
|
|
if (!this.enabled) {
|
|
declaration = "/* " + escapeCSSComment(declaration) + " */";
|
|
}
|
|
|
|
return declaration;
|
|
},
|
|
|
|
/**
|
|
* See whether this property's name is known.
|
|
*
|
|
* @return {Boolean} true if the property name is known, false otherwise.
|
|
*/
|
|
isKnownProperty: function () {
|
|
return this.cssProperties.isKnown(this.name);
|
|
},
|
|
|
|
/**
|
|
* Validate this property. Does it make sense for this value to be assigned
|
|
* to this property name?
|
|
*
|
|
* @return {Boolean} true if the property value is valid, false otherwise.
|
|
*/
|
|
isValid: function () {
|
|
// Starting with FF49, StyleRuleActors provide a list of parsed
|
|
// declarations, with data about their validity, but if we don't have this,
|
|
// compute validity locally (which might not be correct, but better than
|
|
// nothing).
|
|
if (!this.rule.domRule.declarations) {
|
|
return this.cssProperties.isValidOnClient(this.name, this.value, this.panelDoc);
|
|
}
|
|
|
|
let selfIndex = this.rule.textProps.indexOf(this);
|
|
|
|
// When adding a new property in the rule-view, the TextProperty object is
|
|
// created right away before the rule gets updated on the server, so we're
|
|
// not going to find the corresponding declaration object yet. Default to
|
|
// true.
|
|
if (!this.rule.domRule.declarations[selfIndex]) {
|
|
return true;
|
|
}
|
|
|
|
return this.rule.domRule.declarations[selfIndex].isValid;
|
|
}
|
|
};
|
|
|
|
module.exports = TextProperty;
|