1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567
|
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/url_pattern/simple_url_pattern_matcher.h"
#include <algorithm>
#include <memory>
#include <optional>
#include <string_view>
#include "base/logging.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/types/expected.h"
#include "components/url_pattern/url_pattern_util.h"
#include "third_party/abseil-cpp/absl/status/status.h"
#include "third_party/liburlpattern/constructor_string_parser.h"
#include "third_party/liburlpattern/parse.h"
#include "third_party/liburlpattern/pattern.h"
#include "third_party/liburlpattern/utils.h"
#include "third_party/re2/src/re2/re2.h"
#include "url/gurl.h"
#include "url/url_util.h"
namespace url_pattern {
namespace {
// https://urlpattern.spec.whatwg.org/#default-options
constexpr liburlpattern::Options kDefaultOptions = {.delimiter_list = "",
.prefix_list = "",
.sensitive = true,
.strict = true};
// https://urlpattern.spec.whatwg.org/#hostname-options
constexpr liburlpattern::Options kHostnameOptions = {.delimiter_list = ".",
.prefix_list = "",
.sensitive = true,
.strict = true};
// https://urlpattern.spec.whatwg.org/#pathname-options
constexpr liburlpattern::Options kPathnameOptions = {.delimiter_list = "/",
.prefix_list = "/",
.sensitive = true,
.strict = true};
std::string EscapePatternString(std::string_view input) {
std::string result;
result.reserve(input.length());
liburlpattern::EscapePatternStringAndAppend(input, result);
return result;
}
// Utility function to determine if a pathname is absolute or not. We do some
// additional checking for escaped or grouped slashes.
//
// Note: This is partially copied from
// third_party/blink/renderer/core/url_pattern/url_pattern.cc
bool IsAbsolutePathname(std::string_view pathname) {
if (pathname.empty()) {
return false;
}
if (pathname[0] == '/') {
return true;
}
if (pathname.length() < 2) {
return false;
}
// Patterns treat escaped slashes and slashes within an explicit grouping as
// valid leading slashes. For example, "\/foo" or "{/foo}". Patterns do
// not consider slashes within a custom regexp group as valid for the leading
// pathname slash for now. To support that we would need to be able to
// detect things like ":name_123(/foo)" as a valid leading group in a pattern,
// but that is considered too complex for now.
if ((pathname[0] == '\\' || pathname[0] == '{') && pathname[1] == '/') {
return true;
}
return false;
}
std::string ResolveRelativePathnamePattern(const GURL* base_url,
std::string_view pathname) {
if (base_url && base_url->IsStandard() && !IsAbsolutePathname(pathname)) {
std::string base_path = EscapePatternString(base_url->GetPath());
auto slash_index = base_path.rfind('/');
if (slash_index != std::string::npos) {
// Extract the baseURL path up to and including the first slash. Append
// the relative init pathname to it.
base_path.resize(slash_index + 1);
base_path += pathname;
return base_path;
}
}
return std::string(pathname);
}
} // namespace
SimpleUrlPatternMatcher::PatternInit::PatternInit(
std::optional<std::string> protocol,
std::optional<std::string> username,
std::optional<std::string> password,
std::optional<std::string> hostname,
std::optional<std::string> port,
std::optional<std::string> pathname,
std::optional<std::string> search,
std::optional<std::string> hash)
: protocol_(std::move(protocol)),
username_(std::move(username)),
password_(std::move(password)),
hostname_(std::move(hostname)),
port_(std::move(port)),
pathname_(std::move(pathname)),
search_(std::move(search)),
hash_(std::move(hash)) {}
SimpleUrlPatternMatcher::PatternInit::~PatternInit() = default;
SimpleUrlPatternMatcher::PatternInit::PatternInit(PatternInit&&) = default;
SimpleUrlPatternMatcher::PatternInit&
SimpleUrlPatternMatcher::PatternInit::operator=(PatternInit&&) = default;
// static
base::expected<SimpleUrlPatternMatcher::Component, std::string>
SimpleUrlPatternMatcher::Component::Create(
std::optional<std::string_view> pattern,
liburlpattern::EncodeCallback encode_callback,
const liburlpattern::Options& options) {
base::expected<liburlpattern::Pattern, absl::Status> parse_result =
liburlpattern::Parse(pattern.value_or("*"), encode_callback, options);
if (!parse_result.has_value()) {
return base::unexpected("Failed to parse pattern");
}
if (parse_result->HasRegexGroups()) {
return base::unexpected("Regexp groups are not supported");
}
std::unique_ptr<re2::RE2> regex;
if (!parse_result->CanDirectMatch()) {
const std::string regex_string = parse_result->GenerateRegexString();
regex = std::make_unique<RE2>(regex_string);
if (!regex->ok()) {
return base::unexpected(
base::StrCat({"Failed to compile pattern ", regex_string}));
}
}
return Component(std::move(parse_result.value()), std::move(regex),
base::PassKey<Component>());
}
SimpleUrlPatternMatcher::Component::Component(liburlpattern::Pattern pattern,
std::unique_ptr<re2::RE2> regex,
base::PassKey<Component>)
: pattern_(std::move(pattern)), regex_(std::move(regex)) {}
SimpleUrlPatternMatcher::Component::Component(Component&&) = default;
SimpleUrlPatternMatcher::Component&
SimpleUrlPatternMatcher::Component::operator=(Component&&) = default;
SimpleUrlPatternMatcher::Component::~Component() = default;
bool SimpleUrlPatternMatcher::Component::Match(std::string_view value) const {
if (pattern_.CanDirectMatch()) {
return pattern_.DirectMatch(value, nullptr);
}
CHECK(regex_);
return RE2::FullMatch(value, *regex_);
}
// static
base::expected<std::unique_ptr<SimpleUrlPatternMatcher>, std::string>
SimpleUrlPatternMatcher::Create(std::string_view constructor_string,
const GURL* base_url) {
std::optional<Component> protocol_component;
bool protocol_matches_a_special_scheme_flag = false;
auto pattern_result =
CreatePatternInit(constructor_string, base_url, &protocol_component,
&protocol_matches_a_special_scheme_flag);
if (!pattern_result.has_value()) {
return base::unexpected(pattern_result.error());
}
return CreateFromPatternInit(pattern_result.value(),
std::move(protocol_component),
protocol_matches_a_special_scheme_flag);
}
static base::expected<liburlpattern::ConstructorStringParser, std::string>
ParseConstructorString(
std::string_view constructor_string,
std::optional<SimpleUrlPatternMatcher::Component>* protocol_component_out,
bool* protocol_matches_a_special_scheme_flag_out) {
// Spec: Set init to the result of running parse a constructor string given
// input.
// https://urlpattern.spec.whatwg.org/#parse-a-constructor-string
liburlpattern::ConstructorStringParser constructor_string_parser(
constructor_string);
std::optional<SimpleUrlPatternMatcher::Component> protocol_component;
bool protocol_matches_a_special_scheme_flag = false;
absl::Status result = constructor_string_parser.Parse(
[&protocol_component, &protocol_matches_a_special_scheme_flag](
std::string_view protocol_string)
-> base::expected<bool, absl::Status> {
// Spec: Let protocol component be the result of compiling a component
// given protocol string, canonicalize a protocol, and default options.
auto component_result = SimpleUrlPatternMatcher::Component::Create(
protocol_string, url_pattern::ProtocolEncodeCallback,
kDefaultOptions);
if (!component_result.has_value()) {
return base::unexpected(absl::InvalidArgumentError(
base::StrCat({component_result.error(), " for protocol"})));
}
protocol_component = std::move(component_result.value());
// Spec: If the result of running protocol component matches a special
// scheme given protocol component is true, then set parser’s protocol
// matches a special scheme flag to true.
protocol_matches_a_special_scheme_flag = std::ranges::any_of(
url::GetStandardSchemes(),
[&protocol_component](const std::string& scheme) {
return protocol_component->Match(scheme);
});
return protocol_matches_a_special_scheme_flag;
});
if (!result.ok()) {
return base::unexpected(std::string(result.message()));
}
// `protocol_matcher_out` can be nullptr in tests.
if (protocol_component_out && protocol_component) {
*protocol_component_out = std::move(*protocol_component);
}
// `protocol_matches_a_special_scheme_flag_out` can be nullptr in tests.
if (protocol_matches_a_special_scheme_flag_out) {
*protocol_matches_a_special_scheme_flag_out =
protocol_matches_a_special_scheme_flag;
}
return std::move(constructor_string_parser);
}
// static
base::expected<SimpleUrlPatternMatcher::PatternInit, std::string>
SimpleUrlPatternMatcher::CreatePatternInit(
std::string_view constructor_string,
const GURL* base_url,
std::optional<Component>* protocol_component_out,
bool* protocol_matches_a_special_scheme_flag_out) {
if (base_url && !base_url->is_valid()) {
return base::unexpected("Invalid base URL");
}
auto constructor_string_parser =
ParseConstructorString(constructor_string, protocol_component_out,
protocol_matches_a_special_scheme_flag_out);
if (!constructor_string_parser.has_value()) {
return base::unexpected(constructor_string_parser.error());
}
const liburlpattern::ConstructorStringParser::Result& init =
constructor_string_parser->GetResult();
// Spec: Let processedInit be the result of process a URLPatternInit given
// init, "pattern", null, null, null, null, null, null, null, and null.
//
// The following code are running shortcuts of the steps of "process a
// URLPatternInit".
// https://urlpattern.spec.whatwg.org/#process-a-urlpatterninit
// If base_url is required to complete the URLPatternInit according to the
// spec but is not given, an error is returned.
//
// [protocol]
// - Spec: If init["protocol"] does not exist, then set result["protocol"] to
// the result of processing a base URL string given baseURL’s scheme and
// type.
// Note: "process a base URL string" returns the result of "escaping a pattern
// string" when |type| is "pattern".
// - Spec: If init["protocol"] exists, then set result["protocol"] to the
// result of process protocol for init given init["protocol"] and type.
// Note: "process protocol for init" removes a single trailing ":". But
// ConstructorStringParser doesn't set the trailing ":". So we don't
// need the logic for the trailing ":" removal.
if (!init.protocol && !base_url) {
return base::unexpected("Protocol may not be omitted");
}
std::optional<std::string> protocol =
init.protocol ? std::string(*init.protocol)
: EscapePatternString(base_url->GetScheme());
// [username]
// - Spec: If type is not "pattern" and init contains none of "protocol",
// "hostname", "port" and "username", then set result["username"] to the
// result of processing a base URL string given baseURL’s username and type.
// Note: We do nothing here because type is "pattern".
// - Spec: If init["username"] exists, then set result["username"] to the
// result of process username for init given init["username"] and type.
// Note: "process username for init" do nothing when type is "pattern".
std::optional<std::string> username =
init.username ? std::make_optional(std::string(*init.username))
: std::nullopt;
// [password]
// - Spec: If type is not "pattern" and init contains none of "protocol",
// "hostname", "port", "username" and "password", then set
// result["password"] to the result of processing a base URL string given
// baseURL’s password and type.
// Note: We do nothing here because type is "pattern".
// - Spec: If init["password"] exists, then set result["password"] to the
// result of process password for init given init["password"] and type.
// Note: "process password for init" do nothing when type is "pattern".
std::optional<std::string> password =
init.password ? std::make_optional(std::string(*init.password))
: std::nullopt;
// [hostname]
// - Spec: If init contains neither "protocol" nor "hostname", then:
// - Let baseHost be baseURL’s host.
// - If baseHost is null, then set baseHost to the empty string.
// - Set result["hostname"] to the result of processing a base URL string
// given baseHost and type.
// - Spec: If init["hostname"] exists, then set result["hostname"] to the
// result of process hostname for init given init["hostname"] and type.
// Note: "process hostname for init" do nothing when type is "pattern".
std::optional<std::string> hostname =
init.hostname ? std::make_optional(std::string(*init.hostname))
: (init.protocol || !base_url
? std::nullopt
: std::make_optional(
EscapePatternString(base_url->GetHost())));
// [port]
// - Spec: If init contains none of "protocol", "hostname", and "port", then:
// - If baseURL’s port is null, then set result["port"] to the empty string.
// - Otherwise, set result["port"] to baseURL’s port, serialized.
// - Spec: If init["port"] exists, then set result["port"] to the result of
// process port for init given init["port"], result["protocol"], and type.
// Note: "process port for init" do nothing when type is "pattern".
std::optional<std::string> port =
init.port ? std::make_optional(std::string(*init.port))
: ((init.protocol || init.hostname || !base_url)
? std::nullopt
: std::make_optional(base_url->GetPort()));
// [pathname]
// - Spec: If init contains none of "protocol", "hostname", "port", and
// "pathname", then set result["pathname"] to the result of processing a
// base URL string given the result of URL path serializing baseURL and
// type.
// - Spec: If init["pathname"] exists:
// - Set result["pathname"] to init["pathname"].
// - If the following are all true:
// * baseURL is not null;
// * baseURL has an opaque path; and
// * the result of running is an absolute pathname given
// result["pathname"] and type is false,
// then:
// - Let baseURLPath be the result of running process a base URL string
// given the result of URL path serializing baseURL and type.
// - Let slash index be the index of the last U+002F (/) code point found
// in baseURLPath, interpreted as a sequence of code points, or null if
// there are no instances of the code point.
// - If slash index is not null:
// - Let new pathname be the code point substring from 0 to slash index +
// 1 within baseURLPath.
// - Append result["pathname"] to the end of new pathname.
// - Set result["pathname"] to new pathname.
// - Set result["pathname"] to the result of process pathname for init given
// result["pathname"], result["protocol"], and type.
// Note: The second logic is implemented in ResolveRelativePathnamePattern().
// "process pathname for init" do nothing when type is "pattern".
if (!base_url && init.pathname && url::IsStandard(*protocol) &&
!IsAbsolutePathname(*init.pathname)) {
return base::unexpected("Relative pathname not allowed");
}
std::optional<std::string> pathname =
init.pathname
? std::make_optional(
ResolveRelativePathnamePattern(base_url, *init.pathname))
: ((init.protocol || init.hostname || init.port || !base_url)
? std::nullopt
: std::make_optional(
EscapePatternString(base_url->GetPath())));
// [search]
// - Spec: If init contains none of "protocol", "hostname", "port",
// "pathname", and "search", then:
// - Let baseQuery be baseURL’s query.
// - If baseQuery is null, then set baseQuery to the empty string.
// - Set result["search"] to the result of processing a base URL string
// given baseQuery and type.
// - Spec: If init["search"] exists then set result["search"] to the result of
// process search for init given init["search"] and type.
// Note: "process search for init" removes a single leading "?". But
// ConstructorStringParser doesn't set the leading "?". So we don't
// need the logic for the leading "?" removal.
std::optional<std::string> search =
init.search ? std::make_optional(std::string(*init.search))
: ((init.protocol || init.hostname || init.port ||
init.pathname || !base_url)
? std::nullopt
: std::make_optional(
EscapePatternString(base_url->GetQuery())));
// [hash]
// - Spec: If init contains none of "protocol", "hostname", "port",
// "pathname", "search", and "hash", then:
// - Let baseFragment be baseURL’s fragment.
// - If baseFragment is null, then set baseFragment to the empty string.
// - Set result["hash"] to the result of processing a base URL string given
// baseFragment and type.
// - Spec: If init["hash"] exists then set result["hash"] to the result of
// process hash for init given init["hash"] and type.
// Note: "process hash for init" removes a single leading "#". But
// ConstructorStringParser doesn't set the leading "#". So we don't
// need the logic for the leading "#" removal.
std::optional<std::string> hash =
init.hash
? std::make_optional(std::string(*init.hash))
: ((init.protocol || init.hostname || init.port || init.pathname ||
init.search || !base_url)
? std::nullopt
: std::make_optional(EscapePatternString(base_url->GetRef())));
CHECK(protocol);
// Treat default port as an empty string pattern. This logic is in the
// "initialize a URLPattern" step.
// https://urlpattern.spec.whatwg.org/#urlpattern-initialize
// - Spec: If processedInit["protocol"] is a special scheme and
// processedInit["port"] is its corresponding default port, then set
// processedInit["port"] to the empty string.
if (port) {
int default_port = url::DefaultPortForScheme(*protocol);
if (default_port != url::PORT_UNSPECIFIED &&
base::NumberToString(default_port) == *port) {
port = "";
}
}
return PatternInit(std::move(protocol), std::move(username),
std::move(password), std::move(hostname), std::move(port),
std::move(pathname), std::move(search), std::move(hash));
}
// static
base::expected<std::unique_ptr<SimpleUrlPatternMatcher>, std::string>
SimpleUrlPatternMatcher::CreateFromPatternInit(
const PatternInit& pattern,
std::optional<Component> precomputed_protocol_component,
bool protocol_matches_a_special_scheme_flag) {
// We run the steps of compiling components in the "initialize a URLPattern"
// step.
// https://urlpattern.spec.whatwg.org/#urlpattern-initialize
// We always provide `base_url` for creating a `SimpleUrlPatternMatcher`. So
// `pattern.protocol()` must be set.
CHECK(pattern.protocol());
std::optional<Component> protocol_component =
std::move(precomputed_protocol_component);
if (!protocol_component) {
// Spec: Set this’s protocol component to the result of compiling a
// component given processedInit["protocol"], canonicalize a protocol,
// and default options.
auto protocol_component_result =
Component::Create(*pattern.protocol(),
url_pattern::ProtocolEncodeCallback, kDefaultOptions);
if (!protocol_component_result.has_value()) {
return base::unexpected(
base::StrCat({protocol_component_result.error(), " for protocol"}));
}
protocol_component = std::move(protocol_component_result.value());
protocol_matches_a_special_scheme_flag =
std::ranges::any_of(url::GetStandardSchemes(),
[&protocol_component](const std::string& scheme) {
return protocol_component->Match(scheme);
});
}
#define MAYBE_COMPILE_PATTERN(type, callback, options) \
std::optional<Component> type##_component; \
auto type##_result = Component::Create(pattern.type(), callback, options); \
if (!type##_result.has_value()) { \
return base::unexpected( \
base::StrCat({type##_result.error(), " for " #type})); \
} \
type##_component = std::move(type##_result.value());
// Spec: Set this’s username component to the result of compiling a component
// given processedInit["username"], canonicalize a username, and default
// options.
MAYBE_COMPILE_PATTERN(username, url_pattern::UsernameEncodeCallback,
kDefaultOptions);
// Spec: Set this’s password component to the result of compiling a component
// given processedInit["password"], canonicalize a password, and default
// options.
MAYBE_COMPILE_PATTERN(password, url_pattern::PasswordEncodeCallback,
kDefaultOptions);
// Spec:
// - If the result running hostname pattern is an IPv6 address given
// processedInit["hostname"] is true, then set this’s hostname component to
// the result of compiling a component given processedInit["hostname"],
// canonicalize an IPv6 hostname, and hostname options.
// - Otherwise, set this’s hostname component to the result of compiling a
// component given processedInit["hostname"], canonicalize a hostname, and
// hostname options.
MAYBE_COMPILE_PATTERN(hostname,
url_pattern::TreatAsIPv6Hostname(*pattern.hostname())
? url_pattern::IPv6HostnameEncodeCallback
: url_pattern::HostnameEncodeCallback,
kHostnameOptions);
// Spec: Set this’s port component to the result of compiling a component
// given processedInit["port"], canonicalize a port, and default options.
MAYBE_COMPILE_PATTERN(port, url_pattern::PortEncodeCallback, kDefaultOptions);
// Spec:
// - Let compileOptions be a copy of the default options with the ignore case
// property set to options["ignoreCase"].
// - If the result of running protocol component matches a special scheme
// given this’s protocol component is true, then:
// - Let pathCompileOptions be copy of the pathname options with the ignore
// case property set to options["ignoreCase"].
// - Set this’s pathname component to the result of compiling a component
// given processedInit["pathname"], canonicalize a pathname, and
// pathCompileOptions.
// - Otherwise set this’s pathname component to the result of compiling a
// component given processedInit["pathname"], canonicalize an opaque
// pathname, and compileOptions.
// Note: We use the default false for options["ignoreCase"].
MAYBE_COMPILE_PATTERN(pathname,
protocol_matches_a_special_scheme_flag
? url_pattern::StandardURLPathnameEncodeCallback
: url_pattern::PathURLPathnameEncodeCallback,
protocol_matches_a_special_scheme_flag
? kPathnameOptions
: kDefaultOptions);
// Spec: Set this’s search component to the result of compiling a component
// given processedInit["search"], canonicalize a search, and compileOptions.
MAYBE_COMPILE_PATTERN(search, url_pattern::SearchEncodeCallback,
kDefaultOptions);
// Spec: Set this’s hash component to the result of compiling a component
// given processedInit["hash"], canonicalize a hash, and compileOptions.
MAYBE_COMPILE_PATTERN(hash, url_pattern::HashEncodeCallback, kDefaultOptions);
#undef MAYBE_COMPILE_PATTERN
return std::make_unique<SimpleUrlPatternMatcher>(
std::move(*protocol_component), std::move(*username_component),
std::move(*password_component), std::move(*hostname_component),
std::move(*port_component), std::move(*pathname_component),
std::move(*search_component), std::move(*hash_component),
base::PassKey<SimpleUrlPatternMatcher>());
}
SimpleUrlPatternMatcher::SimpleUrlPatternMatcher(
Component protocol,
Component username,
Component password,
Component hostname,
Component port,
Component pathname,
Component search,
Component hash,
base::PassKey<SimpleUrlPatternMatcher>)
: protocol_(std::move(protocol)),
username_(std::move(username)),
password_(std::move(password)),
hostname_(std::move(hostname)),
port_(std::move(port)),
pathname_(std::move(pathname)),
search_(std::move(search)),
hash_(std::move(hash)) {}
bool SimpleUrlPatternMatcher::Match(const GURL& url) const {
return protocol_.Match(url.GetScheme()) &&
username_.Match(url.GetUsername()) &&
password_.Match(url.GetPassword()) && hostname_.Match(url.GetHost()) &&
port_.Match(url.GetPort()) && pathname_.Match(url.GetPath()) &&
search_.Match(url.GetQuery()) && hash_.Match(url.GetRef());
}
SimpleUrlPatternMatcher::~SimpleUrlPatternMatcher() = default;
} // namespace url_pattern
|