diff options
Diffstat (limited to 'devtools/client/inspector/rules/models/text-property.js')
-rw-r--r-- | devtools/client/inspector/rules/models/text-property.js | 215 |
1 files changed, 215 insertions, 0 deletions
diff --git a/devtools/client/inspector/rules/models/text-property.js b/devtools/client/inspector/rules/models/text-property.js new file mode 100644 index 000000000..3bbe6e91d --- /dev/null +++ b/devtools/client/inspector/rules/models/text-property.js @@ -0,0 +1,215 @@ +/* -*- 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; + } +}; + +exports.TextProperty = TextProperty; |