1 /*
2 * CSS Media Query Evaluator
3 *
4 * Copyright (C) 2006 Kimmo Kinnunen <kimmo.t.kinnunen@nokia.com>.
5 * Copyright (C) 2013 Apple Inc. All rights reserved.
6 * Copyright (C) 2013 Intel Corporation. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
21 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
25 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include "config.h"
31 #include "core/css/MediaQueryEvaluator.h"
32
33 #include "CSSValueKeywords.h"
34 #include "core/css/CSSAspectRatioValue.h"
35 #include "core/css/CSSHelper.h"
36 #include "core/css/CSSPrimitiveValue.h"
37 #include "core/css/CSSToLengthConversionData.h"
38 #include "core/css/MediaFeatureNames.h"
39 #include "core/css/MediaList.h"
40 #include "core/css/MediaQuery.h"
41 #include "core/css/resolver/MediaQueryResult.h"
42 #include "core/dom/NodeRenderStyle.h"
43 #include "core/inspector/InspectorInstrumentation.h"
44 #include "core/frame/Frame.h"
45 #include "core/frame/FrameView.h"
46 #include "core/page/Page.h"
47 #include "core/frame/Settings.h"
48 #include "core/rendering/RenderLayerCompositor.h"
49 #include "core/rendering/RenderView.h"
50 #include "core/rendering/style/RenderStyle.h"
51 #include "platform/PlatformScreen.h"
52 #include "platform/geometry/FloatRect.h"
53 #include "wtf/HashMap.h"
54
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
MediaQueryEvaluator(bool mediaFeatureResult)65 MediaQueryEvaluator::MediaQueryEvaluator(bool mediaFeatureResult)
66 : m_frame(0)
67 , m_style(0)
68 , m_expResult(mediaFeatureResult)
69 {
70 }
71
MediaQueryEvaluator(const AtomicString & acceptedMediaType,bool mediaFeatureResult)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
MediaQueryEvaluator(const char * acceptedMediaType,bool mediaFeatureResult)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
MediaQueryEvaluator(const AtomicString & acceptedMediaType,Frame * frame,RenderStyle * style)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
~MediaQueryEvaluator()96 MediaQueryEvaluator::~MediaQueryEvaluator()
97 {
98 }
99
mediaTypeMatch(const AtomicString & mediaTypeToMatch) const100 bool MediaQueryEvaluator::mediaTypeMatch(const AtomicString& mediaTypeToMatch) const
101 {
102 return mediaTypeToMatch.isEmpty()
103 || equalIgnoringCase(mediaTypeToMatch, "all")
104 || equalIgnoringCase(mediaTypeToMatch, m_mediaType);
105 }
106
mediaTypeMatchSpecific(const char * mediaTypeToMatch) const107 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
applyRestrictor(MediaQuery::Restrictor r,bool value)116 static bool applyRestrictor(MediaQuery::Restrictor r, bool value)
117 {
118 return r == MediaQuery::Not ? !value : value;
119 }
120
eval(const MediaQuerySet * querySet,MediaQueryResultList * viewportDependentMediaQueryResults) const121 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>
compareValue(T a,T b,MediaFeaturePrefix op)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
compareAspectRatioValue(CSSValue * value,int width,int height,MediaFeaturePrefix op)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
numberValue(CSSValue * value,float & result)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
colorMediaFeatureEval(CSSValue * value,RenderStyle *,Frame * frame,MediaFeaturePrefix op)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
colorIndexMediaFeatureEval(CSSValue * value,RenderStyle *,Frame *,MediaFeaturePrefix op)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
monochromeMediaFeatureEval(CSSValue * value,RenderStyle * style,Frame * frame,MediaFeaturePrefix op)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
viewportSize(FrameView * view)225 static IntSize viewportSize(FrameView* view)
226 {
227 return view->layoutSize(ScrollableArea::IncludeScrollbars);
228 }
229
orientationMediaFeatureEval(CSSValue * value,RenderStyle *,Frame * frame,MediaFeaturePrefix)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
aspectRatioMediaFeatureEval(CSSValue * value,RenderStyle *,Frame * frame,MediaFeaturePrefix op)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
deviceAspectRatioMediaFeatureEval(CSSValue * value,RenderStyle *,Frame * frame,MediaFeaturePrefix op)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
evalResolution(CSSValue * value,Frame * frame,MediaFeaturePrefix op)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
devicePixelRatioMediaFeatureEval(CSSValue * value,RenderStyle *,Frame * frame,MediaFeaturePrefix op)318 static bool devicePixelRatioMediaFeatureEval(CSSValue *value, RenderStyle*, Frame* frame, MediaFeaturePrefix op)
319 {
320 return (!value || toCSSPrimitiveValue(value)->isNumber()) && evalResolution(value, frame, op);
321 }
322
resolutionMediaFeatureEval(CSSValue * value,RenderStyle *,Frame * frame,MediaFeaturePrefix op)323 static bool resolutionMediaFeatureEval(CSSValue* value, RenderStyle*, Frame* frame, MediaFeaturePrefix op)
324 {
325 return (!value || toCSSPrimitiveValue(value)->isResolution()) && evalResolution(value, frame, op);
326 }
327
gridMediaFeatureEval(CSSValue * value,RenderStyle *,Frame *,MediaFeaturePrefix op)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
computeLength(CSSValue * value,bool strict,RenderStyle * initialStyle,int & result)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
deviceHeightMediaFeatureEval(CSSValue * value,RenderStyle * style,Frame * frame,MediaFeaturePrefix op)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
deviceWidthMediaFeatureEval(CSSValue * value,RenderStyle * style,Frame * frame,MediaFeaturePrefix op)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
heightMediaFeatureEval(CSSValue * value,RenderStyle * style,Frame * frame,MediaFeaturePrefix op)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
widthMediaFeatureEval(CSSValue * value,RenderStyle * style,Frame * frame,MediaFeaturePrefix op)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
minColorMediaFeatureEval(CSSValue * value,RenderStyle * style,Frame * frame,MediaFeaturePrefix)424 static bool minColorMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
425 {
426 return colorMediaFeatureEval(value, style, frame, MinPrefix);
427 }
428
maxColorMediaFeatureEval(CSSValue * value,RenderStyle * style,Frame * frame,MediaFeaturePrefix)429 static bool maxColorMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
430 {
431 return colorMediaFeatureEval(value, style, frame, MaxPrefix);
432 }
433
minColorIndexMediaFeatureEval(CSSValue * value,RenderStyle * style,Frame * frame,MediaFeaturePrefix)434 static bool minColorIndexMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
435 {
436 return colorIndexMediaFeatureEval(value, style, frame, MinPrefix);
437 }
438
maxColorIndexMediaFeatureEval(CSSValue * value,RenderStyle * style,Frame * frame,MediaFeaturePrefix)439 static bool maxColorIndexMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
440 {
441 return colorIndexMediaFeatureEval(value, style, frame, MaxPrefix);
442 }
443
minMonochromeMediaFeatureEval(CSSValue * value,RenderStyle * style,Frame * frame,MediaFeaturePrefix)444 static bool minMonochromeMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
445 {
446 return monochromeMediaFeatureEval(value, style, frame, MinPrefix);
447 }
448
maxMonochromeMediaFeatureEval(CSSValue * value,RenderStyle * style,Frame * frame,MediaFeaturePrefix)449 static bool maxMonochromeMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
450 {
451 return monochromeMediaFeatureEval(value, style, frame, MaxPrefix);
452 }
453
minAspectRatioMediaFeatureEval(CSSValue * value,RenderStyle * style,Frame * frame,MediaFeaturePrefix)454 static bool minAspectRatioMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
455 {
456 return aspectRatioMediaFeatureEval(value, style, frame, MinPrefix);
457 }
458
maxAspectRatioMediaFeatureEval(CSSValue * value,RenderStyle * style,Frame * frame,MediaFeaturePrefix)459 static bool maxAspectRatioMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
460 {
461 return aspectRatioMediaFeatureEval(value, style, frame, MaxPrefix);
462 }
463
minDeviceAspectRatioMediaFeatureEval(CSSValue * value,RenderStyle * style,Frame * frame,MediaFeaturePrefix)464 static bool minDeviceAspectRatioMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
465 {
466 return deviceAspectRatioMediaFeatureEval(value, style, frame, MinPrefix);
467 }
468
maxDeviceAspectRatioMediaFeatureEval(CSSValue * value,RenderStyle * style,Frame * frame,MediaFeaturePrefix)469 static bool maxDeviceAspectRatioMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
470 {
471 return deviceAspectRatioMediaFeatureEval(value, style, frame, MaxPrefix);
472 }
473
minDevicePixelRatioMediaFeatureEval(CSSValue * value,RenderStyle * style,Frame * frame,MediaFeaturePrefix)474 static bool minDevicePixelRatioMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
475 {
476 return devicePixelRatioMediaFeatureEval(value, style, frame, MinPrefix);
477 }
478
maxDevicePixelRatioMediaFeatureEval(CSSValue * value,RenderStyle * style,Frame * frame,MediaFeaturePrefix)479 static bool maxDevicePixelRatioMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
480 {
481 return devicePixelRatioMediaFeatureEval(value, style, frame, MaxPrefix);
482 }
483
minHeightMediaFeatureEval(CSSValue * value,RenderStyle * style,Frame * frame,MediaFeaturePrefix)484 static bool minHeightMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
485 {
486 return heightMediaFeatureEval(value, style, frame, MinPrefix);
487 }
488
maxHeightMediaFeatureEval(CSSValue * value,RenderStyle * style,Frame * frame,MediaFeaturePrefix)489 static bool maxHeightMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
490 {
491 return heightMediaFeatureEval(value, style, frame, MaxPrefix);
492 }
493
minWidthMediaFeatureEval(CSSValue * value,RenderStyle * style,Frame * frame,MediaFeaturePrefix)494 static bool minWidthMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
495 {
496 return widthMediaFeatureEval(value, style, frame, MinPrefix);
497 }
498
maxWidthMediaFeatureEval(CSSValue * value,RenderStyle * style,Frame * frame,MediaFeaturePrefix)499 static bool maxWidthMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
500 {
501 return widthMediaFeatureEval(value, style, frame, MaxPrefix);
502 }
503
minDeviceHeightMediaFeatureEval(CSSValue * value,RenderStyle * style,Frame * frame,MediaFeaturePrefix)504 static bool minDeviceHeightMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
505 {
506 return deviceHeightMediaFeatureEval(value, style, frame, MinPrefix);
507 }
508
maxDeviceHeightMediaFeatureEval(CSSValue * value,RenderStyle * style,Frame * frame,MediaFeaturePrefix)509 static bool maxDeviceHeightMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
510 {
511 return deviceHeightMediaFeatureEval(value, style, frame, MaxPrefix);
512 }
513
minDeviceWidthMediaFeatureEval(CSSValue * value,RenderStyle * style,Frame * frame,MediaFeaturePrefix)514 static bool minDeviceWidthMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
515 {
516 return deviceWidthMediaFeatureEval(value, style, frame, MinPrefix);
517 }
518
maxDeviceWidthMediaFeatureEval(CSSValue * value,RenderStyle * style,Frame * frame,MediaFeaturePrefix)519 static bool maxDeviceWidthMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
520 {
521 return deviceWidthMediaFeatureEval(value, style, frame, MaxPrefix);
522 }
523
minResolutionMediaFeatureEval(CSSValue * value,RenderStyle * style,Frame * frame,MediaFeaturePrefix)524 static bool minResolutionMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
525 {
526 return resolutionMediaFeatureEval(value, style, frame, MinPrefix);
527 }
528
maxResolutionMediaFeatureEval(CSSValue * value,RenderStyle * style,Frame * frame,MediaFeaturePrefix)529 static bool maxResolutionMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
530 {
531 return resolutionMediaFeatureEval(value, style, frame, MaxPrefix);
532 }
533
animationMediaFeatureEval(CSSValue * value,RenderStyle *,Frame *,MediaFeaturePrefix op)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
deprecatedTransitionMediaFeatureEval(CSSValue * value,RenderStyle *,Frame * frame,MediaFeaturePrefix op)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
transform2dMediaFeatureEval(CSSValue * value,RenderStyle *,Frame *,MediaFeaturePrefix op)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
transform3dMediaFeatureEval(CSSValue * value,RenderStyle *,Frame * frame,MediaFeaturePrefix op)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
viewModeMediaFeatureEval(CSSValue * value,RenderStyle *,Frame * frame,MediaFeaturePrefix)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
leastCapablePrimaryPointerDeviceType(Frame * frame)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
hoverMediaFeatureEval(CSSValue * value,RenderStyle *,Frame * frame,MediaFeaturePrefix)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
pointerMediaFeatureEval(CSSValue * value,RenderStyle *,Frame * frame,MediaFeaturePrefix)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
scanMediaFeatureEval(CSSValue * value,RenderStyle *,Frame * frame,MediaFeaturePrefix)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
createFunctionMap()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
eval(const MediaQueryExp * expr) const678 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