/src/serenity/Userland/Libraries/LibWeb/HTML/TokenizedFeatures.cpp
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2024, Andrew Kaster <akaster@serenityos.org> |
3 | | * |
4 | | * SPDX-License-Identifier: BSD-2-Clause |
5 | | */ |
6 | | |
7 | | #include <AK/GenericLexer.h> |
8 | | #include <LibWeb/HTML/TokenizedFeatures.h> |
9 | | #include <LibWeb/Infra/CharacterTypes.h> |
10 | | |
11 | | namespace Web::HTML { |
12 | | |
13 | | // https://html.spec.whatwg.org/multipage/nav-history-apis.html#normalizing-the-feature-name |
14 | | static String normalize_feature_name(String const& name) |
15 | 0 | { |
16 | | // For legacy reasons, there are some aliases of some feature names. To normalize a feature name name, switch on name: |
17 | | |
18 | | // "screenx" |
19 | 0 | if (name == "screenx"sv) { |
20 | | // Return "left". |
21 | 0 | return "left"_string; |
22 | 0 | } |
23 | | // "screeny" |
24 | 0 | else if (name == "screeny"sv) { |
25 | | // Return "top". |
26 | 0 | return "top"_string; |
27 | 0 | } |
28 | | // "innerwidth" |
29 | 0 | else if (name == "innerwidth"sv) { |
30 | | // Return "width". |
31 | 0 | return "width"_string; |
32 | 0 | } |
33 | | // "innerheight" |
34 | 0 | else if (name == "innerheight") { |
35 | | // Return "height". |
36 | 0 | return "height"_string; |
37 | 0 | } |
38 | | // Anything else |
39 | 0 | else { |
40 | | // Return name. |
41 | 0 | return name; |
42 | 0 | } |
43 | 0 | } |
44 | | |
45 | | // https://html.spec.whatwg.org/multipage/nav-history-apis.html#concept-window-open-features-tokenize |
46 | | TokenizedFeature::Map tokenize_open_features(StringView features) |
47 | 0 | { |
48 | | // 1. Let tokenizedFeatures be a new ordered map. |
49 | 0 | TokenizedFeature::Map tokenized_features; |
50 | | |
51 | | // 2. Let position point at the first code point of features. |
52 | 0 | GenericLexer lexer(features); |
53 | | |
54 | | // https://html.spec.whatwg.org/multipage/nav-history-apis.html#feature-separator |
55 | 0 | auto is_feature_separator = [](auto character) { |
56 | 0 | return Infra::is_ascii_whitespace(character) || character == '=' || character == ','; |
57 | 0 | }; |
58 | | |
59 | | // 3. While position is not past the end of features: |
60 | 0 | while (!lexer.is_eof()) { |
61 | | // 1. Let name be the empty string. |
62 | 0 | String name; |
63 | | |
64 | | // 2. Let value be the empty string. |
65 | 0 | String value; |
66 | | |
67 | | // 3. Collect a sequence of code points that are feature separators from features given position. This skips past leading separators before the name. |
68 | 0 | lexer.ignore_while(is_feature_separator); |
69 | | |
70 | | // 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. |
71 | 0 | name = MUST(String::from_byte_string(lexer.consume_until(is_feature_separator).to_lowercase_string())); |
72 | | |
73 | | // 5. Set name to the result of normalizing the feature name name. |
74 | 0 | name = normalize_feature_name(name); |
75 | | |
76 | | // 6. While position is not past the end of features and the code point at position in features is not U+003D (=): |
77 | | // 1. If the code point at position in features is U+002C (,), or if it is not a feature separator, then break. |
78 | | // 2. Advance position by 1. |
79 | 0 | lexer.ignore_while(Infra::is_ascii_whitespace); |
80 | | |
81 | | // 7. If the code point at position in features is a feature separator: |
82 | | // 1. While position is not past the end of features and the code point at position in features is a feature separator: |
83 | | // 1. If the code point at position in features is U+002C (,), then break. |
84 | | // 2. Advance position by 1. |
85 | 0 | lexer.ignore_while([](auto character) { return Infra::is_ascii_whitespace(character) || character == '='; }); |
86 | | |
87 | | // 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. |
88 | 0 | value = MUST(String::from_byte_string(lexer.consume_until(is_feature_separator).to_lowercase_string())); |
89 | | |
90 | | // 8. If name is not the empty string, then set tokenizedFeatures[name] to value. |
91 | 0 | if (!name.is_empty()) |
92 | 0 | tokenized_features.set(move(name), move(value)); |
93 | 0 | } |
94 | | |
95 | | // 4. Return tokenizedFeatures. |
96 | 0 | return tokenized_features; |
97 | 0 | } |
98 | | |
99 | | // https://html.spec.whatwg.org/multipage/nav-history-apis.html#concept-window-open-features-parse-boolean |
100 | | template<Enum T> |
101 | | T parse_boolean_feature(StringView value) |
102 | 0 | { |
103 | | // 1. If value is the empty string, then return true. |
104 | 0 | if (value.is_empty()) |
105 | 0 | return T::Yes; |
106 | | |
107 | | // 2. If value is "yes", then return true. |
108 | 0 | if (value == "yes"sv) |
109 | 0 | return T::Yes; |
110 | | |
111 | | // 3. If value is "true", then return true. |
112 | 0 | if (value == "true"sv) |
113 | 0 | return T::Yes; |
114 | | |
115 | | // 4. Let parsed be the result of parsing value as an integer. |
116 | 0 | auto parsed = value.to_number<i64>(); |
117 | | |
118 | | // 5. If parsed is an error, then set it to 0. |
119 | 0 | if (!parsed.has_value()) |
120 | 0 | parsed = 0; |
121 | | |
122 | | // 6. Return false if parsed is 0, and true otherwise. |
123 | 0 | return parsed == 0 ? T::No : T::Yes; |
124 | 0 | } Unexecuted instantiation: _ZN3Web4HTML21parse_boolean_featureITkN2AK8Concepts4EnumENS0_16TokenizedFeature8LocationEEET_NS2_10StringViewE Unexecuted instantiation: _ZN3Web4HTML21parse_boolean_featureITkN2AK8Concepts4EnumENS0_16TokenizedFeature7MenubarEEET_NS2_10StringViewE Unexecuted instantiation: _ZN3Web4HTML21parse_boolean_featureITkN2AK8Concepts4EnumENS0_16TokenizedFeature8NoOpenerEEET_NS2_10StringViewE Unexecuted instantiation: _ZN3Web4HTML21parse_boolean_featureITkN2AK8Concepts4EnumENS0_16TokenizedFeature10NoReferrerEEET_NS2_10StringViewE Unexecuted instantiation: _ZN3Web4HTML21parse_boolean_featureITkN2AK8Concepts4EnumENS0_16TokenizedFeature5PopupEEET_NS2_10StringViewE Unexecuted instantiation: _ZN3Web4HTML21parse_boolean_featureITkN2AK8Concepts4EnumENS0_16TokenizedFeature9ResizableEEET_NS2_10StringViewE Unexecuted instantiation: _ZN3Web4HTML21parse_boolean_featureITkN2AK8Concepts4EnumENS0_16TokenizedFeature10ScrollbarsEEET_NS2_10StringViewE Unexecuted instantiation: _ZN3Web4HTML21parse_boolean_featureITkN2AK8Concepts4EnumENS0_16TokenizedFeature6StatusEEET_NS2_10StringViewE Unexecuted instantiation: _ZN3Web4HTML21parse_boolean_featureITkN2AK8Concepts4EnumENS0_16TokenizedFeature7ToolbarEEET_NS2_10StringViewE |
125 | | |
126 | | template TokenizedFeature::Location parse_boolean_feature<TokenizedFeature::Location>(StringView value); |
127 | | template TokenizedFeature::Menubar parse_boolean_feature<TokenizedFeature::Menubar>(StringView value); |
128 | | template TokenizedFeature::NoOpener parse_boolean_feature<TokenizedFeature::NoOpener>(StringView value); |
129 | | template TokenizedFeature::NoReferrer parse_boolean_feature<TokenizedFeature::NoReferrer>(StringView value); |
130 | | template TokenizedFeature::Popup parse_boolean_feature<TokenizedFeature::Popup>(StringView value); |
131 | | template TokenizedFeature::Resizable parse_boolean_feature<TokenizedFeature::Resizable>(StringView value); |
132 | | template TokenizedFeature::Scrollbars parse_boolean_feature<TokenizedFeature::Scrollbars>(StringView value); |
133 | | template TokenizedFeature::Status parse_boolean_feature<TokenizedFeature::Status>(StringView value); |
134 | | template TokenizedFeature::Toolbar parse_boolean_feature<TokenizedFeature::Toolbar>(StringView value); |
135 | | |
136 | | // https://html.spec.whatwg.org/multipage/window-object.html#popup-window-is-requested |
137 | | TokenizedFeature::Popup check_if_a_popup_window_is_requested(TokenizedFeature::Map const& tokenized_features) |
138 | 0 | { |
139 | | // 1. If tokenizedFeatures is empty, then return false. |
140 | 0 | if (tokenized_features.is_empty()) |
141 | 0 | return TokenizedFeature::Popup::No; |
142 | | |
143 | | // 2. If tokenizedFeatures["popup"] exists, then return the result of parsing tokenizedFeatures["popup"] as a boolean feature. |
144 | 0 | if (auto popup_feature = tokenized_features.get("popup"sv); popup_feature.has_value()) |
145 | 0 | return parse_boolean_feature<TokenizedFeature::Popup>(*popup_feature); |
146 | | |
147 | | // https://html.spec.whatwg.org/multipage/window-object.html#window-feature-is-set |
148 | 0 | auto check_if_a_window_feature_is_set = [&]<Enum T>(StringView feature_name, T default_value) { |
149 | | // 1. If tokenizedFeatures[featureName] exists, then return the result of parsing tokenizedFeatures[featureName] as a boolean feature. |
150 | 0 | if (auto feature = tokenized_features.get(feature_name); feature.has_value()) |
151 | 0 | return parse_boolean_feature<T>(*feature); |
152 | | |
153 | | // 2. Return defaultValue. |
154 | 0 | return default_value; |
155 | 0 | }; Unexecuted instantiation: TokenizedFeatures.cpp:auto Web::HTML::check_if_a_popup_window_is_requested(AK::HashMap<AK::String, AK::String, AK::Traits<AK::String>, AK::Traits<AK::String>, true> const&)::$_0::operator()<Web::HTML::TokenizedFeature::Location>(AK::StringView, Web::HTML::TokenizedFeature::Location) const Unexecuted instantiation: TokenizedFeatures.cpp:auto Web::HTML::check_if_a_popup_window_is_requested(AK::HashMap<AK::String, AK::String, AK::Traits<AK::String>, AK::Traits<AK::String>, true> const&)::$_0::operator()<Web::HTML::TokenizedFeature::Toolbar>(AK::StringView, Web::HTML::TokenizedFeature::Toolbar) const Unexecuted instantiation: TokenizedFeatures.cpp:auto Web::HTML::check_if_a_popup_window_is_requested(AK::HashMap<AK::String, AK::String, AK::Traits<AK::String>, AK::Traits<AK::String>, true> const&)::$_0::operator()<Web::HTML::TokenizedFeature::Menubar>(AK::StringView, Web::HTML::TokenizedFeature::Menubar) const Unexecuted instantiation: TokenizedFeatures.cpp:auto Web::HTML::check_if_a_popup_window_is_requested(AK::HashMap<AK::String, AK::String, AK::Traits<AK::String>, AK::Traits<AK::String>, true> const&)::$_0::operator()<Web::HTML::TokenizedFeature::Resizable>(AK::StringView, Web::HTML::TokenizedFeature::Resizable) const Unexecuted instantiation: TokenizedFeatures.cpp:auto Web::HTML::check_if_a_popup_window_is_requested(AK::HashMap<AK::String, AK::String, AK::Traits<AK::String>, AK::Traits<AK::String>, true> const&)::$_0::operator()<Web::HTML::TokenizedFeature::Scrollbars>(AK::StringView, Web::HTML::TokenizedFeature::Scrollbars) const Unexecuted instantiation: TokenizedFeatures.cpp:auto Web::HTML::check_if_a_popup_window_is_requested(AK::HashMap<AK::String, AK::String, AK::Traits<AK::String>, AK::Traits<AK::String>, true> const&)::$_0::operator()<Web::HTML::TokenizedFeature::Status>(AK::StringView, Web::HTML::TokenizedFeature::Status) const |
156 | | |
157 | | // 3. Let location be the result of checking if a window feature is set, given tokenizedFeatures, "location", and false. |
158 | 0 | auto location = check_if_a_window_feature_is_set("location"sv, TokenizedFeature::Location::No); |
159 | | |
160 | | // 4. Let toolbar be the result of checking if a window feature is set, given tokenizedFeatures, "toolbar", and false. |
161 | 0 | auto toolbar = check_if_a_window_feature_is_set("toolbar"sv, TokenizedFeature::Toolbar::No); |
162 | | |
163 | | // 5. If location and toolbar are both false, then return true. |
164 | 0 | if (location == TokenizedFeature::Location::No && toolbar == TokenizedFeature::Toolbar::No) |
165 | 0 | return TokenizedFeature::Popup::Yes; |
166 | | |
167 | | // 6. Let menubar be the result of checking if a window feature is set, given tokenizedFeatures, menubar", and false. |
168 | 0 | auto menubar = check_if_a_window_feature_is_set("menubar"sv, TokenizedFeature::Menubar::No); |
169 | | |
170 | | // 7. If menubar is false, then return true. |
171 | 0 | if (menubar == TokenizedFeature::Menubar::No) |
172 | 0 | return TokenizedFeature::Popup::Yes; |
173 | | |
174 | | // 8. Let resizable be the result of checking if a window feature is set, given tokenizedFeatures, "resizable", and true. |
175 | 0 | auto resizable = check_if_a_window_feature_is_set("resizable"sv, TokenizedFeature::Resizable::Yes); |
176 | | |
177 | | // 9. If resizable is false, then return true. |
178 | 0 | if (resizable == TokenizedFeature::Resizable::No) |
179 | 0 | return TokenizedFeature::Popup::Yes; |
180 | | |
181 | | // 10. Let scrollbars be the result of checking if a window feature is set, given tokenizedFeatures, "scrollbars", and false. |
182 | 0 | auto scrollbars = check_if_a_window_feature_is_set("scrollbars"sv, TokenizedFeature::Scrollbars::No); |
183 | | |
184 | | // 11. If scrollbars is false, then return true. |
185 | 0 | if (scrollbars == TokenizedFeature::Scrollbars::No) |
186 | 0 | return TokenizedFeature::Popup::Yes; |
187 | | |
188 | | // 12. Let status be the result of checking if a window feature is set, given tokenizedFeatures, "status", and false. |
189 | 0 | auto status = check_if_a_window_feature_is_set("status"sv, TokenizedFeature::Status::No); |
190 | | |
191 | | // 13. If status is false, then return true. |
192 | 0 | if (status == TokenizedFeature::Status::No) |
193 | 0 | return TokenizedFeature::Popup::Yes; |
194 | | |
195 | | // 14. Return false. |
196 | 0 | return TokenizedFeature::Popup::No; |
197 | 0 | } |
198 | | |
199 | | } |