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 "core/CSSValueKeywords.h"
34#include "core/MediaFeatureNames.h"
35#include "core/MediaFeatures.h"
36#include "core/MediaTypeNames.h"
37#include "core/css/CSSAspectRatioValue.h"
38#include "core/css/CSSHelper.h"
39#include "core/css/CSSPrimitiveValue.h"
40#include "core/css/CSSToLengthConversionData.h"
41#include "core/css/MediaList.h"
42#include "core/css/MediaQuery.h"
43#include "core/css/MediaValuesDynamic.h"
44#include "core/css/PointerProperties.h"
45#include "core/css/resolver/MediaQueryResult.h"
46#include "core/dom/NodeRenderStyle.h"
47#include "core/frame/FrameHost.h"
48#include "core/frame/FrameView.h"
49#include "core/frame/LocalFrame.h"
50#include "core/frame/Settings.h"
51#include "core/frame/UseCounter.h"
52#include "core/inspector/InspectorInstrumentation.h"
53#include "core/rendering/RenderView.h"
54#include "core/rendering/compositing/RenderLayerCompositor.h"
55#include "core/rendering/style/RenderStyle.h"
56#include "platform/PlatformScreen.h"
57#include "platform/RuntimeEnabledFeatures.h"
58#include "platform/geometry/FloatRect.h"
59#include "wtf/HashMap.h"
60
61namespace blink {
62
63using namespace MediaFeatureNames;
64
65enum MediaFeaturePrefix { MinPrefix, MaxPrefix, NoPrefix };
66
67typedef bool (*EvalFunc)(const MediaQueryExpValue&, MediaFeaturePrefix, const MediaValues&);
68typedef HashMap<StringImpl*, EvalFunc> FunctionMap;
69static FunctionMap* gFunctionMap;
70
71MediaQueryEvaluator::MediaQueryEvaluator(bool mediaFeatureResult)
72    : m_expectedResult(mediaFeatureResult)
73{
74}
75
76MediaQueryEvaluator::MediaQueryEvaluator(const char* acceptedMediaType, bool mediaFeatureResult)
77    : m_mediaType(acceptedMediaType)
78    , m_expectedResult(mediaFeatureResult)
79{
80}
81
82MediaQueryEvaluator::MediaQueryEvaluator(LocalFrame* frame)
83    : m_expectedResult(false) // Doesn't matter when we have m_frame and m_style.
84    , m_mediaValues(MediaValues::createDynamicIfFrameExists(frame))
85{
86}
87
88MediaQueryEvaluator::MediaQueryEvaluator(const MediaValues& mediaValues)
89    : m_expectedResult(false) // Doesn't matter when we have mediaValues.
90    , m_mediaValues(mediaValues.copy())
91{
92}
93
94MediaQueryEvaluator::~MediaQueryEvaluator()
95{
96}
97
98const String MediaQueryEvaluator::mediaType() const
99{
100    // If a static mediaType was given by the constructor, we use it here.
101    if (!m_mediaType.isEmpty())
102        return m_mediaType;
103    // Otherwise, we get one from mediaValues (which may be dynamic or cached).
104    if (m_mediaValues)
105        return m_mediaValues->mediaType();
106    return nullAtom;
107}
108
109bool MediaQueryEvaluator::mediaTypeMatch(const String& mediaTypeToMatch) const
110{
111    return mediaTypeToMatch.isEmpty()
112        || equalIgnoringCase(mediaTypeToMatch, MediaTypeNames::all)
113        || equalIgnoringCase(mediaTypeToMatch, 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 MediaQuery* query, MediaQueryResultList* viewportDependentMediaQueryResults) const
122{
123    if (!mediaTypeMatch(query->mediaType()))
124        return applyRestrictor(query->restrictor(), false);
125
126    const ExpressionHeapVector& expressions = query->expressions();
127    // Iterate through expressions, stop if any of them eval to false (AND semantics).
128    size_t i = 0;
129    for (; i < expressions.size(); ++i) {
130        bool exprResult = eval(expressions.at(i).get());
131        if (viewportDependentMediaQueryResults && expressions.at(i)->isViewportDependent())
132            viewportDependentMediaQueryResults->append(adoptRefWillBeNoop(new MediaQueryResult(*expressions.at(i), exprResult)));
133        if (!exprResult)
134            break;
135    }
136
137    // Assume true if we are at the end of the list, otherwise assume false.
138    return applyRestrictor(query->restrictor(), expressions.size() == i);
139}
140
141bool MediaQueryEvaluator::eval(const MediaQuerySet* querySet, MediaQueryResultList* viewportDependentMediaQueryResults) const
142{
143    if (!querySet)
144        return true;
145
146    const WillBeHeapVector<OwnPtrWillBeMember<MediaQuery> >& queries = querySet->queryVector();
147    if (!queries.size())
148        return true; // Empty query list evaluates to true.
149
150    // Iterate over queries, stop if any of them eval to true (OR semantics).
151    bool result = false;
152    for (size_t i = 0; i < queries.size() && !result; ++i)
153        result = eval(queries[i].get(), viewportDependentMediaQueryResults);
154
155    return result;
156}
157
158template<typename T>
159bool compareValue(T a, T b, MediaFeaturePrefix op)
160{
161    switch (op) {
162    case MinPrefix:
163        return a >= b;
164    case MaxPrefix:
165        return a <= b;
166    case NoPrefix:
167        return a == b;
168    }
169    return false;
170}
171
172static bool compareAspectRatioValue(const MediaQueryExpValue& value, int width, int height, MediaFeaturePrefix op)
173{
174    if (value.isRatio)
175        return compareValue(width * static_cast<int>(value.denominator), height * static_cast<int>(value.numerator), op);
176
177    return false;
178}
179
180static bool numberValue(const MediaQueryExpValue& value, float& result)
181{
182    if (value.isValue && value.unit == CSSPrimitiveValue::CSS_NUMBER) {
183        result = value.value;
184        return true;
185    }
186    return false;
187}
188
189static bool colorMediaFeatureEval(const MediaQueryExpValue& value, MediaFeaturePrefix op, const MediaValues& mediaValues)
190{
191    float number;
192    int bitsPerComponent = mediaValues.colorBitsPerComponent();
193    if (value.isValid())
194        return numberValue(value, number) && compareValue(bitsPerComponent, static_cast<int>(number), op);
195
196    return bitsPerComponent != 0;
197}
198
199static bool colorIndexMediaFeatureEval(const MediaQueryExpValue& value, MediaFeaturePrefix op, const MediaValues&)
200{
201    // FIXME: We currently assume that we do not support indexed displays, as it is unknown
202    // how to retrieve the information if the display mode is indexed. This matches Firefox.
203    if (!value.isValid())
204        return false;
205
206    // Acording to spec, if the device does not use a color lookup table, the value is zero.
207    float number;
208    return numberValue(value, number) && compareValue(0, static_cast<int>(number), op);
209}
210
211static bool monochromeMediaFeatureEval(const MediaQueryExpValue& value, MediaFeaturePrefix op, const MediaValues& mediaValues)
212{
213    if (!mediaValues.monochromeBitsPerComponent()) {
214        if (value.isValid()) {
215            float number;
216            return numberValue(value, number) && compareValue(0, static_cast<int>(number), op);
217        }
218        return false;
219    }
220
221    return colorMediaFeatureEval(value, op, mediaValues);
222}
223
224static bool orientationMediaFeatureEval(const MediaQueryExpValue& value, MediaFeaturePrefix, const MediaValues& mediaValues)
225{
226    int width = mediaValues.viewportWidth();
227    int height = mediaValues.viewportHeight();
228
229    if (value.isID) {
230        if (width > height) // Square viewport is portrait.
231            return CSSValueLandscape == value.id;
232        return CSSValuePortrait == value.id;
233    }
234
235    // Expression (orientation) evaluates to true if width and height >= 0.
236    return height >= 0 && width >= 0;
237}
238
239static bool aspectRatioMediaFeatureEval(const MediaQueryExpValue& value, MediaFeaturePrefix op, const MediaValues& mediaValues)
240{
241    if (value.isValid())
242        return compareAspectRatioValue(value, mediaValues.viewportWidth(), mediaValues.viewportHeight(), op);
243
244    // ({,min-,max-}aspect-ratio)
245    // assume if we have a device, its aspect ratio is non-zero.
246    return true;
247}
248
249static bool deviceAspectRatioMediaFeatureEval(const MediaQueryExpValue& value, MediaFeaturePrefix op, const MediaValues& mediaValues)
250{
251    if (value.isValid())
252        return compareAspectRatioValue(value, mediaValues.deviceWidth(), mediaValues.deviceHeight(), op);
253
254    // ({,min-,max-}device-aspect-ratio)
255    // assume if we have a device, its aspect ratio is non-zero.
256    return true;
257}
258
259static bool evalResolution(const MediaQueryExpValue& value, MediaFeaturePrefix op, const MediaValues& mediaValues)
260{
261    // According to MQ4, only 'screen', 'print' and 'speech' may match.
262    // FIXME: What should speech match? https://www.w3.org/Style/CSS/Tracker/issues/348
263    float actualResolution = 0;
264
265    // This checks the actual media type applied to the document, and we know
266    // this method only got called if this media type matches the one defined
267    // in the query. Thus, if if the document's media type is "print", the
268    // media type of the query will either be "print" or "all".
269    if (equalIgnoringCase(mediaValues.mediaType(), MediaTypeNames::screen)) {
270        actualResolution = clampTo<float>(mediaValues.devicePixelRatio());
271    } else if (equalIgnoringCase(mediaValues.mediaType(), MediaTypeNames::print)) {
272        // The resolution of images while printing should not depend on the DPI
273        // of the screen. Until we support proper ways of querying this info
274        // we use 300px which is considered minimum for current printers.
275        actualResolution = 300 / cssPixelsPerInch;
276    }
277
278    if (!value.isValid())
279        return !!actualResolution;
280
281    if (!value.isValue)
282        return false;
283
284    if (value.unit == CSSPrimitiveValue::CSS_NUMBER)
285        return compareValue(actualResolution, clampTo<float>(value.value), op);
286
287    if (!CSSPrimitiveValue::isResolution(value.unit))
288        return false;
289
290    double canonicalFactor = CSSPrimitiveValue::conversionToCanonicalUnitsScaleFactor(value.unit);
291    double dppxFactor = CSSPrimitiveValue::conversionToCanonicalUnitsScaleFactor(CSSPrimitiveValue::CSS_DPPX);
292    float valueInDppx = clampTo<float>(value.value * (canonicalFactor / dppxFactor));
293    if (CSSPrimitiveValue::isDotsPerCentimeter(value.unit)) {
294        // To match DPCM to DPPX values, we limit to 2 decimal points.
295        // The http://dev.w3.org/csswg/css3-values/#absolute-lengths recommends
296        // "that the pixel unit refer to the whole number of device pixels that best
297        // approximates the reference pixel". With that in mind, allowing 2 decimal
298        // point precision seems appropriate.
299        return compareValue(
300            floorf(0.5 + 100 * actualResolution) / 100,
301            floorf(0.5 + 100 * valueInDppx) / 100, op);
302    }
303
304    return compareValue(actualResolution, valueInDppx, op);
305}
306
307static bool devicePixelRatioMediaFeatureEval(const MediaQueryExpValue& value, MediaFeaturePrefix op, const MediaValues& mediaValues)
308{
309    UseCounter::count(mediaValues.document(), UseCounter::PrefixedDevicePixelRatioMediaFeature);
310
311    return (!value.isValid() || value.unit == CSSPrimitiveValue::CSS_NUMBER) && evalResolution(value, op, mediaValues);
312}
313
314static bool resolutionMediaFeatureEval(const MediaQueryExpValue& value, MediaFeaturePrefix op, const MediaValues& MediaValues)
315{
316    return (!value.isValid() || CSSPrimitiveValue::isResolution(value.unit)) && evalResolution(value, op, MediaValues);
317}
318
319static bool gridMediaFeatureEval(const MediaQueryExpValue& value, MediaFeaturePrefix op, const MediaValues&)
320{
321    // if output device is bitmap, grid: 0 == true
322    // assume we have bitmap device
323    float number;
324    if (value.isValid() && numberValue(value, number))
325        return compareValue(static_cast<int>(number), 0, op);
326    return false;
327}
328
329static bool computeLength(const MediaQueryExpValue& value, const MediaValues& mediaValues, int& result)
330{
331    if (!value.isValue)
332        return false;
333
334    if (value.unit == CSSPrimitiveValue::CSS_NUMBER) {
335        result = clampTo<int>(value.value);
336        return !mediaValues.strictMode() || !result;
337    }
338
339    if (CSSPrimitiveValue::isLength(value.unit))
340        return mediaValues.computeLength(value.value, value.unit, result);
341    return false;
342}
343
344static bool deviceHeightMediaFeatureEval(const MediaQueryExpValue& value, MediaFeaturePrefix op, const MediaValues& mediaValues)
345{
346    if (value.isValid()) {
347        int length;
348        return computeLength(value, mediaValues, length) && compareValue(static_cast<int>(mediaValues.deviceHeight()), length, op);
349    }
350    // ({,min-,max-}device-height)
351    // assume if we have a device, assume non-zero
352    return true;
353}
354
355static bool deviceWidthMediaFeatureEval(const MediaQueryExpValue& value, MediaFeaturePrefix op, const MediaValues& mediaValues)
356{
357    if (value.isValid()) {
358        int length;
359        return computeLength(value, mediaValues, length) && compareValue(static_cast<int>(mediaValues.deviceWidth()), length, op);
360    }
361    // ({,min-,max-}device-width)
362    // assume if we have a device, assume non-zero
363    return true;
364}
365
366static bool heightMediaFeatureEval(const MediaQueryExpValue& value, MediaFeaturePrefix op, const MediaValues& mediaValues)
367{
368    int height = mediaValues.viewportHeight();
369    if (value.isValid()) {
370        int length;
371        return computeLength(value, mediaValues, length) && compareValue(height, length, op);
372    }
373
374    return height;
375}
376
377static bool widthMediaFeatureEval(const MediaQueryExpValue& value, MediaFeaturePrefix op, const MediaValues& mediaValues)
378{
379    int width = mediaValues.viewportWidth();
380    if (value.isValid()) {
381        int length;
382        return computeLength(value, mediaValues, length) && compareValue(width, length, op);
383    }
384
385    return width;
386}
387
388// Rest of the functions are trampolines which set the prefix according to the media feature expression used.
389
390static bool minColorMediaFeatureEval(const MediaQueryExpValue& value, MediaFeaturePrefix, const MediaValues& mediaValues)
391{
392    return colorMediaFeatureEval(value, MinPrefix, mediaValues);
393}
394
395static bool maxColorMediaFeatureEval(const MediaQueryExpValue& value, MediaFeaturePrefix, const MediaValues& mediaValues)
396{
397    return colorMediaFeatureEval(value, MaxPrefix, mediaValues);
398}
399
400static bool minColorIndexMediaFeatureEval(const MediaQueryExpValue& value, MediaFeaturePrefix, const MediaValues& mediaValues)
401{
402    return colorIndexMediaFeatureEval(value, MinPrefix, mediaValues);
403}
404
405static bool maxColorIndexMediaFeatureEval(const MediaQueryExpValue& value, MediaFeaturePrefix, const MediaValues& mediaValues)
406{
407    return colorIndexMediaFeatureEval(value, MaxPrefix, mediaValues);
408}
409
410static bool minMonochromeMediaFeatureEval(const MediaQueryExpValue& value, MediaFeaturePrefix, const MediaValues& mediaValues)
411{
412    return monochromeMediaFeatureEval(value, MinPrefix, mediaValues);
413}
414
415static bool maxMonochromeMediaFeatureEval(const MediaQueryExpValue& value, MediaFeaturePrefix, const MediaValues& mediaValues)
416{
417    return monochromeMediaFeatureEval(value, MaxPrefix, mediaValues);
418}
419
420static bool minAspectRatioMediaFeatureEval(const MediaQueryExpValue& value, MediaFeaturePrefix, const MediaValues& mediaValues)
421{
422    return aspectRatioMediaFeatureEval(value, MinPrefix, mediaValues);
423}
424
425static bool maxAspectRatioMediaFeatureEval(const MediaQueryExpValue& value, MediaFeaturePrefix, const MediaValues& mediaValues)
426{
427    return aspectRatioMediaFeatureEval(value, MaxPrefix, mediaValues);
428}
429
430static bool minDeviceAspectRatioMediaFeatureEval(const MediaQueryExpValue& value, MediaFeaturePrefix, const MediaValues& mediaValues)
431{
432    return deviceAspectRatioMediaFeatureEval(value, MinPrefix, mediaValues);
433}
434
435static bool maxDeviceAspectRatioMediaFeatureEval(const MediaQueryExpValue& value, MediaFeaturePrefix, const MediaValues& mediaValues)
436{
437    return deviceAspectRatioMediaFeatureEval(value, MaxPrefix, mediaValues);
438}
439
440static bool minDevicePixelRatioMediaFeatureEval(const MediaQueryExpValue& value, MediaFeaturePrefix, const MediaValues& mediaValues)
441{
442    UseCounter::count(mediaValues.document(), UseCounter::PrefixedMinDevicePixelRatioMediaFeature);
443
444    return devicePixelRatioMediaFeatureEval(value, MinPrefix, mediaValues);
445}
446
447static bool maxDevicePixelRatioMediaFeatureEval(const MediaQueryExpValue& value, MediaFeaturePrefix, const MediaValues& mediaValues)
448{
449    UseCounter::count(mediaValues.document(), UseCounter::PrefixedMaxDevicePixelRatioMediaFeature);
450
451    return devicePixelRatioMediaFeatureEval(value, MaxPrefix, mediaValues);
452}
453
454static bool minHeightMediaFeatureEval(const MediaQueryExpValue& value, MediaFeaturePrefix, const MediaValues& mediaValues)
455{
456    return heightMediaFeatureEval(value, MinPrefix, mediaValues);
457}
458
459static bool maxHeightMediaFeatureEval(const MediaQueryExpValue& value, MediaFeaturePrefix, const MediaValues& mediaValues)
460{
461    return heightMediaFeatureEval(value, MaxPrefix, mediaValues);
462}
463
464static bool minWidthMediaFeatureEval(const MediaQueryExpValue& value, MediaFeaturePrefix, const MediaValues& mediaValues)
465{
466    return widthMediaFeatureEval(value, MinPrefix, mediaValues);
467}
468
469static bool maxWidthMediaFeatureEval(const MediaQueryExpValue& value, MediaFeaturePrefix, const MediaValues& mediaValues)
470{
471    return widthMediaFeatureEval(value, MaxPrefix, mediaValues);
472}
473
474static bool minDeviceHeightMediaFeatureEval(const MediaQueryExpValue& value, MediaFeaturePrefix, const MediaValues& mediaValues)
475{
476    return deviceHeightMediaFeatureEval(value, MinPrefix, mediaValues);
477}
478
479static bool maxDeviceHeightMediaFeatureEval(const MediaQueryExpValue& value, MediaFeaturePrefix, const MediaValues& mediaValues)
480{
481    return deviceHeightMediaFeatureEval(value, MaxPrefix, mediaValues);
482}
483
484static bool minDeviceWidthMediaFeatureEval(const MediaQueryExpValue& value, MediaFeaturePrefix, const MediaValues& mediaValues)
485{
486    return deviceWidthMediaFeatureEval(value, MinPrefix, mediaValues);
487}
488
489static bool maxDeviceWidthMediaFeatureEval(const MediaQueryExpValue& value, MediaFeaturePrefix, const MediaValues& mediaValues)
490{
491    return deviceWidthMediaFeatureEval(value, MaxPrefix, mediaValues);
492}
493
494static bool minResolutionMediaFeatureEval(const MediaQueryExpValue& value, MediaFeaturePrefix, const MediaValues& mediaValues)
495{
496    return resolutionMediaFeatureEval(value, MinPrefix, mediaValues);
497}
498
499static bool maxResolutionMediaFeatureEval(const MediaQueryExpValue& value, MediaFeaturePrefix, const MediaValues& mediaValues)
500{
501    return resolutionMediaFeatureEval(value, MaxPrefix, mediaValues);
502}
503
504static bool transform3dMediaFeatureEval(const MediaQueryExpValue& value, MediaFeaturePrefix op, const MediaValues& mediaValues)
505{
506    UseCounter::count(mediaValues.document(), UseCounter::PrefixedTransform3dMediaFeature);
507
508    bool returnValueIfNoParameter;
509    int have3dRendering;
510
511    bool threeDEnabled = mediaValues.threeDEnabled();
512
513    returnValueIfNoParameter = threeDEnabled;
514    have3dRendering = threeDEnabled ? 1 : 0;
515
516    if (value.isValid()) {
517        float number;
518        return numberValue(value, number) && compareValue(have3dRendering, static_cast<int>(number), op);
519    }
520    return returnValueIfNoParameter;
521}
522
523static bool hoverMediaFeatureEval(const MediaQueryExpValue& value, MediaFeaturePrefix, const MediaValues& mediaValues)
524{
525    HoverType hover = mediaValues.primaryHoverType();
526
527    if (RuntimeEnabledFeatures::hoverMediaQueryKeywordsEnabled()) {
528        if (!value.isValid())
529            return hover != HoverTypeNone;
530
531        if (!value.isID)
532            return false;
533
534        return (hover == HoverTypeNone && value.id == CSSValueNone)
535            || (hover == HoverTypeOnDemand && value.id == CSSValueOnDemand)
536            || (hover == HoverTypeHover && value.id == CSSValueHover);
537    } else {
538        float number = 1;
539        if (value.isValid()) {
540            if (!numberValue(value, number))
541                return false;
542        }
543
544        return (hover == HoverTypeNone && !number)
545            || (hover == HoverTypeOnDemand && !number)
546            || (hover == HoverTypeHover && number == 1);
547    }
548}
549
550static bool anyHoverMediaFeatureEval(const MediaQueryExpValue& value, MediaFeaturePrefix, const MediaValues& mediaValues)
551{
552    if (!RuntimeEnabledFeatures::anyPointerMediaQueriesEnabled())
553        return false;
554
555    int availableHoverTypes = mediaValues.availableHoverTypes();
556
557    if (!value.isValid())
558        return availableHoverTypes & ~HoverTypeNone;
559
560    if (!value.isID)
561        return false;
562
563    switch (value.id) {
564    case CSSValueNone:
565        return availableHoverTypes & HoverTypeNone;
566    case CSSValueOnDemand:
567        return availableHoverTypes & HoverTypeOnDemand;
568    case CSSValueHover:
569        return availableHoverTypes & HoverTypeHover;
570    default:
571        ASSERT_NOT_REACHED();
572        return false;
573    }
574}
575
576static bool pointerMediaFeatureEval(const MediaQueryExpValue& value, MediaFeaturePrefix, const MediaValues& mediaValues)
577{
578    PointerType pointer = mediaValues.primaryPointerType();
579
580    if (!value.isValid())
581        return pointer != PointerTypeNone;
582
583    if (!value.isID)
584        return false;
585
586    return (pointer == PointerTypeNone && value.id == CSSValueNone)
587        || (pointer == PointerTypeCoarse && value.id == CSSValueCoarse)
588        || (pointer == PointerTypeFine && value.id == CSSValueFine);
589}
590
591static bool anyPointerMediaFeatureEval(const MediaQueryExpValue& value, MediaFeaturePrefix, const MediaValues& mediaValues)
592{
593    if (!RuntimeEnabledFeatures::anyPointerMediaQueriesEnabled())
594        return false;
595
596    int availablePointers = mediaValues.availablePointerTypes();
597
598    if (!value.isValid())
599        return availablePointers & ~PointerTypeNone;
600
601    if (!value.isID)
602        return false;
603
604    switch (value.id) {
605    case CSSValueCoarse:
606        return availablePointers & PointerTypeCoarse;
607    case CSSValueFine:
608        return availablePointers & PointerTypeFine;
609    case CSSValueNone:
610        return availablePointers & PointerTypeNone;
611    default:
612        ASSERT_NOT_REACHED();
613        return false;
614    }
615}
616
617static bool scanMediaFeatureEval(const MediaQueryExpValue& value, MediaFeaturePrefix, const MediaValues& mediaValues)
618{
619    // Scan only applies to 'tv' media.
620    if (!equalIgnoringCase(mediaValues.mediaType(), MediaTypeNames::tv))
621        return false;
622
623    if (!value.isValid())
624        return true;
625
626    if (!value.isID)
627        return false;
628
629    // If a platform interface supplies progressive/interlace info for TVs in the
630    // future, it needs to be handled here. For now, assume a modern TV with
631    // progressive display.
632    return (value.id == CSSValueProgressive);
633}
634
635static void createFunctionMap()
636{
637    // Create the table.
638    gFunctionMap = new FunctionMap;
639#define ADD_TO_FUNCTIONMAP(name)  \
640    gFunctionMap->set(name##MediaFeature.impl(), name##MediaFeatureEval);
641    CSS_MEDIAQUERY_NAMES_FOR_EACH_MEDIAFEATURE(ADD_TO_FUNCTIONMAP);
642#undef ADD_TO_FUNCTIONMAP
643}
644
645bool MediaQueryEvaluator::eval(const MediaQueryExp* expr) const
646{
647    if (!m_mediaValues || !m_mediaValues->hasValues())
648        return m_expectedResult;
649
650    if (!gFunctionMap)
651        createFunctionMap();
652
653    // Call the media feature evaluation function. Assume no prefix and let
654    // trampoline functions override the prefix if prefix is used.
655    EvalFunc func = gFunctionMap->get(expr->mediaFeature().impl());
656    if (func)
657        return func(expr->expValue(), NoPrefix, *m_mediaValues);
658
659    return false;
660}
661
662} // namespace
663