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