From e2d50dc5dd409ce359a6c0a2968c7d3b9e4eaf58 Mon Sep 17 00:00:00 2001 From: Andrew Kaster Date: Mon, 27 May 2024 15:54:11 -0600 Subject: [PATCH] LibWeb: Move window.open TokenizedFeature parsing into its own file --- .../Userland/Libraries/LibWeb/HTML/BUILD.gn | 1 + Userland/Libraries/LibWeb/CMakeLists.txt | 1 + .../LibWeb/HTML/TokenizedFeatures.cpp | 199 ++++++++++++++++++ .../Libraries/LibWeb/HTML/TokenizedFeatures.h | 15 ++ Userland/Libraries/LibWeb/HTML/Window.cpp | 177 ---------------- 5 files changed, 216 insertions(+), 177 deletions(-) create mode 100644 Userland/Libraries/LibWeb/HTML/TokenizedFeatures.cpp diff --git a/Meta/gn/secondary/Userland/Libraries/LibWeb/HTML/BUILD.gn b/Meta/gn/secondary/Userland/Libraries/LibWeb/HTML/BUILD.gn index 5b8fb0c291e..60c739e390e 100644 --- a/Meta/gn/secondary/Userland/Libraries/LibWeb/HTML/BUILD.gn +++ b/Meta/gn/secondary/Userland/Libraries/LibWeb/HTML/BUILD.gn @@ -163,6 +163,7 @@ source_set("HTML") { "TimeRanges.cpp", "Timer.cpp", "ToggleEvent.cpp", + "TokenizedFeatures.cpp", "TrackEvent.cpp", "TraversableNavigable.cpp", "UserActivation.cpp", diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt index 23ae9847a08..ff0b5896f97 100644 --- a/Userland/Libraries/LibWeb/CMakeLists.txt +++ b/Userland/Libraries/LibWeb/CMakeLists.txt @@ -430,6 +430,7 @@ set(SOURCES HTML/Timer.cpp HTML/TimeRanges.cpp HTML/ToggleEvent.cpp + HTML/TokenizedFeatures.cpp HTML/TrackEvent.cpp HTML/TraversableNavigable.cpp HTML/UserActivation.cpp diff --git a/Userland/Libraries/LibWeb/HTML/TokenizedFeatures.cpp b/Userland/Libraries/LibWeb/HTML/TokenizedFeatures.cpp new file mode 100644 index 00000000000..287de6c7d30 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/TokenizedFeatures.cpp @@ -0,0 +1,199 @@ +/* + * 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; +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/TokenizedFeatures.h b/Userland/Libraries/LibWeb/HTML/TokenizedFeatures.h index 3b2404a3ff3..9744509e663 100644 --- a/Userland/Libraries/LibWeb/HTML/TokenizedFeatures.h +++ b/Userland/Libraries/LibWeb/HTML/TokenizedFeatures.h @@ -6,6 +6,9 @@ #pragma once +#include +#include + namespace Web::HTML::TokenizedFeature { #define TOKENIZED_FEATURE(Feature) \ @@ -24,4 +27,16 @@ TOKENIZED_FEATURE(Scrollbars); TOKENIZED_FEATURE(Status); TOKENIZED_FEATURE(Toolbar); +using Map = OrderedHashMap; + +} + +namespace Web::HTML { + +TokenizedFeature::Map tokenize_open_features(StringView features); +TokenizedFeature::Popup check_if_a_popup_window_is_requested(TokenizedFeature::Map const&); + +template +T parse_boolean_feature(StringView value); + } diff --git a/Userland/Libraries/LibWeb/HTML/Window.cpp b/Userland/Libraries/LibWeb/HTML/Window.cpp index 6c1e7fef77d..d63a846cc66 100644 --- a/Userland/Libraries/LibWeb/HTML/Window.cpp +++ b/Userland/Libraries/LibWeb/HTML/Window.cpp @@ -7,7 +7,6 @@ */ #include -#include #include #include #include @@ -140,182 +139,6 @@ void Window::finalize() Window::~Window() = default; -// 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 -static OrderedHashMap tokenize_open_features(StringView features) -{ - // 1. Let tokenizedFeatures be a new ordered map. - OrderedHashMap 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 -static 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; -} - -// https://html.spec.whatwg.org/multipage/window-object.html#popup-window-is-requested -static TokenizedFeature::Popup check_if_a_popup_window_is_requested(OrderedHashMap 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; -} - // https://html.spec.whatwg.org/multipage/window-object.html#window-open-steps WebIDL::ExceptionOr> Window::open_impl(StringView url, StringView target, StringView features) {