b7b2e3bcff0e3506c355816160465219ac089245
[WebKit-https.git] / Source / WebCore / css / SelectorCheckerTestFunctions.h
1 /*
2  * Copyright (C) 2014-2016 Apple Inc. All rights reserved.
3  * Copyright (C) 2014 Dhi Aurrahman <diorahman@rockybars.com>
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
15  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
18  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
24  * THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #pragma once
28
29 #include "FocusController.h"
30 #include "HTMLInputElement.h"
31 #include "HTMLOptionElement.h"
32 #include "RenderScrollbar.h"
33 #include "ScrollableArea.h"
34 #include "ScrollbarTheme.h"
35 #include <wtf/Compiler.h>
36
37 #if ENABLE(VIDEO_TRACK)
38 #include "WebVTTElement.h"
39 #endif
40
41 namespace WebCore {
42
43 ALWAYS_INLINE bool isAutofilled(const Element& element)
44 {
45     return is<HTMLInputElement>(element) && downcast<HTMLInputElement>(element).isAutoFilled();
46 }
47
48 ALWAYS_INLINE bool isAutofilledStrongPassword(const Element& element)
49 {
50     return is<HTMLInputElement>(element) && downcast<HTMLInputElement>(element).isAutoFilled() && downcast<HTMLInputElement>(element).hasAutoFillStrongPasswordButton();
51 }
52
53 ALWAYS_INLINE bool matchesDefaultPseudoClass(const Element& element)
54 {
55     return element.matchesDefaultPseudoClass();
56 }
57
58 // https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled
59 ALWAYS_INLINE bool matchesDisabledPseudoClass(const Element& element)
60 {
61     return is<HTMLElement>(element) && downcast<HTMLElement>(element).isActuallyDisabled();
62 }
63
64 // https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled
65 ALWAYS_INLINE bool matchesEnabledPseudoClass(const Element& element)
66 {
67     return is<HTMLElement>(element) && downcast<HTMLElement>(element).canBeActuallyDisabled() && !element.isDisabledFormControl();
68 }
69
70 ALWAYS_INLINE bool isDefinedElement(const Element& element)
71 {
72     return !element.isUndefinedCustomElement();
73 }
74
75 ALWAYS_INLINE bool isMediaDocument(const Element& element)
76 {
77     return element.document().isMediaDocument();
78 }
79
80 ALWAYS_INLINE bool isChecked(const Element& element)
81 {
82     // Even though WinIE allows checked and indeterminate to co-exist, the CSS selector spec says that
83     // you can't be both checked and indeterminate. We will behave like WinIE behind the scenes and just
84     // obey the CSS spec here in the test for matching the pseudo.
85     if (is<HTMLInputElement>(element)) {
86         auto& inputElement = downcast<HTMLInputElement>(element);
87         return inputElement.shouldAppearChecked() && !inputElement.shouldAppearIndeterminate();
88     }
89     if (is<HTMLOptionElement>(element))
90         return const_cast<HTMLOptionElement&>(downcast<HTMLOptionElement>(element)).selected();
91
92     return false;
93 }
94
95 ALWAYS_INLINE bool isInRange(const Element& element)
96 {
97     return element.isInRange();
98 }
99
100 ALWAYS_INLINE bool isOutOfRange(const Element& element)
101 {
102     return element.isOutOfRange();
103 }
104
105 ALWAYS_INLINE bool isInvalid(const Element& element)
106 {
107     return element.matchesInvalidPseudoClass();
108 }
109
110 ALWAYS_INLINE bool isOptionalFormControl(const Element& element)
111 {
112     return element.isOptionalFormControl();
113 }
114
115 ALWAYS_INLINE bool isRequiredFormControl(const Element& element)
116 {
117     return element.isRequiredFormControl();
118 }
119
120 ALWAYS_INLINE bool isValid(const Element& element)
121 {
122     return element.matchesValidPseudoClass();
123 }
124
125 ALWAYS_INLINE bool isWindowInactive(const Element& element)
126 {
127     auto* page = element.document().page();
128     if (!page)
129         return false;
130     return !page->focusController().isActive();
131 }
132
133 ALWAYS_INLINE bool containslanguageSubtagMatchingRange(StringView language, StringView range, unsigned languageLength, unsigned& position)
134 {
135     unsigned languageSubtagsStartIndex = position;
136     unsigned languageSubtagsEndIndex = languageLength;
137     bool isAsteriskRange = range == "*";
138     do {
139         if (languageSubtagsStartIndex > 0)
140             languageSubtagsStartIndex += 1;
141         
142         languageSubtagsEndIndex = std::min<unsigned>(language.find('-', languageSubtagsStartIndex), languageLength);
143
144         if (languageSubtagsStartIndex > languageSubtagsEndIndex)
145             return false;
146
147         StringView languageSubtag = language.substring(languageSubtagsStartIndex, languageSubtagsEndIndex - languageSubtagsStartIndex);
148         bool isEqual = equalIgnoringASCIICase(range, languageSubtag);
149         if (!isAsteriskRange) {
150             if ((!isEqual && !languageSubtagsStartIndex) || (languageSubtag.length() == 1 && languageSubtagsStartIndex > 0))
151                 return false;
152         }
153         languageSubtagsStartIndex = languageSubtagsEndIndex;
154         if (isEqual || isAsteriskRange) {
155             position = languageSubtagsStartIndex;
156             return true;
157         }
158
159     } while (languageSubtagsStartIndex < languageLength);
160     return false;
161 }
162
163 ALWAYS_INLINE bool matchesLangPseudoClass(const Element& element, const Vector<AtomicString>& argumentList)
164 {
165     AtomicString language;
166 #if ENABLE(VIDEO_TRACK)
167     if (is<WebVTTElement>(element))
168         language = downcast<WebVTTElement>(element).language();
169     else
170 #endif
171         language = element.computeInheritedLanguage();
172
173     if (language.isEmpty())
174         return false;
175
176     // Implement basic and extended filterings of given language tags
177     // as specified in www.ietf.org/rfc/rfc4647.txt.
178     StringView languageStringView = language.string();
179     unsigned languageLength = language.length();
180     for (const AtomicString& range : argumentList) {
181         if (range.isEmpty())
182             continue;
183
184         if (range == "*")
185             return true;
186
187         StringView rangeStringView = range.string();
188         if (equalIgnoringASCIICase(languageStringView, rangeStringView) && !languageStringView.contains('-'))
189             return true;
190         
191         unsigned rangeLength = rangeStringView.length();
192         unsigned rangeSubtagsStartIndex = 0;
193         unsigned rangeSubtagsEndIndex = rangeLength;
194         unsigned lastMatchedLanguageSubtagIndex = 0;
195
196         bool matchedRange = true;
197         do {
198             if (rangeSubtagsStartIndex > 0)
199                 rangeSubtagsStartIndex += 1;
200             if (rangeSubtagsStartIndex > languageLength)
201                 return false;
202             rangeSubtagsEndIndex = std::min<unsigned>(rangeStringView.find('-', rangeSubtagsStartIndex), rangeLength);
203             StringView rangeSubtag = rangeStringView.substring(rangeSubtagsStartIndex, rangeSubtagsEndIndex - rangeSubtagsStartIndex);
204             if (!containslanguageSubtagMatchingRange(languageStringView, rangeSubtag, languageLength, lastMatchedLanguageSubtagIndex)) {
205                 matchedRange = false;
206                 break;
207             }
208             rangeSubtagsStartIndex = rangeSubtagsEndIndex;
209         } while (rangeSubtagsStartIndex < rangeLength);
210         if (matchedRange)
211             return true;
212     }
213     return false;
214 }
215
216 ALWAYS_INLINE bool matchesReadOnlyPseudoClass(const Element& element)
217 {
218     return !element.matchesReadWritePseudoClass();
219 }
220
221 ALWAYS_INLINE bool matchesReadWritePseudoClass(const Element& element)
222 {
223     return element.matchesReadWritePseudoClass();
224 }
225
226 ALWAYS_INLINE bool matchesIndeterminatePseudoClass(const Element& element)
227 {
228     return element.matchesIndeterminatePseudoClass();
229 }
230
231 ALWAYS_INLINE bool scrollbarMatchesEnabledPseudoClass(const SelectorChecker::CheckingContext& context)
232 {
233     return context.scrollbar && context.scrollbar->enabled();
234 }
235
236 ALWAYS_INLINE bool scrollbarMatchesDisabledPseudoClass(const SelectorChecker::CheckingContext& context)
237 {
238     return context.scrollbar && !context.scrollbar->enabled();
239 }
240
241 ALWAYS_INLINE bool scrollbarMatchesHoverPseudoClass(const SelectorChecker::CheckingContext& context)
242 {
243     if (!context.scrollbar)
244         return false;
245     ScrollbarPart hoveredPart = context.scrollbar->hoveredPart();
246     if (context.scrollbarPart == ScrollbarBGPart)
247         return hoveredPart != NoPart;
248     if (context.scrollbarPart == TrackBGPart)
249         return hoveredPart == BackTrackPart || hoveredPart == ForwardTrackPart || hoveredPart == ThumbPart;
250     return context.scrollbarPart == hoveredPart;
251 }
252
253 ALWAYS_INLINE bool scrollbarMatchesActivePseudoClass(const SelectorChecker::CheckingContext& context)
254 {
255     if (!context.scrollbar)
256         return false;
257     ScrollbarPart pressedPart = context.scrollbar->pressedPart();
258     if (context.scrollbarPart == ScrollbarBGPart)
259         return pressedPart != NoPart;
260     if (context.scrollbarPart == TrackBGPart)
261         return pressedPart == BackTrackPart || pressedPart == ForwardTrackPart || pressedPart == ThumbPart;
262     return context.scrollbarPart == pressedPart;
263 }
264
265 ALWAYS_INLINE bool scrollbarMatchesHorizontalPseudoClass(const SelectorChecker::CheckingContext& context)
266 {
267     return context.scrollbar && context.scrollbar->orientation() == HorizontalScrollbar;
268 }
269
270 ALWAYS_INLINE bool scrollbarMatchesVerticalPseudoClass(const SelectorChecker::CheckingContext& context)
271 {
272     return context.scrollbar && context.scrollbar->orientation() == VerticalScrollbar;
273 }
274
275 ALWAYS_INLINE bool scrollbarMatchesDecrementPseudoClass(const SelectorChecker::CheckingContext& context)
276 {
277     return context.scrollbarPart == BackButtonStartPart || context.scrollbarPart == BackButtonEndPart || context.scrollbarPart == BackTrackPart;
278 }
279
280 ALWAYS_INLINE bool scrollbarMatchesIncrementPseudoClass(const SelectorChecker::CheckingContext& context)
281 {
282     return context.scrollbarPart == ForwardButtonStartPart || context.scrollbarPart == ForwardButtonEndPart || context.scrollbarPart == ForwardTrackPart;
283 }
284
285 ALWAYS_INLINE bool scrollbarMatchesStartPseudoClass(const SelectorChecker::CheckingContext& context)
286 {
287     return context.scrollbarPart == BackButtonStartPart || context.scrollbarPart == ForwardButtonStartPart || context.scrollbarPart == BackTrackPart;
288 }
289
290 ALWAYS_INLINE bool scrollbarMatchesEndPseudoClass(const SelectorChecker::CheckingContext& context)
291 {
292     return context.scrollbarPart == BackButtonEndPart || context.scrollbarPart == ForwardButtonEndPart || context.scrollbarPart == ForwardTrackPart;
293 }
294
295 ALWAYS_INLINE bool scrollbarMatchesDoubleButtonPseudoClass(const SelectorChecker::CheckingContext& context)
296 {
297     if (!context.scrollbar)
298         return false;
299     ScrollbarButtonsPlacement buttonsPlacement = context.scrollbar->theme().buttonsPlacement();
300     if (context.scrollbarPart == BackButtonStartPart || context.scrollbarPart == ForwardButtonStartPart || context.scrollbarPart == BackTrackPart)
301         return buttonsPlacement == ScrollbarButtonsDoubleStart || buttonsPlacement == ScrollbarButtonsDoubleBoth;
302     if (context.scrollbarPart == BackButtonEndPart || context.scrollbarPart == ForwardButtonEndPart || context.scrollbarPart == ForwardTrackPart)
303         return buttonsPlacement == ScrollbarButtonsDoubleEnd || buttonsPlacement == ScrollbarButtonsDoubleBoth;
304     return false;
305 }
306
307 ALWAYS_INLINE bool scrollbarMatchesSingleButtonPseudoClass(const SelectorChecker::CheckingContext& context)
308 {
309     if (!context.scrollbar)
310         return false;
311     ScrollbarButtonsPlacement buttonsPlacement = context.scrollbar->theme().buttonsPlacement();
312     if (context.scrollbarPart == BackButtonStartPart || context.scrollbarPart == ForwardButtonEndPart || context.scrollbarPart == BackTrackPart || context.scrollbarPart == ForwardTrackPart)
313         return buttonsPlacement == ScrollbarButtonsSingle;
314     return false;
315 }
316
317 ALWAYS_INLINE bool scrollbarMatchesNoButtonPseudoClass(const SelectorChecker::CheckingContext& context)
318 {
319     if (!context.scrollbar)
320         return false;
321     ScrollbarButtonsPlacement buttonsPlacement = context.scrollbar->theme().buttonsPlacement();
322     if (context.scrollbarPart == BackTrackPart)
323         return buttonsPlacement == ScrollbarButtonsNone || buttonsPlacement == ScrollbarButtonsDoubleEnd;
324     if (context.scrollbarPart == ForwardTrackPart)
325         return buttonsPlacement == ScrollbarButtonsNone || buttonsPlacement == ScrollbarButtonsDoubleStart;
326     return false;
327 }
328
329 ALWAYS_INLINE bool scrollbarMatchesCornerPresentPseudoClass(const SelectorChecker::CheckingContext& context)
330 {
331     return context.scrollbar && context.scrollbar->scrollableArea().isScrollCornerVisible();
332 }
333
334 #if ENABLE(FULLSCREEN_API)
335 ALWAYS_INLINE bool matchesFullScreenPseudoClass(const Element& element)
336 {
337     // While a Document is in the fullscreen state, and the document's current fullscreen
338     // element is an element in the document, the 'full-screen' pseudoclass applies to
339     // that element. Also, an <iframe>, <object> or <embed> element whose child browsing
340     // context's Document is in the fullscreen state has the 'full-screen' pseudoclass applied.
341     if (element.isFrameElementBase() && element.containsFullScreenElement())
342         return true;
343     if (!element.document().webkitIsFullScreen())
344         return false;
345     return &element == element.document().webkitCurrentFullScreenElement();
346 }
347
348 ALWAYS_INLINE bool matchesFullScreenAnimatingFullScreenTransitionPseudoClass(const Element& element)
349 {
350     if (&element != element.document().webkitCurrentFullScreenElement())
351         return false;
352     return element.document().isAnimatingFullScreen();
353 }
354
355 ALWAYS_INLINE bool matchesFullScreenAncestorPseudoClass(const Element& element)
356 {
357     return element.containsFullScreenElement();
358 }
359
360 ALWAYS_INLINE bool matchesFullScreenDocumentPseudoClass(const Element& element)
361 {
362     // While a Document is in the fullscreen state, the 'full-screen-document' pseudoclass applies
363     // to all elements of that Document.
364     if (!element.document().webkitIsFullScreen())
365         return false;
366     return true;
367 }
368
369 ALWAYS_INLINE bool matchesFullScreenControlsHiddenPseudoClass(const Element& element)
370 {
371     if (&element != element.document().webkitCurrentFullScreenElement())
372         return false;
373     return element.document().areFullscreenControlsHidden();
374 }
375 #endif
376
377 #if ENABLE(VIDEO_TRACK)
378 ALWAYS_INLINE bool matchesFutureCuePseudoClass(const Element& element)
379 {
380     return is<WebVTTElement>(element) && !downcast<WebVTTElement>(element).isPastNode();
381 }
382
383 ALWAYS_INLINE bool matchesPastCuePseudoClass(const Element& element)
384 {
385     return is<WebVTTElement>(element) && downcast<WebVTTElement>(element).isPastNode();
386 }
387 #endif
388
389 } // namespace WebCore