[WTF] Import std::optional reference implementation as WTF::Optional
[WebKit-https.git] / Source / WebCore / css / MediaQueryEvaluator.cpp
1 /*
2  * CSS Media Query Evaluator
3  *
4  * Copyright (C) 2006 Kimmo Kinnunen <kimmo.t.kinnunen@nokia.com>.
5  * Copyright (C) 2013 Apple Inc. All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
20  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
24  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include "config.h"
30 #include "MediaQueryEvaluator.h"
31
32 #include "CSSAspectRatioValue.h"
33 #include "CSSPrimitiveValue.h"
34 #include "CSSToLengthConversionData.h"
35 #include "CSSValueKeywords.h"
36 #include "CSSValueList.h"
37 #include "FloatRect.h"
38 #include "FrameView.h"
39 #include "IntRect.h"
40 #include "MainFrame.h"
41 #include "MediaFeatureNames.h"
42 #include "MediaList.h"
43 #include "MediaQuery.h"
44 #include "NodeRenderStyle.h"
45 #include "Page.h"
46 #include "PlatformScreen.h"
47 #include "RenderStyle.h"
48 #include "RenderView.h"
49 #include "Screen.h"
50 #include "Settings.h"
51 #include "StyleResolver.h"
52 #include "Theme.h"
53 #include <wtf/HashMap.h>
54
55 #if ENABLE(3D_TRANSFORMS)
56 #include "RenderLayerCompositor.h"
57 #endif
58
59 namespace WebCore {
60
61 enum MediaFeaturePrefix { MinPrefix, MaxPrefix, NoPrefix };
62
63 typedef bool (*MediaQueryFunction)(CSSValue*, const CSSToLengthConversionData&, Frame&, MediaFeaturePrefix);
64 typedef HashMap<AtomicStringImpl*, MediaQueryFunction> MediaQueryFunctionMap;
65
66 static bool isAccessibilitySettingsDependent(const AtomicString& mediaFeature)
67 {
68     return mediaFeature == MediaFeatureNames::invertedColors
69         || mediaFeature == MediaFeatureNames::maxMonochrome
70         || mediaFeature == MediaFeatureNames::minMonochrome
71         || mediaFeature == MediaFeatureNames::monochrome
72         || mediaFeature == MediaFeatureNames::prefersReducedMotion;
73 }
74
75 static bool isViewportDependent(const AtomicString& mediaFeature)
76 {
77     return mediaFeature == MediaFeatureNames::width
78         || mediaFeature == MediaFeatureNames::height
79         || mediaFeature == MediaFeatureNames::minWidth
80         || mediaFeature == MediaFeatureNames::minHeight
81         || mediaFeature == MediaFeatureNames::maxWidth
82         || mediaFeature == MediaFeatureNames::maxHeight
83         || mediaFeature == MediaFeatureNames::orientation
84         || mediaFeature == MediaFeatureNames::aspectRatio
85         || mediaFeature == MediaFeatureNames::minAspectRatio
86         || mediaFeature == MediaFeatureNames::maxAspectRatio;
87 }
88
89 MediaQueryEvaluator::MediaQueryEvaluator(bool mediaFeatureResult)
90     : m_fallbackResult(mediaFeatureResult)
91 {
92 }
93
94 MediaQueryEvaluator::MediaQueryEvaluator(const String& acceptedMediaType, bool mediaFeatureResult)
95     : m_mediaType(acceptedMediaType)
96     , m_fallbackResult(mediaFeatureResult)
97 {
98 }
99
100 MediaQueryEvaluator::MediaQueryEvaluator(const String& acceptedMediaType, const Document& document, const RenderStyle* style)
101     : m_mediaType(acceptedMediaType)
102     , m_frame(document.frame())
103     , m_style(style)
104 {
105 }
106
107 bool MediaQueryEvaluator::mediaTypeMatch(const String& mediaTypeToMatch) const
108 {
109     return mediaTypeToMatch.isEmpty()
110         || equalLettersIgnoringASCIICase(mediaTypeToMatch, "all")
111         || equalIgnoringASCIICase(mediaTypeToMatch, m_mediaType);
112 }
113
114 bool MediaQueryEvaluator::mediaTypeMatchSpecific(const char* mediaTypeToMatch) const
115 {
116     // Like mediaTypeMatch, but without the special cases for "" and "all".
117     ASSERT(mediaTypeToMatch);
118     ASSERT(mediaTypeToMatch[0] != '\0');
119     ASSERT(!equalLettersIgnoringASCIICase(StringView(mediaTypeToMatch), "all"));
120     return equalIgnoringASCIICase(m_mediaType, mediaTypeToMatch);
121 }
122
123 static bool applyRestrictor(MediaQuery::Restrictor r, bool value)
124 {
125     return r == MediaQuery::Not ? !value : value;
126 }
127
128 bool MediaQueryEvaluator::evaluate(const MediaQuerySet& querySet, StyleResolver* styleResolver) const
129 {
130     auto& queries = querySet.queryVector();
131     if (!queries.size())
132         return true; // Empty query list evaluates to true.
133
134     // Iterate over queries, stop if any of them eval to true (OR semantics).
135     bool result = false;
136     for (size_t i = 0; i < queries.size() && !result; ++i) {
137         auto& query = queries[i];
138
139         if (query.ignored() || (!query.expressions().size() && query.mediaType().isEmpty()))
140             continue;
141
142         if (mediaTypeMatch(query.mediaType())) {
143             auto& expressions = query.expressions();
144             // Iterate through expressions, stop if any of them eval to false (AND semantics).
145             size_t j = 0;
146             for (; j < expressions.size(); ++j) {
147                 bool expressionResult = evaluate(expressions[j]);
148                 if (styleResolver && isViewportDependent(expressions[j].mediaFeature()))
149                     styleResolver->addViewportDependentMediaQueryResult(expressions[j], expressionResult);
150                 if (styleResolver && isAccessibilitySettingsDependent(expressions[j].mediaFeature()))
151                     styleResolver->addAccessibilitySettingsDependentMediaQueryResult(expressions[j], expressionResult);
152                 if (!expressionResult)
153                     break;
154             }
155
156             // Assume true if we are at the end of the list, otherwise assume false.
157             result = applyRestrictor(query.restrictor(), expressions.size() == j);
158         } else
159             result = applyRestrictor(query.restrictor(), false);
160     }
161
162     return result;
163 }
164
165 bool MediaQueryEvaluator::evaluate(const MediaQuerySet& querySet, Vector<MediaQueryResult>& results) const
166 {
167     auto& queries = querySet.queryVector();
168     if (!queries.size())
169         return true;
170
171     bool result = false;
172     for (size_t i = 0; i < queries.size() && !result; ++i) {
173         auto& query = queries[i];
174
175         if (query.ignored())
176             continue;
177
178         if (mediaTypeMatch(query.mediaType())) {
179             auto& expressions = query.expressions();
180             size_t j = 0;
181             for (; j < expressions.size(); ++j) {
182                 bool expressionResult = evaluate(expressions[j]);
183                 if (isViewportDependent(expressions[j].mediaFeature()))
184                     results.append({ expressions[j], expressionResult });
185                 if (!expressionResult)
186                     break;
187             }
188             result = applyRestrictor(query.restrictor(), expressions.size() == j);
189         } else
190             result = applyRestrictor(query.restrictor(), false);
191     }
192
193     return result;
194 }
195
196 template<typename T, typename U> bool compareValue(T a, U b, MediaFeaturePrefix op)
197 {
198     switch (op) {
199     case MinPrefix:
200         return a >= b;
201     case MaxPrefix:
202         return a <= b;
203     case NoPrefix:
204         return a == b;
205     }
206     return false;
207 }
208
209 static bool compareAspectRatioValue(CSSValue* value, int width, int height, MediaFeaturePrefix op)
210 {
211     if (!is<CSSAspectRatioValue>(value))
212         return false;
213     auto& aspectRatio = downcast<CSSAspectRatioValue>(*value);
214     return compareValue(width * aspectRatio.denominatorValue(), height * aspectRatio.numeratorValue(), op);
215 }
216
217 static std::optional<double> doubleValue(CSSValue* value)
218 {
219     if (!is<CSSPrimitiveValue>(value) || !downcast<CSSPrimitiveValue>(*value).isNumber())
220         return std::nullopt;
221     return downcast<CSSPrimitiveValue>(*value).doubleValue(CSSPrimitiveValue::CSS_NUMBER);
222 }
223
224 static bool zeroEvaluate(CSSValue* value, MediaFeaturePrefix op)
225 {
226     auto numericValue = doubleValue(value);
227     return numericValue && compareValue(0, numericValue.value(), op);
228 }
229
230 static bool oneEvaluate(CSSValue* value, MediaFeaturePrefix op)
231 {
232     if (!value)
233         return true;
234     auto numericValue = doubleValue(value);
235     return numericValue && compareValue(1, numericValue.value(), op);
236 }
237
238 static bool colorEvaluate(CSSValue* value, const CSSToLengthConversionData&, Frame& frame, MediaFeaturePrefix op)
239 {
240     int bitsPerComponent = screenDepthPerComponent(frame.mainFrame().view());
241     auto numericValue = doubleValue(value);
242     if (!numericValue)
243         return bitsPerComponent;
244     return compareValue(bitsPerComponent, numericValue.value(), op);
245 }
246
247 static bool colorIndexEvaluate(CSSValue* value, const CSSToLengthConversionData&, Frame&, MediaFeaturePrefix op)
248 {
249     // Always return false for indexed display.
250     return zeroEvaluate(value, op);
251 }
252
253 static bool colorGamutEvaluate(CSSValue* value, const CSSToLengthConversionData&, Frame& frame, MediaFeaturePrefix)
254 {
255     if (!value)
256         return true;
257
258     switch (downcast<CSSPrimitiveValue>(*value).valueID()) {
259     case CSSValueSrgb:
260         return true;
261     case CSSValueP3:
262         // FIXME: For the moment we just assume any "extended color" display is at least as good as P3.
263         return screenSupportsExtendedColor(frame.mainFrame().view());
264     case CSSValueRec2020:
265         // FIXME: At some point we should start detecting displays that support more colors.
266         return false;
267     default:
268         return true;
269     }
270 }
271
272 static bool monochromeEvaluate(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame& frame, MediaFeaturePrefix op)
273 {
274     bool isMonochrome;
275
276     if (frame.settings().forcedDisplayIsMonochromeAccessibilityValue() == Settings::ForcedAccessibilityValue::On)
277         isMonochrome = true;
278     else if (frame.settings().forcedDisplayIsMonochromeAccessibilityValue() == Settings::ForcedAccessibilityValue::Off)
279         isMonochrome = false;
280     else
281         isMonochrome = screenIsMonochrome(frame.mainFrame().view());
282
283     if (!isMonochrome)
284         return zeroEvaluate(value, op);
285     return colorEvaluate(value, conversionData, frame, op);
286 }
287
288 static bool invertedColorsEvaluate(CSSValue* value, const CSSToLengthConversionData&, Frame& frame, MediaFeaturePrefix)
289 {
290     bool isInverted;
291
292     if (frame.settings().forcedColorsAreInvertedAccessibilityValue() == Settings::ForcedAccessibilityValue::On)
293         isInverted = true;
294     else if (frame.settings().forcedColorsAreInvertedAccessibilityValue() == Settings::ForcedAccessibilityValue::Off)
295         isInverted = false;
296     else
297         isInverted = screenHasInvertedColors();
298
299     if (!value)
300         return isInverted;
301
302     return downcast<CSSPrimitiveValue>(*value).valueID() == (isInverted ? CSSValueInverted : CSSValueNone);
303 }
304
305 static bool orientationEvaluate(CSSValue* value, const CSSToLengthConversionData&, Frame& frame, MediaFeaturePrefix)
306 {
307     FrameView* view = frame.view();
308     if (!view)
309         return false;
310
311     auto width = view->layoutWidth();
312     auto height = view->layoutHeight();
313
314     if (!is<CSSPrimitiveValue>(value)) {
315         // Expression (orientation) evaluates to true if width and height >= 0.
316         return height >= 0 && width >= 0;
317     }
318
319     auto keyword = downcast<CSSPrimitiveValue>(*value).valueID();
320     if (width > height) // Square viewport is portrait.
321         return keyword == CSSValueLandscape;
322     return keyword == CSSValuePortrait;
323 }
324
325 static bool aspectRatioEvaluate(CSSValue* value, const CSSToLengthConversionData&, Frame& frame, MediaFeaturePrefix op)
326 {
327     // ({,min-,max-}aspect-ratio)
328     // assume if we have a device, its aspect ratio is non-zero
329     if (!value)
330         return true;
331
332     FrameView* view = frame.view();
333     if (!view)
334         return true;
335
336     return compareAspectRatioValue(value, view->layoutWidth(), view->layoutHeight(), op);
337 }
338
339 static bool deviceAspectRatioEvaluate(CSSValue* value, const CSSToLengthConversionData&, Frame& frame, MediaFeaturePrefix op)
340 {
341     // ({,min-,max-}device-aspect-ratio)
342     // assume if we have a device, its aspect ratio is non-zero
343     if (!value)
344         return true;
345
346     auto size = screenRect(frame.mainFrame().view()).size();
347     return compareAspectRatioValue(value, size.width(), size.height(), op);
348 }
349
350 static bool evaluateResolution(CSSValue* value, Frame& frame, MediaFeaturePrefix op)
351 {
352     // FIXME: Possible handle other media types than 'screen' and 'print'.
353     FrameView* view = frame.view();
354     if (!view)
355         return false;
356
357     float deviceScaleFactor = 0;
358
359     // This checks the actual media type applied to the document, and we know
360     // this method only got called if this media type matches the one defined
361     // in the query. Thus, if if the document's media type is "print", the
362     // media type of the query will either be "print" or "all".
363     String mediaType = view->mediaType();
364     if (equalLettersIgnoringASCIICase(mediaType, "screen"))
365         deviceScaleFactor = frame.page() ? frame.page()->deviceScaleFactor() : 1;
366     else if (equalLettersIgnoringASCIICase(mediaType, "print")) {
367         // The resolution of images while printing should not depend on the dpi
368         // of the screen. Until we support proper ways of querying this info
369         // we use 300px which is considered minimum for current printers.
370         deviceScaleFactor = 3.125; // 300dpi / 96dpi;
371     }
372
373     if (!value)
374         return !!deviceScaleFactor;
375
376     if (!is<CSSPrimitiveValue>(value))
377         return false;
378
379     auto& resolution = downcast<CSSPrimitiveValue>(*value);
380     return compareValue(deviceScaleFactor, resolution.isNumber() ? resolution.floatValue() : resolution.floatValue(CSSPrimitiveValue::CSS_DPPX), op);
381 }
382
383 static bool devicePixelRatioEvaluate(CSSValue* value, const CSSToLengthConversionData&, Frame& frame, MediaFeaturePrefix op)
384 {
385     return (!value || (is<CSSPrimitiveValue>(*value) && downcast<CSSPrimitiveValue>(*value).isNumber())) && evaluateResolution(value, frame, op);
386 }
387
388 static bool resolutionEvaluate(CSSValue* value, const CSSToLengthConversionData&, Frame& frame, MediaFeaturePrefix op)
389 {
390 #if ENABLE(RESOLUTION_MEDIA_QUERY)
391     return (!value || (is<CSSPrimitiveValue>(*value) && downcast<CSSPrimitiveValue>(*value).isResolution())) && evaluateResolution(value, frame, op);
392 #else
393     UNUSED_PARAM(value);
394     UNUSED_PARAM(frame);
395     UNUSED_PARAM(op);
396     return false;
397 #endif
398 }
399
400 static bool gridEvaluate(CSSValue* value, const CSSToLengthConversionData&, Frame&, MediaFeaturePrefix op)
401 {
402     return zeroEvaluate(value, op);
403 }
404
405 static bool computeLength(CSSValue* value, bool strict, const CSSToLengthConversionData& conversionData, int& result)
406 {
407     if (!is<CSSPrimitiveValue>(value))
408         return false;
409
410     auto& primitiveValue = downcast<CSSPrimitiveValue>(*value);
411
412     if (primitiveValue.isNumber()) {
413         result = primitiveValue.intValue();
414         return !strict || !result;
415     }
416
417     if (primitiveValue.isLength()) {
418         result = primitiveValue.computeLength<int>(conversionData);
419         return true;
420     }
421
422     return false;
423 }
424
425 static bool deviceHeightEvaluate(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame& frame, MediaFeaturePrefix op)
426 {
427     // ({,min-,max-}device-height)
428     // assume if we have a device, assume non-zero
429     if (!value)
430         return true;
431     int length;
432     auto height = screenRect(frame.mainFrame().view()).height();
433     return computeLength(value, !frame.document()->inQuirksMode(), conversionData, length) && compareValue(height, length, op);
434 }
435
436 static bool deviceWidthEvaluate(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame& frame, MediaFeaturePrefix op)
437 {
438     // ({,min-,max-}device-width)
439     // assume if we have a device, assume non-zero
440     if (!value)
441         return true;
442     int length;
443     auto width = screenRect(frame.mainFrame().view()).width();
444     return computeLength(value, !frame.document()->inQuirksMode(), conversionData, length) && compareValue(width, length, op);
445 }
446
447 static bool heightEvaluate(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame& frame, MediaFeaturePrefix op)
448 {
449     FrameView* view = frame.view();
450     if (!view)
451         return false;
452     int height = view->layoutHeight();
453     if (!value)
454         return height;
455     if (auto* renderView = frame.document()->renderView())
456         height = adjustForAbsoluteZoom(height, *renderView);
457     int length;
458     return computeLength(value, !frame.document()->inQuirksMode(), conversionData, length) && compareValue(height, length, op);
459 }
460
461 static bool widthEvaluate(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame& frame, MediaFeaturePrefix op)
462 {
463     FrameView* view = frame.view();
464     if (!view)
465         return false;
466     int width = view->layoutWidth();
467     if (!value)
468         return width;
469     if (auto* renderView = frame.document()->renderView())
470         width = adjustForAbsoluteZoom(width, *renderView);
471     int length;
472     return computeLength(value, !frame.document()->inQuirksMode(), conversionData, length) && compareValue(width, length, op);
473 }
474
475 static bool minColorEvaluate(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame& frame, MediaFeaturePrefix)
476 {
477     return colorEvaluate(value, conversionData, frame, MinPrefix);
478 }
479
480 static bool maxColorEvaluate(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame& frame, MediaFeaturePrefix)
481 {
482     return colorEvaluate(value, conversionData, frame, MaxPrefix);
483 }
484
485 static bool minColorIndexEvaluate(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame& frame, MediaFeaturePrefix)
486 {
487     return colorIndexEvaluate(value, conversionData, frame, MinPrefix);
488 }
489
490 static bool maxColorIndexEvaluate(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame& frame, MediaFeaturePrefix)
491 {
492     return colorIndexEvaluate(value, conversionData, frame, MaxPrefix);
493 }
494
495 static bool minMonochromeEvaluate(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame& frame, MediaFeaturePrefix)
496 {
497     return monochromeEvaluate(value, conversionData, frame, MinPrefix);
498 }
499
500 static bool maxMonochromeEvaluate(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame& frame, MediaFeaturePrefix)
501 {
502     return monochromeEvaluate(value, conversionData, frame, MaxPrefix);
503 }
504
505 static bool minAspectRatioEvaluate(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame& frame, MediaFeaturePrefix)
506 {
507     return aspectRatioEvaluate(value, conversionData, frame, MinPrefix);
508 }
509
510 static bool maxAspectRatioEvaluate(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame& frame, MediaFeaturePrefix)
511 {
512     return aspectRatioEvaluate(value, conversionData, frame, MaxPrefix);
513 }
514
515 static bool minDeviceAspectRatioEvaluate(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame& frame, MediaFeaturePrefix)
516 {
517     return deviceAspectRatioEvaluate(value, conversionData, frame, MinPrefix);
518 }
519
520 static bool maxDeviceAspectRatioEvaluate(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame& frame, MediaFeaturePrefix)
521 {
522     return deviceAspectRatioEvaluate(value, conversionData, frame, MaxPrefix);
523 }
524
525 static bool minDevicePixelRatioEvaluate(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame& frame, MediaFeaturePrefix)
526 {
527     return devicePixelRatioEvaluate(value, conversionData, frame, MinPrefix);
528 }
529
530 static bool maxDevicePixelRatioEvaluate(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame& frame, MediaFeaturePrefix)
531 {
532     return devicePixelRatioEvaluate(value, conversionData, frame, MaxPrefix);
533 }
534
535 static bool minHeightEvaluate(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame& frame, MediaFeaturePrefix)
536 {
537     return heightEvaluate(value, conversionData, frame, MinPrefix);
538 }
539
540 static bool maxHeightEvaluate(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame& frame, MediaFeaturePrefix)
541 {
542     return heightEvaluate(value, conversionData, frame, MaxPrefix);
543 }
544
545 static bool minWidthEvaluate(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame& frame, MediaFeaturePrefix)
546 {
547     return widthEvaluate(value, conversionData, frame, MinPrefix);
548 }
549
550 static bool maxWidthEvaluate(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame& frame, MediaFeaturePrefix)
551 {
552     return widthEvaluate(value, conversionData, frame, MaxPrefix);
553 }
554
555 static bool minDeviceHeightEvaluate(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame& frame, MediaFeaturePrefix)
556 {
557     return deviceHeightEvaluate(value, conversionData, frame, MinPrefix);
558 }
559
560 static bool maxDeviceHeightEvaluate(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame& frame, MediaFeaturePrefix)
561 {
562     return deviceHeightEvaluate(value, conversionData, frame, MaxPrefix);
563 }
564
565 static bool minDeviceWidthEvaluate(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame& frame, MediaFeaturePrefix)
566 {
567     return deviceWidthEvaluate(value, conversionData, frame, MinPrefix);
568 }
569
570 static bool maxDeviceWidthEvaluate(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame& frame, MediaFeaturePrefix)
571 {
572     return deviceWidthEvaluate(value, conversionData, frame, MaxPrefix);
573 }
574
575 static bool minResolutionEvaluate(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame& frame, MediaFeaturePrefix)
576 {
577     return resolutionEvaluate(value, conversionData, frame, MinPrefix);
578 }
579
580 static bool maxResolutionEvaluate(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame& frame, MediaFeaturePrefix)
581 {
582     return resolutionEvaluate(value, conversionData, frame, MaxPrefix);
583 }
584
585 static bool animationEvaluate(CSSValue* value, const CSSToLengthConversionData&, Frame&, MediaFeaturePrefix op)
586 {
587     return oneEvaluate(value, op);
588 }
589
590 static bool transitionEvaluate(CSSValue* value, const CSSToLengthConversionData&, Frame&, MediaFeaturePrefix op)
591 {
592     return oneEvaluate(value, op);
593 }
594
595 static bool transform2dEvaluate(CSSValue* value, const CSSToLengthConversionData&, Frame&, MediaFeaturePrefix op)
596 {
597     return oneEvaluate(value, op);
598 }
599
600 static bool transform3dEvaluate(CSSValue* value, const CSSToLengthConversionData&, Frame& frame, MediaFeaturePrefix op)
601 {
602 #if ENABLE(3D_TRANSFORMS)
603     auto* view = frame.contentRenderer();
604     return view && view->compositor().canRender3DTransforms() ? oneEvaluate(value, op) : zeroEvaluate(value, op);
605 #else
606     UNUSED_PARAM(frame);
607     return zeroEvaluate(value, op);
608 #endif
609 }
610
611 #if ENABLE(VIEW_MODE_CSS_MEDIA)
612
613 static bool viewModeEvaluate(CSSValue* value, const CSSToLengthConversionData&, Frame& frame, MediaFeaturePrefix)
614 {
615     if (!value)
616         return true;
617
618     auto keyword = downcast<CSSPrimitiveValue>(*value).valueID();
619
620     switch (frame.page()->viewMode()) {
621     case Page::ViewModeWindowed:
622         return keyword == CSSValueWindowed;
623     case Page::ViewModeFloating:
624         return keyword == CSSValueFloating;
625     case Page::ViewModeFullscreen:
626         return keyword == CSSValueFullscreen;
627     case Page::ViewModeMaximized:
628         return keyword == CSSValueMaximized;
629     case Page::ViewModeMinimized:
630         return keyword == CSSValueMinimized;
631     default:
632         break;
633     }
634
635     return false;
636 }
637
638 #endif // ENABLE(VIEW_MODE_CSS_MEDIA)
639
640 static bool videoPlayableInlineEvaluate(CSSValue*, const CSSToLengthConversionData&, Frame& frame, MediaFeaturePrefix)
641 {
642     return frame.settings().allowsInlineMediaPlayback();
643 }
644
645 static bool hoverEvaluate(CSSValue* value, const CSSToLengthConversionData&, Frame&, MediaFeaturePrefix)
646 {
647     if (!is<CSSPrimitiveValue>(value)) {
648 #if ENABLE(TOUCH_EVENTS)
649         return false;
650 #else
651         return true;
652 #endif
653     }
654
655     auto keyword = downcast<CSSPrimitiveValue>(*value).valueID();
656 #if ENABLE(TOUCH_EVENTS)
657     return keyword == CSSValueNone;
658 #else
659     return keyword == CSSValueHover;
660 #endif
661 }
662
663 static bool anyHoverEvaluate(CSSValue* value, const CSSToLengthConversionData& cssToLengthConversionData, Frame& frame, MediaFeaturePrefix prefix)
664 {
665     return hoverEvaluate(value, cssToLengthConversionData, frame, prefix);
666 }
667
668 static bool pointerEvaluate(CSSValue* value, const CSSToLengthConversionData&, Frame&, MediaFeaturePrefix)
669 {
670     if (!is<CSSPrimitiveValue>(value))
671         return true;
672
673     auto keyword = downcast<CSSPrimitiveValue>(*value).valueID();
674 #if ENABLE(TOUCH_EVENTS)
675     return keyword == CSSValueCoarse;
676 #else
677     return keyword == CSSValueFine;
678 #endif
679 }
680
681 static bool anyPointerEvaluate(CSSValue* value, const CSSToLengthConversionData& cssToLengthConversionData, Frame& frame, MediaFeaturePrefix prefix)
682 {
683     return pointerEvaluate(value, cssToLengthConversionData, frame, prefix);
684 }
685
686 static bool prefersReducedMotionEvaluate(CSSValue* value, const CSSToLengthConversionData&, Frame& frame, MediaFeaturePrefix)
687 {
688     bool userPrefersReducedMotion = false;
689
690     if (frame.settings().forcedPrefersReducedMotionAccessibilityValue() == Settings::ForcedAccessibilityValue::On)
691         userPrefersReducedMotion = true;
692     else if (frame.settings().forcedPrefersReducedMotionAccessibilityValue() == Settings::ForcedAccessibilityValue::Off)
693         userPrefersReducedMotion = false;
694 #if PLATFORM(IOS) || USE(NEW_THEME)
695     else
696         userPrefersReducedMotion = platformTheme()->userPrefersReducedMotion();
697 #endif
698
699     if (!value)
700         return userPrefersReducedMotion;
701
702     return downcast<CSSPrimitiveValue>(*value).valueID() == (userPrefersReducedMotion ? CSSValueReduce : CSSValueDefault);
703 }
704
705 // Use this function instead of calling add directly to avoid inlining.
706 static void add(MediaQueryFunctionMap& map, AtomicStringImpl* key, MediaQueryFunction value)
707 {
708     map.add(key, value);
709 }
710
711 bool MediaQueryEvaluator::evaluate(const MediaQueryExpression& expression) const
712 {
713     if (!m_frame || !m_frame->view() || !m_style)
714         return m_fallbackResult;
715
716     if (!expression.isValid())
717         return false;
718
719     static NeverDestroyed<MediaQueryFunctionMap> map = [] {
720         MediaQueryFunctionMap map;
721 #define ADD_TO_FUNCTIONMAP(name, str) add(map, MediaFeatureNames::name.impl(), name##Evaluate);
722         CSS_MEDIAQUERY_NAMES_FOR_EACH_MEDIAFEATURE(ADD_TO_FUNCTIONMAP);
723 #undef ADD_TO_FUNCTIONMAP
724         return map;
725     }();
726
727     auto function = map.get().get(expression.mediaFeature().impl());
728     if (!function)
729         return false;
730
731     Document& document = *m_frame->document();
732     if (!document.documentElement())
733         return false;
734     return function(expression.value(), { m_style, document.documentElement()->renderStyle(), document.renderView(), 1, false }, *m_frame, NoPrefix);
735 }
736
737 } // namespace