diff --git a/Libraries/LibURL/CMakeLists.txt b/Libraries/LibURL/CMakeLists.txt index 63f9bd7d469..9c72b4165cf 100644 --- a/Libraries/LibURL/CMakeLists.txt +++ b/Libraries/LibURL/CMakeLists.txt @@ -10,6 +10,7 @@ set(SOURCES Pattern/Canonicalization.cpp Pattern/ConstructorStringParser.cpp Pattern/Pattern.cpp + Pattern/String.cpp Pattern/Tokenizer.cpp ) diff --git a/Libraries/LibURL/Pattern/String.cpp b/Libraries/LibURL/Pattern/String.cpp new file mode 100644 index 00000000000..a829b12e9c6 --- /dev/null +++ b/Libraries/LibURL/Pattern/String.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2025, Shannon Booth + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +namespace URL::Pattern { + +// https://urlpattern.spec.whatwg.org/#escape-a-pattern-string +String escape_a_pattern_string(String const& input) +{ + // 1. Assert: input is an ASCII string. + VERIFY(all_of(input.code_points(), is_ascii)); + + // 2. Let result be the empty string. + StringBuilder result; + + // 3. Let index be 0. + // 4. While index is less than input’s length: + for (auto c : input.bytes_as_string_view()) { + // 1. Let c be input[index]. + // 2. Increment index by 1. + + // 3. If c is one of: + // * U+002B (+); + // * U+002A (*); + // * U+003F (?); + // * U+003A (:); + // * U+007B ({); + // * U+007D (}); + // * U+0028 ((); + // * U+0029 ()); or + // * U+005C (\), + // then append U+005C (\) to the end of result. + if ("+*?:{}()\\"sv.contains(c)) + result.append('\\'); + + // 4. Append c to the end of result. + result.append(c); + } + + // 5. Return result. + return result.to_string_without_validation(); +} + +} diff --git a/Libraries/LibURL/Pattern/String.h b/Libraries/LibURL/Pattern/String.h new file mode 100644 index 00000000000..dc617484f28 --- /dev/null +++ b/Libraries/LibURL/Pattern/String.h @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2025, Shannon Booth + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +namespace URL::Pattern { + +String escape_a_pattern_string(String const&); + +}