#include "config.h"
#include "core/css/ElementRuleCollector.h"
+#include "core/css/CSSImportRule.h"
+#include "core/css/CSSKeyframesRule.h"
+#include "core/css/CSSMediaRule.h"
#include "core/css/CSSRuleList.h"
#include "core/css/CSSSelector.h"
#include "core/css/CSSStyleRule.h"
-#include "core/css/SelectorCheckerFastPath.h"
+#include "core/css/CSSStyleSheet.h"
+#include "core/css/CSSSupportsRule.h"
#include "core/css/SiblingTraversalStrategies.h"
#include "core/css/StylePropertySet.h"
#include "core/css/resolver/StyleResolver.h"
#include "core/dom/shadow/ShadowRoot.h"
-#include "core/rendering/RenderRegion.h"
+#include "core/rendering/style/StyleInheritedData.h"
-namespace WebCore {
+namespace blink {
ElementRuleCollector::ElementRuleCollector(const ElementResolveContext& context,
- const SelectorFilter& filter, RenderStyle* style, ShouldIncludeStyleSheetInCSSOMWrapper includeStyleSheet)
+ const SelectorFilter& filter, RenderStyle* style)
: m_context(context)
, m_selectorFilter(filter)
, m_style(style)
- , m_regionForStyling(0)
, m_pseudoStyleRequest(NOPSEUDO)
, m_mode(SelectorChecker::ResolvingStyle)
, m_canUseFastReject(m_selectorFilter.parentStackIsConsistent(context.parentNode()))
, m_sameOriginOnly(false)
, m_matchingUARules(false)
- , m_includeStyleSheet(includeStyleSheet)
+ , m_scopeContainsLastMatchedElement(false)
{ }
ElementRuleCollector::~ElementRuleCollector()
return m_result;
}
-PassRefPtr<StyleRuleList> ElementRuleCollector::matchedStyleRuleList()
+PassRefPtrWillBeRawPtr<StyleRuleList> ElementRuleCollector::matchedStyleRuleList()
{
ASSERT(m_mode == SelectorChecker::CollectingStyleRules);
return m_styleRuleList.release();
}
-PassRefPtr<CSSRuleList> ElementRuleCollector::matchedCSSRuleList()
+PassRefPtrWillBeRawPtr<CSSRuleList> ElementRuleCollector::matchedCSSRuleList()
{
ASSERT(m_mode == SelectorChecker::CollectingCSSRules);
return m_cssRuleList.release();
}
-inline void ElementRuleCollector::addMatchedRule(const RuleData* rule, CascadeScope cascadeScope, CascadeOrder cascadeOrder)
+inline void ElementRuleCollector::addMatchedRule(const RuleData* rule, unsigned specificity, CascadeScope cascadeScope, CascadeOrder cascadeOrder, unsigned styleSheetIndex, const CSSStyleSheet* parentStyleSheet)
{
if (!m_matchedRules)
- m_matchedRules = adoptPtr(new Vector<MatchedRule, 32>);
- m_matchedRules->append(MatchedRule(rule, cascadeScope, cascadeOrder));
+ m_matchedRules = adoptPtrWillBeNoop(new WillBeHeapVector<MatchedRule, 32>);
+ m_matchedRules->append(MatchedRule(rule, specificity, cascadeScope, cascadeOrder, styleSheetIndex, parentStyleSheet));
}
void ElementRuleCollector::clearMatchedRules()
m_result.isCacheable = false;
}
-static bool rulesApplicableInCurrentTreeScope(const Element* element, const ContainerNode* scopingNode, SelectorChecker::BehaviorAtBoundary behaviorAtBoundary, bool elementApplyAuthorStyles)
+static bool rulesApplicableInCurrentTreeScope(const Element* element, const ContainerNode* scopingNode, bool matchingTreeBoundaryRules)
{
- TreeScope& treeScope = element->treeScope();
-
// [skipped, because already checked] a) it's a UA rule
- // b) element is allowed to apply author rules
- if (elementApplyAuthorStyles)
+ // b) We're mathcing tree boundary rules.
+ if (matchingTreeBoundaryRules)
return true;
// c) the rules comes from a scoped style sheet within the same tree scope
- if (!scopingNode || treeScope == scopingNode->treeScope())
+ if (!scopingNode || element->treeScope() == scopingNode->treeScope())
return true;
// d) the rules comes from a scoped style sheet within an active shadow root whose host is the given element
- if (element->isInShadowTree() && (behaviorAtBoundary & SelectorChecker::ScopeIsShadowHost) && scopingNode == element->containingShadowRoot()->host())
- return true;
- // e) the rules can cross boundaries
- if ((behaviorAtBoundary & SelectorChecker::BoundaryBehaviorMask) == SelectorChecker::CrossesBoundary)
+ if (SelectorChecker::isHostInItsShadowTree(*element, scopingNode))
return true;
return false;
}
-void ElementRuleCollector::collectMatchingRules(const MatchRequest& matchRequest, RuleRange& ruleRange, SelectorChecker::BehaviorAtBoundary behaviorAtBoundary, CascadeScope cascadeScope, CascadeOrder cascadeOrder)
+void ElementRuleCollector::collectMatchingRules(const MatchRequest& matchRequest, RuleRange& ruleRange, CascadeScope cascadeScope, CascadeOrder cascadeOrder, bool matchingTreeBoundaryRules)
{
ASSERT(matchRequest.ruleSet);
ASSERT(m_context.element());
const AtomicString& pseudoId = element.shadowPseudoId();
if (!pseudoId.isEmpty()) {
ASSERT(element.isStyledElement());
- collectMatchingRulesForList(matchRequest.ruleSet->shadowPseudoElementRules(pseudoId.impl()), behaviorAtBoundary, ignoreCascadeScope, cascadeOrder, matchRequest, ruleRange);
+ collectMatchingRulesForList(matchRequest.ruleSet->shadowPseudoElementRules(pseudoId), ignoreCascadeScope, cascadeOrder, matchRequest, ruleRange);
}
- if (element.isWebVTTElement())
- collectMatchingRulesForList(matchRequest.ruleSet->cuePseudoRules(), behaviorAtBoundary, cascadeScope, cascadeOrder, matchRequest, ruleRange);
+ if (element.isVTTElement())
+ collectMatchingRulesForList(matchRequest.ruleSet->cuePseudoRules(), cascadeScope, cascadeOrder, matchRequest, ruleRange);
// 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
- // d) the rules comes from a scoped style sheet within an active shadow root whose host is the given element
- // e) the rules can cross boundaries
+ // b) the rules comes from a scoped style sheet within the same tree scope
+ // c) the rules comes from a scoped style sheet within an active shadow root whose host is the given element
+ // d) the rules can cross boundaries
// b)-e) is checked in rulesApplicableInCurrentTreeScope.
- if (!m_matchingUARules && !rulesApplicableInCurrentTreeScope(&element, matchRequest.scope, behaviorAtBoundary, matchRequest.elementApplyAuthorStyles))
+ if (!m_matchingUARules && !rulesApplicableInCurrentTreeScope(&element, matchRequest.scope, matchingTreeBoundaryRules))
return;
// 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()), behaviorAtBoundary, cascadeScope, cascadeOrder, matchRequest, ruleRange);
+ collectMatchingRulesForList(matchRequest.ruleSet->idRules(element.idForStyleResolution()), cascadeScope, cascadeOrder, matchRequest, ruleRange);
if (element.isStyledElement() && element.hasClass()) {
for (size_t i = 0; i < element.classNames().size(); ++i)
- collectMatchingRulesForList(matchRequest.ruleSet->classRules(element.classNames()[i].impl()), behaviorAtBoundary, cascadeScope, cascadeOrder, matchRequest, ruleRange);
+ collectMatchingRulesForList(matchRequest.ruleSet->classRules(element.classNames()[i]), cascadeScope, cascadeOrder, matchRequest, ruleRange);
}
if (element.isLink())
- collectMatchingRulesForList(matchRequest.ruleSet->linkPseudoClassRules(), behaviorAtBoundary, cascadeScope, cascadeOrder, matchRequest, ruleRange);
+ collectMatchingRulesForList(matchRequest.ruleSet->linkPseudoClassRules(), cascadeScope, cascadeOrder, matchRequest, ruleRange);
if (SelectorChecker::matchesFocusPseudoClass(element))
- collectMatchingRulesForList(matchRequest.ruleSet->focusPseudoClassRules(), behaviorAtBoundary, cascadeScope, cascadeOrder, matchRequest, ruleRange);
- collectMatchingRulesForList(matchRequest.ruleSet->tagRules(element.localName().impl()), behaviorAtBoundary, cascadeScope, cascadeOrder, matchRequest, ruleRange);
- collectMatchingRulesForList(matchRequest.ruleSet->universalRules(), behaviorAtBoundary, cascadeScope, cascadeOrder, matchRequest, ruleRange);
+ collectMatchingRulesForList(matchRequest.ruleSet->focusPseudoClassRules(), cascadeScope, cascadeOrder, matchRequest, ruleRange);
+ collectMatchingRulesForList(matchRequest.ruleSet->tagRules(element.localName()), cascadeScope, cascadeOrder, matchRequest, ruleRange);
+ collectMatchingRulesForList(matchRequest.ruleSet->universalRules(), cascadeScope, cascadeOrder, matchRequest, ruleRange);
}
-void ElementRuleCollector::collectMatchingRulesForRegion(const MatchRequest& matchRequest, RuleRange& ruleRange, SelectorChecker::BehaviorAtBoundary behaviorAtBoundary, CascadeScope cascadeScope, CascadeOrder cascadeOrder)
+CSSRuleList* ElementRuleCollector::nestedRuleList(CSSRule* rule)
{
- if (!m_regionForStyling)
- return;
-
- unsigned size = matchRequest.ruleSet->m_regionSelectorsAndRuleSets.size();
- for (unsigned i = 0; i < size; ++i) {
- const CSSSelector* regionSelector = matchRequest.ruleSet->m_regionSelectorsAndRuleSets.at(i).selector;
- if (checkRegionSelector(regionSelector, toElement(m_regionForStyling->node()))) {
- RuleSet* regionRules = matchRequest.ruleSet->m_regionSelectorsAndRuleSets.at(i).ruleSet.get();
- ASSERT(regionRules);
- collectMatchingRules(MatchRequest(regionRules, matchRequest.includeEmptyRules, matchRequest.scope), ruleRange, behaviorAtBoundary, cascadeScope, cascadeOrder);
- }
+ switch (rule->type()) {
+ case CSSRule::MEDIA_RULE:
+ return toCSSMediaRule(rule)->cssRules();
+ case CSSRule::KEYFRAMES_RULE:
+ return toCSSKeyframesRule(rule)->cssRules();
+ case CSSRule::SUPPORTS_RULE:
+ return toCSSSupportsRule(rule)->cssRules();
+ default:
+ return 0;
}
}
-
-static CSSStyleSheet* findStyleSheet(StyleEngine* styleEngine, StyleRule* rule)
+template<class CSSRuleCollection>
+CSSRule* ElementRuleCollector::findStyleRule(CSSRuleCollection* cssRules, StyleRule* styleRule)
{
- // FIXME: StyleEngine has a bunch of different accessors for StyleSheet lists, is this the only one we need to care about?
- const Vector<RefPtr<CSSStyleSheet> >& stylesheets = styleEngine->activeAuthorStyleSheets();
- for (size_t i = 0; i < stylesheets.size(); ++i) {
- CSSStyleSheet* sheet = stylesheets[i].get();
- for (unsigned j = 0; j < sheet->length(); ++j) {
- CSSRule* cssRule = sheet->item(j);
- if (cssRule->type() != CSSRule::STYLE_RULE)
- continue;
+ if (!cssRules)
+ return 0;
+ CSSRule* result = 0;
+ for (unsigned i = 0; i < cssRules->length() && !result; ++i) {
+ CSSRule* cssRule = cssRules->item(i);
+ CSSRule::Type cssRuleType = cssRule->type();
+ if (cssRuleType == CSSRule::STYLE_RULE) {
CSSStyleRule* cssStyleRule = toCSSStyleRule(cssRule);
- if (cssStyleRule->styleRule() == rule)
- return sheet;
+ if (cssStyleRule->styleRule() == styleRule)
+ result = cssRule;
+ } else if (cssRuleType == CSSRule::IMPORT_RULE) {
+ CSSImportRule* cssImportRule = toCSSImportRule(cssRule);
+ result = findStyleRule(cssImportRule->styleSheet(), styleRule);
+ } else {
+ result = findStyleRule(nestedRuleList(cssRule), styleRule);
}
}
- return 0;
+ return result;
}
-void ElementRuleCollector::appendCSSOMWrapperForRule(StyleRule* rule)
+void ElementRuleCollector::appendCSSOMWrapperForRule(CSSStyleSheet* parentStyleSheet, StyleRule* rule)
{
- // FIXME: There should be no codepath that creates a CSSOMWrapper without a parent stylesheet or rule because
- // then that codepath can lead to the CSSStyleSheet contents not getting correctly copied when the rule is modified
- // through the wrapper (e.g. rule.selectorText="div"). Right now, the inspector uses the pointers for identity though,
- // so calling CSSStyleSheet->willMutateRules breaks the inspector.
- CSSStyleSheet* sheet = m_includeStyleSheet == IncludeStyleSheetInCSSOMWrapper ? findStyleSheet(m_context.element()->document().styleEngine(), rule) : 0;
- RefPtr<CSSRule> cssRule = rule->createCSSOMWrapper(sheet);
- if (sheet)
- sheet->registerExtraChildRuleCSSOMWrapper(cssRule);
+ // |parentStyleSheet| is 0 if and only if the |rule| is coming from User Agent. In this case,
+ // it is safe to create CSSOM wrappers without parentStyleSheets as they will be used only
+ // by inspector which will not try to edit them.
+ RefPtrWillBeRawPtr<CSSRule> cssRule = nullptr;
+ if (parentStyleSheet)
+ cssRule = findStyleRule(parentStyleSheet, rule);
+ else
+ cssRule = rule->createCSSOMWrapper();
+ ASSERT(!parentStyleSheet || cssRule);
ensureRuleList()->rules().append(cssRule);
}
sortMatchedRules();
- Vector<MatchedRule, 32>& matchedRules = *m_matchedRules;
+ WillBeHeapVector<MatchedRule, 32>& matchedRules = *m_matchedRules;
if (m_mode == SelectorChecker::CollectingStyleRules) {
for (unsigned i = 0; i < matchedRules.size(); ++i)
ensureStyleRuleList()->m_list.append(matchedRules[i].ruleData()->rule());
if (m_mode == SelectorChecker::CollectingCSSRules) {
for (unsigned i = 0; i < matchedRules.size(); ++i)
- appendCSSOMWrapperForRule(matchedRules[i].ruleData()->rule());
+ appendCSSOMWrapperForRule(const_cast<CSSStyleSheet*>(matchedRules[i].parentStyleSheet()), matchedRules[i].ruleData()->rule());
return;
}
const RuleData* ruleData = matchedRules[i].ruleData();
if (m_style && ruleData->containsUncommonAttributeSelector())
m_style->setUnique();
- m_result.addMatchedProperties(ruleData->rule()->properties(), ruleData->rule(), ruleData->linkMatchType(), ruleData->propertyWhitelistType(m_matchingUARules));
+ m_result.addMatchedProperties(&ruleData->rule()->properties(), ruleData->linkMatchType(), ruleData->propertyWhitelistType(m_matchingUARules));
}
}
-inline bool ElementRuleCollector::ruleMatches(const RuleData& ruleData, const ContainerNode* scope, PseudoId& dynamicPseudo, SelectorChecker::BehaviorAtBoundary behaviorAtBoundary)
+inline bool ElementRuleCollector::ruleMatches(const RuleData& ruleData, const ContainerNode* scope, SelectorChecker::MatchResult* result)
{
- // They can't match because the fast path uses a pool of tag/class/ids, collected from
- // elements in that tree and those will never match the host, since it's in a different pool.
- // So when adding scoped rules to RuleSet, RuleCanUseFastCheckSelector is not used.
- if (ruleData.hasFastCheckableSelector()) {
- // 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.
- ASSERT(m_context.element());
- if (ruleData.hasRightmostSelectorMatchingHTMLBasedOnRuleHash() && m_context.element()->isHTMLElement()) {
- if (!ruleData.hasMultipartSelector())
- return true;
- }
- if (ruleData.selector()->m_match == CSSSelector::Tag && !SelectorChecker::tagMatches(*m_context.element(), ruleData.selector()->tagQName()))
- return false;
- SelectorCheckerFastPath selectorCheckerFastPath(ruleData.selector(), *m_context.element());
- if (!selectorCheckerFastPath.matchesRightmostAttributeSelector())
- return false;
-
- return selectorCheckerFastPath.matches();
- }
-
- // Slow path.
SelectorChecker selectorChecker(m_context.element()->document(), m_mode);
SelectorChecker::SelectorCheckingContext context(ruleData.selector(), m_context.element(), SelectorChecker::VisitedMatchEnabled);
context.elementStyle = m_style.get();
context.pseudoId = m_pseudoStyleRequest.pseudoId;
context.scrollbar = m_pseudoStyleRequest.scrollbar;
context.scrollbarPart = m_pseudoStyleRequest.scrollbarPart;
- context.behaviorAtBoundary = behaviorAtBoundary;
- SelectorChecker::Match match = selectorChecker.match(context, dynamicPseudo, DOMSiblingTraversalStrategy());
+ context.isUARule = m_matchingUARules;
+ context.scopeContainsLastMatchedElement = m_scopeContainsLastMatchedElement;
+ SelectorChecker::Match match = selectorChecker.match(context, DOMSiblingTraversalStrategy(), result);
if (match != SelectorChecker::SelectorMatches)
return false;
- if (m_pseudoStyleRequest.pseudoId != NOPSEUDO && m_pseudoStyleRequest.pseudoId != dynamicPseudo)
+ if (m_pseudoStyleRequest.pseudoId != NOPSEUDO && m_pseudoStyleRequest.pseudoId != result->dynamicPseudo)
return false;
return true;
}
-void ElementRuleCollector::collectRuleIfMatches(const RuleData& ruleData, SelectorChecker::BehaviorAtBoundary behaviorAtBoundary, CascadeScope cascadeScope, CascadeOrder cascadeOrder, const MatchRequest& matchRequest, RuleRange& ruleRange)
+void ElementRuleCollector::collectRuleIfMatches(const RuleData& ruleData, CascadeScope cascadeScope, CascadeOrder cascadeOrder, const MatchRequest& matchRequest, RuleRange& ruleRange)
{
if (m_canUseFastReject && m_selectorFilter.fastRejectSelector<RuleData::maximumIdentifierCount>(ruleData.descendantSelectorIdentifierHashes()))
return;
StyleRule* rule = ruleData.rule();
- PseudoId dynamicPseudo = NOPSEUDO;
- if (ruleMatches(ruleData, matchRequest.scope, dynamicPseudo, behaviorAtBoundary)) {
+ SelectorChecker::MatchResult result;
+ if (ruleMatches(ruleData, matchRequest.scope, &result)) {
// If the rule has no properties to apply, then ignore it in the non-debug mode.
- const StylePropertySet* properties = rule->properties();
- if (!properties || (properties->isEmpty() && !matchRequest.includeEmptyRules))
+ const StylePropertySet& properties = rule->properties();
+ if (properties.isEmpty() && !matchRequest.includeEmptyRules)
return;
// FIXME: Exposing the non-standard getMatchedCSSRules API to web is the only reason this is needed.
if (m_sameOriginOnly && !ruleData.hasDocumentSecurityOrigin())
return;
+
+ PseudoId dynamicPseudo = result.dynamicPseudo;
// 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::CollectingCSSRules || m_mode == SelectorChecker::CollectingStyleRules)
return;
// FIXME: Matching should not modify the style directly.
- if (m_style && dynamicPseudo < FIRST_INTERNAL_PSEUDOID)
- m_style->setHasPseudoStyle(dynamicPseudo);
+ if (!m_style || dynamicPseudo >= FIRST_INTERNAL_PSEUDOID)
+ return;
+ if ((dynamicPseudo == BEFORE || dynamicPseudo == AFTER) && !ruleData.rule()->properties().hasProperty(CSSPropertyContent))
+ return;
+ m_style->setHasPseudoStyle(dynamicPseudo);
} else {
// Update our first/last rule indices in the matched rules array.
++ruleRange.lastRuleIndex;
ruleRange.firstRuleIndex = ruleRange.lastRuleIndex;
// Add this rule to our list of matched rules.
- addMatchedRule(&ruleData, cascadeScope, cascadeOrder);
+ addMatchedRule(&ruleData, result.specificity, cascadeScope, cascadeOrder, matchRequest.styleSheetIndex, matchRequest.styleSheet);
return;
}
}
}
-void ElementRuleCollector::collectMatchingRulesForList(const RuleData* rules, SelectorChecker::BehaviorAtBoundary behaviorAtBoundary, CascadeScope cascadeScope, CascadeOrder cascadeOrder, const MatchRequest& matchRequest, RuleRange& ruleRange)
+static inline bool compareRules(const MatchedRule& matchedRule1, const MatchedRule& matchedRule2)
{
- if (!rules)
- return;
- while (!rules->isLastInArray())
- collectRuleIfMatches(*rules++, behaviorAtBoundary, cascadeScope, cascadeOrder, matchRequest, ruleRange);
- collectRuleIfMatches(*rules, behaviorAtBoundary, cascadeScope, cascadeOrder, matchRequest, ruleRange);
-}
+ if (matchedRule1.cascadeScope() != matchedRule2.cascadeScope())
+ return matchedRule1.cascadeScope() > matchedRule2.cascadeScope();
-void ElementRuleCollector::collectMatchingRulesForList(const Vector<RuleData>* rules, SelectorChecker::BehaviorAtBoundary behaviorAtBoundary, CascadeScope cascadeScope, CascadeOrder cascadeOrder, const MatchRequest& matchRequest, RuleRange& ruleRange)
-{
- if (!rules)
- return;
- unsigned size = rules->size();
- for (unsigned i = 0; i < size; ++i)
- collectRuleIfMatches(rules->at(i), behaviorAtBoundary, cascadeScope, cascadeOrder, matchRequest, ruleRange);
-}
+ unsigned specificity1 = matchedRule1.specificity();
+ unsigned specificity2 = matchedRule2.specificity();
+ if (specificity1 != specificity2)
+ return specificity1 < specificity2;
-static inline bool compareRules(const MatchedRule& matchedRule1, const MatchedRule& matchedRule2)
-{
- unsigned specificity1 = matchedRule1.ruleData()->specificity();
- unsigned specificity2 = matchedRule2.ruleData()->specificity();
- return matchedRule1.cascadeScope() == matchedRule2.cascadeScope() ?
- ((specificity1 == specificity2) ? matchedRule1.position() < matchedRule2.position() : specificity1 < specificity2) :
- matchedRule1.cascadeScope() > matchedRule2.cascadeScope();
+ return matchedRule1.position() < matchedRule2.position();
}
void ElementRuleCollector::sortMatchedRules()
int firstRuleIndex = -1, lastRuleIndex = -1;
RuleRange ruleRange(firstRuleIndex, lastRuleIndex);
// FIXME: Verify whether it's ok to ignore CascadeScope here.
- collectMatchingRules(MatchRequest(ruleSet), ruleRange, SelectorChecker::StaysWithinTreeScope);
+ collectMatchingRules(MatchRequest(ruleSet), ruleRange);
return m_matchedRules && !m_matchedRules->isEmpty();
}
-} // namespace WebCore
+} // namespace blink