Source code

Revision control

Copy as Markdown

Other Tools

/* 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";
/**
* 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.
*
* @param {InspectorPanel} inspector
* The inspector panel.
* @param {Document} doc
* A DOM document that can be used to test style rules.
* @param {Array} 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({ inspector, doc, elementRules }) {
this._doc = doc;
this._inspector = inspector;
this._rules = elementRules;
this._modifications = new Map();
}
EditingSession.prototype = {
/**
* Gets the value of a single property from the CSS rule.
*
* @param {StyleRuleFront} rule
* The CSS rule.
* @param {String} property
* The name of the property.
* @return {String} the value.
*/
getPropertyFromRule(rule, property) {
// Use the parsed declarations in the StyleRuleFront object if available.
const index = this.getPropertyIndex(property, rule);
if (index !== -1) {
return rule.declarations[index].value;
}
// Fallback to parsing the cssText locally otherwise.
const dummyStyle = this._element.style;
dummyStyle.cssText = rule.cssText;
return dummyStyle.getPropertyValue(property);
},
/**
* Returns the current value for a property as a string or the empty string if
* no style rules affect the property.
*
* @param {String} property
* The name of the property as a string
*/
getProperty(property) {
// Create a hidden element for getPropertyFromRule to use
const div = this._doc.createElement("div");
div.setAttribute("style", "display: none");
this._doc.getElementById("inspector-main-content").appendChild(div);
this._element = this._doc.createElement("p");
div.appendChild(this._element);
// As the rules are in order of priority we can just iterate until we find
// the first that defines a value for the property and return that.
for (const rule of this._rules) {
const value = this.getPropertyFromRule(rule, property);
if (value !== "") {
div.remove();
return value;
}
}
div.remove();
return "";
},
/**
* Get the index of a given css property name in a CSS rule.
* Or -1, if there are no properties in the rule yet.
*
* @param {String} name
* The property name.
* @param {StyleRuleFront} rule
* Optional, defaults to the element style rule.
* @return {Number} The property index in the rule.
*/
getPropertyIndex(name, rule = this._rules[0]) {
if (!rule.declarations.length) {
return -1;
}
return rule.declarations.findIndex(p => p.name === name);
},
/**
* Sets a number of properties on the node.
*
* @param {Array} properties
* An array of properties, each is an object with name and
* value properties. If the value is "" then the property
* is removed.
* @return {Promise} Resolves when the modifications are complete.
*/
async setProperties(properties) {
for (const property of properties) {
// Get a RuleModificationList or RuleRewriter helper object from the
// 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.
const modifications = this._rules[0].startModifyingProperties(
this._inspector.cssProperties
);
// Remember the property so it can be reverted.
if (!this._modifications.has(property.name)) {
this._modifications.set(
property.name,
this.getPropertyFromRule(this._rules[0], property.name)
);
}
// Find the index of the property to be changed, or get the next index to
// insert the new property at.
let index = this.getPropertyIndex(property.name);
if (index === -1) {
index = this._rules[0].declarations.length;
}
if (property.value == "") {
modifications.removeProperty(index, property.name);
} else {
modifications.setProperty(index, property.name, property.value, "");
}
await modifications.apply();
}
},
/**
* Reverts all of the property changes made by this instance.
*
* @return {Promise} Resolves when all properties have been reverted.
*/
async revert() {
// Revert each property that we modified previously, one by one. See
// setProperties for information about why.
for (const [property, value] of this._modifications) {
const modifications = this._rules[0].startModifyingProperties(
this._inspector.cssProperties
);
// Find the index of the property to be reverted.
let index = this.getPropertyIndex(property);
if (value != "") {
// If the property doesn't exist anymore, insert at the beginning of the
// rule.
if (index === -1) {
index = 0;
}
modifications.setProperty(index, property, value, "");
} else {
// If the property doesn't exist anymore, no need to remove it. It had
// not been added after all.
if (index === -1) {
continue;
}
modifications.removeProperty(index, property);
}
await modifications.apply();
}
},
destroy() {
this._modifications.clear();
this._cssProperties = null;
this._doc = null;
this._inspector = null;
this._modifications = null;
this._rules = null;
},
};
module.exports = EditingSession;