diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2022-05-17 17:24:03 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2022-06-22 07:51:41 +0000 |
commit | 774f54339e5db91f785733232d3950366db65d07 (patch) | |
tree | 068e1b47bd1af94d77094ed12b604a6b83d9c22a /chromium/components/custom_handlers | |
parent | f7eaed5286974984ba5f9e3189d8f49d03e99f81 (diff) | |
download | qtwebengine-chromium-774f54339e5db91f785733232d3950366db65d07.tar.gz |
BASELINE: Update Chromium to 102.0.5005.57
Change-Id: I885f714bb40ee724c28f94ca6bd8dbdb39915158
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/components/custom_handlers')
14 files changed, 988 insertions, 137 deletions
diff --git a/chromium/components/custom_handlers/BUILD.gn b/chromium/components/custom_handlers/BUILD.gn index d66b5ac8b23..b036c5a479f 100644 --- a/chromium/components/custom_handlers/BUILD.gn +++ b/chromium/components/custom_handlers/BUILD.gn @@ -6,6 +6,8 @@ static_library("custom_handlers") { sources = [ "pref_names.cc", "pref_names.h", + "protocol_handler.cc", + "protocol_handler.h", "protocol_handler_registry.cc", "protocol_handler_registry.h", "protocol_handler_throttle.cc", @@ -14,12 +16,15 @@ static_library("custom_handlers") { deps = [ "//build:chromeos_buildflags", + "//components/keyed_service/content", "//components/keyed_service/core", "//components/pref_registry", "//components/prefs", + "//components/strings", "//components/user_prefs", "//content/public/browser", "//content/public/common", + "//net", "//services/network/public/cpp:cpp_base", "//third_party/blink/public/common:headers", ] @@ -32,7 +37,6 @@ static_library("custom_handlers") { deps += [ "//components/permissions", - "//components/strings", "//ui/base", ] } @@ -41,12 +45,15 @@ static_library("custom_handlers") { source_set("test_support") { testonly = true sources = [ + "simple_protocol_handler_registry_factory.cc", + "simple_protocol_handler_registry_factory.h", "test_protocol_handler_registry_delegate.cc", "test_protocol_handler_registry_delegate.h", ] deps = [ ":custom_handlers", "//base", + "//components/keyed_service/content", ] } @@ -71,3 +78,21 @@ source_set("unit_tests") { "//third_party/blink/public/common:headers", ] } + +source_set("browser_tests") { + testonly = true + defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ] + sources = [ "protocol_handler_registry_browsertest.cc" ] + deps = [ + ":custom_handlers", + ":test_support", + "//base/test:test_support", + "//components/keyed_service/content", + "//content/shell:content_shell_lib", + "//content/test:browsertest_support", + "//content/test:test_support", + "//net:test_support", + "//testing/gmock", + "//testing/gtest", + ] +} diff --git a/chromium/components/custom_handlers/DEPS b/chromium/components/custom_handlers/DEPS index 32f80b99277..ba81a85a8c2 100644 --- a/chromium/components/custom_handlers/DEPS +++ b/chromium/components/custom_handlers/DEPS @@ -9,8 +9,10 @@ include_rules = [ "+content/public/browser", "+content/public/common", "+content/public/test", - "+third_party/blink/public/common/loader", - "+third_party/blink/public/common/security", + "+net/base", + "+content/shell", + "+net/test/embedded_test_server", + "+third_party/blink/public/common", "+services/network/public/cpp", "+ui/base/l10n", ] diff --git a/chromium/components/custom_handlers/OWNERS b/chromium/components/custom_handlers/OWNERS new file mode 100644 index 00000000000..a048d5d1ece --- /dev/null +++ b/chromium/components/custom_handlers/OWNERS @@ -0,0 +1,4 @@ +jfernandez@igalia.com + +# for backup +dominickn@chromium.org
\ No newline at end of file diff --git a/chromium/components/custom_handlers/README.md b/chromium/components/custom_handlers/README.md new file mode 100644 index 00000000000..ff93324343a --- /dev/null +++ b/chromium/components/custom_handlers/README.md @@ -0,0 +1,180 @@ +# Custom Handlers + +The custom handlers component provides a way to register custom +protocol handlers for specific URL schemes. This is part of the +implementation of the [System state and capabilities](https://html.spec.whatwg.org/multipage/system-state.html#system-state-and-capabilities) +defined in the HTML spec. Specifically, it implements the interface +[NavigatorContentUtils](https://html.spec.whatwg.org/multipage/system-state.html#navigatorcontentutils) +from which the Navigator object derives. + +These handlers may be used to translate the http request's URL so that +it is redirected to the appropriated service (e.g. email, apps) or a +different http(s) site. + +All the component's code is intended to be run by the Browser process, +in the UI thread. + +## Security and privacy + +The component addresses the [security and privacy](https://html.spec.whatwg.org/multipage/system-state.html#security-and-privacy) +considerations described in the HTML spec. + +A protocol handler is only valid if it passes all [protocol handler +parameters normalization](https://html.spec.whatwg.org/multipage/system-state.html#normalize-protocol-handler-parameters) +steps. These security and privacy checks are: + +* The handler's url must be HTTP or HTTPS; 'chrome-extension' schemes + may be allowed depending on the [`security level`]. +* Ensure the handler's url fulfills the [potentially trustworthy criteria](https://w3c.github.io/webappsec-secure-contexts/#potentially-trustworthy-url) + defined in the Secure Context spec. This logic is implemented by the + [`IsUrlPotentiallyTrustworthy`] in the `//services/network` module. + +* The protocol being handled must be on the [safelisted scheme](https://html.spec.whatwg.org/multipage/system-state.html#safelisted-scheme) + described in the spec. The [`IsValidCustomHandlerScheme`] function + in `blink` implements this check. + +Chromium defines a hierarchy of security levels to **relax the +restrictions** imposed by the spec and allow the implementation of +certain features. Being `strict` the default behavior and the one +defined in the spec, there are levels for allowing untrusted-origins +and schemes not listed in the mentioned safelist defined in the spec. + +For instance, on order to make possible for extensions to register +their own pages as handlers, the `chrome-extension` scheme is also +allowed when security level is +`blink::ProtocolHandlerSecurityLevel::kExtensionFeatures`. + +It's also worth mentioned that Chromium defines its own +[`kProtocolSafelist`] that includes some additional [decentralized schemes](https://github.com/whatwg/html/pull/5482) +that are not being explicitly defined in the mentioned. + +## High-level architecture + +``` +Browser + + +--------------------------------------------------------------------+ + | //components/custom_handlers | + | | + | +------------------------------------------+ | + | | RegisterProtocolHandlerPermissionRequest | | + | +------------------------------------------+ | + | / \ | + | +-----------------+ | | + | | ProtocolHandler | <-------------+ | | + | +-----------------+ | | | + | / \ | | | + | | | | | + | | | | | + | | | | | + | +-------------------------+ | | | + | | ProtocolHandlerRegistry | <--------+ | | + | +-------------------------+ | | | + | / \ | | | + | | | | | + | | | | | + +--------------------------------------------------------------------+ + | | | + | | | + +--------------------------------------------------------------------+ + | //chrome | | | | + | | | | | + | +--------------------------------+ | +---------+ | +--------------------------+ + | | ProtocolHandlerRegistryFactory | <----- | Browser | ----------------> | PermissionRequestManager | + | +--------------------------------+ +---------+ | +--------------------------+ + | / \ | + | | | + +--------------------------------------------------------------------+ + | + +--------------------------------------------------------------------+ + | //content | | + | | | + | +-----------------+ +---------------------+ | + | | WebContentsImpl | ---------> | WebContentsDelegate | | + | +-----------------+ +---------------------+ | + | / \ | + | | | + | | | + | | | + | +-------------------------+ +---------------------+ | + | | RenderFrameHostDelegate | <--------- | RenderFrameHostImpl | | + | +-------------------------+ +---------------------+ | + | | | + | | | + +--------------------------------------------------------------------+ + | + | ++--------------------------------------------------------------------------------------------------------+ +Renderer | + | + +--------------------------------------------------------------------+ + | //blink | | + | V | + | +-----------------------+ +------------------------------+ | + | | NavigatorContentUtils | ----> | mojom::blink::LocalFrameHost | | + | +-----------------------+ +------------------------------+ | + | | + +--------------------------------------------------------------------+ + ``` + + +Here is a summary of the core responsibilities of the classes and interfaces: + +* [`ProtocolHandler`] + + It's the class responsible of the security and privacy validation + mentioned before, and eventually of the http request's URL + translation, using the protocol handler's url spec. + +* [`ProtocolHandlerRegistry`] + + This class is implemented as a [`KeyedService`] which means it is + attached to a [`BrowserContext`]. + + The registry holds different kind of protocol handlers lists, + depending on their source during the registration: user or internal + policies. The registry also provides an API to selectively ignore + protocol handlers, which are managed in an independent list. + + There are also some **predefined-handlers**, which are automatically + registered by the registry factory during the service's + initialization. + + Finally there is a list of the default handlers for each protocol. + + All the protocol handlers managed by the registry are stored in the + user preference storage, based on the user profile (the Browser + Context) used to initialize the keyed service. This makes possible + to guarantee the persistence of the protocol handlers state. + +* [`ProtocolHandlerThrottle`] + + It implements the blink's [`blink::URLLoaderThrottle`] interface to + manage the http request. It holds a pointer to a + ProtocolHandlerRegistry instance to performs the URL translation if + there is a custom handler for the protocol used for the request. + +* [`RegisterProtocolHandlerPermissionRequest`] + + It implements the [`PermissionRequest`] interface to manage user + authorization for the requests issued by the Navigator object's + `registerProtocolHandler()` method. An instance of this class holds + a pointer to a _ProtocolHandlerRegistry_ instance and a + _ProtocolHandler_ reference to be registered. + + It performs the handler registration of granted, or adds it to the + ignored list if denied. + + +[`security level`]: https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/public/common/security/protocol_handler_security_level.h;bpv=1;bpt=1;l=12?q=ProtocolHandlerSecurityLevel&ss=chromium%2Fchromium%2Fsrc +[`IsUrlPotentiallyTrustworthy`]: https://source.chromium.org/chromium/chromium/src/+/main:services/network/public/cpp/is_potentially_trustworthy.cc;l=334?q=IsUrlPotentiallyTrustworthy&ss=chromium%2Fchromium%2Fsrc +[`IsValidCustomHandlerScheme`]: https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/common/custom_handlers/protocol_handler_utils.cc;l=13?q=IsValidCustomHandlerScheme&ss=chromium%2Fchromium%2Fsrc +[`kProtocolSafelist`]: https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/common/custom_handlers/protocol_handler_utils.cc;l=40?q=IsValidCustomHandlerScheme&ss=chromium%2Fchromium%2Fsrc +[`KeyedService`]: https://cs.chromium.org/search?q=file:/keyed_service.h$ +[`BrowserContext`]: https://cs.chromium.org/search?q=file:/browser_context.h$ +[`ProtocolHandler`]: https://cs.chromium.org/search?q=file:/protocol_handler.h$ +[`ProtocolHandlerRegistry`]: https://cs.chromium.org/search?q=file:/protocol_handler_registry.h$ +[`ProtocolHandlerThrottle`]: https://cs.chromium.org/search?q=file:/protocol_handler_throttle.h$ +[`blink::URLLoaderThrottle`]: https://cs.chromium.org/search?q=file:/loader/url_loader_throttle.h$ +[`RegisterProtocolHandlerPermissionRequest`]: https://cs.chromium.org/search?q=file:/register_protocol_handler_request.h$ +[`PermissionRequest`]: https://cs.chromium.org/search?q=file:/permission_request.h$
\ No newline at end of file diff --git a/chromium/components/custom_handlers/protocol_handler.cc b/chromium/components/custom_handlers/protocol_handler.cc new file mode 100644 index 00000000000..aedebf65620 --- /dev/null +++ b/chromium/components/custom_handlers/protocol_handler.cc @@ -0,0 +1,187 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/custom_handlers/protocol_handler.h" + +#include "base/json/values_util.h" +#include "base/strings/utf_string_conversions.h" +#include "components/strings/grit/components_strings.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/common/content_client.h" +#include "content/public/common/origin_util.h" +#include "net/base/escape.h" +#include "services/network/public/cpp/is_potentially_trustworthy.h" +#include "third_party/blink/public/common/custom_handlers/protocol_handler_utils.h" +#include "third_party/blink/public/common/scheme_registry.h" +#include "third_party/blink/public/common/security/protocol_handler_security_level.h" +#include "ui/base/l10n/l10n_util.h" + +using content::BrowserThread; + +namespace custom_handlers { + +ProtocolHandler::ProtocolHandler( + const std::string& protocol, + const GURL& url, + base::Time last_modified, + blink::ProtocolHandlerSecurityLevel security_level) + : protocol_(base::ToLowerASCII(protocol)), + url_(url), + last_modified_(last_modified), + security_level_(security_level) {} + +ProtocolHandler::ProtocolHandler(const ProtocolHandler&) = default; +ProtocolHandler::~ProtocolHandler() = default; + +ProtocolHandler ProtocolHandler::CreateProtocolHandler( + const std::string& protocol, + const GURL& url, + blink::ProtocolHandlerSecurityLevel security_level) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + return ProtocolHandler(protocol, url, base::Time::Now(), security_level); +} + +ProtocolHandler::ProtocolHandler( + const std::string& protocol, + const GURL& url, + const std::string& app_id, + base::Time last_modified, + blink::ProtocolHandlerSecurityLevel security_level) + : protocol_(base::ToLowerASCII(protocol)), + url_(url), + web_app_id_(app_id), + last_modified_(last_modified), + security_level_(security_level) {} + +// static +ProtocolHandler ProtocolHandler::CreateWebAppProtocolHandler( + const std::string& protocol, + const GURL& url, + const std::string& app_id) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + return ProtocolHandler(protocol, url, app_id, base::Time::Now(), + blink::ProtocolHandlerSecurityLevel::kStrict); +} + +ProtocolHandler::ProtocolHandler() = default; + +bool ProtocolHandler::IsValidDict(const base::Value::Dict& value) { + // Note that "title" parameter is ignored. + // The |last_modified| field is optional as it was introduced in M68. + return value.FindString("protocol") && value.FindString("url"); +} + +bool ProtocolHandler::IsValid() const { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + if (!blink::IsAllowedCustomHandlerURL(url_, security_level_)) + return false; + + return blink::IsValidCustomHandlerScheme(protocol_, security_level_); +} + +bool ProtocolHandler::IsSameOrigin(const ProtocolHandler& handler) const { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + return handler.url().DeprecatedGetOriginAsURL() == + url_.DeprecatedGetOriginAsURL(); +} + +const ProtocolHandler& ProtocolHandler::EmptyProtocolHandler() { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + static const ProtocolHandler* const kEmpty = new ProtocolHandler(); + return *kEmpty; +} + +ProtocolHandler ProtocolHandler::CreateProtocolHandler( + const base::Value::Dict& value) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + if (!IsValidDict(value)) { + return EmptyProtocolHandler(); + } + std::string protocol, url; + // |time| defaults to the beginning of time if it is not specified. + base::Time time; + blink::ProtocolHandlerSecurityLevel security_level = + blink::ProtocolHandlerSecurityLevel::kStrict; + if (const std::string* protocol_in = value.FindString("protocol")) + protocol = *protocol_in; + if (const std::string* url_in = value.FindString("url")) + url = *url_in; + absl::optional<base::Time> time_value = + base::ValueToTime(value.Find("last_modified")); + // Treat invalid times as the default value. + if (time_value) + time = *time_value; + absl::optional<int> security_level_value = value.FindInt("security_level"); + if (security_level_value) { + security_level = + blink::ProtocolHandlerSecurityLevelFrom(*security_level_value); + } + + if (const base::Value* app_id_val = value.Find("app_id")) { + std::string app_id; + if (app_id_val->is_string()) + app_id = app_id_val->GetString(); + return ProtocolHandler(protocol, GURL(url), app_id, time, security_level); + } + + return ProtocolHandler(protocol, GURL(url), time, security_level); +} + +GURL ProtocolHandler::TranslateUrl(const GURL& url) const { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + std::string translatedUrlSpec(url_.spec()); + base::ReplaceFirstSubstringAfterOffset( + &translatedUrlSpec, 0, "%s", + net::EscapeQueryParamValue(url.spec(), false)); + return GURL(translatedUrlSpec); +} + +base::Value::Dict ProtocolHandler::Encode() const { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + base::Value::Dict d; + d.Set("protocol", protocol_); + d.Set("url", url_.spec()); + d.Set("last_modified", base::TimeToValue(last_modified_)); + d.Set("security_level", static_cast<int>(security_level_)); + + if (web_app_id_.has_value()) + d.Set("app_id", web_app_id_.value()); + + return d; +} + +std::u16string ProtocolHandler::GetProtocolDisplayName( + const std::string& protocol) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + if (protocol == "mailto") + return l10n_util::GetStringUTF16(IDS_REGISTER_PROTOCOL_HANDLER_MAILTO_NAME); + if (protocol == "webcal") + return l10n_util::GetStringUTF16(IDS_REGISTER_PROTOCOL_HANDLER_WEBCAL_NAME); + return base::UTF8ToUTF16(protocol); +} + +std::u16string ProtocolHandler::GetProtocolDisplayName() const { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + return GetProtocolDisplayName(protocol_); +} + +#if !defined(NDEBUG) +std::string ProtocolHandler::ToString() const { + return "{ protocol=" + protocol_ + ", url=" + url_.spec() + " }"; +} +#endif + +bool ProtocolHandler::operator==(const ProtocolHandler& other) const { + return protocol_ == other.protocol_ && url_ == other.url_; +} + +bool ProtocolHandler::IsEquivalent(const ProtocolHandler& other) const { + return protocol_ == other.protocol_ && url_ == other.url_; +} + +bool ProtocolHandler::operator<(const ProtocolHandler& other) const { + return url_ < other.url_; +} + +} // namespace custom_handlers diff --git a/chromium/components/custom_handlers/protocol_handler.h b/chromium/components/custom_handlers/protocol_handler.h new file mode 100644 index 00000000000..318959495ad --- /dev/null +++ b/chromium/components/custom_handlers/protocol_handler.h @@ -0,0 +1,116 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_CUSTOM_HANDLERS_PROTOCOL_HANDLER_H_ +#define COMPONENTS_CUSTOM_HANDLERS_PROTOCOL_HANDLER_H_ + +#include <memory> +#include <string> + +#include "base/time/time.h" +#include "base/values.h" +#include "third_party/abseil-cpp/absl/types/optional.h" +#include "third_party/blink/public/common/security/protocol_handler_security_level.h" +#include "url/gurl.h" + +namespace custom_handlers { + +// A single tuple of (protocol, url, last_modified) that indicates how URLs +// of the given protocol should be rewritten to be handled. +// The |last_modified| field is used to correctly perform deletion +// of protocol handlers based on time ranges. +class ProtocolHandler { + public: + static ProtocolHandler CreateProtocolHandler( + const std::string& protocol, + const GURL& url, + blink::ProtocolHandlerSecurityLevel security_level = + blink::ProtocolHandlerSecurityLevel::kStrict); + + ProtocolHandler(const std::string& protocol, + const GURL& url, + base::Time last_modified, + blink::ProtocolHandlerSecurityLevel security_level); + + static ProtocolHandler CreateWebAppProtocolHandler( + const std::string& protocol, + const GURL& url, + const std::string& app_id); + + ProtocolHandler(const std::string& protocol, + const GURL& url, + const std::string& app_id, + base::Time last_modified, + blink::ProtocolHandlerSecurityLevel security_level); + + ProtocolHandler(const ProtocolHandler& other); + ~ProtocolHandler(); + + // Creates a ProtocolHandler with fields from the dictionary. Returns an + // empty ProtocolHandler if the input is invalid. + static ProtocolHandler CreateProtocolHandler(const base::Value::Dict& value); + + // Returns true if the dictionary value has all the necessary fields to + // define a ProtocolHandler. + static bool IsValidDict(const base::Value::Dict& value); + + // Return true if the protocol handler meets security constraints. + // Verify custom handler URLs security and syntax as well as the schemes + // safelist as described in steps 1, 2, 6 and 7 (except same origin). + // https://html.spec.whatwg.org/multipage/system-state.html#custom-handlers. + bool IsValid() const; + + // Returns true if this handler's url has the same origin as the given one. + bool IsSameOrigin(const ProtocolHandler& handler) const; + + // Canonical empty ProtocolHandler. + static const ProtocolHandler& EmptyProtocolHandler(); + + // Interpolates the given URL into the URL template of this handler. + GURL TranslateUrl(const GURL& url) const; + + // Returns true if the handlers are considered equivalent when determining + // if both handlers can be registered, or if a handler has previously been + // ignored. + bool IsEquivalent(const ProtocolHandler& other) const; + + // Encodes this protocol handler as a DictionaryValue. + base::Value::Dict Encode() const; + + // Returns a friendly name for |protocol| if one is available, otherwise + // this function returns |protocol|. + static std::u16string GetProtocolDisplayName(const std::string& protocol); + + // Returns a friendly name for |this.protocol_| if one is available, otherwise + // this function returns |this.protocol_|. + std::u16string GetProtocolDisplayName() const; + + const std::string& protocol() const { return protocol_; } + const GURL& url() const { return url_; } + const absl::optional<std::string>& web_app_id() const { return web_app_id_; } + const base::Time& last_modified() const { return last_modified_; } + + bool IsEmpty() const { return protocol_.empty(); } + +#if !defined(NDEBUG) + // Returns a string representation suitable for use in debugging. + std::string ToString() const; +#endif + + bool operator==(const ProtocolHandler& other) const; + bool operator<(const ProtocolHandler& other) const; + + private: + ProtocolHandler(); + + std::string protocol_; + GURL url_; + absl::optional<std::string> web_app_id_; + base::Time last_modified_; + blink::ProtocolHandlerSecurityLevel security_level_; +}; + +} // namespace custom_handlers + +#endif // COMPONENTS_CUSTOM_HANDLERS_PROTOCOL_HANDLER_H_ diff --git a/chromium/components/custom_handlers/protocol_handler_registry.cc b/chromium/components/custom_handlers/protocol_handler_registry.cc index 407528e8163..394408003db 100644 --- a/chromium/components/custom_handlers/protocol_handler_registry.cc +++ b/chromium/components/custom_handlers/protocol_handler_registry.cc @@ -14,19 +14,19 @@ #include "base/containers/contains.h" #include "base/memory/ref_counted.h" #include "base/notreached.h" +#include "base/observer_list.h" #include "build/build_config.h" #include "build/chromeos_buildflags.h" #include "components/custom_handlers/pref_names.h" +#include "components/custom_handlers/protocol_handler.h" #include "components/pref_registry/pref_registry_syncable.h" #include "components/prefs/pref_service.h" #include "components/user_prefs/user_prefs.h" #include "content/public/browser/child_process_security_policy.h" -#include "content/public/common/custom_handlers/protocol_handler.h" #include "third_party/abseil-cpp/absl/types/optional.h" using content::BrowserThread; using content::ChildProcessSecurityPolicy; -using content::ProtocolHandler; namespace custom_handlers { @@ -63,9 +63,9 @@ GURL TranslateUrl( // ProtocolHandlerRegistry ----------------------------------------------------- ProtocolHandlerRegistry::ProtocolHandlerRegistry( - content::BrowserContext* context, + PrefService* prefs, std::unique_ptr<Delegate> delegate) - : context_(context), + : prefs_(prefs), delegate_(std::move(delegate)), enabled_(true), is_loading_(false), @@ -118,8 +118,8 @@ bool ProtocolHandlerRegistry::AttemptReplace(const ProtocolHandler& handler) { ProtocolHandlerList to_replace(GetReplacedHandlers(handler)); if (to_replace.empty()) return false; - for (auto p = to_replace.begin(); p != to_replace.end(); ++p) { - RemoveHandler(*p); + for (const auto& replaced_handler : to_replace) { + RemoveHandler(replaced_handler); } if (make_new_handler_default) { OnAcceptRegisterProtocolHandler(handler); @@ -137,9 +137,9 @@ ProtocolHandlerRegistry::GetReplacedHandlers( const ProtocolHandlerList* handlers = GetHandlerList(handler.protocol()); if (!handlers) return replaced_handlers; - for (auto p = handlers->begin(); p != handlers->end(); ++p) { - if (handler.IsSameOrigin(*p)) { - replaced_handlers.push_back(*p); + for (const auto& old_handler : *handlers) { + if (handler.IsSameOrigin(old_handler)) { + replaced_handlers.push_back(old_handler); } } return replaced_handlers; @@ -181,9 +181,13 @@ void ProtocolHandlerRegistry::InitProtocolSettings() { is_loaded_ = true; is_loading_ = true; - PrefService* prefs = user_prefs::UserPrefs::Get(context_); - if (prefs->HasPrefPath(prefs::kCustomHandlersEnabled)) { - if (prefs->GetBoolean(prefs::kCustomHandlersEnabled)) { + if (!prefs_) { + is_loading_ = false; + return; + } + + if (prefs_->HasPrefPath(prefs::kCustomHandlersEnabled)) { + if (prefs_->GetBoolean(prefs::kCustomHandlersEnabled)) { Enable(); } else { Disable(); @@ -201,9 +205,7 @@ void ProtocolHandlerRegistry::InitProtocolSettings() { // For each default protocol handler, check that we are still registered // with the OS as the default application. if (delegate_->ShouldRemoveHandlersNotInOS()) { - for (ProtocolHandlerMap::const_iterator p = default_handlers_.begin(); - p != default_handlers_.end(); ++p) { - const std::string& protocol = p->second.protocol(); + for (const auto& [protocol, handler] : default_handlers_) { delegate_->CheckDefaultClientWithOS( protocol, GetDefaultWebClientCallback(protocol)); } @@ -212,18 +214,18 @@ void ProtocolHandlerRegistry::InitProtocolSettings() { int ProtocolHandlerRegistry::GetHandlerIndex(base::StringPiece scheme) const { DCHECK_CURRENTLY_ON(BrowserThread::UI); - const ProtocolHandler& handler = GetHandlerFor(scheme); - if (handler.IsEmpty()) + const ProtocolHandler& candidate = GetHandlerFor(scheme); + if (candidate.IsEmpty()) return -1; const ProtocolHandlerList* handlers = GetHandlerList(scheme); if (!handlers) return -1; - ProtocolHandlerList::const_iterator p; - int i; - for (i = 0, p = handlers->begin(); p != handlers->end(); ++p, ++i) { - if (*p == handler) + int i = 0; + for (const auto& handler : *handlers) { + if (handler == candidate) return i; + i++; } return -1; } @@ -242,8 +244,8 @@ ProtocolHandlerRegistry::ProtocolHandlerList ProtocolHandlerRegistry::GetUserDefinedHandlers(base::Time begin, base::Time end) const { ProtocolHandlerRegistry::ProtocolHandlerList result; - for (const auto& entry : user_protocol_handlers_) { - for (const ProtocolHandler& handler : entry.second) { + for (const auto& [protocol, handlers_list] : user_protocol_handlers_) { + for (const ProtocolHandler& handler : handlers_list) { if (base::Contains(predefined_protocol_handlers_, handler)) continue; if (begin <= handler.last_modified() && handler.last_modified() < end) @@ -281,10 +283,9 @@ ProtocolHandlerRegistry::GetIgnoredHandlers() { void ProtocolHandlerRegistry::GetRegisteredProtocols( std::vector<std::string>* output) const { DCHECK_CURRENTLY_ON(BrowserThread::UI); - ProtocolHandlerMultiMap::const_iterator p; - for (p = protocol_handlers_.begin(); p != protocol_handlers_.end(); ++p) { - if (!p->second.empty()) - output->push_back(p->first); + for (const auto& [protocol, handlers_list] : protocol_handlers_) { + if (!handlers_list.empty()) + output->push_back(protocol); } } @@ -323,10 +324,8 @@ bool ProtocolHandlerRegistry::HasPolicyRegisteredHandler( bool ProtocolHandlerRegistry::IsIgnored(const ProtocolHandler& handler) const { DCHECK_CURRENTLY_ON(BrowserThread::UI); - ProtocolHandlerList::const_iterator i; - for (i = ignored_protocol_handlers_.begin(); - i != ignored_protocol_handlers_.end(); ++i) { - if (*i == handler) { + for (const auto& ignored_handler : ignored_protocol_handlers_) { + if (ignored_handler == handler) { return true; } } @@ -340,9 +339,8 @@ bool ProtocolHandlerRegistry::HasRegisteredEquivalent( if (!handlers) { return false; } - ProtocolHandlerList::const_iterator i; - for (i = handlers->begin(); i != handlers->end(); ++i) { - if (handler.IsEquivalent(*i)) { + for (const auto& registered_handler : *handlers) { + if (handler.IsEquivalent(registered_handler)) { return true; } } @@ -352,10 +350,8 @@ bool ProtocolHandlerRegistry::HasRegisteredEquivalent( bool ProtocolHandlerRegistry::HasIgnoredEquivalent( const ProtocolHandler& handler) const { DCHECK_CURRENTLY_ON(BrowserThread::UI); - ProtocolHandlerList::const_iterator i; - for (i = ignored_protocol_handlers_.begin(); - i != ignored_protocol_handlers_.end(); ++i) { - if (handler.IsEquivalent(*i)) { + for (const auto& ignored_handler : ignored_protocol_handlers_) { + if (handler.IsEquivalent(ignored_handler)) { return true; } } @@ -444,9 +440,8 @@ void ProtocolHandlerRegistry::Enable() { return; } enabled_ = true; - ProtocolHandlerMap::const_iterator p; - for (p = default_handlers_.begin(); p != default_handlers_.end(); ++p) { - delegate_->RegisterExternalHandler(p->first); + for (const auto& [protocol, handler] : default_handlers_) { + delegate_->RegisterExternalHandler(protocol); } Save(); NotifyChanged(); @@ -459,9 +454,8 @@ void ProtocolHandlerRegistry::Disable() { } enabled_ = false; - ProtocolHandlerMap::const_iterator p; - for (p = default_handlers_.begin(); p != default_handlers_.end(); ++p) { - delegate_->DeregisterExternalHandler(p->first); + for (const auto& [protocol, handler] : default_handlers_) { + delegate_->DeregisterExternalHandler(protocol); } Save(); NotifyChanged(); @@ -510,15 +504,16 @@ void ProtocolHandlerRegistry::Save() { if (is_loading_) { return; } - std::unique_ptr<base::Value> registered_protocol_handlers( - EncodeRegisteredHandlers()); - std::unique_ptr<base::Value> ignored_protocol_handlers( - EncodeIgnoredHandlers()); - PrefService* prefs = user_prefs::UserPrefs::Get(context_); - prefs->Set(prefs::kRegisteredProtocolHandlers, *registered_protocol_handlers); - prefs->Set(prefs::kIgnoredProtocolHandlers, *ignored_protocol_handlers); - prefs->SetBoolean(prefs::kCustomHandlersEnabled, enabled_); + if (!prefs_) + return; + + base::Value registered_protocol_handlers(EncodeRegisteredHandlers()); + base::Value ignored_protocol_handlers(EncodeIgnoredHandlers()); + + prefs_->Set(prefs::kRegisteredProtocolHandlers, registered_protocol_handlers); + prefs_->Set(prefs::kIgnoredProtocolHandlers, ignored_protocol_handlers); + prefs_->SetBoolean(prefs::kCustomHandlersEnabled, enabled_); } const ProtocolHandlerRegistry::ProtocolHandlerList* @@ -561,30 +556,28 @@ void ProtocolHandlerRegistry::InsertHandler(const ProtocolHandler& handler) { protocol_handlers_[handler.protocol()] = new_list; } -base::Value* ProtocolHandlerRegistry::EncodeRegisteredHandlers() { +base::Value::List ProtocolHandlerRegistry::EncodeRegisteredHandlers() { DCHECK_CURRENTLY_ON(BrowserThread::UI); - base::ListValue* protocol_handlers = new base::ListValue(); - for (auto i = user_protocol_handlers_.begin(); - i != user_protocol_handlers_.end(); ++i) { - for (auto j = i->second.begin(); j != i->second.end(); ++j) { - std::unique_ptr<base::DictionaryValue> encoded = j->Encode(); - if (IsDefault(*j)) { - encoded->Set("default", std::make_unique<base::Value>(true)); + base::Value::List encoded_handlers; + for (const auto& [protocol, handlers_list] : user_protocol_handlers_) { + for (const auto& handler : handlers_list) { + base::Value::Dict encoded = handler.Encode(); + if (IsDefault(handler)) { + encoded.Set("default", true); } - protocol_handlers->Append(std::move(encoded)); + encoded_handlers.Append(std::move(encoded)); } } - return protocol_handlers; + return encoded_handlers; } -base::Value* ProtocolHandlerRegistry::EncodeIgnoredHandlers() { +base::Value::List ProtocolHandlerRegistry::EncodeIgnoredHandlers() { DCHECK_CURRENTLY_ON(BrowserThread::UI); - base::ListValue* handlers = new base::ListValue(); - for (auto i = user_ignored_protocol_handlers_.begin(); - i != user_ignored_protocol_handlers_.end(); ++i) { - handlers->Append(i->Encode()); + base::Value::List encoded_handlers; + for (const auto& handler : user_ignored_protocol_handlers_) { + encoded_handlers.Append(handler.Encode()); } - return handlers; + return encoded_handlers; } void ProtocolHandlerRegistry::NotifyChanged() { @@ -617,24 +610,21 @@ bool ProtocolHandlerRegistry::RegisterProtocolHandler( return true; } -std::vector<const base::DictionaryValue*> +std::vector<const base::Value::Dict*> ProtocolHandlerRegistry::GetHandlersFromPref(const char* pref_name) const { DCHECK_CURRENTLY_ON(BrowserThread::UI); - std::vector<const base::DictionaryValue*> result; - PrefService* prefs = user_prefs::UserPrefs::Get(context_); - if (!prefs->HasPrefPath(pref_name)) { + std::vector<const base::Value::Dict*> result; + if (!prefs_ || !prefs_->HasPrefPath(pref_name)) { return result; } - const base::Value* handlers = prefs->GetList(pref_name); + const base::Value* handlers = prefs_->GetList(pref_name); if (handlers) { - for (const auto& dict : handlers->GetListDeprecated()) { - if (!dict.is_dict()) - continue; - const base::DictionaryValue* dict_value = - static_cast<const base::DictionaryValue*>(&dict); - if (ProtocolHandler::IsValidDict(dict_value)) { - result.push_back(dict_value); + for (const auto& list_item : handlers->GetList()) { + if (const base::Value::Dict* encoded_handler = list_item.GetIfDict()) { + if (ProtocolHandler::IsValidDict(*encoded_handler)) { + result.push_back(encoded_handler); + } } } } @@ -644,15 +634,14 @@ ProtocolHandlerRegistry::GetHandlersFromPref(const char* pref_name) const { void ProtocolHandlerRegistry::RegisterProtocolHandlersFromPref( const char* pref_name, const HandlerSource source) { - std::vector<const base::DictionaryValue*> registered_handlers = + std::vector<const base::Value::Dict*> registered_handlers = GetHandlersFromPref(pref_name); - for (std::vector<const base::DictionaryValue*>::const_iterator p = - registered_handlers.begin(); - p != registered_handlers.end(); ++p) { - ProtocolHandler handler = ProtocolHandler::CreateProtocolHandler(*p); + for (const auto* encoded_handler : registered_handlers) { + ProtocolHandler handler = + ProtocolHandler::CreateProtocolHandler(*encoded_handler); if (!RegisterProtocolHandler(handler, source)) continue; - if ((*p)->FindBoolKey("default").value_or(false)) { + if (encoded_handler->FindBool("default").value_or(false)) { SetDefault(handler); } } @@ -675,13 +664,11 @@ void ProtocolHandlerRegistry::IgnoreProtocolHandler( void ProtocolHandlerRegistry::IgnoreProtocolHandlersFromPref( const char* pref_name, const HandlerSource source) { - std::vector<const base::DictionaryValue*> ignored_handlers = + std::vector<const base::Value::Dict*> ignored_handlers = GetHandlersFromPref(pref_name); - for (std::vector<const base::DictionaryValue*>::const_iterator p = - ignored_handlers.begin(); - p != ignored_handlers.end(); ++p) { - IgnoreProtocolHandler(ProtocolHandler::CreateProtocolHandler(*p), source); - } + for (const auto* encoded_handler : ignored_handlers) + IgnoreProtocolHandler( + ProtocolHandler::CreateProtocolHandler(*encoded_handler), source); } bool ProtocolHandlerRegistry::HandlerExists(const ProtocolHandler& handler, diff --git a/chromium/components/custom_handlers/protocol_handler_registry.h b/chromium/components/custom_handlers/protocol_handler_registry.h index fa0a2f00ddb..887c0c316d4 100644 --- a/chromium/components/custom_handlers/protocol_handler_registry.h +++ b/chromium/components/custom_handlers/protocol_handler_registry.h @@ -14,21 +14,24 @@ #include "base/memory/weak_ptr.h" #include "base/observer_list.h" #include "base/task/sequenced_task_runner_helpers.h" +#include "base/time/time.h" #include "base/values.h" #include "components/keyed_service/core/keyed_service.h" -#include "content/public/browser/browser_context.h" #include "content/public/browser/browser_thread.h" -#include "content/public/common/custom_handlers/protocol_handler.h" namespace user_prefs { class PrefRegistrySyncable; } -using content::ProtocolHandler; +class PrefService; +class GURL; + using DefaultClientCallback = base::OnceCallback<void(bool)>; namespace custom_handlers { +class ProtocolHandler; + // This is where handlers for protocols registered with // navigator.registerProtocolHandler() are registered. Each Profile owns an // instance of this class, which is initialized on browser start through @@ -73,7 +76,7 @@ class ProtocolHandlerRegistry : public KeyedService { }; // Creates a new instance. - ProtocolHandlerRegistry(content::BrowserContext* context, + ProtocolHandlerRegistry(PrefService* prefs, std::unique_ptr<Delegate> delegate); ProtocolHandlerRegistry(const ProtocolHandlerRegistry&) = delete; @@ -246,11 +249,11 @@ class ProtocolHandlerRegistry : public KeyedService { // Returns a JSON list of protocol handlers. The caller is responsible for // deleting this Value. - base::Value* EncodeRegisteredHandlers(); + base::Value::List EncodeRegisteredHandlers(); // Returns a JSON list of ignored protocol handlers. The caller is // responsible for deleting this Value. - base::Value* EncodeIgnoredHandlers(); + base::Value::List EncodeIgnoredHandlers(); // Notifies observers of a change to the registry. void NotifyChanged(); @@ -273,9 +276,12 @@ class ProtocolHandlerRegistry : public KeyedService { ProtocolHandlerList GetUserIgnoredHandlers(base::Time begin, base::Time end) const; - // Get the DictionaryValues stored under the given pref name that are valid + // Get the Dict values stored under the given pref name that are valid // ProtocolHandler values. - std::vector<const base::DictionaryValue*> GetHandlersFromPref( + // These pointers may be invalidated by other changes in the preferences + // storage, hence they must not be stored in a way that outlives the current + // stack frame. + std::vector<const base::Value::Dict*> GetHandlersFromPref( const char* pref_name) const; // Ignores future requests to register the given protocol handler. @@ -337,8 +343,9 @@ class ProtocolHandlerRegistry : public KeyedService { // Protocol handlers that are the defaults for a given protocol. ProtocolHandlerMap default_handlers_; - // The browser context that owns this ProtocolHandlerRegistry. - raw_ptr<content::BrowserContext> context_; + // The PrefService to store the registered handlers in the user profile (it + // may be null for testing) + raw_ptr<PrefService> prefs_ = nullptr; // The Delegate that registers / deregisters external handlers on our behalf. std::unique_ptr<Delegate> delegate_; diff --git a/chromium/components/custom_handlers/protocol_handler_registry_browsertest.cc b/chromium/components/custom_handlers/protocol_handler_registry_browsertest.cc new file mode 100644 index 00000000000..5d3a0025edd --- /dev/null +++ b/chromium/components/custom_handlers/protocol_handler_registry_browsertest.cc @@ -0,0 +1,226 @@ +// Copyright (c) 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <memory> +#include <string> + +#include "base/scoped_observation.h" +#include "base/strings/utf_string_conversions.h" +#include "base/test/scoped_feature_list.h" +#include "components/custom_handlers/protocol_handler.h" +#include "components/custom_handlers/protocol_handler_registry.h" +#include "components/custom_handlers/simple_protocol_handler_registry_factory.h" +#include "content/public/browser/navigation_controller.h" +#include "content/public/browser/navigation_entry.h" +#include "content/public/browser/web_contents.h" +#include "content/public/common/content_client.h" +#include "content/public/common/content_features.h" +#include "content/public/test/browser_test.h" +#include "content/public/test/browser_test_utils.h" +#include "content/public/test/content_browser_test.h" +#include "content/public/test/content_browser_test_utils.h" +#include "content/public/test/fenced_frame_test_util.h" +#include "content/shell/browser/shell.h" +#include "net/test/embedded_test_server/embedded_test_server.h" + +using content::WebContents; + +namespace { + +using custom_handlers::ProtocolHandlerRegistry; + +class ProtocolHandlerChangeWaiter : public ProtocolHandlerRegistry::Observer { + public: + explicit ProtocolHandlerChangeWaiter(ProtocolHandlerRegistry* registry) { + registry_observation_.Observe(registry); + } + ProtocolHandlerChangeWaiter(const ProtocolHandlerChangeWaiter&) = delete; + ProtocolHandlerChangeWaiter& operator=(const ProtocolHandlerChangeWaiter&) = + delete; + ~ProtocolHandlerChangeWaiter() override = default; + + void Wait() { run_loop_.Run(); } + // ProtocolHandlerRegistry::Observer: + void OnProtocolHandlerRegistryChanged() override { run_loop_.Quit(); } + + private: + base::ScopedObservation<custom_handlers::ProtocolHandlerRegistry, + custom_handlers::ProtocolHandlerRegistry::Observer> + registry_observation_{this}; + base::RunLoop run_loop_; +}; + +} // namespace + +namespace custom_handlers { + +class RegisterProtocolHandlerBrowserTest : public content::ContentBrowserTest { + public: + RegisterProtocolHandlerBrowserTest() = default; + + void SetUpOnMainThread() override { + embedded_test_server()->ServeFilesFromSourceDirectory( + "components/test/data/"); + } + + void AddProtocolHandler(const std::string& protocol, const GURL& url) { + ProtocolHandler handler = + ProtocolHandler::CreateProtocolHandler(protocol, url); + ProtocolHandlerRegistry* registry = + SimpleProtocolHandlerRegistryFactory::GetForBrowserContext( + browser_context(), true); + // Fake that this registration is happening on profile startup. Otherwise + // it'll try to register with the OS, which causes DCHECKs on Windows when + // running as admin on Windows 7. + registry->SetIsLoading(true); + registry->OnAcceptRegisterProtocolHandler(handler); + registry->SetIsLoading(true); + ASSERT_TRUE(registry->IsHandledProtocol(protocol)); + } + + void RemoveProtocolHandler(const std::string& protocol, const GURL& url) { + ProtocolHandler handler = + ProtocolHandler::CreateProtocolHandler(protocol, url); + ProtocolHandlerRegistry* registry = + SimpleProtocolHandlerRegistryFactory::GetForBrowserContext( + browser_context(), true); + registry->RemoveHandler(handler); + ASSERT_FALSE(registry->IsHandledProtocol(protocol)); + } + + content::WebContents* web_contents() { return shell()->web_contents(); } + content::BrowserContext* browser_context() { + return web_contents()->GetBrowserContext(); + } + + protected: + content::test::FencedFrameTestHelper& fenced_frame_test_helper() { + return fenced_frame_helper_; + } + + private: + content::test::FencedFrameTestHelper fenced_frame_helper_; +}; + +IN_PROC_BROWSER_TEST_F(RegisterProtocolHandlerBrowserTest, CustomHandler) { + ASSERT_TRUE(embedded_test_server()->Start()); + GURL handler_url = embedded_test_server()->GetURL("/custom_handler.html"); + AddProtocolHandler("news", handler_url); + + ASSERT_TRUE(NavigateToURL(shell(), GURL("news:test"), handler_url)); + + ASSERT_EQ(handler_url, web_contents()->GetLastCommittedURL()); + + // Also check redirects. + GURL redirect_url = + embedded_test_server()->GetURL("/server-redirect?news:test"); + ASSERT_TRUE(NavigateToURL(shell(), redirect_url, handler_url)); + + ASSERT_EQ(handler_url, web_contents()->GetLastCommittedURL()); +} + +// https://crbug.com/178097: Implement registerProtocolHandler on Android +#if !BUILDFLAG(IS_ANDROID) +IN_PROC_BROWSER_TEST_F(RegisterProtocolHandlerBrowserTest, + IgnoreRequestWithoutUserGesture) { + ASSERT_TRUE(embedded_test_server()->Start()); + ASSERT_TRUE( + NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html"))); + + // Ensure the registry is currently empty. + GURL url("web+search:testing"); + ProtocolHandlerRegistry* registry = + SimpleProtocolHandlerRegistryFactory::GetForBrowserContext( + browser_context(), true); + ASSERT_EQ(0u, registry->GetHandlersFor(url.scheme()).size()); + + // Attempt to add an entry. + ProtocolHandlerChangeWaiter waiter(registry); + ASSERT_TRUE(content::ExecuteScriptWithoutUserGesture( + web_contents(), + "navigator.registerProtocolHandler('web+" + "search', 'test.html?%s', 'test');")); + waiter.Wait(); + + // Verify the registration is ignored if no user gesture involved. + ASSERT_EQ(1u, registry->GetHandlersFor(url.scheme()).size()); + ASSERT_FALSE(registry->IsHandledProtocol(url.scheme())); +} + +// FencedFrames can not register to handle any protocols. +IN_PROC_BROWSER_TEST_F(RegisterProtocolHandlerBrowserTest, FencedFrame) { + ASSERT_TRUE(embedded_test_server()->Start()); + ASSERT_TRUE( + NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html"))); + + // Create a FencedFrame. + content::RenderFrameHost* fenced_frame_host = + fenced_frame_test_helper().CreateFencedFrame( + web_contents()->GetMainFrame(), + embedded_test_server()->GetURL("/fenced_frames/title1.html")); + ASSERT_TRUE(fenced_frame_host); + + // Ensure the registry is currently empty. + GURL url("web+search:testing"); + ProtocolHandlerRegistry* registry = + SimpleProtocolHandlerRegistryFactory::GetForBrowserContext( + browser_context(), true); + ASSERT_EQ(0u, registry->GetHandlersFor(url.scheme()).size()); + + // Attempt to add an entry. + ProtocolHandlerChangeWaiter waiter(registry); + ASSERT_TRUE(content::ExecuteScript(fenced_frame_host, + "navigator.registerProtocolHandler('web+" + "search', 'test.html?%s', 'test');")); + waiter.Wait(); + + // Ensure the registry is still empty. + ASSERT_EQ(0u, registry->GetHandlersFor(url.scheme()).size()); +} +#endif + +// https://crbug.com/178097: Implement registerProtocolHandler on Android +#if !BUILDFLAG(IS_ANDROID) +class RegisterProtocolHandlerAndServiceWorkerInterceptor + : public RegisterProtocolHandlerBrowserTest { + public: + void SetUpOnMainThread() override { + RegisterProtocolHandlerBrowserTest::SetUpOnMainThread(); + + ASSERT_TRUE(embedded_test_server()->Start()); + + // Navigate to the test page. + ASSERT_TRUE(NavigateToURL( + shell(), embedded_test_server()->GetURL( + "/protocol_handler/service_workers/" + "test_protocol_handler_and_service_workers.html"))); + } +}; + +// TODO(crbug.com/1204127): Fix flakiness. +IN_PROC_BROWSER_TEST_F(RegisterProtocolHandlerAndServiceWorkerInterceptor, + DISABLED_RegisterFetchListenerForHTMLHandler) { + // Register a service worker intercepting requests to the HTML handler. + EXPECT_EQ(true, + content::EvalJs(shell(), "registerFetchListenerForHTMLHandler();")); + + { + // Register a HTML handler with a user gesture. + ProtocolHandlerRegistry* registry = + SimpleProtocolHandlerRegistryFactory::GetForBrowserContext( + browser_context(), true); + ProtocolHandlerChangeWaiter waiter(registry); + ASSERT_TRUE(content::ExecJs(shell(), "registerHTMLHandler();")); + waiter.Wait(); + } + + // Verify that a page with the registered scheme is managed by the service + // worker, not the HTML handler. + EXPECT_EQ(true, + content::EvalJs(shell(), + "pageWithCustomSchemeHandledByServiceWorker();")); +} +#endif + +} // namespace custom_handlers diff --git a/chromium/components/custom_handlers/protocol_handler_registry_unittest.cc b/chromium/components/custom_handlers/protocol_handler_registry_unittest.cc index dc49e122e51..58703a4ca1b 100644 --- a/chromium/components/custom_handlers/protocol_handler_registry_unittest.cc +++ b/chromium/components/custom_handlers/protocol_handler_registry_unittest.cc @@ -14,9 +14,9 @@ #include "base/run_loop.h" #include "base/scoped_observation.h" #include "base/strings/utf_string_conversions.h" -#include "base/task/post_task.h" #include "build/build_config.h" #include "components/custom_handlers/pref_names.h" +#include "components/custom_handlers/protocol_handler.h" #include "components/custom_handlers/protocol_handler_registry.h" #include "components/custom_handlers/test_protocol_handler_registry_delegate.h" #include "components/pref_registry/pref_registry_syncable.h" @@ -24,7 +24,6 @@ #include "components/sync_preferences/testing_pref_service_syncable.h" #include "components/user_prefs/user_prefs.h" #include "content/public/browser/browser_task_traits.h" -#include "content/public/common/custom_handlers/protocol_handler.h" #include "content/public/test/browser_task_environment.h" #include "content/public/test/test_browser_context.h" #include "content/public/test/test_renderer_host.h" @@ -32,26 +31,22 @@ #include "third_party/blink/public/common/security/protocol_handler_security_level.h" using content::BrowserThread; -using content::ProtocolHandler; namespace custom_handlers { -std::unique_ptr<base::DictionaryValue> GetProtocolHandlerValue( - const std::string& protocol, - const std::string& url) { - auto value = std::make_unique<base::DictionaryValue>(); - value->SetString("protocol", protocol); - value->SetString("url", url); - return value; +base::Value GetProtocolHandlerValue(const std::string& protocol, + const std::string& url) { + base::Value::Dict value; + value.Set("protocol", protocol); + value.Set("url", url); + return base::Value(std::move(value)); } -std::unique_ptr<base::DictionaryValue> GetProtocolHandlerValueWithDefault( - const std::string& protocol, - const std::string& url, - bool is_default) { - std::unique_ptr<base::DictionaryValue> value = - GetProtocolHandlerValue(protocol, url); - value->SetBoolean("default", is_default); +base::Value GetProtocolHandlerValueWithDefault(const std::string& protocol, + const std::string& url, + bool is_default) { + base::Value value = GetProtocolHandlerValue(protocol, url); + value.GetDict().Set("default", is_default); return value; } @@ -196,8 +191,8 @@ class ProtocolHandlerRegistryTest : public testing::Test { DCHECK(browser_context_); auto delegate = std::make_unique<TestProtocolHandlerRegistryDelegate>(); delegate_ = delegate.get(); - registry_ = std::make_unique<ProtocolHandlerRegistry>( - browser_context_.get(), std::move(delegate)); + registry_ = std::make_unique<ProtocolHandlerRegistry>(GetPrefs(), + std::move(delegate)); if (initialize) registry_->InitProtocolSettings(); } @@ -301,8 +296,7 @@ TEST_F(ProtocolHandlerRegistryTest, Encode) { ProtocolHandler handler("news", GURL("https://example.com"), "app_id", now, blink::ProtocolHandlerSecurityLevel::kStrict); auto value = handler.Encode(); - ProtocolHandler recreated = - ProtocolHandler::CreateProtocolHandler(value.get()); + ProtocolHandler recreated = ProtocolHandler::CreateProtocolHandler(value); EXPECT_EQ("news", recreated.protocol()); EXPECT_EQ(GURL("https://example.com"), recreated.url()); EXPECT_EQ(now, recreated.last_modified()); diff --git a/chromium/components/custom_handlers/protocol_handler_throttle.cc b/chromium/components/custom_handlers/protocol_handler_throttle.cc index 690cfa8ae39..6e474efbac2 100644 --- a/chromium/components/custom_handlers/protocol_handler_throttle.cc +++ b/chromium/components/custom_handlers/protocol_handler_throttle.cc @@ -16,14 +16,6 @@ ProtocolHandlerThrottle::ProtocolHandlerThrottle( void ProtocolHandlerThrottle::WillStartRequest( network::ResourceRequest* request, bool* defer) { - // Don't translate the urn: scheme URL while loading the resource from the - // specified web bundle. - // TODO(https://crbug.com/1257045): Remove this when we drop urn: scheme - // support in WebBundles. - if (request->web_bundle_token_params && - request->url.SchemeIs(url::kUrnScheme)) { - return; - } TranslateUrl(request->url); } diff --git a/chromium/components/custom_handlers/register_protocol_handler_permission_request.h b/chromium/components/custom_handlers/register_protocol_handler_permission_request.h index fa1cb430931..173e14eb2f1 100644 --- a/chromium/components/custom_handlers/register_protocol_handler_permission_request.h +++ b/chromium/components/custom_handlers/register_protocol_handler_permission_request.h @@ -7,15 +7,13 @@ #include "base/callback_helpers.h" #include "base/memory/raw_ptr.h" +#include "components/custom_handlers/protocol_handler.h" #include "components/permissions/permission_request.h" -#include "content/public/common/custom_handlers/protocol_handler.h" namespace permissions { enum class RequestType; } // namespace permissions -using content::ProtocolHandler; - class GURL; namespace custom_handlers { class ProtocolHandlerRegistry; diff --git a/chromium/components/custom_handlers/simple_protocol_handler_registry_factory.cc b/chromium/components/custom_handlers/simple_protocol_handler_registry_factory.cc new file mode 100644 index 00000000000..de310882ebb --- /dev/null +++ b/chromium/components/custom_handlers/simple_protocol_handler_registry_factory.cc @@ -0,0 +1,66 @@ +// Copyright (c) 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/custom_handlers/simple_protocol_handler_registry_factory.h" + +#include <memory> + +#include "base/no_destructor.h" +#include "build/build_config.h" +#include "components/custom_handlers/protocol_handler_registry.h" +#include "components/custom_handlers/test_protocol_handler_registry_delegate.h" +#include "components/keyed_service/content/browser_context_dependency_manager.h" + +namespace custom_handlers { + +// static +SimpleProtocolHandlerRegistryFactory* +SimpleProtocolHandlerRegistryFactory::GetInstance() { + static base::NoDestructor<SimpleProtocolHandlerRegistryFactory> factory; + return factory.get(); +} + +// static +ProtocolHandlerRegistry* +SimpleProtocolHandlerRegistryFactory::GetForBrowserContext( + content::BrowserContext* context, + bool create) { + return static_cast<ProtocolHandlerRegistry*>( + GetInstance()->GetServiceForBrowserContext(context, create)); +} + +SimpleProtocolHandlerRegistryFactory::SimpleProtocolHandlerRegistryFactory() + : BrowserContextKeyedServiceFactory( + "ProtocolHandlerRegistry", + BrowserContextDependencyManager::GetInstance()) {} + +// Will be created when initializing profile_io_data, so we might +// as well have the framework create this along with other +// PKSs to preserve orderly civic conduct :) +bool SimpleProtocolHandlerRegistryFactory::ServiceIsCreatedWithBrowserContext() + const { + return true; +} + +// Do not create this service for tests. MANY tests will fail +// due to the threading requirements of this service. ALSO, +// not creating this increases test isolation (which is GOOD!) +bool SimpleProtocolHandlerRegistryFactory::ServiceIsNULLWhileTesting() const { + return true; +} + +KeyedService* SimpleProtocolHandlerRegistryFactory::BuildServiceInstanceFor( + content::BrowserContext* context) const { + // We can't ensure the UserPref has been set, so we pass a nullptr + // PrefService. + ProtocolHandlerRegistry* registry = new ProtocolHandlerRegistry( + nullptr, std::make_unique<TestProtocolHandlerRegistryDelegate>()); + + // Must be called as a part of the creation process. + registry->InitProtocolSettings(); + + return registry; +} + +} // namespace custom_handlers diff --git a/chromium/components/custom_handlers/simple_protocol_handler_registry_factory.h b/chromium/components/custom_handlers/simple_protocol_handler_registry_factory.h new file mode 100644 index 00000000000..a1b0f64bf39 --- /dev/null +++ b/chromium/components/custom_handlers/simple_protocol_handler_registry_factory.h @@ -0,0 +1,67 @@ +// Copyright (c) 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_CUSTOM_HANDLERS_SIMPLE_PROTOCOL_HANDLER_REGISTRY_FACTORY_H_ +#define COMPONENTS_CUSTOM_HANDLERS_SIMPLE_PROTOCOL_HANDLER_REGISTRY_FACTORY_H_ + +#include "base/no_destructor.h" +#include "components/keyed_service/content/browser_context_keyed_service_factory.h" + +namespace custom_handlers { + +class ProtocolHandlerRegistry; + +// Simgleton that owns all the ProtocolHandlerRegistrys and associates them with +// BrowserContext instances. +// +// It creates the Registry instances without access to the PrefService storage. +// This is useful for testing purposes, since we can't be sure the UserPref +// storage has been set (eg, Web Platform Tests). +// +// It uses the TestProtocolHandlerRegistryDelegate, hence it doesn't provide any +// OS integration during the registration process. +// +// It does not override the GetBrowserContextToUse method from +// BrowserContextKeyedServiceFactory, which means that no service is returned in +// Incognito. +class SimpleProtocolHandlerRegistryFactory + : public BrowserContextKeyedServiceFactory { + public: + // Returns the singleton instance of the ProtocolHandlerRegistryFactory. + static SimpleProtocolHandlerRegistryFactory* GetInstance(); + + // Returns the ProtocolHandlerRegistry that provides intent registration for + // |context|. Ownership stays with this factory object. + // Allows the caller to indicate that the KeyedService should not be created + // if it's not registered. This is particularly useful for testings purposes, + // since the TestBrowserContext doesn't implement the TwoPhaseShutdown of + // KeyedService instances. + static ProtocolHandlerRegistry* GetForBrowserContext( + content::BrowserContext* context, + bool create = false); + + SimpleProtocolHandlerRegistryFactory( + const SimpleProtocolHandlerRegistryFactory&) = delete; + SimpleProtocolHandlerRegistryFactory& operator=( + const SimpleProtocolHandlerRegistryFactory&) = delete; + + protected: + // BrowserContextKeyedServiceFactory implementation. + bool ServiceIsCreatedWithBrowserContext() const override; + bool ServiceIsNULLWhileTesting() const override; + + private: + friend class base::NoDestructor<SimpleProtocolHandlerRegistryFactory>; + + SimpleProtocolHandlerRegistryFactory(); + ~SimpleProtocolHandlerRegistryFactory() override = default; + + // BrowserContextKeyedServiceFactory implementation. + KeyedService* BuildServiceInstanceFor( + content::BrowserContext* profile) const override; +}; + +} // namespace custom_handlers + +#endif // COMPONENTS_CUSTOM_HANDLERS_SIMPLE_PROTOCOL_HANDLER_REGISTRY_FACTORY_H |