This source file includes following definitions.
- m_matchingUARules
- matchedResult
- matchedStyleRuleList
- matchedCSSRuleList
- addMatchedRule
- clearMatchedRules
- ensureStyleRuleList
- ensureRuleList
- addElementStyleProperties
- rulesApplicableInCurrentTreeScope
- collectMatchingRules
- nestedRuleList
- findStyleRule
- appendCSSOMWrapperForRule
- sortAndTransferMatchedRules
- ruleMatches
- collectRuleIfMatches
- compareRules
- sortMatchedRules
- hasAnyMatchingRules
#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/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"
namespace WebCore {
ElementRuleCollector::ElementRuleCollector(const ElementResolveContext& context,
const SelectorFilter& filter, RenderStyle* style)
: m_context(context)
, m_selectorFilter(filter)
, m_style(style)
, m_pseudoStyleRequest(NOPSEUDO)
, m_mode(SelectorChecker::ResolvingStyle)
, m_canUseFastReject(m_selectorFilter.parentStackIsConsistent(context.parentNode()))
, m_sameOriginOnly(false)
, m_matchingUARules(false)
{ }
ElementRuleCollector::~ElementRuleCollector()
{
}
MatchResult& ElementRuleCollector::matchedResult()
{
return m_result;
}
PassRefPtrWillBeRawPtr<StyleRuleList> ElementRuleCollector::matchedStyleRuleList()
{
ASSERT(m_mode == SelectorChecker::CollectingStyleRules);
return m_styleRuleList.release();
}
PassRefPtrWillBeRawPtr<CSSRuleList> ElementRuleCollector::matchedCSSRuleList()
{
ASSERT(m_mode == SelectorChecker::CollectingCSSRules);
return m_cssRuleList.release();
}
inline void ElementRuleCollector::addMatchedRule(const RuleData* rule, unsigned specificity, CascadeScope cascadeScope, CascadeOrder cascadeOrder, unsigned styleSheetIndex, const CSSStyleSheet* parentStyleSheet)
{
if (!m_matchedRules)
m_matchedRules = adoptPtrWillBeNoop(new WillBeHeapVector<MatchedRule, 32>);
m_matchedRules->append(MatchedRule(rule, specificity, cascadeScope, cascadeOrder, styleSheetIndex, parentStyleSheet));
}
void ElementRuleCollector::clearMatchedRules()
{
if (!m_matchedRules)
return;
m_matchedRules->clear();
}
inline StyleRuleList* ElementRuleCollector::ensureStyleRuleList()
{
if (!m_styleRuleList)
m_styleRuleList = StyleRuleList::create();
return m_styleRuleList.get();
}
inline StaticCSSRuleList* ElementRuleCollector::ensureRuleList()
{
if (!m_cssRuleList)
m_cssRuleList = StaticCSSRuleList::create();
return m_cssRuleList.get();
}
void ElementRuleCollector::addElementStyleProperties(const StylePropertySet* propertySet, bool isCacheable)
{
if (!propertySet)
return;
m_result.ranges.authorRuleRange().shiftLast(m_result.matchedProperties.size());
m_result.addMatchedProperties(propertySet);
if (!isCacheable)
m_result.isCacheable = false;
}
static bool rulesApplicableInCurrentTreeScope(const Element* element, const ContainerNode* scopingNode, SelectorChecker::BehaviorAtBoundary behaviorAtBoundary, bool elementApplyAuthorStyles)
{
TreeScope& treeScope = element->treeScope();
if (elementApplyAuthorStyles)
return true;
if (!scopingNode || treeScope == scopingNode->treeScope())
return true;
if (element->isInShadowTree() && (behaviorAtBoundary & SelectorChecker::ScopeIsShadowHost) && scopingNode == element->containingShadowRoot()->host())
return true;
return false;
}
void ElementRuleCollector::collectMatchingRules(const MatchRequest& matchRequest, RuleRange& ruleRange, SelectorChecker::BehaviorAtBoundary behaviorAtBoundary, CascadeScope cascadeScope, CascadeOrder cascadeOrder)
{
ASSERT(matchRequest.ruleSet);
ASSERT(m_context.element());
Element& element = *m_context.element();
const AtomicString& pseudoId = element.shadowPseudoId();
if (!pseudoId.isEmpty()) {
ASSERT(element.isStyledElement());
collectMatchingRulesForList(matchRequest.ruleSet->shadowPseudoElementRules(pseudoId), behaviorAtBoundary, ignoreCascadeScope, cascadeOrder, matchRequest, ruleRange);
}
if (element.isVTTElement())
collectMatchingRulesForList(matchRequest.ruleSet->cuePseudoRules(), behaviorAtBoundary, cascadeScope, cascadeOrder, matchRequest, ruleRange);
if (!m_matchingUARules && !rulesApplicableInCurrentTreeScope(&element, matchRequest.scope, behaviorAtBoundary, matchRequest.elementApplyAuthorStyles))
return;
if (element.hasID())
collectMatchingRulesForList(matchRequest.ruleSet->idRules(element.idForStyleResolution()), behaviorAtBoundary, 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]), behaviorAtBoundary, cascadeScope, cascadeOrder, matchRequest, ruleRange);
}
if (element.isLink())
collectMatchingRulesForList(matchRequest.ruleSet->linkPseudoClassRules(), behaviorAtBoundary, cascadeScope, cascadeOrder, matchRequest, ruleRange);
if (SelectorChecker::matchesFocusPseudoClass(element))
collectMatchingRulesForList(matchRequest.ruleSet->focusPseudoClassRules(), behaviorAtBoundary, cascadeScope, cascadeOrder, matchRequest, ruleRange);
collectMatchingRulesForList(matchRequest.ruleSet->tagRules(element.localName()), behaviorAtBoundary, cascadeScope, cascadeOrder, matchRequest, ruleRange);
collectMatchingRulesForList(matchRequest.ruleSet->universalRules(), behaviorAtBoundary, cascadeScope, cascadeOrder, matchRequest, ruleRange);
}
CSSRuleList* ElementRuleCollector::nestedRuleList(CSSRule* rule)
{
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;
}
}
template<class CSSRuleCollection>
CSSRule* ElementRuleCollector::findStyleRule(CSSRuleCollection* cssRules, StyleRule* styleRule)
{
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() == 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 result;
}
void ElementRuleCollector::appendCSSOMWrapperForRule(CSSStyleSheet* parentStyleSheet, StyleRule* rule)
{
RefPtrWillBeRawPtr<CSSRule> cssRule = nullptr;
if (parentStyleSheet)
cssRule = findStyleRule(parentStyleSheet, rule);
else
cssRule = rule->createCSSOMWrapper();
ASSERT(!parentStyleSheet || cssRule);
ensureRuleList()->rules().append(cssRule);
}
void ElementRuleCollector::sortAndTransferMatchedRules()
{
if (!m_matchedRules || m_matchedRules->isEmpty())
return;
sortMatchedRules();
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());
return;
}
if (m_mode == SelectorChecker::CollectingCSSRules) {
for (unsigned i = 0; i < matchedRules.size(); ++i)
appendCSSOMWrapperForRule(const_cast<CSSStyleSheet*>(matchedRules[i].parentStyleSheet()), matchedRules[i].ruleData()->rule());
return;
}
for (unsigned i = 0; i < matchedRules.size(); i++) {
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));
}
}
inline bool ElementRuleCollector::ruleMatches(const RuleData& ruleData, const ContainerNode* scope, SelectorChecker::BehaviorAtBoundary behaviorAtBoundary, SelectorChecker::MatchResult* result)
{
SelectorChecker selectorChecker(m_context.element()->document(), m_mode);
SelectorChecker::SelectorCheckingContext context(ruleData.selector(), m_context.element(), SelectorChecker::VisitedMatchEnabled);
context.elementStyle = m_style.get();
context.scope = scope;
context.pseudoId = m_pseudoStyleRequest.pseudoId;
context.scrollbar = m_pseudoStyleRequest.scrollbar;
context.scrollbarPart = m_pseudoStyleRequest.scrollbarPart;
context.behaviorAtBoundary = behaviorAtBoundary;
SelectorChecker::Match match = selectorChecker.match(context, DOMSiblingTraversalStrategy(), result);
if (match != SelectorChecker::SelectorMatches)
return false;
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)
{
if (m_canUseFastReject && m_selectorFilter.fastRejectSelector<RuleData::maximumIdentifierCount>(ruleData.descendantSelectorIdentifierHashes()))
return;
StyleRule* rule = ruleData.rule();
SelectorChecker::MatchResult result;
if (ruleMatches(ruleData, matchRequest.scope, behaviorAtBoundary, &result)) {
const StylePropertySet& properties = rule->properties();
if (properties.isEmpty() && !matchRequest.includeEmptyRules)
return;
if (m_sameOriginOnly && !ruleData.hasDocumentSecurityOrigin())
return;
PseudoId dynamicPseudo = result.dynamicPseudo;
if (dynamicPseudo != NOPSEUDO && m_pseudoStyleRequest.pseudoId == NOPSEUDO) {
if (m_mode == SelectorChecker::CollectingCSSRules || m_mode == SelectorChecker::CollectingStyleRules)
return;
if (m_style && dynamicPseudo < FIRST_INTERNAL_PSEUDOID)
m_style->setHasPseudoStyle(dynamicPseudo);
} else {
ruleRange.shiftLastByOne();
addMatchedRule(&ruleData, result.specificity, cascadeScope, cascadeOrder, matchRequest.styleSheetIndex, matchRequest.styleSheet);
return;
}
}
}
static inline bool compareRules(const MatchedRule& matchedRule1, const MatchedRule& matchedRule2)
{
if (matchedRule1.cascadeScope() != matchedRule2.cascadeScope())
return matchedRule1.cascadeScope() > matchedRule2.cascadeScope();
unsigned specificity1 = matchedRule1.specificity();
unsigned specificity2 = matchedRule2.specificity();
if (specificity1 != specificity2)
return specificity1 < specificity2;
return matchedRule1.position() < matchedRule2.position();
}
void ElementRuleCollector::sortMatchedRules()
{
ASSERT(m_matchedRules);
std::sort(m_matchedRules->begin(), m_matchedRules->end(), compareRules);
}
bool ElementRuleCollector::hasAnyMatchingRules(RuleSet* ruleSet)
{
clearMatchedRules();
m_mode = SelectorChecker::SharingRules;
RuleRange ruleRange;
collectMatchingRules(MatchRequest(ruleSet), ruleRange, SelectorChecker::StaysWithinTreeScope);
return m_matchedRules && !m_matchedRules->isEmpty();
}
}