mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-20 22:27:31 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
412
devtools/client/inspector/rules/models/element-style.js
Normal file
412
devtools/client/inspector/rules/models/element-style.js
Normal file
|
|
@ -0,0 +1,412 @@
|
|||
/* -*- 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 promise = require("promise");
|
||||
const {Rule} = require("devtools/client/inspector/rules/models/rule");
|
||||
const {promiseWarn} = require("devtools/client/inspector/shared/utils");
|
||||
const {ELEMENT_STYLE} = require("devtools/shared/specs/styles");
|
||||
const {getCssProperties} = require("devtools/shared/fronts/css-properties");
|
||||
|
||||
/**
|
||||
* ElementStyle is responsible for the following:
|
||||
* Keeps track of which properties are overridden.
|
||||
* Maintains a list of Rule objects for a given element.
|
||||
*
|
||||
* @param {Element} element
|
||||
* The element whose style we are viewing.
|
||||
* @param {CssRuleView} ruleView
|
||||
* The instance of the rule-view panel.
|
||||
* @param {Object} store
|
||||
* The ElementStyle can use this object to store metadata
|
||||
* that might outlast the rule view, particularly the current
|
||||
* set of disabled properties.
|
||||
* @param {PageStyleFront} pageStyle
|
||||
* Front for the page style actor that will be providing
|
||||
* the style information.
|
||||
* @param {Boolean} showUserAgentStyles
|
||||
* Should user agent styles be inspected?
|
||||
*/
|
||||
function ElementStyle(element, ruleView, store, pageStyle,
|
||||
showUserAgentStyles) {
|
||||
this.element = element;
|
||||
this.ruleView = ruleView;
|
||||
this.store = store || {};
|
||||
this.pageStyle = pageStyle;
|
||||
this.showUserAgentStyles = showUserAgentStyles;
|
||||
this.rules = [];
|
||||
this.cssProperties = getCssProperties(this.ruleView.inspector.toolbox);
|
||||
|
||||
// We don't want to overwrite this.store.userProperties so we only create it
|
||||
// if it doesn't already exist.
|
||||
if (!("userProperties" in this.store)) {
|
||||
this.store.userProperties = new UserProperties();
|
||||
}
|
||||
|
||||
if (!("disabled" in this.store)) {
|
||||
this.store.disabled = new WeakMap();
|
||||
}
|
||||
}
|
||||
|
||||
ElementStyle.prototype = {
|
||||
// The element we're looking at.
|
||||
element: null,
|
||||
|
||||
destroy: function () {
|
||||
if (this.destroyed) {
|
||||
return;
|
||||
}
|
||||
this.destroyed = true;
|
||||
|
||||
for (let rule of this.rules) {
|
||||
if (rule.editor) {
|
||||
rule.editor.destroy();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Called by the Rule object when it has been changed through the
|
||||
* setProperty* methods.
|
||||
*/
|
||||
_changed: function () {
|
||||
if (this.onChanged) {
|
||||
this.onChanged();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Refresh the list of rules to be displayed for the active element.
|
||||
* Upon completion, this.rules[] will hold a list of Rule objects.
|
||||
*
|
||||
* Returns a promise that will be resolved when the elementStyle is
|
||||
* ready.
|
||||
*/
|
||||
populate: function () {
|
||||
let populated = this.pageStyle.getApplied(this.element, {
|
||||
inherited: true,
|
||||
matchedSelectors: true,
|
||||
filter: this.showUserAgentStyles ? "ua" : undefined,
|
||||
}).then(entries => {
|
||||
if (this.destroyed) {
|
||||
return promise.resolve(undefined);
|
||||
}
|
||||
|
||||
if (this.populated !== populated) {
|
||||
// Don't care anymore.
|
||||
return promise.resolve(undefined);
|
||||
}
|
||||
|
||||
// Store the current list of rules (if any) during the population
|
||||
// process. They will be reused if possible.
|
||||
let existingRules = this.rules;
|
||||
|
||||
this.rules = [];
|
||||
|
||||
for (let entry of entries) {
|
||||
this._maybeAddRule(entry, existingRules);
|
||||
}
|
||||
|
||||
// Mark overridden computed styles.
|
||||
this.markOverriddenAll();
|
||||
|
||||
this._sortRulesForPseudoElement();
|
||||
|
||||
// We're done with the previous list of rules.
|
||||
for (let r of existingRules) {
|
||||
if (r && r.editor) {
|
||||
r.editor.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}).then(null, e => {
|
||||
// populate is often called after a setTimeout,
|
||||
// the connection may already be closed.
|
||||
if (this.destroyed) {
|
||||
return promise.resolve(undefined);
|
||||
}
|
||||
return promiseWarn(e);
|
||||
});
|
||||
this.populated = populated;
|
||||
return this.populated;
|
||||
},
|
||||
|
||||
/**
|
||||
* Put pseudo elements in front of others.
|
||||
*/
|
||||
_sortRulesForPseudoElement: function () {
|
||||
this.rules = this.rules.sort((a, b) => {
|
||||
return (a.pseudoElement || "z") > (b.pseudoElement || "z");
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Add a rule if it's one we care about. Filters out duplicates and
|
||||
* inherited styles with no inherited properties.
|
||||
*
|
||||
* @param {Object} options
|
||||
* Options for creating the Rule, see the Rule constructor.
|
||||
* @param {Array} existingRules
|
||||
* Rules to reuse if possible. If a rule is reused, then it
|
||||
* it will be deleted from this array.
|
||||
* @return {Boolean} true if we added the rule.
|
||||
*/
|
||||
_maybeAddRule: function (options, existingRules) {
|
||||
// If we've already included this domRule (for example, when a
|
||||
// common selector is inherited), ignore it.
|
||||
if (options.rule &&
|
||||
this.rules.some(rule => rule.domRule === options.rule)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (options.system) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let rule = null;
|
||||
|
||||
// If we're refreshing and the rule previously existed, reuse the
|
||||
// Rule object.
|
||||
if (existingRules) {
|
||||
let ruleIndex = existingRules.findIndex((r) => r.matches(options));
|
||||
if (ruleIndex >= 0) {
|
||||
rule = existingRules[ruleIndex];
|
||||
rule.refresh(options);
|
||||
existingRules.splice(ruleIndex, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// If this is a new rule, create its Rule object.
|
||||
if (!rule) {
|
||||
rule = new Rule(this, options);
|
||||
}
|
||||
|
||||
// Ignore inherited rules with no visible properties.
|
||||
if (options.inherited && !rule.hasAnyVisibleProperties()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.rules.push(rule);
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Calls markOverridden with all supported pseudo elements
|
||||
*/
|
||||
markOverriddenAll: function () {
|
||||
this.markOverridden();
|
||||
for (let pseudo of this.cssProperties.pseudoElements) {
|
||||
this.markOverridden(pseudo);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Mark the properties listed in this.rules for a given pseudo element
|
||||
* with an overridden flag if an earlier property overrides it.
|
||||
*
|
||||
* @param {String} pseudo
|
||||
* Which pseudo element to flag as overridden.
|
||||
* Empty string or undefined will default to no pseudo element.
|
||||
*/
|
||||
markOverridden: function (pseudo = "") {
|
||||
// Gather all the text properties applied by these rules, ordered
|
||||
// from more- to less-specific. Text properties from keyframes rule are
|
||||
// excluded from being marked as overridden since a number of criteria such
|
||||
// as time, and animation overlay are required to be check in order to
|
||||
// determine if the property is overridden.
|
||||
let textProps = [];
|
||||
for (let rule of this.rules) {
|
||||
if ((rule.matchedSelectors.length > 0 ||
|
||||
rule.domRule.type === ELEMENT_STYLE) &&
|
||||
rule.pseudoElement === pseudo && !rule.keyframes) {
|
||||
for (let textProp of rule.textProps.slice(0).reverse()) {
|
||||
if (textProp.enabled) {
|
||||
textProps.push(textProp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Gather all the computed properties applied by those text
|
||||
// properties.
|
||||
let computedProps = [];
|
||||
for (let textProp of textProps) {
|
||||
computedProps = computedProps.concat(textProp.computed);
|
||||
}
|
||||
|
||||
// Walk over the computed properties. As we see a property name
|
||||
// for the first time, mark that property's name as taken by this
|
||||
// property.
|
||||
//
|
||||
// If we come across a property whose name is already taken, check
|
||||
// its priority against the property that was found first:
|
||||
//
|
||||
// If the new property is a higher priority, mark the old
|
||||
// property overridden and mark the property name as taken by
|
||||
// the new property.
|
||||
//
|
||||
// If the new property is a lower or equal priority, mark it as
|
||||
// overridden.
|
||||
//
|
||||
// _overriddenDirty will be set on each prop, indicating whether its
|
||||
// dirty status changed during this pass.
|
||||
let taken = {};
|
||||
for (let computedProp of computedProps) {
|
||||
let earlier = taken[computedProp.name];
|
||||
|
||||
// Prevent -webkit-gradient from being selected after unchecking
|
||||
// linear-gradient in this case:
|
||||
// -moz-linear-gradient: ...;
|
||||
// -webkit-linear-gradient: ...;
|
||||
// linear-gradient: ...;
|
||||
if (!computedProp.textProp.isValid()) {
|
||||
computedProp.overridden = true;
|
||||
continue;
|
||||
}
|
||||
let overridden;
|
||||
if (earlier &&
|
||||
computedProp.priority === "important" &&
|
||||
earlier.priority !== "important" &&
|
||||
(earlier.textProp.rule.inherited ||
|
||||
!computedProp.textProp.rule.inherited)) {
|
||||
// New property is higher priority. Mark the earlier property
|
||||
// overridden (which will reverse its dirty state).
|
||||
earlier._overriddenDirty = !earlier._overriddenDirty;
|
||||
earlier.overridden = true;
|
||||
overridden = false;
|
||||
} else {
|
||||
overridden = !!earlier;
|
||||
}
|
||||
|
||||
computedProp._overriddenDirty =
|
||||
(!!computedProp.overridden !== overridden);
|
||||
computedProp.overridden = overridden;
|
||||
if (!computedProp.overridden && computedProp.textProp.enabled) {
|
||||
taken[computedProp.name] = computedProp;
|
||||
}
|
||||
}
|
||||
|
||||
// For each TextProperty, mark it overridden if all of its
|
||||
// computed properties are marked overridden. Update the text
|
||||
// property's associated editor, if any. This will clear the
|
||||
// _overriddenDirty state on all computed properties.
|
||||
for (let textProp of textProps) {
|
||||
// _updatePropertyOverridden will return true if the
|
||||
// overridden state has changed for the text property.
|
||||
if (this._updatePropertyOverridden(textProp)) {
|
||||
textProp.updateEditor();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Mark a given TextProperty as overridden or not depending on the
|
||||
* state of its computed properties. Clears the _overriddenDirty state
|
||||
* on all computed properties.
|
||||
*
|
||||
* @param {TextProperty} prop
|
||||
* The text property to update.
|
||||
* @return {Boolean} true if the TextProperty's overridden state (or any of
|
||||
* its computed properties overridden state) changed.
|
||||
*/
|
||||
_updatePropertyOverridden: function (prop) {
|
||||
let overridden = true;
|
||||
let dirty = false;
|
||||
for (let computedProp of prop.computed) {
|
||||
if (!computedProp.overridden) {
|
||||
overridden = false;
|
||||
}
|
||||
dirty = computedProp._overriddenDirty || dirty;
|
||||
delete computedProp._overriddenDirty;
|
||||
}
|
||||
|
||||
dirty = (!!prop.overridden !== overridden) || dirty;
|
||||
prop.overridden = overridden;
|
||||
return dirty;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Store of CSSStyleDeclarations mapped to properties that have been changed by
|
||||
* the user.
|
||||
*/
|
||||
function UserProperties() {
|
||||
this.map = new Map();
|
||||
}
|
||||
|
||||
UserProperties.prototype = {
|
||||
/**
|
||||
* Get a named property for a given CSSStyleDeclaration.
|
||||
*
|
||||
* @param {CSSStyleDeclaration} style
|
||||
* The CSSStyleDeclaration against which the property is mapped.
|
||||
* @param {String} name
|
||||
* The name of the property to get.
|
||||
* @param {String} value
|
||||
* Default value.
|
||||
* @return {String}
|
||||
* The property value if it has previously been set by the user, null
|
||||
* otherwise.
|
||||
*/
|
||||
getProperty: function (style, name, value) {
|
||||
let key = this.getKey(style);
|
||||
let entry = this.map.get(key, null);
|
||||
|
||||
if (entry && name in entry) {
|
||||
return entry[name];
|
||||
}
|
||||
return value;
|
||||
},
|
||||
|
||||
/**
|
||||
* Set a named property for a given CSSStyleDeclaration.
|
||||
*
|
||||
* @param {CSSStyleDeclaration} style
|
||||
* The CSSStyleDeclaration against which the property is to be mapped.
|
||||
* @param {String} bame
|
||||
* The name of the property to set.
|
||||
* @param {String} userValue
|
||||
* The value of the property to set.
|
||||
*/
|
||||
setProperty: function (style, bame, userValue) {
|
||||
let key = this.getKey(style, bame);
|
||||
let entry = this.map.get(key, null);
|
||||
|
||||
if (entry) {
|
||||
entry[bame] = userValue;
|
||||
} else {
|
||||
let props = {};
|
||||
props[bame] = userValue;
|
||||
this.map.set(key, props);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Check whether a named property for a given CSSStyleDeclaration is stored.
|
||||
*
|
||||
* @param {CSSStyleDeclaration} style
|
||||
* The CSSStyleDeclaration against which the property would be mapped.
|
||||
* @param {String} name
|
||||
* The name of the property to check.
|
||||
*/
|
||||
contains: function (style, name) {
|
||||
let key = this.getKey(style, name);
|
||||
let entry = this.map.get(key, null);
|
||||
return !!entry && name in entry;
|
||||
},
|
||||
|
||||
getKey: function (style, name) {
|
||||
return style.actorID + ":" + name;
|
||||
},
|
||||
|
||||
clear: function () {
|
||||
this.map.clear();
|
||||
}
|
||||
};
|
||||
|
||||
exports.ElementStyle = ElementStyle;
|
||||
Loading…
Add table
Add a link
Reference in a new issue