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