| "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); |
| }; |