Coverage Report

Created: 2026-07-25 07:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/serenity/Userland/Libraries/LibWeb/Infra/Strings.cpp
Line
Count
Source
1
/*
2
 * Copyright (c) 2022-2023, Linus Groh <linusg@serenityos.org>
3
 * Copyright (c) 2022, networkException <networkexception@serenityos.org>
4
 * Copyright (c) 2023, Kenneth Myhra <kennethmyhra@serenityos.org>
5
 * Copyright (c) 2023, Sam Atkins <atkinssj@serenityos.org>
6
 * Copyright (c) 2024, Andreas Kling <andreas@ladybird.org>
7
 *
8
 * SPDX-License-Identifier: BSD-2-Clause
9
 */
10
11
#include <AK/CharacterTypes.h>
12
#include <AK/FlyString.h>
13
#include <AK/GenericLexer.h>
14
#include <AK/String.h>
15
#include <AK/Utf16View.h>
16
#include <AK/Utf8View.h>
17
#include <LibWeb/Infra/CharacterTypes.h>
18
#include <LibWeb/Infra/Strings.h>
19
20
namespace Web::Infra {
21
22
// https://infra.spec.whatwg.org/#ascii-case-insensitive
23
bool is_ascii_case_insensitive_match(StringView a, StringView b)
24
0
{
25
    // A string A is an ASCII case-insensitive match for a string B,
26
    // if the ASCII lowercase of A is the ASCII lowercase of B.
27
0
    return AK::StringUtils::equals_ignoring_ascii_case(a, b);
28
0
}
29
30
// https://infra.spec.whatwg.org/#normalize-newlines
31
String normalize_newlines(String const& string)
32
0
{
33
    // To normalize newlines in a string, replace every U+000D CR U+000A LF code point pair with a single U+000A LF
34
    // code point, and then replace every remaining U+000D CR code point with a U+000A LF code point.
35
0
    if (!string.contains('\r'))
36
0
        return string;
37
38
0
    StringBuilder builder;
39
0
    GenericLexer lexer { string };
40
41
0
    while (!lexer.is_eof()) {
42
0
        builder.append(lexer.consume_until('\r'));
43
44
0
        if (lexer.peek() == '\r') {
45
0
            lexer.ignore(1 + static_cast<size_t>(lexer.peek(1) == '\n'));
46
0
            builder.append('\n');
47
0
        }
48
0
    }
49
50
0
    return MUST(builder.to_string());
51
0
}
52
53
// https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace
54
ErrorOr<String> strip_and_collapse_whitespace(StringView string)
55
0
{
56
    // Replace any sequence of one or more consecutive code points that are ASCII whitespace in the string with a single U+0020 SPACE code point.
57
0
    StringBuilder builder;
58
0
    for (auto code_point : Utf8View { string }) {
59
0
        if (Infra::is_ascii_whitespace(code_point)) {
60
0
            if (!builder.string_view().ends_with(' '))
61
0
                builder.append(' ');
62
0
            continue;
63
0
        }
64
0
        TRY(builder.try_append_code_point(code_point));
65
0
    }
66
67
    // ...and then remove any leading and trailing ASCII whitespace from that string.
68
0
    return String::from_utf8(builder.string_view().trim(Infra::ASCII_WHITESPACE));
69
0
}
70
71
// https://infra.spec.whatwg.org/#code-unit-prefix
72
bool is_code_unit_prefix(StringView potential_prefix, StringView input)
73
0
{
74
0
    auto potential_prefix_utf16 = utf8_to_utf16(potential_prefix).release_value_but_fixme_should_propagate_errors();
75
0
    auto input_utf16 = utf8_to_utf16(input).release_value_but_fixme_should_propagate_errors();
76
77
    // 1. Let i be 0.
78
0
    size_t i = 0;
79
80
    // 2. While true:
81
0
    while (true) {
82
        // 1. If i is greater than or equal to potentialPrefix’s length, then return true.
83
0
        if (i >= potential_prefix.length())
84
0
            return true;
85
86
        // 2. If i is greater than or equal to input’s length, then return false.
87
0
        if (i >= input.length())
88
0
            return false;
89
90
        // 3. Let potentialPrefixCodeUnit be the ith code unit of potentialPrefix.
91
0
        auto potential_prefix_code_unit = Utf16View(potential_prefix_utf16).code_unit_at(i);
92
93
        // 4. Let inputCodeUnit be the ith code unit of input.
94
0
        auto input_code_unit = Utf16View(input_utf16).code_unit_at(i);
95
96
        // 5. Return false if potentialPrefixCodeUnit is not inputCodeUnit.
97
0
        if (potential_prefix_code_unit != input_code_unit)
98
0
            return false;
99
100
        // 6. Set i to i + 1.
101
0
        ++i;
102
0
    }
103
0
}
104
105
// https://infra.spec.whatwg.org/#scalar-value-string
106
ErrorOr<String> convert_to_scalar_value_string(StringView string)
107
0
{
108
    // To convert a string into a scalar value string, replace any surrogates with U+FFFD.
109
0
    StringBuilder scalar_value_builder;
110
0
    auto utf8_view = Utf8View { string };
111
0
    for (u32 code_point : utf8_view) {
112
0
        if (is_unicode_surrogate(code_point))
113
0
            code_point = 0xFFFD;
114
0
        scalar_value_builder.append_code_point(code_point);
115
0
    }
116
0
    return scalar_value_builder.to_string();
117
0
}
118
119
// https://infra.spec.whatwg.org/#ascii-lowercase
120
ErrorOr<String> to_ascii_lowercase(StringView string)
121
0
{
122
    // To ASCII lowercase a string, replace all ASCII upper alphas in the string with their
123
    // corresponding code point in ASCII lower alpha.
124
0
    StringBuilder string_builder;
125
0
    auto utf8_view = Utf8View { string };
126
0
    for (u32 code_point : utf8_view) {
127
0
        code_point = AK::to_ascii_lowercase(code_point);
128
0
        string_builder.append_code_point(code_point);
129
0
    }
130
0
    return string_builder.to_string();
131
0
}
132
133
// https://infra.spec.whatwg.org/#ascii-uppercase
134
ErrorOr<String> to_ascii_uppercase(StringView string)
135
0
{
136
    // To ASCII uppercase a string, replace all ASCII lower alphas in the string with their
137
    // corresponding code point in ASCII upper alpha.
138
0
    StringBuilder string_builder;
139
0
    auto utf8_view = Utf8View { string };
140
0
    for (u32 code_point : utf8_view) {
141
0
        code_point = AK::to_ascii_uppercase(code_point);
142
0
        string_builder.append_code_point(code_point);
143
0
    }
144
0
    return string_builder.to_string();
145
0
}
146
147
}