2
* Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3
* (C) 1999 Antti Koivisto (koivisto@kde.org)
4
* (C) 2001 Dirk Mueller (mueller@kde.org)
5
* (C) 2006 Alexey Proskuryakov (ap@webkit.org)
6
* Copyright (C) 2004, 2005, 2006, 2007, 2008, 2011 Apple Inc. All rights reserved.
7
* Copyright (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
8
* Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies)
9
* Copyright (C) 2012 Intel Corporation. All rights reserved.
11
* This library is free software; you can redistribute it and/or
12
* modify it under the terms of the GNU Library General Public
13
* License as published by the Free Software Foundation; either
14
* version 2 of the License, or (at your option) any later version.
16
* This library is distributed in the hope that it will be useful,
17
* but WITHOUT ANY WARRANTY; without even the implied warranty of
18
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19
* Library General Public License for more details.
21
* You should have received a copy of the GNU Library General Public License
22
* along with this library; see the file COPYING.LIB. If not, write to
23
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
24
* Boston, MA 02110-1301, USA.
29
#include "ViewportArguments.h"
33
#include "DOMWindow.h"
38
#include "ScriptableDocumentParser.h"
39
#include <wtf/text/WTFString.h>
45
const float ViewportArguments::deprecatedTargetDPI = 160;
47
static const float& compareIgnoringAuto(const float& value1, const float& value2, const float& (*compare) (const float&, const float&))
49
ASSERT(value1 != ViewportArguments::ValueAuto || value2 != ViewportArguments::ValueAuto);
51
if (value1 == ViewportArguments::ValueAuto)
54
if (value2 == ViewportArguments::ValueAuto)
57
return compare(value1, value2);
60
static inline float clampLengthValue(float value)
62
ASSERT(value != ViewportArguments::ValueDeviceWidth);
63
ASSERT(value != ViewportArguments::ValueDeviceHeight);
65
// Limits as defined in the css-device-adapt spec.
66
if (value != ViewportArguments::ValueAuto)
67
return min(float(10000), max(value, float(1)));
71
static inline float clampScaleValue(float value)
73
ASSERT(value != ViewportArguments::ValueDeviceWidth);
74
ASSERT(value != ViewportArguments::ValueDeviceHeight);
76
// Limits as defined in the css-device-adapt spec.
77
if (value != ViewportArguments::ValueAuto)
78
return min(float(10), max(value, float(0.1)));
82
ViewportAttributes ViewportArguments::resolve(const FloatSize& initialViewportSize, const FloatSize& deviceSize, int defaultWidth) const
84
float resultWidth = width;
85
float resultMaxWidth = maxWidth;
86
float resultMinWidth = minWidth;
87
float resultHeight = height;
88
float resultMinHeight = minHeight;
89
float resultMaxHeight = maxHeight;
90
float resultZoom = zoom;
91
float resultMinZoom = minZoom;
92
float resultMaxZoom = maxZoom;
93
float resultUserZoom = userZoom;
95
switch (int(resultWidth)) {
96
case ViewportArguments::ValueDeviceWidth:
97
resultWidth = deviceSize.width();
99
case ViewportArguments::ValueDeviceHeight:
100
resultWidth = deviceSize.height();
104
switch (int(resultHeight)) {
105
case ViewportArguments::ValueDeviceWidth:
106
resultHeight = deviceSize.width();
108
case ViewportArguments::ValueDeviceHeight:
109
resultHeight = deviceSize.height();
113
if (type == ViewportArguments::CSSDeviceAdaptation) {
114
switch (int(resultMinWidth)) {
115
case ViewportArguments::ValueDeviceWidth:
116
resultMinWidth = deviceSize.width();
118
case ViewportArguments::ValueDeviceHeight:
119
resultMinWidth = deviceSize.height();
123
switch (int(resultMaxWidth)) {
124
case ViewportArguments::ValueDeviceWidth:
125
resultMaxWidth = deviceSize.width();
127
case ViewportArguments::ValueDeviceHeight:
128
resultMaxWidth = deviceSize.height();
132
switch (int(resultMinHeight)) {
133
case ViewportArguments::ValueDeviceWidth:
134
resultMinHeight = deviceSize.width();
136
case ViewportArguments::ValueDeviceHeight:
137
resultMinHeight = deviceSize.height();
141
switch (int(resultMaxHeight)) {
142
case ViewportArguments::ValueDeviceWidth:
143
resultMaxHeight = deviceSize.width();
145
case ViewportArguments::ValueDeviceHeight:
146
resultMaxHeight = deviceSize.height();
150
if (resultMinWidth != ViewportArguments::ValueAuto || resultMaxWidth != ViewportArguments::ValueAuto)
151
resultWidth = compareIgnoringAuto(resultMinWidth, compareIgnoringAuto(resultMaxWidth, deviceSize.width(), min), max);
153
if (resultMinHeight != ViewportArguments::ValueAuto || resultMaxHeight != ViewportArguments::ValueAuto)
154
resultHeight = compareIgnoringAuto(resultMinHeight, compareIgnoringAuto(resultMaxHeight, deviceSize.height(), min), max);
156
if (resultMinZoom != ViewportArguments::ValueAuto && resultMaxZoom != ViewportArguments::ValueAuto)
157
resultMaxZoom = max(resultMinZoom, resultMaxZoom);
159
if (resultZoom != ViewportArguments::ValueAuto)
160
resultZoom = compareIgnoringAuto(resultMinZoom, compareIgnoringAuto(resultMaxZoom, resultZoom, min), max);
162
if (resultWidth == ViewportArguments::ValueAuto && resultZoom == ViewportArguments::ValueAuto)
163
resultWidth = deviceSize.width();
165
if (resultWidth == ViewportArguments::ValueAuto && resultHeight == ViewportArguments::ValueAuto)
166
resultWidth = deviceSize.width() / resultZoom;
168
if (resultWidth == ViewportArguments::ValueAuto)
169
resultWidth = resultHeight * deviceSize.width() / deviceSize.height();
171
if (resultHeight == ViewportArguments::ValueAuto)
172
resultHeight = resultWidth * deviceSize.height() / deviceSize.width();
174
if (resultZoom != ViewportArguments::ValueAuto || resultMaxZoom != ViewportArguments::ValueAuto) {
175
resultWidth = compareIgnoringAuto(resultWidth, deviceSize.width() / compareIgnoringAuto(resultZoom, resultMaxZoom, min), max);
176
resultHeight = compareIgnoringAuto(resultHeight, deviceSize.height() / compareIgnoringAuto(resultZoom, resultMaxZoom, min), max);
179
resultWidth = max<float>(1, resultWidth);
180
resultHeight = max<float>(1, resultHeight);
183
if (type != ViewportArguments::CSSDeviceAdaptation && type != ViewportArguments::Implicit) {
184
// Clamp values to a valid range, but not for @viewport since is
185
// not mandated by the specification.
186
resultWidth = clampLengthValue(resultWidth);
187
resultHeight = clampLengthValue(resultHeight);
188
resultZoom = clampScaleValue(resultZoom);
189
resultMinZoom = clampScaleValue(resultMinZoom);
190
resultMaxZoom = clampScaleValue(resultMaxZoom);
193
ViewportAttributes result;
194
result.orientation = orientation;
196
// Resolve minimum-scale and maximum-scale values according to spec.
197
if (resultMinZoom == ViewportArguments::ValueAuto)
198
result.minimumScale = float(0.25);
200
result.minimumScale = resultMinZoom;
202
if (resultMaxZoom == ViewportArguments::ValueAuto) {
203
result.maximumScale = float(5.0);
204
result.minimumScale = min(float(5.0), result.minimumScale);
206
result.maximumScale = resultMaxZoom;
207
result.maximumScale = max(result.minimumScale, result.maximumScale);
209
// Resolve initial-scale value.
210
result.initialScale = resultZoom;
211
if (resultZoom == ViewportArguments::ValueAuto) {
212
result.initialScale = initialViewportSize.width() / defaultWidth;
213
if (resultWidth != ViewportArguments::ValueAuto)
214
result.initialScale = initialViewportSize.width() / resultWidth;
215
if (resultHeight != ViewportArguments::ValueAuto) {
216
// if 'auto', the initial-scale will be negative here and thus ignored.
217
result.initialScale = max<float>(result.initialScale, initialViewportSize.height() / resultHeight);
221
// Constrain initial-scale value to minimum-scale/maximum-scale range.
222
result.initialScale = min(result.maximumScale, max(result.minimumScale, result.initialScale));
224
// Resolve width value.
225
if (resultWidth == ViewportArguments::ValueAuto) {
226
if (resultZoom == ViewportArguments::ValueAuto)
227
resultWidth = defaultWidth;
228
else if (resultHeight != ViewportArguments::ValueAuto)
229
resultWidth = resultHeight * (initialViewportSize.width() / initialViewportSize.height());
231
resultWidth = initialViewportSize.width() / result.initialScale;
234
// Resolve height value.
235
if (resultHeight == ViewportArguments::ValueAuto)
236
resultHeight = resultWidth * (initialViewportSize.height() / initialViewportSize.width());
238
if (type == ViewportArguments::ViewportMeta) {
239
// Extend width and height to fill the visual viewport for the resolved initial-scale.
240
resultWidth = max<float>(resultWidth, initialViewportSize.width() / result.initialScale);
241
resultHeight = max<float>(resultHeight, initialViewportSize.height() / result.initialScale);
244
result.layoutSize.setWidth(resultWidth);
245
result.layoutSize.setHeight(resultHeight);
247
// FIXME: This might affect some ports, but is the right thing to do.
248
// Only set initialScale to a value if it was explicitly set.
249
// if (resultZoom == ViewportArguments::ValueAuto)
250
// result.initialScale = ViewportArguments::ValueAuto;
252
result.userScalable = resultUserZoom;
253
result.orientation = orientation;
258
static FloatSize convertToUserSpace(const FloatSize& deviceSize, float devicePixelRatio)
260
FloatSize result = deviceSize;
261
if (devicePixelRatio != 1)
262
result.scale(1 / devicePixelRatio);
266
ViewportAttributes computeViewportAttributes(ViewportArguments args, int desktopWidth, int deviceWidth, int deviceHeight, float devicePixelRatio, IntSize visibleViewport)
268
FloatSize initialViewportSize = convertToUserSpace(visibleViewport, devicePixelRatio);
269
FloatSize deviceSize = convertToUserSpace(FloatSize(deviceWidth, deviceHeight), devicePixelRatio);
271
return args.resolve(initialViewportSize, deviceSize, desktopWidth);
274
float computeMinimumScaleFactorForContentContained(const ViewportAttributes& result, const IntSize& visibleViewport, const IntSize& contentsSize, float devicePixelRatio)
276
FloatSize viewportSize = convertToUserSpace(visibleViewport, devicePixelRatio);
278
return max<float>(result.minimumScale, max(viewportSize.width() / contentsSize.width(), viewportSize.height() / contentsSize.height()));
281
void restrictMinimumScaleFactorToViewportSize(ViewportAttributes& result, IntSize visibleViewport, float devicePixelRatio)
283
FloatSize viewportSize = convertToUserSpace(visibleViewport, devicePixelRatio);
285
result.minimumScale = max<float>(result.minimumScale, max(viewportSize.width() / result.layoutSize.width(), viewportSize.height() / result.layoutSize.height()));
288
void restrictScaleFactorToInitialScaleIfNotUserScalable(ViewportAttributes& result)
290
if (!result.userScalable)
291
result.maximumScale = result.minimumScale = result.initialScale;
294
static float numericPrefix(const String& keyString, const String& valueString, Document* document, bool* ok = 0)
298
if (valueString.is8Bit())
299
value = charactersToFloat(valueString.characters8(), valueString.length(), parsedLength);
301
value = charactersToFloat(valueString.characters16(), valueString.length(), parsedLength);
303
reportViewportWarning(document, UnrecognizedViewportArgumentValueError, valueString, keyString);
308
if (parsedLength < valueString.length())
309
reportViewportWarning(document, TruncatedViewportArgumentValueError, valueString, keyString);
315
static float findSizeValue(const String& keyString, const String& valueString, Document* document)
317
// 1) Non-negative number values are translated to px lengths.
318
// 2) Negative number values are translated to auto.
319
// 3) device-width and device-height are used as keywords.
320
// 4) Other keywords and unknown values translate to 0.0.
322
if (equalIgnoringCase(valueString, "device-width"))
323
return ViewportArguments::ValueDeviceWidth;
324
if (equalIgnoringCase(valueString, "device-height"))
325
return ViewportArguments::ValueDeviceHeight;
327
float value = numericPrefix(keyString, valueString, document);
330
return ViewportArguments::ValueAuto;
335
static float findScaleValue(const String& keyString, const String& valueString, Document* document)
337
// 1) Non-negative number values are translated to <number> values.
338
// 2) Negative number values are translated to auto.
339
// 3) yes is translated to 1.0.
340
// 4) device-width and device-height are translated to 10.0.
341
// 5) no and unknown values are translated to 0.0
343
if (equalIgnoringCase(valueString, "yes"))
345
if (equalIgnoringCase(valueString, "no"))
347
if (equalIgnoringCase(valueString, "device-width"))
349
if (equalIgnoringCase(valueString, "device-height"))
352
float value = numericPrefix(keyString, valueString, document);
355
return ViewportArguments::ValueAuto;
358
reportViewportWarning(document, MaximumScaleTooLargeError, String(), String());
363
static float findUserScalableValue(const String& keyString, const String& valueString, Document* document)
365
// yes and no are used as keywords.
366
// Numbers >= 1, numbers <= -1, device-width and device-height are mapped to yes.
367
// Numbers in the range <-1, 1>, and unknown values, are mapped to no.
369
if (equalIgnoringCase(valueString, "yes"))
371
if (equalIgnoringCase(valueString, "no"))
373
if (equalIgnoringCase(valueString, "device-width"))
375
if (equalIgnoringCase(valueString, "device-height"))
378
float value = numericPrefix(keyString, valueString, document);
386
void setViewportFeature(const String& keyString, const String& valueString, Document* document, void* data)
388
ViewportArguments* arguments = static_cast<ViewportArguments*>(data);
390
if (keyString == "width")
391
arguments->width = findSizeValue(keyString, valueString, document);
392
else if (keyString == "height")
393
arguments->height = findSizeValue(keyString, valueString, document);
394
else if (keyString == "initial-scale")
395
arguments->zoom = findScaleValue(keyString, valueString, document);
396
else if (keyString == "minimum-scale")
397
arguments->minZoom = findScaleValue(keyString, valueString, document);
398
else if (keyString == "maximum-scale")
399
arguments->maxZoom = findScaleValue(keyString, valueString, document);
400
else if (keyString == "user-scalable")
401
arguments->userZoom = findUserScalableValue(keyString, valueString, document);
402
else if (keyString == "target-densitydpi")
403
reportViewportWarning(document, TargetDensityDpiUnsupported, String(), String());
405
reportViewportWarning(document, UnrecognizedViewportArgumentKeyError, keyString, String());
408
static const char* viewportErrorMessageTemplate(ViewportErrorCode errorCode)
410
static const char* const errors[] = {
411
"Viewport argument key \"%replacement1\" not recognized and ignored.",
412
"Viewport argument value \"%replacement1\" for key \"%replacement2\" not recognized. Content ignored.",
413
"Viewport argument value \"%replacement1\" for key \"%replacement2\" was truncated to its numeric prefix.",
414
"Viewport maximum-scale cannot be larger than 10.0. The maximum-scale will be set to 10.0.",
415
"Viewport target-densitydpi is not supported.",
418
return errors[errorCode];
421
static MessageLevel viewportErrorMessageLevel(ViewportErrorCode errorCode)
424
case TruncatedViewportArgumentValueError:
425
case TargetDensityDpiUnsupported:
426
return TipMessageLevel;
427
case UnrecognizedViewportArgumentKeyError:
428
case UnrecognizedViewportArgumentValueError:
429
case MaximumScaleTooLargeError:
430
return ErrorMessageLevel;
433
ASSERT_NOT_REACHED();
434
return ErrorMessageLevel;
437
// FIXME: Why is this different from SVGDocumentExtensions parserLineNumber?
438
// FIXME: Callers should probably use ScriptController::eventHandlerLineNumber()
439
static int parserLineNumber(Document* document)
443
ScriptableDocumentParser* parser = document->scriptableDocumentParser();
446
return parser->lineNumber().oneBasedInt();
449
void reportViewportWarning(Document* document, ViewportErrorCode errorCode, const String& replacement1, const String& replacement2)
451
Frame* frame = document->frame();
455
String message = viewportErrorMessageTemplate(errorCode);
456
if (!replacement1.isNull())
457
message.replace("%replacement1", replacement1);
458
if (!replacement2.isNull())
459
message.replace("%replacement2", replacement2);
461
if ((errorCode == UnrecognizedViewportArgumentValueError || errorCode == TruncatedViewportArgumentValueError) && replacement1.find(';') != WTF::notFound)
462
message.append(" Note that ';' is not a separator in viewport values. The list should be comma-separated.");
464
document->domWindow()->console()->addMessage(HTMLMessageSource, LogMessageType, viewportErrorMessageLevel(errorCode), message, document->url().string(), parserLineNumber(document));
467
} // namespace WebCore