blob: 303155b0e251bc97dd1ef490cfcbe80565839179 [file] [log] [blame]
"use strict";
const cssom = require("cssom");
const defaultStyleSheet = require("../../browser/default-stylesheet");
const { matchesDontThrow } = require("./selectors");
const { forEach, indexOf } = Array.prototype;
let parsedDefaultStyleSheet;
// Properties for which getResolvedValue is implemented. This is less than
// every supported property.
// https://drafts.csswg.org/indexes/#properties
exports.propertiesWithResolvedValueImplemented = {
__proto__: null,
// https://drafts.csswg.org/css2/visufx.html#visibility
visibility: {
inherited: true,
initial: "visible",
computedValue: "as-specified"
}
};
exports.forEachMatchingSheetRuleOfElement = (elementImpl, handleRule) => {
function handleSheet(sheet) {
forEach.call(sheet.cssRules, rule => {
if (rule.media) {
if (indexOf.call(rule.media, "screen") !== -1) {
forEach.call(rule.cssRules, innerRule => {
if (matches(innerRule, elementImpl)) {
handleRule(innerRule);
}
});
}
} else if (matches(rule, elementImpl)) {
handleRule(rule);
}
});
}
if (!parsedDefaultStyleSheet) {
parsedDefaultStyleSheet = cssom.parse(defaultStyleSheet);
}
handleSheet(parsedDefaultStyleSheet);
forEach.call(elementImpl._ownerDocument.styleSheets._list, handleSheet);
};
function matches(rule, element) {
return matchesDontThrow(element, rule.selectorText);
}
// Naive implementation of https://drafts.csswg.org/css-cascade-4/#cascading
// based on the previous jsdom implementation of getComputedStyle.
// Does not implement https://drafts.csswg.org/css-cascade-4/#cascade-specificity,
// or rather specificity is only implemented by the order in which the matching
// rules appear. The last rule is the most specific while the first rule is
// the least specific.
function getCascadedPropertyValue(element, property) {
let value = "";
exports.forEachMatchingSheetRuleOfElement(element, rule => {
value = rule.style.getPropertyValue(property);
});
const inlineValue = element.style.getPropertyValue(property);
if (inlineValue !== "" && inlineValue !== null) {
value = inlineValue;
}
return value;
}
// https://drafts.csswg.org/css-cascade-4/#specified-value
function getSpecifiedValue(element, property) {
const cascade = getCascadedPropertyValue(element, property);
if (cascade !== "") {
return cascade;
}
// Defaulting
const { initial, inherited } = exports.propertiesWithResolvedValueImplemented[property];
if (inherited && element.parentElement !== null) {
return getComputedValue(element.parentElement, property);
}
// root element without parent element or inherited property
return initial;
}
// https://drafts.csswg.org/css-cascade-4/#computed-value
function getComputedValue(element, property) {
const { computedValue } = exports.propertiesWithResolvedValueImplemented[property];
if (computedValue === "as-specified") {
return getSpecifiedValue(element, property);
}
throw new TypeError(`Internal error: unrecognized computed value instruction '${computedValue}'`);
}
// https://drafts.csswg.org/cssom/#resolved-value
// Only implements `visibility`
exports.getResolvedValue = (element, property) => {
// Determined for special case properties, none of which are implemented here.
// So we skip to "any other property: The resolved value is the computed value."
return getComputedValue(element, property);
};