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-2016 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 * Copyright (C) 2014 Yusuke Suzuki <utatane.tea@gmail.com>
12 *
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Library General Public
15 * License as published by the Free Software Foundation; either
16 * version 2 of the License, or (at your option) any later version.
17 *
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Library General Public License for more details.
22 *
23 * You should have received a copy of the GNU Library General Public License
24 * along with this library; see the file COPYING.LIB. If not, write to
25 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
26 * Boston, MA 02110-1301, USA.
27 */
28
29#include "config.h"
30#include "SelectorChecker.h"
31
32#include "CSSSelector.h"
33#include "CSSSelectorList.h"
34#include "Document.h"
35#include "ElementTraversal.h"
36#include "Frame.h"
37#include "FrameSelection.h"
38#include "HTMLDocument.h"
39#include "HTMLNames.h"
40#include "HTMLParserIdioms.h"
41#include "HTMLSlotElement.h"
42#include "InspectorInstrumentation.h"
43#include "Page.h"
44#include "RenderElement.h"
45#include "SelectorCheckerTestFunctions.h"
46#include "ShadowRoot.h"
47#include "Text.h"
48
49namespace WebCore {
50
51using namespace HTMLNames;
52
53enum class VisitedMatchType : unsigned char {
54 Disabled, Enabled
55};
56
57struct SelectorChecker::LocalContext {
58 LocalContext(const CSSSelector& selector, const Element& element, VisitedMatchType visitedMatchType, PseudoId pseudoId)
59 : selector(&selector)
60 , element(&element)
61 , visitedMatchType(visitedMatchType)
62 , firstSelectorOfTheFragment(&selector)
63 , pseudoId(pseudoId)
64 { }
65
66 const CSSSelector* selector;
67 const Element* element;
68 VisitedMatchType visitedMatchType;
69 const CSSSelector* firstSelectorOfTheFragment;
70 PseudoId pseudoId;
71 bool isMatchElement { true };
72 bool isSubjectOrAdjacentElement { true };
73 bool inFunctionalPseudoClass { false };
74 bool pseudoElementEffective { true };
75 bool hasScrollbarPseudo { false };
76 bool hasSelectionPseudo { false };
77 bool mayMatchHostPseudoClass { false };
78
79};
80
81static inline void addStyleRelation(SelectorChecker::CheckingContext& checkingContext, const Element& element, Style::Relation::Type type, unsigned value = 1)
82{
83 ASSERT(value == 1 || type == Style::Relation::NthChildIndex || type == Style::Relation::AffectedByEmpty);
84 if (checkingContext.resolvingMode != SelectorChecker::Mode::ResolvingStyle)
85 return;
86 if (type == Style::Relation::AffectsNextSibling && !checkingContext.styleRelations.isEmpty()) {
87 auto& last = checkingContext.styleRelations.last();
88 if (last.type == Style::Relation::AffectsNextSibling && last.element == element.nextElementSibling()) {
89 ++last.value;
90 last.element = &element;
91 return;
92 }
93 }
94 checkingContext.styleRelations.append({ element, type, value });
95}
96
97static inline bool isFirstChildElement(const Element& element)
98{
99 return !ElementTraversal::previousSibling(element);
100}
101
102static inline bool isLastChildElement(const Element& element)
103{
104 return !ElementTraversal::nextSibling(element);
105}
106
107static inline bool isFirstOfType(const Element& element, const QualifiedName& type)
108{
109 for (const Element* sibling = ElementTraversal::previousSibling(element); sibling; sibling = ElementTraversal::previousSibling(*sibling)) {
110 if (sibling->hasTagName(type))
111 return false;
112 }
113 return true;
114}
115
116static inline bool isLastOfType(const Element& element, const QualifiedName& type)
117{
118 for (const Element* sibling = ElementTraversal::nextSibling(element); sibling; sibling = ElementTraversal::nextSibling(*sibling)) {
119 if (sibling->hasTagName(type))
120 return false;
121 }
122 return true;
123}
124
125static inline int countElementsBefore(const Element& element)
126{
127 int count = 0;
128 for (const Element* sibling = ElementTraversal::previousSibling(element); sibling; sibling = ElementTraversal::previousSibling(*sibling)) {
129 unsigned index = sibling->childIndex();
130 if (index) {
131 count += index;
132 break;
133 }
134 count++;
135 }
136 return count;
137}
138
139static inline int countElementsOfTypeBefore(const Element& element, const QualifiedName& type)
140{
141 int count = 0;
142 for (const Element* sibling = ElementTraversal::previousSibling(element); sibling; sibling = ElementTraversal::previousSibling(*sibling)) {
143 if (sibling->hasTagName(type))
144 ++count;
145 }
146 return count;
147}
148
149static inline int countElementsAfter(const Element& element)
150{
151 int count = 0;
152 for (const Element* sibling = ElementTraversal::nextSibling(element); sibling; sibling = ElementTraversal::nextSibling(*sibling))
153 ++count;
154 return count;
155}
156
157static inline int countElementsOfTypeAfter(const Element& element, const QualifiedName& type)
158{
159 int count = 0;
160 for (const Element* sibling = ElementTraversal::nextSibling(element); sibling; sibling = ElementTraversal::nextSibling(*sibling)) {
161 if (sibling->hasTagName(type))
162 ++count;
163 }
164 return count;
165}
166
167SelectorChecker::SelectorChecker(Document& document)
168 : m_strictParsing(!document.inQuirksMode())
169 , m_documentIsHTML(document.isHTMLDocument())
170{
171}
172
173bool SelectorChecker::match(const CSSSelector& selector, const Element& element, CheckingContext& checkingContext, unsigned& specificity) const
174{
175 specificity = 0;
176
177 LocalContext context(selector, element, checkingContext.resolvingMode == SelectorChecker::Mode::QueryingRules ? VisitedMatchType::Disabled : VisitedMatchType::Enabled, checkingContext.pseudoId);
178
179 if (checkingContext.isMatchingHostPseudoClass) {
180 ASSERT(element.shadowRoot());
181 context.mayMatchHostPseudoClass = true;
182 }
183
184 PseudoIdSet pseudoIdSet;
185 MatchResult result = matchRecursively(checkingContext, context, pseudoIdSet, specificity);
186 if (result.match != Match::SelectorMatches)
187 return false;
188 if (checkingContext.pseudoId != PseudoId::None && !pseudoIdSet.has(checkingContext.pseudoId))
189 return false;
190
191 if (checkingContext.pseudoId == PseudoId::None && pseudoIdSet) {
192 PseudoIdSet publicPseudoIdSet = pseudoIdSet & PseudoIdSet::fromMask(static_cast<unsigned>(PseudoId::PublicPseudoIdMask));
193 if (checkingContext.resolvingMode == Mode::ResolvingStyle && publicPseudoIdSet)
194 checkingContext.pseudoIDSet = publicPseudoIdSet;
195
196 // When ignoring virtual pseudo elements, the context's pseudo should also be PseudoId::None but that does
197 // not cause a failure.
198 return checkingContext.resolvingMode == Mode::CollectingRulesIgnoringVirtualPseudoElements || result.matchType == MatchType::Element;
199 }
200 return true;
201}
202
203bool SelectorChecker::matchHostPseudoClass(const CSSSelector& selector, const Element& element, CheckingContext& checkingContext, unsigned& specificity) const
204{
205 ASSERT(element.shadowRoot());
206 ASSERT(selector.match() == CSSSelector::PseudoClass && selector.pseudoClassType() == CSSSelector::PseudoClassHost);
207
208 specificity = selector.simpleSelectorSpecificity();
209
210 if (auto* selectorList = selector.selectorList()) {
211 LocalContext context(*selectorList->first(), element, VisitedMatchType::Enabled, PseudoId::None);
212 context.inFunctionalPseudoClass = true;
213 context.pseudoElementEffective = false;
214 PseudoIdSet ignoreDynamicPseudo;
215 unsigned subselectorSpecificity = 0;
216 if (matchRecursively(checkingContext, context, ignoreDynamicPseudo, subselectorSpecificity).match != Match::SelectorMatches)
217 return false;
218 specificity = CSSSelector::addSpecificities(specificity, subselectorSpecificity);
219 }
220 return true;
221}
222
223inline static bool hasScrollbarPseudoElement(const PseudoIdSet& dynamicPseudoIdSet)
224{
225 PseudoIdSet scrollbarIdSet = { PseudoId::Scrollbar, PseudoId::ScrollbarThumb, PseudoId::ScrollbarButton, PseudoId::ScrollbarTrack, PseudoId::ScrollbarTrackPiece, PseudoId::ScrollbarCorner };
226 if (dynamicPseudoIdSet & scrollbarIdSet)
227 return true;
228
229 // PseudoId::Resizer does not always have a scrollbar but it is a scrollbar-like pseudo element
230 // because it can have more than one pseudo element.
231 return dynamicPseudoIdSet.has(PseudoId::Resizer);
232}
233
234static SelectorChecker::LocalContext localContextForParent(const SelectorChecker::LocalContext& context)
235{
236 SelectorChecker::LocalContext updatedContext(context);
237 // Disable :visited matching when we see the first link.
238 if (context.element->isLink())
239 updatedContext.visitedMatchType = VisitedMatchType::Disabled;
240
241 updatedContext.isMatchElement = false;
242 updatedContext.isSubjectOrAdjacentElement = false;
243
244 if (updatedContext.mayMatchHostPseudoClass) {
245 updatedContext.element = nullptr;
246 return updatedContext;
247 }
248
249 // Move to the shadow host if matching :host and the parent is the shadow root.
250 if (context.selector->match() == CSSSelector::PseudoClass && context.selector->pseudoClassType() == CSSSelector::PseudoClassHost && is<ShadowRoot>(context.element->parentNode())) {
251 updatedContext.element = downcast<ShadowRoot>(*context.element->parentNode()).host();
252 updatedContext.mayMatchHostPseudoClass = true;
253 return updatedContext;
254 }
255
256 updatedContext.element = context.element->parentElement();
257 return updatedContext;
258}
259
260// Recursive check of selectors and combinators
261// It can return 4 different values:
262// * SelectorMatches - the selector matches the element e
263// * SelectorFailsLocally - the selector fails for the element e
264// * SelectorFailsAllSiblings - the selector fails for e and any sibling of e
265// * SelectorFailsCompletely - the selector fails for e and any sibling or ancestor of e
266SelectorChecker::MatchResult SelectorChecker::matchRecursively(CheckingContext& checkingContext, const LocalContext& context, PseudoIdSet& dynamicPseudoIdSet, unsigned& specificity) const
267{
268 MatchType matchType = MatchType::Element;
269
270 // The first selector has to match.
271 if (!checkOne(checkingContext, context, dynamicPseudoIdSet, matchType, specificity))
272 return MatchResult::fails(Match::SelectorFailsLocally);
273
274 if (context.selector->match() == CSSSelector::PseudoElement) {
275 if (context.selector->isCustomPseudoElement()) {
276 // In functional pseudo class, custom pseudo elements are always disabled.
277 // FIXME: We should accept custom pseudo elements inside :matches().
278 if (context.inFunctionalPseudoClass)
279 return MatchResult::fails(Match::SelectorFailsCompletely);
280 if (ShadowRoot* root = context.element->containingShadowRoot()) {
281 if (context.element->shadowPseudoId() != context.selector->value())
282 return MatchResult::fails(Match::SelectorFailsLocally);
283
284 if (context.selector->isWebKitCustomPseudoElement() && root->mode() != ShadowRootMode::UserAgent)
285 return MatchResult::fails(Match::SelectorFailsLocally);
286 } else
287 return MatchResult::fails(Match::SelectorFailsLocally);
288 } else {
289 if (!context.pseudoElementEffective)
290 return MatchResult::fails(Match::SelectorFailsCompletely);
291
292 if (checkingContext.resolvingMode == Mode::QueryingRules)
293 return MatchResult::fails(Match::SelectorFailsCompletely);
294
295 PseudoId pseudoId = CSSSelector::pseudoId(context.selector->pseudoElementType());
296 if (pseudoId != PseudoId::None)
297 dynamicPseudoIdSet.add(pseudoId);
298 matchType = MatchType::VirtualPseudoElementOnly;
299 }
300 }
301
302 // The rest of the selectors has to match
303 auto relation = context.selector->relation();
304
305 // Prepare next selector
306 const CSSSelector* leftSelector = context.selector->tagHistory();
307 if (!leftSelector)
308 return MatchResult::matches(matchType);
309
310 LocalContext nextContext(context);
311 nextContext.selector = leftSelector;
312
313 if (relation != CSSSelector::Subselector) {
314 // Bail-out if this selector is irrelevant for the pseudoId
315 if (context.pseudoId != PseudoId::None && !dynamicPseudoIdSet.has(context.pseudoId))
316 return MatchResult::fails(Match::SelectorFailsCompletely);
317
318 // Disable :visited matching when we try to match anything else than an ancestors.
319 if (!context.selector->hasDescendantOrChildRelation())
320 nextContext.visitedMatchType = VisitedMatchType::Disabled;
321
322 nextContext.pseudoId = PseudoId::None;
323 // Virtual pseudo element is only effective in the rightmost fragment.
324 nextContext.pseudoElementEffective = false;
325 nextContext.isMatchElement = false;
326 }
327
328 switch (relation) {
329 case CSSSelector::DescendantSpace:
330 nextContext = localContextForParent(nextContext);
331 nextContext.firstSelectorOfTheFragment = nextContext.selector;
332 for (; nextContext.element; nextContext = localContextForParent(nextContext)) {
333 PseudoIdSet ignoreDynamicPseudo;
334 unsigned descendantsSpecificity = 0;
335 MatchResult result = matchRecursively(checkingContext, nextContext, ignoreDynamicPseudo, descendantsSpecificity);
336 ASSERT(!nextContext.pseudoElementEffective && !ignoreDynamicPseudo);
337
338 if (result.match == Match::SelectorMatches)
339 specificity = CSSSelector::addSpecificities(specificity, descendantsSpecificity);
340
341 if (result.match == Match::SelectorMatches || result.match == Match::SelectorFailsCompletely)
342 return MatchResult::updateWithMatchType(result, matchType);
343 }
344 return MatchResult::fails(Match::SelectorFailsCompletely);
345
346 case CSSSelector::Child:
347 {
348 nextContext = localContextForParent(nextContext);
349 if (!nextContext.element)
350 return MatchResult::fails(Match::SelectorFailsCompletely);
351 nextContext.firstSelectorOfTheFragment = nextContext.selector;
352 PseudoIdSet ignoreDynamicPseudo;
353 unsigned childSpecificity = 0;
354 MatchResult result = matchRecursively(checkingContext, nextContext, ignoreDynamicPseudo, childSpecificity);
355 ASSERT(!nextContext.pseudoElementEffective && !ignoreDynamicPseudo);
356
357 if (result.match == Match::SelectorMatches)
358 specificity = CSSSelector::addSpecificities(specificity, childSpecificity);
359
360 if (result.match == Match::SelectorMatches || result.match == Match::SelectorFailsCompletely)
361 return MatchResult::updateWithMatchType(result, matchType);
362 return MatchResult::fails(Match::SelectorFailsAllSiblings);
363 }
364
365 case CSSSelector::DirectAdjacent:
366 {
367 auto relation = context.isMatchElement ? Style::Relation::AffectedByPreviousSibling : Style::Relation::DescendantsAffectedByPreviousSibling;
368 addStyleRelation(checkingContext, *context.element, relation);
369
370 Element* previousElement = context.element->previousElementSibling();
371 if (!previousElement)
372 return MatchResult::fails(Match::SelectorFailsAllSiblings);
373
374 addStyleRelation(checkingContext, *previousElement, Style::Relation::AffectsNextSibling);
375
376 nextContext.element = previousElement;
377 nextContext.firstSelectorOfTheFragment = nextContext.selector;
378 PseudoIdSet ignoreDynamicPseudo;
379 unsigned adjacentSpecificity = 0;
380 MatchResult result = matchRecursively(checkingContext, nextContext, ignoreDynamicPseudo, adjacentSpecificity);
381 ASSERT(!nextContext.pseudoElementEffective && !ignoreDynamicPseudo);
382
383 if (result.match == Match::SelectorMatches)
384 specificity = CSSSelector::addSpecificities(specificity, adjacentSpecificity);
385
386 return MatchResult::updateWithMatchType(result, matchType);
387 }
388 case CSSSelector::IndirectAdjacent: {
389 auto relation = context.isMatchElement ? Style::Relation::AffectedByPreviousSibling : Style::Relation::DescendantsAffectedByPreviousSibling;
390 addStyleRelation(checkingContext, *context.element, relation);
391
392 nextContext.element = context.element->previousElementSibling();
393 nextContext.firstSelectorOfTheFragment = nextContext.selector;
394 for (; nextContext.element; nextContext.element = nextContext.element->previousElementSibling()) {
395 addStyleRelation(checkingContext, *nextContext.element, Style::Relation::AffectsNextSibling);
396
397 PseudoIdSet ignoreDynamicPseudo;
398 unsigned indirectAdjacentSpecificity = 0;
399 MatchResult result = matchRecursively(checkingContext, nextContext, ignoreDynamicPseudo, indirectAdjacentSpecificity);
400 ASSERT(!nextContext.pseudoElementEffective && !ignoreDynamicPseudo);
401
402 if (result.match == Match::SelectorMatches)
403 specificity = CSSSelector::addSpecificities(specificity, indirectAdjacentSpecificity);
404
405 if (result.match == Match::SelectorMatches || result.match == Match::SelectorFailsAllSiblings || result.match == Match::SelectorFailsCompletely)
406 return MatchResult::updateWithMatchType(result, matchType);
407 };
408 return MatchResult::fails(Match::SelectorFailsAllSiblings);
409 }
410 case CSSSelector::Subselector:
411 {
412 // a selector is invalid if something follows a pseudo-element
413 // We make an exception for scrollbar pseudo elements and allow a set of pseudo classes (but nothing else)
414 // to follow the pseudo elements.
415 nextContext.hasScrollbarPseudo = hasScrollbarPseudoElement(dynamicPseudoIdSet);
416 nextContext.hasSelectionPseudo = dynamicPseudoIdSet.has(PseudoId::Selection);
417 if ((context.isMatchElement || checkingContext.resolvingMode == Mode::CollectingRules) && dynamicPseudoIdSet
418 && !nextContext.hasSelectionPseudo
419 && !(nextContext.hasScrollbarPseudo && nextContext.selector->match() == CSSSelector::PseudoClass))
420 return MatchResult::fails(Match::SelectorFailsCompletely);
421
422 unsigned subselectorSpecificity = 0;
423 MatchResult result = matchRecursively(checkingContext, nextContext, dynamicPseudoIdSet, subselectorSpecificity);
424
425 if (result.match == Match::SelectorMatches)
426 specificity = CSSSelector::addSpecificities(specificity, subselectorSpecificity);
427
428 return MatchResult::updateWithMatchType(result, matchType);
429 }
430 case CSSSelector::ShadowDescendant:
431 {
432 Element* shadowHostNode = context.element->shadowHost();
433 if (!shadowHostNode)
434 return MatchResult::fails(Match::SelectorFailsCompletely);
435 nextContext.element = shadowHostNode;
436 nextContext.firstSelectorOfTheFragment = nextContext.selector;
437 nextContext.isSubjectOrAdjacentElement = false;
438 PseudoIdSet ignoreDynamicPseudo;
439 unsigned shadowDescendantSpecificity = 0;
440 MatchResult result = matchRecursively(checkingContext, nextContext, ignoreDynamicPseudo, shadowDescendantSpecificity);
441
442 if (result.match == Match::SelectorMatches)
443 specificity = CSSSelector::addSpecificities(specificity, shadowDescendantSpecificity);
444
445 return MatchResult::updateWithMatchType(result, matchType);
446 }
447 }
448
449
450 ASSERT_NOT_REACHED();
451 return MatchResult::fails(Match::SelectorFailsCompletely);
452}
453
454static bool attributeValueMatches(const Attribute& attribute, CSSSelector::Match match, const AtomicString& selectorValue, bool caseSensitive)
455{
456 const AtomicString& value = attribute.value();
457 ASSERT(!value.isNull());
458
459 switch (match) {
460 case CSSSelector::Set:
461 break;
462 case CSSSelector::Exact:
463 if (caseSensitive ? selectorValue != value : !equalIgnoringASCIICase(selectorValue, value))
464 return false;
465 break;
466 case CSSSelector::List:
467 {
468 // Ignore empty selectors or selectors containing spaces.
469 if (selectorValue.isEmpty() || selectorValue.find(isHTMLSpace<UChar>) != notFound)
470 return false;
471
472 unsigned startSearchAt = 0;
473 while (true) {
474 size_t foundPos;
475 if (caseSensitive)
476 foundPos = value.find(selectorValue, startSearchAt);
477 else
478 foundPos = value.findIgnoringASCIICase(selectorValue, startSearchAt);
479 if (foundPos == notFound)
480 return false;
481 if (!foundPos || isHTMLSpace(value[foundPos - 1])) {
482 unsigned endStr = foundPos + selectorValue.length();
483 if (endStr == value.length() || isHTMLSpace(value[endStr]))
484 break; // We found a match.
485 }
486
487 // No match. Keep looking.
488 startSearchAt = foundPos + 1;
489 }
490 break;
491 }
492 case CSSSelector::Contain: {
493 bool valueContainsSelectorValue;
494 if (caseSensitive)
495 valueContainsSelectorValue = value.contains(selectorValue);
496 else
497 valueContainsSelectorValue = value.containsIgnoringASCIICase(selectorValue);
498
499 if (!valueContainsSelectorValue || selectorValue.isEmpty())
500 return false;
501
502 break;
503 }
504 case CSSSelector::Begin:
505 if (selectorValue.isEmpty())
506 return false;
507 if (caseSensitive) {
508 if (!value.startsWith(selectorValue))
509 return false;
510 } else {
511 if (!value.startsWithIgnoringASCIICase(selectorValue))
512 return false;
513 }
514 break;
515 case CSSSelector::End:
516 if (selectorValue.isEmpty())
517 return false;
518 if (caseSensitive) {
519 if (!value.endsWith(selectorValue))
520 return false;
521 } else {
522 if (!value.endsWithIgnoringASCIICase(selectorValue))
523 return false;
524 }
525 break;
526 case CSSSelector::Hyphen:
527 if (value.length() < selectorValue.length())
528 return false;
529 if (caseSensitive) {
530 if (!value.startsWith(selectorValue))
531 return false;
532 } else {
533 if (!value.startsWithIgnoringASCIICase(selectorValue))
534 return false;
535 }
536 // It they start the same, check for exact match or following '-':
537 if (value.length() != selectorValue.length() && value[selectorValue.length()] != '-')
538 return false;
539 break;
540 default:
541 ASSERT_NOT_REACHED();
542 return false;
543 }
544
545 return true;
546}
547
548static bool anyAttributeMatches(const Element& element, const CSSSelector& selector, const QualifiedName& selectorAttr, bool caseSensitive)
549{
550 ASSERT(element.hasAttributesWithoutUpdate());
551 for (const Attribute& attribute : element.attributesIterator()) {
552 if (!attribute.matches(selectorAttr.prefix(), element.isHTMLElement() ? selector.attributeCanonicalLocalName() : selectorAttr.localName(), selectorAttr.namespaceURI()))
553 continue;
554
555 if (attributeValueMatches(attribute, selector.match(), selector.value(), caseSensitive))
556 return true;
557 }
558
559 return false;
560}
561
562bool SelectorChecker::attributeSelectorMatches(const Element& element, const QualifiedName& attributeName, const AtomicString& attributeValue, const CSSSelector& selector)
563{
564 ASSERT(selector.isAttributeSelector());
565 auto& selectorAttribute = selector.attribute();
566 auto& selectorName = element.isHTMLElement() ? selector.attributeCanonicalLocalName() : selectorAttribute.localName();
567 if (!Attribute::nameMatchesFilter(attributeName, selectorAttribute.prefix(), selectorName, selectorAttribute.namespaceURI()))
568 return false;
569 bool caseSensitive = true;
570 if (selector.attributeValueMatchingIsCaseInsensitive())
571 caseSensitive = false;
572 else if (element.document().isHTMLDocument() && element.isHTMLElement() && !HTMLDocument::isCaseSensitiveAttribute(selector.attribute()))
573 caseSensitive = false;
574 return attributeValueMatches(Attribute(attributeName, attributeValue), selector.match(), selector.value(), caseSensitive);
575}
576
577static bool canMatchHoverOrActiveInQuirksMode(const SelectorChecker::LocalContext& context)
578{
579 // For quirks mode, follow this: http://quirks.spec.whatwg.org/#the-:active-and-:hover-quirk
580 // In quirks mode, a compound selector 'selector' that matches the following conditions must not match elements that would not also match the ':any-link' selector.
581 //
582 // selector uses the ':active' or ':hover' pseudo-classes.
583 // selector does not use a type selector.
584 // selector does not use an attribute selector.
585 // selector does not use an ID selector.
586 // selector does not use a class selector.
587 // selector does not use a pseudo-class selector other than ':active' and ':hover'.
588 // selector does not use a pseudo-element selector.
589 // selector is not part of an argument to a functional pseudo-class or pseudo-element.
590 if (context.inFunctionalPseudoClass)
591 return true;
592
593 for (const CSSSelector* selector = context.firstSelectorOfTheFragment; selector; selector = selector->tagHistory()) {
594 switch (selector->match()) {
595 case CSSSelector::Tag:
596 if (selector->tagQName() != anyQName())
597 return true;
598 break;
599 case CSSSelector::PseudoClass: {
600 CSSSelector::PseudoClassType pseudoClassType = selector->pseudoClassType();
601 if (pseudoClassType != CSSSelector::PseudoClassHover && pseudoClassType != CSSSelector::PseudoClassActive)
602 return true;
603 break;
604 }
605 case CSSSelector::Id:
606 case CSSSelector::Class:
607 case CSSSelector::Exact:
608 case CSSSelector::Set:
609 case CSSSelector::List:
610 case CSSSelector::Hyphen:
611 case CSSSelector::Contain:
612 case CSSSelector::Begin:
613 case CSSSelector::End:
614 case CSSSelector::PagePseudoClass:
615 case CSSSelector::PseudoElement:
616 return true;
617 case CSSSelector::Unknown:
618 ASSERT_NOT_REACHED();
619 break;
620 }
621
622 auto relation = selector->relation();
623 if (relation == CSSSelector::ShadowDescendant)
624 return true;
625
626 if (relation != CSSSelector::Subselector)
627 return false;
628 }
629 return false;
630}
631
632static inline bool tagMatches(const Element& element, const CSSSelector& simpleSelector)
633{
634 const QualifiedName& tagQName = simpleSelector.tagQName();
635
636 if (tagQName == anyQName())
637 return true;
638
639 const AtomicString& localName = (element.isHTMLElement() && element.document().isHTMLDocument()) ? simpleSelector.tagLowercaseLocalName() : tagQName.localName();
640
641 if (localName != starAtom() && localName != element.localName())
642 return false;
643 const AtomicString& namespaceURI = tagQName.namespaceURI();
644 return namespaceURI == starAtom() || namespaceURI == element.namespaceURI();
645}
646
647bool SelectorChecker::checkOne(CheckingContext& checkingContext, const LocalContext& context, PseudoIdSet& dynamicPseudoIdSet, MatchType& matchType, unsigned& specificity) const
648{
649 const Element& element = *context.element;
650 const CSSSelector& selector = *context.selector;
651
652 specificity = CSSSelector::addSpecificities(specificity, selector.simpleSelectorSpecificity());
653
654 if (context.mayMatchHostPseudoClass) {
655 // :host doesn't combine with anything except pseudo elements.
656 bool isHostPseudoClass = selector.match() == CSSSelector::PseudoClass && selector.pseudoClassType() == CSSSelector::PseudoClassHost;
657 bool isPseudoElement = selector.match() == CSSSelector::PseudoElement;
658 if (!isHostPseudoClass && !isPseudoElement)
659 return false;
660 }
661
662 if (selector.match() == CSSSelector::Tag)
663 return tagMatches(element, selector);
664
665 if (selector.match() == CSSSelector::Class)
666 return element.hasClass() && element.classNames().contains(selector.value());
667
668 if (selector.match() == CSSSelector::Id) {
669 ASSERT(!selector.value().isNull());
670 return element.idForStyleResolution() == selector.value();
671 }
672
673 if (selector.isAttributeSelector()) {
674 if (!element.hasAttributes())
675 return false;
676
677 const QualifiedName& attr = selector.attribute();
678 bool caseSensitive = true;
679 if (selector.attributeValueMatchingIsCaseInsensitive())
680 caseSensitive = false;
681 else if (m_documentIsHTML && element.isHTMLElement() && !HTMLDocument::isCaseSensitiveAttribute(attr))
682 caseSensitive = false;
683
684 return anyAttributeMatches(element, selector, attr, caseSensitive);
685 }
686
687 if (selector.match() == CSSSelector::PseudoClass) {
688 // Handle :not up front.
689 if (selector.pseudoClassType() == CSSSelector::PseudoClassNot) {
690 const CSSSelectorList* selectorList = selector.selectorList();
691
692 for (const CSSSelector* subselector = selectorList->first(); subselector; subselector = CSSSelectorList::next(subselector)) {
693 LocalContext subcontext(context);
694 subcontext.inFunctionalPseudoClass = true;
695 subcontext.pseudoElementEffective = false;
696 subcontext.selector = subselector;
697 subcontext.firstSelectorOfTheFragment = selectorList->first();
698 PseudoIdSet ignoreDynamicPseudo;
699
700 unsigned ignoredSpecificity;
701 if (matchRecursively(checkingContext, subcontext, ignoreDynamicPseudo, ignoredSpecificity).match == Match::SelectorMatches) {
702 ASSERT(!ignoreDynamicPseudo);
703 return false;
704 }
705 }
706 return true;
707 }
708 if (context.hasScrollbarPseudo) {
709 // CSS scrollbars match a specific subset of pseudo classes, and they have specialized rules for each
710 // (since there are no elements involved except with window-inactive).
711 return checkScrollbarPseudoClass(checkingContext, element, selector);
712 }
713
714 // Normal element pseudo class checking.
715 switch (selector.pseudoClassType()) {
716 // Pseudo classes:
717 case CSSSelector::PseudoClassNot:
718 break; // Already handled up above.
719 case CSSSelector::PseudoClassEmpty:
720 {
721 bool result = true;
722 for (Node* node = element.firstChild(); node; node = node->nextSibling()) {
723 if (is<Element>(*node)) {
724 result = false;
725 break;
726 }
727 if (is<Text>(*node)) {
728 Text& textNode = downcast<Text>(*node);
729 if (!textNode.data().isEmpty()) {
730 result = false;
731 break;
732 }
733 }
734 }
735 addStyleRelation(checkingContext, *context.element, Style::Relation::AffectedByEmpty, result);
736
737 return result;
738 }
739 case CSSSelector::PseudoClassFirstChild: {
740 // first-child matches the first child that is an element
741 bool isFirstChild = isFirstChildElement(element);
742 auto* parent = element.parentNode();
743 if (is<Element>(parent))
744 addStyleRelation(checkingContext, downcast<Element>(*parent), Style::Relation::ChildrenAffectedByFirstChildRules);
745 else if (!is<ShadowRoot>(parent))
746 break; // FIXME: Add the support for specifying relations on ShadowRoot.
747 if (!isFirstChild)
748 break;
749 addStyleRelation(checkingContext, element, Style::Relation::FirstChild);
750 return true;
751 }
752 case CSSSelector::PseudoClassFirstOfType: {
753 // first-of-type matches the first element of its type
754 auto* parent = element.parentNode();
755 if (is<Element>(parent)) {
756 auto relation = context.isSubjectOrAdjacentElement ? Style::Relation::ChildrenAffectedByForwardPositionalRules : Style::Relation::DescendantsAffectedByForwardPositionalRules;
757 addStyleRelation(checkingContext, downcast<Element>(*parent), relation);
758 } else if (!is<ShadowRoot>(parent))
759 break; // FIXME: Add the support for specifying relations on ShadowRoot.
760 return isFirstOfType(element, element.tagQName());
761 }
762 case CSSSelector::PseudoClassLastChild: {
763 // last-child matches the last child that is an element
764 auto* parent = element.parentNode();
765 bool isLastChild = isLastChildElement(element);
766 if (is<Element>(parent)) {
767 auto& parentElement = downcast<Element>(*parent);
768 if (!parentElement.isFinishedParsingChildren())
769 isLastChild = false;
770 addStyleRelation(checkingContext, parentElement, Style::Relation::ChildrenAffectedByLastChildRules);
771 } else if (!is<ShadowRoot>(parent))
772 break; // FIXME: Add the support for specifying relations on ShadowRoot.
773 if (!isLastChild)
774 break;
775 addStyleRelation(checkingContext, element, Style::Relation::LastChild);
776 return true;
777 }
778 case CSSSelector::PseudoClassLastOfType: {
779 // last-of-type matches the last element of its type
780 auto* parent = element.parentNode();
781 if (is<Element>(parent)) {
782 auto& parentElement = downcast<Element>(*parent);
783 auto relation = context.isSubjectOrAdjacentElement ? Style::Relation::ChildrenAffectedByBackwardPositionalRules : Style::Relation::DescendantsAffectedByBackwardPositionalRules;
784 addStyleRelation(checkingContext, parentElement, relation);
785 if (!parentElement.isFinishedParsingChildren())
786 return false;
787 } else if (!is<ShadowRoot>(parent))
788 break; // FIXME: Add the support for specifying relations on ShadowRoot.
789 return isLastOfType(element, element.tagQName());
790 }
791 case CSSSelector::PseudoClassOnlyChild: {
792 auto* parent = element.parentNode();
793 bool firstChild = isFirstChildElement(element);
794 bool onlyChild = firstChild && isLastChildElement(element);
795 if (is<Element>(parent)) {
796 auto& parentElement = downcast<Element>(*parent);
797 addStyleRelation(checkingContext, parentElement, Style::Relation::ChildrenAffectedByFirstChildRules);
798 addStyleRelation(checkingContext, parentElement, Style::Relation::ChildrenAffectedByLastChildRules);
799 if (!parentElement.isFinishedParsingChildren())
800 onlyChild = false;
801 } else if (!is<ShadowRoot>(parent))
802 break; // FIXME: Add the support for specifying relations on ShadowRoot.
803 if (firstChild)
804 addStyleRelation(checkingContext, element, Style::Relation::FirstChild);
805 if (onlyChild)
806 addStyleRelation(checkingContext, element, Style::Relation::LastChild);
807 return onlyChild;
808 }
809 case CSSSelector::PseudoClassOnlyOfType: {
810 // FIXME: This selector is very slow.
811 auto* parent = element.parentNode();
812 if (is<Element>(parent)) {
813 auto& parentElement = downcast<Element>(*parent);
814 auto forwardRelation = context.isSubjectOrAdjacentElement ? Style::Relation::ChildrenAffectedByForwardPositionalRules : Style::Relation::DescendantsAffectedByForwardPositionalRules;
815 addStyleRelation(checkingContext, parentElement, forwardRelation);
816 auto backwardRelation = context.isSubjectOrAdjacentElement ? Style::Relation::ChildrenAffectedByBackwardPositionalRules : Style::Relation::DescendantsAffectedByBackwardPositionalRules;
817 addStyleRelation(checkingContext, parentElement, backwardRelation);
818
819 if (!parentElement.isFinishedParsingChildren())
820 return false;
821 } else if (!is<ShadowRoot>(parent))
822 break; // FIXME: Add the support for specifying relations on ShadowRoot.
823 return isFirstOfType(element, element.tagQName()) && isLastOfType(element, element.tagQName());
824 }
825 case CSSSelector::PseudoClassMatches:
826 {
827 bool hasMatchedAnything = false;
828 unsigned maxSpecificity = 0;
829
830 MatchType localMatchType = MatchType::VirtualPseudoElementOnly;
831 for (const CSSSelector* subselector = selector.selectorList()->first(); subselector; subselector = CSSSelectorList::next(subselector)) {
832 LocalContext subcontext(context);
833 subcontext.inFunctionalPseudoClass = true;
834 subcontext.pseudoElementEffective = context.pseudoElementEffective;
835 subcontext.selector = subselector;
836 subcontext.firstSelectorOfTheFragment = subselector;
837 PseudoIdSet localDynamicPseudoIdSet;
838 unsigned localSpecificity = 0;
839 MatchResult result = matchRecursively(checkingContext, subcontext, localDynamicPseudoIdSet, localSpecificity);
840 if (result.match == Match::SelectorMatches) {
841 maxSpecificity = std::max(maxSpecificity, localSpecificity);
842
843 if (result.matchType == MatchType::Element)
844 localMatchType = MatchType::Element;
845
846 dynamicPseudoIdSet.merge(localDynamicPseudoIdSet);
847 hasMatchedAnything = true;
848 }
849 }
850 if (hasMatchedAnything) {
851 matchType = localMatchType;
852 specificity = CSSSelector::addSpecificities(specificity, maxSpecificity);
853 }
854 return hasMatchedAnything;
855 }
856 case CSSSelector::PseudoClassPlaceholderShown:
857 if (is<HTMLTextFormControlElement>(element)) {
858 addStyleRelation(checkingContext, element, Style::Relation::Unique);
859 return downcast<HTMLTextFormControlElement>(element).isPlaceholderVisible();
860 }
861 return false;
862 case CSSSelector::PseudoClassNthChild: {
863 auto* parent = element.parentNode();
864 if (is<Element>(parent)) {
865 auto relation = context.isSubjectOrAdjacentElement ? Style::Relation::ChildrenAffectedByForwardPositionalRules : Style::Relation::DescendantsAffectedByForwardPositionalRules;
866 addStyleRelation(checkingContext, downcast<Element>(*parent), relation);
867 } else if (!is<ShadowRoot>(parent))
868 break; // FIXME: Add the support for specifying relations on ShadowRoot.
869
870 if (const CSSSelectorList* selectorList = selector.selectorList()) {
871 unsigned selectorListSpecificity;
872 if (!matchSelectorList(checkingContext, context, element, *selectorList, selectorListSpecificity))
873 return false;
874 specificity = CSSSelector::addSpecificities(specificity, selectorListSpecificity);
875 }
876
877 int count = 1;
878 if (const CSSSelectorList* selectorList = selector.selectorList()) {
879 for (Element* sibling = ElementTraversal::previousSibling(element); sibling; sibling = ElementTraversal::previousSibling(*sibling)) {
880 unsigned ignoredSpecificity;
881 if (matchSelectorList(checkingContext, context, *sibling, *selectorList, ignoredSpecificity))
882 ++count;
883 }
884 } else {
885 count += countElementsBefore(element);
886 addStyleRelation(checkingContext, element, Style::Relation::NthChildIndex, count);
887 }
888
889 if (selector.matchNth(count))
890 return true;
891 break;
892 }
893 case CSSSelector::PseudoClassNthOfType: {
894 auto* parent = element.parentNode();
895 if (is<Element>(parent)) {
896 auto relation = context.isSubjectOrAdjacentElement ? Style::Relation::ChildrenAffectedByForwardPositionalRules : Style::Relation::DescendantsAffectedByForwardPositionalRules;
897 addStyleRelation(checkingContext, downcast<Element>(*parent), relation);
898 } else if (!is<ShadowRoot>(parent))
899 break; // FIXME: Add the support for specifying relations on ShadowRoot.
900
901 int count = 1 + countElementsOfTypeBefore(element, element.tagQName());
902 if (selector.matchNth(count))
903 return true;
904 break;
905 }
906 case CSSSelector::PseudoClassNthLastChild: {
907 auto* parent = element.parentNode();
908 if (is<Element>(parent)) {
909 auto& parentElement = downcast<Element>(*parent);
910 if (const CSSSelectorList* selectorList = selector.selectorList()) {
911 unsigned selectorListSpecificity;
912 if (!matchSelectorList(checkingContext, context, element, *selectorList, selectorListSpecificity))
913 return false;
914 specificity = CSSSelector::addSpecificities(specificity, selectorListSpecificity);
915
916 addStyleRelation(checkingContext, parentElement, Style::Relation::ChildrenAffectedByPropertyBasedBackwardPositionalRules);
917 } else {
918 auto relation = context.isSubjectOrAdjacentElement ? Style::Relation::ChildrenAffectedByBackwardPositionalRules : Style::Relation::DescendantsAffectedByBackwardPositionalRules;
919 addStyleRelation(checkingContext, parentElement, relation);
920 }
921 if (!parentElement.isFinishedParsingChildren())
922 return false;
923 } else if (!is<ShadowRoot>(parent))
924 break; // FIXME: Add the support for specifying relations on ShadowRoot.
925
926 int count = 1;
927 if (const CSSSelectorList* selectorList = selector.selectorList()) {
928 for (Element* sibling = ElementTraversal::nextSibling(element); sibling; sibling = ElementTraversal::nextSibling(*sibling)) {
929 unsigned ignoredSpecificity;
930 if (matchSelectorList(checkingContext, context, *sibling, *selectorList, ignoredSpecificity))
931 ++count;
932 }
933 } else
934 count += countElementsAfter(element);
935
936 return selector.matchNth(count);
937 }
938 case CSSSelector::PseudoClassNthLastOfType: {
939 auto* parent = element.parentNode();
940 if (is<Element>(parent)) {
941 auto& parentElement = downcast<Element>(*parent);
942 auto relation = context.isSubjectOrAdjacentElement ? Style::Relation::ChildrenAffectedByBackwardPositionalRules : Style::Relation::DescendantsAffectedByBackwardPositionalRules;
943 addStyleRelation(checkingContext, parentElement, relation);
944
945 if (!parentElement.isFinishedParsingChildren())
946 return false;
947 } else if (!is<ShadowRoot>(parent))
948 break; // FIXME: Add the support for specifying relations on ShadowRoot.
949 int count = 1 + countElementsOfTypeAfter(element, element.tagQName());
950 return selector.matchNth(count);
951 }
952 case CSSSelector::PseudoClassTarget:
953 if (&element == element.document().cssTarget())
954 return true;
955 break;
956 case CSSSelector::PseudoClassAny:
957 {
958 LocalContext subcontext(context);
959 subcontext.inFunctionalPseudoClass = true;
960 subcontext.pseudoElementEffective = false;
961 for (subcontext.selector = selector.selectorList()->first(); subcontext.selector; subcontext.selector = CSSSelectorList::next(subcontext.selector)) {
962 subcontext.firstSelectorOfTheFragment = subcontext.selector;
963 PseudoIdSet ignoreDynamicPseudo;
964 unsigned ingoredSpecificity = 0;
965 if (matchRecursively(checkingContext, subcontext, ignoreDynamicPseudo, ingoredSpecificity).match == Match::SelectorMatches)
966 return true;
967 }
968 }
969 break;
970 case CSSSelector::PseudoClassAutofill:
971 return isAutofilled(element);
972 case CSSSelector::PseudoClassAutofillStrongPassword:
973 return isAutofilledStrongPassword(element);
974 case CSSSelector::PseudoClassAnyLink:
975 case CSSSelector::PseudoClassAnyLinkDeprecated:
976 case CSSSelector::PseudoClassLink:
977 // :visited and :link matches are separated later when applying the style. Here both classes match all links...
978 return element.isLink();
979 case CSSSelector::PseudoClassVisited:
980 // ...except if :visited matching is disabled for ancestor/sibling matching.
981 // Inside functional pseudo class except for :not, :visited never matches.
982 if (context.inFunctionalPseudoClass)
983 return false;
984 return element.isLink() && context.visitedMatchType == VisitedMatchType::Enabled;
985 case CSSSelector::PseudoClassDrag:
986 addStyleRelation(checkingContext, element, Style::Relation::AffectedByDrag);
987
988 if (element.renderer() && element.renderer()->isDragging())
989 return true;
990 break;
991 case CSSSelector::PseudoClassFocus:
992 return matchesFocusPseudoClass(element);
993 case CSSSelector::PseudoClassFocusWithin:
994 addStyleRelation(checkingContext, element, Style::Relation::AffectedByFocusWithin);
995 return element.hasFocusWithin();
996 case CSSSelector::PseudoClassHover:
997 if (m_strictParsing || element.isLink() || canMatchHoverOrActiveInQuirksMode(context)) {
998 addStyleRelation(checkingContext, element, Style::Relation::AffectedByHover);
999
1000 // See the comment in generateElementIsHovered() in SelectorCompiler.
1001 if (checkingContext.resolvingMode == SelectorChecker::Mode::CollectingRulesIgnoringVirtualPseudoElements && !context.isMatchElement)
1002 return true;
1003
1004 if (element.hovered() || InspectorInstrumentation::forcePseudoState(element, CSSSelector::PseudoClassHover))
1005 return true;
1006 }
1007 break;
1008 case CSSSelector::PseudoClassActive:
1009 if (m_strictParsing || element.isLink() || canMatchHoverOrActiveInQuirksMode(context)) {
1010 addStyleRelation(checkingContext, element, Style::Relation::AffectedByActive);
1011
1012 if (element.active() || InspectorInstrumentation::forcePseudoState(element, CSSSelector::PseudoClassActive))
1013 return true;
1014 }
1015 break;
1016 case CSSSelector::PseudoClassEnabled:
1017 return matchesEnabledPseudoClass(element);
1018 case CSSSelector::PseudoClassFullPageMedia:
1019 return isMediaDocument(element);
1020 case CSSSelector::PseudoClassDefault:
1021 return matchesDefaultPseudoClass(element);
1022 case CSSSelector::PseudoClassDisabled:
1023 return matchesDisabledPseudoClass(element);
1024 case CSSSelector::PseudoClassReadOnly:
1025 return matchesReadOnlyPseudoClass(element);
1026 case CSSSelector::PseudoClassReadWrite:
1027 return matchesReadWritePseudoClass(element);
1028 case CSSSelector::PseudoClassOptional:
1029 return isOptionalFormControl(element);
1030 case CSSSelector::PseudoClassRequired:
1031 return isRequiredFormControl(element);
1032 case CSSSelector::PseudoClassValid:
1033 return isValid(element);
1034 case CSSSelector::PseudoClassInvalid:
1035 return isInvalid(element);
1036 case CSSSelector::PseudoClassChecked:
1037 return isChecked(element);
1038 case CSSSelector::PseudoClassIndeterminate:
1039 return matchesIndeterminatePseudoClass(element);
1040 case CSSSelector::PseudoClassRoot:
1041 if (&element == element.document().documentElement())
1042 return true;
1043 break;
1044 case CSSSelector::PseudoClassLang:
1045 {
1046 ASSERT(selector.langArgumentList() && !selector.langArgumentList()->isEmpty());
1047 return matchesLangPseudoClass(element, *selector.langArgumentList());
1048 }
1049#if ENABLE(FULLSCREEN_API)
1050 case CSSSelector::PseudoClassFullScreen:
1051 return matchesFullScreenPseudoClass(element);
1052 case CSSSelector::PseudoClassAnimatingFullScreenTransition:
1053 return matchesFullScreenAnimatingFullScreenTransitionPseudoClass(element);
1054 case CSSSelector::PseudoClassFullScreenAncestor:
1055 return matchesFullScreenAncestorPseudoClass(element);
1056 case CSSSelector::PseudoClassFullScreenDocument:
1057 return matchesFullScreenDocumentPseudoClass(element);
1058 case CSSSelector::PseudoClassFullScreenControlsHidden:
1059 return matchesFullScreenControlsHiddenPseudoClass(element);
1060#endif
1061 case CSSSelector::PseudoClassInRange:
1062 return isInRange(element);
1063 case CSSSelector::PseudoClassOutOfRange:
1064 return isOutOfRange(element);
1065#if ENABLE(VIDEO_TRACK)
1066 case CSSSelector::PseudoClassFuture:
1067 return matchesFutureCuePseudoClass(element);
1068 case CSSSelector::PseudoClassPast:
1069 return matchesPastCuePseudoClass(element);
1070#endif
1071
1072 case CSSSelector::PseudoClassScope: {
1073 const Node* contextualReferenceNode = !checkingContext.scope ? element.document().documentElement() : checkingContext.scope;
1074 if (&element == contextualReferenceNode)
1075 return true;
1076 break;
1077 }
1078 case CSSSelector::PseudoClassHost: {
1079 if (!context.mayMatchHostPseudoClass)
1080 return false;
1081 unsigned hostSpecificity;
1082 if (!matchHostPseudoClass(selector, element, checkingContext, hostSpecificity))
1083 return false;
1084 specificity = CSSSelector::addSpecificities(specificity, hostSpecificity);
1085 return true;
1086 }
1087 case CSSSelector::PseudoClassDefined:
1088 return isDefinedElement(element);
1089 case CSSSelector::PseudoClassWindowInactive:
1090 return isWindowInactive(element);
1091
1092 case CSSSelector::PseudoClassHorizontal:
1093 case CSSSelector::PseudoClassVertical:
1094 case CSSSelector::PseudoClassDecrement:
1095 case CSSSelector::PseudoClassIncrement:
1096 case CSSSelector::PseudoClassStart:
1097 case CSSSelector::PseudoClassEnd:
1098 case CSSSelector::PseudoClassDoubleButton:
1099 case CSSSelector::PseudoClassSingleButton:
1100 case CSSSelector::PseudoClassNoButton:
1101 case CSSSelector::PseudoClassCornerPresent:
1102 return false;
1103
1104#if ENABLE(CSS_SELECTORS_LEVEL4)
1105 // FIXME: Implement :dir() selector.
1106 case CSSSelector::PseudoClassDir:
1107 return false;
1108
1109 // FIXME: Implement :role() selector.
1110 case CSSSelector::PseudoClassRole:
1111 return false;
1112#endif
1113
1114#if ENABLE(ATTACHMENT_ELEMENT)
1115 case CSSSelector::PseudoClassHasAttachment:
1116 return hasAttachment(element);
1117#endif
1118
1119 case CSSSelector::PseudoClassUnknown:
1120 ASSERT_NOT_REACHED();
1121 break;
1122 }
1123 return false;
1124 }
1125#if ENABLE(VIDEO_TRACK)
1126 if (selector.match() == CSSSelector::PseudoElement && selector.pseudoElementType() == CSSSelector::PseudoElementCue) {
1127 LocalContext subcontext(context);
1128
1129 const CSSSelector* const & selector = context.selector;
1130 for (subcontext.selector = selector->selectorList()->first(); subcontext.selector; subcontext.selector = CSSSelectorList::next(subcontext.selector)) {
1131 subcontext.firstSelectorOfTheFragment = subcontext.selector;
1132 subcontext.inFunctionalPseudoClass = true;
1133 subcontext.pseudoElementEffective = false;
1134 PseudoIdSet ignoredDynamicPseudo;
1135 unsigned ignoredSpecificity = 0;
1136 if (matchRecursively(checkingContext, subcontext, ignoredDynamicPseudo, ignoredSpecificity).match == Match::SelectorMatches)
1137 return true;
1138 }
1139 return false;
1140 }
1141#endif
1142 if (selector.match() == CSSSelector::PseudoElement && selector.pseudoElementType() == CSSSelector::PseudoElementSlotted) {
1143 // We see ::slotted() pseudo elements when collecting slotted rules from the slot shadow tree only.
1144 ASSERT(checkingContext.resolvingMode == Mode::CollectingRules);
1145 return is<HTMLSlotElement>(element);
1146 }
1147 return true;
1148}
1149
1150bool SelectorChecker::matchSelectorList(CheckingContext& checkingContext, const LocalContext& context, const Element& element, const CSSSelectorList& selectorList, unsigned& specificity) const
1151{
1152 specificity = 0;
1153 bool hasMatchedAnything = false;
1154
1155 for (const CSSSelector* subselector = selectorList.first(); subselector; subselector = CSSSelectorList::next(subselector)) {
1156 LocalContext subcontext(context);
1157 subcontext.element = &element;
1158 subcontext.selector = subselector;
1159 subcontext.inFunctionalPseudoClass = true;
1160 subcontext.pseudoElementEffective = false;
1161 subcontext.firstSelectorOfTheFragment = subselector;
1162 PseudoIdSet ignoreDynamicPseudo;
1163 unsigned localSpecificity = 0;
1164 if (matchRecursively(checkingContext, subcontext, ignoreDynamicPseudo, localSpecificity).match == Match::SelectorMatches) {
1165 ASSERT(!ignoreDynamicPseudo);
1166
1167 hasMatchedAnything = true;
1168 specificity = std::max(specificity, localSpecificity);
1169 }
1170 }
1171 return hasMatchedAnything;
1172}
1173
1174bool SelectorChecker::checkScrollbarPseudoClass(const CheckingContext& checkingContext, const Element& element, const CSSSelector& selector) const
1175{
1176 ASSERT(selector.match() == CSSSelector::PseudoClass);
1177
1178 switch (selector.pseudoClassType()) {
1179 case CSSSelector::PseudoClassWindowInactive:
1180 return isWindowInactive(element);
1181 case CSSSelector::PseudoClassEnabled:
1182 return scrollbarMatchesEnabledPseudoClass(checkingContext);
1183 case CSSSelector::PseudoClassDisabled:
1184 return scrollbarMatchesDisabledPseudoClass(checkingContext);
1185 case CSSSelector::PseudoClassHover:
1186 return scrollbarMatchesHoverPseudoClass(checkingContext);
1187 case CSSSelector::PseudoClassActive:
1188 return scrollbarMatchesActivePseudoClass(checkingContext);
1189 case CSSSelector::PseudoClassHorizontal:
1190 return scrollbarMatchesHorizontalPseudoClass(checkingContext);
1191 case CSSSelector::PseudoClassVertical:
1192 return scrollbarMatchesVerticalPseudoClass(checkingContext);
1193 case CSSSelector::PseudoClassDecrement:
1194 return scrollbarMatchesDecrementPseudoClass(checkingContext);
1195 case CSSSelector::PseudoClassIncrement:
1196 return scrollbarMatchesIncrementPseudoClass(checkingContext);
1197 case CSSSelector::PseudoClassStart:
1198 return scrollbarMatchesStartPseudoClass(checkingContext);
1199 case CSSSelector::PseudoClassEnd:
1200 return scrollbarMatchesEndPseudoClass(checkingContext);
1201 case CSSSelector::PseudoClassDoubleButton:
1202 return scrollbarMatchesDoubleButtonPseudoClass(checkingContext);
1203 case CSSSelector::PseudoClassSingleButton:
1204 return scrollbarMatchesSingleButtonPseudoClass(checkingContext);
1205 case CSSSelector::PseudoClassNoButton:
1206 return scrollbarMatchesNoButtonPseudoClass(checkingContext);
1207 case CSSSelector::PseudoClassCornerPresent:
1208 return scrollbarMatchesCornerPresentPseudoClass(checkingContext);
1209 default:
1210 return false;
1211 }
1212}
1213
1214unsigned SelectorChecker::determineLinkMatchType(const CSSSelector* selector)
1215{
1216 unsigned linkMatchType = MatchAll;
1217
1218 // Statically determine if this selector will match a link in visited, unvisited or any state, or never.
1219 // :visited never matches other elements than the innermost link element.
1220 for (; selector; selector = selector->tagHistory()) {
1221 if (selector->match() == CSSSelector::PseudoClass) {
1222 switch (selector->pseudoClassType()) {
1223 case CSSSelector::PseudoClassLink:
1224 linkMatchType &= ~SelectorChecker::MatchVisited;
1225 break;
1226 case CSSSelector::PseudoClassVisited:
1227 linkMatchType &= ~SelectorChecker::MatchLink;
1228 break;
1229 default:
1230 break;
1231 }
1232 }
1233 auto relation = selector->relation();
1234 if (relation == CSSSelector::Subselector)
1235 continue;
1236 if (!selector->hasDescendantOrChildRelation())
1237 return linkMatchType;
1238 if (linkMatchType != MatchAll)
1239 return linkMatchType;
1240 }
1241 return linkMatchType;
1242}
1243
1244static bool isFrameFocused(const Element& element)
1245{
1246 return element.document().frame() && element.document().frame()->selection().isFocusedAndActive();
1247}
1248
1249bool SelectorChecker::matchesFocusPseudoClass(const Element& element)
1250{
1251 if (InspectorInstrumentation::forcePseudoState(element, CSSSelector::PseudoClassFocus))
1252 return true;
1253 return element.focused() && isFrameFocused(element);
1254}
1255
1256}
1257