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