1 /*
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, 2012, 2013 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.
11 *
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.
16 *
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.
21 *
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.
26 */
27
28 #include "config.h"
29 #include "core/css/SelectorChecker.h"
30
31 #include "core/HTMLNames.h"
32 #include "core/css/CSSSelectorList.h"
33 #include "core/css/SiblingTraversalStrategies.h"
34 #include "core/dom/Document.h"
35 #include "core/dom/ElementTraversal.h"
36 #include "core/dom/Fullscreen.h"
37 #include "core/dom/NodeRenderStyle.h"
38 #include "core/dom/StyleEngine.h"
39 #include "core/dom/Text.h"
40 #include "core/dom/shadow/InsertionPoint.h"
41 #include "core/dom/shadow/ShadowRoot.h"
42 #include "core/editing/FrameSelection.h"
43 #include "core/frame/LocalFrame.h"
44 #include "core/html/HTMLDocument.h"
45 #include "core/html/HTMLFrameElementBase.h"
46 #include "core/html/HTMLInputElement.h"
47 #include "core/html/HTMLOptionElement.h"
48 #include "core/html/HTMLSelectElement.h"
49 #include "core/html/parser/HTMLParserIdioms.h"
50 #include "core/html/track/vtt/VTTElement.h"
51 #include "core/inspector/InspectorInstrumentation.h"
52 #include "core/page/FocusController.h"
53 #include "core/page/Page.h"
54 #include "core/rendering/RenderObject.h"
55 #include "core/rendering/RenderScrollbar.h"
56 #include "core/rendering/style/RenderStyle.h"
57 #include "platform/scroll/ScrollableArea.h"
58 #include "platform/scroll/ScrollbarTheme.h"
59
60 namespace blink {
61
62 using namespace HTMLNames;
63
SelectorChecker(Document & document,Mode mode)64 SelectorChecker::SelectorChecker(Document& document, Mode mode)
65 : m_strictParsing(!document.inQuirksMode())
66 , m_mode(mode)
67 {
68 }
69
matchesCustomPseudoElement(const Element * element,const CSSSelector & selector)70 static bool matchesCustomPseudoElement(const Element* element, const CSSSelector& selector)
71 {
72 ShadowRoot* root = element->containingShadowRoot();
73 if (!root || root->type() != ShadowRoot::UserAgentShadowRoot)
74 return false;
75
76 if (element->shadowPseudoId() != selector.value())
77 return false;
78
79 return true;
80 }
81
parentElement(const SelectorChecker::SelectorCheckingContext & context)82 static Element* parentElement(const SelectorChecker::SelectorCheckingContext& context)
83 {
84 // - If context.scope is a shadow root, we should walk up to its shadow host.
85 // - If context.scope is some element in some shadow tree and querySelector initialized the context,
86 // e.g. shadowRoot.querySelector(':host *'),
87 // (a) context.element has the same treescope as context.scope, need to walk up to its shadow host.
88 // (b) Otherwise, should not walk up from a shadow root to a shadow host.
89 if (context.scope && (context.scope == context.element->containingShadowRoot() || context.scope->treeScope() == context.element->treeScope()))
90 return context.element->parentOrShadowHostElement();
91 return context.element->parentElement();
92 }
93
scopeContainsLastMatchedElement(const SelectorChecker::SelectorCheckingContext & context)94 static bool scopeContainsLastMatchedElement(const SelectorChecker::SelectorCheckingContext& context)
95 {
96 if (!(context.contextFlags & SelectorChecker::ScopeContainsLastMatchedElement))
97 return true;
98
99 ASSERT(context.scope);
100 if (context.scope->treeScope() == context.element->treeScope())
101 return true;
102
103 // Because Blink treats a shadow host's TreeScope as a separate one from its descendent shadow roots,
104 // if the last matched element is a shadow host, the condition above isn't met, even though it
105 // should be.
106 return context.element == context.scope->shadowHost() && (!context.previousElement || context.previousElement->isInDescendantTreeOf(context.element));
107 }
108
nextSelectorExceedsScope(const SelectorChecker::SelectorCheckingContext & context)109 static inline bool nextSelectorExceedsScope(const SelectorChecker::SelectorCheckingContext& context)
110 {
111 if (context.scope && context.scope->isInShadowTree())
112 return context.element == context.scope->shadowHost();
113
114 return false;
115 }
116
117 // Recursive check of selectors and combinators
118 // It can return 4 different values:
119 // * SelectorMatches - the selector matches the element e
120 // * SelectorFailsLocally - the selector fails for the element e
121 // * SelectorFailsAllSiblings - the selector fails for e and any sibling of e
122 // * SelectorFailsCompletely - the selector fails for e and any sibling or ancestor of e
123 template<typename SiblingTraversalStrategy>
match(const SelectorCheckingContext & context,const SiblingTraversalStrategy & siblingTraversalStrategy,MatchResult * result) const124 SelectorChecker::Match SelectorChecker::match(const SelectorCheckingContext& context, const SiblingTraversalStrategy& siblingTraversalStrategy, MatchResult* result) const
125 {
126 // first selector has to match
127 unsigned specificity = 0;
128 if (!checkOne(context, siblingTraversalStrategy, &specificity))
129 return SelectorFailsLocally;
130
131 if (context.selector->match() == CSSSelector::PseudoElement) {
132 if (context.selector->isCustomPseudoElement()) {
133 if (!matchesCustomPseudoElement(context.element, *context.selector))
134 return SelectorFailsLocally;
135 } else if (context.selector->isContentPseudoElement()) {
136 if (!context.element->isInShadowTree() || !context.element->isInsertionPoint())
137 return SelectorFailsLocally;
138 } else if (context.selector->isShadowPseudoElement()) {
139 if (!context.element->isInShadowTree() || !context.previousElement)
140 return SelectorFailsCompletely;
141 } else {
142 if ((!context.elementStyle && m_mode == ResolvingStyle) || m_mode == QueryingRules)
143 return SelectorFailsLocally;
144
145 PseudoId pseudoId = CSSSelector::pseudoId(context.selector->pseudoType());
146 if (pseudoId == FIRST_LETTER)
147 context.element->document().styleEngine()->setUsesFirstLetterRules(true);
148 if (pseudoId != NOPSEUDO && m_mode != SharingRules && result)
149 result->dynamicPseudo = pseudoId;
150 }
151 }
152
153 // Prepare next selector
154 if (context.selector->isLastInTagHistory()) {
155 if (scopeContainsLastMatchedElement(context)) {
156 if (result)
157 result->specificity += specificity;
158 return SelectorMatches;
159 }
160 return SelectorFailsLocally;
161 }
162
163 Match match;
164 if (context.selector->relation() != CSSSelector::SubSelector) {
165 // Abort if the next selector would exceed the scope.
166 if (nextSelectorExceedsScope(context))
167 return SelectorFailsCompletely;
168
169 // Bail-out if this selector is irrelevant for the pseudoId
170 if (context.pseudoId != NOPSEUDO && (!result || context.pseudoId != result->dynamicPseudo))
171 return SelectorFailsCompletely;
172
173 if (result) {
174 TemporaryChange<PseudoId> dynamicPseudoScope(result->dynamicPseudo, NOPSEUDO);
175 match = matchForRelation(context, siblingTraversalStrategy, result);
176 } else {
177 return matchForRelation(context, siblingTraversalStrategy, 0);
178 }
179 } else {
180 match = matchForSubSelector(context, siblingTraversalStrategy, result);
181 }
182 if (match != SelectorMatches || !result)
183 return match;
184
185 result->specificity += specificity;
186 return SelectorMatches;
187 }
188
prepareNextContextForRelation(const SelectorChecker::SelectorCheckingContext & context)189 static inline SelectorChecker::SelectorCheckingContext prepareNextContextForRelation(const SelectorChecker::SelectorCheckingContext& context)
190 {
191 SelectorChecker::SelectorCheckingContext nextContext(context);
192 ASSERT(context.selector->tagHistory());
193 nextContext.selector = context.selector->tagHistory();
194 return nextContext;
195 }
196
isAuthorShadowRoot(const Node * node)197 static inline bool isAuthorShadowRoot(const Node* node)
198 {
199 return node && node->isShadowRoot() && toShadowRoot(node)->type() == ShadowRoot::AuthorShadowRoot;
200 }
201
202 template<typename SiblingTraversalStrategy>
matchForSubSelector(const SelectorCheckingContext & context,const SiblingTraversalStrategy & siblingTraversalStrategy,MatchResult * result) const203 SelectorChecker::Match SelectorChecker::matchForSubSelector(const SelectorCheckingContext& context, const SiblingTraversalStrategy& siblingTraversalStrategy, MatchResult* result) const
204 {
205 SelectorCheckingContext nextContext = prepareNextContextForRelation(context);
206
207 PseudoId dynamicPseudo = result ? result->dynamicPseudo : NOPSEUDO;
208 // a selector is invalid if something follows a pseudo-element
209 // We make an exception for scrollbar pseudo elements and allow a set of pseudo classes (but nothing else)
210 // to follow the pseudo elements.
211 nextContext.hasScrollbarPseudo = dynamicPseudo != NOPSEUDO && (context.scrollbar || dynamicPseudo == SCROLLBAR_CORNER || dynamicPseudo == RESIZER);
212 nextContext.hasSelectionPseudo = dynamicPseudo == SELECTION;
213 if ((context.elementStyle || m_mode == CollectingCSSRules || m_mode == CollectingStyleRules || m_mode == QueryingRules) && dynamicPseudo != NOPSEUDO
214 && !nextContext.hasSelectionPseudo
215 && !(nextContext.hasScrollbarPseudo && nextContext.selector->match() == CSSSelector::PseudoClass))
216 return SelectorFailsCompletely;
217
218 nextContext.isSubSelector = true;
219 return match(nextContext, siblingTraversalStrategy, result);
220 }
221
selectorMatchesShadowRoot(const CSSSelector * selector)222 static bool selectorMatchesShadowRoot(const CSSSelector* selector)
223 {
224 return selector && selector->isShadowPseudoElement();
225 }
226
227 template<typename SiblingTraversalStrategy>
matchForPseudoShadow(const ContainerNode * node,const SelectorCheckingContext & context,const SiblingTraversalStrategy & siblingTraversalStrategy,MatchResult * result) const228 SelectorChecker::Match SelectorChecker::matchForPseudoShadow(const ContainerNode* node, const SelectorCheckingContext& context, const SiblingTraversalStrategy& siblingTraversalStrategy, MatchResult* result) const
229 {
230 if (!isAuthorShadowRoot(node))
231 return SelectorFailsCompletely;
232 return match(context, siblingTraversalStrategy, result);
233 }
234
235 template<typename SiblingTraversalStrategy>
matchForRelation(const SelectorCheckingContext & context,const SiblingTraversalStrategy & siblingTraversalStrategy,MatchResult * result) const236 SelectorChecker::Match SelectorChecker::matchForRelation(const SelectorCheckingContext& context, const SiblingTraversalStrategy& siblingTraversalStrategy, MatchResult* result) const
237 {
238 SelectorCheckingContext nextContext = prepareNextContextForRelation(context);
239 nextContext.previousElement = context.element;
240
241 CSSSelector::Relation relation = context.selector->relation();
242
243 // Disable :visited matching when we see the first link or try to match anything else than an ancestors.
244 if (!context.isSubSelector && (context.element->isLink() || (relation != CSSSelector::Descendant && relation != CSSSelector::Child)))
245 nextContext.visitedMatchType = VisitedMatchDisabled;
246
247 nextContext.pseudoId = NOPSEUDO;
248
249 switch (relation) {
250 case CSSSelector::Descendant:
251 if (context.selector->relationIsAffectedByPseudoContent()) {
252 for (Element* element = context.element; element; element = element->parentElement()) {
253 if (matchForShadowDistributed(element, siblingTraversalStrategy, nextContext, result) == SelectorMatches)
254 return SelectorMatches;
255 }
256 return SelectorFailsCompletely;
257 }
258 nextContext.isSubSelector = false;
259 nextContext.elementStyle = 0;
260
261 if (selectorMatchesShadowRoot(nextContext.selector))
262 return matchForPseudoShadow(context.element->containingShadowRoot(), nextContext, siblingTraversalStrategy, result);
263
264 for (nextContext.element = parentElement(context); nextContext.element; nextContext.element = parentElement(nextContext)) {
265 Match match = this->match(nextContext, siblingTraversalStrategy, result);
266 if (match == SelectorMatches || match == SelectorFailsCompletely)
267 return match;
268 if (nextSelectorExceedsScope(nextContext))
269 return SelectorFailsCompletely;
270 }
271 return SelectorFailsCompletely;
272 case CSSSelector::Child:
273 {
274 if (context.selector->relationIsAffectedByPseudoContent())
275 return matchForShadowDistributed(context.element, siblingTraversalStrategy, nextContext, result);
276
277 nextContext.isSubSelector = false;
278 nextContext.elementStyle = 0;
279
280 if (selectorMatchesShadowRoot(nextContext.selector))
281 return matchForPseudoShadow(context.element->parentNode(), nextContext, siblingTraversalStrategy, result);
282
283 nextContext.element = parentElement(context);
284 if (!nextContext.element)
285 return SelectorFailsCompletely;
286 return match(nextContext, siblingTraversalStrategy, result);
287 }
288 case CSSSelector::DirectAdjacent:
289 // Shadow roots can't have sibling elements
290 if (selectorMatchesShadowRoot(nextContext.selector))
291 return SelectorFailsCompletely;
292
293 if (m_mode == ResolvingStyle) {
294 if (ContainerNode* parent = context.element->parentElementOrShadowRoot())
295 parent->setChildrenAffectedByDirectAdjacentRules();
296 }
297 nextContext.element = ElementTraversal::previousSibling(*context.element);
298 if (!nextContext.element)
299 return SelectorFailsAllSiblings;
300 nextContext.isSubSelector = false;
301 nextContext.elementStyle = 0;
302 return match(nextContext, siblingTraversalStrategy, result);
303
304 case CSSSelector::IndirectAdjacent:
305 // Shadow roots can't have sibling elements
306 if (selectorMatchesShadowRoot(nextContext.selector))
307 return SelectorFailsCompletely;
308
309 if (m_mode == ResolvingStyle) {
310 if (ContainerNode* parent = context.element->parentElementOrShadowRoot())
311 parent->setChildrenAffectedByIndirectAdjacentRules();
312 }
313 nextContext.element = ElementTraversal::previousSibling(*context.element);
314 nextContext.isSubSelector = false;
315 nextContext.elementStyle = 0;
316 for (; nextContext.element; nextContext.element = ElementTraversal::previousSibling(*nextContext.element)) {
317 Match match = this->match(nextContext, siblingTraversalStrategy, result);
318 if (match == SelectorMatches || match == SelectorFailsAllSiblings || match == SelectorFailsCompletely)
319 return match;
320 };
321 return SelectorFailsAllSiblings;
322
323 case CSSSelector::ShadowPseudo:
324 {
325 // 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.
326 if (context.scope && context.scope->shadowHost() && context.scope->shadowHost()->treeScope() == context.element->treeScope())
327 return SelectorFailsCompletely;
328
329 Element* shadowHost = context.element->shadowHost();
330 if (!shadowHost)
331 return SelectorFailsCompletely;
332 nextContext.element = shadowHost;
333 nextContext.isSubSelector = false;
334 nextContext.elementStyle = 0;
335 return this->match(nextContext, siblingTraversalStrategy, result);
336 }
337
338 case CSSSelector::ShadowDeep:
339 {
340 nextContext.isSubSelector = false;
341 nextContext.elementStyle = 0;
342 for (nextContext.element = context.element->parentOrShadowHostElement(); nextContext.element; nextContext.element = nextContext.element->parentOrShadowHostElement()) {
343 Match match = this->match(nextContext, siblingTraversalStrategy, result);
344 if (match == SelectorMatches || match == SelectorFailsCompletely)
345 return match;
346 if (nextSelectorExceedsScope(nextContext))
347 return SelectorFailsCompletely;
348 }
349 return SelectorFailsCompletely;
350 }
351
352 case CSSSelector::SubSelector:
353 ASSERT_NOT_REACHED();
354 }
355
356 ASSERT_NOT_REACHED();
357 return SelectorFailsCompletely;
358 }
359
360 template<typename SiblingTraversalStrategy>
matchForShadowDistributed(const Element * element,const SiblingTraversalStrategy & siblingTraversalStrategy,SelectorCheckingContext & nextContext,MatchResult * result) const361 SelectorChecker::Match SelectorChecker::matchForShadowDistributed(const Element* element, const SiblingTraversalStrategy& siblingTraversalStrategy, SelectorCheckingContext& nextContext, MatchResult* result) const
362 {
363 ASSERT(element);
364 WillBeHeapVector<RawPtrWillBeMember<InsertionPoint>, 8> insertionPoints;
365 collectDestinationInsertionPoints(*element, insertionPoints);
366 for (size_t i = 0; i < insertionPoints.size(); ++i) {
367 nextContext.element = insertionPoints[i];
368 if (m_mode == SharingRules)
369 nextContext.scope = insertionPoints[i]->containingShadowRoot();
370 nextContext.isSubSelector = false;
371 nextContext.elementStyle = 0;
372 if (match(nextContext, siblingTraversalStrategy, result) == SelectorMatches)
373 return SelectorMatches;
374 }
375 return SelectorFailsLocally;
376 }
377
378 template<typename CharType>
containsHTMLSpaceTemplate(const CharType * string,unsigned length)379 static inline bool containsHTMLSpaceTemplate(const CharType* string, unsigned length)
380 {
381 for (unsigned i = 0; i < length; ++i)
382 if (isHTMLSpace<CharType>(string[i]))
383 return true;
384 return false;
385 }
386
containsHTMLSpace(const AtomicString & string)387 static inline bool containsHTMLSpace(const AtomicString& string)
388 {
389 if (LIKELY(string.is8Bit()))
390 return containsHTMLSpaceTemplate<LChar>(string.characters8(), string.length());
391 return containsHTMLSpaceTemplate<UChar>(string.characters16(), string.length());
392 }
393
attributeValueMatches(const Attribute & attributeItem,CSSSelector::Match match,const AtomicString & selectorValue,bool caseSensitive)394 static bool attributeValueMatches(const Attribute& attributeItem, CSSSelector::Match match, const AtomicString& selectorValue, bool caseSensitive)
395 {
396 const AtomicString& value = attributeItem.value();
397 if (value.isNull())
398 return false;
399
400 switch (match) {
401 case CSSSelector::Exact:
402 if (caseSensitive ? selectorValue != value : !equalIgnoringCase(selectorValue, value))
403 return false;
404 break;
405 case CSSSelector::List:
406 {
407 // Ignore empty selectors or selectors containing HTML spaces
408 if (selectorValue.isEmpty() || containsHTMLSpace(selectorValue))
409 return false;
410
411 unsigned startSearchAt = 0;
412 while (true) {
413 size_t foundPos = value.find(selectorValue, startSearchAt, caseSensitive);
414 if (foundPos == kNotFound)
415 return false;
416 if (!foundPos || isHTMLSpace<UChar>(value[foundPos - 1])) {
417 unsigned endStr = foundPos + selectorValue.length();
418 if (endStr == value.length() || isHTMLSpace<UChar>(value[endStr]))
419 break; // We found a match.
420 }
421
422 // No match. Keep looking.
423 startSearchAt = foundPos + 1;
424 }
425 break;
426 }
427 case CSSSelector::Contain:
428 if (!value.contains(selectorValue, caseSensitive) || selectorValue.isEmpty())
429 return false;
430 break;
431 case CSSSelector::Begin:
432 if (!value.startsWith(selectorValue, caseSensitive) || selectorValue.isEmpty())
433 return false;
434 break;
435 case CSSSelector::End:
436 if (!value.endsWith(selectorValue, caseSensitive) || selectorValue.isEmpty())
437 return false;
438 break;
439 case CSSSelector::Hyphen:
440 if (value.length() < selectorValue.length())
441 return false;
442 if (!value.startsWith(selectorValue, caseSensitive))
443 return false;
444 // It they start the same, check for exact match or following '-':
445 if (value.length() != selectorValue.length() && value[selectorValue.length()] != '-')
446 return false;
447 break;
448 case CSSSelector::PseudoClass:
449 case CSSSelector::PseudoElement:
450 default:
451 break;
452 }
453
454 return true;
455 }
456
anyAttributeMatches(Element & element,CSSSelector::Match match,const CSSSelector & selector)457 static bool anyAttributeMatches(Element& element, CSSSelector::Match match, const CSSSelector& selector)
458 {
459 const QualifiedName& selectorAttr = selector.attribute();
460 ASSERT(selectorAttr.localName() != starAtom); // Should not be possible from the CSS grammar.
461
462 // Synchronize the attribute in case it is lazy-computed.
463 // Currently all lazy properties have a null namespace, so only pass localName().
464 element.synchronizeAttribute(selectorAttr.localName());
465
466 const AtomicString& selectorValue = selector.value();
467 bool caseInsensitive = selector.attributeMatchType() == CSSSelector::CaseInsensitive;
468
469 AttributeCollection attributes = element.attributesWithoutUpdate();
470 AttributeCollection::iterator end = attributes.end();
471 for (AttributeCollection::iterator it = attributes.begin(); it != end; ++it) {
472 const Attribute& attributeItem = *it;
473
474 if (!attributeItem.matches(selectorAttr))
475 continue;
476
477 if (attributeValueMatches(attributeItem, match, selectorValue, !caseInsensitive))
478 return true;
479
480 if (caseInsensitive)
481 continue;
482
483 // Legacy dictates that values of some attributes should be compared in
484 // a case-insensitive manner regardless of whether the case insensitive
485 // flag is set or not.
486 bool legacyCaseInsensitive = element.document().isHTMLDocument() && !HTMLDocument::isCaseSensitiveAttribute(selectorAttr);
487
488 // If case-insensitive, re-check, and count if result differs.
489 // See http://code.google.com/p/chromium/issues/detail?id=327060
490 if (legacyCaseInsensitive && attributeValueMatches(attributeItem, match, selectorValue, false)) {
491 UseCounter::count(element.document(), UseCounter::CaseInsensitiveAttrSelectorMatch);
492 return true;
493 }
494 }
495
496 return false;
497 }
498
499 template<typename SiblingTraversalStrategy>
checkOne(const SelectorCheckingContext & context,const SiblingTraversalStrategy & siblingTraversalStrategy,unsigned * specificity) const500 bool SelectorChecker::checkOne(const SelectorCheckingContext& context, const SiblingTraversalStrategy& siblingTraversalStrategy, unsigned* specificity) const
501 {
502 ASSERT(context.element);
503 Element& element = *context.element;
504 ASSERT(context.selector);
505 const CSSSelector& selector = *context.selector;
506
507 bool elementIsHostInItsShadowTree = isHostInItsShadowTree(element, context.scope);
508
509 // Only :host and :host-context() should match the host: http://drafts.csswg.org/css-scoping/#host-element
510 if (elementIsHostInItsShadowTree && (!selector.isHostPseudoClass()
511 && !(context.contextFlags & TreatShadowHostAsNormalScope)
512 && selector.match() != CSSSelector::PseudoElement))
513 return false;
514
515 if (selector.match() == CSSSelector::Tag)
516 return SelectorChecker::tagMatches(element, selector.tagQName());
517
518 if (selector.match() == CSSSelector::Class)
519 return element.hasClass() && element.classNames().contains(selector.value());
520
521 if (selector.match() == CSSSelector::Id)
522 return element.hasID() && element.idForStyleResolution() == selector.value();
523
524 if (selector.isAttributeSelector())
525 return anyAttributeMatches(element, selector.match(), selector);
526
527 if (selector.match() == CSSSelector::PseudoClass) {
528 // Handle :not up front.
529 if (selector.pseudoType() == CSSSelector::PseudoNot) {
530 SelectorCheckingContext subContext(context);
531 subContext.isSubSelector = true;
532 ASSERT(selector.selectorList());
533 for (subContext.selector = selector.selectorList()->first(); subContext.selector; subContext.selector = subContext.selector->tagHistory()) {
534 // :not cannot nest. I don't really know why this is a
535 // restriction in CSS3, but it is, so let's honor it.
536 // the parser enforces that this never occurs
537 ASSERT(subContext.selector->pseudoType() != CSSSelector::PseudoNot);
538 // We select between :visited and :link when applying. We don't know which one applied (or not) yet.
539 if (subContext.selector->pseudoType() == CSSSelector::PseudoVisited || (subContext.selector->pseudoType() == CSSSelector::PseudoLink && subContext.visitedMatchType == VisitedMatchEnabled))
540 return true;
541 // context.scope is not available if m_mode == SharingRules.
542 // We cannot determine whether :host or :scope matches a given element or not.
543 if (m_mode == SharingRules && (subContext.selector->isHostPseudoClass() || subContext.selector->pseudoType() == CSSSelector::PseudoScope))
544 return true;
545 if (!checkOne(subContext, DOMSiblingTraversalStrategy()))
546 return true;
547 }
548 } else if (context.hasScrollbarPseudo) {
549 // CSS scrollbars match a specific subset of pseudo classes, and they have specialized rules for each
550 // (since there are no elements involved).
551 return checkScrollbarPseudoClass(context, &element.document(), selector);
552 } else if (context.hasSelectionPseudo) {
553 if (selector.pseudoType() == CSSSelector::PseudoWindowInactive)
554 return !element.document().page()->focusController().isActive();
555 }
556
557 // Normal element pseudo class checking.
558 switch (selector.pseudoType()) {
559 // Pseudo classes:
560 case CSSSelector::PseudoNot:
561 break; // Already handled up above.
562 case CSSSelector::PseudoEmpty:
563 {
564 bool result = true;
565 for (Node* n = element.firstChild(); n; n = n->nextSibling()) {
566 if (n->isElementNode()) {
567 result = false;
568 break;
569 }
570 if (n->isTextNode()) {
571 Text* textNode = toText(n);
572 if (!textNode->data().isEmpty()) {
573 result = false;
574 break;
575 }
576 }
577 }
578 if (m_mode == ResolvingStyle) {
579 element.setStyleAffectedByEmpty();
580 if (context.elementStyle)
581 context.elementStyle->setEmptyState(result);
582 else if (element.renderStyle() && (element.document().styleEngine()->usesSiblingRules() || element.renderStyle()->unique()))
583 element.renderStyle()->setEmptyState(result);
584 }
585 return result;
586 }
587 case CSSSelector::PseudoFirstChild:
588 // first-child matches the first child that is an element
589 if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
590 bool result = siblingTraversalStrategy.isFirstChild(element);
591 if (m_mode == ResolvingStyle) {
592 RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element.renderStyle();
593 parent->setChildrenAffectedByFirstChildRules();
594 if (result && childStyle)
595 childStyle->setFirstChildState();
596 }
597 return result;
598 }
599 break;
600 case CSSSelector::PseudoFirstOfType:
601 // first-of-type matches the first element of its type
602 if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
603 bool result = siblingTraversalStrategy.isFirstOfType(element, element.tagQName());
604 if (m_mode == ResolvingStyle)
605 parent->setChildrenAffectedByForwardPositionalRules();
606 return result;
607 }
608 break;
609 case CSSSelector::PseudoLastChild:
610 // last-child matches the last child that is an element
611 if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
612 bool result = parent->isFinishedParsingChildren() && siblingTraversalStrategy.isLastChild(element);
613 if (m_mode == ResolvingStyle) {
614 RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element.renderStyle();
615 parent->setChildrenAffectedByLastChildRules();
616 if (result && childStyle)
617 childStyle->setLastChildState();
618 }
619 return result;
620 }
621 break;
622 case CSSSelector::PseudoLastOfType:
623 // last-of-type matches the last element of its type
624 if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
625 if (m_mode == ResolvingStyle)
626 parent->setChildrenAffectedByBackwardPositionalRules();
627 if (!parent->isFinishedParsingChildren())
628 return false;
629 return siblingTraversalStrategy.isLastOfType(element, element.tagQName());
630 }
631 break;
632 case CSSSelector::PseudoOnlyChild:
633 if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
634 bool firstChild = siblingTraversalStrategy.isFirstChild(element);
635 bool onlyChild = firstChild && parent->isFinishedParsingChildren() && siblingTraversalStrategy.isLastChild(element);
636 if (m_mode == ResolvingStyle) {
637 RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element.renderStyle();
638 parent->setChildrenAffectedByFirstChildRules();
639 parent->setChildrenAffectedByLastChildRules();
640 if (firstChild && childStyle)
641 childStyle->setFirstChildState();
642 if (onlyChild && childStyle)
643 childStyle->setLastChildState();
644 }
645 return onlyChild;
646 }
647 break;
648 case CSSSelector::PseudoOnlyOfType:
649 // FIXME: This selector is very slow.
650 if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
651 if (m_mode == ResolvingStyle) {
652 parent->setChildrenAffectedByForwardPositionalRules();
653 parent->setChildrenAffectedByBackwardPositionalRules();
654 }
655 if (!parent->isFinishedParsingChildren())
656 return false;
657 return siblingTraversalStrategy.isFirstOfType(element, element.tagQName()) && siblingTraversalStrategy.isLastOfType(element, element.tagQName());
658 }
659 break;
660 case CSSSelector::PseudoNthChild:
661 if (!selector.parseNth())
662 break;
663 if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
664 int count = 1 + siblingTraversalStrategy.countElementsBefore(element);
665 if (m_mode == ResolvingStyle) {
666 RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element.renderStyle();
667 if (childStyle)
668 childStyle->setUnique();
669 parent->setChildrenAffectedByForwardPositionalRules();
670 }
671
672 if (selector.matchNth(count))
673 return true;
674 }
675 break;
676 case CSSSelector::PseudoNthOfType:
677 if (!selector.parseNth())
678 break;
679 if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
680 int count = 1 + siblingTraversalStrategy.countElementsOfTypeBefore(element, element.tagQName());
681 if (m_mode == ResolvingStyle)
682 parent->setChildrenAffectedByForwardPositionalRules();
683
684 if (selector.matchNth(count))
685 return true;
686 }
687 break;
688 case CSSSelector::PseudoNthLastChild:
689 if (!selector.parseNth())
690 break;
691 if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
692 if (m_mode == ResolvingStyle)
693 parent->setChildrenAffectedByBackwardPositionalRules();
694 if (!parent->isFinishedParsingChildren())
695 return false;
696 int count = 1 + siblingTraversalStrategy.countElementsAfter(element);
697 if (selector.matchNth(count))
698 return true;
699 }
700 break;
701 case CSSSelector::PseudoNthLastOfType:
702 if (!selector.parseNth())
703 break;
704 if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
705 if (m_mode == ResolvingStyle)
706 parent->setChildrenAffectedByBackwardPositionalRules();
707 if (!parent->isFinishedParsingChildren())
708 return false;
709
710 int count = 1 + siblingTraversalStrategy.countElementsOfTypeAfter(element, element.tagQName());
711 if (selector.matchNth(count))
712 return true;
713 }
714 break;
715 case CSSSelector::PseudoTarget:
716 if (element == element.document().cssTarget())
717 return true;
718 break;
719 case CSSSelector::PseudoAny:
720 {
721 SelectorCheckingContext subContext(context);
722 subContext.isSubSelector = true;
723 ASSERT(selector.selectorList());
724 for (subContext.selector = selector.selectorList()->first(); subContext.selector; subContext.selector = CSSSelectorList::next(*subContext.selector)) {
725 if (match(subContext, siblingTraversalStrategy) == SelectorMatches)
726 return true;
727 }
728 }
729 break;
730 case CSSSelector::PseudoAutofill:
731 if (!element.isFormControlElement())
732 break;
733 return toHTMLFormControlElement(element).isAutofilled();
734 case CSSSelector::PseudoAnyLink:
735 case CSSSelector::PseudoLink:
736 // :visited and :link matches are separated later when applying the style. Here both classes match all links...
737 return element.isLink();
738 case CSSSelector::PseudoVisited:
739 // ...except if :visited matching is disabled for ancestor/sibling matching.
740 return element.isLink() && context.visitedMatchType == VisitedMatchEnabled;
741 case CSSSelector::PseudoDrag:
742 if (m_mode == ResolvingStyle) {
743 if (context.elementStyle)
744 context.elementStyle->setAffectedByDrag();
745 else
746 element.setChildrenOrSiblingsAffectedByDrag();
747 }
748 if (element.renderer() && element.renderer()->isDragging())
749 return true;
750 break;
751 case CSSSelector::PseudoFocus:
752 if (m_mode == ResolvingStyle) {
753 if (context.elementStyle)
754 context.elementStyle->setAffectedByFocus();
755 else
756 element.setChildrenOrSiblingsAffectedByFocus();
757 }
758 return matchesFocusPseudoClass(element);
759 case CSSSelector::PseudoHover:
760 // If we're in quirks mode, then hover should never match anchors with no
761 // href and *:hover should not match anything. This is important for sites like wsj.com.
762 if (m_strictParsing || context.isSubSelector || element.isLink()) {
763 if (m_mode == ResolvingStyle) {
764 if (context.elementStyle)
765 context.elementStyle->setAffectedByHover();
766 else
767 element.setChildrenOrSiblingsAffectedByHover();
768 }
769 if (element.hovered() || InspectorInstrumentation::forcePseudoState(&element, CSSSelector::PseudoHover))
770 return true;
771 }
772 break;
773 case CSSSelector::PseudoActive:
774 // If we're in quirks mode, then :active should never match anchors with no
775 // href and *:active should not match anything.
776 if (m_strictParsing || context.isSubSelector || element.isLink()) {
777 if (m_mode == ResolvingStyle) {
778 if (context.elementStyle)
779 context.elementStyle->setAffectedByActive();
780 else
781 element.setChildrenOrSiblingsAffectedByActive();
782 }
783 if (element.active() || InspectorInstrumentation::forcePseudoState(&element, CSSSelector::PseudoActive))
784 return true;
785 }
786 break;
787 case CSSSelector::PseudoEnabled:
788 if (element.isFormControlElement() || isHTMLOptionElement(element) || isHTMLOptGroupElement(element))
789 return !element.isDisabledFormControl();
790 else if (isHTMLAnchorElement(element) || isHTMLAreaElement(element))
791 return element.isLink();
792 break;
793 case CSSSelector::PseudoFullPageMedia:
794 return element.document().isMediaDocument();
795 break;
796 case CSSSelector::PseudoDefault:
797 return element.isDefaultButtonForForm();
798 case CSSSelector::PseudoDisabled:
799 if (element.isFormControlElement() || isHTMLOptionElement(element) || isHTMLOptGroupElement(element))
800 return element.isDisabledFormControl();
801 break;
802 case CSSSelector::PseudoReadOnly:
803 return element.matchesReadOnlyPseudoClass();
804 case CSSSelector::PseudoReadWrite:
805 return element.matchesReadWritePseudoClass();
806 case CSSSelector::PseudoOptional:
807 return element.isOptionalFormControl();
808 case CSSSelector::PseudoRequired:
809 return element.isRequiredFormControl();
810 case CSSSelector::PseudoValid:
811 element.document().setContainsValidityStyleRules();
812 return element.willValidate() && element.isValidFormControlElement();
813 case CSSSelector::PseudoInvalid:
814 element.document().setContainsValidityStyleRules();
815 return element.willValidate() && !element.isValidFormControlElement();
816 case CSSSelector::PseudoChecked:
817 {
818 if (isHTMLInputElement(element)) {
819 HTMLInputElement& inputElement = toHTMLInputElement(element);
820 // Even though WinIE allows checked and indeterminate to
821 // co-exist, the CSS selector spec says that you can't be
822 // both checked and indeterminate. We will behave like WinIE
823 // behind the scenes and just obey the CSS spec here in the
824 // test for matching the pseudo.
825 if (inputElement.shouldAppearChecked() && !inputElement.shouldAppearIndeterminate())
826 return true;
827 } else if (isHTMLOptionElement(element) && toHTMLOptionElement(element).selected())
828 return true;
829 break;
830 }
831 case CSSSelector::PseudoIndeterminate:
832 return element.shouldAppearIndeterminate();
833 case CSSSelector::PseudoRoot:
834 if (element == element.document().documentElement())
835 return true;
836 break;
837 case CSSSelector::PseudoLang:
838 {
839 AtomicString value;
840 if (element.isVTTElement())
841 value = toVTTElement(element).language();
842 else
843 value = element.computeInheritedLanguage();
844 const AtomicString& argument = selector.argument();
845 if (value.isEmpty() || !value.startsWith(argument, false))
846 break;
847 if (value.length() != argument.length() && value[argument.length()] != '-')
848 break;
849 return true;
850 }
851 case CSSSelector::PseudoFullScreen:
852 // While a Document is in the fullscreen state, and the document's current fullscreen
853 // element is an element in the document, the 'full-screen' pseudoclass applies to
854 // that element. Also, an <iframe>, <object> or <embed> element whose child browsing
855 // context's Document is in the fullscreen state has the 'full-screen' pseudoclass applied.
856 if (isHTMLFrameElementBase(element) && element.containsFullScreenElement())
857 return true;
858 return Fullscreen::isActiveFullScreenElement(element);
859 case CSSSelector::PseudoFullScreenAncestor:
860 return element.containsFullScreenElement();
861 case CSSSelector::PseudoFullScreenDocument:
862 // While a Document is in the fullscreen state, the 'full-screen-document' pseudoclass applies
863 // to all elements of that Document.
864 if (!Fullscreen::isFullScreen(element.document()))
865 return false;
866 return true;
867 case CSSSelector::PseudoInRange:
868 element.document().setContainsValidityStyleRules();
869 return element.isInRange();
870 case CSSSelector::PseudoOutOfRange:
871 element.document().setContainsValidityStyleRules();
872 return element.isOutOfRange();
873 case CSSSelector::PseudoFutureCue:
874 return (element.isVTTElement() && !toVTTElement(element).isPastNode());
875 case CSSSelector::PseudoPastCue:
876 return (element.isVTTElement() && toVTTElement(element).isPastNode());
877
878 case CSSSelector::PseudoScope:
879 {
880 if (m_mode == SharingRules)
881 return true;
882 const Node* contextualReferenceNode = !context.scope ? element.document().documentElement() : context.scope;
883 if (element == contextualReferenceNode)
884 return true;
885 break;
886 }
887
888 case CSSSelector::PseudoUnresolved:
889 if (element.isUnresolvedCustomElement())
890 return true;
891 break;
892
893 case CSSSelector::PseudoHost:
894 case CSSSelector::PseudoHostContext:
895 {
896 if (m_mode == SharingRules)
897 return true;
898 // :host only matches a shadow host when :host is in a shadow tree of the shadow host.
899 if (!context.scope)
900 return false;
901 const ContainerNode* shadowHost = context.scope->shadowHost();
902 if (!shadowHost || shadowHost != element)
903 return false;
904 ASSERT(element.shadow());
905
906 // For empty parameter case, i.e. just :host or :host().
907 if (!selector.selectorList()) // Use *'s specificity. So just 0.
908 return true;
909
910 SelectorCheckingContext subContext(context);
911 subContext.isSubSelector = true;
912
913 bool matched = false;
914 unsigned maxSpecificity = 0;
915
916 // If one of simple selectors matches an element, returns SelectorMatches. Just "OR".
917 for (subContext.selector = selector.selectorList()->first(); subContext.selector; subContext.selector = CSSSelectorList::next(*subContext.selector)) {
918 subContext.contextFlags = TreatShadowHostAsNormalScope;
919 subContext.scope = context.scope;
920 // Use NodeRenderingTraversal to traverse a composed ancestor list of a given element.
921 Element* nextElement = &element;
922 SelectorCheckingContext hostContext(subContext);
923 do {
924 MatchResult subResult;
925 hostContext.element = nextElement;
926 if (match(hostContext, siblingTraversalStrategy, &subResult) == SelectorMatches) {
927 matched = true;
928 // Consider div:host(div:host(div:host(div:host...))).
929 maxSpecificity = std::max(maxSpecificity, hostContext.selector->specificity() + subResult.specificity);
930 break;
931 }
932 hostContext.contextFlags = DefaultBehavior;
933 hostContext.scope = nullptr;
934
935 if (selector.pseudoType() == CSSSelector::PseudoHost)
936 break;
937
938 hostContext.elementStyle = 0;
939 nextElement = NodeRenderingTraversal::parentElement(nextElement);
940 } while (nextElement);
941 }
942 if (matched) {
943 if (specificity)
944 *specificity = maxSpecificity;
945 return true;
946 }
947 }
948 break;
949 case CSSSelector::PseudoSpatialNavigationFocus:
950 return context.isUARule && matchesSpatialNavigationFocusPseudoClass(element);
951 case CSSSelector::PseudoListBox:
952 return context.isUARule && matchesListBoxPseudoClass(element);
953
954 case CSSSelector::PseudoHorizontal:
955 case CSSSelector::PseudoVertical:
956 case CSSSelector::PseudoDecrement:
957 case CSSSelector::PseudoIncrement:
958 case CSSSelector::PseudoStart:
959 case CSSSelector::PseudoEnd:
960 case CSSSelector::PseudoDoubleButton:
961 case CSSSelector::PseudoSingleButton:
962 case CSSSelector::PseudoNoButton:
963 case CSSSelector::PseudoCornerPresent:
964 return false;
965
966 case CSSSelector::PseudoUnknown:
967 case CSSSelector::PseudoNotParsed:
968 default:
969 ASSERT_NOT_REACHED();
970 break;
971 }
972 return false;
973 } else if (selector.match() == CSSSelector::PseudoElement && selector.pseudoType() == CSSSelector::PseudoCue) {
974 SelectorCheckingContext subContext(context);
975 subContext.isSubSelector = true;
976 subContext.contextFlags = DefaultBehavior;
977
978 const CSSSelector* contextSelector = context.selector;
979 ASSERT(contextSelector);
980 for (subContext.selector = contextSelector->selectorList()->first(); subContext.selector; subContext.selector = CSSSelectorList::next(*subContext.selector)) {
981 if (match(subContext, siblingTraversalStrategy) == SelectorMatches)
982 return true;
983 }
984 return false;
985 }
986 // ### add the rest of the checks...
987 return true;
988 }
989
checkScrollbarPseudoClass(const SelectorCheckingContext & context,Document * document,const CSSSelector & selector) const990 bool SelectorChecker::checkScrollbarPseudoClass(const SelectorCheckingContext& context, Document* document, const CSSSelector& selector) const
991 {
992 RenderScrollbar* scrollbar = context.scrollbar;
993 ScrollbarPart part = context.scrollbarPart;
994
995 // FIXME: This is a temporary hack for resizers and scrollbar corners. Eventually :window-inactive should become a real
996 // pseudo class and just apply to everything.
997 if (selector.pseudoType() == CSSSelector::PseudoWindowInactive)
998 return !document->page()->focusController().isActive();
999
1000 if (!scrollbar)
1001 return false;
1002
1003 ASSERT(selector.match() == CSSSelector::PseudoClass);
1004 switch (selector.pseudoType()) {
1005 case CSSSelector::PseudoEnabled:
1006 return scrollbar->enabled();
1007 case CSSSelector::PseudoDisabled:
1008 return !scrollbar->enabled();
1009 case CSSSelector::PseudoHover:
1010 {
1011 ScrollbarPart hoveredPart = scrollbar->hoveredPart();
1012 if (part == ScrollbarBGPart)
1013 return hoveredPart != NoPart;
1014 if (part == TrackBGPart)
1015 return hoveredPart == BackTrackPart || hoveredPart == ForwardTrackPart || hoveredPart == ThumbPart;
1016 return part == hoveredPart;
1017 }
1018 case CSSSelector::PseudoActive:
1019 {
1020 ScrollbarPart pressedPart = scrollbar->pressedPart();
1021 if (part == ScrollbarBGPart)
1022 return pressedPart != NoPart;
1023 if (part == TrackBGPart)
1024 return pressedPart == BackTrackPart || pressedPart == ForwardTrackPart || pressedPart == ThumbPart;
1025 return part == pressedPart;
1026 }
1027 case CSSSelector::PseudoHorizontal:
1028 return scrollbar->orientation() == HorizontalScrollbar;
1029 case CSSSelector::PseudoVertical:
1030 return scrollbar->orientation() == VerticalScrollbar;
1031 case CSSSelector::PseudoDecrement:
1032 return part == BackButtonStartPart || part == BackButtonEndPart || part == BackTrackPart;
1033 case CSSSelector::PseudoIncrement:
1034 return part == ForwardButtonStartPart || part == ForwardButtonEndPart || part == ForwardTrackPart;
1035 case CSSSelector::PseudoStart:
1036 return part == BackButtonStartPart || part == ForwardButtonStartPart || part == BackTrackPart;
1037 case CSSSelector::PseudoEnd:
1038 return part == BackButtonEndPart || part == ForwardButtonEndPart || part == ForwardTrackPart;
1039 case CSSSelector::PseudoDoubleButton:
1040 {
1041 ScrollbarButtonsPlacement buttonsPlacement = scrollbar->theme()->buttonsPlacement();
1042 if (part == BackButtonStartPart || part == ForwardButtonStartPart || part == BackTrackPart)
1043 return buttonsPlacement == ScrollbarButtonsDoubleStart || buttonsPlacement == ScrollbarButtonsDoubleBoth;
1044 if (part == BackButtonEndPart || part == ForwardButtonEndPart || part == ForwardTrackPart)
1045 return buttonsPlacement == ScrollbarButtonsDoubleEnd || buttonsPlacement == ScrollbarButtonsDoubleBoth;
1046 return false;
1047 }
1048 case CSSSelector::PseudoSingleButton:
1049 {
1050 ScrollbarButtonsPlacement buttonsPlacement = scrollbar->theme()->buttonsPlacement();
1051 if (part == BackButtonStartPart || part == ForwardButtonEndPart || part == BackTrackPart || part == ForwardTrackPart)
1052 return buttonsPlacement == ScrollbarButtonsSingle;
1053 return false;
1054 }
1055 case CSSSelector::PseudoNoButton:
1056 {
1057 ScrollbarButtonsPlacement buttonsPlacement = scrollbar->theme()->buttonsPlacement();
1058 if (part == BackTrackPart)
1059 return buttonsPlacement == ScrollbarButtonsNone || buttonsPlacement == ScrollbarButtonsDoubleEnd;
1060 if (part == ForwardTrackPart)
1061 return buttonsPlacement == ScrollbarButtonsNone || buttonsPlacement == ScrollbarButtonsDoubleStart;
1062 return false;
1063 }
1064 case CSSSelector::PseudoCornerPresent:
1065 return scrollbar->scrollableArea()->isScrollCornerVisible();
1066 default:
1067 return false;
1068 }
1069 }
1070
determineLinkMatchType(const CSSSelector & selector)1071 unsigned SelectorChecker::determineLinkMatchType(const CSSSelector& selector)
1072 {
1073 unsigned linkMatchType = MatchAll;
1074
1075 // Statically determine if this selector will match a link in visited, unvisited or any state, or never.
1076 // :visited never matches other elements than the innermost link element.
1077 for (const CSSSelector* current = &selector; current; current = current->tagHistory()) {
1078 switch (current->pseudoType()) {
1079 case CSSSelector::PseudoNot:
1080 {
1081 // :not(:visited) is equivalent to :link. Parser enforces that :not can't nest.
1082 ASSERT(current->selectorList());
1083 for (const CSSSelector* subSelector = current->selectorList()->first(); subSelector; subSelector = subSelector->tagHistory()) {
1084 CSSSelector::PseudoType subType = subSelector->pseudoType();
1085 if (subType == CSSSelector::PseudoVisited)
1086 linkMatchType &= ~SelectorChecker::MatchVisited;
1087 else if (subType == CSSSelector::PseudoLink)
1088 linkMatchType &= ~SelectorChecker::MatchLink;
1089 }
1090 }
1091 break;
1092 case CSSSelector::PseudoLink:
1093 linkMatchType &= ~SelectorChecker::MatchVisited;
1094 break;
1095 case CSSSelector::PseudoVisited:
1096 linkMatchType &= ~SelectorChecker::MatchLink;
1097 break;
1098 default:
1099 // We don't support :link and :visited inside :-webkit-any.
1100 break;
1101 }
1102 CSSSelector::Relation relation = current->relation();
1103 if (relation == CSSSelector::SubSelector)
1104 continue;
1105 if (relation != CSSSelector::Descendant && relation != CSSSelector::Child)
1106 return linkMatchType;
1107 if (linkMatchType != MatchAll)
1108 return linkMatchType;
1109 }
1110 return linkMatchType;
1111 }
1112
isFrameFocused(const Element & element)1113 bool SelectorChecker::isFrameFocused(const Element& element)
1114 {
1115 return element.document().frame() && element.document().frame()->selection().isFocusedAndActive();
1116 }
1117
matchesFocusPseudoClass(const Element & element)1118 bool SelectorChecker::matchesFocusPseudoClass(const Element& element)
1119 {
1120 if (InspectorInstrumentation::forcePseudoState(const_cast<Element*>(&element), CSSSelector::PseudoFocus))
1121 return true;
1122 return element.focused() && isFrameFocused(element);
1123 }
1124
matchesSpatialNavigationFocusPseudoClass(const Element & element)1125 bool SelectorChecker::matchesSpatialNavigationFocusPseudoClass(const Element& element)
1126 {
1127 return isHTMLOptionElement(element) && toHTMLOptionElement(element).spatialNavigationFocused() && isFrameFocused(element);
1128 }
1129
matchesListBoxPseudoClass(const Element & element)1130 bool SelectorChecker::matchesListBoxPseudoClass(const Element& element)
1131 {
1132 return isHTMLSelectElement(element) && !toHTMLSelectElement(element).usesMenuList();
1133 }
1134
1135 template
1136 SelectorChecker::Match SelectorChecker::match(const SelectorCheckingContext&, const DOMSiblingTraversalStrategy&, MatchResult*) const;
1137
1138 template
1139 SelectorChecker::Match SelectorChecker::match(const SelectorCheckingContext&, const ShadowDOMSiblingTraversalStrategy&, MatchResult*) const;
1140
1141 }
1142