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 "HTMLFrameElementBase.h"
37 #include "HTMLInputElement.h"
38 #include "HTMLNames.h"
39 #include "HTMLProgressElement.h"
40 #include "InspectorInstrumentation.h"
41 #include "NodeRenderStyle.h"
43 #include "PageGroup.h"
44 #include "RenderObject.h"
45 #include "RenderScrollbar.h"
46 #include "RenderStyle.h"
47 #include "ScrollableArea.h"
48 #include "ScrollbarTheme.h"
49 #include "StyledElement.h"
52 #if USE(PLATFORM_STRATEGIES)
53 #include "PlatformStrategies.h"
54 #include "VisitedLinkStrategy.h"
59 #include "XLinkNames.h"
63 //FIXME: Remove this Qt specific code from a platform neutral file.
64 #include <qwebhistoryinterface.h>
69 using namespace HTMLNames;
71 SelectorChecker::SelectorChecker(Document* document, bool strictParsing)
72 : m_document(document)
73 , m_strictParsing(strictParsing)
74 , m_documentIsHTML(document->isHTMLDocument())
75 , m_isCollectingRulesOnly(false)
76 , m_pseudoStyle(NOPSEUDO)
77 , m_hasUnknownPseudoElements(false)
78 , m_isMatchingVisitedPseudoClass(false)
82 static inline void collectElementIdentifierHashes(const Element* element, Vector<unsigned, 4>& identifierHashes)
84 identifierHashes.append(element->localName().impl()->existingHash());
86 identifierHashes.append(element->idForStyleResolution().impl()->existingHash());
87 const StyledElement* styledElement = element->isStyledElement() ? static_cast<const StyledElement*>(element) : 0;
88 if (styledElement && styledElement->hasClass()) {
89 const SpaceSplitString& classNames = styledElement->classNames();
90 size_t count = classNames.size();
91 for (size_t i = 0; i < count; ++i)
92 identifierHashes.append(classNames[i].impl()->existingHash());
96 void SelectorChecker::pushParentStackFrame(Element* parent)
98 ASSERT(m_ancestorIdentifierFilter);
99 ASSERT(m_parentStack.isEmpty() || m_parentStack.last().element == parent->parentOrHostElement());
100 ASSERT(!m_parentStack.isEmpty() || !parent->parentOrHostElement());
101 m_parentStack.append(ParentStackFrame(parent));
102 ParentStackFrame& parentFrame = m_parentStack.last();
103 // Mix tags, class names and ids into some sort of weird bouillabaisse.
104 // The filter is used for fast rejection of child and descendant selectors.
105 collectElementIdentifierHashes(parent, parentFrame.identifierHashes);
106 size_t count = parentFrame.identifierHashes.size();
107 for (size_t i = 0; i < count; ++i)
108 m_ancestorIdentifierFilter->add(parentFrame.identifierHashes[i]);
111 void SelectorChecker::popParentStackFrame()
113 ASSERT(!m_parentStack.isEmpty());
114 ASSERT(m_ancestorIdentifierFilter);
115 const ParentStackFrame& parentFrame = m_parentStack.last();
116 size_t count = parentFrame.identifierHashes.size();
117 for (size_t i = 0; i < count; ++i)
118 m_ancestorIdentifierFilter->remove(parentFrame.identifierHashes[i]);
119 m_parentStack.removeLast();
120 if (m_parentStack.isEmpty()) {
121 ASSERT(m_ancestorIdentifierFilter->likelyEmpty());
122 m_ancestorIdentifierFilter.clear();
126 void SelectorChecker::pushParent(Element* parent)
128 if (m_parentStack.isEmpty()) {
129 ASSERT(!m_ancestorIdentifierFilter);
130 m_ancestorIdentifierFilter = adoptPtr(new BloomFilter<bloomFilterKeyBits>);
131 // If the element is not the root itself, build the stack starting from the root.
132 if (parent->parentOrHostNode()) {
133 Vector<Element*, 30> ancestors;
134 for (Element* ancestor = parent; ancestor; ancestor = ancestor->parentOrHostElement())
135 ancestors.append(ancestor);
136 int count = ancestors.size();
137 for (int n = count - 1; n >= 0; --n)
138 pushParentStackFrame(ancestors[n]);
141 } else if (!parent->parentOrHostElement()) {
142 // We are not always invoked consistently. For example, script execution can cause us to enter
143 // style recalc in the middle of tree building. Reset the stack if we see a new root element.
144 ASSERT(m_ancestorIdentifierFilter);
145 m_ancestorIdentifierFilter->clear();
146 m_parentStack.resize(0);
148 ASSERT(m_ancestorIdentifierFilter);
149 // We may get invoked for some random elements in some wacky cases during style resolve.
150 // Pause maintaining the stack in this case.
151 if (m_parentStack.last().element != parent->parentOrHostElement())
154 pushParentStackFrame(parent);
157 void SelectorChecker::popParent(Element* parent)
159 if (m_parentStack.isEmpty() || m_parentStack.last().element != parent)
161 popParentStackFrame();
164 static inline void collectDescendantSelectorIdentifierHashes(const CSSSelector* selector, unsigned* hash, const unsigned* end)
166 if ((selector->m_match == CSSSelector::Id || selector->m_match == CSSSelector::Class) && !selector->value().isEmpty())
167 (*hash++) = selector->value().impl()->existingHash();
170 const AtomicString& localName = selector->tag().localName();
171 if (localName != starAtom)
172 (*hash++) = localName.impl()->existingHash();
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);
219 if (element->isSVGElement())
220 return &element->fastGetAttribute(XLinkNames::hrefAttr);
226 EInsideLink SelectorChecker::determineLinkStateSlowCase(Element* element) const
228 ASSERT(element->isLink());
230 const AtomicString* attr = linkAttribute(element);
231 if (!attr || attr->isNull())
232 return NotInsideLink;
235 //FIXME: Remove this Qt specific code from a platform neutral file.
236 Vector<UChar, 512> url;
237 visitedURL(m_document->baseURL(), *attr, url);
239 return InsideUnvisitedLink;
241 // If the Qt4.4 interface for the history is used, we will have to fallback
242 // to the old global history.
243 QWebHistoryInterface* iface = QWebHistoryInterface::defaultInterface();
245 return iface->historyContains(QString(reinterpret_cast<QChar*>(url.data()), url.size())) ? InsideVisitedLink : InsideUnvisitedLink;
247 LinkHash hash = visitedLinkHash(url.data(), url.size());
249 return InsideUnvisitedLink;
251 LinkHash hash = visitedLinkHash(m_document->baseURL(), *attr);
253 return InsideUnvisitedLink;
256 Frame* frame = m_document->frame();
258 return InsideUnvisitedLink;
260 Page* page = frame->page();
262 return InsideUnvisitedLink;
264 m_linksCheckedForVisitedState.add(hash);
266 #if USE(PLATFORM_STRATEGIES)
267 return platformStrategies()->visitedLinkStrategy()->isLinkVisited(page, hash) ? InsideVisitedLink : InsideUnvisitedLink;
269 return page->group().isLinkVisited(hash) ? InsideVisitedLink : InsideUnvisitedLink;
273 bool SelectorChecker::checkSelector(CSSSelector* sel, Element* element, bool isFastCheckableSelector) const
275 PseudoId dynamicPseudo = NOPSEUDO;
276 if (isFastCheckableSelector && !element->isSVGElement()) {
277 // fastCheckSelector assumes class and id match for the top selector.
278 if (sel->m_match == CSSSelector::Class) {
279 if (!(element->hasClass() && static_cast<StyledElement*>(element)->classNames().contains(sel->value())))
281 } else if (sel->m_match == CSSSelector::Id) {
282 if (!(element->hasID() && element->idForStyleResolution() == sel->value()))
285 return fastCheckSelector(sel, element);
287 return checkSelector(sel, element, dynamicPseudo, false, false) == SelectorMatches;
292 inline bool selectorTagMatches(const Element* element, const CSSSelector* selector)
294 if (!selector->hasTag())
296 const AtomicString& localName = selector->tag().localName();
297 if (localName != starAtom && localName != element->localName())
299 const AtomicString& namespaceURI = selector->tag().namespaceURI();
300 return namespaceURI == starAtom || namespaceURI == element->namespaceURI();
303 template <bool checkValue(const Element*, AtomicStringImpl*)>
304 inline bool fastCheckSingleSelector(const CSSSelector*& selector, const Element*& element, const CSSSelector*& topChildOrSubselector, const Element*& topChildOrSubselectorMatchElement)
306 AtomicStringImpl* value = selector->value().impl();
307 for (; element; element = element->parentElement()) {
308 if (checkValue(element, value) && selectorTagMatches(element, selector)) {
309 if (selector->relation() == CSSSelector::Descendant)
310 topChildOrSubselector = 0;
311 else if (!topChildOrSubselector) {
312 ASSERT(selector->relation() == CSSSelector::Child || selector->relation() == CSSSelector::SubSelector);
313 topChildOrSubselector = selector;
314 topChildOrSubselectorMatchElement = element;
316 if (selector->relation() != CSSSelector::SubSelector)
317 element = element->parentElement();
318 selector = selector->tagHistory();
321 if (topChildOrSubselector) {
322 // Child or subselector check failed.
323 // If the match element is null, topChildOrSubselector was also the very topmost selector and had to match
324 // the original element we were checking.
325 if (!topChildOrSubselectorMatchElement)
327 // There may be other matches down the ancestor chain.
328 // Rewind to the topmost child or subselector and the element it matched, continue checking ancestors.
329 selector = topChildOrSubselector;
330 element = topChildOrSubselectorMatchElement->parentElement();
331 topChildOrSubselector = 0;
338 inline bool checkClassValue(const Element* element, AtomicStringImpl* value)
340 return element->hasClass() && static_cast<const StyledElement*>(element)->classNames().contains(value);
343 inline bool checkIDValue(const Element* element, AtomicStringImpl* value)
345 return element->hasID() && element->idForStyleResolution().impl() == value;
348 inline bool checkTagValue(const Element*, AtomicStringImpl*)
355 bool SelectorChecker::fastCheckSelector(const CSSSelector* selector, const Element* element)
357 ASSERT(isFastCheckableSelector(selector));
359 // The top selector requires tag check only as rule hashes have already handled id and class matches.
360 if (!selectorTagMatches(element, selector))
363 const CSSSelector* topChildOrSubselector = 0;
364 const Element* topChildOrSubselectorMatchElement = 0;
365 if (selector->relation() == CSSSelector::Child || selector->relation() == CSSSelector::SubSelector)
366 topChildOrSubselector = selector;
368 if (selector->relation() != CSSSelector::SubSelector)
369 element = element->parentElement();
371 selector = selector->tagHistory();
373 // We know this compound selector has descendant, child and subselector combinators only and all components are simple.
375 switch (selector->m_match) {
376 case CSSSelector::Class:
377 if (!fastCheckSingleSelector<checkClassValue>(selector, element, topChildOrSubselector, topChildOrSubselectorMatchElement))
380 case CSSSelector::Id:
381 if (!fastCheckSingleSelector<checkIDValue>(selector, element, topChildOrSubselector, topChildOrSubselectorMatchElement))
384 case CSSSelector::None:
385 if (!fastCheckSingleSelector<checkTagValue>(selector, element, topChildOrSubselector, topChildOrSubselectorMatchElement))
389 ASSERT_NOT_REACHED();
395 bool SelectorChecker::isFastCheckableSelector(const CSSSelector* selector)
397 for (; selector; selector = selector->tagHistory()) {
398 if (selector->relation() != CSSSelector::Descendant && selector->relation() != CSSSelector::Child && selector->relation() != CSSSelector::SubSelector)
400 if (selector->m_match != CSSSelector::None && selector->m_match != CSSSelector::Id && selector->m_match != CSSSelector::Class)
406 // Recursive check of selectors and combinators
407 // It can return 3 different values:
408 // * SelectorMatches - the selector matches the element e
409 // * SelectorFailsLocally - the selector fails for the element e
410 // * SelectorFailsCompletely - the selector fails for e and any sibling or ancestor of e
411 SelectorChecker::SelectorMatch SelectorChecker::checkSelector(CSSSelector* sel, Element* e, PseudoId& dynamicPseudo, bool isSubSelector, bool encounteredLink, RenderStyle* elementStyle, RenderStyle* elementParentStyle) const
414 // Spec: CSS2 selectors cannot be applied to the (conceptually) cloned DOM tree
415 // because its contents are not part of the formal document structure.
416 if (e->isSVGShadowRoot())
417 return SelectorFailsCompletely;
420 // first selector has to match
421 if (!checkOneSelector(sel, e, dynamicPseudo, isSubSelector, encounteredLink, elementStyle, elementParentStyle))
422 return SelectorFailsLocally;
424 // The rest of the selectors has to match
425 CSSSelector::Relation relation = sel->relation();
428 sel = sel->tagHistory();
430 return SelectorMatches;
432 if (relation != CSSSelector::SubSelector)
433 // Bail-out if this selector is irrelevant for the pseudoStyle
434 if (m_pseudoStyle != NOPSEUDO && m_pseudoStyle != dynamicPseudo)
435 return SelectorFailsCompletely;
437 // Check for nested links.
438 if (m_isMatchingVisitedPseudoClass && !isSubSelector) {
439 RenderStyle* currentStyle = elementStyle ? elementStyle : e->renderStyle();
440 if (currentStyle && currentStyle->insideLink() && e->isLink()) {
442 m_isMatchingVisitedPseudoClass = false; // This link is not relevant to the style being resolved, so disable matching.
444 encounteredLink = true;
449 case CSSSelector::Descendant:
451 ContainerNode* n = e->parentNode();
452 if (!n || !n->isElementNode())
453 return SelectorFailsCompletely;
454 e = static_cast<Element*>(n);
455 SelectorMatch match = checkSelector(sel, e, dynamicPseudo, false, encounteredLink);
456 if (match != SelectorFailsLocally)
460 case CSSSelector::Child:
462 ContainerNode* n = e->parentNode();
463 if (!n || !n->isElementNode())
464 return SelectorFailsCompletely;
465 e = static_cast<Element*>(n);
466 return checkSelector(sel, e, dynamicPseudo, false, encounteredLink);
468 case CSSSelector::DirectAdjacent:
470 if (!m_isCollectingRulesOnly) {
471 RenderStyle* currentStyle = elementStyle ? elementStyle : e->renderStyle();
473 currentStyle->setAffectedByDirectAdjacentRules();
475 Node* n = e->previousSibling();
476 while (n && !n->isElementNode())
477 n = n->previousSibling();
479 return SelectorFailsLocally;
480 e = static_cast<Element*>(n);
481 m_isMatchingVisitedPseudoClass = false;
482 return checkSelector(sel, e, dynamicPseudo, false, encounteredLink);
484 case CSSSelector::IndirectAdjacent:
485 if (!m_isCollectingRulesOnly && e->parentNode() && e->parentNode()->isElementNode()) {
486 RenderStyle* parentStyle = elementStyle ? elementParentStyle : e->parentNode()->renderStyle();
488 parentStyle->setChildrenAffectedByForwardPositionalRules();
491 Node* n = e->previousSibling();
492 while (n && !n->isElementNode())
493 n = n->previousSibling();
495 return SelectorFailsLocally;
496 e = static_cast<Element*>(n);
497 m_isMatchingVisitedPseudoClass = false;
498 SelectorMatch match = checkSelector(sel, e, dynamicPseudo, false, encounteredLink);
499 if (match != SelectorFailsLocally)
503 case CSSSelector::SubSelector:
504 // a selector is invalid if something follows a pseudo-element
505 // We make an exception for scrollbar pseudo elements and allow a set of pseudo classes (but nothing else)
506 // to follow the pseudo elements.
507 if ((elementStyle || m_isCollectingRulesOnly) && dynamicPseudo != NOPSEUDO && dynamicPseudo != SELECTION
508 && !((RenderScrollbar::scrollbarForStyleResolve() || dynamicPseudo == SCROLLBAR_CORNER || dynamicPseudo == RESIZER) && sel->m_match == CSSSelector::PseudoClass))
509 return SelectorFailsCompletely;
510 return checkSelector(sel, e, dynamicPseudo, true, encounteredLink, elementStyle, elementParentStyle);
511 case CSSSelector::ShadowDescendant:
513 Node* shadowHostNode = e->shadowAncestorNode();
514 if (shadowHostNode == e || !shadowHostNode->isElementNode())
515 return SelectorFailsCompletely;
516 e = static_cast<Element*>(shadowHostNode);
517 return checkSelector(sel, e, dynamicPseudo, false, encounteredLink);
521 return SelectorFailsCompletely;
524 static void addLocalNameToSet(HashSet<AtomicStringImpl*>* set, const QualifiedName& qName)
526 set->add(qName.localName().impl());
529 static HashSet<AtomicStringImpl*>* createHtmlCaseInsensitiveAttributesSet()
531 // This is the list of attributes in HTML 4.01 with values marked as "[CI]" or case-insensitive
532 // Mozilla treats all other values as case-sensitive, thus so do we.
533 HashSet<AtomicStringImpl*>* attrSet = new HashSet<AtomicStringImpl*>;
535 addLocalNameToSet(attrSet, accept_charsetAttr);
536 addLocalNameToSet(attrSet, acceptAttr);
537 addLocalNameToSet(attrSet, alignAttr);
538 addLocalNameToSet(attrSet, alinkAttr);
539 addLocalNameToSet(attrSet, axisAttr);
540 addLocalNameToSet(attrSet, bgcolorAttr);
541 addLocalNameToSet(attrSet, charsetAttr);
542 addLocalNameToSet(attrSet, checkedAttr);
543 addLocalNameToSet(attrSet, clearAttr);
544 addLocalNameToSet(attrSet, codetypeAttr);
545 addLocalNameToSet(attrSet, colorAttr);
546 addLocalNameToSet(attrSet, compactAttr);
547 addLocalNameToSet(attrSet, declareAttr);
548 addLocalNameToSet(attrSet, deferAttr);
549 addLocalNameToSet(attrSet, dirAttr);
550 addLocalNameToSet(attrSet, disabledAttr);
551 addLocalNameToSet(attrSet, enctypeAttr);
552 addLocalNameToSet(attrSet, faceAttr);
553 addLocalNameToSet(attrSet, frameAttr);
554 addLocalNameToSet(attrSet, hreflangAttr);
555 addLocalNameToSet(attrSet, http_equivAttr);
556 addLocalNameToSet(attrSet, langAttr);
557 addLocalNameToSet(attrSet, languageAttr);
558 addLocalNameToSet(attrSet, linkAttr);
559 addLocalNameToSet(attrSet, mediaAttr);
560 addLocalNameToSet(attrSet, methodAttr);
561 addLocalNameToSet(attrSet, multipleAttr);
562 addLocalNameToSet(attrSet, nohrefAttr);
563 addLocalNameToSet(attrSet, noresizeAttr);
564 addLocalNameToSet(attrSet, noshadeAttr);
565 addLocalNameToSet(attrSet, nowrapAttr);
566 addLocalNameToSet(attrSet, readonlyAttr);
567 addLocalNameToSet(attrSet, relAttr);
568 addLocalNameToSet(attrSet, revAttr);
569 addLocalNameToSet(attrSet, rulesAttr);
570 addLocalNameToSet(attrSet, scopeAttr);
571 addLocalNameToSet(attrSet, scrollingAttr);
572 addLocalNameToSet(attrSet, selectedAttr);
573 addLocalNameToSet(attrSet, shapeAttr);
574 addLocalNameToSet(attrSet, targetAttr);
575 addLocalNameToSet(attrSet, textAttr);
576 addLocalNameToSet(attrSet, typeAttr);
577 addLocalNameToSet(attrSet, valignAttr);
578 addLocalNameToSet(attrSet, valuetypeAttr);
579 addLocalNameToSet(attrSet, vlinkAttr);
584 static bool htmlAttributeHasCaseInsensitiveValue(const QualifiedName& attr)
586 static HashSet<AtomicStringImpl*>* htmlCaseInsensitiveAttributesSet = createHtmlCaseInsensitiveAttributesSet();
587 bool isPossibleHTMLAttr = !attr.hasPrefix() && (attr.namespaceURI() == nullAtom);
588 return isPossibleHTMLAttr && htmlCaseInsensitiveAttributesSet->contains(attr.localName().impl());
591 static bool attributeQualifiedNameMatches(Attribute* attribute, const QualifiedName& selectorAttr)
593 if (selectorAttr.localName() != attribute->localName())
596 return selectorAttr.prefix() == starAtom || selectorAttr.namespaceURI() == attribute->namespaceURI();
599 static bool attributeValueMatches(Attribute* attributeItem, CSSSelector::Match match, const AtomicString& selectorValue, bool caseSensitive)
601 const AtomicString& value = attributeItem->value();
606 case CSSSelector::Exact:
607 if (caseSensitive ? selectorValue != value : !equalIgnoringCase(selectorValue, value))
610 case CSSSelector::List:
612 // Ignore empty selectors or selectors containing spaces
613 if (selectorValue.contains(' ') || selectorValue.isEmpty())
616 unsigned startSearchAt = 0;
618 size_t foundPos = value.find(selectorValue, startSearchAt, caseSensitive);
619 if (foundPos == notFound)
621 if (!foundPos || value[foundPos - 1] == ' ') {
622 unsigned endStr = foundPos + selectorValue.length();
623 if (endStr == value.length() || value[endStr] == ' ')
624 break; // We found a match.
627 // No match. Keep looking.
628 startSearchAt = foundPos + 1;
632 case CSSSelector::Contain:
633 if (!value.contains(selectorValue, caseSensitive) || selectorValue.isEmpty())
636 case CSSSelector::Begin:
637 if (!value.startsWith(selectorValue, caseSensitive) || selectorValue.isEmpty())
640 case CSSSelector::End:
641 if (!value.endsWith(selectorValue, caseSensitive) || selectorValue.isEmpty())
644 case CSSSelector::Hyphen:
645 if (value.length() < selectorValue.length())
647 if (!value.startsWith(selectorValue, caseSensitive))
649 // It they start the same, check for exact match or following '-':
650 if (value.length() != selectorValue.length() && value[selectorValue.length()] != '-')
653 case CSSSelector::PseudoClass:
654 case CSSSelector::PseudoElement:
662 static bool anyAttributeMatches(NamedNodeMap* attributes, CSSSelector::Match match, const QualifiedName& selectorAttr, const AtomicString& selectorValue, bool caseSensitive)
664 for (size_t i = 0; i < attributes->length(); ++i) {
665 Attribute* attributeItem = attributes->attributeItem(i);
667 if (!attributeQualifiedNameMatches(attributeItem, selectorAttr))
670 if (attributeValueMatches(attributeItem, match, selectorValue, caseSensitive))
677 bool SelectorChecker::checkOneSelector(CSSSelector* sel, Element* e, PseudoId& dynamicPseudo, bool isSubSelector, bool encounteredLink, RenderStyle* elementStyle, RenderStyle* elementParentStyle) const
683 if (!selectorTagMatches(e, sel))
686 if (sel->hasAttribute()) {
687 if (sel->m_match == CSSSelector::Class)
688 return e->hasClass() && static_cast<StyledElement*>(e)->classNames().contains(sel->value());
690 if (sel->m_match == CSSSelector::Id)
691 return e->hasID() && e->idForStyleResolution() == sel->value();
693 const QualifiedName& attr = sel->attribute();
695 // FIXME: Handle the case were elementStyle is 0.
696 if (elementStyle && (!e->isStyledElement() || (!static_cast<StyledElement*>(e)->isMappedAttribute(attr) && attr != typeAttr && attr != readonlyAttr)))
697 elementStyle->setAffectedByAttributeSelectors(); // Special-case the "type" and "readonly" attributes so input form controls can share style.
699 NamedNodeMap* attributes = e->attributes(true);
703 bool caseSensitive = !m_documentIsHTML || !htmlAttributeHasCaseInsensitiveValue(attr);
705 if (!anyAttributeMatches(attributes, static_cast<CSSSelector::Match>(sel->m_match), attr, sel->value(), caseSensitive))
709 if (sel->m_match == CSSSelector::PseudoClass) {
710 // Handle :not up front.
711 if (sel->pseudoType() == CSSSelector::PseudoNot) {
712 ASSERT(sel->selectorList());
713 for (CSSSelector* subSel = sel->selectorList()->first(); subSel; subSel = subSel->tagHistory()) {
714 // :not cannot nest. I don't really know why this is a
715 // restriction in CSS3, but it is, so let's honor it.
716 // the parser enforces that this never occurs
717 ASSERT(subSel->pseudoType() != CSSSelector::PseudoNot);
719 if (!checkOneSelector(subSel, e, dynamicPseudo, true, encounteredLink, elementStyle, elementParentStyle))
722 } else if (dynamicPseudo != NOPSEUDO && (RenderScrollbar::scrollbarForStyleResolve() || dynamicPseudo == SCROLLBAR_CORNER || dynamicPseudo == RESIZER)) {
723 // CSS scrollbars match a specific subset of pseudo classes, and they have specialized rules for each
724 // (since there are no elements involved).
725 return checkScrollbarPseudoClass(sel, dynamicPseudo);
726 } else if (dynamicPseudo == SELECTION) {
727 if (sel->pseudoType() == CSSSelector::PseudoWindowInactive)
728 return !m_document->page()->focusController()->isActive();
731 // Normal element pseudo class checking.
732 switch (sel->pseudoType()) {
734 case CSSSelector::PseudoNot:
735 break; // Already handled up above.
736 case CSSSelector::PseudoEmpty:
739 for (Node* n = e->firstChild(); n; n = n->nextSibling()) {
740 if (n->isElementNode()) {
744 if (n->isTextNode()) {
745 Text* textNode = static_cast<Text*>(n);
746 if (!textNode->data().isEmpty()) {
752 if (!m_isCollectingRulesOnly) {
754 elementStyle->setEmptyState(result);
755 else if (e->renderStyle() && (e->document()->usesSiblingRules() || e->renderStyle()->unique()))
756 e->renderStyle()->setEmptyState(result);
760 case CSSSelector::PseudoFirstChild:
761 // first-child matches the first child that is an element
762 if (e->parentNode() && e->parentNode()->isElementNode()) {
764 Node* n = e->previousSibling();
765 while (n && !n->isElementNode())
766 n = n->previousSibling();
769 if (!m_isCollectingRulesOnly) {
770 RenderStyle* childStyle = elementStyle ? elementStyle : e->renderStyle();
771 RenderStyle* parentStyle = elementStyle ? elementParentStyle : e->parentNode()->renderStyle();
773 parentStyle->setChildrenAffectedByFirstChildRules();
774 if (result && childStyle)
775 childStyle->setFirstChildState();
780 case CSSSelector::PseudoFirstOfType:
781 // first-of-type matches the first element of its type
782 if (e->parentNode() && e->parentNode()->isElementNode()) {
784 const QualifiedName& type = e->tagQName();
785 Node* n = e->previousSibling();
787 if (n->isElementNode() && static_cast<Element*>(n)->hasTagName(type))
789 n = n->previousSibling();
793 if (!m_isCollectingRulesOnly) {
794 RenderStyle* parentStyle = elementStyle ? elementParentStyle : e->parentNode()->renderStyle();
796 parentStyle->setChildrenAffectedByForwardPositionalRules();
801 case CSSSelector::PseudoLastChild:
802 // last-child matches the last child that is an element
803 if (Element* parentElement = e->parentElement()) {
805 if (parentElement->isFinishedParsingChildren()) {
806 Node* n = e->nextSibling();
807 while (n && !n->isElementNode())
808 n = n->nextSibling();
812 if (!m_isCollectingRulesOnly) {
813 RenderStyle* childStyle = elementStyle ? elementStyle : e->renderStyle();
814 RenderStyle* parentStyle = elementStyle ? elementParentStyle : parentElement->renderStyle();
816 parentStyle->setChildrenAffectedByLastChildRules();
817 if (result && childStyle)
818 childStyle->setLastChildState();
823 case CSSSelector::PseudoLastOfType:
824 // last-of-type matches the last element of its type
825 if (Element* parentElement = e->parentElement()) {
826 if (!m_isCollectingRulesOnly) {
827 RenderStyle* parentStyle = elementStyle ? elementParentStyle : parentElement->renderStyle();
829 parentStyle->setChildrenAffectedByBackwardPositionalRules();
831 if (!parentElement->isFinishedParsingChildren())
834 const QualifiedName& type = e->tagQName();
835 Node* n = e->nextSibling();
837 if (n->isElementNode() && static_cast<Element*>(n)->hasTagName(type))
839 n = n->nextSibling();
846 case CSSSelector::PseudoOnlyChild:
847 if (Element* parentElement = e->parentElement()) {
848 bool firstChild = false;
849 bool lastChild = false;
851 Node* n = e->previousSibling();
852 while (n && !n->isElementNode())
853 n = n->previousSibling();
856 if (firstChild && parentElement->isFinishedParsingChildren()) {
857 n = e->nextSibling();
858 while (n && !n->isElementNode())
859 n = n->nextSibling();
863 if (!m_isCollectingRulesOnly) {
864 RenderStyle* childStyle = elementStyle ? elementStyle : e->renderStyle();
865 RenderStyle* parentStyle = elementStyle ? elementParentStyle : parentElement->renderStyle();
867 parentStyle->setChildrenAffectedByFirstChildRules();
868 parentStyle->setChildrenAffectedByLastChildRules();
870 if (firstChild && childStyle)
871 childStyle->setFirstChildState();
872 if (lastChild && childStyle)
873 childStyle->setLastChildState();
875 return firstChild && lastChild;
878 case CSSSelector::PseudoOnlyOfType:
879 // FIXME: This selector is very slow.
880 if (Element* parentElement = e->parentElement()) {
881 if (!m_isCollectingRulesOnly) {
882 RenderStyle* parentStyle = elementStyle ? elementParentStyle : parentElement->renderStyle();
884 parentStyle->setChildrenAffectedByForwardPositionalRules();
885 parentStyle->setChildrenAffectedByBackwardPositionalRules();
888 if (!parentElement->isFinishedParsingChildren())
890 bool firstChild = false;
891 bool lastChild = false;
892 const QualifiedName& type = e->tagQName();
893 Node* n = e->previousSibling();
895 if (n->isElementNode() && static_cast<Element*>(n)->hasTagName(type))
897 n = n->previousSibling();
902 n = e->nextSibling();
904 if (n->isElementNode() && static_cast<Element*>(n)->hasTagName(type))
906 n = n->nextSibling();
911 return firstChild && lastChild;
914 case CSSSelector::PseudoNthChild:
915 if (!sel->parseNth())
917 if (Element* parentElement = e->parentElement()) {
919 Node* n = e->previousSibling();
921 if (n->isElementNode()) {
922 RenderStyle* s = n->renderStyle();
923 unsigned index = s ? s->childIndex() : 0;
930 n = n->previousSibling();
933 if (!m_isCollectingRulesOnly) {
934 RenderStyle* childStyle = elementStyle ? elementStyle : e->renderStyle();
935 RenderStyle* parentStyle = elementStyle ? elementParentStyle : parentElement->renderStyle();
937 childStyle->setChildIndex(count);
939 parentStyle->setChildrenAffectedByForwardPositionalRules();
942 if (sel->matchNth(count))
946 case CSSSelector::PseudoNthOfType:
947 if (!sel->parseNth())
949 if (Element* parentElement = e->parentElement()) {
951 const QualifiedName& type = e->tagQName();
952 Node* n = e->previousSibling();
954 if (n->isElementNode() && static_cast<Element*>(n)->hasTagName(type))
956 n = n->previousSibling();
959 if (!m_isCollectingRulesOnly) {
960 RenderStyle* parentStyle = elementStyle ? elementParentStyle : parentElement->renderStyle();
962 parentStyle->setChildrenAffectedByForwardPositionalRules();
965 if (sel->matchNth(count))
969 case CSSSelector::PseudoNthLastChild:
970 if (!sel->parseNth())
972 if (Element* parentElement = e->parentElement()) {
973 if (!m_isCollectingRulesOnly) {
974 RenderStyle* parentStyle = elementStyle ? elementParentStyle : parentElement->renderStyle();
976 parentStyle->setChildrenAffectedByBackwardPositionalRules();
978 if (!parentElement->isFinishedParsingChildren())
981 Node* n = e->nextSibling();
983 if (n->isElementNode())
985 n = n->nextSibling();
987 if (sel->matchNth(count))
991 case CSSSelector::PseudoNthLastOfType:
992 if (!sel->parseNth())
994 if (Element* parentElement = e->parentElement()) {
995 if (!m_isCollectingRulesOnly) {
996 RenderStyle* parentStyle = elementStyle ? elementParentStyle : parentElement->renderStyle();
998 parentStyle->setChildrenAffectedByBackwardPositionalRules();
1000 if (!parentElement->isFinishedParsingChildren())
1003 const QualifiedName& type = e->tagQName();
1004 Node* n = e->nextSibling();
1006 if (n->isElementNode() && static_cast<Element*>(n)->hasTagName(type))
1008 n = n->nextSibling();
1010 if (sel->matchNth(count))
1014 case CSSSelector::PseudoTarget:
1015 if (e == e->document()->cssTarget())
1018 case CSSSelector::PseudoAny:
1019 for (CSSSelector* selector = sel->selectorList()->first(); selector; selector = CSSSelectorList::next(selector)) {
1020 if (checkSelector(selector, e, dynamicPseudo, true, encounteredLink, elementStyle, elementParentStyle) == SelectorMatches)
1024 case CSSSelector::PseudoAnyLink:
1025 if (e && e->isLink())
1028 case CSSSelector::PseudoAutofill:
1029 if (!e || !e->isFormControlElement())
1031 if (HTMLInputElement* inputElement = e->toInputElement())
1032 return inputElement->isAutofilled();
1034 case CSSSelector::PseudoLink:
1035 if (e && e->isLink())
1036 return !m_isMatchingVisitedPseudoClass;
1038 case CSSSelector::PseudoVisited:
1039 if (e && e->isLink())
1040 return m_isMatchingVisitedPseudoClass || InspectorInstrumentation::forcePseudoState(e, CSSSelector::PseudoVisited);
1042 case CSSSelector::PseudoDrag:
1044 elementStyle->setAffectedByDragRules(true);
1045 else if (e->renderStyle())
1046 e->renderStyle()->setAffectedByDragRules(true);
1047 if (e->renderer() && e->renderer()->isDragging())
1050 case CSSSelector::PseudoFocus:
1051 if (e && ((e->focused() && e->document()->frame() && e->document()->frame()->selection()->isFocusedAndActive()) || InspectorInstrumentation::forcePseudoState(e, CSSSelector::PseudoFocus)))
1054 case CSSSelector::PseudoHover:
1055 // If we're in quirks mode, then hover should never match anchors with no
1056 // href and *:hover should not match anything. This is important for sites like wsj.com.
1057 if (m_strictParsing || isSubSelector || (sel->hasTag() && !e->hasTagName(aTag)) || e->isLink()) {
1059 elementStyle->setAffectedByHoverRules(true);
1060 else if (e->renderStyle())
1061 e->renderStyle()->setAffectedByHoverRules(true);
1062 if (e->hovered() || InspectorInstrumentation::forcePseudoState(e, CSSSelector::PseudoHover))
1066 case CSSSelector::PseudoActive:
1067 // If we're in quirks mode, then :active should never match anchors with no
1068 // href and *:active should not match anything.
1069 if (m_strictParsing || isSubSelector || (sel->hasTag() && !e->hasTagName(aTag)) || e->isLink()) {
1071 elementStyle->setAffectedByActiveRules(true);
1072 else if (e->renderStyle())
1073 e->renderStyle()->setAffectedByActiveRules(true);
1074 if (e->active() || InspectorInstrumentation::forcePseudoState(e, CSSSelector::PseudoActive))
1078 case CSSSelector::PseudoEnabled:
1079 if (e && e->isFormControlElement())
1080 return e->isEnabledFormControl();
1082 case CSSSelector::PseudoFullPageMedia:
1083 return e && e->document() && e->document()->isMediaDocument();
1085 case CSSSelector::PseudoDefault:
1086 return e && e->isDefaultButtonForForm();
1087 case CSSSelector::PseudoDisabled:
1088 if (e && e->isFormControlElement())
1089 return !e->isEnabledFormControl();
1091 case CSSSelector::PseudoReadOnly:
1092 if (!e || !e->isFormControlElement())
1094 return e->isTextFormControl() && e->isReadOnlyFormControl();
1095 case CSSSelector::PseudoReadWrite:
1096 if (!e || !e->isFormControlElement())
1098 return e->isTextFormControl() && !e->isReadOnlyFormControl();
1099 case CSSSelector::PseudoOptional:
1100 return e && e->isOptionalFormControl();
1101 case CSSSelector::PseudoRequired:
1102 return e && e->isRequiredFormControl();
1103 case CSSSelector::PseudoValid:
1106 e->document()->setContainsValidityStyleRules();
1107 return e->willValidate() && e->isValidFormControlElement();
1108 case CSSSelector::PseudoInvalid:
1111 e->document()->setContainsValidityStyleRules();
1112 return (e->willValidate() && !e->isValidFormControlElement()) || e->hasUnacceptableValue();
1113 case CSSSelector::PseudoChecked:
1115 if (!e || !e->isFormControlElement())
1117 // Even though WinIE allows checked and indeterminate to co-exist, the CSS selector spec says that
1118 // you can't be both checked and indeterminate. We will behave like WinIE behind the scenes and just
1119 // obey the CSS spec here in the test for matching the pseudo.
1120 HTMLInputElement* inputElement = e->toInputElement();
1121 if (inputElement && inputElement->shouldAppearChecked() && !inputElement->isIndeterminate())
1125 case CSSSelector::PseudoIndeterminate:
1127 if (!e || !e->isFormControlElement())
1129 #if ENABLE(PROGRESS_TAG)
1130 if (e->hasTagName(progressTag)) {
1131 HTMLProgressElement* progress = static_cast<HTMLProgressElement*>(e);
1132 if (progress && !progress->isDeterminate())
1137 HTMLInputElement* inputElement = e->toInputElement();
1138 if (inputElement && inputElement->isIndeterminate())
1142 case CSSSelector::PseudoRoot:
1143 if (e == e->document()->documentElement())
1146 case CSSSelector::PseudoLang:
1148 AtomicString value = e->computeInheritedLanguage();
1149 const AtomicString& argument = sel->argument();
1150 if (value.isEmpty() || !value.startsWith(argument, false))
1152 if (value.length() != argument.length() && value[argument.length()] != '-')
1156 #if ENABLE(FULLSCREEN_API)
1157 case CSSSelector::PseudoFullScreen:
1158 // While a Document is in the fullscreen state, and the document's current fullscreen
1159 // element is an element in the document, the 'full-screen' pseudoclass applies to
1160 // that element. Also, an <iframe>, <object> or <embed> element whose child browsing
1161 // context's Document is in the fullscreen state has the 'full-screen' pseudoclass applied.
1162 if (e->isFrameElementBase() && static_cast<HTMLFrameElementBase*>(e)->containsFullScreenElement())
1164 if (!e->document()->webkitIsFullScreen())
1166 return e == e->document()->webkitCurrentFullScreenElement();
1167 case CSSSelector::PseudoAnimatingFullScreenTransition:
1168 if (e != e->document()->webkitCurrentFullScreenElement())
1170 return e->document()->isAnimatingFullScreen();
1171 case CSSSelector::PseudoFullScreenAncestor:
1172 return e->containsFullScreenElement();
1173 case CSSSelector::PseudoFullScreenDocument:
1174 // While a Document is in the fullscreen state, the 'full-screen-document' pseudoclass applies
1175 // to all elements of that Document.
1176 if (!e->document()->webkitIsFullScreen())
1180 case CSSSelector::PseudoInRange:
1183 e->document()->setContainsValidityStyleRules();
1184 return e->isInRange();
1185 case CSSSelector::PseudoOutOfRange:
1188 e->document()->setContainsValidityStyleRules();
1189 return e->isOutOfRange();
1190 case CSSSelector::PseudoUnknown:
1191 case CSSSelector::PseudoNotParsed:
1193 ASSERT_NOT_REACHED();
1198 if (sel->m_match == CSSSelector::PseudoElement) {
1199 if (!elementStyle && !m_isCollectingRulesOnly)
1202 if (sel->isUnknownPseudoElement()) {
1203 m_hasUnknownPseudoElements = true;
1204 return e->shadowPseudoId() == sel->value();
1207 PseudoId pseudoId = CSSSelector::pseudoId(sel->pseudoType());
1208 if (pseudoId == FIRST_LETTER) {
1209 if (Document* document = e->document())
1210 document->setUsesFirstLetterRules(true);
1212 if (pseudoId != NOPSEUDO)
1213 dynamicPseudo = pseudoId;
1215 // ### add the rest of the checks...
1219 bool SelectorChecker::checkScrollbarPseudoClass(CSSSelector* sel, PseudoId&) const
1221 RenderScrollbar* scrollbar = RenderScrollbar::scrollbarForStyleResolve();
1222 ScrollbarPart part = RenderScrollbar::partForStyleResolve();
1224 // FIXME: This is a temporary hack for resizers and scrollbar corners. Eventually :window-inactive should become a real
1225 // pseudo class and just apply to everything.
1226 if (sel->pseudoType() == CSSSelector::PseudoWindowInactive)
1227 return !m_document->page()->focusController()->isActive();
1232 ASSERT(sel->m_match == CSSSelector::PseudoClass);
1233 switch (sel->pseudoType()) {
1234 case CSSSelector::PseudoEnabled:
1235 return scrollbar->enabled();
1236 case CSSSelector::PseudoDisabled:
1237 return !scrollbar->enabled();
1238 case CSSSelector::PseudoHover:
1240 ScrollbarPart hoveredPart = scrollbar->hoveredPart();
1241 if (part == ScrollbarBGPart)
1242 return hoveredPart != NoPart;
1243 if (part == TrackBGPart)
1244 return hoveredPart == BackTrackPart || hoveredPart == ForwardTrackPart || hoveredPart == ThumbPart;
1245 return part == hoveredPart;
1247 case CSSSelector::PseudoActive:
1249 ScrollbarPart pressedPart = scrollbar->pressedPart();
1250 if (part == ScrollbarBGPart)
1251 return pressedPart != NoPart;
1252 if (part == TrackBGPart)
1253 return pressedPart == BackTrackPart || pressedPart == ForwardTrackPart || pressedPart == ThumbPart;
1254 return part == pressedPart;
1256 case CSSSelector::PseudoHorizontal:
1257 return scrollbar->orientation() == HorizontalScrollbar;
1258 case CSSSelector::PseudoVertical:
1259 return scrollbar->orientation() == VerticalScrollbar;
1260 case CSSSelector::PseudoDecrement:
1261 return part == BackButtonStartPart || part == BackButtonEndPart || part == BackTrackPart;
1262 case CSSSelector::PseudoIncrement:
1263 return part == ForwardButtonStartPart || part == ForwardButtonEndPart || part == ForwardTrackPart;
1264 case CSSSelector::PseudoStart:
1265 return part == BackButtonStartPart || part == ForwardButtonStartPart || part == BackTrackPart;
1266 case CSSSelector::PseudoEnd:
1267 return part == BackButtonEndPart || part == ForwardButtonEndPart || part == ForwardTrackPart;
1268 case CSSSelector::PseudoDoubleButton:
1270 ScrollbarButtonsPlacement buttonsPlacement = scrollbar->theme()->buttonsPlacement();
1271 if (part == BackButtonStartPart || part == ForwardButtonStartPart || part == BackTrackPart)
1272 return buttonsPlacement == ScrollbarButtonsDoubleStart || buttonsPlacement == ScrollbarButtonsDoubleBoth;
1273 if (part == BackButtonEndPart || part == ForwardButtonEndPart || part == ForwardTrackPart)
1274 return buttonsPlacement == ScrollbarButtonsDoubleEnd || buttonsPlacement == ScrollbarButtonsDoubleBoth;
1277 case CSSSelector::PseudoSingleButton:
1279 ScrollbarButtonsPlacement buttonsPlacement = scrollbar->theme()->buttonsPlacement();
1280 if (part == BackButtonStartPart || part == ForwardButtonEndPart || part == BackTrackPart || part == ForwardTrackPart)
1281 return buttonsPlacement == ScrollbarButtonsSingle;
1284 case CSSSelector::PseudoNoButton:
1286 ScrollbarButtonsPlacement buttonsPlacement = scrollbar->theme()->buttonsPlacement();
1287 if (part == BackTrackPart)
1288 return buttonsPlacement == ScrollbarButtonsNone || buttonsPlacement == ScrollbarButtonsDoubleEnd;
1289 if (part == ForwardTrackPart)
1290 return buttonsPlacement == ScrollbarButtonsNone || buttonsPlacement == ScrollbarButtonsDoubleStart;
1293 case CSSSelector::PseudoCornerPresent:
1294 return scrollbar->scrollableArea()->isScrollCornerVisible();
1300 void SelectorChecker::allVisitedStateChanged()
1302 if (m_linksCheckedForVisitedState.isEmpty())
1304 for (Node* node = m_document; node; node = node->traverseNextNode()) {
1306 node->setNeedsStyleRecalc();
1310 void SelectorChecker::visitedStateChanged(LinkHash visitedHash)
1312 if (!m_linksCheckedForVisitedState.contains(visitedHash))
1314 for (Node* node = m_document; node; node = node->traverseNextNode()) {
1315 const AtomicString* attr = linkAttribute(node);
1316 if (attr && visitedLinkHash(m_document->baseURL(), *attr) == visitedHash)
1317 node->setNeedsStyleRecalc();