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