summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/dom/ViewportArguments.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/dom/ViewportArguments.cpp')
-rw-r--r--Source/WebCore/dom/ViewportArguments.cpp371
1 files changed, 371 insertions, 0 deletions
diff --git a/Source/WebCore/dom/ViewportArguments.cpp b/Source/WebCore/dom/ViewportArguments.cpp
new file mode 100644
index 0000000..d3026e7
--- /dev/null
+++ b/Source/WebCore/dom/ViewportArguments.cpp
@@ -0,0 +1,371 @@
+/*
+ * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
+ * (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * (C) 2001 Dirk Mueller (mueller@kde.org)
+ * (C) 2006 Alexey Proskuryakov (ap@webkit.org)
+ * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "config.h"
+#include "ViewportArguments.h"
+
+#include "Chrome.h"
+#include "Console.h"
+#include "DOMWindow.h"
+#include "Document.h"
+#include "Frame.h"
+#include "IntSize.h"
+#include "Page.h"
+#include "PlatformString.h"
+#include "ScriptableDocumentParser.h"
+
+using namespace std;
+
+namespace WebCore {
+
+ViewportAttributes computeViewportAttributes(ViewportArguments args, int desktopWidth, int deviceWidth, int deviceHeight, int deviceDPI, IntSize visibleViewport)
+{
+ ViewportAttributes result;
+
+ float availableWidth = visibleViewport.width();
+ float availableHeight = visibleViewport.height();
+
+ ASSERT(availableWidth > 0 && availableHeight > 0);
+
+ switch (int(args.width)) {
+ case ViewportArguments::ValueDesktopWidth:
+ args.width = desktopWidth;
+ break;
+ case ViewportArguments::ValueDeviceWidth:
+ args.width = deviceWidth;
+ break;
+ case ViewportArguments::ValueDeviceHeight:
+ args.width = deviceHeight;
+ break;
+ }
+
+ switch (int(args.height)) {
+ case ViewportArguments::ValueDesktopWidth:
+ args.height = desktopWidth;
+ break;
+ case ViewportArguments::ValueDeviceWidth:
+ args.height = deviceWidth;
+ break;
+ case ViewportArguments::ValueDeviceHeight:
+ args.height = deviceHeight;
+ break;
+ }
+
+ switch (int(args.targetDensityDpi)) {
+ case ViewportArguments::ValueDeviceDPI:
+ args.targetDensityDpi = deviceDPI;
+ break;
+ case ViewportArguments::ValueLowDPI:
+ args.targetDensityDpi = 120;
+ break;
+ case ViewportArguments::ValueAuto:
+ case ViewportArguments::ValueMediumDPI:
+ args.targetDensityDpi = 160;
+ break;
+ case ViewportArguments::ValueHighDPI:
+ args.targetDensityDpi = 240;
+ break;
+ }
+
+ result.devicePixelRatio = float(deviceDPI / args.targetDensityDpi);
+
+ // Resolve non-'auto' width and height to pixel values.
+ if (result.devicePixelRatio != 1.0) {
+ availableWidth /= result.devicePixelRatio;
+ availableHeight /= result.devicePixelRatio;
+ deviceWidth /= result.devicePixelRatio;
+ deviceHeight /= result.devicePixelRatio;
+
+ if (args.width != ViewportArguments::ValueAuto)
+ args.width /= result.devicePixelRatio;
+ if (args.height != ViewportArguments::ValueAuto)
+ args.height /= result.devicePixelRatio;
+ }
+
+ // Clamp values to range defined by spec and resolve minimum-scale and maximum-scale values
+ if (args.width != ViewportArguments::ValueAuto)
+ args.width = min(float(10000), max(args.width, float(1)));
+ if (args.height != ViewportArguments::ValueAuto)
+ args.height = min(float(10000), max(args.height, float(1)));
+
+ if (args.initialScale != ViewportArguments::ValueAuto)
+ args.initialScale = min(float(10), max(args.initialScale, float(0.1)));
+ if (args.minimumScale != ViewportArguments::ValueAuto)
+ args.minimumScale = min(float(10), max(args.minimumScale, float(0.1)));
+ if (args.maximumScale != ViewportArguments::ValueAuto)
+ args.maximumScale = min(float(10), max(args.maximumScale, float(0.1)));
+
+ // Resolve minimum-scale and maximum-scale values according to spec.
+ if (args.minimumScale == ViewportArguments::ValueAuto)
+ result.minimumScale = float(0.25);
+ else
+ result.minimumScale = args.minimumScale;
+
+ if (args.maximumScale == ViewportArguments::ValueAuto) {
+ result.maximumScale = float(5.0);
+ result.minimumScale = min(float(5.0), result.minimumScale);
+ } else
+ result.maximumScale = args.maximumScale;
+ result.maximumScale = max(result.minimumScale, result.maximumScale);
+
+ // Resolve initial-scale value.
+ result.initialScale = args.initialScale;
+ if (result.initialScale == ViewportArguments::ValueAuto) {
+ result.initialScale = availableWidth / desktopWidth;
+ if (args.width != ViewportArguments::ValueAuto)
+ result.initialScale = availableWidth / args.width;
+ if (args.height != ViewportArguments::ValueAuto) {
+ // if 'auto', the initial-scale will be negative here and thus ignored.
+ result.initialScale = max(result.initialScale, availableHeight / args.height);
+ }
+ }
+
+ // Constrain initial-scale value to minimum-scale/maximum-scale range.
+ result.initialScale = min(result.maximumScale, max(result.minimumScale, result.initialScale));
+
+ // Resolve width value.
+ float width;
+ if (args.width != ViewportArguments::ValueAuto)
+ width = args.width;
+ else {
+ if (args.initialScale == ViewportArguments::ValueAuto)
+ width = desktopWidth;
+ else if (args.height != ViewportArguments::ValueAuto)
+ width = args.height * (availableWidth / availableHeight);
+ else
+ width = availableWidth / result.initialScale;
+ }
+
+ // Resolve height value.
+ float height;
+ if (args.height != ViewportArguments::ValueAuto)
+ height = args.height;
+ else
+ height = width * availableHeight / availableWidth;
+
+ // Extend width and height to fill the visual viewport for the resolved initial-scale.
+ width = max(width, availableWidth / result.initialScale);
+ height = max(height, availableHeight / result.initialScale);
+ result.layoutSize.setWidth(width);
+ result.layoutSize.setHeight(height);
+
+ // Update minimum scale factor, to never allow zooming out more than viewport
+ result.minimumScale = max(result.minimumScale, max(availableWidth / width, availableHeight / height));
+
+ result.userScalable = args.userScalable;
+ // Make maximum and minimum scale equal to the initial scale if user is not allowed to zoom in/out.
+ if (!args.userScalable)
+ result.maximumScale = result.minimumScale = result.initialScale;
+
+ return result;
+}
+
+static float findSizeValue(const String& keyString, const String& valueString, Document* document)
+{
+ // 1) Non-negative number values are translated to px lengths.
+ // 2) Negative number values are translated to auto.
+ // 3) device-width and device-height are used as keywords.
+ // 4) Other keywords and unknown values translate to 0.0.
+
+ if (equalIgnoringCase(valueString, "desktop-width"))
+ return ViewportArguments::ValueDesktopWidth;
+ if (equalIgnoringCase(valueString, "device-width"))
+ return ViewportArguments::ValueDeviceWidth;
+ if (equalIgnoringCase(valueString, "device-height"))
+ return ViewportArguments::ValueDeviceHeight;
+
+ bool ok;
+ float value = valueString.toFloat(&ok);
+ if (!ok) {
+ reportViewportWarning(document, UnrecognizedViewportArgumentError, keyString);
+ return float(0.0);
+ }
+
+ if (value < 0)
+ return ViewportArguments::ValueAuto;
+
+ if (keyString == "width")
+ reportViewportWarning(document, DeviceWidthShouldBeUsedWarning, keyString);
+ else if (keyString == "height")
+ reportViewportWarning(document, DeviceHeightShouldBeUsedWarning, keyString);
+
+ return value;
+}
+
+static float findScaleValue(const String& keyString, const String& valueString, Document* document)
+{
+ // 1) Non-negative number values are translated to <number> values.
+ // 2) Negative number values are translated to auto.
+ // 3) yes is translated to 1.0.
+ // 4) device-width and device-height are translated to 10.0.
+ // 5) no and unknown values are translated to 0.0
+
+ if (equalIgnoringCase(valueString, "yes"))
+ return float(1.0);
+ if (equalIgnoringCase(valueString, "no"))
+ return float(0.0);
+ if (equalIgnoringCase(valueString, "desktop-width"))
+ return float(10.0);
+ if (equalIgnoringCase(valueString, "device-width"))
+ return float(10.0);
+ if (equalIgnoringCase(valueString, "device-height"))
+ return float(10.0);
+
+ bool ok;
+ float value = valueString.toFloat(&ok);
+ if (!ok) {
+ reportViewportWarning(document, UnrecognizedViewportArgumentError, keyString);
+ return float(0.0);
+ }
+
+ if (value < 0)
+ return ViewportArguments::ValueAuto;
+
+ if (value > 10.0)
+ reportViewportWarning(document, MaximumScaleTooLargeError, keyString);
+
+ return value;
+}
+
+static bool findUserScalableValue(const String& keyString, const String& valueString, Document* document)
+{
+ // yes and no are used as keywords.
+ // Numbers >= 1, numbers <= -1, device-width and device-height are mapped to yes.
+ // Numbers in the range <-1, 1>, and unknown values, are mapped to no.
+
+ if (equalIgnoringCase(valueString, "yes"))
+ return true;
+ if (equalIgnoringCase(valueString, "no"))
+ return false;
+ if (equalIgnoringCase(valueString, "desktop-width"))
+ return true;
+ if (equalIgnoringCase(valueString, "device-width"))
+ return true;
+ if (equalIgnoringCase(valueString, "device-height"))
+ return true;
+
+ bool ok;
+ float value = valueString.toFloat(&ok);
+ if (!ok) {
+ reportViewportWarning(document, UnrecognizedViewportArgumentError, keyString);
+ return false;
+ }
+
+ if (fabs(value) < 1)
+ return false;
+
+ return true;
+}
+
+static float findTargetDensityDPIValue(const String& keyString, const String& valueString, Document* document)
+{
+ if (equalIgnoringCase(valueString, "device-dpi"))
+ return ViewportArguments::ValueDeviceDPI;
+ if (equalIgnoringCase(valueString, "low-dpi"))
+ return ViewportArguments::ValueLowDPI;
+ if (equalIgnoringCase(valueString, "medium-dpi"))
+ return ViewportArguments::ValueMediumDPI;
+ if (equalIgnoringCase(valueString, "high-dpi"))
+ return ViewportArguments::ValueHighDPI;
+
+ bool ok;
+ float value = valueString.toFloat(&ok);
+ if (!ok) {
+ reportViewportWarning(document, UnrecognizedViewportArgumentError, keyString);
+ return ViewportArguments::ValueAuto;
+ }
+
+ if (value < 70 || value > 400) {
+ reportViewportWarning(document, TargetDensityDpiTooSmallOrLargeError, keyString);
+ return ViewportArguments::ValueAuto;
+ }
+
+ return value;
+}
+
+void setViewportFeature(const String& keyString, const String& valueString, Document* document, void* data)
+{
+ ViewportArguments* arguments = static_cast<ViewportArguments*>(data);
+
+ if (keyString == "width")
+ arguments->width = findSizeValue(keyString, valueString, document);
+ else if (keyString == "height")
+ arguments->height = findSizeValue(keyString, valueString, document);
+ else if (keyString == "initial-scale")
+ arguments->initialScale = findScaleValue(keyString, valueString, document);
+ else if (keyString == "minimum-scale")
+ arguments->minimumScale = findScaleValue(keyString, valueString, document);
+ else if (keyString == "maximum-scale")
+ arguments->maximumScale = findScaleValue(keyString, valueString, document);
+ else if (keyString == "user-scalable")
+ arguments->userScalable = findUserScalableValue(keyString, valueString, document);
+ else if (keyString == "target-densitydpi")
+ arguments->targetDensityDpi = findTargetDensityDPIValue(keyString, valueString, document);
+}
+
+static const char* viewportErrorMessageTemplate(ViewportErrorCode errorCode)
+{
+ static const char* const errors[] = {
+ "Viewport width or height set to physical device width, try using \"device-width\" constant instead for future compatibility.",
+ "Viewport height or height set to physical device height, try using \"device-height\" constant instead for future compatibility.",
+ "Viewport argument \"%replacement\" not recognized. Content ignored.",
+ "Viewport maximum-scale cannot be larger than 10.0. The maximum-scale will be set to 10.0.",
+ "Viewport target-densitydpi has to take a number between 70 and 400 as a valid target dpi, try using \"device-dpi\", \"low-dpi\", \"medium-dpi\" or \"high-dpi\" instead for future compatibility."
+ };
+
+ return errors[errorCode];
+}
+
+static MessageLevel viewportErrorMessageLevel(ViewportErrorCode errorCode)
+{
+ return errorCode == UnrecognizedViewportArgumentError || errorCode == MaximumScaleTooLargeError ? ErrorMessageLevel : TipMessageLevel;
+}
+
+// FIXME: Why is this different from SVGDocumentExtensions parserLineNumber?
+// FIXME: Callers should probably use ScriptController::eventHandlerLineNumber()
+static int parserLineNumber(Document* document)
+{
+ if (!document)
+ return 0;
+ ScriptableDocumentParser* parser = document->scriptableDocumentParser();
+ if (!parser)
+ return 0;
+ return parser->lineNumber() + 1;
+}
+
+void reportViewportWarning(Document* document, ViewportErrorCode errorCode, const String& replacement)
+{
+ Frame* frame = document->frame();
+ if (!frame)
+ return;
+
+ String message = viewportErrorMessageTemplate(errorCode);
+ message.replace("%replacement", replacement);
+
+ frame->domWindow()->console()->addMessage(HTMLMessageSource, LogMessageType, viewportErrorMessageLevel(errorCode), message, parserLineNumber(document), document->url().string());
+}
+
+} // namespace WebCore