Part 2 of removing PlatformString.h, remove PlatformString.h
[WebKit-https.git] / Source / WebCore / dom / ViewportArguments.cpp
1 /*
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  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Library General Public
12  * License as published by the Free Software Foundation; either
13  * version 2 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Library General Public License for more details.
19  *
20  * You should have received a copy of the GNU Library General Public License
21  * along with this library; see the file COPYING.LIB.  If not, write to
22  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23  * Boston, MA 02110-1301, USA.
24  *
25  */
26
27 #include "config.h"
28 #include "ViewportArguments.h"
29
30 #include "Chrome.h"
31 #include "Console.h"
32 #include "DOMWindow.h"
33 #include "Document.h"
34 #include "Frame.h"
35 #include "IntSize.h"
36 #include "Page.h"
37 #include "ScriptableDocumentParser.h"
38 #include <wtf/text/WTFString.h>
39
40 using namespace std;
41
42 namespace WebCore {
43
44 const float ViewportArguments::deprecatedTargetDPI = 160;
45
46 ViewportAttributes computeViewportAttributes(ViewportArguments args, int desktopWidth, int deviceWidth, int deviceHeight, float devicePixelRatio, IntSize visibleViewport)
47 {
48     ViewportAttributes result;
49
50     float availableWidth = visibleViewport.width();
51     float availableHeight = visibleViewport.height();
52
53     ASSERT(availableWidth > 0 && availableHeight > 0);
54
55     result.devicePixelRatio = devicePixelRatio;
56
57     // Resolve non-'auto' width and height to pixel values.
58     if (result.devicePixelRatio != 1.0) {
59         availableWidth /= result.devicePixelRatio;
60         availableHeight /= result.devicePixelRatio;
61         deviceWidth /= result.devicePixelRatio;
62         deviceHeight /= result.devicePixelRatio;
63     }
64
65     switch (int(args.width)) {
66     case ViewportArguments::ValueDesktopWidth:
67         args.width = desktopWidth;
68         break;
69     case ViewportArguments::ValueDeviceWidth:
70         args.width = deviceWidth;
71         break;
72     case ViewportArguments::ValueDeviceHeight:
73         args.width = deviceHeight;
74         break;
75     }
76
77     switch (int(args.height)) {
78     case ViewportArguments::ValueDesktopWidth:
79         args.height = desktopWidth;
80         break;
81     case ViewportArguments::ValueDeviceWidth:
82         args.height = deviceWidth;
83         break;
84     case ViewportArguments::ValueDeviceHeight:
85         args.height = deviceHeight;
86         break;
87     }
88
89     // Clamp values to range defined by spec and resolve minimum-scale and maximum-scale values
90     if (args.width != ViewportArguments::ValueAuto)
91         args.width = min(float(10000), max(args.width, float(1)));
92     if (args.height != ViewportArguments::ValueAuto)
93         args.height = min(float(10000), max(args.height, float(1)));
94
95     if (args.initialScale != ViewportArguments::ValueAuto)
96         args.initialScale = min(float(10), max(args.initialScale, float(0.1)));
97     if (args.minimumScale != ViewportArguments::ValueAuto)
98         args.minimumScale = min(float(10), max(args.minimumScale, float(0.1)));
99     if (args.maximumScale != ViewportArguments::ValueAuto)
100         args.maximumScale = min(float(10), max(args.maximumScale, float(0.1)));
101
102     // Resolve minimum-scale and maximum-scale values according to spec.
103     if (args.minimumScale == ViewportArguments::ValueAuto)
104         result.minimumScale = float(0.25);
105     else
106         result.minimumScale = args.minimumScale;
107
108     if (args.maximumScale == ViewportArguments::ValueAuto) {
109         result.maximumScale = float(5.0);
110         result.minimumScale = min(float(5.0), result.minimumScale);
111     } else
112         result.maximumScale = args.maximumScale;
113     result.maximumScale = max(result.minimumScale, result.maximumScale);
114
115     // Resolve initial-scale value.
116     result.initialScale = args.initialScale;
117     if (result.initialScale == ViewportArguments::ValueAuto) {
118         result.initialScale = availableWidth / desktopWidth;
119         if (args.width != ViewportArguments::ValueAuto)
120             result.initialScale = availableWidth / args.width;
121         if (args.height != ViewportArguments::ValueAuto) {
122             // if 'auto', the initial-scale will be negative here and thus ignored.
123             result.initialScale = max<float>(result.initialScale, availableHeight / args.height);
124         }
125     }
126
127     // Constrain initial-scale value to minimum-scale/maximum-scale range.
128     result.initialScale = min(result.maximumScale, max(result.minimumScale, result.initialScale));
129
130     // Resolve width value.
131     float width;
132     if (args.width != ViewportArguments::ValueAuto)
133         width = args.width;
134     else {
135         if (args.initialScale == ViewportArguments::ValueAuto)
136             width = desktopWidth;
137         else if (args.height != ViewportArguments::ValueAuto)
138             width = args.height * (availableWidth / availableHeight);
139         else
140             width = availableWidth / result.initialScale;
141     }
142
143     // Resolve height value.
144     float height;
145     if (args.height != ViewportArguments::ValueAuto)
146         height = args.height;
147     else
148         height = width * availableHeight / availableWidth;
149
150     // Extend width and height to fill the visual viewport for the resolved initial-scale.
151     width = max<float>(width, availableWidth / result.initialScale);
152     height = max<float>(height, availableHeight / result.initialScale);
153     result.layoutSize.setWidth(width);
154     result.layoutSize.setHeight(height);
155
156     result.userScalable = args.userScalable;
157
158     return result;
159 }
160
161 float computeMinimumScaleFactorForContentContained(const ViewportAttributes& result, const IntSize& viewportSize, const IntSize& contentsSize)
162 {
163     float availableWidth = viewportSize.width();
164     float availableHeight = viewportSize.height();
165
166     if (result.devicePixelRatio != 1.0) {
167         availableWidth /= result.devicePixelRatio;
168         availableHeight /= result.devicePixelRatio;
169     }
170
171     return max<float>(result.minimumScale, max(availableWidth / contentsSize.width(), availableHeight / contentsSize.height()));
172 }
173
174 void restrictMinimumScaleFactorToViewportSize(ViewportAttributes& result, IntSize visibleViewport)
175 {
176     float availableWidth = visibleViewport.width();
177     float availableHeight = visibleViewport.height();
178
179     if (result.devicePixelRatio != 1.0) {
180         availableWidth /= result.devicePixelRatio;
181         availableHeight /= result.devicePixelRatio;
182     }
183
184     result.minimumScale = max<float>(result.minimumScale, max(availableWidth / result.layoutSize.width(), availableHeight / result.layoutSize.height()));
185 }
186
187 void restrictScaleFactorToInitialScaleIfNotUserScalable(ViewportAttributes& result)
188 {
189     if (!result.userScalable)
190         result.maximumScale = result.minimumScale = result.initialScale;
191 }
192
193 static float numericPrefix(const String& keyString, const String& valueString, Document* document, bool* ok = 0)
194 {
195     size_t parsedLength;
196     float value;
197     if (valueString.is8Bit())
198         value = charactersToFloat(valueString.characters8(), valueString.length(), parsedLength);
199     else
200         value = charactersToFloat(valueString.characters16(), valueString.length(), parsedLength);
201     if (!parsedLength) {
202         reportViewportWarning(document, UnrecognizedViewportArgumentValueError, valueString, keyString);
203         if (ok)
204             *ok = false;
205         return 0;
206     }
207     if (parsedLength < valueString.length())
208         reportViewportWarning(document, TruncatedViewportArgumentValueError, valueString, keyString);
209     if (ok)
210         *ok = true;
211     return value;
212 }
213
214 static float findSizeValue(const String& keyString, const String& valueString, Document* document)
215 {
216     // 1) Non-negative number values are translated to px lengths.
217     // 2) Negative number values are translated to auto.
218     // 3) device-width and device-height are used as keywords.
219     // 4) Other keywords and unknown values translate to 0.0.
220
221     if (equalIgnoringCase(valueString, "desktop-width"))
222         return ViewportArguments::ValueDesktopWidth;
223     if (equalIgnoringCase(valueString, "device-width"))
224         return ViewportArguments::ValueDeviceWidth;
225     if (equalIgnoringCase(valueString, "device-height"))
226         return ViewportArguments::ValueDeviceHeight;
227
228     float value = numericPrefix(keyString, valueString, document);
229
230     if (value < 0)
231         return ViewportArguments::ValueAuto;
232
233     return value;
234 }
235
236 static float findScaleValue(const String& keyString, const String& valueString, Document* document)
237 {
238     // 1) Non-negative number values are translated to <number> values.
239     // 2) Negative number values are translated to auto.
240     // 3) yes is translated to 1.0.
241     // 4) device-width and device-height are translated to 10.0.
242     // 5) no and unknown values are translated to 0.0
243
244     if (equalIgnoringCase(valueString, "yes"))
245         return 1;
246     if (equalIgnoringCase(valueString, "no"))
247         return 0;
248     if (equalIgnoringCase(valueString, "desktop-width"))
249         return 10;
250     if (equalIgnoringCase(valueString, "device-width"))
251         return 10;
252     if (equalIgnoringCase(valueString, "device-height"))
253         return 10;
254
255     float value = numericPrefix(keyString, valueString, document);
256
257     if (value < 0)
258         return ViewportArguments::ValueAuto;
259
260     if (value > 10.0)
261         reportViewportWarning(document, MaximumScaleTooLargeError, String(), String());
262
263     return value;
264 }
265
266 static float findUserScalableValue(const String& keyString, const String& valueString, Document* document)
267 {
268     // yes and no are used as keywords.
269     // Numbers >= 1, numbers <= -1, device-width and device-height are mapped to yes.
270     // Numbers in the range <-1, 1>, and unknown values, are mapped to no.
271
272     if (equalIgnoringCase(valueString, "yes"))
273         return 1;
274     if (equalIgnoringCase(valueString, "no"))
275         return 0;
276     if (equalIgnoringCase(valueString, "desktop-width"))
277         return 1;
278     if (equalIgnoringCase(valueString, "device-width"))
279         return 1;
280     if (equalIgnoringCase(valueString, "device-height"))
281         return 1;
282
283     float value = numericPrefix(keyString, valueString, document);
284
285     if (fabs(value) < 1)
286         return 0;
287
288     return 1;
289 }
290
291 void setViewportFeature(const String& keyString, const String& valueString, Document* document, void* data)
292 {
293     ViewportArguments* arguments = static_cast<ViewportArguments*>(data);
294
295     if (keyString == "width")
296         arguments->width = findSizeValue(keyString, valueString, document);
297     else if (keyString == "height")
298         arguments->height = findSizeValue(keyString, valueString, document);
299     else if (keyString == "initial-scale")
300         arguments->initialScale = findScaleValue(keyString, valueString, document);
301     else if (keyString == "minimum-scale")
302         arguments->minimumScale = findScaleValue(keyString, valueString, document);
303     else if (keyString == "maximum-scale")
304         arguments->maximumScale = findScaleValue(keyString, valueString, document);
305     else if (keyString == "user-scalable")
306         arguments->userScalable = findUserScalableValue(keyString, valueString, document);
307     else if (keyString == "target-densitydpi")
308         reportViewportWarning(document, TargetDensityDpiUnsupported, String(), String());
309     else
310         reportViewportWarning(document, UnrecognizedViewportArgumentKeyError, keyString, String());
311 }
312
313 static const char* viewportErrorMessageTemplate(ViewportErrorCode errorCode)
314 {
315     static const char* const errors[] = {
316         "Viewport argument key \"%replacement1\" not recognized and ignored.",
317         "Viewport argument value \"%replacement1\" for key \"%replacement2\" not recognized. Content ignored.",
318         "Viewport argument value \"%replacement1\" for key \"%replacement2\" was truncated to its numeric prefix.",
319         "Viewport maximum-scale cannot be larger than 10.0. The maximum-scale will be set to 10.0.",
320         "Viewport target-densitydpi is not supported.",
321     };
322
323     return errors[errorCode];
324 }
325
326 static MessageLevel viewportErrorMessageLevel(ViewportErrorCode errorCode)
327 {
328     switch (errorCode) {
329     case TruncatedViewportArgumentValueError:
330     case TargetDensityDpiUnsupported:
331         return TipMessageLevel;
332     case UnrecognizedViewportArgumentKeyError:
333     case UnrecognizedViewportArgumentValueError:
334     case MaximumScaleTooLargeError:
335         return ErrorMessageLevel;
336     }
337
338     ASSERT_NOT_REACHED();
339     return ErrorMessageLevel;
340 }
341
342 // FIXME: Why is this different from SVGDocumentExtensions parserLineNumber?
343 // FIXME: Callers should probably use ScriptController::eventHandlerLineNumber()
344 static int parserLineNumber(Document* document)
345 {
346     if (!document)
347         return 0;
348     ScriptableDocumentParser* parser = document->scriptableDocumentParser();
349     if (!parser)
350         return 0;
351     return parser->lineNumber().oneBasedInt();
352 }
353
354 void reportViewportWarning(Document* document, ViewportErrorCode errorCode, const String& replacement1, const String& replacement2)
355 {
356     Frame* frame = document->frame();
357     if (!frame)
358         return;
359
360     String message = viewportErrorMessageTemplate(errorCode);
361     if (!replacement1.isNull())
362         message.replace("%replacement1", replacement1);
363     if (!replacement2.isNull())
364         message.replace("%replacement2", replacement2);
365
366     document->domWindow()->console()->addMessage(HTMLMessageSource, LogMessageType, viewportErrorMessageLevel(errorCode), message, document->url().string(), parserLineNumber(document));
367 }
368
369 } // namespace WebCore