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