From 43dcc539848d97c60cd01772b061d920c828c089 Mon Sep 17 00:00:00 2001 From: Ben Kelly Date: Thu, 2 Sep 2021 15:59:14 +0000 Subject: [PATCH] URLPattern: Support constructor strings with ipv6 addresses. This adds support for constructor strings like: new URLPattern("http://[\\:\\:1]/"); As discussed in spec issue #113: https://github.com/WICG/urlpattern/issues/113 Fixed: 1245760 Change-Id: I712341e72a5c2af745dbbdbb8673a79809a98425 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3138336 Commit-Queue: Ben Kelly Reviewed-by: Jeremy Roman Cr-Commit-Position: refs/heads/main@{#917682} --- .../modules/url_pattern/url_pattern_parser.cc | 17 ++++++- .../modules/url_pattern/url_pattern_parser.h | 8 +++ .../resources/urlpatterntestdata.json | 49 +++++++++++++++++++ 3 files changed, 73 insertions(+), 1 deletion(-) diff --git a/third_party/blink/renderer/modules/url_pattern/url_pattern_parser.cc b/third_party/blink/renderer/modules/url_pattern/url_pattern_parser.cc index 1358eaa71bb87c..f960d42b37e187 100644 --- a/third_party/blink/renderer/modules/url_pattern/url_pattern_parser.cc +++ b/third_party/blink/renderer/modules/url_pattern/url_pattern_parser.cc @@ -210,8 +210,15 @@ void Parser::Parse(ExceptionState& exception_state) { break; case StringParseState::kHostname: + // Track whether we are inside ipv6 address brackets. + if (IsIPv6Open()) + hostname_ipv6_bracket_depth_ += 1; + else if (IsIPv6Close()) + hostname_ipv6_bracket_depth_ -= 1; + // If we find a `:` then we transition to the port component state. - if (IsPortPrefix()) + // However, we ignore `:` when parsing an ipv6 address. + else if (IsPortPrefix() && !hostname_ipv6_bracket_depth_) ChangeState(StringParseState::kPort, Skip(1)); // If we find a `/` then we transition to the pathname component state. @@ -424,6 +431,14 @@ bool Parser::IsGroupClose() const { return token_list_[token_index_].type == liburlpattern::TokenType::kClose; } +bool Parser::IsIPv6Open() const { + return IsNonSpecialPatternChar(token_index_, "["); +} + +bool Parser::IsIPv6Close() const { + return IsNonSpecialPatternChar(token_index_, "]"); +} + String Parser::MakeComponentString() const { DCHECK_LT(token_index_, token_list_.size()); const auto& token = token_list_[token_index_]; diff --git a/third_party/blink/renderer/modules/url_pattern/url_pattern_parser.h b/third_party/blink/renderer/modules/url_pattern/url_pattern_parser.h index c3392018b79f8b..f6a757ca0bf342 100644 --- a/third_party/blink/renderer/modules/url_pattern/url_pattern_parser.h +++ b/third_party/blink/renderer/modules/url_pattern/url_pattern_parser.h @@ -135,6 +135,11 @@ class Parser final { bool IsGroupOpen() const; bool IsGroupClose() const; + // These methods indicate if the current token is an opening or closing + // bracket for an ipv6 hostname; e.g. '[' or ']'. + bool IsIPv6Open() const; + bool IsIPv6Close() const; + // This method returns a String consisting of the tokens between // `component_start_` and the current `token_index_`. String MakeComponentString() const; @@ -177,6 +182,9 @@ class Parser final { // The current nesting depth of `{ }` pattern groupings. int group_depth_ = 0; + // The current netsting depth of `[ ]` in hostname patterns. + int hostname_ipv6_bracket_depth_ = 0; + // The current parse state. This should only be changed via `ChangeState()` // or `RewindAndSetState()`. StringParseState state_ = StringParseState::kInit; diff --git a/third_party/blink/web_tests/external/wpt/urlpattern/resources/urlpatterntestdata.json b/third_party/blink/web_tests/external/wpt/urlpattern/resources/urlpatterntestdata.json index e9967adf219778..5ef045e560acb5 100644 --- a/third_party/blink/web_tests/external/wpt/urlpattern/resources/urlpatterntestdata.json +++ b/third_party/blink/web_tests/external/wpt/urlpattern/resources/urlpatterntestdata.json @@ -2150,6 +2150,55 @@ "pathname": { "input": "/data:channel.html", "groups": {} } } }, + { + "pattern": [ "http://[\\:\\:1]/" ], + "inputs": [ "http://[::1]/" ], + "exactly_empty_components": [ "username", "password", "port", "search", + "hash" ], + "expected_obj": { + "protocol": "http", + "hostname": "[\\:\\:1]", + "pathname": "/" + }, + "expected_match": { + "protocol": { "input": "http", "groups": {} }, + "hostname": { "input": "[::1]", "groups": {} }, + "pathname": { "input": "/", "groups": {} } + } + }, + { + "pattern": [ "http://[\\:\\:1]:8080/" ], + "inputs": [ "http://[::1]:8080/" ], + "exactly_empty_components": [ "username", "password", "search", "hash" ], + "expected_obj": { + "protocol": "http", + "hostname": "[\\:\\:1]", + "port": "8080", + "pathname": "/" + }, + "expected_match": { + "protocol": { "input": "http", "groups": {} }, + "hostname": { "input": "[::1]", "groups": {} }, + "port": { "input": "8080", "groups": {} }, + "pathname": { "input": "/", "groups": {} } + } + }, + { + "pattern": [ "http://[\\:\\:a]/" ], + "inputs": [ "http://[::a]/" ], + "exactly_empty_components": [ "username", "password", "port", "search", + "hash" ], + "expected_obj": { + "protocol": "http", + "hostname": "[\\:\\:a]", + "pathname": "/" + }, + "expected_match": { + "protocol": { "input": "http", "groups": {} }, + "hostname": { "input": "[::a]", "groups": {} }, + "pathname": { "input": "/", "groups": {} } + } + }, { "pattern": [ "https://foo{{@}}example.com" ], "inputs": [ "https://foo@example.com" ],