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