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