summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/css/SelectorChecker.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/css/SelectorChecker.cpp')
-rw-r--r--Source/WebCore/css/SelectorChecker.cpp1327
1 files changed, 766 insertions, 561 deletions
diff --git a/Source/WebCore/css/SelectorChecker.cpp b/Source/WebCore/css/SelectorChecker.cpp
index dd466bb92..0f0d365c0 100644
--- a/Source/WebCore/css/SelectorChecker.cpp
+++ b/Source/WebCore/css/SelectorChecker.cpp
@@ -2,12 +2,13 @@
* 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-2016 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/)
* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
* Copyright (C) Research In Motion Limited 2011. All rights reserved.
+ * Copyright (C) 2014 Yusuke Suzuki <utatane.tea@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@@ -31,7 +32,7 @@
#include "CSSSelector.h"
#include "CSSSelectorList.h"
#include "Document.h"
-#include "FocusController.h"
+#include "ElementTraversal.h"
#include "Frame.h"
#include "FrameSelection.h"
#include "HTMLAnchorElement.h"
@@ -41,61 +42,92 @@
#include "HTMLNames.h"
#include "HTMLOptGroupElement.h"
#include "HTMLOptionElement.h"
+#include "HTMLParserIdioms.h"
#include "HTMLProgressElement.h"
#include "HTMLStyleElement.h"
-#include "InsertionPoint.h"
#include "InspectorInstrumentation.h"
-#include "NodeRenderStyle.h"
#include "Page.h"
-#include "RenderObject.h"
-#include "RenderScrollbar.h"
-#include "RenderStyle.h"
-#include "ScrollableArea.h"
-#include "ScrollbarTheme.h"
+#include "RenderElement.h"
+#include "SelectorCheckerTestFunctions.h"
#include "ShadowRoot.h"
#include "StyledElement.h"
#include "Text.h"
-#if ENABLE(VIDEO_TRACK)
-#include "WebVTTElement.h"
-#endif
-
namespace WebCore {
using namespace HTMLNames;
-
-static inline bool isFirstChildElement(const Element* element)
+
+enum class VisitedMatchType : unsigned char {
+ Disabled, Enabled
+};
+
+struct SelectorChecker::LocalContext {
+ LocalContext(const CSSSelector& selector, const Element& element, VisitedMatchType visitedMatchType, PseudoId pseudoId)
+ : selector(&selector)
+ , element(&element)
+ , visitedMatchType(visitedMatchType)
+ , firstSelectorOfTheFragment(&selector)
+ , pseudoId(pseudoId)
+ { }
+
+ const CSSSelector* selector;
+ const Element* element;
+ VisitedMatchType visitedMatchType;
+ const CSSSelector* firstSelectorOfTheFragment;
+ PseudoId pseudoId;
+ bool isMatchElement { true };
+ bool inFunctionalPseudoClass { false };
+ bool pseudoElementEffective { true };
+ bool hasScrollbarPseudo { false };
+ bool hasSelectionPseudo { false };
+
+};
+
+static inline void addStyleRelation(SelectorChecker::CheckingContext& checkingContext, const Element& element, SelectorChecker::StyleRelation::Type type, unsigned value = 1)
+{
+ ASSERT(value == 1 || type == SelectorChecker::StyleRelation::NthChildIndex || type == SelectorChecker::StyleRelation::AffectedByEmpty);
+ if (checkingContext.resolvingMode != SelectorChecker::Mode::ResolvingStyle)
+ return;
+ checkingContext.styleRelations.append({ const_cast<Element&>(element), type, value });
+}
+
+static inline bool isFirstChildElement(const Element& element)
{
- return !element->previousElementSibling();
+ return !ElementTraversal::previousSibling(element);
}
-static inline bool isLastChildElement(const Element* element)
+static inline bool isLastChildElement(const Element& element)
{
- return !element->nextElementSibling();
+ return !ElementTraversal::nextSibling(element);
}
-static inline bool isFirstOfType(const Element* element, const QualifiedName& type)
+static inline bool isFirstOfType(SelectorChecker::CheckingContext& checkingContext, const Element& element, const QualifiedName& type)
{
- for (const Element* sibling = element->previousElementSibling(); sibling; sibling = sibling->previousElementSibling()) {
+ for (const Element* sibling = ElementTraversal::previousSibling(element); sibling; sibling = ElementTraversal::previousSibling(*sibling)) {
+ addStyleRelation(checkingContext, *sibling, SelectorChecker::StyleRelation::AffectsNextSibling);
+
if (sibling->hasTagName(type))
return false;
}
return true;
}
-static inline bool isLastOfType(const Element* element, const QualifiedName& type)
+static inline bool isLastOfType(const Element& element, const QualifiedName& type)
{
- for (const Element* sibling = element->nextElementSibling(); sibling; sibling = sibling->nextElementSibling()) {
+ for (const Element* sibling = ElementTraversal::nextSibling(element); sibling; sibling = ElementTraversal::nextSibling(*sibling)) {
if (sibling->hasTagName(type))
return false;
}
return true;
}
-static inline int countElementsBefore(const Element* element)
+static inline int countElementsBefore(SelectorChecker::CheckingContext& checkingContext, const Element& element)
{
int count = 0;
- for (const Element* sibling = element->previousElementSibling(); sibling; sibling = sibling->previousElementSibling()) {
+ for (const Element* sibling = ElementTraversal::previousSibling(element); sibling; sibling = ElementTraversal::previousSibling(*sibling)) {
+
+ addStyleRelation(checkingContext, *sibling, SelectorChecker::StyleRelation::AffectsNextSibling);
+
unsigned index = sibling->childIndex();
if (index) {
count += index;
@@ -106,74 +138,127 @@ static inline int countElementsBefore(const Element* element)
return count;
}
-static inline int countElementsOfTypeBefore(const Element* element, const QualifiedName& type)
+static inline int countElementsOfTypeBefore(SelectorChecker::CheckingContext& checkingContext, const Element& element, const QualifiedName& type)
{
int count = 0;
- for (const Element* sibling = element->previousElementSibling(); sibling; sibling = sibling->previousElementSibling()) {
+ for (const Element* sibling = ElementTraversal::previousSibling(element); sibling; sibling = ElementTraversal::previousSibling(*sibling)) {
+ addStyleRelation(checkingContext, *sibling, SelectorChecker::StyleRelation::AffectsNextSibling);
+
if (sibling->hasTagName(type))
++count;
}
return count;
}
-static inline int countElementsAfter(const Element* element)
+static inline int countElementsAfter(const Element& element)
{
int count = 0;
- for (const Element* sibling = element->nextElementSibling(); sibling; sibling = sibling->nextElementSibling())
+ for (const Element* sibling = ElementTraversal::nextSibling(element); sibling; sibling = ElementTraversal::nextSibling(*sibling))
++count;
return count;
}
-static inline int countElementsOfTypeAfter(const Element* element, const QualifiedName& type)
+static inline int countElementsOfTypeAfter(const Element& element, const QualifiedName& type)
{
int count = 0;
- for (const Element* sibling = element->nextElementSibling(); sibling; sibling = sibling->nextElementSibling()) {
+ for (const Element* sibling = ElementTraversal::nextSibling(element); sibling; sibling = ElementTraversal::nextSibling(*sibling)) {
if (sibling->hasTagName(type))
++count;
}
return count;
}
-SelectorChecker::SelectorChecker(Document* document, Mode mode)
- : m_strictParsing(!document->inQuirksMode())
- , m_documentIsHTML(document->isHTMLDocument())
- , m_mode(mode)
+SelectorChecker::SelectorChecker(Document& document)
+ : m_strictParsing(!document.inQuirksMode())
+ , m_documentIsHTML(document.isHTMLDocument())
{
}
+bool SelectorChecker::match(const CSSSelector& selector, const Element& element, CheckingContext& checkingContext, unsigned& specificity) const
+{
+ specificity = 0;
+
+ LocalContext context(selector, element, checkingContext.resolvingMode == SelectorChecker::Mode::QueryingRules ? VisitedMatchType::Disabled : VisitedMatchType::Enabled, checkingContext.pseudoId);
+ PseudoIdSet pseudoIdSet;
+ MatchResult result = matchRecursively(checkingContext, context, pseudoIdSet, specificity);
+ if (result.match != Match::SelectorMatches)
+ return false;
+ if (checkingContext.pseudoId != NOPSEUDO && !pseudoIdSet.has(checkingContext.pseudoId))
+ return false;
+
+ if (checkingContext.pseudoId == NOPSEUDO && pseudoIdSet) {
+ PseudoIdSet publicPseudoIdSet = pseudoIdSet & PseudoIdSet::fromMask(PUBLIC_PSEUDOID_MASK);
+ if (checkingContext.resolvingMode == Mode::ResolvingStyle && publicPseudoIdSet)
+ checkingContext.pseudoIDSet = publicPseudoIdSet;
+
+ // When ignoring virtual pseudo elements, the context's pseudo should also be NOPSEUDO but that does
+ // not cause a failure.
+ return checkingContext.resolvingMode == Mode::CollectingRulesIgnoringVirtualPseudoElements || result.matchType == MatchType::Element;
+ }
+ return true;
+}
+
+inline static bool hasScrollbarPseudoElement(const PseudoIdSet& dynamicPseudoIdSet)
+{
+ PseudoIdSet scrollbarIdSet = { SCROLLBAR, SCROLLBAR_THUMB, SCROLLBAR_BUTTON, SCROLLBAR_TRACK, SCROLLBAR_TRACK_PIECE, SCROLLBAR_CORNER };
+ if (dynamicPseudoIdSet & scrollbarIdSet)
+ return true;
+
+ // RESIZER does not always have a scrollbar but it is a scrollbar-like pseudo element
+ // because it can have more than one pseudo element.
+ return dynamicPseudoIdSet.has(RESIZER);
+}
+
+static SelectorChecker::LocalContext localContextForParent(const SelectorChecker::LocalContext& context)
+{
+ SelectorChecker::LocalContext updatedContext(context);
+ // Disable :visited matching when we see the first link.
+ if (context.element->isLink())
+ updatedContext.visitedMatchType = VisitedMatchType::Disabled;
+ updatedContext.element = context.element->parentElement();
+ updatedContext.isMatchElement = false;
+ return updatedContext;
+}
+
// Recursive check of selectors and combinators
// It can return 4 different values:
// * SelectorMatches - the selector matches the element e
// * SelectorFailsLocally - the selector fails for the element e
// * SelectorFailsAllSiblings - the selector fails for e and any sibling of e
// * SelectorFailsCompletely - the selector fails for e and any sibling or ancestor of e
-SelectorChecker::Match SelectorChecker::match(const SelectorCheckingContext& context, PseudoId& dynamicPseudo) const
+SelectorChecker::MatchResult SelectorChecker::matchRecursively(CheckingContext& checkingContext, const LocalContext& context, PseudoIdSet& dynamicPseudoIdSet, unsigned& specificity) const
{
- // first selector has to match
- if (!checkOne(context))
- return SelectorFailsLocally;
+ MatchType matchType = MatchType::Element;
- if (context.selector->m_match == CSSSelector::PseudoElement) {
+ // The first selector has to match.
+ if (!checkOne(checkingContext, context, dynamicPseudoIdSet, matchType, specificity))
+ return MatchResult::fails(Match::SelectorFailsLocally);
+
+ if (context.selector->match() == CSSSelector::PseudoElement) {
if (context.selector->isCustomPseudoElement()) {
+ // In functional pseudo class, custom pseudo elements are always disabled.
+ // FIXME: We should accept custom pseudo elements inside :matches().
+ if (context.inFunctionalPseudoClass)
+ return MatchResult::fails(Match::SelectorFailsCompletely);
if (ShadowRoot* root = context.element->containingShadowRoot()) {
if (context.element->shadowPseudoId() != context.selector->value())
- return SelectorFailsLocally;
+ return MatchResult::fails(Match::SelectorFailsLocally);
- if (context.selector->pseudoType() == CSSSelector::PseudoWebKitCustomElement && root->type() != ShadowRoot::UserAgentShadowRoot)
- return SelectorFailsLocally;
+ if (context.selector->pseudoElementType() == CSSSelector::PseudoElementWebKitCustom && root->type() != ShadowRoot::Type::UserAgent)
+ return MatchResult::fails(Match::SelectorFailsLocally);
} else
- return SelectorFailsLocally;
+ return MatchResult::fails(Match::SelectorFailsLocally);
} else {
- if ((!context.elementStyle && m_mode == ResolvingStyle) || m_mode == QueryingRules)
- return SelectorFailsLocally;
+ if (!context.pseudoElementEffective)
+ return MatchResult::fails(Match::SelectorFailsCompletely);
- PseudoId pseudoId = CSSSelector::pseudoId(context.selector->pseudoType());
- if (pseudoId == FIRST_LETTER) {
- if (Document* document = context.element->document())
- document->styleSheetCollection()->setUsesFirstLetterRules(true);
- }
- if (pseudoId != NOPSEUDO && m_mode != SharingRules)
- dynamicPseudo = pseudoId;
+ if (checkingContext.resolvingMode == Mode::QueryingRules)
+ return MatchResult::fails(Match::SelectorFailsCompletely);
+
+ PseudoId pseudoId = CSSSelector::pseudoId(context.selector->pseudoElementType());
+ if (pseudoId != NOPSEUDO)
+ dynamicPseudoIdSet.add(pseudoId);
+ matchType = MatchType::VirtualPseudoElementOnly;
}
}
@@ -182,140 +267,179 @@ SelectorChecker::Match SelectorChecker::match(const SelectorCheckingContext& con
// Prepare next selector
const CSSSelector* historySelector = context.selector->tagHistory();
- if (!historySelector) {
- if (context.behaviorAtBoundary == CrossesBoundary) {
- ASSERT(context.scope);
- return context.scope->contains(context.element) ? SelectorMatches : SelectorFailsLocally;
- }
- return SelectorMatches;
- }
+ if (!historySelector)
+ return MatchResult::matches(matchType);
- SelectorCheckingContext nextContext(context);
+ LocalContext nextContext(context);
nextContext.selector = historySelector;
- PseudoId ignoreDynamicPseudo = NOPSEUDO;
if (relation != CSSSelector::SubSelector) {
- // Abort if the next selector would exceed the scope.
- if (context.element == context.scope && context.behaviorAtBoundary != StaysWithinTreeScope)
- return SelectorFailsCompletely;
-
// Bail-out if this selector is irrelevant for the pseudoId
- if (context.pseudoId != NOPSEUDO && context.pseudoId != dynamicPseudo)
- return SelectorFailsCompletely;
+ if (context.pseudoId != NOPSEUDO && !dynamicPseudoIdSet.has(context.pseudoId))
+ return MatchResult::fails(Match::SelectorFailsCompletely);
- // Disable :visited matching when we see the first link or try to match anything else than an ancestors.
- if (!context.isSubSelector && (context.element->isLink() || (relation != CSSSelector::Descendant && relation != CSSSelector::Child)))
- nextContext.visitedMatchType = VisitedMatchDisabled;
+ // Disable :visited matching when we try to match anything else than an ancestors.
+ if (relation != CSSSelector::Descendant && relation != CSSSelector::Child)
+ nextContext.visitedMatchType = VisitedMatchType::Disabled;
nextContext.pseudoId = NOPSEUDO;
+ // Virtual pseudo element is only effective in the rightmost fragment.
+ nextContext.pseudoElementEffective = false;
+ nextContext.isMatchElement = false;
}
switch (relation) {
case CSSSelector::Descendant:
- nextContext.element = context.element->parentElement();
- nextContext.isSubSelector = false;
- nextContext.elementStyle = 0;
- for (; nextContext.element; nextContext.element = nextContext.element->parentElement()) {
- Match match = this->match(nextContext, ignoreDynamicPseudo);
- if (match == SelectorMatches || match == SelectorFailsCompletely)
- return match;
- if (nextContext.element == nextContext.scope && nextContext.behaviorAtBoundary != StaysWithinTreeScope)
- return SelectorFailsCompletely;
+ nextContext = localContextForParent(nextContext);
+ nextContext.firstSelectorOfTheFragment = nextContext.selector;
+ for (; nextContext.element; nextContext = localContextForParent(nextContext)) {
+ PseudoIdSet ignoreDynamicPseudo;
+ unsigned descendantsSpecificity = 0;
+ MatchResult result = matchRecursively(checkingContext, nextContext, ignoreDynamicPseudo, descendantsSpecificity);
+ ASSERT(!nextContext.pseudoElementEffective && !ignoreDynamicPseudo);
+
+ if (result.match == Match::SelectorMatches)
+ specificity = CSSSelector::addSpecificities(specificity, descendantsSpecificity);
+
+ if (result.match == Match::SelectorMatches || result.match == Match::SelectorFailsCompletely)
+ return MatchResult::updateWithMatchType(result, matchType);
}
- return SelectorFailsCompletely;
+ return MatchResult::fails(Match::SelectorFailsCompletely);
case CSSSelector::Child:
- nextContext.element = context.element->parentElement();
- if (!nextContext.element)
- return SelectorFailsCompletely;
- nextContext.isSubSelector = false;
- nextContext.elementStyle = 0;
- return match(nextContext, ignoreDynamicPseudo);
+ {
+ nextContext = localContextForParent(nextContext);
+ if (!nextContext.element)
+ return MatchResult::fails(Match::SelectorFailsCompletely);
+ nextContext.firstSelectorOfTheFragment = nextContext.selector;
+ PseudoIdSet ignoreDynamicPseudo;
+ unsigned childSpecificity = 0;
+ MatchResult result = matchRecursively(checkingContext, nextContext, ignoreDynamicPseudo, childSpecificity);
+ ASSERT(!nextContext.pseudoElementEffective && !ignoreDynamicPseudo);
+
+ if (result.match == Match::SelectorMatches)
+ specificity = CSSSelector::addSpecificities(specificity, childSpecificity);
+
+ if (result.match == Match::SelectorMatches || result.match == Match::SelectorFailsCompletely)
+ return MatchResult::updateWithMatchType(result, matchType);
+ return MatchResult::fails(Match::SelectorFailsAllSiblings);
+ }
case CSSSelector::DirectAdjacent:
- if (m_mode == ResolvingStyle) {
- if (Element* parentElement = context.element->parentElement())
- parentElement->setChildrenAffectedByDirectAdjacentRules();
- }
- nextContext.element = context.element->previousElementSibling();
- if (!nextContext.element)
- return SelectorFailsAllSiblings;
- nextContext.isSubSelector = false;
- nextContext.elementStyle = 0;
- return match(nextContext, ignoreDynamicPseudo);
+ {
+ addStyleRelation(checkingContext, *context.element, StyleRelation::AffectedByPreviousSibling);
- case CSSSelector::IndirectAdjacent:
- if (m_mode == ResolvingStyle) {
- if (Element* parentElement = context.element->parentElement())
- parentElement->setChildrenAffectedByForwardPositionalRules();
+ Element* previousElement = context.element->previousElementSibling();
+ if (!previousElement)
+ return MatchResult::fails(Match::SelectorFailsAllSiblings);
+
+ addStyleRelation(checkingContext, *previousElement, StyleRelation::AffectsNextSibling);
+
+ nextContext.element = previousElement;
+ nextContext.firstSelectorOfTheFragment = nextContext.selector;
+ PseudoIdSet ignoreDynamicPseudo;
+ unsigned adjacentSpecificity = 0;
+ MatchResult result = matchRecursively(checkingContext, nextContext, ignoreDynamicPseudo, adjacentSpecificity);
+ ASSERT(!nextContext.pseudoElementEffective && !ignoreDynamicPseudo);
+
+ if (result.match == Match::SelectorMatches)
+ specificity = CSSSelector::addSpecificities(specificity, adjacentSpecificity);
+
+ return MatchResult::updateWithMatchType(result, matchType);
}
+ case CSSSelector::IndirectAdjacent:
+ addStyleRelation(checkingContext, *context.element, StyleRelation::AffectedByPreviousSibling);
+
nextContext.element = context.element->previousElementSibling();
- nextContext.isSubSelector = false;
- nextContext.elementStyle = 0;
+ nextContext.firstSelectorOfTheFragment = nextContext.selector;
for (; nextContext.element; nextContext.element = nextContext.element->previousElementSibling()) {
- Match match = this->match(nextContext, ignoreDynamicPseudo);
- if (match == SelectorMatches || match == SelectorFailsAllSiblings || match == SelectorFailsCompletely)
- return match;
+ addStyleRelation(checkingContext, *nextContext.element, StyleRelation::AffectsNextSibling);
+
+ PseudoIdSet ignoreDynamicPseudo;
+ unsigned indirectAdjacentSpecificity = 0;
+ MatchResult result = matchRecursively(checkingContext, nextContext, ignoreDynamicPseudo, indirectAdjacentSpecificity);
+ ASSERT(!nextContext.pseudoElementEffective && !ignoreDynamicPseudo);
+
+ if (result.match == Match::SelectorMatches)
+ specificity = CSSSelector::addSpecificities(specificity, indirectAdjacentSpecificity);
+
+ if (result.match == Match::SelectorMatches || result.match == Match::SelectorFailsAllSiblings || result.match == Match::SelectorFailsCompletely)
+ return MatchResult::updateWithMatchType(result, matchType);
};
- return SelectorFailsAllSiblings;
+ return MatchResult::fails(Match::SelectorFailsAllSiblings);
case CSSSelector::SubSelector:
- // a selector is invalid if something follows a pseudo-element
- // We make an exception for scrollbar pseudo elements and allow a set of pseudo classes (but nothing else)
- // to follow the pseudo elements.
- nextContext.hasScrollbarPseudo = dynamicPseudo != NOPSEUDO && (context.scrollbar || dynamicPseudo == SCROLLBAR_CORNER || dynamicPseudo == RESIZER);
- nextContext.hasSelectionPseudo = dynamicPseudo == SELECTION;
- if ((context.elementStyle || m_mode == CollectingRules || m_mode == QueryingRules) && dynamicPseudo != NOPSEUDO
- && !nextContext.hasSelectionPseudo
- && !(nextContext.hasScrollbarPseudo && nextContext.selector->m_match == CSSSelector::PseudoClass))
- return SelectorFailsCompletely;
- nextContext.isSubSelector = true;
- return match(nextContext, dynamicPseudo);
-
+ {
+ // a selector is invalid if something follows a pseudo-element
+ // We make an exception for scrollbar pseudo elements and allow a set of pseudo classes (but nothing else)
+ // to follow the pseudo elements.
+ nextContext.hasScrollbarPseudo = hasScrollbarPseudoElement(dynamicPseudoIdSet);
+ nextContext.hasSelectionPseudo = dynamicPseudoIdSet.has(SELECTION);
+ if ((context.isMatchElement || checkingContext.resolvingMode == Mode::CollectingRules) && dynamicPseudoIdSet
+ && !nextContext.hasSelectionPseudo
+ && !(nextContext.hasScrollbarPseudo && nextContext.selector->match() == CSSSelector::PseudoClass))
+ return MatchResult::fails(Match::SelectorFailsCompletely);
+
+ unsigned subselectorSpecificity = 0;
+ MatchResult result = matchRecursively(checkingContext, nextContext, dynamicPseudoIdSet, subselectorSpecificity);
+
+ if (result.match == Match::SelectorMatches)
+ specificity = CSSSelector::addSpecificities(specificity, subselectorSpecificity);
+
+ return MatchResult::updateWithMatchType(result, matchType);
+ }
case CSSSelector::ShadowDescendant:
{
- // If we're in the same tree-scope as the scoping element, then following a shadow descendant combinator would escape that and thus the scope.
- if (context.scope && context.scope->treeScope() == context.element->treeScope() && context.behaviorAtBoundary != StaysWithinTreeScope)
- return SelectorFailsCompletely;
Element* shadowHostNode = context.element->shadowHost();
if (!shadowHostNode)
- return SelectorFailsCompletely;
+ return MatchResult::fails(Match::SelectorFailsCompletely);
nextContext.element = shadowHostNode;
- nextContext.isSubSelector = false;
- nextContext.elementStyle = 0;
- return match(nextContext, ignoreDynamicPseudo);
+ nextContext.firstSelectorOfTheFragment = nextContext.selector;
+ PseudoIdSet ignoreDynamicPseudo;
+ unsigned shadowDescendantSpecificity = 0;
+ MatchResult result = matchRecursively(checkingContext, nextContext, ignoreDynamicPseudo, shadowDescendantSpecificity);
+
+ if (result.match == Match::SelectorMatches)
+ specificity = CSSSelector::addSpecificities(specificity, shadowDescendantSpecificity);
+
+ return MatchResult::updateWithMatchType(result, matchType);
}
}
ASSERT_NOT_REACHED();
- return SelectorFailsCompletely;
+ return MatchResult::fails(Match::SelectorFailsCompletely);
}
-static bool attributeValueMatches(const Attribute* attributeItem, CSSSelector::Match match, const AtomicString& selectorValue, bool caseSensitive)
+static bool attributeValueMatches(const Attribute& attribute, CSSSelector::Match match, const AtomicString& selectorValue, bool caseSensitive)
{
- const AtomicString& value = attributeItem->value();
- if (value.isNull())
- return false;
+ const AtomicString& value = attribute.value();
+ ASSERT(!value.isNull());
switch (match) {
+ case CSSSelector::Set:
+ break;
case CSSSelector::Exact:
- if (caseSensitive ? selectorValue != value : !equalIgnoringCase(selectorValue, value))
+ if (caseSensitive ? selectorValue != value : !equalIgnoringASCIICase(selectorValue, value))
return false;
break;
case CSSSelector::List:
{
- // Ignore empty selectors or selectors containing spaces
- if (selectorValue.contains(' ') || selectorValue.isEmpty())
+ // Ignore empty selectors or selectors containing spaces.
+ if (selectorValue.isEmpty() || selectorValue.find(isHTMLSpace<UChar>) != notFound)
return false;
unsigned startSearchAt = 0;
while (true) {
- size_t foundPos = value.find(selectorValue, startSearchAt, caseSensitive);
+ size_t foundPos;
+ if (caseSensitive)
+ foundPos = value.find(selectorValue, startSearchAt);
+ else
+ foundPos = value.findIgnoringASCIICase(selectorValue, startSearchAt);
if (foundPos == notFound)
return false;
- if (!foundPos || value[foundPos - 1] == ' ') {
+ if (!foundPos || isHTMLSpace(value[foundPos - 1])) {
unsigned endStr = foundPos + selectorValue.length();
- if (endStr == value.length() || value[endStr] == ' ')
+ if (endStr == value.length() || isHTMLSpace(value[endStr]))
break; // We found a match.
}
@@ -324,478 +448,591 @@ static bool attributeValueMatches(const Attribute* attributeItem, CSSSelector::M
}
break;
}
- case CSSSelector::Contain:
- if (!value.contains(selectorValue, caseSensitive) || selectorValue.isEmpty())
+ case CSSSelector::Contain: {
+ bool valueContainsSelectorValue;
+ if (caseSensitive)
+ valueContainsSelectorValue = value.contains(selectorValue);
+ else
+ valueContainsSelectorValue = value.containsIgnoringASCIICase(selectorValue);
+
+ if (!valueContainsSelectorValue || selectorValue.isEmpty())
return false;
+
break;
+ }
case CSSSelector::Begin:
- if (!value.startsWith(selectorValue, caseSensitive) || selectorValue.isEmpty())
+ if (selectorValue.isEmpty())
return false;
+ if (caseSensitive) {
+ if (!value.startsWith(selectorValue))
+ return false;
+ } else {
+ if (!value.startsWithIgnoringASCIICase(selectorValue))
+ return false;
+ }
break;
case CSSSelector::End:
- if (!value.endsWith(selectorValue, caseSensitive) || selectorValue.isEmpty())
+ if (selectorValue.isEmpty())
return false;
+ if (caseSensitive) {
+ if (!value.endsWith(selectorValue))
+ return false;
+ } else {
+ if (!value.endsWithIgnoringASCIICase(selectorValue))
+ return false;
+ }
break;
case CSSSelector::Hyphen:
if (value.length() < selectorValue.length())
return false;
- if (!value.startsWith(selectorValue, caseSensitive))
- return false;
+ if (caseSensitive) {
+ if (!value.startsWith(selectorValue))
+ return false;
+ } else {
+ if (!value.startsWithIgnoringASCIICase(selectorValue))
+ return false;
+ }
// It they start the same, check for exact match or following '-':
if (value.length() != selectorValue.length() && value[selectorValue.length()] != '-')
return false;
break;
- case CSSSelector::PseudoClass:
- case CSSSelector::PseudoElement:
default:
- break;
+ ASSERT_NOT_REACHED();
+ return false;
}
return true;
}
-static bool anyAttributeMatches(Element* element, const CSSSelector* selector, const QualifiedName& selectorAttr, bool caseSensitive)
+static bool anyAttributeMatches(const Element& element, const CSSSelector& selector, const QualifiedName& selectorAttr, bool caseSensitive)
{
- ASSERT(element->hasAttributesWithoutUpdate());
- for (size_t i = 0; i < element->attributeCount(); ++i) {
- const Attribute* attributeItem = element->attributeItem(i);
-
- if (!attributeItem->matches(selectorAttr.prefix(), element->isHTMLElement() ? selector->attributeCanonicalLocalName() : selectorAttr.localName(), selectorAttr.namespaceURI()))
+ ASSERT(element.hasAttributesWithoutUpdate());
+ for (const Attribute& attribute : element.attributesIterator()) {
+ if (!attribute.matches(selectorAttr.prefix(), element.isHTMLElement() ? selector.attributeCanonicalLocalName() : selectorAttr.localName(), selectorAttr.namespaceURI()))
continue;
- if (attributeValueMatches(attributeItem, static_cast<CSSSelector::Match>(selector->m_match), selector->value(), caseSensitive))
+ if (attributeValueMatches(attribute, selector.match(), selector.value(), caseSensitive))
return true;
}
return false;
}
-bool SelectorChecker::checkOne(const SelectorCheckingContext& context) const
+bool SelectorChecker::attributeSelectorMatches(const Element& element, const QualifiedName& attributeName, const AtomicString& attributeValue, const CSSSelector& selector)
{
- Element* const & element = context.element;
- const CSSSelector* const & selector = context.selector;
- ASSERT(element);
- ASSERT(selector);
+ ASSERT(selector.isAttributeSelector());
+ auto& selectorAttribute = selector.attribute();
+ auto& selectorName = element.isHTMLElement() ? selector.attributeCanonicalLocalName() : selectorAttribute.localName();
+ if (!Attribute::nameMatchesFilter(attributeName, selectorAttribute.prefix(), selectorName, selectorAttribute.namespaceURI()))
+ return false;
+ bool caseSensitive = true;
+ if (selector.attributeValueMatchingIsCaseInsensitive())
+ caseSensitive = false;
+ else if (element.document().isHTMLDocument() && element.isHTMLElement() && !HTMLDocument::isCaseSensitiveAttribute(selector.attribute()))
+ caseSensitive = false;
+ return attributeValueMatches(Attribute(attributeName, attributeValue), selector.match(), selector.value(), caseSensitive);
+}
- if (selector->m_match == CSSSelector::Tag)
- return SelectorChecker::tagMatches(element, selector->tagQName());
+static bool canMatchHoverOrActiveInQuirksMode(const SelectorChecker::LocalContext& context)
+{
+ // For quirks mode, follow this: http://quirks.spec.whatwg.org/#the-:active-and-:hover-quirk
+ // In quirks mode, a compound selector 'selector' that matches the following conditions must not match elements that would not also match the ':any-link' selector.
+ //
+ // selector uses the ':active' or ':hover' pseudo-classes.
+ // selector does not use a type selector.
+ // selector does not use an attribute selector.
+ // selector does not use an ID selector.
+ // selector does not use a class selector.
+ // selector does not use a pseudo-class selector other than ':active' and ':hover'.
+ // selector does not use a pseudo-element selector.
+ // selector is not part of an argument to a functional pseudo-class or pseudo-element.
+ if (context.inFunctionalPseudoClass)
+ return true;
- if (selector->m_match == CSSSelector::Class)
- return element->hasClass() && element->classNames().contains(selector->value());
+ for (const CSSSelector* selector = context.firstSelectorOfTheFragment; selector; selector = selector->tagHistory()) {
+ switch (selector->match()) {
+ case CSSSelector::Tag:
+ if (selector->tagQName() != anyQName())
+ return true;
+ break;
+ case CSSSelector::PseudoClass: {
+ CSSSelector::PseudoClassType pseudoClassType = selector->pseudoClassType();
+ if (pseudoClassType != CSSSelector::PseudoClassHover && pseudoClassType != CSSSelector::PseudoClassActive)
+ return true;
+ break;
+ }
+ case CSSSelector::Id:
+ case CSSSelector::Class:
+ case CSSSelector::Exact:
+ case CSSSelector::Set:
+ case CSSSelector::List:
+ case CSSSelector::Hyphen:
+ case CSSSelector::Contain:
+ case CSSSelector::Begin:
+ case CSSSelector::End:
+ case CSSSelector::PagePseudoClass:
+ case CSSSelector::PseudoElement:
+ return true;
+ case CSSSelector::Unknown:
+ ASSERT_NOT_REACHED();
+ break;
+ }
- if (selector->m_match == CSSSelector::Id)
- return element->hasID() && element->idForStyleResolution() == selector->value();
+ CSSSelector::Relation relation = selector->relation();
+ if (relation == CSSSelector::ShadowDescendant)
+ return true;
- if (selector->isAttributeSelector()) {
- if (!element->hasAttributes())
+ if (relation != CSSSelector::SubSelector)
return false;
+ }
+ return false;
+}
+
+static inline bool tagMatches(const Element& element, const CSSSelector& simpleSelector)
+{
+ const QualifiedName& tagQName = simpleSelector.tagQName();
+
+ if (tagQName == anyQName())
+ return true;
+
+ const AtomicString& localName = (element.isHTMLElement() && element.document().isHTMLDocument()) ? simpleSelector.tagLowercaseLocalName() : tagQName.localName();
+
+ if (localName != starAtom && localName != element.localName())
+ return false;
+ const AtomicString& namespaceURI = tagQName.namespaceURI();
+ return namespaceURI == starAtom || namespaceURI == element.namespaceURI();
+}
+
+bool SelectorChecker::checkOne(CheckingContext& checkingContext, const LocalContext& context, PseudoIdSet& dynamicPseudoIdSet, MatchType& matchType, unsigned& specificity) const
+{
+ const Element& element = *context.element;
+ const CSSSelector& selector = *context.selector;
- const QualifiedName& attr = selector->attribute();
- bool caseSensitive = !m_documentIsHTML || HTMLDocument::isCaseSensitiveAttribute(attr);
+ specificity = CSSSelector::addSpecificities(specificity, selector.simpleSelectorSpecificity());
- if (!anyAttributeMatches(element, selector, attr, caseSensitive))
+ if (selector.match() == CSSSelector::Tag)
+ return tagMatches(element, selector);
+
+ if (selector.match() == CSSSelector::Class)
+ return element.hasClass() && element.classNames().contains(selector.value());
+
+ if (selector.match() == CSSSelector::Id)
+ return element.hasID() && element.idForStyleResolution() == selector.value();
+
+ if (selector.isAttributeSelector()) {
+ if (!element.hasAttributes())
return false;
- }
- if (selector->m_match == CSSSelector::PseudoClass) {
- // Handle :not up front.
- if (selector->pseudoType() == CSSSelector::PseudoNot) {
- const CSSSelectorList* selectorList = selector->selectorList();
+ const QualifiedName& attr = selector.attribute();
+ bool caseSensitive = true;
+ if (selector.attributeValueMatchingIsCaseInsensitive())
+ caseSensitive = false;
+ else if (m_documentIsHTML && element.isHTMLElement() && !HTMLDocument::isCaseSensitiveAttribute(attr))
+ caseSensitive = false;
- // FIXME: We probably should fix the parser and make it never produce :not rules with missing selector list.
- if (!selectorList)
- return false;
+ return anyAttributeMatches(element, selector, attr, caseSensitive);
+ }
- SelectorCheckingContext subContext(context);
- subContext.isSubSelector = true;
- for (subContext.selector = selectorList->first(); subContext.selector; subContext.selector = subContext.selector->tagHistory()) {
- // :not cannot nest. I don't really know why this is a
- // restriction in CSS3, but it is, so let's honor it.
- // the parser enforces that this never occurs
- ASSERT(subContext.selector->pseudoType() != CSSSelector::PseudoNot);
- // We select between :visited and :link when applying. We don't know which one applied (or not) yet.
- if (subContext.selector->pseudoType() == CSSSelector::PseudoVisited || (subContext.selector->pseudoType() == CSSSelector::PseudoLink && subContext.visitedMatchType == VisitedMatchEnabled))
- return true;
- if (!checkOne(subContext))
- return true;
+ if (selector.match() == CSSSelector::PseudoClass) {
+ // Handle :not up front.
+ if (selector.pseudoClassType() == CSSSelector::PseudoClassNot) {
+ const CSSSelectorList* selectorList = selector.selectorList();
+
+ for (const CSSSelector* subselector = selectorList->first(); subselector; subselector = CSSSelectorList::next(subselector)) {
+ LocalContext subcontext(context);
+ subcontext.inFunctionalPseudoClass = true;
+ subcontext.pseudoElementEffective = false;
+ subcontext.selector = subselector;
+ subcontext.firstSelectorOfTheFragment = selectorList->first();
+ PseudoIdSet ignoreDynamicPseudo;
+
+ unsigned ignoredSpecificity;
+ if (matchRecursively(checkingContext, subcontext, ignoreDynamicPseudo, ignoredSpecificity).match == Match::SelectorMatches) {
+ ASSERT(!ignoreDynamicPseudo);
+ return false;
+ }
}
- } else if (context.hasScrollbarPseudo) {
+ return true;
+ }
+ if (context.hasScrollbarPseudo) {
// CSS scrollbars match a specific subset of pseudo classes, and they have specialized rules for each
- // (since there are no elements involved).
- return checkScrollbarPseudoClass(context, element->document(), selector);
- } else if (context.hasSelectionPseudo) {
- if (selector->pseudoType() == CSSSelector::PseudoWindowInactive)
- return !element->document()->page()->focusController()->isActive();
+ // (since there are no elements involved except with window-inactive).
+ return checkScrollbarPseudoClass(checkingContext, element, selector);
}
// Normal element pseudo class checking.
- switch (selector->pseudoType()) {
+ switch (selector.pseudoClassType()) {
// Pseudo classes:
- case CSSSelector::PseudoNot:
+ case CSSSelector::PseudoClassNot:
break; // Already handled up above.
- case CSSSelector::PseudoEmpty:
+ case CSSSelector::PseudoClassEmpty:
{
bool result = true;
- for (Node* n = element->firstChild(); n; n = n->nextSibling()) {
- if (n->isElementNode()) {
+ for (Node* node = element.firstChild(); node; node = node->nextSibling()) {
+ if (is<Element>(*node)) {
result = false;
break;
}
- if (n->isTextNode()) {
- Text* textNode = toText(n);
- if (!textNode->data().isEmpty()) {
+ if (is<Text>(*node)) {
+ Text& textNode = downcast<Text>(*node);
+ if (!textNode.data().isEmpty()) {
result = false;
break;
}
}
}
- if (m_mode == ResolvingStyle) {
- element->setStyleAffectedByEmpty();
- if (context.elementStyle)
- context.elementStyle->setEmptyState(result);
- else if (element->renderStyle() && (element->document()->styleSheetCollection()->usesSiblingRules() || element->renderStyle()->unique()))
- element->renderStyle()->setEmptyState(result);
- }
+ addStyleRelation(checkingContext, *context.element, StyleRelation::AffectedByEmpty, result);
+
return result;
}
- case CSSSelector::PseudoFirstChild:
+ case CSSSelector::PseudoClassFirstChild:
// first-child matches the first child that is an element
- if (Element* parentElement = element->parentElement()) {
- bool result = isFirstChildElement(element);
- if (m_mode == ResolvingStyle) {
- RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element->renderStyle();
- parentElement->setChildrenAffectedByFirstChildRules();
- if (result && childStyle)
- childStyle->setFirstChildState();
- }
- return result;
+ if (const Element* parentElement = element.parentElement()) {
+ bool isFirstChild = isFirstChildElement(element);
+ if (isFirstChild)
+ addStyleRelation(checkingContext, element, StyleRelation::FirstChild);
+ addStyleRelation(checkingContext, *parentElement, StyleRelation::ChildrenAffectedByFirstChildRules);
+ return isFirstChild;
}
break;
- case CSSSelector::PseudoFirstOfType:
+ case CSSSelector::PseudoClassFirstOfType:
// first-of-type matches the first element of its type
- if (Element* parentElement = element->parentElement()) {
- bool result = isFirstOfType(element, element->tagQName());
- if (m_mode == ResolvingStyle)
- parentElement->setChildrenAffectedByForwardPositionalRules();
- return result;
+ if (element.parentElement()) {
+ addStyleRelation(checkingContext, element, StyleRelation::AffectedByPreviousSibling);
+ return isFirstOfType(checkingContext, element, element.tagQName());
}
break;
- case CSSSelector::PseudoLastChild:
+ case CSSSelector::PseudoClassLastChild:
// last-child matches the last child that is an element
- if (Element* parentElement = element->parentElement()) {
- bool result = parentElement->isFinishedParsingChildren() && isLastChildElement(element);
- if (m_mode == ResolvingStyle) {
- RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element->renderStyle();
- parentElement->setChildrenAffectedByLastChildRules();
- if (result && childStyle)
- childStyle->setLastChildState();
- }
- return result;
+ if (const Element* parentElement = element.parentElement()) {
+ bool isLastChild = parentElement->isFinishedParsingChildren() && isLastChildElement(element);
+ if (isLastChild)
+ addStyleRelation(checkingContext, element, StyleRelation::LastChild);
+ addStyleRelation(checkingContext, *parentElement, StyleRelation::ChildrenAffectedByLastChildRules);
+ return isLastChild;
}
break;
- case CSSSelector::PseudoLastOfType:
+ case CSSSelector::PseudoClassLastOfType:
// last-of-type matches the last element of its type
- if (Element* parentElement = element->parentElement()) {
- if (m_mode == ResolvingStyle)
- parentElement->setChildrenAffectedByBackwardPositionalRules();
+ if (Element* parentElement = element.parentElement()) {
+ addStyleRelation(checkingContext, *parentElement, StyleRelation::ChildrenAffectedByBackwardPositionalRules);
if (!parentElement->isFinishedParsingChildren())
return false;
- return isLastOfType(element, element->tagQName());
+ return isLastOfType(element, element.tagQName());
}
break;
- case CSSSelector::PseudoOnlyChild:
- if (Element* parentElement = element->parentElement()) {
+ case CSSSelector::PseudoClassOnlyChild:
+ if (Element* parentElement = element.parentElement()) {
bool firstChild = isFirstChildElement(element);
bool onlyChild = firstChild && parentElement->isFinishedParsingChildren() && isLastChildElement(element);
- if (m_mode == ResolvingStyle) {
- RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element->renderStyle();
- parentElement->setChildrenAffectedByFirstChildRules();
- parentElement->setChildrenAffectedByLastChildRules();
- if (firstChild && childStyle)
- childStyle->setFirstChildState();
- if (onlyChild && childStyle)
- childStyle->setLastChildState();
- }
+ addStyleRelation(checkingContext, *parentElement, StyleRelation::ChildrenAffectedByFirstChildRules);
+ addStyleRelation(checkingContext, *parentElement, StyleRelation::ChildrenAffectedByLastChildRules);
+ if (firstChild)
+ addStyleRelation(checkingContext, element, StyleRelation::FirstChild);
+ if (onlyChild)
+ addStyleRelation(checkingContext, element, StyleRelation::LastChild);
return onlyChild;
}
break;
- case CSSSelector::PseudoOnlyOfType:
+ case CSSSelector::PseudoClassOnlyOfType:
// FIXME: This selector is very slow.
- if (Element* parentElement = element->parentElement()) {
- if (m_mode == ResolvingStyle) {
- parentElement->setChildrenAffectedByForwardPositionalRules();
- parentElement->setChildrenAffectedByBackwardPositionalRules();
- }
+ if (Element* parentElement = element.parentElement()) {
+ addStyleRelation(checkingContext, element, StyleRelation::AffectedByPreviousSibling);
+ addStyleRelation(checkingContext, *parentElement, StyleRelation::ChildrenAffectedByBackwardPositionalRules);
if (!parentElement->isFinishedParsingChildren())
return false;
- return isFirstOfType(element, element->tagQName()) && isLastOfType(element, element->tagQName());
+ return isFirstOfType(checkingContext, element, element.tagQName()) && isLastOfType(element, element.tagQName());
}
break;
- case CSSSelector::PseudoNthChild:
- if (!selector->parseNth())
+ case CSSSelector::PseudoClassMatches:
+ {
+ bool hasMatchedAnything = false;
+ unsigned maxSpecificity = 0;
+
+ MatchType localMatchType = MatchType::VirtualPseudoElementOnly;
+ for (const CSSSelector* subselector = selector.selectorList()->first(); subselector; subselector = CSSSelectorList::next(subselector)) {
+ LocalContext subcontext(context);
+ subcontext.inFunctionalPseudoClass = true;
+ subcontext.pseudoElementEffective = context.pseudoElementEffective;
+ subcontext.selector = subselector;
+ subcontext.firstSelectorOfTheFragment = subselector;
+ PseudoIdSet localDynamicPseudoIdSet;
+ unsigned localSpecificity = 0;
+ MatchResult result = matchRecursively(checkingContext, subcontext, localDynamicPseudoIdSet, localSpecificity);
+ if (result.match == Match::SelectorMatches) {
+ maxSpecificity = std::max(maxSpecificity, localSpecificity);
+
+ if (result.matchType == MatchType::Element)
+ localMatchType = MatchType::Element;
+
+ dynamicPseudoIdSet.merge(localDynamicPseudoIdSet);
+ hasMatchedAnything = true;
+ }
+ }
+ if (hasMatchedAnything) {
+ matchType = localMatchType;
+ specificity = CSSSelector::addSpecificities(specificity, maxSpecificity);
+ }
+ return hasMatchedAnything;
+ }
+ case CSSSelector::PseudoClassPlaceholderShown:
+ if (is<HTMLTextFormControlElement>(element)) {
+ addStyleRelation(checkingContext, element, StyleRelation::Unique);
+ return downcast<HTMLTextFormControlElement>(element).isPlaceholderVisible();
+ }
+ return false;
+ case CSSSelector::PseudoClassNthChild:
+ if (!selector.parseNth())
break;
- if (Element* parentElement = element->parentElement()) {
- int count = 1 + countElementsBefore(element);
- if (m_mode == ResolvingStyle) {
- RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element->renderStyle();
- element->setChildIndex(count);
- if (childStyle)
- childStyle->setUnique();
- parentElement->setChildrenAffectedByForwardPositionalRules();
+ if (element.parentElement()) {
+ if (const CSSSelectorList* selectorList = selector.selectorList()) {
+ unsigned selectorListSpecificity;
+ if (!matchSelectorList(checkingContext, context, element, *selectorList, selectorListSpecificity))
+ return false;
+ specificity = CSSSelector::addSpecificities(specificity, selectorListSpecificity);
}
- if (selector->matchNth(count))
+ addStyleRelation(checkingContext, element, StyleRelation::AffectedByPreviousSibling);
+
+ int count = 1;
+ if (const CSSSelectorList* selectorList = selector.selectorList()) {
+ for (Element* sibling = ElementTraversal::previousSibling(element); sibling; sibling = ElementTraversal::previousSibling(*sibling)) {
+ addStyleRelation(checkingContext, *sibling, StyleRelation::AffectsNextSibling);
+
+ unsigned ignoredSpecificity;
+ if (matchSelectorList(checkingContext, context, *sibling, *selectorList, ignoredSpecificity))
+ ++count;
+ }
+ } else {
+ count += countElementsBefore(checkingContext, element);
+ addStyleRelation(checkingContext, element, StyleRelation::NthChildIndex, count);
+ }
+
+ if (selector.matchNth(count))
return true;
}
break;
- case CSSSelector::PseudoNthOfType:
- if (!selector->parseNth())
+ case CSSSelector::PseudoClassNthOfType:
+ if (!selector.parseNth())
break;
- if (Element* parentElement = element->parentElement()) {
- int count = 1 + countElementsOfTypeBefore(element, element->tagQName());
- if (m_mode == ResolvingStyle)
- parentElement->setChildrenAffectedByForwardPositionalRules();
- if (selector->matchNth(count))
+ if (element.parentElement()) {
+ addStyleRelation(checkingContext, element, StyleRelation::AffectedByPreviousSibling);
+
+ int count = 1 + countElementsOfTypeBefore(checkingContext, element, element.tagQName());
+ if (selector.matchNth(count))
return true;
}
break;
- case CSSSelector::PseudoNthLastChild:
- if (!selector->parseNth())
+ case CSSSelector::PseudoClassNthLastChild:
+ if (!selector.parseNth())
break;
- if (Element* parentElement = element->parentElement()) {
- if (m_mode == ResolvingStyle)
- parentElement->setChildrenAffectedByBackwardPositionalRules();
+ if (Element* parentElement = element.parentElement()) {
+ if (const CSSSelectorList* selectorList = selector.selectorList()) {
+ unsigned selectorListSpecificity;
+ if (!matchSelectorList(checkingContext, context, element, *selectorList, selectorListSpecificity))
+ return false;
+ specificity = CSSSelector::addSpecificities(specificity, selectorListSpecificity);
+
+ addStyleRelation(checkingContext, *parentElement, StyleRelation::ChildrenAffectedByPropertyBasedBackwardPositionalRules);
+ } else
+ addStyleRelation(checkingContext, *parentElement, StyleRelation::ChildrenAffectedByBackwardPositionalRules);
+
if (!parentElement->isFinishedParsingChildren())
return false;
- int count = 1 + countElementsAfter(element);
- if (selector->matchNth(count))
+
+ int count = 1;
+ if (const CSSSelectorList* selectorList = selector.selectorList()) {
+ for (Element* sibling = ElementTraversal::nextSibling(element); sibling; sibling = ElementTraversal::nextSibling(*sibling)) {
+ unsigned ignoredSpecificity;
+ if (matchSelectorList(checkingContext, context, *sibling, *selectorList, ignoredSpecificity))
+ ++count;
+ }
+ } else
+ count += countElementsAfter(element);
+
+ if (selector.matchNth(count))
return true;
}
break;
- case CSSSelector::PseudoNthLastOfType:
- if (!selector->parseNth())
+ case CSSSelector::PseudoClassNthLastOfType:
+ if (!selector.parseNth())
break;
- if (Element* parentElement = element->parentElement()) {
- if (m_mode == ResolvingStyle)
- parentElement->setChildrenAffectedByBackwardPositionalRules();
+ if (Element* parentElement = element.parentElement()) {
+ addStyleRelation(checkingContext, *parentElement, StyleRelation::ChildrenAffectedByBackwardPositionalRules);
+
if (!parentElement->isFinishedParsingChildren())
return false;
- int count = 1 + countElementsOfTypeAfter(element, element->tagQName());
- if (selector->matchNth(count))
+ int count = 1 + countElementsOfTypeAfter(element, element.tagQName());
+ if (selector.matchNth(count))
return true;
}
break;
- case CSSSelector::PseudoTarget:
- if (element == element->document()->cssTarget())
+ case CSSSelector::PseudoClassTarget:
+ if (&element == element.document().cssTarget())
return true;
break;
- case CSSSelector::PseudoAny:
+ case CSSSelector::PseudoClassAny:
{
- SelectorCheckingContext subContext(context);
- subContext.isSubSelector = true;
- PseudoId ignoreDynamicPseudo = NOPSEUDO;
- for (subContext.selector = selector->selectorList()->first(); subContext.selector; subContext.selector = CSSSelectorList::next(subContext.selector)) {
- if (match(subContext, ignoreDynamicPseudo) == SelectorMatches)
+ LocalContext subcontext(context);
+ subcontext.inFunctionalPseudoClass = true;
+ subcontext.pseudoElementEffective = false;
+ for (subcontext.selector = selector.selectorList()->first(); subcontext.selector; subcontext.selector = CSSSelectorList::next(subcontext.selector)) {
+ subcontext.firstSelectorOfTheFragment = subcontext.selector;
+ PseudoIdSet ignoreDynamicPseudo;
+ unsigned ingoredSpecificity = 0;
+ if (matchRecursively(checkingContext, subcontext, ignoreDynamicPseudo, ingoredSpecificity).match == Match::SelectorMatches)
return true;
}
}
break;
- case CSSSelector::PseudoAutofill:
- if (!element->isFormControlElement())
- break;
- if (HTMLInputElement* inputElement = element->toInputElement())
- return inputElement->isAutofilled();
- break;
- case CSSSelector::PseudoAnyLink:
- case CSSSelector::PseudoLink:
+ case CSSSelector::PseudoClassAutofill:
+ return isAutofilled(element);
+ case CSSSelector::PseudoClassAnyLink:
+ case CSSSelector::PseudoClassAnyLinkDeprecated:
+ case CSSSelector::PseudoClassLink:
// :visited and :link matches are separated later when applying the style. Here both classes match all links...
- return element->isLink();
- case CSSSelector::PseudoVisited:
+ return element.isLink();
+ case CSSSelector::PseudoClassVisited:
// ...except if :visited matching is disabled for ancestor/sibling matching.
- return element->isLink() && context.visitedMatchType == VisitedMatchEnabled;
- case CSSSelector::PseudoDrag:
- if (m_mode == ResolvingStyle) {
- if (context.elementStyle)
- context.elementStyle->setAffectedByDrag();
- else
- element->setChildrenAffectedByDrag(true);
- }
- if (element->renderer() && element->renderer()->isDragging())
+ // Inside functional pseudo class except for :not, :visited never matches.
+ if (context.inFunctionalPseudoClass)
+ return false;
+ return element.isLink() && context.visitedMatchType == VisitedMatchType::Enabled;
+ case CSSSelector::PseudoClassDrag:
+ addStyleRelation(checkingContext, element, StyleRelation::AffectedByDrag);
+
+ if (element.renderer() && element.renderer()->isDragging())
return true;
break;
- case CSSSelector::PseudoFocus:
+ case CSSSelector::PseudoClassFocus:
return matchesFocusPseudoClass(element);
- 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.
- if (m_strictParsing || context.isSubSelector || (selector->m_match == CSSSelector::Tag && selector->tagQName() != anyQName() && !isHTMLAnchorElement(element)) || element->isLink()) {
- if (m_mode == ResolvingStyle) {
- if (context.elementStyle)
- context.elementStyle->setAffectedByHover();
- else
- element->setChildrenAffectedByHover(true);
- }
- if (element->hovered() || InspectorInstrumentation::forcePseudoState(element, CSSSelector::PseudoHover))
+ case CSSSelector::PseudoClassHover:
+ if (m_strictParsing || element.isLink() || canMatchHoverOrActiveInQuirksMode(context)) {
+ addStyleRelation(checkingContext, element, StyleRelation::AffectedByHover);
+
+ if (element.hovered() || InspectorInstrumentation::forcePseudoState(const_cast<Element&>(element), CSSSelector::PseudoClassHover))
return true;
}
break;
- case CSSSelector::PseudoActive:
- // If we're in quirks mode, then :active should never match anchors with no
- // href and *:active should not match anything.
- if (m_strictParsing || context.isSubSelector || (selector->m_match == CSSSelector::Tag && selector->tagQName() != anyQName() && !isHTMLAnchorElement(element)) || element->isLink()) {
- if (m_mode == ResolvingStyle) {
- if (context.elementStyle)
- context.elementStyle->setAffectedByActive();
- else
- element->setChildrenAffectedByActive(true);
- }
- if (element->active() || InspectorInstrumentation::forcePseudoState(element, CSSSelector::PseudoActive))
+ case CSSSelector::PseudoClassActive:
+ if (m_strictParsing || element.isLink() || canMatchHoverOrActiveInQuirksMode(context)) {
+ addStyleRelation(checkingContext, element, StyleRelation::AffectedByActive);
+
+ if (element.active() || InspectorInstrumentation::forcePseudoState(const_cast<Element&>(element), CSSSelector::PseudoClassActive))
return true;
}
break;
- case CSSSelector::PseudoEnabled:
- if (element->isFormControlElement() || isHTMLOptionElement(element) || isHTMLOptGroupElement(element))
- return !element->isDisabledFormControl();
- break;
- case CSSSelector::PseudoFullPageMedia:
- return element->document() && element->document()->isMediaDocument();
- break;
- case CSSSelector::PseudoDefault:
- return element->isDefaultButtonForForm();
- case CSSSelector::PseudoDisabled:
- if (element->isFormControlElement() || isHTMLOptionElement(element) || isHTMLOptGroupElement(element))
- return element->isDisabledFormControl();
- break;
- case CSSSelector::PseudoReadOnly:
- return element->matchesReadOnlyPseudoClass();
- case CSSSelector::PseudoReadWrite:
- return element->matchesReadWritePseudoClass();
- case CSSSelector::PseudoOptional:
- return element->isOptionalFormControl();
- case CSSSelector::PseudoRequired:
- return element->isRequiredFormControl();
- case CSSSelector::PseudoValid:
- element->document()->setContainsValidityStyleRules();
- return element->willValidate() && element->isValidFormControlElement();
- case CSSSelector::PseudoInvalid:
- element->document()->setContainsValidityStyleRules();
- return element->willValidate() && !element->isValidFormControlElement();
- case CSSSelector::PseudoChecked:
- {
- // Even though WinIE allows checked and indeterminate to co-exist, the CSS selector spec says that
- // you can't be both checked and indeterminate. We will behave like WinIE behind the scenes and just
- // obey the CSS spec here in the test for matching the pseudo.
- HTMLInputElement* inputElement = element->toInputElement();
- if (inputElement && inputElement->shouldAppearChecked() && !inputElement->shouldAppearIndeterminate())
- return true;
- if (isHTMLOptionElement(element) && toHTMLOptionElement(element)->selected())
- return true;
- break;
- }
- case CSSSelector::PseudoIndeterminate:
- return element->shouldAppearIndeterminate();
- case CSSSelector::PseudoRoot:
- if (element == element->document()->documentElement())
+ case CSSSelector::PseudoClassEnabled:
+ return isEnabled(element);
+ case CSSSelector::PseudoClassFullPageMedia:
+ return isMediaDocument(element);
+ case CSSSelector::PseudoClassDefault:
+ return isDefaultButtonForForm(element);
+ case CSSSelector::PseudoClassDisabled:
+ return isDisabled(element);
+ case CSSSelector::PseudoClassReadOnly:
+ return matchesReadOnlyPseudoClass(element);
+ case CSSSelector::PseudoClassReadWrite:
+ return matchesReadWritePseudoClass(element);
+ case CSSSelector::PseudoClassOptional:
+ return isOptionalFormControl(element);
+ case CSSSelector::PseudoClassRequired:
+ return isRequiredFormControl(element);
+ case CSSSelector::PseudoClassValid:
+ return isValid(element);
+ case CSSSelector::PseudoClassInvalid:
+ return isInvalid(element);
+ case CSSSelector::PseudoClassChecked:
+ return isChecked(element);
+ case CSSSelector::PseudoClassIndeterminate:
+ return shouldAppearIndeterminate(element);
+ case CSSSelector::PseudoClassRoot:
+ if (&element == element.document().documentElement())
return true;
break;
- case CSSSelector::PseudoLang:
+ case CSSSelector::PseudoClassLang:
{
- AtomicString value;
-#if ENABLE(VIDEO_TRACK)
- if (element->isWebVTTElement())
- value = toWebVTTElement(element)->language();
- else
-#endif
- value = element->computeInheritedLanguage();
- const AtomicString& argument = selector->argument();
- if (value.isEmpty() || !value.startsWith(argument, false))
- break;
- if (value.length() != argument.length() && value[argument.length()] != '-')
- break;
- return true;
+ ASSERT(selector.langArgumentList() && !selector.langArgumentList()->isEmpty());
+ return matchesLangPseudoClass(element, *selector.langArgumentList());
}
#if ENABLE(FULLSCREEN_API)
- case CSSSelector::PseudoFullScreen:
- // While a Document is in the fullscreen state, and the document's current fullscreen
- // element is an element in the document, the 'full-screen' pseudoclass applies to
- // that element. Also, an <iframe>, <object> or <embed> element whose child browsing
- // context's Document is in the fullscreen state has the 'full-screen' pseudoclass applied.
- if (element->isFrameElementBase() && element->containsFullScreenElement())
- return true;
- if (!element->document()->webkitIsFullScreen())
- return false;
- return element == element->document()->webkitCurrentFullScreenElement();
- case CSSSelector::PseudoAnimatingFullScreenTransition:
- if (element != element->document()->webkitCurrentFullScreenElement())
- return false;
- return element->document()->isAnimatingFullScreen();
- case CSSSelector::PseudoFullScreenAncestor:
- return element->containsFullScreenElement();
- case CSSSelector::PseudoFullScreenDocument:
- // While a Document is in the fullscreen state, the 'full-screen-document' pseudoclass applies
- // to all elements of that Document.
- if (!element->document()->webkitIsFullScreen())
- return false;
- return true;
-#endif
-#if ENABLE(IFRAME_SEAMLESS)
- case CSSSelector::PseudoSeamlessDocument:
- // While a document is rendered in a seamless iframe, the 'seamless-document' pseudoclass applies
- // to all elements of that Document.
- return element->document()->shouldDisplaySeamlesslyWithParent();
+ case CSSSelector::PseudoClassFullScreen:
+ return matchesFullScreenPseudoClass(element);
+ case CSSSelector::PseudoClassAnimatingFullScreenTransition:
+ return matchesFullScreenAnimatingFullScreenTransitionPseudoClass(element);
+ case CSSSelector::PseudoClassFullScreenAncestor:
+ return matchesFullScreenAncestorPseudoClass(element);
+ case CSSSelector::PseudoClassFullScreenDocument:
+ return matchesFullScreenDocumentPseudoClass(element);
#endif
- case CSSSelector::PseudoInRange:
- element->document()->setContainsValidityStyleRules();
- return element->isInRange();
- case CSSSelector::PseudoOutOfRange:
- element->document()->setContainsValidityStyleRules();
- return element->isOutOfRange();
+ case CSSSelector::PseudoClassInRange:
+ return isInRange(element);
+ case CSSSelector::PseudoClassOutOfRange:
+ return isOutOfRange(element);
#if ENABLE(VIDEO_TRACK)
- case CSSSelector::PseudoFutureCue:
- return (element->isWebVTTElement() && !toWebVTTElement(element)->isPastNode());
- case CSSSelector::PseudoPastCue:
- return (element->isWebVTTElement() && toWebVTTElement(element)->isPastNode());
+ case CSSSelector::PseudoClassFuture:
+ return matchesFutureCuePseudoClass(element);
+ case CSSSelector::PseudoClassPast:
+ return matchesPastCuePseudoClass(element);
#endif
- case CSSSelector::PseudoScope:
+ case CSSSelector::PseudoClassScope:
{
- const Node* contextualReferenceNode = !context.scope || context.behaviorAtBoundary == CrossesBoundary ? element->document()->documentElement() : context.scope;
- if (element == contextualReferenceNode)
+ const Node* contextualReferenceNode = !checkingContext.scope ? element.document().documentElement() : checkingContext.scope;
+ if (&element == contextualReferenceNode)
return true;
break;
}
+#if ENABLE(SHADOW_DOM)
+ case CSSSelector::PseudoClassHost:
+ // :host matches based on context. Cases that reach selector checker don't match.
+ return false;
+#endif
+ case CSSSelector::PseudoClassWindowInactive:
+ return isWindowInactive(element);
+
+ case CSSSelector::PseudoClassHorizontal:
+ case CSSSelector::PseudoClassVertical:
+ case CSSSelector::PseudoClassDecrement:
+ case CSSSelector::PseudoClassIncrement:
+ case CSSSelector::PseudoClassStart:
+ case CSSSelector::PseudoClassEnd:
+ case CSSSelector::PseudoClassDoubleButton:
+ case CSSSelector::PseudoClassSingleButton:
+ case CSSSelector::PseudoClassNoButton:
+ case CSSSelector::PseudoClassCornerPresent:
+ return false;
- case CSSSelector::PseudoHorizontal:
- case CSSSelector::PseudoVertical:
- case CSSSelector::PseudoDecrement:
- case CSSSelector::PseudoIncrement:
- case CSSSelector::PseudoStart:
- case CSSSelector::PseudoEnd:
- case CSSSelector::PseudoDoubleButton:
- case CSSSelector::PseudoSingleButton:
- case CSSSelector::PseudoNoButton:
- case CSSSelector::PseudoCornerPresent:
+#if ENABLE(CSS_SELECTORS_LEVEL4)
+ // FIXME: Implement :dir() selector.
+ case CSSSelector::PseudoClassDir:
return false;
- case CSSSelector::PseudoUnknown:
- case CSSSelector::PseudoNotParsed:
- default:
+ // FIXME: Implement :role() selector.
+ case CSSSelector::PseudoClassRole:
+ return false;
+#endif
+
+ case CSSSelector::PseudoClassUnknown:
ASSERT_NOT_REACHED();
break;
}
return false;
}
#if ENABLE(VIDEO_TRACK)
- else if (selector->m_match == CSSSelector::PseudoElement && selector->pseudoType() == CSSSelector::PseudoCue) {
- SelectorCheckingContext subContext(context);
- subContext.isSubSelector = true;
+ if (selector.match() == CSSSelector::PseudoElement && selector.pseudoElementType() == CSSSelector::PseudoElementCue) {
+ LocalContext subcontext(context);
- PseudoId ignoreDynamicPseudo = NOPSEUDO;
const CSSSelector* const & selector = context.selector;
- for (subContext.selector = selector->selectorList()->first(); subContext.selector; subContext.selector = CSSSelectorList::next(subContext.selector)) {
- if (match(subContext, ignoreDynamicPseudo) == SelectorMatches)
+ for (subcontext.selector = selector->selectorList()->first(); subcontext.selector; subcontext.selector = CSSSelectorList::next(subcontext.selector)) {
+ subcontext.firstSelectorOfTheFragment = subcontext.selector;
+ subcontext.inFunctionalPseudoClass = true;
+ subcontext.pseudoElementEffective = false;
+ PseudoIdSet ignoredDynamicPseudo;
+ unsigned ignoredSpecificity = 0;
+ if (matchRecursively(checkingContext, subcontext, ignoredDynamicPseudo, ignoredSpecificity).match == Match::SelectorMatches)
return true;
}
return false;
@@ -805,82 +1042,65 @@ bool SelectorChecker::checkOne(const SelectorCheckingContext& context) const
return true;
}
-bool SelectorChecker::checkScrollbarPseudoClass(const SelectorCheckingContext& context, Document* document, const CSSSelector* selector) const
+bool SelectorChecker::matchSelectorList(CheckingContext& checkingContext, const LocalContext& context, const Element& element, const CSSSelectorList& selectorList, unsigned& specificity) const
{
- RenderScrollbar* scrollbar = context.scrollbar;
- ScrollbarPart part = context.scrollbarPart;
-
- // FIXME: This is a temporary hack for resizers and scrollbar corners. Eventually :window-inactive should become a real
- // pseudo class and just apply to everything.
- if (selector->pseudoType() == CSSSelector::PseudoWindowInactive)
- return !document->page()->focusController()->isActive();
-
- if (!scrollbar)
- return false;
-
- ASSERT(selector->m_match == CSSSelector::PseudoClass);
- switch (selector->pseudoType()) {
- case CSSSelector::PseudoEnabled:
- return scrollbar->enabled();
- case CSSSelector::PseudoDisabled:
- return !scrollbar->enabled();
- case CSSSelector::PseudoHover:
- {
- ScrollbarPart hoveredPart = scrollbar->hoveredPart();
- if (part == ScrollbarBGPart)
- return hoveredPart != NoPart;
- if (part == TrackBGPart)
- return hoveredPart == BackTrackPart || hoveredPart == ForwardTrackPart || hoveredPart == ThumbPart;
- return part == hoveredPart;
- }
- case CSSSelector::PseudoActive:
- {
- ScrollbarPart pressedPart = scrollbar->pressedPart();
- if (part == ScrollbarBGPart)
- return pressedPart != NoPart;
- if (part == TrackBGPart)
- return pressedPart == BackTrackPart || pressedPart == ForwardTrackPart || pressedPart == ThumbPart;
- return part == pressedPart;
- }
- case CSSSelector::PseudoHorizontal:
- return scrollbar->orientation() == HorizontalScrollbar;
- case CSSSelector::PseudoVertical:
- return scrollbar->orientation() == VerticalScrollbar;
- case CSSSelector::PseudoDecrement:
- return part == BackButtonStartPart || part == BackButtonEndPart || part == BackTrackPart;
- case CSSSelector::PseudoIncrement:
- return part == ForwardButtonStartPart || part == ForwardButtonEndPart || part == ForwardTrackPart;
- case CSSSelector::PseudoStart:
- return part == BackButtonStartPart || part == ForwardButtonStartPart || part == BackTrackPart;
- case CSSSelector::PseudoEnd:
- return part == BackButtonEndPart || part == ForwardButtonEndPart || part == ForwardTrackPart;
- case CSSSelector::PseudoDoubleButton:
- {
- ScrollbarButtonsPlacement buttonsPlacement = scrollbar->theme()->buttonsPlacement();
- if (part == BackButtonStartPart || part == ForwardButtonStartPart || part == BackTrackPart)
- return buttonsPlacement == ScrollbarButtonsDoubleStart || buttonsPlacement == ScrollbarButtonsDoubleBoth;
- if (part == BackButtonEndPart || part == ForwardButtonEndPart || part == ForwardTrackPart)
- return buttonsPlacement == ScrollbarButtonsDoubleEnd || buttonsPlacement == ScrollbarButtonsDoubleBoth;
- return false;
- }
- case CSSSelector::PseudoSingleButton:
- {
- ScrollbarButtonsPlacement buttonsPlacement = scrollbar->theme()->buttonsPlacement();
- if (part == BackButtonStartPart || part == ForwardButtonEndPart || part == BackTrackPart || part == ForwardTrackPart)
- return buttonsPlacement == ScrollbarButtonsSingle;
- return false;
- }
- case CSSSelector::PseudoNoButton:
- {
- ScrollbarButtonsPlacement buttonsPlacement = scrollbar->theme()->buttonsPlacement();
- if (part == BackTrackPart)
- return buttonsPlacement == ScrollbarButtonsNone || buttonsPlacement == ScrollbarButtonsDoubleEnd;
- if (part == ForwardTrackPart)
- return buttonsPlacement == ScrollbarButtonsNone || buttonsPlacement == ScrollbarButtonsDoubleStart;
- return false;
+ specificity = 0;
+ bool hasMatchedAnything = false;
+
+ for (const CSSSelector* subselector = selectorList.first(); subselector; subselector = CSSSelectorList::next(subselector)) {
+ LocalContext subcontext(context);
+ subcontext.element = &element;
+ subcontext.selector = subselector;
+ subcontext.inFunctionalPseudoClass = true;
+ subcontext.pseudoElementEffective = false;
+ subcontext.firstSelectorOfTheFragment = subselector;
+ PseudoIdSet ignoreDynamicPseudo;
+ unsigned localSpecificity = 0;
+ if (matchRecursively(checkingContext, subcontext, ignoreDynamicPseudo, localSpecificity).match == Match::SelectorMatches) {
+ ASSERT(!ignoreDynamicPseudo);
+
+ hasMatchedAnything = true;
+ specificity = std::max(specificity, localSpecificity);
}
- case CSSSelector::PseudoCornerPresent:
- return scrollbar->scrollableArea() ? scrollbar->scrollableArea()->isScrollCornerVisible() : false;
+ }
+ return hasMatchedAnything;
+}
+
+bool SelectorChecker::checkScrollbarPseudoClass(const CheckingContext& checkingContext, const Element& element, const CSSSelector& selector) const
+{
+ ASSERT(selector.match() == CSSSelector::PseudoClass);
+
+ switch (selector.pseudoClassType()) {
+ case CSSSelector::PseudoClassWindowInactive:
+ return isWindowInactive(element);
+ case CSSSelector::PseudoClassEnabled:
+ return scrollbarMatchesEnabledPseudoClass(checkingContext);
+ case CSSSelector::PseudoClassDisabled:
+ return scrollbarMatchesDisabledPseudoClass(checkingContext);
+ case CSSSelector::PseudoClassHover:
+ return scrollbarMatchesHoverPseudoClass(checkingContext);
+ case CSSSelector::PseudoClassActive:
+ return scrollbarMatchesActivePseudoClass(checkingContext);
+ case CSSSelector::PseudoClassHorizontal:
+ return scrollbarMatchesHorizontalPseudoClass(checkingContext);
+ case CSSSelector::PseudoClassVertical:
+ return scrollbarMatchesVerticalPseudoClass(checkingContext);
+ case CSSSelector::PseudoClassDecrement:
+ return scrollbarMatchesDecrementPseudoClass(checkingContext);
+ case CSSSelector::PseudoClassIncrement:
+ return scrollbarMatchesIncrementPseudoClass(checkingContext);
+ case CSSSelector::PseudoClassStart:
+ return scrollbarMatchesStartPseudoClass(checkingContext);
+ case CSSSelector::PseudoClassEnd:
+ return scrollbarMatchesEndPseudoClass(checkingContext);
+ case CSSSelector::PseudoClassDoubleButton:
+ return scrollbarMatchesDoubleButtonPseudoClass(checkingContext);
+ case CSSSelector::PseudoClassSingleButton:
+ return scrollbarMatchesSingleButtonPseudoClass(checkingContext);
+ case CSSSelector::PseudoClassNoButton:
+ return scrollbarMatchesNoButtonPseudoClass(checkingContext);
+ case CSSSelector::PseudoClassCornerPresent:
+ return scrollbarMatchesCornerPresentPseudoClass(checkingContext);
default:
return false;
}
@@ -893,32 +1113,17 @@ unsigned SelectorChecker::determineLinkMatchType(const CSSSelector* selector)
// Statically determine if this selector will match a link in visited, unvisited or any state, or never.
// :visited never matches other elements than the innermost link element.
for (; selector; selector = selector->tagHistory()) {
- switch (selector->pseudoType()) {
- case CSSSelector::PseudoNot:
- {
- // :not(:visited) is equivalent to :link. Parser enforces that :not can't nest.
- const CSSSelectorList* selectorList = selector->selectorList();
- if (!selectorList)
- break;
-
- for (const CSSSelector* subSelector = selectorList->first(); subSelector; subSelector = subSelector->tagHistory()) {
- CSSSelector::PseudoType subType = subSelector->pseudoType();
- if (subType == CSSSelector::PseudoVisited)
- linkMatchType &= ~SelectorChecker::MatchVisited;
- else if (subType == CSSSelector::PseudoLink)
- linkMatchType &= ~SelectorChecker::MatchLink;
- }
+ if (selector->match() == CSSSelector::PseudoClass) {
+ switch (selector->pseudoClassType()) {
+ case CSSSelector::PseudoClassLink:
+ linkMatchType &= ~SelectorChecker::MatchVisited;
+ break;
+ case CSSSelector::PseudoClassVisited:
+ linkMatchType &= ~SelectorChecker::MatchLink;
+ break;
+ default:
+ break;
}
- break;
- case CSSSelector::PseudoLink:
- linkMatchType &= ~SelectorChecker::MatchVisited;
- break;
- case CSSSelector::PseudoVisited:
- linkMatchType &= ~SelectorChecker::MatchLink;
- break;
- default:
- // We don't support :link and :visited inside :-webkit-any.
- break;
}
CSSSelector::Relation relation = selector->relation();
if (relation == CSSSelector::SubSelector)
@@ -931,16 +1136,16 @@ unsigned SelectorChecker::determineLinkMatchType(const CSSSelector* selector)
return linkMatchType;
}
-bool SelectorChecker::isFrameFocused(const Element* element)
+static bool isFrameFocused(const Element& element)
{
- return element->document()->frame() && element->document()->frame()->selection()->isFocusedAndActive();
+ return element.document().frame() && element.document().frame()->selection().isFocusedAndActive();
}
-bool SelectorChecker::matchesFocusPseudoClass(const Element* element)
+bool SelectorChecker::matchesFocusPseudoClass(const Element& element)
{
- if (InspectorInstrumentation::forcePseudoState(const_cast<Element*>(element), CSSSelector::PseudoFocus))
+ if (InspectorInstrumentation::forcePseudoState(const_cast<Element&>(element), CSSSelector::PseudoClassFocus))
return true;
- return element->focused() && isFrameFocused(element);
+ return element.focused() && isFrameFocused(element);
}
}