/* * Copyright (c) 2024, Andrew Kaster * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include namespace Web::HTML { // https://html.spec.whatwg.org/multipage/nav-history-apis.html#normalizing-the-feature-name static String normalize_feature_name(String const& name) { // For legacy reasons, there are some aliases of some feature names. To normalize a feature name name, switch on name: // "screenx" if (name == "screenx"sv) { // Return "left". return "left"_string; } // "screeny" else if (name == "screeny"sv) { // Return "top". return "top"_string; } // "innerwidth" else if (name == "innerwidth"sv) { // Return "width". return "width"_string; } // "innerheight" else if (name == "innerheight") { // Return "height". return "height"_string; } // Anything else else { // Return name. return name; } } // https://html.spec.whatwg.org/multipage/nav-history-apis.html#concept-window-open-features-tokenize TokenizedFeature::Map tokenize_open_features(StringView features) { // 1. Let tokenizedFeatures be a new ordered map. TokenizedFeature::Map tokenized_features; // 2. Let position point at the first code point of features. GenericLexer lexer(features); // https://html.spec.whatwg.org/multipage/nav-history-apis.html#feature-separator auto is_feature_separator = [](auto character) { return Infra::is_ascii_whitespace(character) || character == '=' || character == ','; }; // 3. While position is not past the end of features: while (!lexer.is_eof()) { // 1. Let name be the empty string. String name; // 2. Let value be the empty string. String value; // 3. Collect a sequence of code points that are feature separators from features given position. This skips past leading separators before the name. lexer.ignore_while(is_feature_separator); // 4. Collect a sequence of code points that are not feature separators from features given position. Set name to the collected characters, converted to ASCII lowercase. name = MUST(String::from_byte_string(lexer.consume_until(is_feature_separator).to_lowercase_string())); // 5. Set name to the result of normalizing the feature name name. name = normalize_feature_name(name); // 6. While position is not past the end of features and the code point at position in features is not U+003D (=): // 1. If the code point at position in features is U+002C (,), or if it is not a feature separator, then break. // 2. Advance position by 1. lexer.ignore_while(Infra::is_ascii_whitespace); // 7. If the code point at position in features is a feature separator: // 1. While position is not past the end of features and the code point at position in features is a feature separator: // 1. If the code point at position in features is U+002C (,), then break. // 2. Advance position by 1. lexer.ignore_while([](auto character) { return Infra::is_ascii_whitespace(character) || character == '='; }); // 2. Collect a sequence of code points that are not feature separators code points from features given position. Set value to the collected code points, converted to ASCII lowercase. value = MUST(String::from_byte_string(lexer.consume_until(is_feature_separator).to_lowercase_string())); // 8. If name is not the empty string, then set tokenizedFeatures[name] to value. if (!name.is_empty()) tokenized_features.set(move(name), move(value)); } // 4. Return tokenizedFeatures. return tokenized_features; } // https://html.spec.whatwg.org/multipage/nav-history-apis.html#concept-window-open-features-parse-boolean template T parse_boolean_feature(StringView value) { // 1. If value is the empty string, then return true. if (value.is_empty()) return T::Yes; // 2. If value is "yes", then return true. if (value == "yes"sv) return T::Yes; // 3. If value is "true", then return true. if (value == "true"sv) return T::Yes; // 4. Let parsed be the result of parsing value as an integer. auto parsed = value.to_number(); // 5. If parsed is an error, then set it to 0. if (!parsed.has_value()) parsed = 0; // 6. Return false if parsed is 0, and true otherwise. return parsed == 0 ? T::No : T::Yes; } template TokenizedFeature::Location parse_boolean_feature(StringView value); template TokenizedFeature::Menubar parse_boolean_feature(StringView value); template TokenizedFeature::NoOpener parse_boolean_feature(StringView value); template TokenizedFeature::NoReferrer parse_boolean_feature(StringView value); template TokenizedFeature::Popup parse_boolean_feature(StringView value); template TokenizedFeature::Resizable parse_boolean_feature(StringView value); template TokenizedFeature::Scrollbars parse_boolean_feature(StringView value); template TokenizedFeature::Status parse_boolean_feature(StringView value); template TokenizedFeature::Toolbar parse_boolean_feature(StringView value); // https://html.spec.whatwg.org/multipage/window-object.html#popup-window-is-requested TokenizedFeature::Popup check_if_a_popup_window_is_requested(TokenizedFeature::Map const& tokenized_features) { // 1. If tokenizedFeatures is empty, then return false. if (tokenized_features.is_empty()) return TokenizedFeature::Popup::No; // 2. If tokenizedFeatures["popup"] exists, then return the result of parsing tokenizedFeatures["popup"] as a boolean feature. if (auto popup_feature = tokenized_features.get("popup"sv); popup_feature.has_value()) return parse_boolean_feature(*popup_feature); // https://html.spec.whatwg.org/multipage/window-object.html#window-feature-is-set auto check_if_a_window_feature_is_set = [&](StringView feature_name, T default_value) { // 1. If tokenizedFeatures[featureName] exists, then return the result of parsing tokenizedFeatures[featureName] as a boolean feature. if (auto feature = tokenized_features.get(feature_name); feature.has_value()) return parse_boolean_feature(*feature); // 2. Return defaultValue. return default_value; }; // 3. Let location be the result of checking if a window feature is set, given tokenizedFeatures, "location", and false. auto location = check_if_a_window_feature_is_set("location"sv, TokenizedFeature::Location::No); // 4. Let toolbar be the result of checking if a window feature is set, given tokenizedFeatures, "toolbar", and false. auto toolbar = check_if_a_window_feature_is_set("toolbar"sv, TokenizedFeature::Toolbar::No); // 5. If location and toolbar are both false, then return true. if (location == TokenizedFeature::Location::No && toolbar == TokenizedFeature::Toolbar::No) return TokenizedFeature::Popup::Yes; // 6. Let menubar be the result of checking if a window feature is set, given tokenizedFeatures, menubar", and false. auto menubar = check_if_a_window_feature_is_set("menubar"sv, TokenizedFeature::Menubar::No); // 7. If menubar is false, then return true. if (menubar == TokenizedFeature::Menubar::No) return TokenizedFeature::Popup::Yes; // 8. Let resizable be the result of checking if a window feature is set, given tokenizedFeatures, "resizable", and true. auto resizable = check_if_a_window_feature_is_set("resizable"sv, TokenizedFeature::Resizable::Yes); // 9. If resizable is false, then return true. if (resizable == TokenizedFeature::Resizable::No) return TokenizedFeature::Popup::Yes; // 10. Let scrollbars be the result of checking if a window feature is set, given tokenizedFeatures, "scrollbars", and false. auto scrollbars = check_if_a_window_feature_is_set("scrollbars"sv, TokenizedFeature::Scrollbars::No); // 11. If scrollbars is false, then return true. if (scrollbars == TokenizedFeature::Scrollbars::No) return TokenizedFeature::Popup::Yes; // 12. Let status be the result of checking if a window feature is set, given tokenizedFeatures, "status", and false. auto status = check_if_a_window_feature_is_set("status"sv, TokenizedFeature::Status::No); // 13. If status is false, then return true. if (status == TokenizedFeature::Status::No) return TokenizedFeature::Popup::Yes; // 14. Return false. return TokenizedFeature::Popup::No; } }