#include "config.h"
#include "core/css/SelectorChecker.h"
-#include "HTMLNames.h"
+#include "core/HTMLNames.h"
#include "core/css/CSSSelectorList.h"
#include "core/css/SiblingTraversalStrategies.h"
#include "core/dom/Document.h"
#include "core/dom/ElementTraversal.h"
-#include "core/dom/FullscreenElementStack.h"
+#include "core/dom/Fullscreen.h"
#include "core/dom/NodeRenderStyle.h"
+#include "core/dom/StyleEngine.h"
#include "core/dom/Text.h"
#include "core/dom/shadow/InsertionPoint.h"
#include "core/dom/shadow/ShadowRoot.h"
#include "core/editing/FrameSelection.h"
+#include "core/frame/LocalFrame.h"
#include "core/html/HTMLDocument.h"
#include "core/html/HTMLFrameElementBase.h"
#include "core/html/HTMLInputElement.h"
#include "core/html/HTMLOptionElement.h"
+#include "core/html/HTMLSelectElement.h"
#include "core/html/parser/HTMLParserIdioms.h"
#include "core/html/track/vtt/VTTElement.h"
#include "core/inspector/InspectorInstrumentation.h"
#include "core/page/FocusController.h"
-#include "core/frame/Frame.h"
+#include "core/page/Page.h"
#include "core/rendering/RenderObject.h"
#include "core/rendering/RenderScrollbar.h"
#include "core/rendering/style/RenderStyle.h"
#include "platform/scroll/ScrollableArea.h"
#include "platform/scroll/ScrollbarTheme.h"
-namespace WebCore {
+namespace blink {
using namespace HTMLNames;
SelectorChecker::SelectorChecker(Document& document, Mode mode)
: m_strictParsing(!document.inQuirksMode())
- , m_documentIsHTML(document.isHTMLDocument())
, m_mode(mode)
{
}
return true;
}
-Element* SelectorChecker::parentElement(const SelectorCheckingContext& context, bool allowToCrossBoundary) const
+static Element* parentElement(const SelectorChecker::SelectorCheckingContext& context)
{
- // CrossesBoundary means we don't care any context.scope. So we can walk up from a shadow root to its shadow host.
- if (allowToCrossBoundary)
+ // - If context.scope is a shadow root, we should walk up to its shadow host.
+ // - If context.scope is some element in some shadow tree and querySelector initialized the context,
+ // e.g. shadowRoot.querySelector(':host *'),
+ // (a) context.element has the same treescope as context.scope, need to walk up to its shadow host.
+ // (b) Otherwise, should not walk up from a shadow root to a shadow host.
+ if (context.scope && (context.scope == context.element->containingShadowRoot() || context.scope->treeScope() == context.element->treeScope()))
return context.element->parentOrShadowHostElement();
-
- // If context.scope is not a shadow host, we cannot walk up from a shadow root to its shadow host.
- if (!(context.behaviorAtBoundary & SelectorChecker::ScopeIsShadowHost))
- return context.element->parentElement();
-
- // If behaviorAtBoundary is StaysWithInTreeScope, we cannot walk up from a shadow root to its shadow host.
- return (context.behaviorAtBoundary & SelectorChecker::BoundaryBehaviorMask) != SelectorChecker::StaysWithinTreeScope ? context.element->parentOrShadowHostElement() : context.element->parentElement();
+ return context.element->parentElement();
}
-bool SelectorChecker::scopeContainsLastMatchedElement(const SelectorCheckingContext& context) const
+static bool scopeContainsLastMatchedElement(const SelectorChecker::SelectorCheckingContext& context)
{
- if (!(context.behaviorAtBoundary & SelectorChecker::ScopeContainsLastMatchedElement))
+ if (!(context.contextFlags & SelectorChecker::ScopeContainsLastMatchedElement))
return true;
ASSERT(context.scope);
- // If behaviorAtBoundary is not ScopeIsShadowHost, we can use "contains".
- if (!(context.behaviorAtBoundary & SelectorChecker::ScopeIsShadowHost))
- return context.scope->contains(context.element);
-
- // If a given element is scope, i.e. shadow host, matches.
- if (context.element == context.scope)
+ if (context.scope->treeScope() == context.element->treeScope())
return true;
- ShadowRoot* root = context.element->containingShadowRoot();
- if (!root)
- return false;
-
- // If a host of the containing shadow root is scope, matches.
- return root->host() == context.scope;
+ // Because Blink treats a shadow host's TreeScope as a separate one from its descendent shadow roots,
+ // if the last matched element is a shadow host, the condition above isn't met, even though it
+ // should be.
+ return context.element == context.scope->shadowHost() && (!context.previousElement || context.previousElement->isInDescendantTreeOf(context.element));
}
static inline bool nextSelectorExceedsScope(const SelectorChecker::SelectorCheckingContext& context)
{
- return context.element == context.scope && (context.behaviorAtBoundary & SelectorChecker::BoundaryBehaviorMask) != SelectorChecker::StaysWithinTreeScope;
+ if (context.scope && context.scope->isInShadowTree())
+ return context.element == context.scope->shadowHost();
+
+ return false;
}
// Recursive check of selectors and combinators
if (!checkOne(context, siblingTraversalStrategy, &specificity))
return SelectorFailsLocally;
- if (context.selector->m_match == CSSSelector::PseudoElement) {
+ if (context.selector->match() == CSSSelector::PseudoElement) {
if (context.selector->isCustomPseudoElement()) {
if (!matchesCustomPseudoElement(context.element, *context.selector))
return SelectorFailsLocally;
} else if (context.selector->isContentPseudoElement()) {
if (!context.element->isInShadowTree() || !context.element->isInsertionPoint())
return SelectorFailsLocally;
+ } else if (context.selector->isShadowPseudoElement()) {
+ if (!context.element->isInShadowTree() || !context.previousElement)
+ return SelectorFailsCompletely;
} else {
if ((!context.elementStyle && m_mode == ResolvingStyle) || m_mode == QueryingRules)
return SelectorFailsLocally;
}
// Prepare next selector
- const CSSSelector* historySelector = context.selector->tagHistory();
- if (!historySelector) {
+ if (context.selector->isLastInTagHistory()) {
if (scopeContainsLastMatchedElement(context)) {
if (result)
result->specificity += specificity;
return nextContext;
}
+static inline bool isAuthorShadowRoot(const Node* node)
+{
+ return node && node->isShadowRoot() && toShadowRoot(node)->type() == ShadowRoot::AuthorShadowRoot;
+}
+
template<typename SiblingTraversalStrategy>
SelectorChecker::Match SelectorChecker::matchForSubSelector(const SelectorCheckingContext& context, const SiblingTraversalStrategy& siblingTraversalStrategy, MatchResult* result) const
{
nextContext.hasSelectionPseudo = dynamicPseudo == SELECTION;
if ((context.elementStyle || m_mode == CollectingCSSRules || m_mode == CollectingStyleRules || m_mode == QueryingRules) && dynamicPseudo != NOPSEUDO
&& !nextContext.hasSelectionPseudo
- && !(nextContext.hasScrollbarPseudo && nextContext.selector->m_match == CSSSelector::PseudoClass))
+ && !(nextContext.hasScrollbarPseudo && nextContext.selector->match() == CSSSelector::PseudoClass))
return SelectorFailsCompletely;
nextContext.isSubSelector = true;
return match(nextContext, siblingTraversalStrategy, result);
}
+static bool selectorMatchesShadowRoot(const CSSSelector* selector)
+{
+ return selector && selector->isShadowPseudoElement();
+}
+
+template<typename SiblingTraversalStrategy>
+SelectorChecker::Match SelectorChecker::matchForPseudoShadow(const ContainerNode* node, const SelectorCheckingContext& context, const SiblingTraversalStrategy& siblingTraversalStrategy, MatchResult* result) const
+{
+ if (!isAuthorShadowRoot(node))
+ return SelectorFailsCompletely;
+ return match(context, siblingTraversalStrategy, result);
+}
+
template<typename SiblingTraversalStrategy>
SelectorChecker::Match SelectorChecker::matchForRelation(const SelectorCheckingContext& context, const SiblingTraversalStrategy& siblingTraversalStrategy, MatchResult* result) const
{
SelectorCheckingContext nextContext = prepareNextContextForRelation(context);
+ nextContext.previousElement = context.element;
CSSSelector::Relation relation = context.selector->relation();
}
nextContext.isSubSelector = false;
nextContext.elementStyle = 0;
+
+ if (selectorMatchesShadowRoot(nextContext.selector))
+ return matchForPseudoShadow(context.element->containingShadowRoot(), nextContext, siblingTraversalStrategy, result);
+
for (nextContext.element = parentElement(context); nextContext.element; nextContext.element = parentElement(nextContext)) {
Match match = this->match(nextContext, siblingTraversalStrategy, result);
if (match == SelectorMatches || match == SelectorFailsCompletely)
if (context.selector->relationIsAffectedByPseudoContent())
return matchForShadowDistributed(context.element, siblingTraversalStrategy, nextContext, result);
+ nextContext.isSubSelector = false;
+ nextContext.elementStyle = 0;
+
+ if (selectorMatchesShadowRoot(nextContext.selector))
+ return matchForPseudoShadow(context.element->parentNode(), nextContext, siblingTraversalStrategy, result);
+
nextContext.element = parentElement(context);
if (!nextContext.element)
return SelectorFailsCompletely;
-
- nextContext.isSubSelector = false;
- nextContext.elementStyle = 0;
return match(nextContext, siblingTraversalStrategy, result);
}
case CSSSelector::DirectAdjacent:
+ // Shadow roots can't have sibling elements
+ if (selectorMatchesShadowRoot(nextContext.selector))
+ return SelectorFailsCompletely;
+
if (m_mode == ResolvingStyle) {
- if (Element* parent = parentElement(context))
+ if (ContainerNode* parent = context.element->parentElementOrShadowRoot())
parent->setChildrenAffectedByDirectAdjacentRules();
}
nextContext.element = ElementTraversal::previousSibling(*context.element);
return match(nextContext, siblingTraversalStrategy, result);
case CSSSelector::IndirectAdjacent:
+ // Shadow roots can't have sibling elements
+ if (selectorMatchesShadowRoot(nextContext.selector))
+ return SelectorFailsCompletely;
+
if (m_mode == ResolvingStyle) {
- if (Element* parent = parentElement(context))
- parent->setChildrenAffectedByForwardPositionalRules();
+ if (ContainerNode* parent = context.element->parentElementOrShadowRoot())
+ parent->setChildrenAffectedByIndirectAdjacentRules();
}
nextContext.element = ElementTraversal::previousSibling(*context.element);
nextContext.isSubSelector = false;
return SelectorFailsAllSiblings;
case CSSSelector::ShadowPseudo:
- case CSSSelector::ChildTree:
{
// 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 & BoundaryBehaviorMask) != StaysWithinTreeScope)
+ if (context.scope && context.scope->shadowHost() && context.scope->shadowHost()->treeScope() == context.element->treeScope())
return SelectorFailsCompletely;
Element* shadowHost = context.element->shadowHost();
return this->match(nextContext, siblingTraversalStrategy, result);
}
- case CSSSelector::DescendantTree:
+ case CSSSelector::ShadowDeep:
{
nextContext.isSubSelector = false;
nextContext.elementStyle = 0;
- for (nextContext.element = parentElement(context, true); nextContext.element; nextContext.element = parentElement(nextContext, true)) {
+ for (nextContext.element = context.element->parentOrShadowHostElement(); nextContext.element; nextContext.element = nextContext.element->parentOrShadowHostElement()) {
Match match = this->match(nextContext, siblingTraversalStrategy, result);
if (match == SelectorMatches || match == SelectorFailsCompletely)
return match;
SelectorChecker::Match SelectorChecker::matchForShadowDistributed(const Element* element, const SiblingTraversalStrategy& siblingTraversalStrategy, SelectorCheckingContext& nextContext, MatchResult* result) const
{
ASSERT(element);
- Vector<InsertionPoint*, 8> insertionPoints;
-
- const ContainerNode* scope = nextContext.scope;
- BehaviorAtBoundary behaviorAtBoundary = nextContext.behaviorAtBoundary;
-
+ WillBeHeapVector<RawPtrWillBeMember<InsertionPoint>, 8> insertionPoints;
collectDestinationInsertionPoints(*element, insertionPoints);
for (size_t i = 0; i < insertionPoints.size(); ++i) {
nextContext.element = insertionPoints[i];
-
- // If a given scope is a shadow host of an insertion point but behaviorAtBoundary doesn't have ScopeIsShadowHost,
- // we need to update behaviorAtBoundary to make selectors like ":host > ::content" work correctly.
- if (scope == insertionPoints[i]->containingShadowRoot()->shadowHost() && !(behaviorAtBoundary & ScopeIsShadowHost))
- nextContext.behaviorAtBoundary = static_cast<BehaviorAtBoundary>(behaviorAtBoundary | ScopeIsShadowHost);
- else
- nextContext.behaviorAtBoundary = behaviorAtBoundary;
-
+ if (m_mode == SharingRules)
+ nextContext.scope = insertionPoints[i]->containingShadowRoot();
nextContext.isSubSelector = false;
nextContext.elementStyle = 0;
if (match(nextContext, siblingTraversalStrategy, result) == SelectorMatches)
return SelectorMatches;
}
- return SelectorFailsCompletely;
+ return SelectorFailsLocally;
}
-static inline bool containsHTMLSpace(const AtomicString& string)
+template<typename CharType>
+static inline bool containsHTMLSpaceTemplate(const CharType* string, unsigned length)
{
- for (unsigned i = 0; i < string.length(); i++)
- if (isHTMLSpace<UChar>(string[i]))
+ for (unsigned i = 0; i < length; ++i)
+ if (isHTMLSpace<CharType>(string[i]))
return true;
return false;
}
-static bool attributeValueMatches(const Attribute* attributeItem, CSSSelector::Match match, const AtomicString& selectorValue, bool caseSensitive)
+static inline bool containsHTMLSpace(const AtomicString& string)
+{
+ if (LIKELY(string.is8Bit()))
+ return containsHTMLSpaceTemplate<LChar>(string.characters8(), string.length());
+ return containsHTMLSpaceTemplate<UChar>(string.characters16(), string.length());
+}
+
+static bool attributeValueMatches(const Attribute& attributeItem, CSSSelector::Match match, const AtomicString& selectorValue, bool caseSensitive)
{
- const AtomicString& value = attributeItem->value();
+ const AtomicString& value = attributeItem.value();
if (value.isNull())
return false;
// Currently all lazy properties have a null namespace, so only pass localName().
element.synchronizeAttribute(selectorAttr.localName());
- if (!element.hasAttributesWithoutUpdate())
- return false;
-
- const AtomicString& selectorValue = selector.value();
+ const AtomicString& selectorValue = selector.value();
+ bool caseInsensitive = selector.attributeMatchType() == CSSSelector::CaseInsensitive;
- for (size_t i = 0; i < element.attributeCount(); ++i) {
- const Attribute* attributeItem = element.attributeItem(i);
+ AttributeCollection attributes = element.attributesWithoutUpdate();
+ AttributeCollection::iterator end = attributes.end();
+ for (AttributeCollection::iterator it = attributes.begin(); it != end; ++it) {
+ const Attribute& attributeItem = *it;
- if (!attributeItem->matches(selectorAttr))
+ if (!attributeItem.matches(selectorAttr))
continue;
- if (attributeValueMatches(attributeItem, match, selectorValue, true))
+ if (attributeValueMatches(attributeItem, match, selectorValue, !caseInsensitive))
return true;
- // Case sensitivity for attribute matching is looser than hasAttribute or
- // Element::shouldIgnoreAttributeCase() for now. Unclear if that's correct.
- bool caseSensitive = !element.document().isHTMLDocument() || HTMLDocument::isCaseSensitiveAttribute(selectorAttr);
+ if (caseInsensitive)
+ continue;
+
+ // Legacy dictates that values of some attributes should be compared in
+ // a case-insensitive manner regardless of whether the case insensitive
+ // flag is set or not.
+ bool legacyCaseInsensitive = element.document().isHTMLDocument() && !HTMLDocument::isCaseSensitiveAttribute(selectorAttr);
// If case-insensitive, re-check, and count if result differs.
// See http://code.google.com/p/chromium/issues/detail?id=327060
- if (!caseSensitive && attributeValueMatches(attributeItem, match, selectorValue, false)) {
+ if (legacyCaseInsensitive && attributeValueMatches(attributeItem, match, selectorValue, false)) {
UseCounter::count(element.document(), UseCounter::CaseInsensitiveAttrSelectorMatch);
return true;
}
ASSERT(context.selector);
const CSSSelector& selector = *context.selector;
- bool elementIsHostInItsShadowTree = isHostInItsShadowTree(element, context.behaviorAtBoundary, context.scope);
+ bool elementIsHostInItsShadowTree = isHostInItsShadowTree(element, context.scope);
- if (selector.m_match == CSSSelector::Tag)
- return SelectorChecker::tagMatches(element, selector.tagQName(), elementIsHostInItsShadowTree ? MatchingHostInItsShadowTree : MatchingElement);
+ // Only :host and :host-context() should match the host: http://drafts.csswg.org/css-scoping/#host-element
+ if (elementIsHostInItsShadowTree && (!selector.isHostPseudoClass()
+ && !(context.contextFlags & TreatShadowHostAsNormalScope)
+ && selector.match() != CSSSelector::PseudoElement))
+ return false;
- if (selector.m_match == CSSSelector::Class)
- return element.hasClass() && element.classNames().contains(selector.value()) && !elementIsHostInItsShadowTree;
+ if (selector.match() == CSSSelector::Tag)
+ return SelectorChecker::tagMatches(element, selector.tagQName());
- if (selector.m_match == CSSSelector::Id)
- return element.hasID() && element.idForStyleResolution() == selector.value() && !elementIsHostInItsShadowTree;
+ if (selector.match() == CSSSelector::Class)
+ return element.hasClass() && element.classNames().contains(selector.value());
- if (selector.isAttributeSelector()) {
- if (elementIsHostInItsShadowTree)
- return false;
- if (!anyAttributeMatches(element, static_cast<CSSSelector::Match>(selector.m_match), selector))
- return false;
- }
+ if (selector.match() == CSSSelector::Id)
+ return element.hasID() && element.idForStyleResolution() == selector.value();
- if (selector.m_match == CSSSelector::PseudoClass) {
+ if (selector.isAttributeSelector())
+ return anyAttributeMatches(element, selector.match(), selector);
+
+ if (selector.match() == CSSSelector::PseudoClass) {
// Handle :not up front.
if (selector.pseudoType() == CSSSelector::PseudoNot) {
SelectorCheckingContext subContext(context);
// 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;
+ // context.scope is not available if m_mode == SharingRules.
+ // We cannot determine whether :host or :scope matches a given element or not.
+ if (m_mode == SharingRules && (subContext.selector->isHostPseudoClass() || subContext.selector->pseudoType() == CSSSelector::PseudoScope))
+ return true;
if (!checkOne(subContext, DOMSiblingTraversalStrategy()))
return true;
}
}
case CSSSelector::PseudoFirstChild:
// first-child matches the first child that is an element
- if (Element* parent = element.parentElement()) {
+ if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
bool result = siblingTraversalStrategy.isFirstChild(element);
if (m_mode == ResolvingStyle) {
RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element.renderStyle();
break;
case CSSSelector::PseudoFirstOfType:
// first-of-type matches the first element of its type
- if (Element* parent = element.parentElement()) {
+ if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
bool result = siblingTraversalStrategy.isFirstOfType(element, element.tagQName());
if (m_mode == ResolvingStyle)
parent->setChildrenAffectedByForwardPositionalRules();
break;
case CSSSelector::PseudoLastChild:
// last-child matches the last child that is an element
- if (Element* parent = element.parentElement()) {
+ if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
bool result = parent->isFinishedParsingChildren() && siblingTraversalStrategy.isLastChild(element);
if (m_mode == ResolvingStyle) {
RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element.renderStyle();
break;
case CSSSelector::PseudoLastOfType:
// last-of-type matches the last element of its type
- if (Element* parent = element.parentElement()) {
+ if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
if (m_mode == ResolvingStyle)
parent->setChildrenAffectedByBackwardPositionalRules();
if (!parent->isFinishedParsingChildren())
}
break;
case CSSSelector::PseudoOnlyChild:
- if (Element* parent = element.parentElement()) {
+ if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
bool firstChild = siblingTraversalStrategy.isFirstChild(element);
bool onlyChild = firstChild && parent->isFinishedParsingChildren() && siblingTraversalStrategy.isLastChild(element);
if (m_mode == ResolvingStyle) {
break;
case CSSSelector::PseudoOnlyOfType:
// FIXME: This selector is very slow.
- if (Element* parent = element.parentElement()) {
+ if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
if (m_mode == ResolvingStyle) {
parent->setChildrenAffectedByForwardPositionalRules();
parent->setChildrenAffectedByBackwardPositionalRules();
case CSSSelector::PseudoNthChild:
if (!selector.parseNth())
break;
- if (Element* parent = element.parentElement()) {
+ if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
int count = 1 + siblingTraversalStrategy.countElementsBefore(element);
if (m_mode == ResolvingStyle) {
RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element.renderStyle();
- element.setChildIndex(count);
if (childStyle)
childStyle->setUnique();
parent->setChildrenAffectedByForwardPositionalRules();
case CSSSelector::PseudoNthOfType:
if (!selector.parseNth())
break;
- if (Element* parent = element.parentElement()) {
+ if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
int count = 1 + siblingTraversalStrategy.countElementsOfTypeBefore(element, element.tagQName());
if (m_mode == ResolvingStyle)
parent->setChildrenAffectedByForwardPositionalRules();
case CSSSelector::PseudoNthLastChild:
if (!selector.parseNth())
break;
- if (Element* parent = element.parentElement()) {
+ if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
if (m_mode == ResolvingStyle)
parent->setChildrenAffectedByBackwardPositionalRules();
if (!parent->isFinishedParsingChildren())
case CSSSelector::PseudoNthLastOfType:
if (!selector.parseNth())
break;
- if (Element* parent = element.parentElement()) {
+ if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
if (m_mode == ResolvingStyle)
parent->setChildrenAffectedByBackwardPositionalRules();
if (!parent->isFinishedParsingChildren())
if (context.elementStyle)
context.elementStyle->setAffectedByDrag();
else
- element.setChildrenAffectedByDrag();
+ element.setChildrenOrSiblingsAffectedByDrag();
}
if (element.renderer() && element.renderer()->isDragging())
return true;
if (context.elementStyle)
context.elementStyle->setAffectedByFocus();
else
- element.setChildrenAffectedByFocus();
+ element.setChildrenOrSiblingsAffectedByFocus();
}
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() && !element.hasTagName(aTag)) || element.isLink()) {
+ if (m_strictParsing || context.isSubSelector || element.isLink()) {
if (m_mode == ResolvingStyle) {
if (context.elementStyle)
context.elementStyle->setAffectedByHover();
else
- element.setChildrenAffectedByHover();
+ element.setChildrenOrSiblingsAffectedByHover();
}
if (element.hovered() || InspectorInstrumentation::forcePseudoState(&element, CSSSelector::PseudoHover))
return true;
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() && !element.hasTagName(aTag)) || element.isLink()) {
+ if (m_strictParsing || context.isSubSelector || element.isLink()) {
if (m_mode == ResolvingStyle) {
if (context.elementStyle)
context.elementStyle->setAffectedByActive();
else
- element.setChildrenAffectedByActive();
+ element.setChildrenOrSiblingsAffectedByActive();
}
if (element.active() || InspectorInstrumentation::forcePseudoState(&element, CSSSelector::PseudoActive))
return true;
}
break;
case CSSSelector::PseudoEnabled:
- if (element.isFormControlElement() || element.hasTagName(optionTag) || element.hasTagName(optgroupTag))
+ if (element.isFormControlElement() || isHTMLOptionElement(element) || isHTMLOptGroupElement(element))
return !element.isDisabledFormControl();
+ else if (isHTMLAnchorElement(element) || isHTMLAreaElement(element))
+ return element.isLink();
break;
case CSSSelector::PseudoFullPageMedia:
return element.document().isMediaDocument();
case CSSSelector::PseudoDefault:
return element.isDefaultButtonForForm();
case CSSSelector::PseudoDisabled:
- if (element.isFormControlElement() || element.hasTagName(optionTag) || element.hasTagName(optgroupTag))
+ if (element.isFormControlElement() || isHTMLOptionElement(element) || isHTMLOptGroupElement(element))
return element.isDisabledFormControl();
break;
case CSSSelector::PseudoReadOnly:
return element.willValidate() && !element.isValidFormControlElement();
case CSSSelector::PseudoChecked:
{
- if (element.hasTagName(inputTag)) {
+ if (isHTMLInputElement(element)) {
HTMLInputElement& inputElement = toHTMLInputElement(element);
// Even though WinIE allows checked and indeterminate to
// co-exist, the CSS selector spec says that you can't be
// test for matching the pseudo.
if (inputElement.shouldAppearChecked() && !inputElement.shouldAppearIndeterminate())
return true;
- } else if (element.hasTagName(optionTag) && toHTMLOptionElement(element).selected())
+ } else if (isHTMLOptionElement(element) && toHTMLOptionElement(element).selected())
return true;
break;
}
// 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())
+ if (isHTMLFrameElementBase(element) && element.containsFullScreenElement())
return true;
- if (FullscreenElementStack* fullscreen = FullscreenElementStack::fromIfExists(&element.document())) {
- if (!fullscreen->webkitIsFullScreen())
- return false;
- return element == fullscreen->webkitCurrentFullScreenElement();
- }
- return false;
+ return Fullscreen::isActiveFullScreenElement(element);
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 (!FullscreenElementStack::isFullScreen(&element.document()))
+ if (!Fullscreen::isFullScreen(element.document()))
return false;
return true;
case CSSSelector::PseudoInRange:
case CSSSelector::PseudoScope:
{
+ if (m_mode == SharingRules)
+ return true;
const Node* contextualReferenceNode = !context.scope ? element.document().documentElement() : context.scope;
if (element == contextualReferenceNode)
return true;
break;
case CSSSelector::PseudoHost:
- case CSSSelector::PseudoAncestor:
+ case CSSSelector::PseudoHostContext:
{
+ if (m_mode == SharingRules)
+ return true;
// :host only matches a shadow host when :host is in a shadow tree of the shadow host.
- if (!context.scope || !(context.behaviorAtBoundary & ScopeIsShadowHost) || context.scope != element)
+ if (!context.scope)
+ return false;
+ const ContainerNode* shadowHost = context.scope->shadowHost();
+ if (!shadowHost || shadowHost != element)
return false;
ASSERT(element.shadow());
// If one of simple selectors matches an element, returns SelectorMatches. Just "OR".
for (subContext.selector = selector.selectorList()->first(); subContext.selector; subContext.selector = CSSSelectorList::next(*subContext.selector)) {
- subContext.behaviorAtBoundary = ScopeIsShadowHostInPseudoHostParameter;
+ subContext.contextFlags = TreatShadowHostAsNormalScope;
subContext.scope = context.scope;
// Use NodeRenderingTraversal to traverse a composed ancestor list of a given element.
Element* nextElement = &element;
+ SelectorCheckingContext hostContext(subContext);
do {
MatchResult subResult;
- subContext.element = nextElement;
- if (match(subContext, siblingTraversalStrategy, &subResult) == SelectorMatches) {
+ hostContext.element = nextElement;
+ if (match(hostContext, siblingTraversalStrategy, &subResult) == SelectorMatches) {
matched = true;
// Consider div:host(div:host(div:host(div:host...))).
- maxSpecificity = std::max(maxSpecificity, subContext.selector->specificity() + subResult.specificity);
+ maxSpecificity = std::max(maxSpecificity, hostContext.selector->specificity() + subResult.specificity);
break;
}
- subContext.behaviorAtBoundary = DoesNotCrossBoundary;
- subContext.scope = 0;
+ hostContext.contextFlags = DefaultBehavior;
+ hostContext.scope = nullptr;
if (selector.pseudoType() == CSSSelector::PseudoHost)
break;
+ hostContext.elementStyle = 0;
nextElement = NodeRenderingTraversal::parentElement(nextElement);
} while (nextElement);
}
}
}
break;
+ case CSSSelector::PseudoSpatialNavigationFocus:
+ return context.isUARule && matchesSpatialNavigationFocusPseudoClass(element);
+ case CSSSelector::PseudoListBox:
+ return context.isUARule && matchesListBoxPseudoClass(element);
case CSSSelector::PseudoHorizontal:
case CSSSelector::PseudoVertical:
break;
}
return false;
- } else if (selector.m_match == CSSSelector::PseudoElement && selector.pseudoType() == CSSSelector::PseudoCue) {
+ } else if (selector.match() == CSSSelector::PseudoElement && selector.pseudoType() == CSSSelector::PseudoCue) {
SelectorCheckingContext subContext(context);
subContext.isSubSelector = true;
- subContext.behaviorAtBoundary = StaysWithinTreeScope;
+ subContext.contextFlags = DefaultBehavior;
const CSSSelector* contextSelector = context.selector;
ASSERT(contextSelector);
if (!scrollbar)
return false;
- ASSERT(selector.m_match == CSSSelector::PseudoClass);
+ ASSERT(selector.match() == CSSSelector::PseudoClass);
switch (selector.pseudoType()) {
case CSSSelector::PseudoEnabled:
return scrollbar->enabled();
return element.focused() && isFrameFocused(element);
}
+bool SelectorChecker::matchesSpatialNavigationFocusPseudoClass(const Element& element)
+{
+ return isHTMLOptionElement(element) && toHTMLOptionElement(element).spatialNavigationFocused() && isFrameFocused(element);
+}
+
+bool SelectorChecker::matchesListBoxPseudoClass(const Element& element)
+{
+ return isHTMLSelectElement(element) && !toHTMLSelectElement(element).usesMenuList();
+}
+
template
SelectorChecker::Match SelectorChecker::match(const SelectorCheckingContext&, const DOMSiblingTraversalStrategy&, MatchResult*) const;