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