Coverage Report

Created: 2025-12-18 07:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}