summaryrefslogtreecommitdiff
path: root/chromium/components/custom_handlers
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2022-05-17 17:24:03 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2022-06-22 07:51:41 +0000
commit774f54339e5db91f785733232d3950366db65d07 (patch)
tree068e1b47bd1af94d77094ed12b604a6b83d9c22a /chromium/components/custom_handlers
parentf7eaed5286974984ba5f9e3189d8f49d03e99f81 (diff)
downloadqtwebengine-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')
-rw-r--r--chromium/components/custom_handlers/BUILD.gn27
-rw-r--r--chromium/components/custom_handlers/DEPS6
-rw-r--r--chromium/components/custom_handlers/OWNERS4
-rw-r--r--chromium/components/custom_handlers/README.md180
-rw-r--r--chromium/components/custom_handlers/protocol_handler.cc187
-rw-r--r--chromium/components/custom_handlers/protocol_handler.h116
-rw-r--r--chromium/components/custom_handlers/protocol_handler_registry.cc171
-rw-r--r--chromium/components/custom_handlers/protocol_handler_registry.h27
-rw-r--r--chromium/components/custom_handlers/protocol_handler_registry_browsertest.cc226
-rw-r--r--chromium/components/custom_handlers/protocol_handler_registry_unittest.cc36
-rw-r--r--chromium/components/custom_handlers/protocol_handler_throttle.cc8
-rw-r--r--chromium/components/custom_handlers/register_protocol_handler_permission_request.h4
-rw-r--r--chromium/components/custom_handlers/simple_protocol_handler_registry_factory.cc66
-rw-r--r--chromium/components/custom_handlers/simple_protocol_handler_registry_factory.h67
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