1 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
2 /* vim: set ts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
9 const { generateUUID } = require("devtools/shared/generate-uuid");
11 loader.lazyRequireGetter(this, "escapeCSSComment", "devtools/shared/css/parsing-utils", true);
14 * TextProperty is responsible for the following:
15 * Manages a single property from the authoredText attribute of the
16 * relevant declaration.
17 * Maintains a list of computed properties that come from this
18 * property declaration.
19 * Changes to the TextProperty are sent to its related Rule for
25 * The rule this TextProperty came from.
26 * @param {String} name
27 * The text property name (such as "background" or "border-top").
28 * @param {String} value
29 * The property's value (not including priority).
30 * @param {String} priority
31 * The property's priority (either "important" or an empty string).
32 * @param {Boolean} enabled
33 * Whether the property is enabled.
34 * @param {Boolean} invisible
35 * Whether the property is invisible. In an inherited rule, only show
36 * the inherited declarations. The other declarations are considered
37 * invisible and does not show up in the UI. These are needed so that
38 * the index of a property in Rule.textProps is the same as the index
39 * coming from parseDeclarations.
41 constructor(rule, name, value, priority, enabled = true, invisible = false) {
42 this.id = name + "_" + generateUUID().toString();
46 this.priority = priority;
47 this.enabled = !!enabled;
48 this.invisible = invisible;
49 this.cssProperties = this.rule.elementStyle.ruleView.cssProperties;
50 this.panelDoc = this.rule.elementStyle.ruleView.inspector.panelDoc;
52 this.updateComputed();
55 get computedProperties() {
57 .filter(computed => computed.name !== this.name)
60 isOverridden: computed.overridden,
62 priority: computed.priority,
63 value: computed.value,
69 * See whether this property's name is known.
71 * @return {Boolean} true if the property name is known, false otherwise.
73 get isKnownProperty() {
74 return this.cssProperties.isKnown(this.name);
78 * Update the editor associated with this text property,
88 * Update the list of computed properties for this text property.
95 // This is a bit funky. To get the list of computed properties
96 // for this text property, we'll set the property on a dummy element
97 // and see what the computed style looks like.
98 const dummyElement = this.rule.elementStyle.ruleView.dummyElement;
99 const dummyStyle = dummyElement.style;
100 dummyStyle.cssText = "";
101 dummyStyle.setProperty(this.name, this.value, this.priority);
105 // Manually get all the properties that are set when setting a value on
106 // this.name and check the computed style on dummyElement for each one.
107 // If we just read dummyStyle, it would skip properties when value === "".
108 const subProps = this.cssProperties.getSubproperties(this.name);
110 for (const prop of subProps) {
114 value: dummyStyle.getPropertyValue(prop),
115 priority: dummyStyle.getPropertyPriority(prop),
121 * Set all the values from another TextProperty instance into
122 * this TextProperty instance.
124 * @param {TextProperty} prop
125 * The other TextProperty instance.
129 for (const item of ["name", "value", "priority", "enabled"]) {
130 if (this[item] !== prop[item]) {
131 this[item] = prop[item];
141 setValue(value, priority, force = false) {
142 const store = this.rule.elementStyle.store;
144 if (value !== this.value || force) {
145 store.userProperties.setProperty(this.rule.domRule, this.name, value);
148 return this.rule.setPropertyValue(this, value, priority)
149 .then(() => this.updateEditor());
153 * Called when the property's value has been updated externally, and
154 * the property and editor should update to reflect that value.
156 * @param {String} value
160 if (value !== this.value) {
166 async setName(name) {
167 if (name !== this.name) {
168 const store = this.rule.elementStyle.store;
169 store.userProperties.setProperty(this.rule.domRule, name, this.value);
172 await this.rule.setPropertyName(this, name);
177 this.rule.setPropertyEnabled(this, value);
182 this.rule.removeProperty(this);
186 * Return a string representation of the rule property.
188 stringifyProperty() {
189 // Get the displayed property value
190 let declaration = this.name + ": " + this.value;
193 declaration += " !" + this.priority;
198 // Comment out property declarations that are not enabled
200 declaration = "/* " + escapeCSSComment(declaration) + " */";
207 * Validate this property. Does it make sense for this value to be assigned
208 * to this property name?
210 * @return {Boolean} true if the whole CSS declaration is valid, false otherwise.
213 const selfIndex = this.rule.textProps.indexOf(this);
215 // When adding a new property in the rule-view, the TextProperty object is
216 // created right away before the rule gets updated on the server, so we're
217 // not going to find the corresponding declaration object yet. Default to
219 if (!this.rule.domRule.declarations[selfIndex]) {
223 return this.rule.domRule.declarations[selfIndex].isValid;
227 * Validate the name of this property.
229 * @return {Boolean} true if the property name is valid, false otherwise.
232 const selfIndex = this.rule.textProps.indexOf(this);
234 // When adding a new property in the rule-view, the TextProperty object is
235 // created right away before the rule gets updated on the server, so we're
236 // not going to find the corresponding declaration object yet. Default to
238 if (!this.rule.domRule.declarations[selfIndex]) {
242 // Starting with FF61, StyleRuleActor provides an accessor to signal if the property
243 // name is valid. If we don't have this, assume the name is valid. In use, rely on
244 // isValid() as a guard against false positives.
245 return (this.rule.domRule.declarations[selfIndex].isNameValid !== undefined)
246 ? this.rule.domRule.declarations[selfIndex].isNameValid
251 module.exports = TextProperty;