REGRESSION(r244635): [GTK] Wrong background color used in non-dark mode
[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 "FullscreenManager.h"
31 #include "HTMLInputElement.h"
32 #include "HTMLOptionElement.h"
33 #include "RenderScrollbar.h"
34 #include "ScrollableArea.h"
35 #include "ScrollbarTheme.h"
36 #include <wtf/Compiler.h>
37
38 #if ENABLE(ATTACHMENT_ELEMENT)
39 #include "HTMLAttachmentElement.h"
40 #endif
41
42 #if ENABLE(VIDEO_TRACK)
43 #include "WebVTTElement.h"
44 #endif
45
46 namespace WebCore {
47
48 ALWAYS_INLINE bool isAutofilled(const Element& element)
49 {
50     return is<HTMLInputElement>(element) && downcast<HTMLInputElement>(element).isAutoFilled();
51 }
52
53 ALWAYS_INLINE bool isAutofilledStrongPassword(const Element& element)
54 {
55     return is<HTMLInputElement>(element) && downcast<HTMLInputElement>(element).isAutoFilled() && downcast<HTMLInputElement>(element).hasAutoFillStrongPasswordButton();
56 }
57
58 ALWAYS_INLINE bool matchesDefaultPseudoClass(const Element& element)
59 {
60     return element.matchesDefaultPseudoClass();
61 }
62
63 // https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled
64 ALWAYS_INLINE bool matchesDisabledPseudoClass(const Element& element)
65 {
66     return is<HTMLElement>(element) && downcast<HTMLElement>(element).isActuallyDisabled();
67 }
68
69 // https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled
70 ALWAYS_INLINE bool matchesEnabledPseudoClass(const Element& element)
71 {
72     return is<HTMLElement>(element) && downcast<HTMLElement>(element).canBeActuallyDisabled() && !element.isDisabledFormControl();
73 }
74
75 ALWAYS_INLINE bool isDefinedElement(const Element& element)
76 {
77     return !element.isUndefinedCustomElement();
78 }
79
80 ALWAYS_INLINE bool isMediaDocument(const Element& element)
81 {
82     return element.document().isMediaDocument();
83 }
84
85 ALWAYS_INLINE bool isChecked(const Element& element)
86 {
87     // Even though WinIE allows checked and indeterminate to co-exist, the CSS selector spec says that
88     // you can't be both checked and indeterminate. We will behave like WinIE behind the scenes and just
89     // obey the CSS spec here in the test for matching the pseudo.
90     if (is<HTMLInputElement>(element)) {
91         auto& inputElement = downcast<HTMLInputElement>(element);
92         return inputElement.shouldAppearChecked() && !inputElement.shouldAppearIndeterminate();
93     }
94     if (is<HTMLOptionElement>(element))
95         return const_cast<HTMLOptionElement&>(downcast<HTMLOptionElement>(element)).selected();
96
97     return false;
98 }
99
100 ALWAYS_INLINE bool isInRange(const Element& element)
101 {
102     return element.isInRange();
103 }
104
105 ALWAYS_INLINE bool isOutOfRange(const Element& element)
106 {
107     return element.isOutOfRange();
108 }
109
110 ALWAYS_INLINE bool isInvalid(const Element& element)
111 {
112     return element.matchesInvalidPseudoClass();
113 }
114
115 ALWAYS_INLINE bool isOptionalFormControl(const Element& element)
116 {
117     return element.isOptionalFormControl();
118 }
119
120 ALWAYS_INLINE bool isRequiredFormControl(const Element& element)
121 {
122     return element.isRequiredFormControl();
123 }
124
125 ALWAYS_INLINE bool isValid(const Element& element)
126 {
127     return element.matchesValidPseudoClass();
128 }
129
130 ALWAYS_INLINE bool isWindowInactive(const Element& element)
131 {
132     auto* page = element.document().page();
133     if (!page)
134         return false;
135     return !page->focusController().isActive();
136 }
137
138 #if ENABLE(ATTACHMENT_ELEMENT)
139 ALWAYS_INLINE bool hasAttachment(const Element& element)
140 {
141     return is<HTMLImageElement>(element) && downcast<HTMLImageElement>(element).attachmentElement();
142 }
143 #endif
144
145 ALWAYS_INLINE bool containslanguageSubtagMatchingRange(StringView language, StringView range, unsigned languageLength, unsigned& position)
146 {
147     unsigned languageSubtagsStartIndex = position;
148     unsigned languageSubtagsEndIndex = languageLength;
149     bool isAsteriskRange = range == "*";
150     do {
151         if (languageSubtagsStartIndex > 0)
152             languageSubtagsStartIndex += 1;
153         
154         languageSubtagsEndIndex = std::min<unsigned>(language.find('-', languageSubtagsStartIndex), languageLength);
155
156         if (languageSubtagsStartIndex > languageSubtagsEndIndex)
157             return false;
158
159         StringView languageSubtag = language.substring(languageSubtagsStartIndex, languageSubtagsEndIndex - languageSubtagsStartIndex);
160         bool isEqual = equalIgnoringASCIICase(range, languageSubtag);
161         if (!isAsteriskRange) {
162             if ((!isEqual && !languageSubtagsStartIndex) || (languageSubtag.length() == 1 && languageSubtagsStartIndex > 0))
163                 return false;
164         }
165         languageSubtagsStartIndex = languageSubtagsEndIndex;
166         if (isEqual || isAsteriskRange) {
167             position = languageSubtagsStartIndex;
168             return true;
169         }
170
171     } while (languageSubtagsStartIndex < languageLength);
172     return false;
173 }
174
175 ALWAYS_INLINE bool matchesLangPseudoClass(const Element& element, const Vector<AtomicString>& argumentList)
176 {
177     AtomicString language;
178 #if ENABLE(VIDEO_TRACK)
179     if (is<WebVTTElement>(element))
180         language = downcast<WebVTTElement>(element).language();
181     else
182 #endif
183         language = element.computeInheritedLanguage();
184
185     if (language.isEmpty())
186         return false;
187
188     // Implement basic and extended filterings of given language tags
189     // as specified in www.ietf.org/rfc/rfc4647.txt.
190     StringView languageStringView = language.string();
191     unsigned languageLength = language.length();
192     for (const AtomicString& range : argumentList) {
193         if (range.isEmpty())
194             continue;
195
196         if (range == "*")
197             return true;
198
199         StringView rangeStringView = range.string();
200         if (equalIgnoringASCIICase(languageStringView, rangeStringView) && !languageStringView.contains('-'))
201             return true;
202         
203         unsigned rangeLength = rangeStringView.length();
204         unsigned rangeSubtagsStartIndex = 0;
205         unsigned rangeSubtagsEndIndex = rangeLength;
206         unsigned lastMatchedLanguageSubtagIndex = 0;
207
208         bool matchedRange = true;
209         do {
210             if (rangeSubtagsStartIndex > 0)
211                 rangeSubtagsStartIndex += 1;
212             if (rangeSubtagsStartIndex > languageLength)
213                 return false;
214             rangeSubtagsEndIndex = std::min<unsigned>(rangeStringView.find('-', rangeSubtagsStartIndex), rangeLength);
215             StringView rangeSubtag = rangeStringView.substring(rangeSubtagsStartIndex, rangeSubtagsEndIndex - rangeSubtagsStartIndex);
216             if (!containslanguageSubtagMatchingRange(languageStringView, rangeSubtag, languageLength, lastMatchedLanguageSubtagIndex)) {
217                 matchedRange = false;
218                 break;
219             }
220             rangeSubtagsStartIndex = rangeSubtagsEndIndex;
221         } while (rangeSubtagsStartIndex < rangeLength);
222         if (matchedRange)
223             return true;
224     }
225     return false;
226 }
227
228 ALWAYS_INLINE bool matchesReadOnlyPseudoClass(const Element& element)
229 {
230     return !element.matchesReadWritePseudoClass();
231 }
232
233 ALWAYS_INLINE bool matchesReadWritePseudoClass(const Element& element)
234 {
235     return element.matchesReadWritePseudoClass();
236 }
237
238 ALWAYS_INLINE bool matchesIndeterminatePseudoClass(const Element& element)
239 {
240     return element.matchesIndeterminatePseudoClass();
241 }
242
243 ALWAYS_INLINE bool scrollbarMatchesEnabledPseudoClass(const SelectorChecker::CheckingContext& context)
244 {
245     return context.scrollbar && context.scrollbar->enabled();
246 }
247
248 ALWAYS_INLINE bool scrollbarMatchesDisabledPseudoClass(const SelectorChecker::CheckingContext& context)
249 {
250     return context.scrollbar && !context.scrollbar->enabled();
251 }
252
253 ALWAYS_INLINE bool scrollbarMatchesHoverPseudoClass(const SelectorChecker::CheckingContext& context)
254 {
255     if (!context.scrollbar)
256         return false;
257     ScrollbarPart hoveredPart = context.scrollbar->hoveredPart();
258     if (context.scrollbarPart == ScrollbarBGPart)
259         return hoveredPart != NoPart;
260     if (context.scrollbarPart == TrackBGPart)
261         return hoveredPart == BackTrackPart || hoveredPart == ForwardTrackPart || hoveredPart == ThumbPart;
262     return context.scrollbarPart == hoveredPart;
263 }
264
265 ALWAYS_INLINE bool scrollbarMatchesActivePseudoClass(const SelectorChecker::CheckingContext& context)
266 {
267     if (!context.scrollbar)
268         return false;
269     ScrollbarPart pressedPart = context.scrollbar->pressedPart();
270     if (context.scrollbarPart == ScrollbarBGPart)
271         return pressedPart != NoPart;
272     if (context.scrollbarPart == TrackBGPart)
273         return pressedPart == BackTrackPart || pressedPart == ForwardTrackPart || pressedPart == ThumbPart;
274     return context.scrollbarPart == pressedPart;
275 }
276
277 ALWAYS_INLINE bool scrollbarMatchesHorizontalPseudoClass(const SelectorChecker::CheckingContext& context)
278 {
279     return context.scrollbar && context.scrollbar->orientation() == HorizontalScrollbar;
280 }
281
282 ALWAYS_INLINE bool scrollbarMatchesVerticalPseudoClass(const SelectorChecker::CheckingContext& context)
283 {
284     return context.scrollbar && context.scrollbar->orientation() == VerticalScrollbar;
285 }
286
287 ALWAYS_INLINE bool scrollbarMatchesDecrementPseudoClass(const SelectorChecker::CheckingContext& context)
288 {
289     return context.scrollbarPart == BackButtonStartPart || context.scrollbarPart == BackButtonEndPart || context.scrollbarPart == BackTrackPart;
290 }
291
292 ALWAYS_INLINE bool scrollbarMatchesIncrementPseudoClass(const SelectorChecker::CheckingContext& context)
293 {
294     return context.scrollbarPart == ForwardButtonStartPart || context.scrollbarPart == ForwardButtonEndPart || context.scrollbarPart == ForwardTrackPart;
295 }
296
297 ALWAYS_INLINE bool scrollbarMatchesStartPseudoClass(const SelectorChecker::CheckingContext& context)
298 {
299     return context.scrollbarPart == BackButtonStartPart || context.scrollbarPart == ForwardButtonStartPart || context.scrollbarPart == BackTrackPart;
300 }
301
302 ALWAYS_INLINE bool scrollbarMatchesEndPseudoClass(const SelectorChecker::CheckingContext& context)
303 {
304     return context.scrollbarPart == BackButtonEndPart || context.scrollbarPart == ForwardButtonEndPart || context.scrollbarPart == ForwardTrackPart;
305 }
306
307 ALWAYS_INLINE bool scrollbarMatchesDoubleButtonPseudoClass(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 == ForwardButtonStartPart || context.scrollbarPart == BackTrackPart)
313         return buttonsPlacement == ScrollbarButtonsDoubleStart || buttonsPlacement == ScrollbarButtonsDoubleBoth;
314     if (context.scrollbarPart == BackButtonEndPart || context.scrollbarPart == ForwardButtonEndPart || context.scrollbarPart == ForwardTrackPart)
315         return buttonsPlacement == ScrollbarButtonsDoubleEnd || buttonsPlacement == ScrollbarButtonsDoubleBoth;
316     return false;
317 }
318
319 ALWAYS_INLINE bool scrollbarMatchesSingleButtonPseudoClass(const SelectorChecker::CheckingContext& context)
320 {
321     if (!context.scrollbar)
322         return false;
323     ScrollbarButtonsPlacement buttonsPlacement = context.scrollbar->theme().buttonsPlacement();
324     if (context.scrollbarPart == BackButtonStartPart || context.scrollbarPart == ForwardButtonEndPart || context.scrollbarPart == BackTrackPart || context.scrollbarPart == ForwardTrackPart)
325         return buttonsPlacement == ScrollbarButtonsSingle;
326     return false;
327 }
328
329 ALWAYS_INLINE bool scrollbarMatchesNoButtonPseudoClass(const SelectorChecker::CheckingContext& context)
330 {
331     if (!context.scrollbar)
332         return false;
333     ScrollbarButtonsPlacement buttonsPlacement = context.scrollbar->theme().buttonsPlacement();
334     if (context.scrollbarPart == BackTrackPart)
335         return buttonsPlacement == ScrollbarButtonsNone || buttonsPlacement == ScrollbarButtonsDoubleEnd;
336     if (context.scrollbarPart == ForwardTrackPart)
337         return buttonsPlacement == ScrollbarButtonsNone || buttonsPlacement == ScrollbarButtonsDoubleStart;
338     return false;
339 }
340
341 ALWAYS_INLINE bool scrollbarMatchesCornerPresentPseudoClass(const SelectorChecker::CheckingContext& context)
342 {
343     return context.scrollbar && context.scrollbar->scrollableArea().isScrollCornerVisible();
344 }
345
346 #if ENABLE(FULLSCREEN_API)
347 ALWAYS_INLINE bool matchesFullScreenPseudoClass(const Element& element)
348 {
349     // While a Document is in the fullscreen state, and the document's current fullscreen
350     // element is an element in the document, the 'full-screen' pseudoclass applies to
351     // that element. Also, an <iframe>, <object> or <embed> element whose child browsing
352     // context's Document is in the fullscreen state has the 'full-screen' pseudoclass applied.
353     if (element.isFrameElementBase() && element.containsFullScreenElement())
354         return true;
355     if (!element.document().fullscreenManager().isFullscreen())
356         return false;
357     return &element == element.document().fullscreenManager().currentFullscreenElement();
358 }
359
360 ALWAYS_INLINE bool matchesFullScreenAnimatingFullScreenTransitionPseudoClass(const Element& element)
361 {
362     if (&element != element.document().fullscreenManager().currentFullscreenElement())
363         return false;
364     return element.document().fullscreenManager().isAnimatingFullscreen();
365 }
366
367 ALWAYS_INLINE bool matchesFullScreenAncestorPseudoClass(const Element& element)
368 {
369     return element.containsFullScreenElement();
370 }
371
372 ALWAYS_INLINE bool matchesFullScreenDocumentPseudoClass(const Element& element)
373 {
374     // While a Document is in the fullscreen state, the 'full-screen-document' pseudoclass applies
375     // to all elements of that Document.
376     if (!element.document().fullscreenManager().isFullscreen())
377         return false;
378     return true;
379 }
380
381 ALWAYS_INLINE bool matchesFullScreenControlsHiddenPseudoClass(const Element& element)
382 {
383     if (&element != element.document().fullscreenManager().currentFullscreenElement())
384         return false;
385     return element.document().fullscreenManager().areFullscreenControlsHidden();
386 }
387 #endif
388
389 #if ENABLE(VIDEO_TRACK)
390 ALWAYS_INLINE bool matchesFutureCuePseudoClass(const Element& element)
391 {
392     return is<WebVTTElement>(element) && !downcast<WebVTTElement>(element).isPastNode();
393 }
394
395 ALWAYS_INLINE bool matchesPastCuePseudoClass(const Element& element)
396 {
397     return is<WebVTTElement>(element) && downcast<WebVTTElement>(element).isPastNode();
398 }
399 #endif
400
401 } // namespace WebCore