https://bugs.webkit.org/show_bug.cgi?id=68633
Reviewed by Dave Hyatt, Darin Adler, Dimitri Glazkov.
:link, :visited and :focus are quite common. They often used as univeral selectors (including in our
default stylesheet) so we try to match them for all elements in the document. They take always the
slow matching path. In addition we match link styles twice due to visited link pseudo style generation
so the overhead is doubled. As a result substantial portion of our style matching time is spent
dealing with these pseudo classes.
This patch adds new lists to RuleSet for common pseudo class rules. The rules on the lists are only checked
if the element has approprate type and stat. ases where the rightmost pseudo class can then be rejected immediately.
We can also enable the fast path checking for the rest of the selector in many cases.
This seems to be >30% progression in selector matching performance with typical style sheets. It saves ~0.9s
when loading the full HTML5 spec.
* css/CSSStyleSelector.cpp:
(WebCore::RuleData::hasRightmostSelectorMatchingHTMLBasedOnRuleHash):
(WebCore::RuleSet::idRules):
(WebCore::RuleSet::classRules):
(WebCore::RuleSet::tagRules):
(WebCore::RuleSet::shadowPseudoElementRules):
(WebCore::RuleSet::linkPseudoClassRules):
(WebCore::RuleSet::visitedPseudoClassRules):
(WebCore::RuleSet::focusPseudoClassRules):
(WebCore::RuleSet::universalRules):
(WebCore::RuleSet::pageRules):
Add a new lists, some stylistic renamings.
(WebCore::CSSStyleSelector::matchRules):
New link and focus checks.
(WebCore::CSSStyleSelector::matchRulesForList):
(WebCore::CSSStyleSelector::checkSelector):
Inline the rightmost selector tag checking, skip if unnecessary.
(WebCore::isSelectorMatchingHTMLBasedOnRuleHash):
Common pseudo classes now match based on early filtering (though it is not a hash in this case).
(WebCore::RuleData::RuleData):
(WebCore::RuleSet::~RuleSet):
(WebCore::RuleSet::addRule):
Sort pseudo classes to new lists.
(WebCore::RuleSet::collectFeatures):
(WebCore::RuleSet::shrinkToFit):
(WebCore::CSSStyleSelector::matchPageRules):
* css/SelectorChecker.cpp:
(WebCore::SelectorChecker::checkSelector):
Adopt to expanded fast path (this is used by querySelectorAll).
(WebCore::SelectorChecker::fastCheckRightmostSelector):
(WebCore::SelectorChecker::fastCheckSelector):
Rightmost selector is now checked differently than the rest. RuleSet based selection in CSSStyleSelector
is equivalent to fastCheckRightmostSelector().
(WebCore::isFastCheckableRelation):
(WebCore::isFastCheckableMatch):
(WebCore::isFastCheckableRightmostSelector):
(WebCore::SelectorChecker::isFastCheckableSelector):
(WebCore::SelectorChecker::checkOneSelector):
(WebCore::SelectorChecker::commonPseudoClassSelectorMatches):
(WebCore::SelectorChecker::isFrameFocused):
* css/SelectorChecker.h:
(WebCore::SelectorChecker::isCommonPseudoClassSelector):
(WebCore::SelectorChecker::linkMatchesVisitedPseudoClass):
(WebCore::SelectorChecker::matchesFocusPseudoClass):
(WebCore::SelectorChecker::tagMatches):
Refactor a bunch of shared checks into functions.
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@95966
268f45cc-cd09-0410-ab3c-
d52691b4dbfc
+2011-09-26 Antti Koivisto <antti@apple.com>
+
+ Optimize matching of common pseudo classes
+ https://bugs.webkit.org/show_bug.cgi?id=68633
+
+ Reviewed by Dave Hyatt, Darin Adler, Dimitri Glazkov.
+
+ :link, :visited and :focus are quite common. They often used as univeral selectors (including in our
+ default stylesheet) so we try to match them for all elements in the document. They take always the
+ slow matching path. In addition we match link styles twice due to visited link pseudo style generation
+ so the overhead is doubled. As a result substantial portion of our style matching time is spent
+ dealing with these pseudo classes.
+
+ This patch adds new lists to RuleSet for common pseudo class rules. The rules on the lists are only checked
+ if the element has approprate type and stat. ases where the rightmost pseudo class can then be rejected immediately.
+ We can also enable the fast path checking for the rest of the selector in many cases.
+
+ This seems to be >30% progression in selector matching performance with typical style sheets. It saves ~0.9s
+ when loading the full HTML5 spec.
+
+ * css/CSSStyleSelector.cpp:
+ (WebCore::RuleData::hasRightmostSelectorMatchingHTMLBasedOnRuleHash):
+ (WebCore::RuleSet::idRules):
+ (WebCore::RuleSet::classRules):
+ (WebCore::RuleSet::tagRules):
+ (WebCore::RuleSet::shadowPseudoElementRules):
+ (WebCore::RuleSet::linkPseudoClassRules):
+ (WebCore::RuleSet::visitedPseudoClassRules):
+ (WebCore::RuleSet::focusPseudoClassRules):
+ (WebCore::RuleSet::universalRules):
+ (WebCore::RuleSet::pageRules):
+
+ Add a new lists, some stylistic renamings.
+
+ (WebCore::CSSStyleSelector::matchRules):
+
+ New link and focus checks.
+
+ (WebCore::CSSStyleSelector::matchRulesForList):
+ (WebCore::CSSStyleSelector::checkSelector):
+
+ Inline the rightmost selector tag checking, skip if unnecessary.
+
+ (WebCore::isSelectorMatchingHTMLBasedOnRuleHash):
+
+ Common pseudo classes now match based on early filtering (though it is not a hash in this case).
+
+ (WebCore::RuleData::RuleData):
+ (WebCore::RuleSet::~RuleSet):
+ (WebCore::RuleSet::addRule):
+
+ Sort pseudo classes to new lists.
+
+ (WebCore::RuleSet::collectFeatures):
+ (WebCore::RuleSet::shrinkToFit):
+ (WebCore::CSSStyleSelector::matchPageRules):
+ * css/SelectorChecker.cpp:
+ (WebCore::SelectorChecker::checkSelector):
+
+ Adopt to expanded fast path (this is used by querySelectorAll).
+
+ (WebCore::SelectorChecker::fastCheckRightmostSelector):
+ (WebCore::SelectorChecker::fastCheckSelector):
+
+ Rightmost selector is now checked differently than the rest. RuleSet based selection in CSSStyleSelector
+ is equivalent to fastCheckRightmostSelector().
+
+ (WebCore::isFastCheckableRelation):
+ (WebCore::isFastCheckableMatch):
+ (WebCore::isFastCheckableRightmostSelector):
+ (WebCore::SelectorChecker::isFastCheckableSelector):
+ (WebCore::SelectorChecker::checkOneSelector):
+ (WebCore::SelectorChecker::commonPseudoClassSelectorMatches):
+ (WebCore::SelectorChecker::isFrameFocused):
+ * css/SelectorChecker.h:
+ (WebCore::SelectorChecker::isCommonPseudoClassSelector):
+ (WebCore::SelectorChecker::linkMatchesVisitedPseudoClass):
+ (WebCore::SelectorChecker::matchesFocusPseudoClass):
+ (WebCore::SelectorChecker::tagMatches):
+
+ Refactor a bunch of shared checks into functions.
+
2011-09-12 Ryosuke Niwa <rniwa@webkit.org>
REGRESSION(r74971): Selection doesn't work correctly in BiDi Text
bool hasFastCheckableSelector() const { return m_hasFastCheckableSelector; }
bool hasMultipartSelector() const { return m_hasMultipartSelector; }
- bool hasTopSelectorMatchingHTMLBasedOnRuleHash() const { return m_hasTopSelectorMatchingHTMLBasedOnRuleHash; }
+ bool hasRightmostSelectorMatchingHTMLBasedOnRuleHash() const { return m_hasRightmostSelectorMatchingHTMLBasedOnRuleHash; }
unsigned specificity() const { return m_specificity; }
// Try to balance between memory usage (there can be lots of RuleData objects) and good filtering performance.
unsigned m_position : 29;
bool m_hasFastCheckableSelector : 1;
bool m_hasMultipartSelector : 1;
- bool m_hasTopSelectorMatchingHTMLBasedOnRuleHash : 1;
+ bool m_hasRightmostSelectorMatchingHTMLBasedOnRuleHash : 1;
// Use plain array instead of a Vector to minimize memory overhead.
unsigned m_descendantSelectorIdentifierHashes[maximumIdentifierCount];
};
void collectFeatures(CSSStyleSelector::Features&) const;
- const Vector<RuleData>* getIDRules(AtomicStringImpl* key) const { return m_idRules.get(key); }
- const Vector<RuleData>* getClassRules(AtomicStringImpl* key) const { return m_classRules.get(key); }
- const Vector<RuleData>* getTagRules(AtomicStringImpl* key) const { return m_tagRules.get(key); }
- const Vector<RuleData>* getPseudoRules(AtomicStringImpl* key) const { return m_pseudoRules.get(key); }
- const Vector<RuleData>* getUniversalRules() const { return &m_universalRules; }
- const Vector<RuleData>* getPageRules() const { return &m_pageRules; }
+ const Vector<RuleData>* idRules(AtomicStringImpl* key) const { return m_idRules.get(key); }
+ const Vector<RuleData>* classRules(AtomicStringImpl* key) const { return m_classRules.get(key); }
+ const Vector<RuleData>* tagRules(AtomicStringImpl* key) const { return m_tagRules.get(key); }
+ const Vector<RuleData>* shadowPseudoElementRules(AtomicStringImpl* key) const { return m_shadowPseudoElementRules.get(key); }
+ const Vector<RuleData>* linkPseudoClassRules() const { return &m_linkPseudoClassRules; }
+ const Vector<RuleData>* visitedPseudoClassRules() const { return &m_visitedPseudoClassRules; }
+ const Vector<RuleData>* focusPseudoClassRules() const { return &m_focusPseudoClassRules; }
+ const Vector<RuleData>* universalRules() const { return &m_universalRules; }
+ const Vector<RuleData>* pageRules() const { return &m_pageRules; }
public:
AtomRuleMap m_idRules;
AtomRuleMap m_classRules;
AtomRuleMap m_tagRules;
- AtomRuleMap m_pseudoRules;
+ AtomRuleMap m_shadowPseudoElementRules;
+ Vector<RuleData> m_linkPseudoClassRules;
+ Vector<RuleData> m_visitedPseudoClassRules;
+ Vector<RuleData> m_focusPseudoClassRules;
Vector<RuleData> m_universalRules;
Vector<RuleData> m_pageRules;
unsigned m_ruleCount;
// We need to collect the rules for id, class, tag, and everything else into a buffer and
// then sort the buffer.
if (m_element->hasID())
- matchRulesForList(rules->getIDRules(m_element->idForStyleResolution().impl()), firstRuleIndex, lastRuleIndex, includeEmptyRules);
+ matchRulesForList(rules->idRules(m_element->idForStyleResolution().impl()), firstRuleIndex, lastRuleIndex, includeEmptyRules);
if (m_element->hasClass()) {
ASSERT(m_styledElement);
const SpaceSplitString& classNames = m_styledElement->classNames();
size_t size = classNames.size();
for (size_t i = 0; i < size; ++i)
- matchRulesForList(rules->getClassRules(classNames[i].impl()), firstRuleIndex, lastRuleIndex, includeEmptyRules);
+ matchRulesForList(rules->classRules(classNames[i].impl()), firstRuleIndex, lastRuleIndex, includeEmptyRules);
}
const AtomicString& pseudoId = m_element->shadowPseudoId();
if (!pseudoId.isEmpty()) {
ASSERT(m_styledElement);
- matchRulesForList(rules->getPseudoRules(pseudoId.impl()), firstRuleIndex, lastRuleIndex, includeEmptyRules);
+ matchRulesForList(rules->shadowPseudoElementRules(pseudoId.impl()), firstRuleIndex, lastRuleIndex, includeEmptyRules);
}
- matchRulesForList(rules->getTagRules(m_element->localName().impl()), firstRuleIndex, lastRuleIndex, includeEmptyRules);
- matchRulesForList(rules->getUniversalRules(), firstRuleIndex, lastRuleIndex, includeEmptyRules);
+ if (m_element->isLink()) {
+ if (m_checker.linkMatchesVisitedPseudoClass(m_element))
+ matchRulesForList(rules->visitedPseudoClassRules(), firstRuleIndex, lastRuleIndex, includeEmptyRules);
+ else
+ matchRulesForList(rules->linkPseudoClassRules(), firstRuleIndex, lastRuleIndex, includeEmptyRules);
+ }
+ if (m_checker.matchesFocusPseudoClass(m_element))
+ matchRulesForList(rules->focusPseudoClassRules(), firstRuleIndex, lastRuleIndex, includeEmptyRules);
+ matchRulesForList(rules->tagRules(m_element->localName().impl()), firstRuleIndex, lastRuleIndex, includeEmptyRules);
+ matchRulesForList(rules->universalRules(), firstRuleIndex, lastRuleIndex, includeEmptyRules);
// If we didn't match any rules, we're done.
if (m_matchedRules.isEmpty())
// Let the slow path handle SVG as it has some additional rules regarding shadow trees.
if (ruleData.hasFastCheckableSelector() && !m_element->isSVGElement()) {
- // We know this selector does not include any pseudo selectors.
+ // We know this selector does not include any pseudo elements.
if (m_checker.pseudoStyle() != 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.hasTopSelectorMatchingHTMLBasedOnRuleHash() && !ruleData.hasMultipartSelector() && m_element->isHTMLElement())
- return true;
- return SelectorChecker::fastCheckSelector(ruleData.selector(), m_element);
+ if (ruleData.hasRightmostSelectorMatchingHTMLBasedOnRuleHash() && m_element->isHTMLElement()) {
+ if (!ruleData.hasMultipartSelector())
+ return true;
+ } else if (!SelectorChecker::tagMatches(m_element, ruleData.selector()))
+ return false;
+ return m_checker.fastCheckSelector(ruleData.selector(), m_element);
}
// Slow path.
return false;
if (selector->m_match == CSSSelector::None)
return true;
- if (selector->m_match != CSSSelector::Id && selector->m_match != CSSSelector::Class)
+ if (selector->tag() != starAtom)
return false;
- return selector->tag() == starAtom;
+ if (SelectorChecker::isCommonPseudoClassSelector(selector))
+ return true;
+ return selector->m_match == CSSSelector::Id || selector->m_match == CSSSelector::Class;
}
RuleData::RuleData(CSSStyleRule* rule, CSSSelector* selector, unsigned position)
, m_position(position)
, m_hasFastCheckableSelector(SelectorChecker::isFastCheckableSelector(selector))
, m_hasMultipartSelector(selector->tagHistory())
- , m_hasTopSelectorMatchingHTMLBasedOnRuleHash(isSelectorMatchingHTMLBasedOnRuleHash(selector))
+ , m_hasRightmostSelectorMatchingHTMLBasedOnRuleHash(isSelectorMatchingHTMLBasedOnRuleHash(selector))
{
SelectorChecker::collectIdentifierHashes(m_selector, m_descendantSelectorIdentifierHashes, maximumIdentifierCount);
}
{
deleteAllValues(m_idRules);
deleteAllValues(m_classRules);
- deleteAllValues(m_pseudoRules);
+ deleteAllValues(m_shadowPseudoElementRules);
deleteAllValues(m_tagRules);
}
addToRuleSet(sel->value().impl(), m_classRules, rule, sel);
return;
}
-
if (sel->isUnknownPseudoElement()) {
- addToRuleSet(sel->value().impl(), m_pseudoRules, rule, sel);
+ addToRuleSet(sel->value().impl(), m_shadowPseudoElementRules, rule, sel);
+ return;
+ }
+ if (SelectorChecker::isCommonPseudoClassSelector(sel)) {
+ RuleData ruleData(rule, sel, m_ruleCount++);
+ switch (sel->pseudoType()) {
+ case CSSSelector::PseudoLink:
+ m_linkPseudoClassRules.append(ruleData);
+ return;
+ case CSSSelector::PseudoVisited:
+ m_visitedPseudoClassRules.append(ruleData);
+ return;
+ case CSSSelector::PseudoAnyLink:
+ // :link and :visited are mutually exclusive. :any-link will apply if and only if one of them applies.
+ m_linkPseudoClassRules.append(ruleData);
+ m_visitedPseudoClassRules.append(ruleData);
+ return;
+ case CSSSelector::PseudoFocus:
+ m_focusPseudoClassRules.append(ruleData);
+ return;
+ default:
+ ASSERT_NOT_REACHED();
+ }
return;
}
-
const AtomicString& localName = sel->tag().localName();
if (localName != starAtom) {
addToRuleSet(localName.impl(), m_tagRules, rule, sel);
end = m_tagRules.end();
for (AtomRuleMap::const_iterator it = m_tagRules.begin(); it != end; ++it)
collectFeaturesFromList(features, *it->second);
- end = m_pseudoRules.end();
- for (AtomRuleMap::const_iterator it = m_pseudoRules.begin(); it != end; ++it)
+ end = m_shadowPseudoElementRules.end();
+ for (AtomRuleMap::const_iterator it = m_shadowPseudoElementRules.begin(); it != end; ++it)
collectFeaturesFromList(features, *it->second);
+ collectFeaturesFromList(features, m_linkPseudoClassRules);
+ collectFeaturesFromList(features, m_visitedPseudoClassRules);
+ collectFeaturesFromList(features, m_focusPseudoClassRules);
collectFeaturesFromList(features, m_universalRules);
}
shrinkMapVectorsToFit(m_idRules);
shrinkMapVectorsToFit(m_classRules);
shrinkMapVectorsToFit(m_tagRules);
- shrinkMapVectorsToFit(m_pseudoRules);
+ shrinkMapVectorsToFit(m_shadowPseudoElementRules);
+ m_linkPseudoClassRules.shrinkToFit();
+ m_visitedPseudoClassRules.shrinkToFit();
+ m_focusPseudoClassRules.shrinkToFit();
m_universalRules.shrinkToFit();
m_pageRules.shrinkToFit();
}
if (!rules)
return;
- matchPageRulesForList(rules->getPageRules(), isLeftPage, isFirstPage, pageName);
+ matchPageRulesForList(rules->pageRules(), isLeftPage, isFirstPage, pageName);
// If we didn't match any rules, we're done.
if (m_matchedRules.isEmpty())
#include "Document.h"
#include "FocusController.h"
#include "Frame.h"
+#include "FrameSelection.h"
#include "HTMLFrameElementBase.h"
#include "HTMLInputElement.h"
#include "HTMLNames.h"
{
PseudoId dynamicPseudo = NOPSEUDO;
if (isFastCheckableSelector && !element->isSVGElement()) {
- // fastCheckSelector assumes class and id match for the top selector.
- if (sel->m_match == CSSSelector::Class) {
- if (!(element->hasClass() && static_cast<StyledElement*>(element)->classNames().contains(sel->value())))
- return false;
- } else if (sel->m_match == CSSSelector::Id) {
- if (!(element->hasID() && element->idForStyleResolution() == sel->value()))
- return false;
- }
+ if (!fastCheckRightmostSelector(sel, element))
+ return false;
return fastCheckSelector(sel, element);
}
return checkSelector(sel, element, dynamicPseudo, false, false) == SelectorMatches;
}
namespace {
-
-inline bool selectorTagMatches(const Element* element, const CSSSelector* selector)
-{
- if (!selector->hasTag())
- return true;
- const AtomicString& localName = selector->tag().localName();
- if (localName != starAtom && localName != element->localName())
- return false;
- const AtomicString& namespaceURI = selector->tag().namespaceURI();
- return namespaceURI == starAtom || namespaceURI == element->namespaceURI();
-}
template <bool checkValue(const Element*, AtomicStringImpl*)>
inline bool fastCheckSingleSelector(const CSSSelector*& selector, const Element*& element, const CSSSelector*& topChildOrSubselector, const Element*& topChildOrSubselectorMatchElement)
{
AtomicStringImpl* value = selector->value().impl();
for (; element; element = element->parentElement()) {
- if (checkValue(element, value) && selectorTagMatches(element, selector)) {
+ if (checkValue(element, value) && SelectorChecker::tagMatches(element, selector)) {
if (selector->relation() == CSSSelector::Descendant)
topChildOrSubselector = 0;
else if (!topChildOrSubselector) {
}
-bool SelectorChecker::fastCheckSelector(const CSSSelector* selector, const Element* element)
+inline bool SelectorChecker::fastCheckRightmostSelector(const CSSSelector* selector, const Element* element) const
{
ASSERT(isFastCheckableSelector(selector));
-
- // The top selector requires tag check only as rule hashes have already handled id and class matches.
- if (!selectorTagMatches(element, selector))
+
+ if (!SelectorChecker::tagMatches(element, selector))
return false;
-
+ switch (selector->m_match) {
+ case CSSSelector::None:
+ return true;
+ case CSSSelector::Class:
+ return element->hasClass() && static_cast<const StyledElement*>(element)->classNames().contains(selector->value());
+ case CSSSelector::Id:
+ return element->hasID() && element->idForStyleResolution() == selector->value();
+ case CSSSelector::PseudoClass:
+ return commonPseudoClassSelectorMatches(element, selector);
+ default:
+ ASSERT_NOT_REACHED();
+ }
+ return false;
+}
+
+bool SelectorChecker::fastCheckSelector(const CSSSelector* selector, const Element* element) const
+{
+ ASSERT(fastCheckRightmostSelector(selector, element));
+
const CSSSelector* topChildOrSubselector = 0;
const Element* topChildOrSubselectorMatchElement = 0;
if (selector->relation() == CSSSelector::Child || selector->relation() == CSSSelector::SubSelector)
return true;
}
+static inline bool isFastCheckableRelation(CSSSelector::Relation relation)
+{
+ return relation == CSSSelector::Descendant || relation == CSSSelector::Child || relation == CSSSelector::SubSelector;
+}
+
+static inline bool isFastCheckableMatch(CSSSelector::Match match)
+{
+ return match == CSSSelector::None || match == CSSSelector::Id || match == CSSSelector::Class;
+}
+
+static inline bool isFastCheckableRightmostSelector(const CSSSelector* selector)
+{
+ if (!isFastCheckableRelation(selector->relation()))
+ return false;
+ return isFastCheckableMatch(static_cast<CSSSelector::Match>(selector->m_match)) || SelectorChecker::isCommonPseudoClassSelector(selector);
+}
+
bool SelectorChecker::isFastCheckableSelector(const CSSSelector* selector)
{
- for (; selector; selector = selector->tagHistory()) {
- if (selector->relation() != CSSSelector::Descendant && selector->relation() != CSSSelector::Child && selector->relation() != CSSSelector::SubSelector)
+ if (!isFastCheckableRightmostSelector(selector))
+ return false;
+ for (selector = selector->tagHistory(); selector; selector = selector->tagHistory()) {
+ if (!isFastCheckableRelation(selector->relation()))
return false;
- if (selector->m_match != CSSSelector::None && selector->m_match != CSSSelector::Id && selector->m_match != CSSSelector::Class)
+ if (!isFastCheckableMatch(static_cast<CSSSelector::Match>(selector->m_match)))
return false;
}
return true;
if (e->isSVGShadowRoot())
return SelectorFailsCompletely;
#endif
-
+
// first selector has to match
if (!checkOneSelector(sel, e, dynamicPseudo, isSubSelector, encounteredLink, elementStyle, elementParentStyle))
return SelectorFailsLocally;
bool SelectorChecker::checkOneSelector(CSSSelector* sel, Element* e, PseudoId& dynamicPseudo, bool isSubSelector, bool encounteredLink, RenderStyle* elementStyle, RenderStyle* elementParentStyle) const
{
ASSERT(e);
- if (!e)
- return false;
-
- if (!selectorTagMatches(e, sel))
+ if (!SelectorChecker::tagMatches(e, sel))
return false;
if (sel->hasAttribute()) {
return inputElement->isAutofilled();
break;
case CSSSelector::PseudoLink:
- if (e && e->isLink())
- return !m_isMatchingVisitedPseudoClass;
- break;
+ return e->isLink() && !linkMatchesVisitedPseudoClass(e);
case CSSSelector::PseudoVisited:
- if (e && e->isLink())
- return m_isMatchingVisitedPseudoClass || InspectorInstrumentation::forcePseudoState(e, CSSSelector::PseudoVisited);
- break;
+ return e->isLink() && linkMatchesVisitedPseudoClass(e);
case CSSSelector::PseudoDrag:
if (elementStyle)
elementStyle->setAffectedByDragRules(true);
return true;
break;
case CSSSelector::PseudoFocus:
- if (e && ((e->focused() && e->document()->frame() && e->document()->frame()->selection()->isFocusedAndActive()) || InspectorInstrumentation::forcePseudoState(e, CSSSelector::PseudoFocus)))
- return true;
- break;
+ return matchesFocusPseudoClass(e);
case CSSSelector::PseudoHover:
// If we're in quirks mode, then hover should never match anchors with no
// href and *:hover should not match anything. This is important for sites like wsj.com.
}
}
+bool SelectorChecker::commonPseudoClassSelectorMatches(const Element* element, const CSSSelector* selector) const
+{
+ ASSERT(isCommonPseudoClassSelector(selector));
+ switch (selector->pseudoType()) {
+ case CSSSelector::PseudoLink:
+ return element->isLink() && !linkMatchesVisitedPseudoClass(element);
+ case CSSSelector::PseudoVisited:
+ return element->isLink() && linkMatchesVisitedPseudoClass(element);
+ case CSSSelector::PseudoAnyLink:
+ return element->isLink();
+ case CSSSelector::PseudoFocus:
+ return matchesFocusPseudoClass(element);
+ default:
+ ASSERT_NOT_REACHED();
+ }
+ return true;
+}
+
+bool SelectorChecker::isFrameFocused(const Element* element)
+{
+ return element->document()->frame() && element->document()->frame()->selection()->isFocusedAndActive();
+}
+
}
#ifndef SelectorChecker_h
#define SelectorChecker_h
+#include "CSSSelector.h"
#include "Element.h"
+#include "InspectorInstrumentation.h"
#include "LinkHash.h"
#include "RenderStyleConstants.h"
#include <wtf/BloomFilter.h>
bool checkSelector(CSSSelector*, Element*, bool isFastCheckableSelector = false) const;
SelectorMatch checkSelector(CSSSelector*, Element*, PseudoId& dynamicPseudo, bool isSubSelector, bool encounteredLink, RenderStyle* = 0, RenderStyle* elementParentStyle = 0) const;
static bool isFastCheckableSelector(const CSSSelector*);
- static bool fastCheckSelector(const CSSSelector*, const Element*);
+ bool fastCheckSelector(const CSSSelector*, const Element*) const;
template <unsigned maximumIdentifierCount>
inline bool fastRejectSelector(const unsigned* identifierHashes) const;
bool hasUnknownPseudoElements() const { return m_hasUnknownPseudoElements; }
void clearHasUnknownPseudoElements() { m_hasUnknownPseudoElements = false; }
+
+ static bool tagMatches(const Element*, const CSSSelector*);
+ static bool isCommonPseudoClassSelector(const CSSSelector*);
+ bool commonPseudoClassSelectorMatches(const Element*, const CSSSelector*) const;
+ bool linkMatchesVisitedPseudoClass(const Element*) const;
+ bool matchesFocusPseudoClass(const Element*) const;
private:
bool checkOneSelector(CSSSelector*, Element*, PseudoId& dynamicPseudo, bool isSubSelector, bool encounteredLink, RenderStyle*, RenderStyle* elementParentStyle) const;
bool checkScrollbarPseudoClass(CSSSelector*, PseudoId& dynamicPseudo) const;
+ static bool isFrameFocused(const Element*);
+
+ bool fastCheckRightmostSelector(const CSSSelector*, const Element*) const;
EInsideLink determineLinkStateSlowCase(Element*) const;
}
return false;
}
+
+inline bool SelectorChecker::isCommonPseudoClassSelector(const CSSSelector* selector)
+{
+ if (selector->m_match != CSSSelector::PseudoClass)
+ return false;
+ CSSSelector::PseudoType pseudoType = selector->pseudoType();
+ return pseudoType == CSSSelector::PseudoLink
+ || pseudoType == CSSSelector::PseudoAnyLink
+ || pseudoType == CSSSelector::PseudoVisited
+ || pseudoType == CSSSelector::PseudoFocus;
+}
+
+inline bool SelectorChecker::linkMatchesVisitedPseudoClass(const Element* element) const
+{
+ ASSERT(element->isLink());
+ return m_isMatchingVisitedPseudoClass || InspectorInstrumentation::forcePseudoState(const_cast<Element*>(element), CSSSelector::PseudoVisited);
+}
+
+inline bool SelectorChecker::matchesFocusPseudoClass(const Element* element) const
+{
+ if (InspectorInstrumentation::forcePseudoState(const_cast<Element*>(element), CSSSelector::PseudoFocus))
+ return true;
+ return element->focused() && isFrameFocused(element);
+}
+
+inline bool SelectorChecker::tagMatches(const Element* element, const CSSSelector* selector)
+{
+ if (!selector->hasTag())
+ return true;
+ const AtomicString& localName = selector->tag().localName();
+ if (localName != starAtom && localName != element->localName())
+ return false;
+ const AtomicString& namespaceURI = selector->tag().namespaceURI();
+ return namespaceURI == starAtom || namespaceURI == element->namespaceURI();
+}
}