blob: b7a719d4f3f4587e168296938027cc6add17451c [file] [log] [blame]
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/custom_handlers/protocol_handler_registry.h"
#include <stddef.h>
#include <utility>
#include "base/check_op.h"
#include "base/command_line.h"
#include "base/containers/contains.h"
#include "base/functional/bind.h"
#include "base/memory/ref_counted.h"
#include "base/notreached.h"
#include "base/observer_list.h"
#include "base/ranges/algorithm.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 "third_party/abseil-cpp/absl/types/optional.h"
#include "url/url_util.h"
using content::BrowserThread;
using content::ChildProcessSecurityPolicy;
namespace custom_handlers {
namespace {
const ProtocolHandler& LookupHandler(
const ProtocolHandlerRegistry::ProtocolHandlerMap& handler_map,
base::StringPiece scheme) {
auto p = handler_map.find(scheme);
if (p != handler_map.end())
return p->second;
return ProtocolHandler::EmptyProtocolHandler();
}
GURL TranslateUrl(
const ProtocolHandlerRegistry::ProtocolHandlerMap& handler_map,
const GURL& url) {
const ProtocolHandler& handler =
LookupHandler(handler_map, url.scheme_piece());
if (handler.IsEmpty())
return GURL();
GURL translated_url(handler.TranslateUrl(url));
if (!translated_url.is_valid())
return GURL();
return translated_url;
}
} // namespace
// ProtocolHandlerRegistry -----------------------------------------------------
std::unique_ptr<ProtocolHandlerRegistry> ProtocolHandlerRegistry::Create(
PrefService* prefs,
std::unique_ptr<Delegate> delegate) {
auto registry =
std::make_unique<ProtocolHandlerRegistry>(prefs, std::move(delegate));
// If installing defaults, they must be installed prior calling
// InitProtocolSettings.
registry->InstallPredefinedHandlers();
registry->InitProtocolSettings();
return registry;
}
ProtocolHandlerRegistry::ProtocolHandlerRegistry(
PrefService* prefs,
std::unique_ptr<Delegate> delegate)
: prefs_(prefs),
delegate_(std::move(delegate)),
enabled_(true),
is_loading_(false),
is_loaded_(false) {}
bool ProtocolHandlerRegistry::SilentlyHandleRegisterHandlerRequest(
const ProtocolHandler& handler) {
if (handler.IsEmpty() || !CanSchemeBeOverridden(handler.protocol()))
return true;
if (!enabled() || IsRegistered(handler) || HasIgnoredEquivalent(handler))
return true;
if (AttemptReplace(handler))
return true;
return false;
}
void ProtocolHandlerRegistry::OnAcceptRegisterProtocolHandler(
const ProtocolHandler& handler) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!RegisterProtocolHandler(handler, USER))
return;
SetDefault(handler);
Save();
NotifyChanged();
}
void ProtocolHandlerRegistry::OnDenyRegisterProtocolHandler(
const ProtocolHandler& handler) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
RegisterProtocolHandler(handler, USER);
Save();
NotifyChanged();
}
void ProtocolHandlerRegistry::OnIgnoreRegisterProtocolHandler(
const ProtocolHandler& handler) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
IgnoreProtocolHandler(handler, USER);
Save();
NotifyChanged();
}
bool ProtocolHandlerRegistry::AttemptReplace(const ProtocolHandler& handler) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
ProtocolHandler old_default = GetHandlerFor(handler.protocol());
bool make_new_handler_default = handler.IsSameOrigin(old_default);
ProtocolHandlerList to_replace(GetReplacedHandlers(handler));
if (to_replace.empty())
return false;
for (const auto& replaced_handler : to_replace) {
RemoveHandler(replaced_handler);
}
if (make_new_handler_default) {
OnAcceptRegisterProtocolHandler(handler);
} else {
InsertHandler(handler);
NotifyChanged();
}
return true;
}
ProtocolHandlerRegistry::ProtocolHandlerList
ProtocolHandlerRegistry::GetReplacedHandlers(
const ProtocolHandler& handler) const {
ProtocolHandlerList replaced_handlers;
const ProtocolHandlerList* handlers = GetHandlerList(handler.protocol());
if (!handlers)
return replaced_handlers;
for (const auto& old_handler : *handlers) {
if (handler.IsSameOrigin(old_handler)) {
replaced_handlers.push_back(old_handler);
}
}
return replaced_handlers;
}
void ProtocolHandlerRegistry::ClearDefault(const std::string& scheme) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
// TODO(jfernandez): If we want to use StringPiece as map's key for erasing,
// we would need to adapt the ProtocolHandlerMap, or just use the iterator
// got from find(scheme).
default_handlers_.erase(scheme);
Save();
NotifyChanged();
}
bool ProtocolHandlerRegistry::IsDefault(const ProtocolHandler& handler) const {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
return GetHandlerFor(handler.protocol()) == handler;
}
void ProtocolHandlerRegistry::InstallPredefinedHandlers() {
for (const auto& [scheme, handler] : url::GetPredefinedHandlerSchemes()) {
AddPredefinedHandler(
ProtocolHandler::CreateProtocolHandler(scheme, GURL(handler)));
}
}
void ProtocolHandlerRegistry::InitProtocolSettings() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
// Any further default additions to the table will get rejected from now on.
is_loaded_ = true;
is_loading_ = true;
if (!prefs_) {
is_loading_ = false;
return;
}
if (prefs_->HasPrefPath(prefs::kCustomHandlersEnabled)) {
if (prefs_->GetBoolean(prefs::kCustomHandlersEnabled)) {
Enable();
} else {
Disable();
}
}
RegisterProtocolHandlersFromPref(prefs::kPolicyRegisteredProtocolHandlers,
POLICY);
RegisterProtocolHandlersFromPref(prefs::kRegisteredProtocolHandlers, USER);
IgnoreProtocolHandlersFromPref(prefs::kPolicyIgnoredProtocolHandlers, POLICY);
IgnoreProtocolHandlersFromPref(prefs::kIgnoredProtocolHandlers, USER);
is_loading_ = false;
// For each default protocol handler, check that we are still registered
// with the OS as the default application.
if (delegate_->ShouldRemoveHandlersNotInOS()) {
for (const auto& [protocol, handler] : default_handlers_) {
delegate_->CheckDefaultClientWithOS(
protocol, GetDefaultWebClientCallback(protocol));
}
}
}
int ProtocolHandlerRegistry::GetHandlerIndex(base::StringPiece scheme) const {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
const ProtocolHandler& candidate = GetHandlerFor(scheme);
if (candidate.IsEmpty())
return -1;
const ProtocolHandlerList* handlers = GetHandlerList(scheme);
if (!handlers)
return -1;
int i = 0;
for (const auto& handler : *handlers) {
if (handler == candidate)
return i;
i++;
}
return -1;
}
ProtocolHandlerRegistry::ProtocolHandlerList
ProtocolHandlerRegistry::GetHandlersFor(base::StringPiece scheme) const {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
auto p = protocol_handlers_.find(scheme);
if (p == protocol_handlers_.end()) {
return ProtocolHandlerList();
}
return p->second;
}
ProtocolHandlerRegistry::ProtocolHandlerList
ProtocolHandlerRegistry::GetUserDefinedHandlers(base::Time begin,
base::Time end) const {
ProtocolHandlerRegistry::ProtocolHandlerList result;
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)
result.push_back(handler);
}
}
return result;
}
ProtocolHandlerRegistry::ProtocolHandlerList
ProtocolHandlerRegistry::GetUserIgnoredHandlers(base::Time begin,
base::Time end) const {
ProtocolHandlerRegistry::ProtocolHandlerList result;
for (const ProtocolHandler& handler : user_ignored_protocol_handlers_) {
if (begin <= handler.last_modified() && handler.last_modified() < end)
result.push_back(handler);
}
return result;
}
void ProtocolHandlerRegistry::ClearUserDefinedHandlers(base::Time begin,
base::Time end) {
for (const ProtocolHandler& handler : GetUserDefinedHandlers(begin, end))
RemoveHandler(handler);
for (const ProtocolHandler& handler : GetUserIgnoredHandlers(begin, end))
RemoveIgnoredHandler(handler);
}
ProtocolHandlerRegistry::ProtocolHandlerList
ProtocolHandlerRegistry::GetIgnoredHandlers() {
return ignored_protocol_handlers_;
}
void ProtocolHandlerRegistry::GetRegisteredProtocols(
std::vector<std::string>* output) const {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
for (const auto& [protocol, handlers_list] : protocol_handlers_) {
if (!handlers_list.empty())
output->push_back(protocol);
}
}
bool ProtocolHandlerRegistry::CanSchemeBeOverridden(
base::StringPiece scheme) const {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
const ProtocolHandlerList* handlers = GetHandlerList(scheme);
// If we already have a handler for this scheme, we can add more.
if (handlers != NULL && !handlers->empty())
return true;
// Don't override a scheme if it already has an external handler.
return !delegate_->IsExternalHandlerRegistered(
static_cast<std::string>(scheme));
}
bool ProtocolHandlerRegistry::IsRegistered(
const ProtocolHandler& handler) const {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
const ProtocolHandlerList* handlers = GetHandlerList(handler.protocol());
if (!handlers) {
return false;
}
return base::Contains(*handlers, handler);
}
bool ProtocolHandlerRegistry::IsRegisteredByUser(
const ProtocolHandler& handler) {
return HandlerExists(handler, &user_protocol_handlers_);
}
bool ProtocolHandlerRegistry::HasPolicyRegisteredHandler(
base::StringPiece scheme) {
return (policy_protocol_handlers_.find(scheme) !=
policy_protocol_handlers_.end());
}
bool ProtocolHandlerRegistry::IsIgnored(const ProtocolHandler& handler) const {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
for (const auto& ignored_handler : ignored_protocol_handlers_) {
if (ignored_handler == handler) {
return true;
}
}
return false;
}
bool ProtocolHandlerRegistry::HasRegisteredEquivalent(
const ProtocolHandler& handler) const {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
const ProtocolHandlerList* handlers = GetHandlerList(handler.protocol());
if (!handlers) {
return false;
}
for (const auto& registered_handler : *handlers) {
if (handler.IsEquivalent(registered_handler)) {
return true;
}
}
return false;
}
bool ProtocolHandlerRegistry::HasIgnoredEquivalent(
const ProtocolHandler& handler) const {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
for (const auto& ignored_handler : ignored_protocol_handlers_) {
if (handler.IsEquivalent(ignored_handler)) {
return true;
}
}
return false;
}
void ProtocolHandlerRegistry::RemoveIgnoredHandler(
const ProtocolHandler& handler) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
bool should_notify = false;
if (HandlerExists(handler, ignored_protocol_handlers_) &&
HandlerExists(handler, user_ignored_protocol_handlers_)) {
EraseHandler(handler, &user_ignored_protocol_handlers_);
Save();
if (!HandlerExists(handler, policy_ignored_protocol_handlers_)) {
EraseHandler(handler, &ignored_protocol_handlers_);
should_notify = true;
}
}
if (should_notify)
NotifyChanged();
}
bool ProtocolHandlerRegistry::IsHandledProtocol(
base::StringPiece scheme) const {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
return enabled_ && !GetHandlerFor(scheme).IsEmpty();
}
void ProtocolHandlerRegistry::RemoveHandler(const ProtocolHandler& handler) {
if (IsIgnored(handler)) {
RemoveIgnoredHandler(handler);
return;
}
DCHECK_CURRENTLY_ON(BrowserThread::UI);
ProtocolHandlerList& handlers = protocol_handlers_[handler.protocol()];
bool erase_success = false;
if (HandlerExists(handler, handlers) &&
HandlerExists(handler, &user_protocol_handlers_)) {
EraseHandler(handler, &user_protocol_handlers_);
erase_success = true;
if (!HandlerExists(handler, &policy_protocol_handlers_))
EraseHandler(handler, &protocol_handlers_);
}
auto q = default_handlers_.find(handler.protocol());
if (erase_success && q != default_handlers_.end() && q->second == handler) {
// Make the new top handler in the list the default.
if (!handlers.empty()) {
// NOTE We pass a copy because SetDefault() modifies handlers.
SetDefault(ProtocolHandler(handlers[0]));
} else {
default_handlers_.erase(q);
}
}
if (erase_success && !IsHandledProtocol(handler.protocol())) {
delegate_->DeregisterExternalHandler(handler.protocol());
}
Save();
if (erase_success)
NotifyChanged();
}
void ProtocolHandlerRegistry::RemoveDefaultHandler(base::StringPiece scheme) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
ProtocolHandler current_default = GetHandlerFor(scheme);
if (!current_default.IsEmpty())
RemoveHandler(current_default);
}
const ProtocolHandler& ProtocolHandlerRegistry::GetHandlerFor(
base::StringPiece scheme) const {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
return LookupHandler(default_handlers_, scheme);
}
GURL ProtocolHandlerRegistry::Translate(const GURL& url) const {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
return TranslateUrl(default_handlers_, url);
}
void ProtocolHandlerRegistry::Enable() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (enabled_) {
return;
}
enabled_ = true;
for (const auto& [protocol, handler] : default_handlers_) {
delegate_->RegisterExternalHandler(protocol);
}
Save();
NotifyChanged();
}
void ProtocolHandlerRegistry::Disable() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!enabled_) {
return;
}
enabled_ = false;
for (const auto& [protocol, handler] : default_handlers_) {
delegate_->DeregisterExternalHandler(protocol);
}
Save();
NotifyChanged();
}
void ProtocolHandlerRegistry::Shutdown() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
delegate_.reset(NULL);
weak_ptr_factory_.InvalidateWeakPtrs();
}
// static
void ProtocolHandlerRegistry::RegisterProfilePrefs(
user_prefs::PrefRegistrySyncable* registry) {
registry->RegisterListPref(prefs::kRegisteredProtocolHandlers);
registry->RegisterListPref(prefs::kIgnoredProtocolHandlers);
registry->RegisterListPref(prefs::kPolicyRegisteredProtocolHandlers);
registry->RegisterListPref(prefs::kPolicyIgnoredProtocolHandlers);
registry->RegisterBooleanPref(prefs::kCustomHandlersEnabled, true);
}
ProtocolHandlerRegistry::~ProtocolHandlerRegistry() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
}
void ProtocolHandlerRegistry::AddObserver(Observer* observer) {
observers_.AddObserver(observer);
}
void ProtocolHandlerRegistry::RemoveObserver(Observer* observer) {
observers_.RemoveObserver(observer);
}
void ProtocolHandlerRegistry::PromoteHandler(const ProtocolHandler& handler) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(IsRegistered(handler));
auto p = protocol_handlers_.find(handler.protocol());
ProtocolHandlerList& list = p->second;
list.erase(base::ranges::find(list, handler));
list.insert(list.begin(), handler);
}
void ProtocolHandlerRegistry::Save() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (is_loading_) {
return;
}
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*
ProtocolHandlerRegistry::GetHandlerList(base::StringPiece scheme) const {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
auto p = protocol_handlers_.find(scheme);
if (p == protocol_handlers_.end()) {
return NULL;
}
return &p->second;
}
void ProtocolHandlerRegistry::SetDefault(const ProtocolHandler& handler) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
const std::string& protocol = handler.protocol();
ProtocolHandlerMap::const_iterator p = default_handlers_.find(protocol);
// If we're not loading, and we are setting a default for a new protocol,
// register with the OS.
if (!is_loading_ && p == default_handlers_.end())
delegate_->RegisterWithOSAsDefaultClient(
protocol, GetDefaultWebClientCallback(protocol));
default_handlers_.erase(protocol);
default_handlers_.insert(std::make_pair(protocol, handler));
PromoteHandler(handler);
}
void ProtocolHandlerRegistry::InsertHandler(const ProtocolHandler& handler) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
auto p = protocol_handlers_.find(handler.protocol());
if (p != protocol_handlers_.end()) {
p->second.push_back(handler);
return;
}
ProtocolHandlerList new_list;
new_list.push_back(handler);
protocol_handlers_[handler.protocol()] = new_list;
}
base::Value::List ProtocolHandlerRegistry::EncodeRegisteredHandlers() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
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);
}
encoded_handlers.Append(std::move(encoded));
}
}
return encoded_handlers;
}
base::Value::List ProtocolHandlerRegistry::EncodeIgnoredHandlers() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
base::Value::List encoded_handlers;
for (const auto& handler : user_ignored_protocol_handlers_) {
encoded_handlers.Append(handler.Encode());
}
return encoded_handlers;
}
void ProtocolHandlerRegistry::NotifyChanged() {
for (auto& observer : observers_)
observer.OnProtocolHandlerRegistryChanged();
}
bool ProtocolHandlerRegistry::RegisterProtocolHandler(
const ProtocolHandler& handler,
const HandlerSource source) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(CanSchemeBeOverridden(handler.protocol()));
DCHECK(!handler.IsEmpty());
// Ignore invalid handlers.
if (!handler.IsValid())
return false;
ProtocolHandlerMultiMap& map =
(source == POLICY) ? policy_protocol_handlers_ : user_protocol_handlers_;
ProtocolHandlerList& list = map[handler.protocol()];
if (!HandlerExists(handler, list))
list.push_back(handler);
if (IsRegistered(handler)) {
return true;
}
if (enabled_ && !delegate_->IsExternalHandlerRegistered(handler.protocol()))
delegate_->RegisterExternalHandler(handler.protocol());
InsertHandler(handler);
return true;
}
std::vector<const base::Value::Dict*>
ProtocolHandlerRegistry::GetHandlersFromPref(const char* pref_name) const {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
std::vector<const base::Value::Dict*> result;
if (!prefs_ || !prefs_->HasPrefPath(pref_name)) {
return result;
}
const base::Value::List& handlers = prefs_->GetList(pref_name);
for (const auto& list_item : handlers) {
if (const base::Value::Dict* encoded_handler = list_item.GetIfDict()) {
if (ProtocolHandler::IsValidDict(*encoded_handler)) {
result.push_back(encoded_handler);
}
}
}
return result;
}
void ProtocolHandlerRegistry::RegisterProtocolHandlersFromPref(
const char* pref_name,
const HandlerSource source) {
std::vector<const base::Value::Dict*> registered_handlers =
GetHandlersFromPref(pref_name);
for (const auto* encoded_handler : registered_handlers) {
ProtocolHandler handler =
ProtocolHandler::CreateProtocolHandler(*encoded_handler);
if (!RegisterProtocolHandler(handler, source))
continue;
if (encoded_handler->FindBool("default").value_or(false)) {
SetDefault(handler);
}
}
}
void ProtocolHandlerRegistry::IgnoreProtocolHandler(
const ProtocolHandler& handler,
const HandlerSource source) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
ProtocolHandlerList& list = (source == POLICY)
? policy_ignored_protocol_handlers_
: user_ignored_protocol_handlers_;
if (!HandlerExists(handler, list))
list.push_back(handler);
if (HandlerExists(handler, ignored_protocol_handlers_))
return;
ignored_protocol_handlers_.push_back(handler);
}
void ProtocolHandlerRegistry::IgnoreProtocolHandlersFromPref(
const char* pref_name,
const HandlerSource source) {
std::vector<const base::Value::Dict*> ignored_handlers =
GetHandlersFromPref(pref_name);
for (const auto* encoded_handler : ignored_handlers)
IgnoreProtocolHandler(
ProtocolHandler::CreateProtocolHandler(*encoded_handler), source);
}
bool ProtocolHandlerRegistry::HandlerExists(const ProtocolHandler& handler,
ProtocolHandlerMultiMap* map) {
return HandlerExists(handler, (*map)[handler.protocol()]);
}
bool ProtocolHandlerRegistry::HandlerExists(const ProtocolHandler& handler,
const ProtocolHandlerList& list) {
return base::Contains(list, handler);
}
void ProtocolHandlerRegistry::EraseHandler(const ProtocolHandler& handler,
ProtocolHandlerMultiMap* map) {
EraseHandler(handler, &(*map)[handler.protocol()]);
}
void ProtocolHandlerRegistry::EraseHandler(const ProtocolHandler& handler,
ProtocolHandlerList* list) {
list->erase(base::ranges::find(*list, handler));
}
void ProtocolHandlerRegistry::OnSetAsDefaultProtocolClientFinished(
const std::string& protocol,
bool is_default) {
// Clear if the default protocol client isn't this installation.
if (!is_default && delegate_->ShouldRemoveHandlersNotInOS())
ClearDefault(protocol);
}
void ProtocolHandlerRegistry::SetIsLoading(bool is_loading) {
is_loading_ = is_loading;
}
base::WeakPtr<ProtocolHandlerRegistry> ProtocolHandlerRegistry::GetWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
}
void ProtocolHandlerRegistry::AddPredefinedHandler(
const ProtocolHandler& handler) {
DCHECK(!is_loaded_); // Must be called prior InitProtocolSettings.
RegisterProtocolHandler(handler, USER);
SetDefault(handler);
predefined_protocol_handlers_.push_back(handler);
}
DefaultClientCallback ProtocolHandlerRegistry::GetDefaultWebClientCallback(
const std::string& protocol) {
return base::BindOnce(
&ProtocolHandlerRegistry::OnSetAsDefaultProtocolClientFinished,
weak_ptr_factory_.GetWeakPtr(), protocol);
}
} // namespace custom_handlers