summaryrefslogtreecommitdiff
path: root/Source/WebCore/css/ElementRuleCollector.cpp
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
commit1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch)
tree46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/css/ElementRuleCollector.cpp
parent32761a6cee1d0dee366b885b7b9c777e67885688 (diff)
downloadWebKitGtk-tarball-master.tar.gz
Diffstat (limited to 'Source/WebCore/css/ElementRuleCollector.cpp')
-rw-r--r--Source/WebCore/css/ElementRuleCollector.cpp536
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