1 /* 2 * Copyright (C) 2000 Harri Porten (porten (at) kde.org) 3 * Copyright (C) 2006 Jon Shier (jshier (at) iastate.edu) 4 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2010 Apple Inc. All rights reseved. 5 * Copyright (C) 2006 Alexey Proskuryakov (ap (at) 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 "core/page/WindowFeatures.h" 25 26 #include "core/platform/graphics/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_WITH_SECURITY_IMPLICATION(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->value; 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->value.toDouble(&ok); 227 if ((!parsedNumber && !ok) || std::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 266