summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/css/MediaQueryEvaluator.cpp
diff options
context:
space:
mode:
authorSteve Block <steveblock@google.com>2011-05-06 11:45:16 +0100
committerSteve Block <steveblock@google.com>2011-05-12 13:44:10 +0100
commitcad810f21b803229eb11403f9209855525a25d57 (patch)
tree29a6fd0279be608e0fe9ffe9841f722f0f4e4269 /Source/WebCore/css/MediaQueryEvaluator.cpp
parent121b0cf4517156d0ac5111caf9830c51b69bae8f (diff)
downloadexternal_webkit-cad810f21b803229eb11403f9209855525a25d57.zip
external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.gz
external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.bz2
Merge WebKit at r75315: Initial merge by git.
Change-Id: I570314b346ce101c935ed22a626b48c2af266b84
Diffstat (limited to 'Source/WebCore/css/MediaQueryEvaluator.cpp')
-rw-r--r--Source/WebCore/css/MediaQueryEvaluator.cpp540
1 files changed, 540 insertions, 0 deletions
diff --git a/Source/WebCore/css/MediaQueryEvaluator.cpp b/Source/WebCore/css/MediaQueryEvaluator.cpp
new file mode 100644
index 0000000..fa2e099
--- /dev/null
+++ b/Source/WebCore/css/MediaQueryEvaluator.cpp
@@ -0,0 +1,540 @@
+/*
+ * CSS Media Query Evaluator
+ *
+ * Copyright (C) 2006 Kimmo Kinnunen <kimmo.t.kinnunen@nokia.com>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "MediaQueryEvaluator.h"
+
+#include "Chrome.h"
+#include "ChromeClient.h"
+#include "CSSPrimitiveValue.h"
+#include "CSSStyleSelector.h"
+#include "CSSValueList.h"
+#include "FloatRect.h"
+#include "Frame.h"
+#include "FrameView.h"
+#include "IntRect.h"
+#include "MediaFeatureNames.h"
+#include "MediaList.h"
+#include "MediaQuery.h"
+#include "MediaQueryExp.h"
+#include "NodeRenderStyle.h"
+#include "Page.h"
+#include "RenderView.h"
+#include "RenderStyle.h"
+#include "PlatformScreen.h"
+#include <wtf/HashMap.h>
+
+#if ENABLE(3D_RENDERING) && USE(ACCELERATED_COMPOSITING)
+#include "RenderLayerCompositor.h"
+#endif
+
+namespace WebCore {
+
+using namespace MediaFeatureNames;
+
+enum MediaFeaturePrefix { MinPrefix, MaxPrefix, NoPrefix };
+
+typedef bool (*EvalFunc)(CSSValue*, RenderStyle*, Frame*, MediaFeaturePrefix);
+typedef HashMap<AtomicStringImpl*, EvalFunc> FunctionMap;
+static FunctionMap* gFunctionMap;
+
+/*
+ * FIXME: following media features are not implemented: color_index, scan, resolution
+ *
+ * color_index, min-color-index, max_color_index: It's unknown how to retrieve
+ * the information if the display mode is indexed
+ * scan: The "scan" media feature describes the scanning process of
+ * tv output devices. It's unknown how to retrieve this information from
+ * the platform
+ * resolution, min-resolution, max-resolution: css parser doesn't seem to
+ * support CSS_DIMENSION
+ */
+
+MediaQueryEvaluator::MediaQueryEvaluator(bool mediaFeatureResult)
+ : m_frame(0)
+ , m_style(0)
+ , m_expResult(mediaFeatureResult)
+{
+}
+
+MediaQueryEvaluator:: MediaQueryEvaluator(const String& acceptedMediaType, bool mediaFeatureResult)
+ : m_mediaType(acceptedMediaType)
+ , m_frame(0)
+ , m_style(0)
+ , m_expResult(mediaFeatureResult)
+{
+}
+
+MediaQueryEvaluator:: MediaQueryEvaluator(const char* acceptedMediaType, bool mediaFeatureResult)
+ : m_mediaType(acceptedMediaType)
+ , m_frame(0)
+ , m_style(0)
+ , m_expResult(mediaFeatureResult)
+{
+}
+
+MediaQueryEvaluator:: MediaQueryEvaluator(const String& acceptedMediaType, Frame* frame, RenderStyle* style)
+ : m_mediaType(acceptedMediaType)
+ , m_frame(frame)
+ , m_style(style)
+ , m_expResult(false) // doesn't matter when we have m_frame and m_style
+{
+}
+
+MediaQueryEvaluator::~MediaQueryEvaluator()
+{
+}
+
+bool MediaQueryEvaluator::mediaTypeMatch(const String& mediaTypeToMatch) const
+{
+ return mediaTypeToMatch.isEmpty()
+ || equalIgnoringCase(mediaTypeToMatch, "all")
+ || equalIgnoringCase(mediaTypeToMatch, m_mediaType);
+}
+
+bool MediaQueryEvaluator::mediaTypeMatchSpecific(const char* mediaTypeToMatch) const
+{
+ // Like mediaTypeMatch, but without the special cases for "" and "all".
+ ASSERT(mediaTypeToMatch);
+ ASSERT(mediaTypeToMatch[0] != '\0');
+ ASSERT(!equalIgnoringCase(mediaTypeToMatch, String("all")));
+ return equalIgnoringCase(mediaTypeToMatch, m_mediaType);
+}
+
+static bool applyRestrictor(MediaQuery::Restrictor r, bool value)
+{
+ return r == MediaQuery::Not ? !value : value;
+}
+
+bool MediaQueryEvaluator::eval(const MediaList* mediaList, CSSStyleSelector* styleSelector) const
+{
+ if (!mediaList)
+ return true;
+
+ const Vector<MediaQuery*>& queries = mediaList->mediaQueries();
+ if (!queries.size())
+ return true; // empty query list evaluates to true
+
+ // iterate over queries, stop if any of them eval to true (OR semantics)
+ bool result = false;
+ for (size_t i = 0; i < queries.size() && !result; ++i) {
+ MediaQuery* query = queries.at(i);
+
+ if (query->ignored())
+ continue;
+
+ if (mediaTypeMatch(query->mediaType())) {
+ const Vector<OwnPtr<MediaQueryExp> >* exps = query->expressions();
+ // iterate through expressions, stop if any of them eval to false
+ // (AND semantics)
+ size_t j = 0;
+ for (; j < exps->size(); ++j) {
+ bool exprResult = eval(exps->at(j).get());
+ if (styleSelector && exps->at(j)->isViewportDependent())
+ styleSelector->addViewportDependentMediaQueryResult(exps->at(j).get(), exprResult);
+ if (!exprResult)
+ break;
+ }
+
+ // assume true if we are at the end of the list,
+ // otherwise assume false
+ result = applyRestrictor(query->restrictor(), exps->size() == j);
+ } else
+ result = applyRestrictor(query->restrictor(), false);
+ }
+
+ return result;
+}
+
+static bool parseAspectRatio(CSSValue* value, int& h, int& v)
+{
+ if (value->isValueList()) {
+ CSSValueList* valueList = static_cast<CSSValueList*>(value);
+ if (valueList->length() == 3) {
+ CSSValue* i0 = valueList->itemWithoutBoundsCheck(0);
+ CSSValue* i1 = valueList->itemWithoutBoundsCheck(1);
+ CSSValue* i2 = valueList->itemWithoutBoundsCheck(2);
+ if (i0->isPrimitiveValue() && static_cast<CSSPrimitiveValue*>(i0)->primitiveType() == CSSPrimitiveValue::CSS_NUMBER
+ && i1->isPrimitiveValue() && static_cast<CSSPrimitiveValue*>(i1)->primitiveType() == CSSPrimitiveValue::CSS_STRING
+ && i2->isPrimitiveValue() && static_cast<CSSPrimitiveValue*>(i2)->primitiveType() == CSSPrimitiveValue::CSS_NUMBER) {
+ String str = static_cast<CSSPrimitiveValue*>(i1)->getStringValue();
+ if (!str.isNull() && str.length() == 1 && str[0] == '/') {
+ h = static_cast<CSSPrimitiveValue*>(i0)->getIntValue(CSSPrimitiveValue::CSS_NUMBER);
+ v = static_cast<CSSPrimitiveValue*>(i2)->getIntValue(CSSPrimitiveValue::CSS_NUMBER);
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+template<typename T>
+bool compareValue(T a, T b, MediaFeaturePrefix op)
+{
+ switch (op) {
+ case MinPrefix:
+ return a >= b;
+ case MaxPrefix:
+ return a <= b;
+ case NoPrefix:
+ return a == b;
+ }
+ return false;
+}
+
+static bool numberValue(CSSValue* value, float& result)
+{
+ if (value->isPrimitiveValue()
+ && static_cast<CSSPrimitiveValue*>(value)->primitiveType() == CSSPrimitiveValue::CSS_NUMBER) {
+ result = static_cast<CSSPrimitiveValue*>(value)->getFloatValue(CSSPrimitiveValue::CSS_NUMBER);
+ return true;
+ }
+ return false;
+}
+
+static bool colorMediaFeatureEval(CSSValue* value, RenderStyle*, Frame* frame, MediaFeaturePrefix op)
+{
+ int bitsPerComponent = screenDepthPerComponent(frame->page()->mainFrame()->view());
+ float number;
+ if (value)
+ return numberValue(value, number) && compareValue(bitsPerComponent, static_cast<int>(number), op);
+
+ return bitsPerComponent != 0;
+}
+
+static bool monochromeMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix op)
+{
+ if (!screenIsMonochrome(frame->page()->mainFrame()->view())) {
+ if (value) {
+ float number;
+ return numberValue(value, number) && compareValue(0, static_cast<int>(number), op);
+ }
+ return false;
+ }
+
+ return colorMediaFeatureEval(value, style, frame, op);
+}
+
+static bool orientationMediaFeatureEval(CSSValue* value, RenderStyle*, Frame* frame, MediaFeaturePrefix)
+{
+ // A missing parameter should fail
+ if (!value)
+ return false;
+
+ FrameView* view = frame->view();
+ int width = view->layoutWidth();
+ int height = view->layoutHeight();
+ if (width > height) // Square viewport is portrait
+ return "landscape" == static_cast<CSSPrimitiveValue*>(value)->getStringValue();
+ return "portrait" == static_cast<CSSPrimitiveValue*>(value)->getStringValue();
+}
+
+static bool aspect_ratioMediaFeatureEval(CSSValue* value, RenderStyle*, Frame* frame, MediaFeaturePrefix op)
+{
+ if (value) {
+ FrameView* view = frame->view();
+ int width = view->layoutWidth();
+ int height = view->layoutHeight();
+ int h = 0;
+ int v = 0;
+ if (parseAspectRatio(value, h, v))
+ return v != 0 && compareValue(width * v, height * h, op);
+ return false;
+ }
+
+ // ({,min-,max-}aspect-ratio)
+ // assume if we have a device, its aspect ratio is non-zero
+ return true;
+}
+
+static bool device_aspect_ratioMediaFeatureEval(CSSValue* value, RenderStyle*, Frame* frame, MediaFeaturePrefix op)
+{
+ if (value) {
+ FloatRect sg = screenRect(frame->page()->mainFrame()->view());
+ int h = 0;
+ int v = 0;
+ if (parseAspectRatio(value, h, v))
+ return v != 0 && compareValue(static_cast<int>(sg.width()) * v, static_cast<int>(sg.height()) * h, op);
+ return false;
+ }
+
+ // ({,min-,max-}device-aspect-ratio)
+ // assume if we have a device, its aspect ratio is non-zero
+ return true;
+}
+
+static bool device_pixel_ratioMediaFeatureEval(CSSValue *value, RenderStyle*, Frame* frame, MediaFeaturePrefix op)
+{
+ if (value)
+ return value->isPrimitiveValue() && compareValue(frame->page()->chrome()->scaleFactor(), static_cast<CSSPrimitiveValue*>(value)->getFloatValue(), op);
+
+ return frame->page()->chrome()->scaleFactor() != 0;
+}
+
+static bool gridMediaFeatureEval(CSSValue* value, RenderStyle*, Frame*, MediaFeaturePrefix op)
+{
+ // if output device is bitmap, grid: 0 == true
+ // assume we have bitmap device
+ float number;
+ if (value && numberValue(value, number))
+ return compareValue(static_cast<int>(number), 0, op);
+ return false;
+}
+
+static bool device_heightMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix op)
+{
+ if (value) {
+ FloatRect sg = screenRect(frame->page()->mainFrame()->view());
+ RenderStyle* rootStyle = frame->document()->documentElement()->renderStyle();
+ return value->isPrimitiveValue() && compareValue(static_cast<int>(sg.height()), static_cast<CSSPrimitiveValue*>(value)->computeLengthInt(style, rootStyle), op);
+ }
+ // ({,min-,max-}device-height)
+ // assume if we have a device, assume non-zero
+ return true;
+}
+
+static bool device_widthMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix op)
+{
+ if (value) {
+ FloatRect sg = screenRect(frame->page()->mainFrame()->view());
+ RenderStyle* rootStyle = frame->document()->documentElement()->renderStyle();
+ return value->isPrimitiveValue() && compareValue(static_cast<int>(sg.width()), static_cast<CSSPrimitiveValue*>(value)->computeLengthInt(style, rootStyle), op);
+ }
+ // ({,min-,max-}device-width)
+ // assume if we have a device, assume non-zero
+ return true;
+}
+
+static bool heightMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix op)
+{
+ FrameView* view = frame->view();
+ RenderStyle* rootStyle = frame->document()->documentElement()->renderStyle();
+
+ if (value)
+ return value->isPrimitiveValue() && compareValue(view->layoutHeight(), static_cast<CSSPrimitiveValue*>(value)->computeLengthInt(style, rootStyle), op);
+
+ return view->layoutHeight() != 0;
+}
+
+static bool widthMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix op)
+{
+ FrameView* view = frame->view();
+ RenderStyle* rootStyle = frame->document()->documentElement()->renderStyle();
+
+ if (value)
+ return value->isPrimitiveValue() && compareValue(view->layoutWidth(), static_cast<CSSPrimitiveValue*>(value)->computeLengthInt(style, rootStyle), op);
+
+ return view->layoutWidth() != 0;
+}
+
+// rest of the functions are trampolines which set the prefix according to the media feature expression used
+
+static bool min_colorMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
+{
+ return colorMediaFeatureEval(value, style, frame, MinPrefix);
+}
+
+static bool max_colorMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
+{
+ return colorMediaFeatureEval(value, style, frame, MaxPrefix);
+}
+
+static bool min_monochromeMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
+{
+ return monochromeMediaFeatureEval(value, style, frame, MinPrefix);
+}
+
+static bool max_monochromeMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
+{
+ return monochromeMediaFeatureEval(value, style, frame, MaxPrefix);
+}
+
+static bool min_aspect_ratioMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
+{
+ return aspect_ratioMediaFeatureEval(value, style, frame, MinPrefix);
+}
+
+static bool max_aspect_ratioMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
+{
+ return aspect_ratioMediaFeatureEval(value, style, frame, MaxPrefix);
+}
+
+static bool min_device_aspect_ratioMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
+{
+ return device_aspect_ratioMediaFeatureEval(value, style, frame, MinPrefix);
+}
+
+static bool max_device_aspect_ratioMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
+{
+ return device_aspect_ratioMediaFeatureEval(value, style, frame, MaxPrefix);
+}
+
+static bool min_device_pixel_ratioMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
+{
+ return device_pixel_ratioMediaFeatureEval(value, style, frame, MinPrefix);
+}
+
+static bool max_device_pixel_ratioMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
+{
+ return device_pixel_ratioMediaFeatureEval(value, style, frame, MaxPrefix);
+}
+
+static bool min_heightMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
+{
+ return heightMediaFeatureEval(value, style, frame, MinPrefix);
+}
+
+static bool max_heightMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
+{
+ return heightMediaFeatureEval(value, style, frame, MaxPrefix);
+}
+
+static bool min_widthMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
+{
+ return widthMediaFeatureEval(value, style, frame, MinPrefix);
+}
+
+static bool max_widthMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
+{
+ return widthMediaFeatureEval(value, style, frame, MaxPrefix);
+}
+
+static bool min_device_heightMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
+{
+ return device_heightMediaFeatureEval(value, style, frame, MinPrefix);
+}
+
+static bool max_device_heightMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
+{
+ return device_heightMediaFeatureEval(value, style, frame, MaxPrefix);
+}
+
+static bool min_device_widthMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
+{
+ return device_widthMediaFeatureEval(value, style, frame, MinPrefix);
+}
+
+static bool max_device_widthMediaFeatureEval(CSSValue* value, RenderStyle* style, Frame* frame, MediaFeaturePrefix)
+{
+ return device_widthMediaFeatureEval(value, style, frame, MaxPrefix);
+}
+
+static bool animationMediaFeatureEval(CSSValue* value, RenderStyle*, Frame*, MediaFeaturePrefix op)
+{
+ if (value) {
+ float number;
+ return numberValue(value, number) && compareValue(1, static_cast<int>(number), op);
+ }
+ return true;
+}
+
+static bool transitionMediaFeatureEval(CSSValue* value, RenderStyle*, Frame*, MediaFeaturePrefix op)
+{
+ if (value) {
+ float number;
+ return numberValue(value, number) && compareValue(1, static_cast<int>(number), op);
+ }
+ return true;
+}
+
+static bool transform_2dMediaFeatureEval(CSSValue* value, RenderStyle*, Frame*, MediaFeaturePrefix op)
+{
+ if (value) {
+ float number;
+ return numberValue(value, number) && compareValue(1, static_cast<int>(number), op);
+ }
+ return true;
+}
+
+static bool transform_3dMediaFeatureEval(CSSValue* value, RenderStyle*, Frame* frame, MediaFeaturePrefix op)
+{
+ bool returnValueIfNoParameter;
+ int have3dRendering;
+
+#if ENABLE(3D_RENDERING)
+ bool threeDEnabled = false;
+#if USE(ACCELERATED_COMPOSITING)
+ if (RenderView* view = frame->contentRenderer())
+ threeDEnabled = view->compositor()->canRender3DTransforms();
+#endif
+
+ returnValueIfNoParameter = threeDEnabled;
+ have3dRendering = threeDEnabled ? 1 : 0;
+#else
+ UNUSED_PARAM(frame);
+ returnValueIfNoParameter = false;
+ have3dRendering = 0;
+#endif
+
+ if (value) {
+ float number;
+ return numberValue(value, number) && compareValue(have3dRendering, static_cast<int>(number), op);
+ }
+ return returnValueIfNoParameter;
+}
+
+static bool view_modeMediaFeatureEval(CSSValue* value, RenderStyle*, Frame* frame, MediaFeaturePrefix op)
+{
+ UNUSED_PARAM(op);
+ if (!value)
+ return true;
+ return Page::stringToViewMode(static_cast<CSSPrimitiveValue*>(value)->getStringValue()) == frame->page()->viewMode();
+}
+
+static void createFunctionMap()
+{
+ // Create the table.
+ gFunctionMap = new FunctionMap;
+#define ADD_TO_FUNCTIONMAP(name, str) \
+ gFunctionMap->set(name##MediaFeature.impl(), name##MediaFeatureEval);
+ CSS_MEDIAQUERY_NAMES_FOR_EACH_MEDIAFEATURE(ADD_TO_FUNCTIONMAP);
+#undef ADD_TO_FUNCTIONMAP
+}
+
+bool MediaQueryEvaluator::eval(const MediaQueryExp* expr) const
+{
+ if (!m_frame || !m_style)
+ return m_expResult;
+
+ if (!expr->isValid())
+ return false;
+
+ if (!gFunctionMap)
+ createFunctionMap();
+
+ // call the media feature evaluation function. Assume no prefix
+ // and let trampoline functions override the prefix if prefix is
+ // used
+ EvalFunc func = gFunctionMap->get(expr->mediaFeature().impl());
+ if (func)
+ return func(expr->value(), m_style, m_frame, NoPrefix);
+
+ return false;
+}
+
+} // namespace