diff options
Diffstat (limited to 'Source/WebCore/css/ElementRuleCollector.cpp')
-rw-r--r-- | Source/WebCore/css/ElementRuleCollector.cpp | 536 |
1 files changed, 316 insertions, 220 deletions
diff --git a/Source/WebCore/css/ElementRuleCollector.cpp b/Source/WebCore/css/ElementRuleCollector.cpp index 88f1eff18..8c2039750 100644 --- a/Source/WebCore/css/ElementRuleCollector.cpp +++ b/Source/WebCore/css/ElementRuleCollector.cpp @@ -2,7 +2,7 @@ * Copyright (C) 1999 Lars Knoll (knoll@kde.org) * (C) 2004-2005 Allan Sandfeld Jensen (kde@carewolf.com) * Copyright (C) 2006, 2007 Nicholas Shanks (webkit@nickshanks.com) - * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 Apple Inc. All rights reserved. * Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org> * Copyright (C) 2007, 2008 Eric Seidel <eric@webkit.org> * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) @@ -36,15 +36,17 @@ #include "CSSSelectorList.h" #include "CSSValueKeywords.h" #include "HTMLElement.h" -#include "InspectorInstrumentation.h" +#include "HTMLSlotElement.h" +#include "NodeRenderStyle.h" #include "RenderRegion.h" #include "SVGElement.h" -#include "SelectorCheckerFastPath.h" #include "SelectorCompiler.h" +#include "SelectorFilter.h" +#include "ShadowRoot.h" #include "StyleProperties.h" +#include "StyleScope.h" #include "StyledElement.h" - -#include <wtf/TemporaryChange.h> +#include <wtf/SetForScope.h> namespace WebCore { @@ -53,7 +55,7 @@ static const StyleProperties& leftToRightDeclaration() static NeverDestroyed<Ref<MutableStyleProperties>> leftToRightDecl(MutableStyleProperties::create()); if (leftToRightDecl.get()->isEmpty()) leftToRightDecl.get()->setProperty(CSSPropertyDirection, CSSValueLtr); - return leftToRightDecl.get().get(); + return leftToRightDecl.get(); } static const StyleProperties& rightToLeftDeclaration() @@ -61,51 +63,72 @@ static const StyleProperties& rightToLeftDeclaration() static NeverDestroyed<Ref<MutableStyleProperties>> rightToLeftDecl(MutableStyleProperties::create()); if (rightToLeftDecl.get()->isEmpty()) rightToLeftDecl.get()->setProperty(CSSPropertyDirection, CSSValueRtl); - return rightToLeftDecl.get().get(); + return rightToLeftDecl.get(); } class MatchRequest { public: - MatchRequest(RuleSet* ruleSet, bool includeEmptyRules = false) + MatchRequest(const RuleSet* ruleSet, bool includeEmptyRules = false, Style::ScopeOrdinal styleScopeOrdinal = Style::ScopeOrdinal::Element) : ruleSet(ruleSet) , includeEmptyRules(includeEmptyRules) + , styleScopeOrdinal(styleScopeOrdinal) { } const RuleSet* ruleSet; const bool includeEmptyRules; + Style::ScopeOrdinal styleScopeOrdinal; }; +ElementRuleCollector::ElementRuleCollector(const Element& element, const DocumentRuleSets& ruleSets, const SelectorFilter* selectorFilter) + : m_element(element) + , m_authorStyle(ruleSets.authorStyle()) + , m_userStyle(ruleSets.userStyle()) + , m_selectorFilter(selectorFilter) +{ + ASSERT(!m_selectorFilter || m_selectorFilter->parentStackIsConsistent(element.parentNode())); +} + +ElementRuleCollector::ElementRuleCollector(const Element& element, const RuleSet& authorStyle, const SelectorFilter* selectorFilter) + : m_element(element) + , m_authorStyle(authorStyle) + , m_selectorFilter(selectorFilter) +{ + ASSERT(!m_selectorFilter || m_selectorFilter->parentStackIsConsistent(element.parentNode())); +} + StyleResolver::MatchResult& ElementRuleCollector::matchedResult() { - ASSERT(m_mode == SelectorChecker::ResolvingStyle); + ASSERT(m_mode == SelectorChecker::Mode::ResolvingStyle); return m_result; } -const Vector<RefPtr<StyleRuleBase>>& ElementRuleCollector::matchedRuleList() const +const Vector<RefPtr<StyleRule>>& ElementRuleCollector::matchedRuleList() const { - ASSERT(m_mode == SelectorChecker::CollectingRules); + ASSERT(m_mode == SelectorChecker::Mode::CollectingRules); return m_matchedRuleList; } -inline void ElementRuleCollector::addMatchedRule(const RuleData* rule) +inline void ElementRuleCollector::addMatchedRule(const RuleData& ruleData, unsigned specificity, Style::ScopeOrdinal styleScopeOrdinal, StyleResolver::RuleRange& ruleRange) { - if (!m_matchedRules) - m_matchedRules = std::make_unique<Vector<const RuleData*, 32>>(); - m_matchedRules->append(rule); + // Update our first/last rule indices in the matched rules array. + ++ruleRange.lastRuleIndex; + if (ruleRange.firstRuleIndex == -1) + ruleRange.firstRuleIndex = ruleRange.lastRuleIndex; + + m_matchedRules.append({ &ruleData, specificity, styleScopeOrdinal }); } -inline void ElementRuleCollector::clearMatchedRules() +void ElementRuleCollector::clearMatchedRules() { - if (!m_matchedRules) - return; - m_matchedRules->clear(); + m_matchedRules.clear(); + m_keepAliveSlottedPseudoElementRules.clear(); } inline void ElementRuleCollector::addElementStyleProperties(const StyleProperties* propertySet, bool isCacheable) { if (!propertySet) return; - m_result.ranges.lastAuthorRule = m_result.matchedProperties.size(); + m_result.ranges.lastAuthorRule = m_result.matchedProperties().size(); if (m_result.ranges.firstAuthorRule == -1) m_result.ranges.firstAuthorRule = m_result.ranges.lastAuthorRule; m_result.addMatchedProperties(*propertySet); @@ -113,75 +136,30 @@ inline void ElementRuleCollector::addElementStyleProperties(const StylePropertie m_result.isCacheable = false; } -class MatchingUARulesScope { -public: - MatchingUARulesScope(); - ~MatchingUARulesScope(); - - static bool isMatchingUARules(); - -private: - static bool m_matchingUARules; -}; - -MatchingUARulesScope::MatchingUARulesScope() -{ - ASSERT(!m_matchingUARules); - m_matchingUARules = true; -} - -MatchingUARulesScope::~MatchingUARulesScope() -{ - m_matchingUARules = false; -} - -inline bool MatchingUARulesScope::isMatchingUARules() -{ - return m_matchingUARules; -} - -bool MatchingUARulesScope::m_matchingUARules = false; - void ElementRuleCollector::collectMatchingRules(const MatchRequest& matchRequest, StyleResolver::RuleRange& ruleRange) { ASSERT(matchRequest.ruleSet); - ASSERT(m_state.element()); - - const StyleResolver::State& state = m_state; - Element* element = state.element(); - const StyledElement* styledElement = state.styledElement(); - const AtomicString& pseudoId = element->shadowPseudoId(); - if (!pseudoId.isEmpty()) { - ASSERT(styledElement); - collectMatchingRulesForList(matchRequest.ruleSet->shadowPseudoElementRules(pseudoId.impl()), matchRequest, ruleRange); - } + ASSERT_WITH_MESSAGE(!(m_mode == SelectorChecker::Mode::CollectingRulesIgnoringVirtualPseudoElements && m_pseudoStyleRequest.pseudoId != NOPSEUDO), "When in StyleInvalidation or SharingRules, SelectorChecker does not try to match the pseudo ID. While ElementRuleCollector supports matching a particular pseudoId in this case, this would indicate a error at the call site since matching a particular element should be unnecessary."); -#if ENABLE(VIDEO_TRACK) - if (element->isWebVTTElement()) - collectMatchingRulesForList(matchRequest.ruleSet->cuePseudoRules(), matchRequest, ruleRange); -#endif - // Check whether other types of rules are applicable in the current tree scope. Criteria for this: - // a) it's a UA rule - // b) the tree scope allows author rules - // c) the rules comes from a scoped style sheet within the same tree scope - if (!MatchingUARulesScope::isMatchingUARules() - && !element->treeScope().applyAuthorStyles()) - return; + auto* shadowRoot = m_element.containingShadowRoot(); + if (shadowRoot && shadowRoot->mode() == ShadowRootMode::UserAgent) + collectMatchingShadowPseudoElementRules(matchRequest, ruleRange); // We need to collect the rules for id, class, tag, and everything else into a buffer and // then sort the buffer. - if (element->hasID()) - collectMatchingRulesForList(matchRequest.ruleSet->idRules(element->idForStyleResolution().impl()), matchRequest, ruleRange); - if (styledElement && styledElement->hasClass()) { - for (size_t i = 0; i < styledElement->classNames().size(); ++i) - collectMatchingRulesForList(matchRequest.ruleSet->classRules(styledElement->classNames()[i].impl()), matchRequest, ruleRange); + auto& id = m_element.idForStyleResolution(); + if (!id.isNull()) + collectMatchingRulesForList(matchRequest.ruleSet->idRules(id), matchRequest, ruleRange); + if (m_element.hasClass()) { + for (size_t i = 0; i < m_element.classNames().size(); ++i) + collectMatchingRulesForList(matchRequest.ruleSet->classRules(m_element.classNames()[i]), matchRequest, ruleRange); } - if (element->isLink()) + if (m_element.isLink()) collectMatchingRulesForList(matchRequest.ruleSet->linkPseudoClassRules(), matchRequest, ruleRange); - if (SelectorChecker::matchesFocusPseudoClass(element)) + if (SelectorChecker::matchesFocusPseudoClass(m_element)) collectMatchingRulesForList(matchRequest.ruleSet->focusPseudoClassRules(), matchRequest, ruleRange); - collectMatchingRulesForList(matchRequest.ruleSet->tagRules(element->localName().impl()), matchRequest, ruleRange); + collectMatchingRulesForList(matchRequest.ruleSet->tagRules(m_element.localName(), m_element.isHTMLElement() && m_element.document().isHTMLDocument()), matchRequest, ruleRange); collectMatchingRulesForList(matchRequest.ruleSet->universalRules(), matchRequest, ruleRange); } @@ -203,54 +181,149 @@ void ElementRuleCollector::collectMatchingRulesForRegion(const MatchRequest& mat void ElementRuleCollector::sortAndTransferMatchedRules() { - const StyleResolver::State& state = m_state; - - if (!m_matchedRules || m_matchedRules->isEmpty()) + if (m_matchedRules.isEmpty()) return; sortMatchedRules(); - Vector<const RuleData*, 32>& matchedRules = *m_matchedRules; - if (m_mode == SelectorChecker::CollectingRules) { - for (unsigned i = 0; i < matchedRules.size(); ++i) - m_matchedRuleList.append(matchedRules[i]->rule()); + if (m_mode == SelectorChecker::Mode::CollectingRules) { + for (const MatchedRule& matchedRule : m_matchedRules) + m_matchedRuleList.append(matchedRule.ruleData->rule()); return; } - // Now transfer the set of matched rules over to our list of declarations. - for (unsigned i = 0; i < matchedRules.size(); i++) { - if (state.style() && matchedRules[i]->containsUncommonAttributeSelector()) - state.style()->setUnique(); - m_result.addMatchedProperties(matchedRules[i]->rule()->properties(), matchedRules[i]->rule(), matchedRules[i]->linkMatchType(), matchedRules[i]->propertyWhitelistType(MatchingUARulesScope::isMatchingUARules())); + for (const MatchedRule& matchedRule : m_matchedRules) { + m_result.addMatchedProperties(matchedRule.ruleData->rule()->properties(), matchedRule.ruleData->rule(), matchedRule.ruleData->linkMatchType(), matchedRule.ruleData->propertyWhitelistType(), matchedRule.styleScopeOrdinal); } } void ElementRuleCollector::matchAuthorRules(bool includeEmptyRules) { clearMatchedRules(); - m_result.ranges.lastAuthorRule = m_result.matchedProperties.size() - 1; - if (!m_state.element()) + m_result.ranges.lastAuthorRule = m_result.matchedProperties().size() - 1; + StyleResolver::RuleRange ruleRange = m_result.ranges.authorRuleRange(); + + { + MatchRequest matchRequest(&m_authorStyle, includeEmptyRules); + collectMatchingRules(matchRequest, ruleRange); + collectMatchingRulesForRegion(matchRequest, ruleRange); + } + + auto* parent = m_element.parentElement(); + if (parent && parent->shadowRoot()) + matchSlottedPseudoElementRules(includeEmptyRules, ruleRange); + + if (m_element.shadowRoot()) + matchHostPseudoClassRules(includeEmptyRules, ruleRange); + + if (m_element.isInShadowTree()) + matchAuthorShadowPseudoElementRules(includeEmptyRules, ruleRange); + + sortAndTransferMatchedRules(); +} + +void ElementRuleCollector::matchAuthorShadowPseudoElementRules(bool includeEmptyRules, StyleResolver::RuleRange& ruleRange) +{ + ASSERT(m_element.isInShadowTree()); + auto& shadowRoot = *m_element.containingShadowRoot(); + if (shadowRoot.mode() != ShadowRootMode::UserAgent) + return; + // Look up shadow pseudo elements also from the host scope author style as they are web-exposed. + auto& hostAuthorRules = Style::Scope::forNode(*shadowRoot.host()).resolver().ruleSets().authorStyle(); + MatchRequest hostAuthorRequest { &hostAuthorRules, includeEmptyRules, Style::ScopeOrdinal::ContainingHost }; + collectMatchingShadowPseudoElementRules(hostAuthorRequest, ruleRange); +} + +void ElementRuleCollector::matchHostPseudoClassRules(bool includeEmptyRules, StyleResolver::RuleRange& ruleRange) +{ + ASSERT(m_element.shadowRoot()); + + auto& shadowAuthorStyle = m_element.shadowRoot()->styleScope().resolver().ruleSets().authorStyle(); + auto& shadowHostRules = shadowAuthorStyle.hostPseudoClassRules(); + if (shadowHostRules.isEmpty()) return; + SetForScope<bool> change(m_isMatchingHostPseudoClass, true); + + MatchRequest hostMatchRequest { nullptr, includeEmptyRules, Style::ScopeOrdinal::Shadow }; + collectMatchingRulesForList(&shadowHostRules, hostMatchRequest, ruleRange); +} + +void ElementRuleCollector::matchSlottedPseudoElementRules(bool includeEmptyRules, StyleResolver::RuleRange& ruleRange) +{ + auto* slot = m_element.assignedSlot(); + auto styleScopeOrdinal = Style::ScopeOrdinal::FirstSlot; + + for (; slot; slot = slot->assignedSlot(), ++styleScopeOrdinal) { + auto& styleScope = Style::Scope::forNode(*slot); + if (!styleScope.resolver().ruleSets().isAuthorStyleDefined()) + continue; + // Find out if there are any ::slotted rules in the shadow tree matching the current slot. + // FIXME: This is really part of the slot style and could be cached when resolving it. + ElementRuleCollector collector(*slot, styleScope.resolver().ruleSets().authorStyle(), nullptr); + auto slottedPseudoElementRules = collector.collectSlottedPseudoElementRulesForSlot(includeEmptyRules); + if (!slottedPseudoElementRules) + continue; + // Match in the current scope. + SetForScope<bool> change(m_isMatchingSlottedPseudoElements, true); + + MatchRequest scopeMatchRequest(nullptr, includeEmptyRules, styleScopeOrdinal); + collectMatchingRulesForList(slottedPseudoElementRules.get(), scopeMatchRequest, ruleRange); + + m_keepAliveSlottedPseudoElementRules.append(WTFMove(slottedPseudoElementRules)); + } +} + +void ElementRuleCollector::collectMatchingShadowPseudoElementRules(const MatchRequest& matchRequest, StyleResolver::RuleRange& ruleRange) +{ + ASSERT(matchRequest.ruleSet); + ASSERT(m_element.containingShadowRoot()->mode() == ShadowRootMode::UserAgent); + + auto& rules = *matchRequest.ruleSet; +#if ENABLE(VIDEO_TRACK) + // FXIME: WebVTT should not be done by styling UA shadow trees like this. + if (m_element.isWebVTTElement()) + collectMatchingRulesForList(rules.cuePseudoRules(), matchRequest, ruleRange); +#endif + auto& pseudoId = m_element.shadowPseudoId(); + if (!pseudoId.isEmpty()) + collectMatchingRulesForList(rules.shadowPseudoElementRules(pseudoId), matchRequest, ruleRange); +} + +std::unique_ptr<RuleSet::RuleDataVector> ElementRuleCollector::collectSlottedPseudoElementRulesForSlot(bool includeEmptyRules) +{ + ASSERT(is<HTMLSlotElement>(m_element)); + + clearMatchedRules(); + + m_mode = SelectorChecker::Mode::CollectingRules; + // Match global author rules. - MatchRequest matchRequest(m_ruleSets.authorStyle(), includeEmptyRules); + MatchRequest matchRequest(&m_authorStyle, includeEmptyRules); StyleResolver::RuleRange ruleRange = m_result.ranges.authorRuleRange(); - collectMatchingRules(matchRequest, ruleRange); - collectMatchingRulesForRegion(matchRequest, ruleRange); + collectMatchingRulesForList(&m_authorStyle.slottedPseudoElementRules(), matchRequest, ruleRange); - sortAndTransferMatchedRules(); + if (m_matchedRules.isEmpty()) + return { }; + + auto ruleDataVector = std::make_unique<RuleSet::RuleDataVector>(); + ruleDataVector->reserveInitialCapacity(m_matchedRules.size()); + for (auto& matchedRule : m_matchedRules) + ruleDataVector->uncheckedAppend(*matchedRule.ruleData); + + return ruleDataVector; } void ElementRuleCollector::matchUserRules(bool includeEmptyRules) { - if (!m_ruleSets.userStyle()) + if (!m_userStyle) return; clearMatchedRules(); - m_result.ranges.lastUserRule = m_result.matchedProperties.size() - 1; - MatchRequest matchRequest(m_ruleSets.userStyle(), includeEmptyRules); + m_result.ranges.lastUserRule = m_result.matchedProperties().size() - 1; + MatchRequest matchRequest(m_userStyle, includeEmptyRules); StyleResolver::RuleRange ruleRange = m_result.ranges.userRuleRange(); collectMatchingRules(matchRequest, ruleRange); collectMatchingRulesForRegion(matchRequest, ruleRange); @@ -260,8 +333,6 @@ void ElementRuleCollector::matchUserRules(bool includeEmptyRules) void ElementRuleCollector::matchUARules() { - MatchingUARulesScope scope; - // First we match rules from the user agent sheet. if (CSSDefaultStyleSheets::simpleDefaultStyleSheet) m_result.isCacheable = false; @@ -270,155 +341,180 @@ void ElementRuleCollector::matchUARules() matchUARules(userAgentStyleSheet); // In quirks mode, we match rules from the quirks user agent sheet. - if (document().inQuirksMode()) + if (m_element.document().inQuirksMode()) matchUARules(CSSDefaultStyleSheets::defaultQuirksStyle); - - // If document uses view source styles (in view source mode or in xml viewer mode), then we match rules from the view source style sheet. - if (document().isViewSource()) - matchUARules(CSSDefaultStyleSheets::viewSourceStyle()); } void ElementRuleCollector::matchUARules(RuleSet* rules) { clearMatchedRules(); - m_result.ranges.lastUARule = m_result.matchedProperties.size() - 1; + m_result.ranges.lastUARule = m_result.matchedProperties().size() - 1; StyleResolver::RuleRange ruleRange = m_result.ranges.UARuleRange(); collectMatchingRules(MatchRequest(rules), ruleRange); sortAndTransferMatchedRules(); } -inline bool ElementRuleCollector::ruleMatches(const RuleData& ruleData, PseudoId& dynamicPseudo) +static const CSSSelector* findSlottedPseudoElementSelector(const CSSSelector* selector) +{ + for (; selector; selector = selector->tagHistory()) { + if (selector->match() == CSSSelector::PseudoElement && selector->pseudoElementType() == CSSSelector::PseudoElementSlotted) { + if (auto* list = selector->selectorList()) + return list->first(); + break; + } + }; + return nullptr; +} + +inline bool ElementRuleCollector::ruleMatches(const RuleData& ruleData, unsigned& specificity) { - const StyleResolver::State& state = m_state; - - bool fastCheckableSelector = ruleData.hasFastCheckableSelector(); - if (fastCheckableSelector) { - // We know this selector does not include any pseudo elements. - if (m_pseudoStyleRequest.pseudoId != NOPSEUDO) - return false; - // We know a sufficiently simple single part selector matches simply because we found it from the rule hash. - // This is limited to HTML only so we don't need to check the namespace. - if (ruleData.hasRightmostSelectorMatchingHTMLBasedOnRuleHash() && state.element()->isHTMLElement()) { - if (!ruleData.hasMultipartSelector()) - return true; + // We know a sufficiently simple single part selector matches simply because we found it from the rule hash when filtering the RuleSet. + // This is limited to HTML only so we don't need to check the namespace (because of tag name match). + MatchBasedOnRuleHash matchBasedOnRuleHash = ruleData.matchBasedOnRuleHash(); + if (matchBasedOnRuleHash != MatchBasedOnRuleHash::None && m_element.isHTMLElement()) { + ASSERT_WITH_MESSAGE(m_pseudoStyleRequest.pseudoId == NOPSEUDO, "If we match based on the rule hash while collecting for a particular pseudo element ID, we would add incorrect rules for that pseudo element ID. We should never end in ruleMatches() with a pseudo element if the ruleData cannot match any pseudo element."); + + switch (matchBasedOnRuleHash) { + case MatchBasedOnRuleHash::None: + ASSERT_NOT_REACHED(); + break; + case MatchBasedOnRuleHash::Universal: + specificity = 0; + break; + case MatchBasedOnRuleHash::ClassA: + specificity = static_cast<unsigned>(SelectorSpecificityIncrement::ClassA); + break; + case MatchBasedOnRuleHash::ClassB: + specificity = static_cast<unsigned>(SelectorSpecificityIncrement::ClassB); + break; + case MatchBasedOnRuleHash::ClassC: + specificity = static_cast<unsigned>(SelectorSpecificityIncrement::ClassC); + break; } + return true; } #if ENABLE(CSS_SELECTOR_JIT) void* compiledSelectorChecker = ruleData.compiledSelectorCodeRef().code().executableAddress(); if (!compiledSelectorChecker && ruleData.compilationStatus() == SelectorCompilationStatus::NotCompiled) { - JSC::VM* vm = document().scriptExecutionContext()->vm(); + JSC::VM& vm = m_element.document().scriptExecutionContext()->vm(); SelectorCompilationStatus compilationStatus; JSC::MacroAssemblerCodeRef compiledSelectorCodeRef; - compilationStatus = SelectorCompiler::compileSelector(ruleData.selector(), vm, compiledSelectorCodeRef); + compilationStatus = SelectorCompiler::compileSelector(ruleData.selector(), &vm, SelectorCompiler::SelectorContext::RuleCollector, compiledSelectorCodeRef); ruleData.setCompiledSelector(compilationStatus, compiledSelectorCodeRef); compiledSelectorChecker = ruleData.compiledSelectorCodeRef().code().executableAddress(); } - if (compiledSelectorChecker) { - if (m_pseudoStyleRequest.pseudoId != NOPSEUDO) - return false; - if (ruleData.compilationStatus() == SelectorCompilationStatus::SimpleSelectorChecker) { - SelectorCompiler::SimpleSelectorChecker selectorChecker = SelectorCompiler::simpleSelectorCheckerFunction(compiledSelectorChecker, ruleData.compilationStatus()); - return selectorChecker(state.element()); - } - ASSERT(ruleData.compilationStatus() == SelectorCompilationStatus::SelectorCheckerWithCheckingContext); - - SelectorCompiler::SelectorCheckerWithCheckingContext selectorChecker = SelectorCompiler::selectorCheckerFunctionWithCheckingContext(compiledSelectorChecker, ruleData.compilationStatus()); - SelectorCompiler::CheckingContext context; - context.elementStyle = state.style(); - context.resolvingMode = m_mode; - return selectorChecker(state.element(), &context); - } -#endif // ENABLE(CSS_SELECTOR_JIT) + if (compiledSelectorChecker && ruleData.compilationStatus() == SelectorCompilationStatus::SimpleSelectorChecker) { + SelectorCompiler::RuleCollectorSimpleSelectorChecker selectorChecker = SelectorCompiler::ruleCollectorSimpleSelectorCheckerFunction(compiledSelectorChecker, ruleData.compilationStatus()); +#if !ASSERT_MSG_DISABLED + unsigned ignoreSpecificity; + ASSERT_WITH_MESSAGE(!selectorChecker(&m_element, &ignoreSpecificity) || m_pseudoStyleRequest.pseudoId == NOPSEUDO, "When matching pseudo elements, we should never compile a selector checker without context unless it cannot match anything."); +#endif +#if CSS_SELECTOR_JIT_PROFILING + ruleData.compiledSelectorUsed(); +#endif + bool selectorMatches = selectorChecker(&m_element, &specificity); - if (fastCheckableSelector) { - if (ruleData.selector()->m_match == CSSSelector::Tag && !SelectorChecker::tagMatches(state.element(), ruleData.selector()->tagQName())) - return false; - SelectorCheckerFastPath selectorCheckerFastPath(ruleData.selector(), state.element()); - if (!selectorCheckerFastPath.matchesRightmostAttributeSelector()) - return false; + if (selectorMatches && ruleData.containsUncommonAttributeSelector()) + m_didMatchUncommonAttributeSelector = true; - return selectorCheckerFastPath.matches(); + return selectorMatches; } +#endif // ENABLE(CSS_SELECTOR_JIT) - // Slow path. - SelectorChecker selectorChecker(document(), m_mode); - SelectorChecker::SelectorCheckingContext context(ruleData.selector(), state.element(), SelectorChecker::VisitedMatchEnabled); - context.elementStyle = state.style(); + SelectorChecker::CheckingContext context(m_mode); context.pseudoId = m_pseudoStyleRequest.pseudoId; context.scrollbar = m_pseudoStyleRequest.scrollbar; context.scrollbarPart = m_pseudoStyleRequest.scrollbarPart; - if (!selectorChecker.match(context, dynamicPseudo)) - return false; - if (m_pseudoStyleRequest.pseudoId != NOPSEUDO && m_pseudoStyleRequest.pseudoId != dynamicPseudo) - return false; - return true; + context.isMatchingHostPseudoClass = m_isMatchingHostPseudoClass; + + bool selectorMatches; +#if ENABLE(CSS_SELECTOR_JIT) + if (compiledSelectorChecker) { + ASSERT(ruleData.compilationStatus() == SelectorCompilationStatus::SelectorCheckerWithCheckingContext); + + SelectorCompiler::RuleCollectorSelectorCheckerWithCheckingContext selectorChecker = SelectorCompiler::ruleCollectorSelectorCheckerFunctionWithCheckingContext(compiledSelectorChecker, ruleData.compilationStatus()); + +#if CSS_SELECTOR_JIT_PROFILING + ruleData.compiledSelectorUsed(); +#endif + selectorMatches = selectorChecker(&m_element, &context, &specificity); + } else +#endif // ENABLE(CSS_SELECTOR_JIT) + { + auto* selector = ruleData.selector(); + if (m_isMatchingSlottedPseudoElements) { + selector = findSlottedPseudoElementSelector(ruleData.selector()); + if (!selector) + return false; + } + // Slow path. + SelectorChecker selectorChecker(m_element.document()); + selectorMatches = selectorChecker.match(*selector, m_element, context, specificity); + } + + if (ruleData.containsUncommonAttributeSelector()) { + if (selectorMatches || context.pseudoIDSet) + m_didMatchUncommonAttributeSelector = true; + } + m_matchedPseudoElementIds.merge(context.pseudoIDSet); + m_styleRelations.appendVector(context.styleRelations); + + return selectorMatches; } -void ElementRuleCollector::collectMatchingRulesForList(const Vector<RuleData>* rules, const MatchRequest& matchRequest, StyleResolver::RuleRange& ruleRange) +void ElementRuleCollector::collectMatchingRulesForList(const RuleSet::RuleDataVector* rules, const MatchRequest& matchRequest, StyleResolver::RuleRange& ruleRange) { if (!rules) return; - const StyleResolver::State& state = m_state; - for (unsigned i = 0, size = rules->size(); i < size; ++i) { const RuleData& ruleData = rules->data()[i]; - if (m_canUseFastReject && m_selectorFilter.fastRejectSelector<RuleData::maximumIdentifierCount>(ruleData.descendantSelectorIdentifierHashes())) + + if (!ruleData.canMatchPseudoElement() && m_pseudoStyleRequest.pseudoId != NOPSEUDO) + continue; + + if (m_selectorFilter && m_selectorFilter->fastRejectSelector<RuleData::maximumIdentifierCount>(ruleData.descendantSelectorIdentifierHashes())) continue; StyleRule* rule = ruleData.rule(); - PseudoId dynamicPseudo = NOPSEUDO; - if (ruleMatches(ruleData, dynamicPseudo)) { - // For SharingRules testing, any match is good enough, we don't care what is matched. - if (m_mode == SelectorChecker::SharingRules) { - addMatchedRule(&ruleData); - break; - } - - // If the rule has no properties to apply, then ignore it in the non-debug mode. - const StyleProperties& properties = rule->properties(); - if (properties.isEmpty() && !matchRequest.includeEmptyRules) - continue; - // FIXME: Exposing the non-standard getMatchedCSSRules API to web is the only reason this is needed. - if (m_sameOriginOnly && !ruleData.hasDocumentSecurityOrigin()) - continue; - // If we're matching normal rules, set a pseudo bit if - // we really just matched a pseudo-element. - if (dynamicPseudo != NOPSEUDO && m_pseudoStyleRequest.pseudoId == NOPSEUDO) { - if (m_mode == SelectorChecker::CollectingRules) - continue; - if (dynamicPseudo < FIRST_INTERNAL_PSEUDOID) - state.style()->setHasPseudoStyle(dynamicPseudo); - } else { - // Update our first/last rule indices in the matched rules array. - ++ruleRange.lastRuleIndex; - if (ruleRange.firstRuleIndex == -1) - ruleRange.firstRuleIndex = ruleRange.lastRuleIndex; - - // Add this rule to our list of matched rules. - addMatchedRule(&ruleData); - continue; - } - } + + // If the rule has no properties to apply, then ignore it in the non-debug mode. + // Note that if we get null back here, it means we have a rule with deferred properties, + // and that means we always have to consider it. + const StyleProperties* properties = rule->propertiesWithoutDeferredParsing(); + if (properties && properties->isEmpty() && !matchRequest.includeEmptyRules) + continue; + + // FIXME: Exposing the non-standard getMatchedCSSRules API to web is the only reason this is needed. + if (m_sameOriginOnly && !ruleData.hasDocumentSecurityOrigin()) + continue; + + unsigned specificity; + if (ruleMatches(ruleData, specificity)) + addMatchedRule(ruleData, specificity, matchRequest.styleScopeOrdinal, ruleRange); } } -static inline bool compareRules(const RuleData* r1, const RuleData* r2) +static inline bool compareRules(MatchedRule r1, MatchedRule r2) { - unsigned specificity1 = r1->specificity(); - unsigned specificity2 = r2->specificity(); - return (specificity1 == specificity2) ? r1->position() < r2->position() : specificity1 < specificity2; + // For normal properties the earlier scope wins. This may be reversed by !important which is handled when resolving cascade. + if (r1.styleScopeOrdinal != r2.styleScopeOrdinal) + return r1.styleScopeOrdinal > r2.styleScopeOrdinal; + + if (r1.specificity != r2.specificity) + return r1.specificity < r2.specificity; + + return r1.ruleData->position() < r2.ruleData->position(); } void ElementRuleCollector::sortMatchedRules() { - ASSERT(m_matchedRules); - std::sort(m_matchedRules->begin(), m_matchedRules->end(), compareRules); + std::sort(m_matchedRules.begin(), m_matchedRules.end(), compareRules); } void ElementRuleCollector::matchAllRules(bool matchAuthorAndUserStyles, bool includeSMILProperties) @@ -430,17 +526,18 @@ void ElementRuleCollector::matchAllRules(bool matchAuthorAndUserStyles, bool inc matchUserRules(false); // Now check author rules, beginning first with presentational attributes mapped from HTML. - if (m_state.styledElement()) { - addElementStyleProperties(m_state.styledElement()->presentationAttributeStyle()); + if (is<StyledElement>(m_element)) { + auto& styledElement = downcast<StyledElement>(m_element); + addElementStyleProperties(styledElement.presentationAttributeStyle()); // Now we check additional mapped declarations. // Tables and table cells share an additional mapped rule that must be applied // after all attributes, since their mapped style depends on the values of multiple attributes. - addElementStyleProperties(m_state.styledElement()->additionalPresentationAttributeStyle()); + addElementStyleProperties(styledElement.additionalPresentationAttributeStyle()); - if (m_state.styledElement()->isHTMLElement()) { + if (is<HTMLElement>(styledElement)) { bool isAuto; - TextDirection textDirection = toHTMLElement(m_state.styledElement())->directionalityIfhasDirAutoAttribute(isAuto); + TextDirection textDirection = downcast<HTMLElement>(styledElement).directionalityIfhasDirAutoAttribute(isAuto); if (isAuto) m_result.addMatchedProperties(textDirection == LTR ? leftToRightDeclaration() : rightToLeftDeclaration()); } @@ -450,34 +547,33 @@ void ElementRuleCollector::matchAllRules(bool matchAuthorAndUserStyles, bool inc if (matchAuthorAndUserStyles) matchAuthorRules(false); - // Now check our inline style attribute. - if (matchAuthorAndUserStyles && m_state.styledElement() && m_state.styledElement()->inlineStyle()) { - // Inline style is immutable as long as there is no CSSOM wrapper. - // FIXME: Media control shadow trees seem to have problems with caching. - bool isInlineStyleCacheable = !m_state.styledElement()->inlineStyle()->isMutable() && !m_state.styledElement()->isInShadowTree(); - // FIXME: Constify. - addElementStyleProperties(m_state.styledElement()->inlineStyle(), isInlineStyleCacheable); - } + if (matchAuthorAndUserStyles && is<StyledElement>(m_element)) { + auto& styledElement = downcast<StyledElement>(m_element); + // Now check our inline style attribute. + if (styledElement.inlineStyle()) { + // Inline style is immutable as long as there is no CSSOM wrapper. + // FIXME: Media control shadow trees seem to have problems with caching. + bool isInlineStyleCacheable = !styledElement.inlineStyle()->isMutable() && !styledElement.isInShadowTree(); + // FIXME: Constify. + addElementStyleProperties(styledElement.inlineStyle(), isInlineStyleCacheable); + } -#if ENABLE(SVG) - // Now check SMIL animation override style. - if (includeSMILProperties && matchAuthorAndUserStyles && m_state.styledElement() && m_state.styledElement()->isSVGElement()) - addElementStyleProperties(toSVGElement(m_state.styledElement())->animatedSMILStyleProperties(), false /* isCacheable */); -#else - UNUSED_PARAM(includeSMILProperties); -#endif + // Now check SMIL animation override style. + if (includeSMILProperties && is<SVGElement>(styledElement)) + addElementStyleProperties(downcast<SVGElement>(styledElement).animatedSMILStyleProperties(), false /* isCacheable */); + } } -bool ElementRuleCollector::hasAnyMatchingRules(RuleSet* ruleSet) +bool ElementRuleCollector::hasAnyMatchingRules(const RuleSet* ruleSet) { clearMatchedRules(); - m_mode = SelectorChecker::SharingRules; + m_mode = SelectorChecker::Mode::CollectingRulesIgnoringVirtualPseudoElements; int firstRuleIndex = -1, lastRuleIndex = -1; StyleResolver::RuleRange ruleRange(firstRuleIndex, lastRuleIndex); collectMatchingRules(MatchRequest(ruleSet), ruleRange); - return m_matchedRules && !m_matchedRules->isEmpty(); + return !m_matchedRules.isEmpty(); } } // namespace WebCore |