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 * Copyright (C) 2013 Intel Corporation. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
21 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
25 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30#include "config.h"
31#include "core/css/MediaQueryEvaluator.h"
32
33#include "CSSValueKeywords.h"
34#include "core/css/CSSAspectRatioValue.h"
35#include "core/css/CSSHelper.h"
36#include "core/css/CSSPrimitiveValue.h"
37#include "core/css/CSSToLengthConversionData.h"
38#include "core/css/MediaFeatureNames.h"
39#include "core/css/MediaList.h"
40#include "core/css/MediaQuery.h"
41#include "core/css/resolver/MediaQueryResult.h"
42#include "core/dom/NodeRenderStyle.h"
43#include "core/inspector/InspectorInstrumentation.h"
44#include "core/frame/Frame.h"
45#include "core/frame/FrameView.h"
46#include "core/page/Page.h"
47#include "core/frame/Settings.h"
48#include "core/rendering/RenderLayerCompositor.h"
49#include "core/rendering/RenderView.h"
50#include "core/rendering/style/RenderStyle.h"
51#include "platform/PlatformScreen.h"
52#include "platform/geometry/FloatRect.h"
53#include "wtf/HashMap.h"
54
55namespace WebCore {
56
57using namespace MediaFeatureNames;
58
59enum MediaFeaturePrefix { MinPrefix, MaxPrefix, NoPrefix };
60
61typedef bool (*EvalFunc)(CSSValue*, RenderStyle*, Frame*, MediaFeaturePrefix);
62typedef HashMap<StringImpl*, EvalFunc> FunctionMap;
63static FunctionMap* gFunctionMap;
64
65MediaQueryEvaluator::MediaQueryEvaluator(bool mediaFeatureResult)
66    : m_frame(0)
67    , m_style(0)
68    , m_expResult(mediaFeatureResult)
69{
70}
71
72MediaQueryEvaluator::MediaQueryEvaluator(const AtomicString& acceptedMediaType, bool mediaFeatureResult)
73    : m_mediaType(acceptedMediaType)
74    , m_frame(0)
75    , m_style(0)
76    , m_expResult(mediaFeatureResult)
77{
78}
79
80MediaQueryEvaluator::MediaQueryEvaluator(const char* acceptedMediaType, bool mediaFeatureResult)
81    : m_mediaType(acceptedMediaType)
82    , m_frame(0)
83    , m_style(0)
84    , m_expResult(mediaFeatureResult)
85{
86}
87
88MediaQueryEvaluator::MediaQueryEvaluator(const AtomicString& acceptedMediaType, Frame* frame, RenderStyle* style)
89    : m_mediaType(acceptedMediaType)
90    , m_frame(frame)
91    , m_style(style)
92    , m_expResult(false) // Doesn't matter when we have m_frame and m_style.
93{
94}
95
96MediaQueryEvaluator::~MediaQueryEvaluator()
97{
98}
99
100bool MediaQueryEvaluator::mediaTypeMatch(const AtomicString& mediaTypeToMatch) const
101{
102    return mediaTypeToMatch.isEmpty()
103        || equalIgnoringCase(mediaTypeToMatch, "all")
104        || equalIgnoringCase(mediaTypeToMatch, m_mediaType);
105}
106
107bool MediaQueryEvaluator::mediaTypeMatchSpecific(const char* mediaTypeToMatch) const
108{
109    // Like mediaTypeMatch, but without the special cases for "" and "all".
110    ASSERT(mediaTypeToMatch);
111    ASSERT(mediaTypeToMatch[0] != '\0');
112    ASSERT(!equalIgnoringCase(mediaTypeToMatch, AtomicString("all")));
113    return equalIgnoringCase(mediaTypeToMatch, m_mediaType);
114}
115
116static bool applyRestrictor(MediaQuery::Restrictor r, bool value)
117{
118    return r == MediaQuery::Not ? !value : value;
119}
120
121bool MediaQueryEvaluator::eval(const MediaQuerySet* querySet, MediaQueryResultList* viewportDependentMediaQueryResults) const
122{
123    if (!querySet)
124        return true;
125
126    const Vector<OwnPtr<MediaQuery> >& queries = querySet->queryVector();
127    if (!queries.size())
128        return true; // Empty query list evaluates to true.
129
130    // Iterate over queries, stop if any of them eval to true (OR semantics).
131    bool result = false;
132    for (size_t i = 0; i < queries.size() && !result; ++i) {
133        MediaQuery* query = queries[i].get();
134
135        if (mediaTypeMatch(query->mediaType())) {
136            const ExpressionVector& expressions = query->expressions();
137            // Iterate through expressions, stop if any of them eval to false (AND semantics).
138            size_t j = 0;
139            for (; j < expressions.size(); ++j) {
140                bool exprResult = eval(expressions.at(j).get());
141                if (viewportDependentMediaQueryResults && expressions.at(j)->isViewportDependent())
142                    viewportDependentMediaQueryResults->append(adoptRef(new MediaQueryResult(*expressions.at(j), exprResult)));
143                if (!exprResult)
144                    break;
145            }
146
147            // Assume true if we are at the end of the list, otherwise assume false.
148            result = applyRestrictor(query->restrictor(), expressions.size() == j);
149        } else
150            result = applyRestrictor(query->restrictor(), false);
151    }
152
153    return result;
154}
155
156template<typename T>
157bool compareValue(T a, T b, MediaFeaturePrefix op)
158{
159    switch (op) {
160    case MinPrefix:
161        return a >= b;
162    case MaxPrefix:
163        return a <= b;
164    case NoPrefix:
165        return a == b;
166    }
167    return false;
168}
169
170static bool compareAspectRatioValue(CSSValue* value, int width, int height, MediaFeaturePrefix op)
171{
172    if (value->isAspectRatioValue()) {
173        CSSAspectRatioValue* aspectRatio = toCSSAspectRatioValue(value);
174        return compareValue(width * static_cast<int>(aspectRatio->denominatorValue()), height * static_cast<int>(aspectRatio->numeratorValue()), op);
175    }
176
177    return false;
178}
179
180static bool numberValue(CSSValue* value, float& result)
181{
182    if (value->isPrimitiveValue()
183        && toCSSPrimitiveValue(value)->isNumber()) {
184        result = toCSSPrimitiveValue(value)->getFloatValue(CSSPrimitiveValue::CSS_NUMBER);
185        return true;
186    }
187    return false;
188}
189
190static bool colorMediaFeatureEval(CSSValue* value, RenderStyle*, Frame* frame, MediaFeaturePrefix op)
191{
192    int bitsPerComponent = screenDepthPerComponent(frame->view());
193    float number;
194    if (value)
195        return numberValue(value, number) && compareValue(bitsPerComponent, static_cast<int>(number), op);
196
197    return bitsPerComponent != 0;
198}
199
200static bool colorIndexMediaFeatureEval(CSSValue* value, RenderStyle*, Frame*, MediaFeaturePrefix op)
201{
202    // FIXME: We currently assume that we do not support indexed displays, as it is unknown
203    // how to retrieve the information if the display mode is indexed. This matches Firefox.
204    if (!value)
205        return false;
206
207    // Acording to spec, if the device does not use a color lookup table, the value is zero.
208    float number;
209    return numberValue(value, number) && compareValue(0, static_cast<int>(number), op);
210}
211
212static bool monochromeMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix op)
213{
214    if (!screenIsMonochrome(frame->view())) {
215        if (value) {
216            float number;
217            return numberValue(value, number) && compareValue(0, static_cast<int>(number), op);
218        }
219        return false;
220    }
221
222    return colorMediaFeatureEval(value, style, frame, op);
223}
224
225static IntSize viewportSize(FrameView* view)
226{
227    return view->layoutSize(ScrollableArea::IncludeScrollbars);
228}
229
230static bool orientationMediaFeatureEval(CSSValue* value, RenderStyle*, Frame* frame, MediaFeaturePrefix)
231{
232    FrameView* view = frame->view();
233    int width = viewportSize(view).width();
234    int height = viewportSize(view).height();
235    if (value && value->isPrimitiveValue()) {
236        const CSSValueID id = toCSSPrimitiveValue(value)->getValueID();
237        if (width > height) // Square viewport is portrait.
238            return CSSValueLandscape == id;
239        return CSSValuePortrait == id;
240    }
241
242    // Expression (orientation) evaluates to true if width and height >= 0.
243    return height >= 0 && width >= 0;
244}
245
246static bool aspectRatioMediaFeatureEval(CSSValue* value, RenderStyle*, Frame* frame, MediaFeaturePrefix op)
247{
248    if (value) {
249        FrameView* view = frame->view();
250        return compareAspectRatioValue(value, viewportSize(view).width(), viewportSize(view).height(), op);
251    }
252
253    // ({,min-,max-}aspect-ratio)
254    // assume if we have a device, its aspect ratio is non-zero.
255    return true;
256}
257
258static bool deviceAspectRatioMediaFeatureEval(CSSValue* value, RenderStyle*, Frame* frame, MediaFeaturePrefix op)
259{
260    if (value) {
261        FloatRect sg = screenRect(frame->view());
262        return compareAspectRatioValue(value, static_cast<int>(sg.width()), static_cast<int>(sg.height()), op);
263    }
264
265    // ({,min-,max-}device-aspect-ratio)
266    // assume if we have a device, its aspect ratio is non-zero.
267    return true;
268}
269
270static bool evalResolution(CSSValue* value, Frame* frame, MediaFeaturePrefix op)
271{
272    // According to MQ4, only 'screen', 'print' and 'speech' may match.
273    // FIXME: What should speech match? https://www.w3.org/Style/CSS/Tracker/issues/348
274    float actualResolution = 0;
275
276    // This checks the actual media type applied to the document, and we know
277    // this method only got called if this media type matches the one defined
278    // in the query. Thus, if if the document's media type is "print", the
279    // media type of the query will either be "print" or "all".
280    String mediaType = frame->view()->mediaType();
281    if (equalIgnoringCase(mediaType, "screen"))
282        actualResolution = clampTo<float>(frame->devicePixelRatio());
283    else if (equalIgnoringCase(mediaType, "print")) {
284        // The resolution of images while printing should not depend on the DPI
285        // of the screen. Until we support proper ways of querying this info
286        // we use 300px which is considered minimum for current printers.
287        actualResolution = 300 / cssPixelsPerInch;
288    }
289
290    if (!value)
291        return !!actualResolution;
292
293    if (!value->isPrimitiveValue())
294        return false;
295
296    CSSPrimitiveValue* resolution = toCSSPrimitiveValue(value);
297
298    if (resolution->isNumber())
299        return compareValue(actualResolution, resolution->getFloatValue(), op);
300
301    if (!resolution->isResolution())
302        return false;
303
304    if (resolution->isDotsPerCentimeter()) {
305        // To match DPCM to DPPX values, we limit to 2 decimal points.
306        // The http://dev.w3.org/csswg/css3-values/#absolute-lengths recommends
307        // "that the pixel unit refer to the whole number of device pixels that best
308        // approximates the reference pixel". With that in mind, allowing 2 decimal
309        // point precision seems appropriate.
310        return compareValue(
311            floorf(0.5 + 100 * actualResolution) / 100,
312            floorf(0.5 + 100 * resolution->getFloatValue(CSSPrimitiveValue::CSS_DPPX)) / 100, op);
313    }
314
315    return compareValue(actualResolution, resolution->getFloatValue(CSSPrimitiveValue::CSS_DPPX), op);
316}
317
318static bool devicePixelRatioMediaFeatureEval(CSSValue *value, RenderStyle*, Frame* frame, MediaFeaturePrefix op)
319{
320    return (!value || toCSSPrimitiveValue(value)->isNumber()) && evalResolution(value, frame, op);
321}
322
323static bool resolutionMediaFeatureEval(CSSValue* value, RenderStyle*, Frame* frame, MediaFeaturePrefix op)
324{
325    return (!value || toCSSPrimitiveValue(value)->isResolution()) && evalResolution(value, frame, op);
326}
327
328static bool gridMediaFeatureEval(CSSValue* value, RenderStyle*, Frame*, MediaFeaturePrefix op)
329{
330    // if output device is bitmap, grid: 0 == true
331    // assume we have bitmap device
332    float number;
333    if (value && numberValue(value, number))
334        return compareValue(static_cast<int>(number), 0, op);
335    return false;
336}
337
338static bool computeLength(CSSValue* value, bool strict, RenderStyle* initialStyle, int& result)
339{
340    if (!value->isPrimitiveValue())
341        return false;
342
343    CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value);
344
345    if (primitiveValue->isNumber()) {
346        result = primitiveValue->getIntValue();
347        return !strict || !result;
348    }
349
350    if (primitiveValue->isLength()) {
351        // Relative (like EM) and root relative (like REM) units are always resolved against
352        // the initial values for media queries, hence the two initialStyle parameters.
353        result = primitiveValue->computeLength<int>(CSSToLengthConversionData(initialStyle, initialStyle, 1.0 /* zoom */, true /* computingFontSize */));
354        return true;
355    }
356
357    return false;
358}
359
360static bool deviceHeightMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix op)
361{
362    if (value) {
363        int length;
364        if (!computeLength(value, !frame->document()->inQuirksMode(), style, length))
365            return false;
366        int height = static_cast<int>(screenRect(frame->view()).height());
367        if (frame->settings()->reportScreenSizeInPhysicalPixelsQuirk())
368            height = lroundf(height * frame->page()->deviceScaleFactor());
369        return compareValue(height, length, op);
370    }
371    // ({,min-,max-}device-height)
372    // assume if we have a device, assume non-zero
373    return true;
374}
375
376static bool deviceWidthMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix op)
377{
378    if (value) {
379        int length;
380        if (!computeLength(value, !frame->document()->inQuirksMode(), style, length))
381            return false;
382        int width = static_cast<int>(screenRect(frame->view()).width());
383        if (frame->settings()->reportScreenSizeInPhysicalPixelsQuirk())
384            width = lroundf(width * frame->page()->deviceScaleFactor());
385        return compareValue(width, length, op);
386    }
387    // ({,min-,max-}device-width)
388    // assume if we have a device, assume non-zero
389    return true;
390}
391
392static bool heightMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix op)
393{
394    FrameView* view = frame->view();
395
396    int height = viewportSize(view).height();
397    if (value) {
398        if (RenderView* renderView = frame->document()->renderView())
399            height = adjustForAbsoluteZoom(height, renderView);
400        int length;
401        return computeLength(value, !frame->document()->inQuirksMode(), style, length) && compareValue(height, length, op);
402    }
403
404    return height;
405}
406
407static bool widthMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix op)
408{
409    FrameView* view = frame->view();
410
411    int width = viewportSize(view).width();
412    if (value) {
413        if (RenderView* renderView = frame->document()->renderView())
414            width = adjustForAbsoluteZoom(width, renderView);
415        int length;
416        return computeLength(value, !frame->document()->inQuirksMode(), style, length) && compareValue(width, length, op);
417    }
418
419    return width;
420}
421
422// Rest of the functions are trampolines which set the prefix according to the media feature expression used.
423
424static bool minColorMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
425{
426    return colorMediaFeatureEval(value, style, frame, MinPrefix);
427}
428
429static bool maxColorMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
430{
431    return colorMediaFeatureEval(value, style, frame, MaxPrefix);
432}
433
434static bool minColorIndexMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
435{
436    return colorIndexMediaFeatureEval(value, style, frame, MinPrefix);
437}
438
439static bool maxColorIndexMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
440{
441    return colorIndexMediaFeatureEval(value, style, frame, MaxPrefix);
442}
443
444static bool minMonochromeMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
445{
446    return monochromeMediaFeatureEval(value, style, frame, MinPrefix);
447}
448
449static bool maxMonochromeMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
450{
451    return monochromeMediaFeatureEval(value, style, frame, MaxPrefix);
452}
453
454static bool minAspectRatioMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
455{
456    return aspectRatioMediaFeatureEval(value, style, frame, MinPrefix);
457}
458
459static bool maxAspectRatioMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
460{
461    return aspectRatioMediaFeatureEval(value, style, frame, MaxPrefix);
462}
463
464static bool minDeviceAspectRatioMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
465{
466    return deviceAspectRatioMediaFeatureEval(value, style, frame, MinPrefix);
467}
468
469static bool maxDeviceAspectRatioMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
470{
471    return deviceAspectRatioMediaFeatureEval(value, style, frame, MaxPrefix);
472}
473
474static bool minDevicePixelRatioMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
475{
476    return devicePixelRatioMediaFeatureEval(value, style, frame, MinPrefix);
477}
478
479static bool maxDevicePixelRatioMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
480{
481    return devicePixelRatioMediaFeatureEval(value, style, frame, MaxPrefix);
482}
483
484static bool minHeightMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
485{
486    return heightMediaFeatureEval(value, style, frame, MinPrefix);
487}
488
489static bool maxHeightMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
490{
491    return heightMediaFeatureEval(value, style, frame, MaxPrefix);
492}
493
494static bool minWidthMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
495{
496    return widthMediaFeatureEval(value, style, frame, MinPrefix);
497}
498
499static bool maxWidthMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
500{
501    return widthMediaFeatureEval(value, style, frame, MaxPrefix);
502}
503
504static bool minDeviceHeightMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
505{
506    return deviceHeightMediaFeatureEval(value, style, frame, MinPrefix);
507}
508
509static bool maxDeviceHeightMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
510{
511    return deviceHeightMediaFeatureEval(value, style, frame, MaxPrefix);
512}
513
514static bool minDeviceWidthMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
515{
516    return deviceWidthMediaFeatureEval(value, style, frame, MinPrefix);
517}
518
519static bool maxDeviceWidthMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
520{
521    return deviceWidthMediaFeatureEval(value, style, frame, MaxPrefix);
522}
523
524static bool minResolutionMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
525{
526    return resolutionMediaFeatureEval(value, style, frame, MinPrefix);
527}
528
529static bool maxResolutionMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
530{
531    return resolutionMediaFeatureEval(value, style, frame, MaxPrefix);
532}
533
534static bool animationMediaFeatureEval(CSSValue* value, RenderStyle*, Frame*, MediaFeaturePrefix op)
535{
536    if (value) {
537        float number;
538        return numberValue(value, number) && compareValue(1, static_cast<int>(number), op);
539    }
540    return true;
541}
542
543static bool deprecatedTransitionMediaFeatureEval(CSSValue* value, RenderStyle*, Frame* frame, MediaFeaturePrefix op)
544{
545    UseCounter::countDeprecation(frame->document(), UseCounter::PrefixedTransitionMediaFeature);
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 transform2dMediaFeatureEval(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 transform3dMediaFeatureEval(CSSValue* value, RenderStyle*, Frame* frame, MediaFeaturePrefix op)
564{
565    bool returnValueIfNoParameter;
566    int have3dRendering;
567
568    bool threeDEnabled = false;
569    if (RenderView* view = frame->contentRenderer())
570        threeDEnabled = view->compositor()->canRender3DTransforms();
571
572    returnValueIfNoParameter = threeDEnabled;
573    have3dRendering = threeDEnabled ? 1 : 0;
574
575    if (value) {
576        float number;
577        return numberValue(value, number) && compareValue(have3dRendering, static_cast<int>(number), op);
578    }
579    return returnValueIfNoParameter;
580}
581
582static bool viewModeMediaFeatureEval(CSSValue* value, RenderStyle*, Frame* frame, MediaFeaturePrefix)
583{
584    if (!value)
585        return true;
586
587    return toCSSPrimitiveValue(value)->getValueID() == CSSValueWindowed;
588}
589
590enum PointerDeviceType { TouchPointer, MousePointer, NoPointer, UnknownPointer };
591
592static PointerDeviceType leastCapablePrimaryPointerDeviceType(Frame* frame)
593{
594    if (frame->settings()->deviceSupportsTouch())
595        return TouchPointer;
596
597    // FIXME: We should also try to determine if we know we have a mouse.
598    // When we do this, we'll also need to differentiate between known not to
599    // have mouse or touch screen (NoPointer) and unknown (UnknownPointer).
600    // We could also take into account other preferences like accessibility
601    // settings to decide which of the available pointers should be considered
602    // "primary".
603
604    return UnknownPointer;
605}
606
607static bool hoverMediaFeatureEval(CSSValue* value, RenderStyle*, Frame* frame, MediaFeaturePrefix)
608{
609    PointerDeviceType pointer = leastCapablePrimaryPointerDeviceType(frame);
610
611    // If we're on a port that hasn't explicitly opted into providing pointer device information
612    // (or otherwise can't be confident in the pointer hardware available), then behave exactly
613    // as if this feature feature isn't supported.
614    if (pointer == UnknownPointer)
615        return false;
616
617    float number = 1;
618    if (value) {
619        if (!numberValue(value, number))
620            return false;
621    }
622
623    return (pointer == NoPointer && !number)
624        || (pointer == TouchPointer && !number)
625        || (pointer == MousePointer && number == 1);
626}
627
628static bool pointerMediaFeatureEval(CSSValue* value, RenderStyle*, Frame* frame, MediaFeaturePrefix)
629{
630    PointerDeviceType pointer = leastCapablePrimaryPointerDeviceType(frame);
631
632    // If we're on a port that hasn't explicitly opted into providing pointer device information
633    // (or otherwise can't be confident in the pointer hardware available), then behave exactly
634    // as if this feature feature isn't supported.
635    if (pointer == UnknownPointer)
636        return false;
637
638    if (!value)
639        return pointer != NoPointer;
640
641    if (!value->isPrimitiveValue())
642        return false;
643
644    const CSSValueID id = toCSSPrimitiveValue(value)->getValueID();
645    return (pointer == NoPointer && id == CSSValueNone)
646        || (pointer == TouchPointer && id == CSSValueCoarse)
647        || (pointer == MousePointer && id == CSSValueFine);
648}
649
650static bool scanMediaFeatureEval(CSSValue* value, RenderStyle*, Frame* frame, MediaFeaturePrefix)
651{
652    // Scan only applies to 'tv' media.
653    if (!equalIgnoringCase(frame->view()->mediaType(), "tv"))
654        return false;
655
656    if (!value)
657        return true;
658
659    if (!value->isPrimitiveValue())
660        return false;
661
662    // If a platform interface supplies progressive/interlace info for TVs in the
663    // future, it needs to be handled here. For now, assume a modern TV with
664    // progressive display.
665    return toCSSPrimitiveValue(value)->getValueID() == CSSValueProgressive;
666}
667
668static void createFunctionMap()
669{
670    // Create the table.
671    gFunctionMap = new FunctionMap;
672#define ADD_TO_FUNCTIONMAP(name, str)  \
673    gFunctionMap->set(name##MediaFeature.impl(), name##MediaFeatureEval);
674    CSS_MEDIAQUERY_NAMES_FOR_EACH_MEDIAFEATURE(ADD_TO_FUNCTIONMAP);
675#undef ADD_TO_FUNCTIONMAP
676}
677
678bool MediaQueryEvaluator::eval(const MediaQueryExp* expr) const
679{
680    if (!m_frame || !m_style)
681        return m_expResult;
682
683    if (!gFunctionMap)
684        createFunctionMap();
685
686    // Call the media feature evaluation function. Assume no prefix and let
687    // trampoline functions override the prefix if prefix is used.
688    EvalFunc func = gFunctionMap->get(expr->mediaFeature().impl());
689    if (func)
690        return func(expr->value(), m_style.get(), m_frame, NoPrefix);
691
692    return false;
693}
694
695} // namespace
696