Home | History | Annotate | Download | only in page
      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 "platform/geometry/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     : 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 
    127 void 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 
    168 WindowFeatures::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 
    214 bool 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 
    223 float 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 
    242 void 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 WebCore
    271