/* * Copyright (C) 2000 Harri Porten (porten@kde.org) * Copyright (C) 2006 Jon Shier (jshier@iastate.edu) * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2010 Apple Inc. All rights reseved. * Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA */ #include "config.h" #include "WindowFeatures.h" #include "FloatRect.h" #include #include #include #include namespace WebCore { typedef HashMap DialogFeaturesMap; static void setWindowFeature(WindowFeatures&, StringView key, StringView value); static DialogFeaturesMap parseDialogFeaturesMap(const String&); static Optional boolFeature(const DialogFeaturesMap&, const char* key); static Optional floatFeature(const DialogFeaturesMap&, const char* key, float min, float max); static bool isSeparator(UChar character) { return character == ' ' || character == '\t' || character == '\n' || character == '\r' || character == '=' || character == ','; } WindowFeatures parseWindowFeatures(StringView featuresString) { // The IE rule is: all features except for channelmode and fullscreen default to YES, but // if the user specifies a feature string, all features default to NO. (There is no public // standard that applies to this method.) // // // We always allow a window to be resized, which is consistent with Firefox. WindowFeatures features; if (featuresString.isEmpty()) return features; features.menuBarVisible = false; features.statusBarVisible = false; features.toolBarVisible = false; features.locationBarVisible = false; features.scrollbarsVisible = false; processFeaturesString(featuresString, [&features](StringView key, StringView value) { setWindowFeature(features, key, value); }); return features; } void processFeaturesString(StringView features, std::function callback) { unsigned length = features.length(); for (unsigned i = 0; i < length; ) { // skip to first non-separator while (i < length && isSeparator(features[i])) ++i; unsigned keyBegin = i; // skip to first separator while (i < length && !isSeparator(features[i])) i++; unsigned keyEnd = i; // skip to first '=', but don't skip past a ',' while (i < length && features[i] != '=' && features[i] != ',') ++i; // skip to first non-separator, but don't skip past a ',' while (i < length && isSeparator(features[i]) && features[i] != ',') ++i; unsigned valueBegin = i; // skip to first separator while (i < length && !isSeparator(features[i])) ++i; unsigned valueEnd = i; callback(features.substring(keyBegin, keyEnd - keyBegin), features.substring(valueBegin, valueEnd - valueBegin)); } } static void setWindowFeature(WindowFeatures& features, StringView key, StringView value) { // Listing a key with no value is shorthand for key=yes int numericValue; if (value.isEmpty() || equalLettersIgnoringASCIICase(value, "yes")) numericValue = 1; else numericValue = value.toInt(); // We treat key of "resizable" here as an additional feature rather than setting resizeable to true. // This is consistent with Firefox, but could also be handled at another level. if (equalLettersIgnoringASCIICase(key, "left") || equalLettersIgnoringASCIICase(key, "screenx")) features.x = numericValue; else if (equalLettersIgnoringASCIICase(key, "top") || equalLettersIgnoringASCIICase(key, "screeny")) features.y = numericValue; else if (equalLettersIgnoringASCIICase(key, "width") || equalLettersIgnoringASCIICase(key, "innerwidth")) features.width = numericValue; else if (equalLettersIgnoringASCIICase(key, "height") || equalLettersIgnoringASCIICase(key, "innerheight")) features.height = numericValue; else if (equalLettersIgnoringASCIICase(key, "menubar")) features.menuBarVisible = numericValue; else if (equalLettersIgnoringASCIICase(key, "toolbar")) features.toolBarVisible = numericValue; else if (equalLettersIgnoringASCIICase(key, "location")) features.locationBarVisible = numericValue; else if (equalLettersIgnoringASCIICase(key, "status")) features.statusBarVisible = numericValue; else if (equalLettersIgnoringASCIICase(key, "fullscreen")) features.fullscreen = numericValue; else if (equalLettersIgnoringASCIICase(key, "scrollbars")) features.scrollbarsVisible = numericValue; else if (numericValue == 1) features.additionalFeatures.append(key.toString()); } WindowFeatures parseDialogFeatures(const String& dialogFeaturesString, const FloatRect& screenAvailableRect) { auto featuresMap = parseDialogFeaturesMap(dialogFeaturesString); // The following features from Microsoft's documentation are not implemented: // - default font settings // - width, height, left, and top specified in units other than "px" // - edge (sunken or raised, default is raised) // - dialogHide: trusted && boolFeature(features, "dialoghide"), makes dialog hide when you print // - help: boolFeature(features, "help", true), makes help icon appear in dialog (what does it do on Windows?) // - unadorned: trusted && boolFeature(features, "unadorned"); WindowFeatures features; features.menuBarVisible = false; features.toolBarVisible = false; features.locationBarVisible = false; features.dialog = true; float width = floatFeature(featuresMap, "dialogwidth", 100, screenAvailableRect.width()).valueOr(620); // default here came from frame size of dialog in MacIE float height = floatFeature(featuresMap, "dialogheight", 100, screenAvailableRect.height()).valueOr(450); // default here came from frame size of dialog in MacIE features.width = width; features.height = height; features.x = floatFeature(featuresMap, "dialogleft", screenAvailableRect.x(), screenAvailableRect.maxX() - width); features.y = floatFeature(featuresMap, "dialogtop", screenAvailableRect.y(), screenAvailableRect.maxY() - height); if (boolFeature(featuresMap, "center").valueOr(true)) { if (!features.x) features.x = screenAvailableRect.x() + (screenAvailableRect.width() - width) / 2; if (!features.y) features.y = screenAvailableRect.y() + (screenAvailableRect.height() - height) / 2; } features.resizable = boolFeature(featuresMap, "resizable").valueOr(false); features.scrollbarsVisible = boolFeature(featuresMap, "scroll").valueOr(true); features.statusBarVisible = boolFeature(featuresMap, "status").valueOr(false); return features; } static Optional boolFeature(const DialogFeaturesMap& features, const char* key) { auto it = features.find(key); if (it == features.end()) return Nullopt; auto& value = it->value; return value.isNull() || value == "1" || equalLettersIgnoringASCIICase(value, "yes") || equalLettersIgnoringASCIICase(value, "on"); } static Optional floatFeature(const DialogFeaturesMap& features, const char* key, float min, float max) { auto it = features.find(key); if (it == features.end()) return Nullopt; // FIXME: The toDouble function does not offer a way to tell "0q" from string with no digits in it: Both // return the number 0 and false for ok. But "0q" should yield the minimum rather than the default. bool ok; double parsedNumber = it->value.toDouble(&ok); if ((!parsedNumber && !ok) || std::isnan(parsedNumber)) return Nullopt; if (parsedNumber < min || max <= min) return min; if (parsedNumber > max) return max; // FIXME: Seems strange to cast a double to int and then convert back to a float. Why is this a good idea? return static_cast(parsedNumber); } static DialogFeaturesMap parseDialogFeaturesMap(const String& string) { // FIXME: Not clear why we take such a different approach to parsing dialog features // as opposed to window features (using a map, different parsing quirks). DialogFeaturesMap features; Vector vector; string.split(';', vector); for (auto& featureString : vector) { size_t separatorPosition = featureString.find('='); size_t colonPosition = featureString.find(':'); if (separatorPosition != notFound && colonPosition != notFound) continue; // ignore strings that have both = and : if (separatorPosition == notFound) separatorPosition = colonPosition; String key = featureString.left(separatorPosition).stripWhiteSpace(); // Null string for value indicates key without value. String value; if (separatorPosition != notFound) { value = featureString.substring(separatorPosition + 1).stripWhiteSpace(); value = value.left(value.find(' ')); } features.set(key, value); } return features; } } // namespace WebCore