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