color-gamut media query shouldn't ASSERT on invalid values
[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).getDoubleValue(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).getValueID()) {
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     if (!screenIsMonochrome(frame.mainFrame().view()))
265         return zeroEvaluate(value, op);
266     return colorEvaluate(value, conversionData, frame, op);
267 }
268
269 static bool invertedColorsEvaluate(CSSValue* value, const CSSToLengthConversionData&, Frame&, MediaFeaturePrefix)
270 {
271     bool isInverted = screenHasInvertedColors();
272     if (!value)
273         return isInverted;
274     return downcast<CSSPrimitiveValue>(*value).getValueID() == (isInverted ? CSSValueInverted : CSSValueNone);
275 }
276
277 static bool orientationEvaluate(CSSValue* value, const CSSToLengthConversionData&, Frame& frame, MediaFeaturePrefix)
278 {
279     FrameView* view = frame.view();
280     if (!view)
281         return false;
282
283     auto width = view->layoutWidth();
284     auto height = view->layoutHeight();
285
286     if (!is<CSSPrimitiveValue>(value)) {
287         // Expression (orientation) evaluates to true if width and height >= 0.
288         return height >= 0 && width >= 0;
289     }
290
291     auto keyword = downcast<CSSPrimitiveValue>(*value).getValueID();
292     if (width > height) // Square viewport is portrait.
293         return keyword == CSSValueLandscape;
294     return keyword == CSSValuePortrait;
295 }
296
297 static bool aspectRatioEvaluate(CSSValue* value, const CSSToLengthConversionData&, Frame& frame, MediaFeaturePrefix op)
298 {
299     // ({,min-,max-}aspect-ratio)
300     // assume if we have a device, its aspect ratio is non-zero
301     if (!value)
302         return true;
303
304     FrameView* view = frame.view();
305     if (!view)
306         return true;
307
308     return compareAspectRatioValue(value, view->layoutWidth(), view->layoutHeight(), op);
309 }
310
311 static bool deviceAspectRatioEvaluate(CSSValue* value, const CSSToLengthConversionData&, Frame& frame, MediaFeaturePrefix op)
312 {
313     // ({,min-,max-}device-aspect-ratio)
314     // assume if we have a device, its aspect ratio is non-zero
315     if (!value)
316         return true;
317
318     auto size = screenRect(frame.mainFrame().view()).size();
319     return compareAspectRatioValue(value, size.width(), size.height(), op);
320 }
321
322 static bool evaluateResolution(CSSValue* value, Frame& frame, MediaFeaturePrefix op)
323 {
324     // FIXME: Possible handle other media types than 'screen' and 'print'.
325     FrameView* view = frame.view();
326     if (!view)
327         return false;
328
329     float deviceScaleFactor = 0;
330
331     // This checks the actual media type applied to the document, and we know
332     // this method only got called if this media type matches the one defined
333     // in the query. Thus, if if the document's media type is "print", the
334     // media type of the query will either be "print" or "all".
335     String mediaType = view->mediaType();
336     if (equalLettersIgnoringASCIICase(mediaType, "screen"))
337         deviceScaleFactor = frame.page() ? frame.page()->deviceScaleFactor() : 1;
338     else if (equalLettersIgnoringASCIICase(mediaType, "print")) {
339         // The resolution of images while printing should not depend on the dpi
340         // of the screen. Until we support proper ways of querying this info
341         // we use 300px which is considered minimum for current printers.
342         deviceScaleFactor = 3.125; // 300dpi / 96dpi;
343     }
344
345     if (!value)
346         return !!deviceScaleFactor;
347
348     if (!is<CSSPrimitiveValue>(value))
349         return false;
350
351     auto& resolution = downcast<CSSPrimitiveValue>(*value);
352     return compareValue(deviceScaleFactor, resolution.isNumber() ? resolution.getFloatValue() : resolution.getFloatValue(CSSPrimitiveValue::CSS_DPPX), op);
353 }
354
355 static bool devicePixelRatioEvaluate(CSSValue* value, const CSSToLengthConversionData&, Frame& frame, MediaFeaturePrefix op)
356 {
357     return (!value || (is<CSSPrimitiveValue>(*value) && downcast<CSSPrimitiveValue>(*value).isNumber())) && evaluateResolution(value, frame, op);
358 }
359
360 static bool resolutionEvaluate(CSSValue* value, const CSSToLengthConversionData&, Frame& frame, MediaFeaturePrefix op)
361 {
362 #if ENABLE(RESOLUTION_MEDIA_QUERY)
363     return (!value || (is<CSSPrimitiveValue>(*value) && downcast<CSSPrimitiveValue>(*value).isResolution())) && evaluateResolution(value, frame, op);
364 #else
365     UNUSED_PARAM(value);
366     UNUSED_PARAM(frame);
367     UNUSED_PARAM(op);
368     return false;
369 #endif
370 }
371
372 static bool gridEvaluate(CSSValue* value, const CSSToLengthConversionData&, Frame&, MediaFeaturePrefix op)
373 {
374     return zeroEvaluate(value, op);
375 }
376
377 static bool computeLength(CSSValue* value, bool strict, const CSSToLengthConversionData& conversionData, int& result)
378 {
379     if (!is<CSSPrimitiveValue>(value))
380         return false;
381
382     auto& primitiveValue = downcast<CSSPrimitiveValue>(*value);
383
384     if (primitiveValue.isNumber()) {
385         result = primitiveValue.getIntValue();
386         return !strict || !result;
387     }
388
389     if (primitiveValue.isLength()) {
390         result = primitiveValue.computeLength<int>(conversionData);
391         return true;
392     }
393
394     return false;
395 }
396
397 static bool deviceHeightEvaluate(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame& frame, MediaFeaturePrefix op)
398 {
399     // ({,min-,max-}device-height)
400     // assume if we have a device, assume non-zero
401     if (!value)
402         return true;
403     int length;
404     auto height = screenRect(frame.mainFrame().view()).height();
405     return computeLength(value, !frame.document()->inQuirksMode(), conversionData, length) && compareValue(height, length, op);
406 }
407
408 static bool deviceWidthEvaluate(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame& frame, MediaFeaturePrefix op)
409 {
410     // ({,min-,max-}device-width)
411     // assume if we have a device, assume non-zero
412     if (!value)
413         return true;
414     int length;
415     auto width = screenRect(frame.mainFrame().view()).width();
416     return computeLength(value, !frame.document()->inQuirksMode(), conversionData, length) && compareValue(width, length, op);
417 }
418
419 static bool heightEvaluate(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame& frame, MediaFeaturePrefix op)
420 {
421     FrameView* view = frame.view();
422     if (!view)
423         return false;
424     int height = view->layoutHeight();
425     if (!value)
426         return height;
427     if (auto* renderView = frame.document()->renderView())
428         height = adjustForAbsoluteZoom(height, *renderView);
429     int length;
430     return computeLength(value, !frame.document()->inQuirksMode(), conversionData, length) && compareValue(height, length, op);
431 }
432
433 static bool widthEvaluate(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame& frame, MediaFeaturePrefix op)
434 {
435     FrameView* view = frame.view();
436     if (!view)
437         return false;
438     int width = view->layoutWidth();
439     if (!value)
440         return width;
441     if (auto* renderView = frame.document()->renderView())
442         width = adjustForAbsoluteZoom(width, *renderView);
443     int length;
444     return computeLength(value, !frame.document()->inQuirksMode(), conversionData, length) && compareValue(width, length, op);
445 }
446
447 static bool minColorEvaluate(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame& frame, MediaFeaturePrefix)
448 {
449     return colorEvaluate(value, conversionData, frame, MinPrefix);
450 }
451
452 static bool maxColorEvaluate(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame& frame, MediaFeaturePrefix)
453 {
454     return colorEvaluate(value, conversionData, frame, MaxPrefix);
455 }
456
457 static bool minColorIndexEvaluate(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame& frame, MediaFeaturePrefix)
458 {
459     return colorIndexEvaluate(value, conversionData, frame, MinPrefix);
460 }
461
462 static bool maxColorIndexEvaluate(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame& frame, MediaFeaturePrefix)
463 {
464     return colorIndexEvaluate(value, conversionData, frame, MaxPrefix);
465 }
466
467 static bool minMonochromeEvaluate(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame& frame, MediaFeaturePrefix)
468 {
469     return monochromeEvaluate(value, conversionData, frame, MinPrefix);
470 }
471
472 static bool maxMonochromeEvaluate(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame& frame, MediaFeaturePrefix)
473 {
474     return monochromeEvaluate(value, conversionData, frame, MaxPrefix);
475 }
476
477 static bool minAspectRatioEvaluate(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame& frame, MediaFeaturePrefix)
478 {
479     return aspectRatioEvaluate(value, conversionData, frame, MinPrefix);
480 }
481
482 static bool maxAspectRatioEvaluate(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame& frame, MediaFeaturePrefix)
483 {
484     return aspectRatioEvaluate(value, conversionData, frame, MaxPrefix);
485 }
486
487 static bool minDeviceAspectRatioEvaluate(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame& frame, MediaFeaturePrefix)
488 {
489     return deviceAspectRatioEvaluate(value, conversionData, frame, MinPrefix);
490 }
491
492 static bool maxDeviceAspectRatioEvaluate(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame& frame, MediaFeaturePrefix)
493 {
494     return deviceAspectRatioEvaluate(value, conversionData, frame, MaxPrefix);
495 }
496
497 static bool minDevicePixelRatioEvaluate(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame& frame, MediaFeaturePrefix)
498 {
499     return devicePixelRatioEvaluate(value, conversionData, frame, MinPrefix);
500 }
501
502 static bool maxDevicePixelRatioEvaluate(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame& frame, MediaFeaturePrefix)
503 {
504     return devicePixelRatioEvaluate(value, conversionData, frame, MaxPrefix);
505 }
506
507 static bool minHeightEvaluate(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame& frame, MediaFeaturePrefix)
508 {
509     return heightEvaluate(value, conversionData, frame, MinPrefix);
510 }
511
512 static bool maxHeightEvaluate(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame& frame, MediaFeaturePrefix)
513 {
514     return heightEvaluate(value, conversionData, frame, MaxPrefix);
515 }
516
517 static bool minWidthEvaluate(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame& frame, MediaFeaturePrefix)
518 {
519     return widthEvaluate(value, conversionData, frame, MinPrefix);
520 }
521
522 static bool maxWidthEvaluate(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame& frame, MediaFeaturePrefix)
523 {
524     return widthEvaluate(value, conversionData, frame, MaxPrefix);
525 }
526
527 static bool minDeviceHeightEvaluate(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame& frame, MediaFeaturePrefix)
528 {
529     return deviceHeightEvaluate(value, conversionData, frame, MinPrefix);
530 }
531
532 static bool maxDeviceHeightEvaluate(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame& frame, MediaFeaturePrefix)
533 {
534     return deviceHeightEvaluate(value, conversionData, frame, MaxPrefix);
535 }
536
537 static bool minDeviceWidthEvaluate(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame& frame, MediaFeaturePrefix)
538 {
539     return deviceWidthEvaluate(value, conversionData, frame, MinPrefix);
540 }
541
542 static bool maxDeviceWidthEvaluate(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame& frame, MediaFeaturePrefix)
543 {
544     return deviceWidthEvaluate(value, conversionData, frame, MaxPrefix);
545 }
546
547 static bool minResolutionEvaluate(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame& frame, MediaFeaturePrefix)
548 {
549     return resolutionEvaluate(value, conversionData, frame, MinPrefix);
550 }
551
552 static bool maxResolutionEvaluate(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame& frame, MediaFeaturePrefix)
553 {
554     return resolutionEvaluate(value, conversionData, frame, MaxPrefix);
555 }
556
557 static bool animationEvaluate(CSSValue* value, const CSSToLengthConversionData&, Frame&, MediaFeaturePrefix op)
558 {
559     return oneEvaluate(value, op);
560 }
561
562 static bool transitionEvaluate(CSSValue* value, const CSSToLengthConversionData&, Frame&, MediaFeaturePrefix op)
563 {
564     return oneEvaluate(value, op);
565 }
566
567 static bool transform2dEvaluate(CSSValue* value, const CSSToLengthConversionData&, Frame&, MediaFeaturePrefix op)
568 {
569     return oneEvaluate(value, op);
570 }
571
572 static bool transform3dEvaluate(CSSValue* value, const CSSToLengthConversionData&, Frame& frame, MediaFeaturePrefix op)
573 {
574 #if ENABLE(3D_TRANSFORMS)
575     auto* view = frame.contentRenderer();
576     return view && view->compositor().canRender3DTransforms() ? oneEvaluate(value, op) : zeroEvaluate(value, op);
577 #else
578     UNUSED_PARAM(frame);
579     return zeroEvaluate(value, op);
580 #endif
581 }
582
583 #if ENABLE(VIEW_MODE_CSS_MEDIA)
584
585 static bool viewModeEvaluate(CSSValue* value, const CSSToLengthConversionData&, Frame& frame, MediaFeaturePrefix)
586 {
587     if (!value)
588         return true;
589
590     auto keyword = downcast<CSSPrimitiveValue>(*value).getValueID();
591
592     switch (frame.page()->viewMode()) {
593     case Page::ViewModeWindowed:
594         return keyword == CSSValueWindowed;
595     case Page::ViewModeFloating:
596         return keyword == CSSValueFloating;
597     case Page::ViewModeFullscreen:
598         return keyword == CSSValueFullscreen;
599     case Page::ViewModeMaximized:
600         return keyword == CSSValueMaximized;
601     case Page::ViewModeMinimized:
602         return keyword == CSSValueMinimized;
603     default:
604         break;
605     }
606
607     return false;
608 }
609
610 #endif // ENABLE(VIEW_MODE_CSS_MEDIA)
611
612 static bool videoPlayableInlineEvaluate(CSSValue*, const CSSToLengthConversionData&, Frame& frame, MediaFeaturePrefix)
613 {
614     return frame.settings().allowsInlineMediaPlayback();
615 }
616
617 static bool hoverEvaluate(CSSValue* value, const CSSToLengthConversionData&, Frame&, MediaFeaturePrefix)
618 {
619     if (!is<CSSPrimitiveValue>(value)) {
620 #if ENABLE(TOUCH_EVENTS)
621         return false;
622 #else
623         return true;
624 #endif
625     }
626
627     auto keyword = downcast<CSSPrimitiveValue>(*value).getValueID();
628 #if ENABLE(TOUCH_EVENTS)
629     return keyword == CSSValueNone;
630 #else
631     return keyword == CSSValueHover;
632 #endif
633 }
634
635 static bool anyHoverEvaluate(CSSValue* value, const CSSToLengthConversionData& cssToLengthConversionData, Frame& frame, MediaFeaturePrefix prefix)
636 {
637     return hoverEvaluate(value, cssToLengthConversionData, frame, prefix);
638 }
639
640 static bool pointerEvaluate(CSSValue* value, const CSSToLengthConversionData&, Frame&, MediaFeaturePrefix)
641 {
642     if (!is<CSSPrimitiveValue>(value))
643         return true;
644
645     auto keyword = downcast<CSSPrimitiveValue>(*value).getValueID();
646 #if ENABLE(TOUCH_EVENTS)
647     return keyword == CSSValueCoarse;
648 #else
649     return keyword == CSSValueFine;
650 #endif
651 }
652
653 static bool anyPointerEvaluate(CSSValue* value, const CSSToLengthConversionData& cssToLengthConversionData, Frame& frame, MediaFeaturePrefix prefix)
654 {
655     return pointerEvaluate(value, cssToLengthConversionData, frame, prefix);
656 }
657
658 static bool prefersReducedMotionEvaluate(CSSValue* value, const CSSToLengthConversionData&, Frame& frame, MediaFeaturePrefix)
659 {
660 #if USE(NEW_THEME)
661     bool userPrefersReducedMotion = platformTheme()->userPrefersReducedMotion();
662 #else
663     bool userPrefersReducedMotion = false;
664 #endif
665
666     if (frame.settings().forcedPrefersReducedMotionValue() == Settings::ForcedPrefersReducedMotionValue::On)
667         userPrefersReducedMotion = true;
668     else if (frame.settings().forcedPrefersReducedMotionValue() == Settings::ForcedPrefersReducedMotionValue::Off)
669         userPrefersReducedMotion = false;
670
671     if (!value)
672         return userPrefersReducedMotion;
673
674     return downcast<CSSPrimitiveValue>(*value).getValueID() == (userPrefersReducedMotion ? CSSValueReduce : CSSValueDefault);
675 }
676
677 // Use this function instead of calling add directly to avoid inlining.
678 static void add(MediaQueryFunctionMap& map, AtomicStringImpl* key, MediaQueryFunction value)
679 {
680     map.add(key, value);
681 }
682
683 bool MediaQueryEvaluator::evaluate(const MediaQueryExpression& expression) const
684 {
685     if (!m_frame || !m_frame->view() || !m_style)
686         return m_fallbackResult;
687
688     if (!expression.isValid())
689         return false;
690
691     static NeverDestroyed<MediaQueryFunctionMap> map = [] {
692         MediaQueryFunctionMap map;
693 #define ADD_TO_FUNCTIONMAP(name, str) add(map, MediaFeatureNames::name.impl(), name##Evaluate);
694         CSS_MEDIAQUERY_NAMES_FOR_EACH_MEDIAFEATURE(ADD_TO_FUNCTIONMAP);
695 #undef ADD_TO_FUNCTIONMAP
696         return map;
697     }();
698
699     auto function = map.get().get(expression.mediaFeature().impl());
700     if (!function)
701         return false;
702
703     Document& document = *m_frame->document();
704     if (!document.documentElement())
705         return false;
706     return function(expression.value(), { m_style, document.documentElement()->renderStyle(), document.renderView(), 1, false }, *m_frame, NoPrefix);
707 }
708
709 } // namespace