blob: 2c0e60a8066a4f45d9d288e26fc31d47ff3f3e6b [file] [log] [blame]
// 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.
#ifndef COMPONENTS_URL_PATTERN_SIMPLE_URL_PATTERN_MATCHER_H_
#define COMPONENTS_URL_PATTERN_SIMPLE_URL_PATTERN_MATCHER_H_
#include <memory>
#include <string>
#include <string_view>
#include "base/component_export.h"
#include "base/gtest_prod_util.h"
#include "base/types/expected.h"
#include "base/types/pass_key.h"
#include "third_party/liburlpattern/parse.h"
#include "third_party/liburlpattern/pattern.h"
class GURL;
namespace re2 {
class RE2;
}
namespace url_pattern {
// This class partially implements URL Pattern spec which is needed for
// Dictionary URL matching of Compression Dictionary Transport.
// https://urlpattern.spec.whatwg.org/
// https://www.ietf.org/archive/id/draft-ietf-httpbis-compression-dictionary-01.html#name-dictionary-url-matching
// It's also used in blink's inspector. The inspector uses it with string inputs
// instead of JS objects, which makes blink::SafeUrlPattern impractical.
//
// This class can be created from a constructor string and a base URL
// and can run the `test` method of URLPattern only with a URL.
//
// This class doesn't support regexp groups, because this class is used in the
// network service, and there is a security concern in allowing user-defined
// regular expressions. Also this class doesn't support setting `ignoreCase`
// option to true.
//
// Note: We share the limitation of regexp groups with ServiceWorker static
// routing API. For that API, we accept URLPattern JS objects without regexp
// groups, and contvet them to blink::SafeUrlPattern structures in Blink, and
// pass them to the browser process via IPC, and evaluate the pattern with
// resource URLs. For Compression Dictionary Transport feature, we parses the
// constructor string and evaluate the pattern with resource URLs in the network
// service. To simplify this class, this class doesn't share any logic with
// ServiceWorker static routing API. But if we will have more usecases of
// URLPattern, we may need to consider combining those logics.
// Context: https://crrev.com/c/5209732/comment/2d53e11c_e4b8c868/
class SimpleUrlPatternMatcher {
public:
// This class partially represents `URLPatternInit` in the URL Pattern spec.
// https://urlpattern.spec.whatwg.org/#dictdef-urlpatterninit
// The only difference is that this class doesn't have a `baseURL` field.
class PatternInit {
public:
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);
~PatternInit();
PatternInit(const PatternInit&) = delete;
PatternInit& operator=(const PatternInit&) = delete;
PatternInit(PatternInit&&);
PatternInit& operator=(PatternInit&&);
bool operator==(const PatternInit& other) const = default;
const std::optional<std::string>& protocol() const { return protocol_; }
const std::optional<std::string>& username() const { return username_; }
const std::optional<std::string>& password() const { return password_; }
const std::optional<std::string>& hostname() const { return hostname_; }
const std::optional<std::string>& port() const { return port_; }
const std::optional<std::string>& pathname() const { return pathname_; }
const std::optional<std::string>& search() const { return search_; }
const std::optional<std::string>& hash() const { return hash_; }
private:
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_;
};
// This class represents `component` struct in the URL Pattern spec.
// https://urlpattern.spec.whatwg.org/#component
class Component {
public:
// Compile a component from a pattern.
// https://urlpattern.spec.whatwg.org/#compile-a-component
static base::expected<Component, std::string> Create(
std::optional<std::string_view> pattern,
liburlpattern::EncodeCallback encode_callback,
const liburlpattern::Options& options);
Component(liburlpattern::Pattern pattern,
std::unique_ptr<re2::RE2> regex,
base::PassKey<Component>);
~Component();
Component(const Component&) = delete;
Component& operator=(const Component&) = delete;
Component(Component&&);
Component& operator=(Component&&);
// This method partially implements `match` method of URLPattern in the
// URL Pattern spec.
// https://urlpattern.spec.whatwg.org/#urlpattern-match
bool Match(std::string_view value) const;
private:
liburlpattern::Pattern pattern_;
// This is null if `pattern_` supports direct matching.
std::unique_ptr<re2::RE2> regex_;
};
// Creates a SimpleUrlPatternMatcher from a constructor string and a base URL.
// If the constructor string is invalid or it contains regexp group, this
// method returns an error string. If `base_url` is nullptr, relative patterns
// in `constructor_string` produce an error.
static base::expected<std::unique_ptr<SimpleUrlPatternMatcher>, std::string>
Create(std::string_view constructor_string, const GURL* base_url);
SimpleUrlPatternMatcher(Component protocol,
Component username,
Component password,
Component hostname,
Component port,
Component pathname,
Component search,
Component hash,
base::PassKey<SimpleUrlPatternMatcher>);
SimpleUrlPatternMatcher(const SimpleUrlPatternMatcher&) = delete;
SimpleUrlPatternMatcher& operator=(const SimpleUrlPatternMatcher&) = delete;
~SimpleUrlPatternMatcher();
// Performs `test` method of URLPattern with a URL.
// https://urlpattern.spec.whatwg.org/#dom-urlpattern-test
bool Match(const GURL& url) const;
private:
friend class SimpleUrlPatternMatcherTest;
// Creates a `PatternInit` with the result of parsing a constructor string and
// apply `base_url` if given. And returns the result of processing a
// URLPatternInit, and processing the default port of the protocol component.
// If `base_url` is nullptr, relative or incomplete PatternInits produce an
// error.
// https://urlpattern.spec.whatwg.org/#parse-a-constructor-string
// https://urlpattern.spec.whatwg.org/#process-a-urlpatterninit
//
// `protocol_component_out` and `protocol_matches_a_special_scheme_flag_out`
// are optional out parameter. When they are set, this method fills them with
// a Component for the protocol component and `protocol matches a special
// scheme flag` in the spec, which are computed while parsing the constructor
// string.
// https://urlpattern.spec.whatwg.org/#compute-protocol-matches-a-special-scheme-flag
static base::expected<PatternInit, std::string> CreatePatternInit(
std::string_view url_pattern,
const GURL* base_url,
std::optional<Component>* protocol_component_out,
bool* protocol_matches_a_special_scheme_flag_out);
// Creates a SimpleUrlPatternMatcher from a `PatternInit` and a precomputed
// protocol component, and a `protocol_matches_a_special_scheme_flag` flag.
static base::expected<std::unique_ptr<SimpleUrlPatternMatcher>, std::string>
CreateFromPatternInit(const PatternInit& pattern,
std::optional<Component> precomputed_protocol_component,
bool protocol_matches_a_special_scheme_flag);
Component protocol_;
Component username_;
Component password_;
Component hostname_;
Component port_;
Component pathname_;
Component search_;
Component hash_;
};
} // namespace url_pattern
#endif // COMPONENTS_URL_PATTERN_SIMPLE_URL_PATTERN_MATCHER_H_