Part 2 of removing PlatformString.h, remove PlatformString.h
[WebKit-https.git] / Source / WebCore / page / WindowFeatures.cpp
1 /*
2  *  Copyright (C) 2000 Harri Porten (porten@kde.org)
3  *  Copyright (C) 2006 Jon Shier (jshier@iastate.edu)
4  *  Copyright (C) 2003, 2004, 2005, 2006, 2007, 2010 Apple Inc. All rights reseved.
5  *  Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org)
6  *
7  *  This library is free software; you can redistribute it and/or
8  *  modify it under the terms of the GNU Lesser General Public
9  *  License as published by the Free Software Foundation; either
10  *  version 2 of the License, or (at your option) any later version.
11  *
12  *  This library is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  *  Lesser General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Lesser General Public
18  *  License along with this library; if not, write to the Free Software
19  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
20  *  USA
21  */
22
23 #include "config.h"
24 #include "WindowFeatures.h"
25
26 #include "FloatRect.h"
27 #include <wtf/Assertions.h>
28 #include <wtf/MathExtras.h>
29 #include <wtf/text/StringHash.h>
30
31 namespace WebCore {
32
33 // Though isspace() considers \t and \v to be whitespace, Win IE doesn't when parsing window features.
34 static bool isWindowFeaturesSeparator(UChar c)
35 {
36     return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '=' || c == ',' || c == '\0';
37 }
38
39 WindowFeatures::WindowFeatures(const String& features)
40     : xSet(false)
41     , ySet(false)
42     , widthSet(false)
43     , heightSet(false)
44     , fullscreen(false)
45     , dialog(false)
46 {
47     /*
48      The IE rule is: all features except for channelmode and fullscreen default to YES, but
49      if the user specifies a feature string, all features default to NO. (There is no public
50      standard that applies to this method.)
51
52      <http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/open_0.asp>
53      We always allow a window to be resized, which is consistent with Firefox.
54      */
55
56     if (features.length() == 0) {
57         menuBarVisible = true;
58         statusBarVisible = true;
59         toolBarVisible = true;
60         locationBarVisible = true;
61         scrollbarsVisible = true;
62         resizable = true;
63         return;
64     }
65
66     menuBarVisible = false;
67     statusBarVisible = false;
68     toolBarVisible = false;
69     locationBarVisible = false;
70     scrollbarsVisible = false;
71     resizable = true;
72
73     // Tread lightly in this code -- it was specifically designed to mimic Win IE's parsing behavior.
74     int keyBegin, keyEnd;
75     int valueBegin, valueEnd;
76
77     int i = 0;
78     int length = features.length();
79     String buffer = features.lower();
80     while (i < length) {
81         // skip to first non-separator, but don't skip past the end of the string
82         while (isWindowFeaturesSeparator(buffer[i])) {
83             if (i >= length)
84                 break;
85             i++;
86         }
87         keyBegin = i;
88
89         // skip to first separator
90         while (!isWindowFeaturesSeparator(buffer[i]))
91             i++;
92         keyEnd = i;
93
94         // skip to first '=', but don't skip past a ',' or the end of the string
95         while (buffer[i] != '=') {
96             if (buffer[i] == ',' || i >= length)
97                 break;
98             i++;
99         }
100
101         // skip to first non-separator, but don't skip past a ',' or the end of the string
102         while (isWindowFeaturesSeparator(buffer[i])) {
103             if (buffer[i] == ',' || i >= length)
104                 break;
105             i++;
106         }
107         valueBegin = i;
108
109         // skip to first separator
110         while (!isWindowFeaturesSeparator(buffer[i]))
111             i++;
112         valueEnd = i;
113
114         ASSERT(i <= length);
115
116         String keyString(buffer.substring(keyBegin, keyEnd - keyBegin));
117         String valueString(buffer.substring(valueBegin, valueEnd - valueBegin));
118         setWindowFeature(keyString, valueString);
119     }
120 }
121
122 void WindowFeatures::setWindowFeature(const String& keyString, const String& valueString)
123 {
124     int value;
125
126     // Listing a key with no value is shorthand for key=yes
127     if (valueString.isEmpty() || valueString == "yes")
128         value = 1;
129     else
130         value = valueString.toInt();
131
132     // We treat keyString of "resizable" here as an additional feature rather than setting resizeable to true.
133     // This is consistent with Firefox, but could also be handled at another level.
134
135     if (keyString == "left" || keyString == "screenx") {
136         xSet = true;
137         x = value;
138     } else if (keyString == "top" || keyString == "screeny") {
139         ySet = true;
140         y = value;
141     } else if (keyString == "width" || keyString == "innerwidth") {
142         widthSet = true;
143         width = value;
144     } else if (keyString == "height" || keyString == "innerheight") {
145         heightSet = true;
146         height = value;
147     } else if (keyString == "menubar")
148         menuBarVisible = value;
149     else if (keyString == "toolbar")
150         toolBarVisible = value;
151     else if (keyString == "location")
152         locationBarVisible = value;
153     else if (keyString == "status")
154         statusBarVisible = value;
155     else if (keyString == "fullscreen")
156         fullscreen = value;
157     else if (keyString == "scrollbars")
158         scrollbarsVisible = value;
159     else if (value == 1)
160         additionalFeatures.append(keyString);
161 }
162
163 WindowFeatures::WindowFeatures(const String& dialogFeaturesString, const FloatRect& screenAvailableRect)
164     : widthSet(true)
165     , heightSet(true)
166     , menuBarVisible(false)
167     , toolBarVisible(false)
168     , locationBarVisible(false)
169     , fullscreen(false)
170     , dialog(true)
171 {
172     DialogFeaturesMap features;
173     parseDialogFeatures(dialogFeaturesString, features);
174
175     const bool trusted = false;
176
177     // The following features from Microsoft's documentation are not implemented:
178     // - default font settings
179     // - width, height, left, and top specified in units other than "px"
180     // - edge (sunken or raised, default is raised)
181     // - dialogHide: trusted && boolFeature(features, "dialoghide"), makes dialog hide when you print
182     // - help: boolFeature(features, "help", true), makes help icon appear in dialog (what does it do on Windows?)
183     // - unadorned: trusted && boolFeature(features, "unadorned");
184
185     width = floatFeature(features, "dialogwidth", 100, screenAvailableRect.width(), 620); // default here came from frame size of dialog in MacIE
186     height = floatFeature(features, "dialogheight", 100, screenAvailableRect.height(), 450); // default here came from frame size of dialog in MacIE
187
188     x = floatFeature(features, "dialogleft", screenAvailableRect.x(), screenAvailableRect.maxX() - width, -1);
189     xSet = x > 0;
190     y = floatFeature(features, "dialogtop", screenAvailableRect.y(), screenAvailableRect.maxY() - height, -1);
191     ySet = y > 0;
192
193     if (boolFeature(features, "center", true)) {
194         if (!xSet) {
195             x = screenAvailableRect.x() + (screenAvailableRect.width() - width) / 2;
196             xSet = true;
197         }
198         if (!ySet) {
199             y = screenAvailableRect.y() + (screenAvailableRect.height() - height) / 2;
200             ySet = true;
201         }
202     }
203
204     resizable = boolFeature(features, "resizable");
205     scrollbarsVisible = boolFeature(features, "scroll", true);
206     statusBarVisible = boolFeature(features, "status", !trusted);
207 }
208
209 bool WindowFeatures::boolFeature(const DialogFeaturesMap& features, const char* key, bool defaultValue)
210 {
211     DialogFeaturesMap::const_iterator it = features.find(key);
212     if (it == features.end())
213         return defaultValue;
214     const String& value = it->second;
215     return value.isNull() || value == "1" || value == "yes" || value == "on";
216 }
217
218 float WindowFeatures::floatFeature(const DialogFeaturesMap& features, const char* key, float min, float max, float defaultValue)
219 {
220     DialogFeaturesMap::const_iterator it = features.find(key);
221     if (it == features.end())
222         return defaultValue;
223     // FIXME: The toDouble function does not offer a way to tell "0q" from string with no digits in it: Both
224     // return the number 0 and false for ok. But "0q" should yield the minimum rather than the default.
225     bool ok;
226     double parsedNumber = it->second.toDouble(&ok);
227     if ((parsedNumber == 0 && !ok) || isnan(parsedNumber))
228         return defaultValue;
229     if (parsedNumber < min || max <= min)
230         return min;
231     if (parsedNumber > max)
232         return max;
233     // FIXME: Seems strange to cast a double to int and then convert back to a float. Why is this a good idea?
234     return static_cast<int>(parsedNumber);
235 }
236
237 void WindowFeatures::parseDialogFeatures(const String& string, DialogFeaturesMap& map)
238 {
239     Vector<String> vector;
240     string.split(';', vector);
241     size_t size = vector.size();
242     for (size_t i = 0; i < size; ++i) {
243         const String& featureString = vector[i];
244
245         size_t separatorPosition = featureString.find('=');
246         size_t colonPosition = featureString.find(':');
247         if (separatorPosition != notFound && colonPosition != notFound)
248             continue; // ignore strings that have both = and :
249         if (separatorPosition == notFound)
250             separatorPosition = colonPosition;
251
252         String key = featureString.left(separatorPosition).stripWhiteSpace().lower();
253
254         // Null string for value indicates key without value.
255         String value;
256         if (separatorPosition != notFound) {
257             value = featureString.substring(separatorPosition + 1).stripWhiteSpace().lower();
258             value = value.left(value.find(' '));
259         }
260
261         map.set(key, value);
262     }
263 }
264
265 } // namespace WebCore