2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 2004-2005 Allan Sandfeld Jensen (kde@carewolf.com)
4 * Copyright (C) 2006, 2007 Nicholas Shanks (webkit@nickshanks.com)
5 * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
6 * Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org>
7 * Copyright (C) 2007, 2008 Eric Seidel <eric@webkit.org>
8 * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
9 * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
10 * Copyright (C) Research In Motion Limited 2011. All rights reserved.
12 * This library is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Library General Public
14 * License as published by the Free Software Foundation; either
15 * version 2 of the License, or (at your option) any later version.
17 * This library is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Library General Public License for more details.
22 * You should have received a copy of the GNU Library General Public License
23 * along with this library; see the file COPYING.LIB. If not, write to
24 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
25 * Boston, MA 02110-1301, USA.
29 #include "SelectorChecker.h"
31 #include "CSSSelector.h"
32 #include "CSSSelectorList.h"
34 #include "FocusController.h"
36 #include "FrameSelection.h"
37 #include "HTMLAnchorElement.h"
38 #include "HTMLFrameElementBase.h"
39 #include "HTMLInputElement.h"
40 #include "HTMLNames.h"
41 #include "HTMLOptionElement.h"
42 #include "HTMLProgressElement.h"
43 #include "HTMLStyleElement.h"
44 #include "InspectorInstrumentation.h"
45 #include "NodeRenderStyle.h"
47 #include "PageGroup.h"
48 #include "RenderObject.h"
49 #include "RenderScrollbar.h"
50 #include "RenderStyle.h"
51 #include "ScrollableArea.h"
52 #include "ScrollbarTheme.h"
53 #include "StyledElement.h"
55 #include "XLinkNames.h"
57 #if USE(PLATFORM_STRATEGIES)
58 #include "PlatformStrategies.h"
59 #include "VisitedLinkStrategy.h"
64 using namespace HTMLNames;
66 static bool htmlAttributeHasCaseInsensitiveValue(const QualifiedName& attr);
68 SelectorChecker::SelectorChecker(Document* document, bool strictParsing)
69 : m_document(document)
70 , m_strictParsing(strictParsing)
71 , m_documentIsHTML(document->isHTMLDocument())
72 , m_mode(ResolvingStyle)
73 , m_pseudoStyle(NOPSEUDO)
74 , m_hasUnknownPseudoElements(false)
78 // Salt to separate otherwise identical string hashes so a class-selector like .article won't match <article> elements.
79 enum { TagNameSalt = 13, IdAttributeSalt = 17, ClassAttributeSalt = 19 };
81 static inline void collectElementIdentifierHashes(const Element* element, Vector<unsigned, 4>& identifierHashes)
83 identifierHashes.append(element->localName().impl()->existingHash() * TagNameSalt);
85 identifierHashes.append(element->idForStyleResolution().impl()->existingHash() * IdAttributeSalt);
86 const StyledElement* styledElement = element->isStyledElement() ? static_cast<const StyledElement*>(element) : 0;
87 if (styledElement && styledElement->hasClass()) {
88 const SpaceSplitString& classNames = styledElement->classNames();
89 size_t count = classNames.size();
90 for (size_t i = 0; i < count; ++i)
91 identifierHashes.append(classNames[i].impl()->existingHash() * ClassAttributeSalt);
95 void SelectorChecker::pushParentStackFrame(Element* parent)
97 ASSERT(m_ancestorIdentifierFilter);
98 ASSERT(m_parentStack.isEmpty() || m_parentStack.last().element == parent->parentOrHostElement());
99 ASSERT(!m_parentStack.isEmpty() || !parent->parentOrHostElement());
100 m_parentStack.append(ParentStackFrame(parent));
101 ParentStackFrame& parentFrame = m_parentStack.last();
102 // Mix tags, class names and ids into some sort of weird bouillabaisse.
103 // The filter is used for fast rejection of child and descendant selectors.
104 collectElementIdentifierHashes(parent, parentFrame.identifierHashes);
105 size_t count = parentFrame.identifierHashes.size();
106 for (size_t i = 0; i < count; ++i)
107 m_ancestorIdentifierFilter->add(parentFrame.identifierHashes[i]);
110 void SelectorChecker::popParentStackFrame()
112 ASSERT(!m_parentStack.isEmpty());
113 ASSERT(m_ancestorIdentifierFilter);
114 const ParentStackFrame& parentFrame = m_parentStack.last();
115 size_t count = parentFrame.identifierHashes.size();
116 for (size_t i = 0; i < count; ++i)
117 m_ancestorIdentifierFilter->remove(parentFrame.identifierHashes[i]);
118 m_parentStack.removeLast();
119 if (m_parentStack.isEmpty()) {
120 ASSERT(m_ancestorIdentifierFilter->likelyEmpty());
121 m_ancestorIdentifierFilter.clear();
125 void SelectorChecker::setupParentStack(Element* parent)
127 ASSERT(m_parentStack.isEmpty() == !m_ancestorIdentifierFilter);
128 // Kill whatever we stored before.
129 m_parentStack.shrink(0);
130 m_ancestorIdentifierFilter = adoptPtr(new BloomFilter<bloomFilterKeyBits>);
131 // Fast version if parent is a root element:
132 if (!parent->parentOrHostNode()) {
133 pushParentStackFrame(parent);
136 // Otherwise climb up the tree.
137 Vector<Element*, 30> ancestors;
138 for (Element* ancestor = parent; ancestor; ancestor = ancestor->parentOrHostElement())
139 ancestors.append(ancestor);
140 for (size_t n = ancestors.size(); n; --n)
141 pushParentStackFrame(ancestors[n - 1]);
144 void SelectorChecker::pushParent(Element* parent)
146 ASSERT(m_ancestorIdentifierFilter);
147 // We may get invoked for some random elements in some wacky cases during style resolve.
148 // Pause maintaining the stack in this case.
149 if (m_parentStack.last().element != parent->parentOrHostElement())
151 pushParentStackFrame(parent);
154 static inline void collectDescendantSelectorIdentifierHashes(const CSSSelector* selector, unsigned*& hash, const unsigned* end)
156 switch (selector->m_match) {
157 case CSSSelector::Id:
158 if (!selector->value().isEmpty())
159 (*hash++) = selector->value().impl()->existingHash() * IdAttributeSalt;
161 case CSSSelector::Class:
162 if (!selector->value().isEmpty())
163 (*hash++) = selector->value().impl()->existingHash() * ClassAttributeSalt;
170 const AtomicString& localName = selector->tag().localName();
171 if (localName != starAtom)
172 (*hash++) = localName.impl()->existingHash() * TagNameSalt;
175 void SelectorChecker::collectIdentifierHashes(const CSSSelector* selector, unsigned* identifierHashes, unsigned maximumIdentifierCount)
177 unsigned* hash = identifierHashes;
178 unsigned* end = identifierHashes + maximumIdentifierCount;
179 CSSSelector::Relation relation = selector->relation();
181 // Skip the topmost selector. It is handled quickly by the rule hashes.
182 bool skipOverSubselectors = true;
183 for (selector = selector->tagHistory(); selector; selector = selector->tagHistory()) {
184 // Only collect identifiers that match ancestors.
186 case CSSSelector::SubSelector:
187 if (!skipOverSubselectors)
188 collectDescendantSelectorIdentifierHashes(selector, hash, end);
190 case CSSSelector::DirectAdjacent:
191 case CSSSelector::IndirectAdjacent:
192 case CSSSelector::ShadowDescendant:
193 skipOverSubselectors = true;
195 case CSSSelector::Descendant:
196 case CSSSelector::Child:
197 skipOverSubselectors = false;
198 collectDescendantSelectorIdentifierHashes(selector, hash, end);
203 relation = selector->relation();
208 static inline const AtomicString* linkAttribute(Node* node)
213 ASSERT(node->isElementNode());
214 Element* element = static_cast<Element*>(node);
215 if (element->isHTMLElement())
216 return &element->fastGetAttribute(hrefAttr);
217 if (element->isSVGElement())
218 return &element->getAttribute(XLinkNames::hrefAttr);
223 EInsideLink SelectorChecker::determineLinkStateSlowCase(Element* element) const
225 ASSERT(element->isLink());
227 const AtomicString* attribute = linkAttribute(element);
228 if (!attribute || attribute->isNull())
229 return NotInsideLink;
231 // An empty href refers to the document itself which is always visited. It is useful to check this explicitly so
232 // that visited links can be tested in platform independent manner, without explicit support in the test harness.
233 if (attribute->isEmpty())
234 return InsideVisitedLink;
237 if (element->hasTagName(aTag))
238 hash = static_cast<HTMLAnchorElement*>(element)->visitedLinkHash();
240 hash = visitedLinkHash(m_document->baseURL(), *attribute);
243 return InsideUnvisitedLink;
245 Frame* frame = m_document->frame();
247 return InsideUnvisitedLink;
249 Page* page = frame->page();
251 return InsideUnvisitedLink;
253 m_linksCheckedForVisitedState.add(hash);
255 #if USE(PLATFORM_STRATEGIES)
256 return platformStrategies()->visitedLinkStrategy()->isLinkVisited(page, hash, m_document->baseURL(), *attribute) ? InsideVisitedLink : InsideUnvisitedLink;
258 return page->group().isLinkVisited(hash) ? InsideVisitedLink : InsideUnvisitedLink;
262 bool SelectorChecker::checkSelector(CSSSelector* sel, Element* element, bool isFastCheckableSelector) const
264 if (isFastCheckableSelector && !element->isSVGElement()) {
265 if (!fastCheckRightmostSelector(sel, element, VisitedMatchDisabled))
267 return fastCheckSelector(sel, element);
270 PseudoId dynamicPseudo = NOPSEUDO;
271 return checkSelector(SelectorCheckingContext(sel, element, SelectorChecker::VisitedMatchDisabled), dynamicPseudo) == SelectorMatches;
276 template <bool checkValue(const Element*, AtomicStringImpl*, const QualifiedName&), bool initAttributeName>
277 inline bool fastCheckSingleSelector(const CSSSelector*& selector, const Element*& element, const CSSSelector*& topChildOrSubselector, const Element*& topChildOrSubselectorMatchElement)
279 AtomicStringImpl* value = selector->value().impl();
280 const QualifiedName& attribute = initAttributeName ? selector->attribute() : anyQName();
281 for (; element; element = element->parentElement()) {
282 if (checkValue(element, value, attribute) && SelectorChecker::tagMatches(element, selector)) {
283 if (selector->relation() == CSSSelector::Descendant)
284 topChildOrSubselector = 0;
285 else if (!topChildOrSubselector) {
286 ASSERT(selector->relation() == CSSSelector::Child || selector->relation() == CSSSelector::SubSelector);
287 topChildOrSubselector = selector;
288 topChildOrSubselectorMatchElement = element;
290 if (selector->relation() != CSSSelector::SubSelector)
291 element = element->parentElement();
292 selector = selector->tagHistory();
295 if (topChildOrSubselector) {
296 // Child or subselector check failed.
297 // If the match element is null, topChildOrSubselector was also the very topmost selector and had to match
298 // the original element we were checking.
299 if (!topChildOrSubselectorMatchElement)
301 // There may be other matches down the ancestor chain.
302 // Rewind to the topmost child or subselector and the element it matched, continue checking ancestors.
303 selector = topChildOrSubselector;
304 element = topChildOrSubselectorMatchElement->parentElement();
305 topChildOrSubselector = 0;
312 inline bool checkClassValue(const Element* element, AtomicStringImpl* value, const QualifiedName&)
314 return element->hasClass() && static_cast<const StyledElement*>(element)->classNames().contains(value);
317 inline bool checkIDValue(const Element* element, AtomicStringImpl* value, const QualifiedName&)
319 return element->hasID() && element->idForStyleResolution().impl() == value;
322 inline bool checkExactAttributeValue(const Element* element, AtomicStringImpl* value, const QualifiedName& attributeName)
324 return SelectorChecker::checkExactAttribute(element, attributeName, value);
327 inline bool checkTagValue(const Element*, AtomicStringImpl*, const QualifiedName&)
334 inline bool SelectorChecker::fastCheckRightmostSelector(const CSSSelector* selector, const Element* element, VisitedMatchType visitedMatchType) const
336 ASSERT(isFastCheckableSelector(selector));
338 if (!SelectorChecker::tagMatches(element, selector))
340 switch (selector->m_match) {
341 case CSSSelector::None:
343 case CSSSelector::Class:
344 return checkClassValue(element, selector->value().impl(), anyQName());
345 case CSSSelector::Id:
346 return checkIDValue(element, selector->value().impl(), anyQName());
347 case CSSSelector::Exact:
348 case CSSSelector::Set:
349 return checkExactAttributeValue(element, selector->value().impl(), selector->attribute());
350 case CSSSelector::PseudoClass:
351 return commonPseudoClassSelectorMatches(element, selector, visitedMatchType);
353 ASSERT_NOT_REACHED();
358 bool SelectorChecker::fastCheckSelector(const CSSSelector* selector, const Element* element) const
360 ASSERT(fastCheckRightmostSelector(selector, element, VisitedMatchEnabled));
362 const CSSSelector* topChildOrSubselector = 0;
363 const Element* topChildOrSubselectorMatchElement = 0;
364 if (selector->relation() == CSSSelector::Child || selector->relation() == CSSSelector::SubSelector)
365 topChildOrSubselector = selector;
367 if (selector->relation() != CSSSelector::SubSelector)
368 element = element->parentElement();
370 selector = selector->tagHistory();
372 // We know this compound selector has descendant, child and subselector combinators only and all components are simple.
374 switch (selector->m_match) {
375 case CSSSelector::Class:
376 if (!fastCheckSingleSelector<checkClassValue, false>(selector, element, topChildOrSubselector, topChildOrSubselectorMatchElement))
379 case CSSSelector::Id:
380 if (!fastCheckSingleSelector<checkIDValue, false>(selector, element, topChildOrSubselector, topChildOrSubselectorMatchElement))
383 case CSSSelector::None:
384 if (!fastCheckSingleSelector<checkTagValue, false>(selector, element, topChildOrSubselector, topChildOrSubselectorMatchElement))
387 case CSSSelector::Set:
388 case CSSSelector::Exact:
389 if (!fastCheckSingleSelector<checkExactAttributeValue, true>(selector, element, topChildOrSubselector, topChildOrSubselectorMatchElement))
393 ASSERT_NOT_REACHED();
399 static inline bool isFastCheckableRelation(CSSSelector::Relation relation)
401 return relation == CSSSelector::Descendant || relation == CSSSelector::Child || relation == CSSSelector::SubSelector;
404 static inline bool isFastCheckableMatch(const CSSSelector* selector)
406 if (selector->m_match == CSSSelector::Set) {
407 // Style attribute is generated lazily but the fast path doesn't trigger it.
408 // Disallow them here rather than making the fast path more branchy.
409 return selector->attribute() != styleAttr;
411 if (selector->m_match == CSSSelector::Exact)
412 return selector->attribute() != styleAttr && !htmlAttributeHasCaseInsensitiveValue(selector->attribute());
413 return selector->m_match == CSSSelector::None || selector->m_match == CSSSelector::Id || selector->m_match == CSSSelector::Class;
416 static inline bool isFastCheckableRightmostSelector(const CSSSelector* selector)
418 if (!isFastCheckableRelation(selector->relation()))
420 return isFastCheckableMatch(selector) || SelectorChecker::isCommonPseudoClassSelector(selector);
423 bool SelectorChecker::isFastCheckableSelector(const CSSSelector* selector)
425 if (!isFastCheckableRightmostSelector(selector))
427 for (selector = selector->tagHistory(); selector; selector = selector->tagHistory()) {
428 if (!isFastCheckableRelation(selector->relation()))
430 if (!isFastCheckableMatch(selector))
436 // Recursive check of selectors and combinators
437 // It can return 4 different values:
438 // * SelectorMatches - the selector matches the element e
439 // * SelectorFailsLocally - the selector fails for the element e
440 // * SelectorFailsAllSiblings - the selector fails for e and any sibling of e
441 // * SelectorFailsCompletely - the selector fails for e and any sibling or ancestor of e
442 SelectorChecker::SelectorMatch SelectorChecker::checkSelector(const SelectorCheckingContext& context, PseudoId& dynamicPseudo) const
444 // first selector has to match
445 if (!checkOneSelector(context, dynamicPseudo))
446 return SelectorFailsLocally;
448 // The rest of the selectors has to match
449 CSSSelector::Relation relation = context.selector->relation();
451 // Prepare next selector
452 CSSSelector* historySelector = context.selector->tagHistory();
453 if (!historySelector)
454 return SelectorMatches;
456 SelectorCheckingContext nextContext(context);
457 nextContext.selector = historySelector;
459 if (relation != CSSSelector::SubSelector) {
460 // Abort if the next selector would exceed the scope.
461 if (context.element == context.scope)
462 return SelectorFailsCompletely;
464 // Bail-out if this selector is irrelevant for the pseudoStyle
465 if (m_pseudoStyle != NOPSEUDO && m_pseudoStyle != dynamicPseudo)
466 return SelectorFailsCompletely;
468 // Disable :visited matching when we see the first link or try to match anything else than an ancestors.
469 if (!context.isSubSelector && (context.element->isLink() || (relation != CSSSelector::Descendant && relation != CSSSelector::Child)))
470 nextContext.visitedMatchType = VisitedMatchDisabled;
474 case CSSSelector::Descendant:
475 nextContext.element = context.element->parentElement();
476 nextContext.isSubSelector = false;
477 nextContext.elementStyle = 0;
478 nextContext.elementParentStyle = 0;
479 for (; nextContext.element; nextContext.element = nextContext.element->parentElement()) {
480 SelectorMatch match = checkSelector(nextContext, dynamicPseudo);
481 if (match == SelectorMatches || match == SelectorFailsCompletely)
483 if (nextContext.element == nextContext.scope)
484 return SelectorFailsCompletely;
486 return SelectorFailsCompletely;
488 case CSSSelector::Child:
489 nextContext.element = context.element->parentElement();
490 if (!nextContext.element)
491 return SelectorFailsCompletely;
492 nextContext.isSubSelector = false;
493 nextContext.elementStyle = 0;
494 nextContext.elementParentStyle = 0;
495 return checkSelector(nextContext, dynamicPseudo);
497 case CSSSelector::DirectAdjacent:
498 if (m_mode == ResolvingStyle && context.element->parentElement()) {
499 RenderStyle* parentStyle = context.elementStyle ? context.elementParentStyle : context.element->parentNode()->renderStyle();
501 parentStyle->setChildrenAffectedByDirectAdjacentRules();
503 nextContext.element = context.element->previousElementSibling();
504 if (!nextContext.element)
505 return SelectorFailsAllSiblings;
506 nextContext.isSubSelector = false;
507 nextContext.elementStyle = 0;
508 nextContext.elementParentStyle = 0;
509 return checkSelector(nextContext, dynamicPseudo);
511 case CSSSelector::IndirectAdjacent:
512 if (m_mode == ResolvingStyle && context.element->parentElement()) {
513 RenderStyle* parentStyle = context.elementStyle ? context.elementParentStyle : context.element->parentNode()->renderStyle();
515 parentStyle->setChildrenAffectedByForwardPositionalRules();
517 nextContext.element = context.element->previousElementSibling();
518 nextContext.isSubSelector = false;
519 nextContext.elementStyle = 0;
520 nextContext.elementParentStyle = 0;
521 for (; nextContext.element; nextContext.element = nextContext.element->previousElementSibling()) {
522 SelectorMatch match = checkSelector(nextContext, dynamicPseudo);
523 if (match == SelectorMatches || match == SelectorFailsAllSiblings || match == SelectorFailsCompletely)
526 return SelectorFailsAllSiblings;
528 case CSSSelector::SubSelector:
529 // a selector is invalid if something follows a pseudo-element
530 // We make an exception for scrollbar pseudo elements and allow a set of pseudo classes (but nothing else)
531 // to follow the pseudo elements.
532 if ((context.elementStyle || m_mode == CollectingRules || m_mode == QueryingRules) && dynamicPseudo != NOPSEUDO && dynamicPseudo != SELECTION
533 && !((RenderScrollbar::scrollbarForStyleResolve() || dynamicPseudo == SCROLLBAR_CORNER || dynamicPseudo == RESIZER) && nextContext.selector->m_match == CSSSelector::PseudoClass))
534 return SelectorFailsCompletely;
535 nextContext.isSubSelector = true;
536 return checkSelector(nextContext, dynamicPseudo);
538 case CSSSelector::ShadowDescendant:
540 // 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.
541 if (context.scope && context.scope->treeScope() == context.element->treeScope())
542 return SelectorFailsCompletely;
543 Element* shadowHostNode = context.element->shadowHost();
545 return SelectorFailsCompletely;
546 nextContext.element = shadowHostNode;
547 nextContext.isSubSelector = false;
548 nextContext.elementStyle = 0;
549 nextContext.elementParentStyle = 0;
550 return checkSelector(nextContext, dynamicPseudo);
554 ASSERT_NOT_REACHED();
555 return SelectorFailsCompletely;
558 static void addLocalNameToSet(HashSet<AtomicStringImpl*>* set, const QualifiedName& qName)
560 set->add(qName.localName().impl());
563 static HashSet<AtomicStringImpl*>* createHtmlCaseInsensitiveAttributesSet()
565 // This is the list of attributes in HTML 4.01 with values marked as "[CI]" or case-insensitive
566 // Mozilla treats all other values as case-sensitive, thus so do we.
567 HashSet<AtomicStringImpl*>* attrSet = new HashSet<AtomicStringImpl*>;
569 addLocalNameToSet(attrSet, accept_charsetAttr);
570 addLocalNameToSet(attrSet, acceptAttr);
571 addLocalNameToSet(attrSet, alignAttr);
572 addLocalNameToSet(attrSet, alinkAttr);
573 addLocalNameToSet(attrSet, axisAttr);
574 addLocalNameToSet(attrSet, bgcolorAttr);
575 addLocalNameToSet(attrSet, charsetAttr);
576 addLocalNameToSet(attrSet, checkedAttr);
577 addLocalNameToSet(attrSet, clearAttr);
578 addLocalNameToSet(attrSet, codetypeAttr);
579 addLocalNameToSet(attrSet, colorAttr);
580 addLocalNameToSet(attrSet, compactAttr);
581 addLocalNameToSet(attrSet, declareAttr);
582 addLocalNameToSet(attrSet, deferAttr);
583 addLocalNameToSet(attrSet, dirAttr);
584 addLocalNameToSet(attrSet, disabledAttr);
585 addLocalNameToSet(attrSet, enctypeAttr);
586 addLocalNameToSet(attrSet, faceAttr);
587 addLocalNameToSet(attrSet, frameAttr);
588 addLocalNameToSet(attrSet, hreflangAttr);
589 addLocalNameToSet(attrSet, http_equivAttr);
590 addLocalNameToSet(attrSet, langAttr);
591 addLocalNameToSet(attrSet, languageAttr);
592 addLocalNameToSet(attrSet, linkAttr);
593 addLocalNameToSet(attrSet, mediaAttr);
594 addLocalNameToSet(attrSet, methodAttr);
595 addLocalNameToSet(attrSet, multipleAttr);
596 addLocalNameToSet(attrSet, nohrefAttr);
597 addLocalNameToSet(attrSet, noresizeAttr);
598 addLocalNameToSet(attrSet, noshadeAttr);
599 addLocalNameToSet(attrSet, nowrapAttr);
600 addLocalNameToSet(attrSet, readonlyAttr);
601 addLocalNameToSet(attrSet, relAttr);
602 addLocalNameToSet(attrSet, revAttr);
603 addLocalNameToSet(attrSet, rulesAttr);
604 addLocalNameToSet(attrSet, scopeAttr);
605 addLocalNameToSet(attrSet, scrollingAttr);
606 addLocalNameToSet(attrSet, selectedAttr);
607 addLocalNameToSet(attrSet, shapeAttr);
608 addLocalNameToSet(attrSet, targetAttr);
609 addLocalNameToSet(attrSet, textAttr);
610 addLocalNameToSet(attrSet, typeAttr);
611 addLocalNameToSet(attrSet, valignAttr);
612 addLocalNameToSet(attrSet, valuetypeAttr);
613 addLocalNameToSet(attrSet, vlinkAttr);
618 bool htmlAttributeHasCaseInsensitiveValue(const QualifiedName& attr)
620 static HashSet<AtomicStringImpl*>* htmlCaseInsensitiveAttributesSet = createHtmlCaseInsensitiveAttributesSet();
621 bool isPossibleHTMLAttr = !attr.hasPrefix() && (attr.namespaceURI() == nullAtom);
622 return isPossibleHTMLAttr && htmlCaseInsensitiveAttributesSet->contains(attr.localName().impl());
625 static bool attributeValueMatches(const Attribute* attributeItem, CSSSelector::Match match, const AtomicString& selectorValue, bool caseSensitive)
627 const AtomicString& value = attributeItem->value();
632 case CSSSelector::Exact:
633 if (caseSensitive ? selectorValue != value : !equalIgnoringCase(selectorValue, value))
636 case CSSSelector::List:
638 // Ignore empty selectors or selectors containing spaces
639 if (selectorValue.contains(' ') || selectorValue.isEmpty())
642 unsigned startSearchAt = 0;
644 size_t foundPos = value.find(selectorValue, startSearchAt, caseSensitive);
645 if (foundPos == notFound)
647 if (!foundPos || value[foundPos - 1] == ' ') {
648 unsigned endStr = foundPos + selectorValue.length();
649 if (endStr == value.length() || value[endStr] == ' ')
650 break; // We found a match.
653 // No match. Keep looking.
654 startSearchAt = foundPos + 1;
658 case CSSSelector::Contain:
659 if (!value.contains(selectorValue, caseSensitive) || selectorValue.isEmpty())
662 case CSSSelector::Begin:
663 if (!value.startsWith(selectorValue, caseSensitive) || selectorValue.isEmpty())
666 case CSSSelector::End:
667 if (!value.endsWith(selectorValue, caseSensitive) || selectorValue.isEmpty())
670 case CSSSelector::Hyphen:
671 if (value.length() < selectorValue.length())
673 if (!value.startsWith(selectorValue, caseSensitive))
675 // It they start the same, check for exact match or following '-':
676 if (value.length() != selectorValue.length() && value[selectorValue.length()] != '-')
679 case CSSSelector::PseudoClass:
680 case CSSSelector::PseudoElement:
688 static bool anyAttributeMatches(Element* element, CSSSelector::Match match, const QualifiedName& selectorAttr, const AtomicString& selectorValue, bool caseSensitive)
690 ASSERT(element->hasAttributesWithoutUpdate());
691 for (size_t i = 0; i < element->attributeCount(); ++i) {
692 const Attribute* attributeItem = element->attributeItem(i);
694 if (!SelectorChecker::attributeNameMatches(attributeItem, selectorAttr))
697 if (attributeValueMatches(attributeItem, match, selectorValue, caseSensitive))
704 bool SelectorChecker::checkOneSelector(const SelectorCheckingContext& context, PseudoId& dynamicPseudo) const
706 Element* const & element = context.element;
707 CSSSelector* const & selector = context.selector;
711 if (!SelectorChecker::tagMatches(element, selector))
714 if (selector->m_match == CSSSelector::Class)
715 return element->hasClass() && static_cast<StyledElement*>(element)->classNames().contains(selector->value());
717 if (selector->m_match == CSSSelector::Id)
718 return element->hasID() && element->idForStyleResolution() == selector->value();
720 if (selector->isAttributeSelector()) {
721 const QualifiedName& attr = selector->attribute();
723 if (!element->hasAttributes())
726 bool caseSensitive = !m_documentIsHTML || !htmlAttributeHasCaseInsensitiveValue(attr);
728 if (!anyAttributeMatches(element, static_cast<CSSSelector::Match>(selector->m_match), attr, selector->value(), caseSensitive))
732 if (selector->m_match == CSSSelector::PseudoClass) {
733 // Handle :not up front.
734 if (selector->pseudoType() == CSSSelector::PseudoNot) {
735 CSSSelectorList* selectorList = selector->selectorList();
737 // FIXME: We probably should fix the parser and make it never produce :not rules with missing selector list.
741 SelectorCheckingContext subContext(context);
742 subContext.isSubSelector = true;
743 for (subContext.selector = selectorList->first(); subContext.selector; subContext.selector = subContext.selector->tagHistory()) {
744 // :not cannot nest. I don't really know why this is a
745 // restriction in CSS3, but it is, so let's honor it.
746 // the parser enforces that this never occurs
747 ASSERT(subContext.selector->pseudoType() != CSSSelector::PseudoNot);
748 // We select between :visited and :link when applying. We don't know which one applied (or not) yet.
749 if (subContext.selector->pseudoType() == CSSSelector::PseudoVisited || (subContext.selector->pseudoType() == CSSSelector::PseudoLink && subContext.visitedMatchType == VisitedMatchEnabled))
751 if (!checkOneSelector(subContext, dynamicPseudo))
754 } else if (dynamicPseudo != NOPSEUDO && (RenderScrollbar::scrollbarForStyleResolve() || dynamicPseudo == SCROLLBAR_CORNER || dynamicPseudo == RESIZER)) {
755 // CSS scrollbars match a specific subset of pseudo classes, and they have specialized rules for each
756 // (since there are no elements involved).
757 return checkScrollbarPseudoClass(selector, dynamicPseudo);
758 } else if (dynamicPseudo == SELECTION) {
759 if (selector->pseudoType() == CSSSelector::PseudoWindowInactive)
760 return !m_document->page()->focusController()->isActive();
763 // Normal element pseudo class checking.
764 switch (selector->pseudoType()) {
766 case CSSSelector::PseudoNot:
767 break; // Already handled up above.
768 case CSSSelector::PseudoEmpty:
771 for (Node* n = element->firstChild(); n; n = n->nextSibling()) {
772 if (n->isElementNode()) {
776 if (n->isTextNode()) {
777 Text* textNode = toText(n);
778 if (!textNode->data().isEmpty()) {
784 if (m_mode == ResolvingStyle) {
785 if (context.elementStyle)
786 context.elementStyle->setEmptyState(result);
787 else if (element->renderStyle() && (element->document()->usesSiblingRules() || element->renderStyle()->unique()))
788 element->renderStyle()->setEmptyState(result);
792 case CSSSelector::PseudoFirstChild:
793 // first-child matches the first child that is an element
794 if (element->parentElement()) {
796 if (!element->previousElementSibling())
798 if (m_mode == ResolvingStyle) {
799 RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element->renderStyle();
800 RenderStyle* parentStyle = context.elementStyle ? context.elementParentStyle : element->parentNode()->renderStyle();
802 parentStyle->setChildrenAffectedByFirstChildRules();
803 if (result && childStyle)
804 childStyle->setFirstChildState();
809 case CSSSelector::PseudoFirstOfType:
810 // first-of-type matches the first element of its type
811 if (element->parentElement()) {
813 const QualifiedName& type = element->tagQName();
814 for (const Element* sibling = element->previousElementSibling(); sibling; sibling = sibling->previousElementSibling()) {
815 if (sibling->hasTagName(type)) {
820 if (m_mode == ResolvingStyle) {
821 RenderStyle* parentStyle = context.elementStyle ? context.elementParentStyle : element->parentNode()->renderStyle();
823 parentStyle->setChildrenAffectedByForwardPositionalRules();
828 case CSSSelector::PseudoLastChild:
829 // last-child matches the last child that is an element
830 if (Element* parentElement = element->parentElement()) {
831 bool result = parentElement->isFinishedParsingChildren() && !element->nextElementSibling();
832 if (m_mode == ResolvingStyle) {
833 RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element->renderStyle();
834 RenderStyle* parentStyle = context.elementStyle ? context.elementParentStyle : parentElement->renderStyle();
836 parentStyle->setChildrenAffectedByLastChildRules();
837 if (result && childStyle)
838 childStyle->setLastChildState();
843 case CSSSelector::PseudoLastOfType:
844 // last-of-type matches the last element of its type
845 if (Element* parentElement = element->parentElement()) {
846 if (m_mode == ResolvingStyle) {
847 RenderStyle* parentStyle = context.elementStyle ? context.elementParentStyle : parentElement->renderStyle();
849 parentStyle->setChildrenAffectedByBackwardPositionalRules();
851 if (!parentElement->isFinishedParsingChildren())
853 const QualifiedName& type = element->tagQName();
854 for (const Element* sibling = element->nextElementSibling(); sibling; sibling = sibling->nextElementSibling()) {
855 if (sibling->hasTagName(type))
861 case CSSSelector::PseudoOnlyChild:
862 if (Element* parentElement = element->parentElement()) {
863 bool firstChild = !element->previousElementSibling();
864 bool onlyChild = firstChild && parentElement->isFinishedParsingChildren() && !element->nextElementSibling();
866 if (m_mode == ResolvingStyle) {
867 RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element->renderStyle();
868 RenderStyle* parentStyle = context.elementStyle ? context.elementParentStyle : parentElement->renderStyle();
870 parentStyle->setChildrenAffectedByFirstChildRules();
871 parentStyle->setChildrenAffectedByLastChildRules();
873 if (firstChild && childStyle)
874 childStyle->setFirstChildState();
875 if (onlyChild && childStyle)
876 childStyle->setLastChildState();
881 case CSSSelector::PseudoOnlyOfType:
882 // FIXME: This selector is very slow.
883 if (Element* parentElement = element->parentElement()) {
884 if (m_mode == ResolvingStyle) {
885 RenderStyle* parentStyle = context.elementStyle ? context.elementParentStyle : parentElement->renderStyle();
887 parentStyle->setChildrenAffectedByForwardPositionalRules();
888 parentStyle->setChildrenAffectedByBackwardPositionalRules();
891 if (!parentElement->isFinishedParsingChildren())
893 const QualifiedName& type = element->tagQName();
894 for (const Element* sibling = element->previousElementSibling(); sibling; sibling = sibling->previousElementSibling()) {
895 if (sibling->hasTagName(type))
898 for (const Element* sibling = element->nextElementSibling(); sibling; sibling = sibling->nextElementSibling()) {
899 if (sibling->hasTagName(type))
905 case CSSSelector::PseudoNthChild:
906 if (!selector->parseNth())
908 if (Element* parentElement = element->parentElement()) {
910 for (const Element* sibling = element->previousElementSibling(); sibling; sibling = sibling->previousElementSibling()) {
911 RenderStyle* s = sibling->renderStyle();
912 unsigned index = s ? s->childIndex() : 0;
920 if (m_mode == ResolvingStyle) {
921 RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element->renderStyle();
922 RenderStyle* parentStyle = context.elementStyle ? context.elementParentStyle : parentElement->renderStyle();
924 childStyle->setChildIndex(count);
926 parentStyle->setChildrenAffectedByForwardPositionalRules();
929 if (selector->matchNth(count))
933 case CSSSelector::PseudoNthOfType:
934 if (!selector->parseNth())
936 if (Element* parentElement = element->parentElement()) {
938 const QualifiedName& type = element->tagQName();
939 for (const Element* sibling = element->previousElementSibling(); sibling; sibling = sibling->previousElementSibling()) {
940 if (sibling->hasTagName(type))
943 if (m_mode == ResolvingStyle) {
944 RenderStyle* parentStyle = context.elementStyle ? context.elementParentStyle : parentElement->renderStyle();
946 parentStyle->setChildrenAffectedByForwardPositionalRules();
949 if (selector->matchNth(count))
953 case CSSSelector::PseudoNthLastChild:
954 if (!selector->parseNth())
956 if (Element* parentElement = element->parentElement()) {
957 if (m_mode == ResolvingStyle) {
958 RenderStyle* parentStyle = context.elementStyle ? context.elementParentStyle : parentElement->renderStyle();
960 parentStyle->setChildrenAffectedByBackwardPositionalRules();
962 if (!parentElement->isFinishedParsingChildren())
965 for (const Element* sibling = element->nextElementSibling(); sibling; sibling = sibling->nextElementSibling())
967 if (selector->matchNth(count))
971 case CSSSelector::PseudoNthLastOfType:
972 if (!selector->parseNth())
974 if (Element* parentElement = element->parentElement()) {
975 if (m_mode == ResolvingStyle) {
976 RenderStyle* parentStyle = context.elementStyle ? context.elementParentStyle : parentElement->renderStyle();
978 parentStyle->setChildrenAffectedByBackwardPositionalRules();
980 if (!parentElement->isFinishedParsingChildren())
983 const QualifiedName& type = element->tagQName();
984 for (const Element* sibling = element->nextElementSibling(); sibling; sibling = sibling->nextElementSibling()) {
985 if (sibling->hasTagName(type))
988 if (selector->matchNth(count))
992 case CSSSelector::PseudoTarget:
993 if (element == element->document()->cssTarget())
996 case CSSSelector::PseudoAny:
998 SelectorCheckingContext subContext(context);
999 subContext.isSubSelector = true;
1000 for (subContext.selector = selector->selectorList()->first(); subContext.selector; subContext.selector = CSSSelectorList::next(subContext.selector)) {
1001 if (checkSelector(subContext, dynamicPseudo) == SelectorMatches)
1006 case CSSSelector::PseudoAutofill:
1007 if (!element || !element->isFormControlElement())
1009 if (HTMLInputElement* inputElement = element->toInputElement())
1010 return inputElement->isAutofilled();
1012 case CSSSelector::PseudoAnyLink:
1013 case CSSSelector::PseudoLink:
1014 // :visited and :link matches are separated later when applying the style. Here both classes match all links...
1015 return element->isLink();
1016 case CSSSelector::PseudoVisited:
1017 // ...except if :visited matching is disabled for ancestor/sibling matching.
1018 return element->isLink() && context.visitedMatchType == VisitedMatchEnabled;
1019 case CSSSelector::PseudoDrag:
1020 if (context.elementStyle)
1021 context.elementStyle->setAffectedByDragRules(true);
1022 else if (element->renderStyle())
1023 element->renderStyle()->setAffectedByDragRules(true);
1024 if (element->renderer() && element->renderer()->isDragging())
1027 case CSSSelector::PseudoFocus:
1028 return matchesFocusPseudoClass(element);
1029 case CSSSelector::PseudoHover:
1030 // If we're in quirks mode, then hover should never match anchors with no
1031 // href and *:hover should not match anything. This is important for sites like wsj.com.
1032 if (m_strictParsing || context.isSubSelector || (selector->hasTag() && !element->hasTagName(aTag)) || element->isLink()) {
1033 if (context.elementStyle)
1034 context.elementStyle->setAffectedByHoverRules(true);
1035 else if (element->renderStyle())
1036 element->renderStyle()->setAffectedByHoverRules(true);
1037 if (element->hovered() || InspectorInstrumentation::forcePseudoState(element, CSSSelector::PseudoHover))
1041 case CSSSelector::PseudoActive:
1042 // If we're in quirks mode, then :active should never match anchors with no
1043 // href and *:active should not match anything.
1044 if (m_strictParsing || context.isSubSelector || (selector->hasTag() && !element->hasTagName(aTag)) || element->isLink()) {
1045 if (context.elementStyle)
1046 context.elementStyle->setAffectedByActiveRules(true);
1047 else if (element->renderStyle())
1048 element->renderStyle()->setAffectedByActiveRules(true);
1049 if (element->active() || InspectorInstrumentation::forcePseudoState(element, CSSSelector::PseudoActive))
1053 case CSSSelector::PseudoEnabled:
1054 if (element && (element->isFormControlElement() || element->hasTagName(optionTag) || element->hasTagName(optgroupTag)))
1055 return element->isEnabledFormControl();
1057 case CSSSelector::PseudoFullPageMedia:
1058 return element && element->document() && element->document()->isMediaDocument();
1060 case CSSSelector::PseudoDefault:
1061 return element && element->isDefaultButtonForForm();
1062 case CSSSelector::PseudoDisabled:
1063 if (element && (element->isFormControlElement() || element->hasTagName(optionTag) || element->hasTagName(optgroupTag)))
1064 return !element->isEnabledFormControl();
1066 case CSSSelector::PseudoReadOnly:
1067 if (!element || !element->isFormControlElement())
1069 return element->isTextFormControl() && element->isReadOnlyFormControl();
1070 case CSSSelector::PseudoReadWrite:
1071 if (!element || !element->isFormControlElement())
1073 return element->isTextFormControl() && !element->isReadOnlyFormControl();
1074 case CSSSelector::PseudoOptional:
1075 return element && element->isOptionalFormControl();
1076 case CSSSelector::PseudoRequired:
1077 return element && element->isRequiredFormControl();
1078 case CSSSelector::PseudoValid:
1081 element->document()->setContainsValidityStyleRules();
1082 return element->willValidate() && element->isValidFormControlElement();
1083 case CSSSelector::PseudoInvalid:
1086 element->document()->setContainsValidityStyleRules();
1087 return element->willValidate() && !element->isValidFormControlElement();
1088 case CSSSelector::PseudoChecked:
1092 // Even though WinIE allows checked and indeterminate to co-exist, the CSS selector spec says that
1093 // you can't be both checked and indeterminate. We will behave like WinIE behind the scenes and just
1094 // obey the CSS spec here in the test for matching the pseudo.
1095 HTMLInputElement* inputElement = element->toInputElement();
1096 if (inputElement && inputElement->shouldAppearChecked() && !inputElement->isIndeterminate())
1098 if (element->hasTagName(optionTag) && toHTMLOptionElement(element)->selected())
1102 case CSSSelector::PseudoIndeterminate:
1106 #if ENABLE(PROGRESS_ELEMENT)
1107 if (element->hasTagName(progressTag)) {
1108 HTMLProgressElement* progress = static_cast<HTMLProgressElement*>(element);
1109 if (progress && !progress->isDeterminate())
1114 HTMLInputElement* inputElement = element->toInputElement();
1115 if (inputElement && inputElement->isIndeterminate())
1119 case CSSSelector::PseudoScope:
1121 return element == context.scope;
1122 // If there is no scope, :scope should behave as :root -> fall through
1123 case CSSSelector::PseudoRoot:
1124 if (element == element->document()->documentElement())
1127 case CSSSelector::PseudoLang:
1129 AtomicString value = element->computeInheritedLanguage();
1130 const AtomicString& argument = selector->argument();
1131 if (value.isEmpty() || !value.startsWith(argument, false))
1133 if (value.length() != argument.length() && value[argument.length()] != '-')
1137 #if ENABLE(FULLSCREEN_API)
1138 case CSSSelector::PseudoFullScreen:
1139 // While a Document is in the fullscreen state, and the document's current fullscreen
1140 // element is an element in the document, the 'full-screen' pseudoclass applies to
1141 // that element. Also, an <iframe>, <object> or <embed> element whose child browsing
1142 // context's Document is in the fullscreen state has the 'full-screen' pseudoclass applied.
1143 if (element->isFrameElementBase() && static_cast<HTMLFrameElementBase*>(element)->containsFullScreenElement())
1145 if (!element->document()->webkitIsFullScreen())
1147 return element == element->document()->webkitCurrentFullScreenElement();
1148 case CSSSelector::PseudoAnimatingFullScreenTransition:
1149 if (element != element->document()->webkitCurrentFullScreenElement())
1151 return element->document()->isAnimatingFullScreen();
1152 case CSSSelector::PseudoFullScreenAncestor:
1153 return element->containsFullScreenElement();
1154 case CSSSelector::PseudoFullScreenDocument:
1155 // While a Document is in the fullscreen state, the 'full-screen-document' pseudoclass applies
1156 // to all elements of that Document.
1157 if (!element->document()->webkitIsFullScreen())
1161 case CSSSelector::PseudoInRange:
1164 element->document()->setContainsValidityStyleRules();
1165 return element->isInRange();
1166 case CSSSelector::PseudoOutOfRange:
1169 element->document()->setContainsValidityStyleRules();
1170 return element->isOutOfRange();
1171 case CSSSelector::PseudoUnknown:
1172 case CSSSelector::PseudoNotParsed:
1174 ASSERT_NOT_REACHED();
1179 if (selector->m_match == CSSSelector::PseudoElement) {
1180 if (selector->isUnknownPseudoElement()) {
1181 m_hasUnknownPseudoElements = true;
1182 return element->shadowPseudoId() == selector->value();
1185 if ((!context.elementStyle && m_mode == ResolvingStyle) || m_mode == QueryingRules)
1188 PseudoId pseudoId = CSSSelector::pseudoId(selector->pseudoType());
1189 if (pseudoId == FIRST_LETTER) {
1190 if (Document* document = element->document())
1191 document->setUsesFirstLetterRules(true);
1193 if (pseudoId != NOPSEUDO)
1194 dynamicPseudo = pseudoId;
1196 // ### add the rest of the checks...
1200 bool SelectorChecker::checkScrollbarPseudoClass(CSSSelector* sel, PseudoId&) const
1202 RenderScrollbar* scrollbar = RenderScrollbar::scrollbarForStyleResolve();
1203 ScrollbarPart part = RenderScrollbar::partForStyleResolve();
1205 // FIXME: This is a temporary hack for resizers and scrollbar corners. Eventually :window-inactive should become a real
1206 // pseudo class and just apply to everything.
1207 if (sel->pseudoType() == CSSSelector::PseudoWindowInactive)
1208 return !m_document->page()->focusController()->isActive();
1213 ASSERT(sel->m_match == CSSSelector::PseudoClass);
1214 switch (sel->pseudoType()) {
1215 case CSSSelector::PseudoEnabled:
1216 return scrollbar->enabled();
1217 case CSSSelector::PseudoDisabled:
1218 return !scrollbar->enabled();
1219 case CSSSelector::PseudoHover:
1221 ScrollbarPart hoveredPart = scrollbar->hoveredPart();
1222 if (part == ScrollbarBGPart)
1223 return hoveredPart != NoPart;
1224 if (part == TrackBGPart)
1225 return hoveredPart == BackTrackPart || hoveredPart == ForwardTrackPart || hoveredPart == ThumbPart;
1226 return part == hoveredPart;
1228 case CSSSelector::PseudoActive:
1230 ScrollbarPart pressedPart = scrollbar->pressedPart();
1231 if (part == ScrollbarBGPart)
1232 return pressedPart != NoPart;
1233 if (part == TrackBGPart)
1234 return pressedPart == BackTrackPart || pressedPart == ForwardTrackPart || pressedPart == ThumbPart;
1235 return part == pressedPart;
1237 case CSSSelector::PseudoHorizontal:
1238 return scrollbar->orientation() == HorizontalScrollbar;
1239 case CSSSelector::PseudoVertical:
1240 return scrollbar->orientation() == VerticalScrollbar;
1241 case CSSSelector::PseudoDecrement:
1242 return part == BackButtonStartPart || part == BackButtonEndPart || part == BackTrackPart;
1243 case CSSSelector::PseudoIncrement:
1244 return part == ForwardButtonStartPart || part == ForwardButtonEndPart || part == ForwardTrackPart;
1245 case CSSSelector::PseudoStart:
1246 return part == BackButtonStartPart || part == ForwardButtonStartPart || part == BackTrackPart;
1247 case CSSSelector::PseudoEnd:
1248 return part == BackButtonEndPart || part == ForwardButtonEndPart || part == ForwardTrackPart;
1249 case CSSSelector::PseudoDoubleButton:
1251 ScrollbarButtonsPlacement buttonsPlacement = scrollbar->theme()->buttonsPlacement();
1252 if (part == BackButtonStartPart || part == ForwardButtonStartPart || part == BackTrackPart)
1253 return buttonsPlacement == ScrollbarButtonsDoubleStart || buttonsPlacement == ScrollbarButtonsDoubleBoth;
1254 if (part == BackButtonEndPart || part == ForwardButtonEndPart || part == ForwardTrackPart)
1255 return buttonsPlacement == ScrollbarButtonsDoubleEnd || buttonsPlacement == ScrollbarButtonsDoubleBoth;
1258 case CSSSelector::PseudoSingleButton:
1260 ScrollbarButtonsPlacement buttonsPlacement = scrollbar->theme()->buttonsPlacement();
1261 if (part == BackButtonStartPart || part == ForwardButtonEndPart || part == BackTrackPart || part == ForwardTrackPart)
1262 return buttonsPlacement == ScrollbarButtonsSingle;
1265 case CSSSelector::PseudoNoButton:
1267 ScrollbarButtonsPlacement buttonsPlacement = scrollbar->theme()->buttonsPlacement();
1268 if (part == BackTrackPart)
1269 return buttonsPlacement == ScrollbarButtonsNone || buttonsPlacement == ScrollbarButtonsDoubleEnd;
1270 if (part == ForwardTrackPart)
1271 return buttonsPlacement == ScrollbarButtonsNone || buttonsPlacement == ScrollbarButtonsDoubleStart;
1274 case CSSSelector::PseudoCornerPresent:
1275 return scrollbar->scrollableArea()->isScrollCornerVisible();
1281 void SelectorChecker::allVisitedStateChanged()
1283 if (m_linksCheckedForVisitedState.isEmpty())
1285 for (Node* node = m_document; node; node = node->traverseNextNode()) {
1287 node->setNeedsStyleRecalc();
1291 void SelectorChecker::visitedStateChanged(LinkHash visitedHash)
1293 if (!m_linksCheckedForVisitedState.contains(visitedHash))
1295 for (Node* node = m_document; node; node = node->traverseNextNode()) {
1297 if (node->hasTagName(aTag))
1298 hash = static_cast<HTMLAnchorElement*>(node)->visitedLinkHash();
1299 else if (const AtomicString* attr = linkAttribute(node))
1300 hash = visitedLinkHash(m_document->baseURL(), *attr);
1301 if (hash == visitedHash)
1302 node->setNeedsStyleRecalc();
1306 bool SelectorChecker::commonPseudoClassSelectorMatches(const Element* element, const CSSSelector* selector, VisitedMatchType visitedMatchType) const
1308 ASSERT(isCommonPseudoClassSelector(selector));
1309 switch (selector->pseudoType()) {
1310 case CSSSelector::PseudoLink:
1311 case CSSSelector::PseudoAnyLink:
1312 return element->isLink();
1313 case CSSSelector::PseudoVisited:
1314 return element->isLink() && visitedMatchType == VisitedMatchEnabled;
1315 case CSSSelector::PseudoFocus:
1316 return matchesFocusPseudoClass(element);
1318 ASSERT_NOT_REACHED();
1323 unsigned SelectorChecker::determineLinkMatchType(const CSSSelector* selector)
1325 unsigned linkMatchType = MatchAll;
1327 // Statically determine if this selector will match a link in visited, unvisited or any state, or never.
1328 // :visited never matches other elements than the innermost link element.
1329 for (; selector; selector = selector->tagHistory()) {
1330 switch (selector->pseudoType()) {
1331 case CSSSelector::PseudoNot:
1333 // :not(:visited) is equivalent to :link. Parser enforces that :not can't nest.
1334 CSSSelectorList* selectorList = selector->selectorList();
1338 for (CSSSelector* subSelector = selectorList->first(); subSelector; subSelector = subSelector->tagHistory()) {
1339 CSSSelector::PseudoType subType = subSelector->pseudoType();
1340 if (subType == CSSSelector::PseudoVisited)
1341 linkMatchType &= ~SelectorChecker::MatchVisited;
1342 else if (subType == CSSSelector::PseudoLink)
1343 linkMatchType &= ~SelectorChecker::MatchLink;
1347 case CSSSelector::PseudoLink:
1348 linkMatchType &= ~SelectorChecker::MatchVisited;
1350 case CSSSelector::PseudoVisited:
1351 linkMatchType &= ~SelectorChecker::MatchLink;
1354 // We don't support :link and :visited inside :-webkit-any.
1357 CSSSelector::Relation relation = selector->relation();
1358 if (relation == CSSSelector::SubSelector)
1360 if (relation != CSSSelector::Descendant && relation != CSSSelector::Child)
1361 return linkMatchType;
1362 if (linkMatchType != MatchAll)
1363 return linkMatchType;
1365 return linkMatchType;
1368 bool SelectorChecker::isFrameFocused(const Element* element)
1370 return element->document()->frame() && element->document()->frame()->selection()->isFocusedAndActive();
1373 bool SelectorChecker::determineSelectorScopes(const CSSSelectorList& selectorList, HashSet<AtomicStringImpl*>& idScopes, HashSet<AtomicStringImpl*>& classScopes)
1375 for (CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(selector)) {
1376 CSSSelector* scopeSelector = 0;
1377 // This picks the widest scope, not the narrowest, to minimize the number of found scopes.
1378 for (CSSSelector* current = selector; current; current = current->tagHistory()) {
1379 // Prefer ids over classes.
1380 if (current->m_match == CSSSelector::Id)
1381 scopeSelector = current;
1382 else if (current->m_match == CSSSelector::Class && (!scopeSelector || scopeSelector->m_match != CSSSelector::Id))
1383 scopeSelector = current;
1384 CSSSelector::Relation relation = current->relation();
1385 if (relation != CSSSelector::Descendant && relation != CSSSelector::Child && relation != CSSSelector::SubSelector)
1390 ASSERT(scopeSelector->m_match == CSSSelector::Class || scopeSelector->m_match == CSSSelector::Id);
1391 if (scopeSelector->m_match == CSSSelector::Id)
1392 idScopes.add(scopeSelector->value().impl());
1394 classScopes.add(scopeSelector->value().impl());