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