Add color-gamut media query support
[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 "MediaQueryExp.h"
45 #include "NodeRenderStyle.h"
46 #include "Page.h"
47 #include "PlatformScreen.h"
48 #include "RenderStyle.h"
49 #include "RenderView.h"
50 #include "Screen.h"
51 #include "Settings.h"
52 #include "StyleResolver.h"
53 #include <wtf/HashMap.h>
54
55 #if ENABLE(3D_TRANSFORMS)
56 #include "RenderLayerCompositor.h"
57 #endif
58
59 namespace WebCore {
60
61 using namespace MediaFeatureNames;
62
63 enum MediaFeaturePrefix { MinPrefix, MaxPrefix, NoPrefix };
64
65 typedef bool (*EvalFunc)(CSSValue*, const CSSToLengthConversionData&, Frame*, MediaFeaturePrefix);
66 typedef HashMap<AtomicStringImpl*, EvalFunc> FunctionMap;
67 static FunctionMap* gFunctionMap;
68
69 /*
70  * FIXME: following media features are not implemented: scan
71  *
72  * scan: The "scan" media feature describes the scanning process of
73  * tv output devices. It's unknown how to retrieve this information from
74  * the platform
75  */
76
77 MediaQueryEvaluator::MediaQueryEvaluator(bool mediaFeatureResult)
78     : m_frame(0)
79     , m_style(0)
80     , m_expResult(mediaFeatureResult)
81 {
82 }
83
84 MediaQueryEvaluator::MediaQueryEvaluator(const String& acceptedMediaType, bool mediaFeatureResult)
85     : m_mediaType(acceptedMediaType)
86     , m_frame(0)
87     , m_style(0)
88     , m_expResult(mediaFeatureResult)
89 {
90 }
91
92 MediaQueryEvaluator::MediaQueryEvaluator(const String& acceptedMediaType, Frame* frame, RenderStyle* style)
93     : m_mediaType(acceptedMediaType)
94     , m_frame(frame)
95     , m_style(style)
96     , m_expResult(false) // doesn't matter when we have m_frame and m_style
97 {
98 }
99
100 MediaQueryEvaluator::~MediaQueryEvaluator()
101 {
102 }
103
104 bool MediaQueryEvaluator::mediaTypeMatch(const String& mediaTypeToMatch) const
105 {
106     return mediaTypeToMatch.isEmpty()
107         || equalLettersIgnoringASCIICase(mediaTypeToMatch, "all")
108         || equalIgnoringASCIICase(mediaTypeToMatch, m_mediaType);
109 }
110
111 bool MediaQueryEvaluator::mediaTypeMatchSpecific(const char* mediaTypeToMatch) const
112 {
113     // Like mediaTypeMatch, but without the special cases for "" and "all".
114     ASSERT(mediaTypeToMatch);
115     ASSERT(mediaTypeToMatch[0] != '\0');
116     ASSERT(!equalLettersIgnoringASCIICase(StringView(mediaTypeToMatch), "all"));
117     return equalIgnoringASCIICase(m_mediaType, mediaTypeToMatch);
118 }
119
120 static bool applyRestrictor(MediaQuery::Restrictor r, bool value)
121 {
122     return r == MediaQuery::Not ? !value : value;
123 }
124
125 bool MediaQueryEvaluator::eval(const MediaQuerySet* querySet, StyleResolver* styleResolver) const
126 {
127     if (!querySet)
128         return true;
129
130     auto& queries = querySet->queryVector();
131     if (!queries.size())
132         return true; // empty query list evaluates to true
133
134     // iterate over queries, stop if any of them eval to true (OR semantics)
135     bool result = false;
136     for (size_t i = 0; i < queries.size() && !result; ++i) {
137         MediaQuery* query = queries[i].get();
138
139         if (query->ignored() || (!query->expressions().size() && query->mediaType().isEmpty()))
140             continue;
141
142         if (mediaTypeMatch(query->mediaType())) {
143             auto& expressions = query->expressions();
144             // iterate through expressions, stop if any of them eval to false
145             // (AND semantics)
146             size_t j = 0;
147             for (; j < expressions.size(); ++j) {
148                 bool exprResult = eval(expressions.at(j).get());
149                 if (styleResolver && expressions.at(j)->isViewportDependent())
150                     styleResolver->addViewportDependentMediaQueryResult(expressions.at(j).get(), exprResult);
151                 if (!exprResult)
152                     break;
153             }
154
155             // assume true if we are at the end of the list,
156             // otherwise assume false
157             result = applyRestrictor(query->restrictor(), expressions.size() == j);
158         } else
159             result = applyRestrictor(query->restrictor(), false);
160     }
161
162     return result;
163 }
164
165 bool MediaQueryEvaluator::evalCheckingViewportDependentResults(const MediaQuerySet* querySet, Vector<std::unique_ptr<MediaQueryResult>>& results)
166 {
167     if (!querySet)
168         return true;
169
170     auto& queries = querySet->queryVector();
171     if (!queries.size())
172         return true;
173
174     bool result = false;
175     for (size_t i = 0; i < queries.size() && !result; ++i) {
176         MediaQuery* query = queries[i].get();
177
178         if (query->ignored())
179             continue;
180
181         if (mediaTypeMatch(query->mediaType())) {
182             auto& expressions = query->expressions();
183             size_t j = 0;
184             for (; j < expressions.size(); ++j) {
185                 bool exprResult = eval(expressions.at(j).get());
186                 if (expressions.at(j)->isViewportDependent())
187                     results.append(std::make_unique<MediaQueryResult>(*expressions.at(j), exprResult));
188                 if (!exprResult)
189                     break;
190             }
191             result = applyRestrictor(query->restrictor(), expressions.size() == j);
192         } else
193             result = applyRestrictor(query->restrictor(), false);
194     }
195
196     return result;
197 }
198
199 template<typename T>
200 bool compareValue(T a, T b, MediaFeaturePrefix op)
201 {
202     switch (op) {
203     case MinPrefix:
204         return a >= b;
205     case MaxPrefix:
206         return a <= b;
207     case NoPrefix:
208         return a == b;
209     }
210     return false;
211 }
212
213 static bool compareAspectRatioValue(CSSValue* value, int width, int height, MediaFeaturePrefix op)
214 {
215     if (is<CSSAspectRatioValue>(*value)) {
216         CSSAspectRatioValue& aspectRatio = downcast<CSSAspectRatioValue>(*value);
217         return compareValue(width * static_cast<int>(aspectRatio.denominatorValue()), height * static_cast<int>(aspectRatio.numeratorValue()), op);
218     }
219
220     return false;
221 }
222
223 static bool numberValue(CSSValue* value, float& result)
224 {
225     if (is<CSSPrimitiveValue>(*value) && downcast<CSSPrimitiveValue>(*value).isNumber()) {
226         result = downcast<CSSPrimitiveValue>(*value).getFloatValue(CSSPrimitiveValue::CSS_NUMBER);
227         return true;
228     }
229     return false;
230 }
231
232 static bool colorMediaFeatureEval(CSSValue* value, const CSSToLengthConversionData&, Frame* frame, MediaFeaturePrefix op)
233 {
234     int bitsPerComponent = screenDepthPerComponent(frame->page()->mainFrame().view());
235     float number;
236     if (value)
237         return numberValue(value, number) && compareValue(bitsPerComponent, static_cast<int>(number), op);
238
239     return bitsPerComponent != 0;
240 }
241
242 static bool color_indexMediaFeatureEval(CSSValue* value, const CSSToLengthConversionData&, Frame*, MediaFeaturePrefix op)
243 {
244     // FIXME: It's unknown how to retrieve the information if the display mode is indexed
245     // Assume we don't support indexed display.
246     if (!value)
247         return false;
248
249     float number;
250     return numberValue(value, number) && compareValue(0, static_cast<int>(number), op);
251 }
252
253 static bool color_gamutMediaFeatureEval(CSSValue* value, const CSSToLengthConversionData&, Frame*, MediaFeaturePrefix)
254 {
255     if (!value)
256         return true;
257
258     switch (downcast<CSSPrimitiveValue>(*value).getValueID()) {
259     case CSSValueSrgb:
260         return true;
261     case CSSValueP3:
262         // FIXME: For the moment we'll just assume an "extended
263         // color" display is at least as good as P3.
264         return screenSupportsExtendedColor();
265     case CSSValueRec2020:
266         // FIXME: At some point we should start detecting displays that
267         // support more colors.
268         return false;
269     default:
270         ASSERT_NOT_REACHED();
271         return true;
272     }
273 }
274
275 static bool monochromeMediaFeatureEval(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame* frame, MediaFeaturePrefix op)
276 {
277     if (!screenIsMonochrome(frame->page()->mainFrame().view())) {
278         if (value) {
279             float number;
280             return numberValue(value, number) && compareValue(0, static_cast<int>(number), op);
281         }
282         return false;
283     }
284
285     return colorMediaFeatureEval(value, conversionData, frame, op);
286 }
287
288 static bool inverted_colorsMediaFeatureEval(CSSValue* value, const CSSToLengthConversionData&, Frame*, MediaFeaturePrefix)
289 {
290     bool isInverted = screenHasInvertedColors();
291
292     if (!value)
293         return isInverted;
294
295     const CSSValueID id = downcast<CSSPrimitiveValue>(*value).getValueID();
296     return (isInverted && id == CSSValueInverted) || (!isInverted && id == CSSValueNone);
297 }
298
299 static bool orientationMediaFeatureEval(CSSValue* value, const CSSToLengthConversionData&, Frame* frame, MediaFeaturePrefix)
300 {
301     FrameView* view = frame->view();
302     if (!view)
303         return false;
304
305     int width = view->layoutWidth();
306     int height = view->layoutHeight();
307     if (is<CSSPrimitiveValue>(value)) {
308         const CSSValueID id = downcast<CSSPrimitiveValue>(*value).getValueID();
309         if (width > height) // Square viewport is portrait.
310             return CSSValueLandscape == id;
311         return CSSValuePortrait == id;
312     }
313
314     // Expression (orientation) evaluates to true if width and height >= 0.
315     return height >= 0 && width >= 0;
316 }
317
318 static bool aspect_ratioMediaFeatureEval(CSSValue* value, const CSSToLengthConversionData&, Frame* frame, MediaFeaturePrefix op)
319 {
320     FrameView* view = frame->view();
321     if (!view)
322         return true;
323
324     if (value)
325         return compareAspectRatioValue(value, view->layoutWidth(), view->layoutHeight(), op);
326
327     // ({,min-,max-}aspect-ratio)
328     // assume if we have a device, its aspect ratio is non-zero
329     return true;
330 }
331
332 static bool device_aspect_ratioMediaFeatureEval(CSSValue* value, const CSSToLengthConversionData&, Frame* frame, MediaFeaturePrefix op)
333 {
334     if (value) {
335         FloatRect sg = screenRect(frame->page()->mainFrame().view());
336         return compareAspectRatioValue(value, static_cast<int>(sg.width()), static_cast<int>(sg.height()), op);
337     }
338
339     // ({,min-,max-}device-aspect-ratio)
340     // assume if we have a device, its aspect ratio is non-zero
341     return true;
342 }
343
344 static bool evalResolution(CSSValue* value, Frame* frame, MediaFeaturePrefix op)
345 {
346     // FIXME: Possible handle other media types than 'screen' and 'print'.
347     FrameView* view = frame->view();
348     if (!view)
349         return false;
350
351     float deviceScaleFactor = 0;
352     // This checks the actual media type applied to the document, and we know
353     // this method only got called if this media type matches the one defined
354     // in the query. Thus, if if the document's media type is "print", the
355     // media type of the query will either be "print" or "all".
356     String mediaType = view->mediaType();
357     if (equalLettersIgnoringASCIICase(mediaType, "screen"))
358         deviceScaleFactor = frame->page()->deviceScaleFactor();
359     else if (equalLettersIgnoringASCIICase(mediaType, "print")) {
360         // The resolution of images while printing should not depend on the dpi
361         // of the screen. Until we support proper ways of querying this info
362         // we use 300px which is considered minimum for current printers.
363         deviceScaleFactor = 3.125; // 300dpi / 96dpi;
364     }
365
366     if (!value)
367         return !!deviceScaleFactor;
368
369     if (!is<CSSPrimitiveValue>(*value))
370         return false;
371
372     CSSPrimitiveValue& resolution = downcast<CSSPrimitiveValue>(*value);
373     return compareValue(deviceScaleFactor, resolution.isNumber() ? resolution.getFloatValue() : resolution.getFloatValue(CSSPrimitiveValue::CSS_DPPX), op);
374 }
375
376 static bool device_pixel_ratioMediaFeatureEval(CSSValue *value, const CSSToLengthConversionData&, Frame* frame, MediaFeaturePrefix op)
377 {
378     return (!value || downcast<CSSPrimitiveValue>(*value).isNumber()) && evalResolution(value, frame, op);
379 }
380
381 static bool resolutionMediaFeatureEval(CSSValue* value, const CSSToLengthConversionData&, Frame* frame, MediaFeaturePrefix op)
382 {
383 #if ENABLE(RESOLUTION_MEDIA_QUERY)
384     return (!value || downcast<CSSPrimitiveValue>(*value).isResolution()) && evalResolution(value, frame, op);
385 #else
386     UNUSED_PARAM(value);
387     UNUSED_PARAM(frame);
388     UNUSED_PARAM(op);
389     return false;
390 #endif
391 }
392
393 static bool gridMediaFeatureEval(CSSValue* value, const CSSToLengthConversionData&, Frame*, MediaFeaturePrefix op)
394 {
395     // if output device is bitmap, grid: 0 == true
396     // assume we have bitmap device
397     float number;
398     if (value && numberValue(value, number))
399         return compareValue(static_cast<int>(number), 0, op);
400     return false;
401 }
402
403 static bool computeLength(CSSValue* value, bool strict, const CSSToLengthConversionData& conversionData, int& result)
404 {
405     if (!is<CSSPrimitiveValue>(*value))
406         return false;
407
408     CSSPrimitiveValue& primitiveValue = downcast<CSSPrimitiveValue>(*value);
409
410     if (primitiveValue.isNumber()) {
411         result = primitiveValue.getIntValue();
412         return !strict || !result;
413     }
414
415     if (primitiveValue.isLength()) {
416         result = primitiveValue.computeLength<int>(conversionData);
417         return true;
418     }
419
420     return false;
421 }
422
423 static bool device_heightMediaFeatureEval(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame* frame, MediaFeaturePrefix op)
424 {
425     if (value) {
426         FloatRect sg = screenRect(frame->page()->mainFrame().view());
427         int length;
428         long height = sg.height();
429         return computeLength(value, !frame->document()->inQuirksMode(), conversionData, length) && compareValue(static_cast<int>(height), length, op);
430     }
431     // ({,min-,max-}device-height)
432     // assume if we have a device, assume non-zero
433     return true;
434 }
435
436 static bool device_widthMediaFeatureEval(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame* frame, MediaFeaturePrefix op)
437 {
438     if (value) {
439         FloatRect sg = screenRect(frame->page()->mainFrame().view());
440         int length;
441         long width = sg.width();
442         return computeLength(value, !frame->document()->inQuirksMode(), conversionData, length) && compareValue(static_cast<int>(width), length, op);
443     }
444     // ({,min-,max-}device-width)
445     // assume if we have a device, assume non-zero
446     return true;
447 }
448
449 static bool heightMediaFeatureEval(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame* frame, MediaFeaturePrefix op)
450 {
451     FrameView* view = frame->view();
452     if (!view)
453         return false;
454
455     if (value) {
456         int height = view->layoutHeight();
457         if (RenderView* renderView = frame->document()->renderView())
458             height = adjustForAbsoluteZoom(height, *renderView);
459         int length;
460         return computeLength(value, !frame->document()->inQuirksMode(), conversionData, length) && compareValue(height, length, op);
461     }
462
463     return view->layoutHeight() != 0;
464 }
465
466 static bool widthMediaFeatureEval(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame* frame, MediaFeaturePrefix op)
467 {
468     FrameView* view = frame->view();
469     if (!view)
470         return false;
471
472     if (value) {
473         int width = view->layoutWidth();
474         if (RenderView* renderView = frame->document()->renderView())
475             width = adjustForAbsoluteZoom(width, *renderView);
476         int length;
477         return computeLength(value, !frame->document()->inQuirksMode(), conversionData, length) && compareValue(width, length, op);
478     }
479
480     return view->layoutWidth() != 0;
481 }
482
483 // rest of the functions are trampolines which set the prefix according to the media feature expression used
484
485 static bool min_colorMediaFeatureEval(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame* frame, MediaFeaturePrefix)
486 {
487     return colorMediaFeatureEval(value, conversionData, frame, MinPrefix);
488 }
489
490 static bool max_colorMediaFeatureEval(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame* frame, MediaFeaturePrefix)
491 {
492     return colorMediaFeatureEval(value, conversionData, frame, MaxPrefix);
493 }
494
495 static bool min_color_indexMediaFeatureEval(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame* frame, MediaFeaturePrefix)
496 {
497     return color_indexMediaFeatureEval(value, conversionData, frame, MinPrefix);
498 }
499
500 static bool max_color_indexMediaFeatureEval(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame* frame, MediaFeaturePrefix)
501 {
502     return color_indexMediaFeatureEval(value, conversionData, frame, MaxPrefix);
503 }
504
505 static bool min_monochromeMediaFeatureEval(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame* frame, MediaFeaturePrefix)
506 {
507     return monochromeMediaFeatureEval(value, conversionData, frame, MinPrefix);
508 }
509
510 static bool max_monochromeMediaFeatureEval(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame* frame, MediaFeaturePrefix)
511 {
512     return monochromeMediaFeatureEval(value, conversionData, frame, MaxPrefix);
513 }
514
515 static bool min_aspect_ratioMediaFeatureEval(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame* frame, MediaFeaturePrefix)
516 {
517     return aspect_ratioMediaFeatureEval(value, conversionData, frame, MinPrefix);
518 }
519
520 static bool max_aspect_ratioMediaFeatureEval(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame* frame, MediaFeaturePrefix)
521 {
522     return aspect_ratioMediaFeatureEval(value, conversionData, frame, MaxPrefix);
523 }
524
525 static bool min_device_aspect_ratioMediaFeatureEval(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame* frame, MediaFeaturePrefix)
526 {
527     return device_aspect_ratioMediaFeatureEval(value, conversionData, frame, MinPrefix);
528 }
529
530 static bool max_device_aspect_ratioMediaFeatureEval(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame* frame, MediaFeaturePrefix)
531 {
532     return device_aspect_ratioMediaFeatureEval(value, conversionData, frame, MaxPrefix);
533 }
534
535 static bool min_device_pixel_ratioMediaFeatureEval(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame* frame, MediaFeaturePrefix)
536 {
537     return device_pixel_ratioMediaFeatureEval(value, conversionData, frame, MinPrefix);
538 }
539
540 static bool max_device_pixel_ratioMediaFeatureEval(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame* frame, MediaFeaturePrefix)
541 {
542     return device_pixel_ratioMediaFeatureEval(value, conversionData, frame, MaxPrefix);
543 }
544
545 static bool min_heightMediaFeatureEval(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame* frame, MediaFeaturePrefix)
546 {
547     return heightMediaFeatureEval(value, conversionData, frame, MinPrefix);
548 }
549
550 static bool max_heightMediaFeatureEval(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame* frame, MediaFeaturePrefix)
551 {
552     return heightMediaFeatureEval(value, conversionData, frame, MaxPrefix);
553 }
554
555 static bool min_widthMediaFeatureEval(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame* frame, MediaFeaturePrefix)
556 {
557     return widthMediaFeatureEval(value, conversionData, frame, MinPrefix);
558 }
559
560 static bool max_widthMediaFeatureEval(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame* frame, MediaFeaturePrefix)
561 {
562     return widthMediaFeatureEval(value, conversionData, frame, MaxPrefix);
563 }
564
565 static bool min_device_heightMediaFeatureEval(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame* frame, MediaFeaturePrefix)
566 {
567     return device_heightMediaFeatureEval(value, conversionData, frame, MinPrefix);
568 }
569
570 static bool max_device_heightMediaFeatureEval(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame* frame, MediaFeaturePrefix)
571 {
572     return device_heightMediaFeatureEval(value, conversionData, frame, MaxPrefix);
573 }
574
575 static bool min_device_widthMediaFeatureEval(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame* frame, MediaFeaturePrefix)
576 {
577     return device_widthMediaFeatureEval(value, conversionData, frame, MinPrefix);
578 }
579
580 static bool max_device_widthMediaFeatureEval(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame* frame, MediaFeaturePrefix)
581 {
582     return device_widthMediaFeatureEval(value, conversionData, frame, MaxPrefix);
583 }
584
585 static bool min_resolutionMediaFeatureEval(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame* frame, MediaFeaturePrefix)
586 {
587     return resolutionMediaFeatureEval(value, conversionData, frame, MinPrefix);
588 }
589
590 static bool max_resolutionMediaFeatureEval(CSSValue* value, const CSSToLengthConversionData& conversionData, Frame* frame, MediaFeaturePrefix)
591 {
592     return resolutionMediaFeatureEval(value, conversionData, frame, MaxPrefix);
593 }
594
595 static bool animationMediaFeatureEval(CSSValue* value, const CSSToLengthConversionData&, Frame*, MediaFeaturePrefix op)
596 {
597     if (value) {
598         float number;
599         return numberValue(value, number) && compareValue(1, static_cast<int>(number), op);
600     }
601     return true;
602 }
603
604 static bool transitionMediaFeatureEval(CSSValue* value, const CSSToLengthConversionData&, Frame*, MediaFeaturePrefix op)
605 {
606     if (value) {
607         float number;
608         return numberValue(value, number) && compareValue(1, static_cast<int>(number), op);
609     }
610     return true;
611 }
612
613 static bool transform_2dMediaFeatureEval(CSSValue* value, const CSSToLengthConversionData&, Frame*, MediaFeaturePrefix op)
614 {
615     if (value) {
616         float number;
617         return numberValue(value, number) && compareValue(1, static_cast<int>(number), op);
618     }
619     return true;
620 }
621
622 static bool transform_3dMediaFeatureEval(CSSValue* value, const CSSToLengthConversionData&, Frame* frame, MediaFeaturePrefix op)
623 {
624     bool returnValueIfNoParameter;
625     int have3dRendering;
626
627 #if ENABLE(3D_TRANSFORMS)
628     bool threeDEnabled = false;
629     if (RenderView* view = frame->contentRenderer())
630         threeDEnabled = view->compositor().canRender3DTransforms();
631
632     returnValueIfNoParameter = threeDEnabled;
633     have3dRendering = threeDEnabled ? 1 : 0;
634 #else
635     UNUSED_PARAM(frame);
636     returnValueIfNoParameter = false;
637     have3dRendering = 0;
638 #endif
639
640     if (value) {
641         float number;
642         return numberValue(value, number) && compareValue(have3dRendering, static_cast<int>(number), op);
643     }
644     return returnValueIfNoParameter;
645 }
646
647 #if ENABLE(VIEW_MODE_CSS_MEDIA)
648 static bool view_modeMediaFeatureEval(CSSValue* value, const CSSToLengthConversionData&, Frame* frame, MediaFeaturePrefix op)
649 {
650     UNUSED_PARAM(op);
651     if (!value)
652         return true;
653
654     const int viewModeCSSKeywordID = downcast<CSSPrimitiveValue>(*value).getValueID();
655     const Page::ViewMode viewMode = frame->page()->viewMode();
656     bool result = false;
657     switch (viewMode) {
658     case Page::ViewModeWindowed:
659         result = viewModeCSSKeywordID == CSSValueWindowed;
660         break;
661     case Page::ViewModeFloating:
662         result = viewModeCSSKeywordID == CSSValueFloating;
663         break;
664     case Page::ViewModeFullscreen:
665         result = viewModeCSSKeywordID == CSSValueFullscreen;
666         break;
667     case Page::ViewModeMaximized:
668         result = viewModeCSSKeywordID == CSSValueMaximized;
669         break;
670     case Page::ViewModeMinimized:
671         result = viewModeCSSKeywordID == CSSValueMinimized;
672         break;
673     default:
674         result = false;
675         break;
676     }
677
678     return result;
679 }
680 #endif // ENABLE(VIEW_MODE_CSS_MEDIA)
681
682 static bool video_playable_inlineMediaFeatureEval(CSSValue*, const CSSToLengthConversionData&, Frame* frame, MediaFeaturePrefix)
683 {
684     return frame->settings().allowsInlineMediaPlayback();
685 }
686
687 static bool hoverMediaFeatureEval(CSSValue* value, const CSSToLengthConversionData&, Frame*, MediaFeaturePrefix)
688 {
689     if (!is<CSSPrimitiveValue>(value)) {
690 #if ENABLE(TOUCH_EVENTS)
691         return false;
692 #else
693         return true;
694 #endif
695     }
696
697     int hoverCSSKeywordID = downcast<CSSPrimitiveValue>(*value).getValueID();
698 #if ENABLE(TOUCH_EVENTS)
699     return hoverCSSKeywordID == CSSValueNone;
700 #else
701     return hoverCSSKeywordID == CSSValueHover;
702 #endif
703 }
704
705 static bool any_hoverMediaFeatureEval(CSSValue* value, const CSSToLengthConversionData& cssToLengthConversionData, Frame* frame, MediaFeaturePrefix prefix)
706 {
707     return hoverMediaFeatureEval(value, cssToLengthConversionData, frame, prefix);
708 }
709
710 static bool pointerMediaFeatureEval(CSSValue* value, const CSSToLengthConversionData&, Frame*, MediaFeaturePrefix)
711 {
712     if (!is<CSSPrimitiveValue>(value))
713         return true;
714
715     int pointerCSSKeywordID = downcast<CSSPrimitiveValue>(*value).getValueID();
716 #if ENABLE(TOUCH_EVENTS)
717     return pointerCSSKeywordID == CSSValueCoarse;
718 #else
719     return pointerCSSKeywordID == CSSValueFine;
720 #endif
721 }
722
723 static bool any_pointerMediaFeatureEval(CSSValue* value, const CSSToLengthConversionData& cssToLengthConversionData, Frame* frame, MediaFeaturePrefix prefix)
724 {
725     return pointerMediaFeatureEval(value, cssToLengthConversionData, frame, prefix);
726 }
727
728 // FIXME: Remove unnecessary '&' from the following 'ADD_TO_FUNCTIONMAP' definition
729 // once we switch to a non-broken Visual Studio compiler.  https://bugs.webkit.org/show_bug.cgi?id=121235
730 static void createFunctionMap()
731 {
732     // Create the table.
733     gFunctionMap = new FunctionMap;
734 #define ADD_TO_FUNCTIONMAP(name, str)  \
735     gFunctionMap->set(name##MediaFeature.impl(), &name##MediaFeatureEval);
736     CSS_MEDIAQUERY_NAMES_FOR_EACH_MEDIAFEATURE(ADD_TO_FUNCTIONMAP);
737 #undef ADD_TO_FUNCTIONMAP
738 }
739
740 bool MediaQueryEvaluator::eval(const MediaQueryExp* expr) const
741 {
742     if (!m_frame || !m_frame->view() || !m_style)
743         return m_expResult;
744
745     if (!expr->isValid())
746         return false;
747
748     if (!gFunctionMap)
749         createFunctionMap();
750
751     // call the media feature evaluation function. Assume no prefix
752     // and let trampoline functions override the prefix if prefix is
753     // used
754     EvalFunc func = gFunctionMap->get(expr->mediaFeature().impl());
755     if (func) {
756         CSSToLengthConversionData conversionData(m_style.get(),
757             m_frame->document()->documentElement()->renderStyle(),
758             m_frame->document()->renderView(), 1, false);
759         return func(expr->value(), conversionData, m_frame, NoPrefix);
760     }
761
762     return false;
763 }
764
765 } // namespace