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