Home | History | Annotate | Download | only in css
      1 /*
      2  * CSS Media Query Evaluator
      3  *
      4  * Copyright (C) 2006 Kimmo Kinnunen <kimmo.t.kinnunen (at) 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 
     55 namespace WebCore {
     56 
     57 using namespace MediaFeatureNames;
     58 
     59 enum MediaFeaturePrefix { MinPrefix, MaxPrefix, NoPrefix };
     60 
     61 typedef bool (*EvalFunc)(CSSValue*, RenderStyle*, Frame*, MediaFeaturePrefix);
     62 typedef HashMap<StringImpl*, EvalFunc> FunctionMap;
     63 static FunctionMap* gFunctionMap;
     64 
     65 MediaQueryEvaluator::MediaQueryEvaluator(bool mediaFeatureResult)
     66     : m_frame(0)
     67     , m_style(0)
     68     , m_expResult(mediaFeatureResult)
     69 {
     70 }
     71 
     72 MediaQueryEvaluator::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 
     80 MediaQueryEvaluator::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 
     88 MediaQueryEvaluator::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 
     96 MediaQueryEvaluator::~MediaQueryEvaluator()
     97 {
     98 }
     99 
    100 bool MediaQueryEvaluator::mediaTypeMatch(const AtomicString& mediaTypeToMatch) const
    101 {
    102     return mediaTypeToMatch.isEmpty()
    103         || equalIgnoringCase(mediaTypeToMatch, "all")
    104         || equalIgnoringCase(mediaTypeToMatch, m_mediaType);
    105 }
    106 
    107 bool 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 
    116 static bool applyRestrictor(MediaQuery::Restrictor r, bool value)
    117 {
    118     return r == MediaQuery::Not ? !value : value;
    119 }
    120 
    121 bool 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 
    156 template<typename T>
    157 bool 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 
    170 static 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 
    180 static 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 
    190 static 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 
    200 static 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 
    212 static 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 
    225 static IntSize viewportSize(FrameView* view)
    226 {
    227     return view->layoutSize(ScrollableArea::IncludeScrollbars);
    228 }
    229 
    230 static 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 
    246 static 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 
    258 static 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 
    270 static 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 
    318 static bool devicePixelRatioMediaFeatureEval(CSSValue *value, RenderStyle*, Frame* frame, MediaFeaturePrefix op)
    319 {
    320     return (!value || toCSSPrimitiveValue(value)->isNumber()) && evalResolution(value, frame, op);
    321 }
    322 
    323 static bool resolutionMediaFeatureEval(CSSValue* value, RenderStyle*, Frame* frame, MediaFeaturePrefix op)
    324 {
    325     return (!value || toCSSPrimitiveValue(value)->isResolution()) && evalResolution(value, frame, op);
    326 }
    327 
    328 static 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 
    338 static 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 
    360 static 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 
    376 static 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 
    392 static 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 
    407 static 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 
    424 static bool minColorMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
    425 {
    426     return colorMediaFeatureEval(value, style, frame, MinPrefix);
    427 }
    428 
    429 static bool maxColorMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
    430 {
    431     return colorMediaFeatureEval(value, style, frame, MaxPrefix);
    432 }
    433 
    434 static bool minColorIndexMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
    435 {
    436     return colorIndexMediaFeatureEval(value, style, frame, MinPrefix);
    437 }
    438 
    439 static bool maxColorIndexMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
    440 {
    441     return colorIndexMediaFeatureEval(value, style, frame, MaxPrefix);
    442 }
    443 
    444 static bool minMonochromeMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
    445 {
    446     return monochromeMediaFeatureEval(value, style, frame, MinPrefix);
    447 }
    448 
    449 static bool maxMonochromeMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
    450 {
    451     return monochromeMediaFeatureEval(value, style, frame, MaxPrefix);
    452 }
    453 
    454 static bool minAspectRatioMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
    455 {
    456     return aspectRatioMediaFeatureEval(value, style, frame, MinPrefix);
    457 }
    458 
    459 static bool maxAspectRatioMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
    460 {
    461     return aspectRatioMediaFeatureEval(value, style, frame, MaxPrefix);
    462 }
    463 
    464 static bool minDeviceAspectRatioMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
    465 {
    466     return deviceAspectRatioMediaFeatureEval(value, style, frame, MinPrefix);
    467 }
    468 
    469 static bool maxDeviceAspectRatioMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
    470 {
    471     return deviceAspectRatioMediaFeatureEval(value, style, frame, MaxPrefix);
    472 }
    473 
    474 static bool minDevicePixelRatioMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
    475 {
    476     return devicePixelRatioMediaFeatureEval(value, style, frame, MinPrefix);
    477 }
    478 
    479 static bool maxDevicePixelRatioMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
    480 {
    481     return devicePixelRatioMediaFeatureEval(value, style, frame, MaxPrefix);
    482 }
    483 
    484 static bool minHeightMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
    485 {
    486     return heightMediaFeatureEval(value, style, frame, MinPrefix);
    487 }
    488 
    489 static bool maxHeightMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
    490 {
    491     return heightMediaFeatureEval(value, style, frame, MaxPrefix);
    492 }
    493 
    494 static bool minWidthMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
    495 {
    496     return widthMediaFeatureEval(value, style, frame, MinPrefix);
    497 }
    498 
    499 static bool maxWidthMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
    500 {
    501     return widthMediaFeatureEval(value, style, frame, MaxPrefix);
    502 }
    503 
    504 static bool minDeviceHeightMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
    505 {
    506     return deviceHeightMediaFeatureEval(value, style, frame, MinPrefix);
    507 }
    508 
    509 static bool maxDeviceHeightMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
    510 {
    511     return deviceHeightMediaFeatureEval(value, style, frame, MaxPrefix);
    512 }
    513 
    514 static bool minDeviceWidthMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
    515 {
    516     return deviceWidthMediaFeatureEval(value, style, frame, MinPrefix);
    517 }
    518 
    519 static bool maxDeviceWidthMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
    520 {
    521     return deviceWidthMediaFeatureEval(value, style, frame, MaxPrefix);
    522 }
    523 
    524 static bool minResolutionMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
    525 {
    526     return resolutionMediaFeatureEval(value, style, frame, MinPrefix);
    527 }
    528 
    529 static bool maxResolutionMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
    530 {
    531     return resolutionMediaFeatureEval(value, style, frame, MaxPrefix);
    532 }
    533 
    534 static 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 
    543 static 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 
    554 static 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 
    563 static 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 
    582 static bool viewModeMediaFeatureEval(CSSValue* value, RenderStyle*, Frame* frame, MediaFeaturePrefix)
    583 {
    584     if (!value)
    585         return true;
    586 
    587     return toCSSPrimitiveValue(value)->getValueID() == CSSValueWindowed;
    588 }
    589 
    590 enum PointerDeviceType { TouchPointer, MousePointer, NoPointer, UnknownPointer };
    591 
    592 static 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 
    607 static 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 
    628 static 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 
    650 static 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 
    668 static 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 
    678 bool 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