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 "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