зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1509620 - Computed style inspector CSS cascade calculation is wrong r=ladybenko
### Try https://treeherder.mozilla.org/#/jobs?repo=try&revision=f1bad5e5282812225da95c0ea9e2ef173640b5da ### Summary !!Comparing numerous complex websites such as github, facebook, cnn etc. the cascade now matchers that of Chrome so we are in a much better place.!! According to https://www.w3.org/TR/css-cascade-3/#cascading (which platform follows now) and https://www.w3.org/TR/css-cascade-4/#cascading we should now be doing this (descending order): - Transition declarations - User-Agent & !important - User & !important - Author & !important - CSS Animations, @keyframes - Author, normal weight - User, normal weight - User-Agent, normal weight - specificity - Sheet Index - Rule Line - Rule Column We are only dealing with CSS selectors here so we can safely drop Transition declarations and CSS Animations because their presence here is irrelevant when it comes to the CSS cascade information we display in the computed view. This leaves us with: - User-Agent & !important - User & !important - Author & !important - Author, normal weight - User, normal weight - User-Agent, normal weight - specificity - Sheet Index - Rule Line - Rule Column ### Changes - References to content stylesheets have been changed to author stylesheet to closely match the technical terms author, user and agent stylesheets. - Simplified and modernized a bunch of for loops to make the code easier to understand. - Previous to these changes all matching parent rules were classed as equal e.g. color on the body tag was equal to color on a node's immediate container. We now use the `distance` variable to tell how close a rule is to the current node. This is the highest qualifier in our cascade calculation. - The `_agentSheet`, `_authorSheet` and `_userSheet` properties are now used to obtain a sheets origin. - `elementStyle` was renamed to `inlineStyle` in order to correctly identify the rule's origin. - We used to sort the matchedSelectors to move rules with `STATUS.MATCHED` above `STATUS.PARENT_MATCH` but this is unnecessary now that we have the `distance` property so we no longer do this. - The `compareTo()` method has been updated to match https://www.w3.org/TR/css-cascade-3/#cascading (which platform follows now) and https://www.w3.org/TR/css-cascade-4/#cascading. It has also been simplified and made far less prone to error. Differential Revision: https://phabricator.services.mozilla.com/D23711 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
59aaa3ef97
Коммит
639bed7bb2
|
@ -15,7 +15,7 @@ const {
|
||||||
} = require("devtools/shared/layout/utils");
|
} = require("devtools/shared/layout/utils");
|
||||||
const defer = require("devtools/shared/defer");
|
const defer = require("devtools/shared/defer");
|
||||||
const {
|
const {
|
||||||
isContentStylesheet,
|
isAuthorStylesheet,
|
||||||
getCSSStyleRules,
|
getCSSStyleRules,
|
||||||
} = require("devtools/shared/inspector/css-logic");
|
} = require("devtools/shared/inspector/css-logic");
|
||||||
const InspectorUtils = require("InspectorUtils");
|
const InspectorUtils = require("InspectorUtils");
|
||||||
|
@ -773,7 +773,7 @@ var TestActor = exports.TestActor = protocol.ActorClassWithSpec(testSpec, {
|
||||||
const sheet = domRules[i].parentStyleSheet;
|
const sheet = domRules[i].parentStyleSheet;
|
||||||
sheets.push({
|
sheets.push({
|
||||||
href: sheet.href,
|
href: sheet.href,
|
||||||
isContentSheet: isContentStylesheet(sheet),
|
isContentSheet: isAuthorStylesheet(sheet),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,13 +35,20 @@ const {
|
||||||
getBindingElementAndPseudo,
|
getBindingElementAndPseudo,
|
||||||
getCSSStyleRules,
|
getCSSStyleRules,
|
||||||
l10n,
|
l10n,
|
||||||
isContentStylesheet,
|
isAgentStylesheet,
|
||||||
|
isAuthorStylesheet,
|
||||||
|
isUserStylesheet,
|
||||||
shortSource,
|
shortSource,
|
||||||
FILTER,
|
FILTER,
|
||||||
STATUS,
|
STATUS,
|
||||||
} = require("devtools/shared/inspector/css-logic");
|
} = require("devtools/shared/inspector/css-logic");
|
||||||
const InspectorUtils = require("InspectorUtils");
|
const InspectorUtils = require("InspectorUtils");
|
||||||
|
|
||||||
|
const COMPAREMODE = {
|
||||||
|
"BOOLEAN": "bool",
|
||||||
|
"INTEGER": "int",
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {function} isInherited A function that determines if the CSS property
|
* @param {function} isInherited A function that determines if the CSS property
|
||||||
* is inherited.
|
* is inherited.
|
||||||
|
@ -170,8 +177,7 @@ CssLogic.prototype = {
|
||||||
|
|
||||||
// Update the CssSheet objects.
|
// Update the CssSheet objects.
|
||||||
this.forEachSheet(function(sheet) {
|
this.forEachSheet(function(sheet) {
|
||||||
sheet._sheetAllowed = -1;
|
if (sheet.authorSheet && sheet.sheetAllowed) {
|
||||||
if (sheet.contentSheet && sheet.sheetAllowed) {
|
|
||||||
ruleCount += sheet.ruleCount;
|
ruleCount += sheet.ruleCount;
|
||||||
}
|
}
|
||||||
}, this);
|
}, this);
|
||||||
|
@ -281,7 +287,7 @@ CssLogic.prototype = {
|
||||||
|
|
||||||
const sheets = [];
|
const sheets = [];
|
||||||
this.forEachSheet(function(sheet) {
|
this.forEachSheet(function(sheet) {
|
||||||
if (sheet.contentSheet) {
|
if (sheet.authorSheet) {
|
||||||
sheets.push(sheet);
|
sheets.push(sheet);
|
||||||
}
|
}
|
||||||
}, this);
|
}, this);
|
||||||
|
@ -324,10 +330,7 @@ CssLogic.prototype = {
|
||||||
let sheetFound = false;
|
let sheetFound = false;
|
||||||
|
|
||||||
if (cacheId in this._sheets) {
|
if (cacheId in this._sheets) {
|
||||||
for (let i = 0, numSheets = this._sheets[cacheId].length;
|
for (sheet of this._sheets[cacheId]) {
|
||||||
i < numSheets;
|
|
||||||
i++) {
|
|
||||||
sheet = this._sheets[cacheId][i];
|
|
||||||
if (sheet.domSheet === domSheet) {
|
if (sheet.domSheet === domSheet) {
|
||||||
if (index != -1) {
|
if (index != -1) {
|
||||||
sheet.index = index;
|
sheet.index = index;
|
||||||
|
@ -344,7 +347,7 @@ CssLogic.prototype = {
|
||||||
}
|
}
|
||||||
|
|
||||||
sheet = new CssSheet(this, domSheet, index);
|
sheet = new CssSheet(this, domSheet, index);
|
||||||
if (sheet.sheetAllowed && sheet.contentSheet) {
|
if (sheet.sheetAllowed && sheet.authorSheet) {
|
||||||
this._ruleCount += sheet.ruleCount;
|
this._ruleCount += sheet.ruleCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -437,19 +440,18 @@ CssLogic.prototype = {
|
||||||
this._matchedSelectors = [];
|
this._matchedSelectors = [];
|
||||||
this._passId++;
|
this._passId++;
|
||||||
|
|
||||||
for (let i = 0; i < this._matchedRules.length; i++) {
|
for (const matchedRule of this._matchedRules) {
|
||||||
const rule = this._matchedRules[i][0];
|
const [rule, status, distance] = matchedRule;
|
||||||
const status = this._matchedRules[i][1];
|
|
||||||
|
|
||||||
rule.selectors.forEach(function(selector) {
|
rule.selectors.forEach(function(selector) {
|
||||||
if (selector._matchId !== this._matchId &&
|
if (selector._matchId !== this._matchId &&
|
||||||
(selector.elementStyle ||
|
(selector.inlineStyle ||
|
||||||
this.selectorMatchesElement(rule.domRule,
|
this.selectorMatchesElement(rule.domRule,
|
||||||
selector.selectorIndex))) {
|
selector.selectorIndex))) {
|
||||||
selector._matchId = this._matchId;
|
selector._matchId = this._matchId;
|
||||||
this._matchedSelectors.push([ selector, status ]);
|
this._matchedSelectors.push([ selector, status, distance ]);
|
||||||
if (callback) {
|
if (callback) {
|
||||||
callback.call(scope, selector, status);
|
callback.call(scope, selector, status, distance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, this);
|
}, this);
|
||||||
|
@ -532,6 +534,13 @@ CssLogic.prototype = {
|
||||||
const filter = this.sourceFilter;
|
const filter = this.sourceFilter;
|
||||||
let sheetIndex = 0;
|
let sheetIndex = 0;
|
||||||
|
|
||||||
|
// distance is used to tell us how close an ancestor is to an element e.g.
|
||||||
|
// 0: The rule is directly applied to the current element.
|
||||||
|
// -1: The rule is inherited from the current element's first parent.
|
||||||
|
// -2: The rule is inherited from the current element's second parent.
|
||||||
|
// etc.
|
||||||
|
let distance = 0;
|
||||||
|
|
||||||
this._matchId++;
|
this._matchId++;
|
||||||
this._passId++;
|
this._passId++;
|
||||||
this._matchedRules = [];
|
this._matchedRules = [];
|
||||||
|
@ -552,9 +561,7 @@ CssLogic.prototype = {
|
||||||
}
|
}
|
||||||
|
|
||||||
// getCSSStyleRules can return null with a shadow DOM element.
|
// getCSSStyleRules can return null with a shadow DOM element.
|
||||||
const numDomRules = domRules ? domRules.length : 0;
|
for (const domRule of domRules || []) {
|
||||||
for (let i = 0; i < numDomRules; i++) {
|
|
||||||
const domRule = domRules[i];
|
|
||||||
if (domRule.type !== CSSRule.STYLE_RULE) {
|
if (domRule.type !== CSSRule.STYLE_RULE) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -565,7 +572,7 @@ CssLogic.prototype = {
|
||||||
sheet._passId = this._passId;
|
sheet._passId = this._passId;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filter === FILTER.USER && !sheet.contentSheet) {
|
if (filter === FILTER.USER && !sheet.authorSheet) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -576,7 +583,7 @@ CssLogic.prototype = {
|
||||||
|
|
||||||
rule._matchId = this._matchId;
|
rule._matchId = this._matchId;
|
||||||
rule._passId = this._passId;
|
rule._passId = this._passId;
|
||||||
this._matchedRules.push([rule, status]);
|
this._matchedRules.push([rule, status, distance]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add element.style information.
|
// Add element.style information.
|
||||||
|
@ -584,8 +591,10 @@ CssLogic.prototype = {
|
||||||
const rule = new CssRule(null, { style: element.style }, element);
|
const rule = new CssRule(null, { style: element.style }, element);
|
||||||
rule._matchId = this._matchId;
|
rule._matchId = this._matchId;
|
||||||
rule._passId = this._passId;
|
rule._passId = this._passId;
|
||||||
this._matchedRules.push([rule, status]);
|
this._matchedRules.push([rule, status, distance]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
distance--;
|
||||||
} while ((element = element.parentNode) &&
|
} while ((element = element.parentNode) &&
|
||||||
element.nodeType === nodeConstants.ELEMENT_NODE);
|
element.nodeType === nodeConstants.ELEMENT_NODE);
|
||||||
},
|
},
|
||||||
|
@ -720,7 +729,7 @@ CssLogic.href = function(sheet) {
|
||||||
function CssSheet(cssLogic, domSheet, index) {
|
function CssSheet(cssLogic, domSheet, index) {
|
||||||
this._cssLogic = cssLogic;
|
this._cssLogic = cssLogic;
|
||||||
this.domSheet = domSheet;
|
this.domSheet = domSheet;
|
||||||
this.index = this.contentSheet ? index : -100 * index;
|
this.index = this.authorSheet ? index : -100 * index;
|
||||||
|
|
||||||
// Cache of the sheets href. Cached by the getter.
|
// Cache of the sheets href. Cached by the getter.
|
||||||
this._href = null;
|
this._href = null;
|
||||||
|
@ -738,23 +747,49 @@ function CssSheet(cssLogic, domSheet, index) {
|
||||||
|
|
||||||
CssSheet.prototype = {
|
CssSheet.prototype = {
|
||||||
_passId: null,
|
_passId: null,
|
||||||
_contentSheet: null,
|
_agentSheet: null,
|
||||||
|
_authorSheet: null,
|
||||||
|
_userSheet: null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tells if the stylesheet is provided by the browser or not.
|
* Check if the stylesheet is an agent stylesheet (provided by the browser).
|
||||||
*
|
*
|
||||||
* @return {boolean} false if this is a browser-provided stylesheet, or true
|
* @return {boolean} true if this is an agent stylesheet, false otherwise.
|
||||||
* otherwise.
|
|
||||||
*/
|
*/
|
||||||
get contentSheet() {
|
get agentSheet() {
|
||||||
if (this._contentSheet === null) {
|
if (this._agentSheet === null) {
|
||||||
this._contentSheet = isContentStylesheet(this.domSheet);
|
this._agentSheet = isAgentStylesheet(this.domSheet);
|
||||||
}
|
}
|
||||||
return this._contentSheet;
|
return this._agentSheet;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tells if the stylesheet is disabled or not.
|
* Check if the stylesheet is an author stylesheet (provided by the content page).
|
||||||
|
*
|
||||||
|
* @return {boolean} true if this is an author stylesheet, false otherwise.
|
||||||
|
*/
|
||||||
|
get authorSheet() {
|
||||||
|
if (this._authorSheet === null) {
|
||||||
|
this._authorSheet = isAuthorStylesheet(this.domSheet);
|
||||||
|
}
|
||||||
|
return this._authorSheet;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the stylesheet is a user stylesheet (provided by userChrome.css or
|
||||||
|
* userContent.css).
|
||||||
|
*
|
||||||
|
* @return {boolean} true if this is a user stylesheet, false otherwise.
|
||||||
|
*/
|
||||||
|
get userSheet() {
|
||||||
|
if (this._userSheet === null) {
|
||||||
|
this._userSheet = isUserStylesheet(this.domSheet);
|
||||||
|
}
|
||||||
|
return this._userSheet;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the stylesheet is disabled or not.
|
||||||
* @return {boolean} true if this stylesheet is disabled, or false otherwise.
|
* @return {boolean} true if this stylesheet is disabled, or false otherwise.
|
||||||
*/
|
*/
|
||||||
get disabled() {
|
get disabled() {
|
||||||
|
@ -803,7 +838,7 @@ CssSheet.prototype = {
|
||||||
this._sheetAllowed = true;
|
this._sheetAllowed = true;
|
||||||
|
|
||||||
const filter = this._cssLogic.sourceFilter;
|
const filter = this._cssLogic.sourceFilter;
|
||||||
if (filter === FILTER.USER && !this.contentSheet) {
|
if (filter === FILTER.USER && !this.authorSheet) {
|
||||||
this._sheetAllowed = false;
|
this._sheetAllowed = false;
|
||||||
}
|
}
|
||||||
if (filter !== FILTER.USER && filter !== FILTER.UA) {
|
if (filter !== FILTER.USER && filter !== FILTER.UA) {
|
||||||
|
@ -861,10 +896,7 @@ CssSheet.prototype = {
|
||||||
let ruleFound = false;
|
let ruleFound = false;
|
||||||
|
|
||||||
if (cacheId in this._rules) {
|
if (cacheId in this._rules) {
|
||||||
for (let i = 0, rulesLen = this._rules[cacheId].length;
|
for (rule of this._rules[cacheId]) {
|
||||||
i < rulesLen;
|
|
||||||
i++) {
|
|
||||||
rule = this._rules[cacheId][i];
|
|
||||||
if (rule.domRule === domRule) {
|
if (rule.domRule === domRule) {
|
||||||
ruleFound = true;
|
ruleFound = true;
|
||||||
break;
|
break;
|
||||||
|
@ -921,13 +953,17 @@ function CssRule(cssSheet, domRule, element) {
|
||||||
this.source += " @media " + this.mediaText;
|
this.source += " @media " + this.mediaText;
|
||||||
}
|
}
|
||||||
this.href = this._cssSheet.href;
|
this.href = this._cssSheet.href;
|
||||||
this.contentRule = this._cssSheet.contentSheet;
|
this.authorRule = this._cssSheet.authorSheet;
|
||||||
|
this.userRule = this._cssSheet.userSheet;
|
||||||
|
this.agentRule = this._cssSheet.agentSheet;
|
||||||
} else if (element) {
|
} else if (element) {
|
||||||
this._selectors = [ new CssSelector(this, "@element.style", 0) ];
|
this._selectors = [ new CssSelector(this, "@element.style", 0) ];
|
||||||
this.line = -1;
|
this.line = -1;
|
||||||
this.source = l10n("rule.sourceElement");
|
this.source = l10n("rule.sourceElement");
|
||||||
this.href = "#";
|
this.href = "#";
|
||||||
this.contentRule = true;
|
this.authorRule = true;
|
||||||
|
this.userRule = false;
|
||||||
|
this.agentRule = false;
|
||||||
this.sourceElement = element;
|
this.sourceElement = element;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1027,7 +1063,7 @@ CssRule.prototype = {
|
||||||
function CssSelector(cssRule, selector, index) {
|
function CssSelector(cssRule, selector, index) {
|
||||||
this.cssRule = cssRule;
|
this.cssRule = cssRule;
|
||||||
this.text = selector;
|
this.text = selector;
|
||||||
this.elementStyle = this.text == "@element.style";
|
this.inlineStyle = this.text == "@element.style";
|
||||||
this._specificity = null;
|
this._specificity = null;
|
||||||
this.selectorIndex = index;
|
this.selectorIndex = index;
|
||||||
}
|
}
|
||||||
|
@ -1069,13 +1105,31 @@ CssSelector.prototype = {
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the selector comes from a browser-provided stylesheet.
|
* Check if the selector comes from an agent stylesheet (provided by the browser).
|
||||||
*
|
*
|
||||||
* @return {boolean} true if the selector comes from a content-provided
|
* @return {boolean} true if this is an agent stylesheet, false otherwise.
|
||||||
* stylesheet, or false otherwise.
|
|
||||||
*/
|
*/
|
||||||
get contentRule() {
|
get agentRule() {
|
||||||
return this.cssRule.contentRule;
|
return this.cssRule.agentRule;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the selector comes from an author stylesheet (provided by the content page).
|
||||||
|
*
|
||||||
|
* @return {boolean} true if this is an author stylesheet, false otherwise.
|
||||||
|
*/
|
||||||
|
get authorRule() {
|
||||||
|
return this.cssRule.authorRule;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the selector comes from a user stylesheet (provided by userChrome.css or
|
||||||
|
* userContent.css).
|
||||||
|
*
|
||||||
|
* @return {boolean} true if this is a user stylesheet, false otherwise.
|
||||||
|
*/
|
||||||
|
get userRule() {
|
||||||
|
return this.cssRule.userRule;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1127,7 +1181,7 @@ CssSelector.prototype = {
|
||||||
* @return {Number} The selector's specificity.
|
* @return {Number} The selector's specificity.
|
||||||
*/
|
*/
|
||||||
get specificity() {
|
get specificity() {
|
||||||
if (this.elementStyle) {
|
if (this.inlineStyle) {
|
||||||
// We can't ask specificity from DOMUtils as element styles don't provide
|
// We can't ask specificity from DOMUtils as element styles don't provide
|
||||||
// CSSStyleRule interface DOMUtils expect. However, specificity of element
|
// CSSStyleRule interface DOMUtils expect. However, specificity of element
|
||||||
// style is constant, 1,0,0,0 or 0x40000000, just return the constant
|
// style is constant, 1,0,0,0 or 0x40000000, just return the constant
|
||||||
|
@ -1135,13 +1189,11 @@ CssSelector.prototype = {
|
||||||
return 0x40000000;
|
return 0x40000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._specificity) {
|
if (typeof this._specificity !== "number") {
|
||||||
return this._specificity;
|
this._specificity = InspectorUtils.getSpecificity(this.cssRule.domRule,
|
||||||
|
this.selectorIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._specificity = InspectorUtils.getSpecificity(this.cssRule.domRule,
|
|
||||||
this.selectorIndex);
|
|
||||||
|
|
||||||
return this._specificity;
|
return this._specificity;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1232,11 +1284,6 @@ CssPropertyInfo.prototype = {
|
||||||
|
|
||||||
// Sort the selectors by how well they match the given element.
|
// Sort the selectors by how well they match the given element.
|
||||||
this._matchedSelectors.sort(function(selectorInfo1, selectorInfo2) {
|
this._matchedSelectors.sort(function(selectorInfo1, selectorInfo2) {
|
||||||
if (selectorInfo1.status > selectorInfo2.status) {
|
|
||||||
return -1;
|
|
||||||
} else if (selectorInfo2.status > selectorInfo1.status) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return selectorInfo1.compareTo(selectorInfo2);
|
return selectorInfo1.compareTo(selectorInfo2);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1254,7 +1301,7 @@ CssPropertyInfo.prototype = {
|
||||||
* @param {CssSelector} selector the matched CssSelector object.
|
* @param {CssSelector} selector the matched CssSelector object.
|
||||||
* @param {STATUS} status the CssSelector match status.
|
* @param {STATUS} status the CssSelector match status.
|
||||||
*/
|
*/
|
||||||
_processMatchedSelector: function(selector, status) {
|
_processMatchedSelector: function(selector, status, distance) {
|
||||||
const cssRule = selector.cssRule;
|
const cssRule = selector.cssRule;
|
||||||
const value = cssRule.getPropertyValue(this.property);
|
const value = cssRule.getPropertyValue(this.property);
|
||||||
if (value &&
|
if (value &&
|
||||||
|
@ -1262,7 +1309,7 @@ CssPropertyInfo.prototype = {
|
||||||
(status == STATUS.PARENT_MATCH &&
|
(status == STATUS.PARENT_MATCH &&
|
||||||
this._isInherited(this.property)))) {
|
this._isInherited(this.property)))) {
|
||||||
const selectorInfo = new CssSelectorInfo(selector, this.property, value,
|
const selectorInfo = new CssSelectorInfo(selector, this.property, value,
|
||||||
status);
|
status, distance);
|
||||||
this._matchedSelectors.push(selectorInfo);
|
this._matchedSelectors.push(selectorInfo);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1311,10 +1358,11 @@ CssPropertyInfo.prototype = {
|
||||||
* @param {STATUS} status The selector match status.
|
* @param {STATUS} status The selector match status.
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
function CssSelectorInfo(selector, property, value, status) {
|
function CssSelectorInfo(selector, property, value, status, distance) {
|
||||||
this.selector = selector;
|
this.selector = selector;
|
||||||
this.property = property;
|
this.property = property;
|
||||||
this.status = status;
|
this.status = status;
|
||||||
|
this.distance = distance;
|
||||||
this.value = value;
|
this.value = value;
|
||||||
const priority = this.selector.cssRule.getPropertyPriority(this.property);
|
const priority = this.selector.cssRule.getPropertyPriority(this.property);
|
||||||
this.important = (priority === "important");
|
this.important = (priority === "important");
|
||||||
|
@ -1358,8 +1406,8 @@ CssSelectorInfo.prototype = {
|
||||||
* @return {boolean} true if the CssSelector comes from element.style, or
|
* @return {boolean} true if the CssSelector comes from element.style, or
|
||||||
* false otherwise.
|
* false otherwise.
|
||||||
*/
|
*/
|
||||||
get elementStyle() {
|
get inlineStyle() {
|
||||||
return this.selector.elementStyle;
|
return this.selector.inlineStyle;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1418,74 +1466,152 @@ CssSelectorInfo.prototype = {
|
||||||
* @return {boolean} true if the selector comes from a browser-provided
|
* @return {boolean} true if the selector comes from a browser-provided
|
||||||
* stylesheet, or false otherwise.
|
* stylesheet, or false otherwise.
|
||||||
*/
|
*/
|
||||||
get contentRule() {
|
get agentRule() {
|
||||||
return this.selector.contentRule;
|
return this.selector.agentRule;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the selector comes from a webpage-provided stylesheet.
|
||||||
|
*
|
||||||
|
* @return {boolean} true if the selector comes from a webpage-provided
|
||||||
|
* stylesheet, or false otherwise.
|
||||||
|
*/
|
||||||
|
get authorRule() {
|
||||||
|
return this.selector.authorRule;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the selector comes from a user stylesheet (userChrome.css or
|
||||||
|
* userContent.css).
|
||||||
|
*
|
||||||
|
* @return {boolean} true if the selector comes from a webpage-provided
|
||||||
|
* stylesheet, or false otherwise.
|
||||||
|
*/
|
||||||
|
get userRule() {
|
||||||
|
return this.selector.userRule;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compare the current CssSelectorInfo instance to another instance, based on
|
* Compare the current CssSelectorInfo instance to another instance, based on
|
||||||
* specificity information.
|
* the CSS cascade (see https://www.w3.org/TR/css-cascade-4/#cascading):
|
||||||
*
|
*
|
||||||
* @param {CssSelectorInfo} that The instance to compare ourselves against.
|
* The cascade sorts declarations according to the following criteria, in
|
||||||
* @return number -1, 0, 1 depending on how that compares with this.
|
* descending order of priority:
|
||||||
|
*
|
||||||
|
* - Rules targetting a node directly must always win over rules targetting an
|
||||||
|
* ancestor.
|
||||||
|
*
|
||||||
|
* - Origin and Importance
|
||||||
|
* The origin of a declaration is based on where it comes from and its
|
||||||
|
* importance is whether or not it is declared !important (see below). For
|
||||||
|
* our purposes here we can safely ignore Transition declarations and
|
||||||
|
* Animation declarations.
|
||||||
|
* The precedence of the various origins is, in descending order:
|
||||||
|
* - Transition declarations (ignored)
|
||||||
|
* - Important user agent declarations (User-Agent & !important)
|
||||||
|
* - Important user declarations (User & !important)
|
||||||
|
* - Important author declarations (Author & !important)
|
||||||
|
* - Animation declarations (ignored)
|
||||||
|
* - Normal author declarations (Author, normal weight)
|
||||||
|
* - Normal user declarations (User, normal weight)
|
||||||
|
* - Normal user agent declarations (User-Agent, normal weight)
|
||||||
|
*
|
||||||
|
* - Specificity (see https://www.w3.org/TR/selectors/#specificity)
|
||||||
|
* - A selector’s specificity is calculated for a given element as follows:
|
||||||
|
* - count the number of ID selectors in the selector (= A)
|
||||||
|
* - count the number of class selectors, attributes selectors, and
|
||||||
|
* pseudo-classes in the selector (= B)
|
||||||
|
* - count the number of type selectors and pseudo-elements in the
|
||||||
|
* selector (= C)
|
||||||
|
* - ignore the universal selector
|
||||||
|
* - So "UL OL LI.red" has a specificity of a=0 b=1 c=3.
|
||||||
|
*
|
||||||
|
* - Order of Appearance
|
||||||
|
* - The last declaration in document order wins. For this purpose:
|
||||||
|
* - Declarations from imported style sheets are ordered as if their style
|
||||||
|
* sheets were substituted in place of the @import rule.
|
||||||
|
* - Declarations from style sheets independently linked by the
|
||||||
|
* originating document are treated as if they were concatenated in
|
||||||
|
* linking order, as determined by the host document language.
|
||||||
|
* - Declarations from style attributes are ordered according to the
|
||||||
|
* document order of the element the style attribute appears on, and are
|
||||||
|
* all placed after any style sheets.
|
||||||
|
* - We use three methods to calculate this:
|
||||||
|
* - Sheet index
|
||||||
|
* - Rule line
|
||||||
|
* - Rule column
|
||||||
|
*
|
||||||
|
* @param {CssSelectorInfo} that
|
||||||
|
* The instance to compare ourselves against.
|
||||||
|
* @return {Number}
|
||||||
|
* -1, 0, 1 depending on how that compares with this.
|
||||||
*/
|
*/
|
||||||
compareTo: function(that) {
|
compareTo: function(that) {
|
||||||
if (!this.contentRule && that.contentRule) {
|
let current = null;
|
||||||
return 1;
|
|
||||||
}
|
// Rules targetting the node must always win over rules targetting a node's
|
||||||
if (this.contentRule && !that.contentRule) {
|
// ancestor.
|
||||||
return -1;
|
current = this.compare(that, "distance", COMPAREMODE.INTEGER);
|
||||||
|
if (current) {
|
||||||
|
return current;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.elementStyle && !that.elementStyle) {
|
if (this.important) {
|
||||||
if (!this.important && that.important) {
|
// User-Agent & !important
|
||||||
return 1;
|
// User & !important
|
||||||
|
// Author & !important
|
||||||
|
for (const propName of ["agentRule", "userRule", "authorRule"]) {
|
||||||
|
current = this.compare(that, propName, COMPAREMODE.BOOLEAN);
|
||||||
|
if (current) {
|
||||||
|
return current;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.elementStyle && that.elementStyle) {
|
// Author, normal weight
|
||||||
if (this.important && !that.important) {
|
// User, normal weight
|
||||||
return -1;
|
// User-Agent, normal weight
|
||||||
|
for (const propName of ["authorRule", "userRule", "agentRule"]) {
|
||||||
|
current = this.compare(that, propName, COMPAREMODE.BOOLEAN);
|
||||||
|
if (current) {
|
||||||
|
return current;
|
||||||
}
|
}
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.important && !that.important) {
|
// Specificity
|
||||||
return -1;
|
// Sheet index
|
||||||
}
|
// Rule line
|
||||||
if (that.important && !this.important) {
|
// Rule column
|
||||||
return 1;
|
for (const propName of ["specificity", "sheetIndex", "ruleLine", "ruleColumn"]) {
|
||||||
|
current = this.compare(that, propName, COMPAREMODE.INTEGER);
|
||||||
|
if (current) {
|
||||||
|
return current;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.specificity > that.specificity) {
|
// A rule has been compared against itself so return 0.
|
||||||
return -1;
|
return 0;
|
||||||
}
|
},
|
||||||
if (that.specificity > this.specificity) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.sheetIndex > that.sheetIndex) {
|
compare: function(that, propertyName, type) {
|
||||||
return -1;
|
switch (type) {
|
||||||
|
case COMPAREMODE.BOOLEAN:
|
||||||
|
if (this[propertyName] && !that[propertyName]) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (!this[propertyName] && that[propertyName]) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case COMPAREMODE.INTEGER:
|
||||||
|
if (this[propertyName] > that[propertyName]) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (this[propertyName] < that[propertyName]) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (that.sheetIndex > this.sheetIndex) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.ruleLine > that.ruleLine) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (that.ruleLine > this.ruleLine) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.ruleColumn > that.ruleColumn) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (that.ruleColumn > this.ruleColumn) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -483,7 +483,7 @@ var PageStyleActor = protocol.ActorClassWithSpec(pageStyleSpec, {
|
||||||
// node.
|
// node.
|
||||||
getSelectorSource: function(selectorInfo, relativeTo) {
|
getSelectorSource: function(selectorInfo, relativeTo) {
|
||||||
let result = selectorInfo.selector.text;
|
let result = selectorInfo.selector.text;
|
||||||
if (selectorInfo.elementStyle) {
|
if (selectorInfo.inlineStyle) {
|
||||||
const source = selectorInfo.sourceElement;
|
const source = selectorInfo.sourceElement;
|
||||||
if (source === relativeTo) {
|
if (source === relativeTo) {
|
||||||
result = "this";
|
result = "this";
|
||||||
|
@ -642,7 +642,7 @@ var PageStyleActor = protocol.ActorClassWithSpec(pageStyleSpec, {
|
||||||
for (let i = domRules.length - 1; i >= 0; i--) {
|
for (let i = domRules.length - 1; i >= 0; i--) {
|
||||||
const domRule = domRules[i];
|
const domRule = domRules[i];
|
||||||
|
|
||||||
const isSystem = !SharedCssLogic.isContentStylesheet(domRule.parentStyleSheet);
|
const isSystem = !SharedCssLogic.isAuthorStylesheet(domRule.parentStyleSheet);
|
||||||
|
|
||||||
if (isSystem && options.filter != SharedCssLogic.FILTER.UA) {
|
if (isSystem && options.filter != SharedCssLogic.FILTER.UA) {
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -398,7 +398,7 @@ var StyleSheetActor = protocol.ActorClassWithSpec(styleSheetSpec, {
|
||||||
nodeHref: docHref,
|
nodeHref: docHref,
|
||||||
disabled: this.rawSheet.disabled,
|
disabled: this.rawSheet.disabled,
|
||||||
title: this.rawSheet.title,
|
title: this.rawSheet.title,
|
||||||
system: !CssLogic.isContentStylesheet(this.rawSheet),
|
system: !CssLogic.isAuthorStylesheet(this.rawSheet),
|
||||||
styleSheetIndex: this.styleSheetIndex,
|
styleSheetIndex: this.styleSheetIndex,
|
||||||
sourceMapURL: this.rawSheet.sourceMapURL,
|
sourceMapURL: this.rawSheet.sourceMapURL,
|
||||||
};
|
};
|
||||||
|
|
|
@ -112,14 +112,36 @@ exports.CSSRuleTypeName = {
|
||||||
exports.l10n = name => styleInspectorL10N.getStr(name);
|
exports.l10n = name => styleInspectorL10N.getStr(name);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Is the given property sheet a content stylesheet?
|
* Is the given property sheet an author stylesheet?
|
||||||
*
|
*
|
||||||
* @param {CSSStyleSheet} sheet a stylesheet
|
* @param {CSSStyleSheet} sheet a stylesheet
|
||||||
* @return {boolean} true if the given stylesheet is a content stylesheet,
|
* @return {boolean} true if the given stylesheet is an author stylesheet,
|
||||||
* false otherwise.
|
* false otherwise.
|
||||||
*/
|
*/
|
||||||
exports.isContentStylesheet = function(sheet) {
|
exports.isAuthorStylesheet = function(sheet) {
|
||||||
return sheet.parsingMode !== "agent";
|
return sheet.parsingMode === "author";
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is the given property sheet a user stylesheet?
|
||||||
|
*
|
||||||
|
* @param {CSSStyleSheet} sheet a stylesheet
|
||||||
|
* @return {boolean} true if the given stylesheet is a user stylesheet,
|
||||||
|
* false otherwise.
|
||||||
|
*/
|
||||||
|
exports.isUserStylesheet = function(sheet) {
|
||||||
|
return sheet.parsingMode === "user";
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is the given property sheet a agent stylesheet?
|
||||||
|
*
|
||||||
|
* @param {CSSStyleSheet} sheet a stylesheet
|
||||||
|
* @return {boolean} true if the given stylesheet is a agent stylesheet,
|
||||||
|
* false otherwise.
|
||||||
|
*/
|
||||||
|
exports.isAgentStylesheet = function(sheet) {
|
||||||
|
return sheet.parsingMode === "agent";
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Загрузка…
Ссылка в новой задаче