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