blob: 80c2efee0121c585d21dfb46a4e1b2d18b1111a2 [file] [log] [blame]
// Copyright 2016 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 "chrome/browser/ui/webui/settings/protocol_handlers_handler.h"
#include <memory>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/macros.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/web_applications/os_integration_manager.h"
#include "chrome/browser/web_applications/web_app_sync_bridge.h"
#include "chrome/common/url_constants.h"
#include "components/google/core/common/google_util.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/web_ui.h"
namespace settings {
namespace {
// TODO(https://crbug.com/1251039): Remove usages of base::ListValue
void GetHandlersAsListValue(
const ProtocolHandlerRegistry* registry,
const ProtocolHandlerRegistry::ProtocolHandlerList& handlers,
base::ListValue* handler_list) {
for (const auto& handler : handlers) {
base::DictionaryValue handler_value;
handler_value.SetStringPath("protocol_display_name",
handler.GetProtocolDisplayName());
handler_value.SetStringPath("protocol", handler.protocol());
handler_value.SetStringPath("spec", handler.url().spec());
handler_value.SetStringPath("host", handler.url().host());
if (registry)
handler_value.SetBoolPath("is_default", registry->IsDefault(handler));
if (handler.web_app_id().has_value())
handler_value.SetStringPath("app_id", handler.web_app_id().value());
handler_list->Append(std::move(handler_value));
}
}
} // namespace
ProtocolHandlersHandler::ProtocolHandlersHandler() = default;
ProtocolHandlersHandler::~ProtocolHandlersHandler() = default;
void ProtocolHandlersHandler::OnJavascriptAllowed() {
registry_observation_.Observe(GetProtocolHandlerRegistry());
app_observation_.Observe(&GetWebAppProvider()->registrar());
}
void ProtocolHandlersHandler::OnJavascriptDisallowed() {
registry_observation_.Reset();
app_observation_.Reset();
}
void ProtocolHandlersHandler::RegisterMessages() {
web_ui()->RegisterDeprecatedMessageCallback(
"observeProtocolHandlers",
base::BindRepeating(
&ProtocolHandlersHandler::HandleObserveProtocolHandlers,
base::Unretained(this)));
web_ui()->RegisterDeprecatedMessageCallback(
"observeProtocolHandlersEnabledState",
base::BindRepeating(
&ProtocolHandlersHandler::HandleObserveProtocolHandlersEnabledState,
base::Unretained(this)));
web_ui()->RegisterDeprecatedMessageCallback(
"removeHandler",
base::BindRepeating(&ProtocolHandlersHandler::HandleRemoveHandler,
base::Unretained(this)));
web_ui()->RegisterDeprecatedMessageCallback(
"setHandlersEnabled",
base::BindRepeating(&ProtocolHandlersHandler::HandleSetHandlersEnabled,
base::Unretained(this)));
web_ui()->RegisterDeprecatedMessageCallback(
"setDefault",
base::BindRepeating(&ProtocolHandlersHandler::HandleSetDefault,
base::Unretained(this)));
// Web App Protocol Handlers register message callbacks:
web_ui()->RegisterMessageCallback(
"observeAppProtocolHandlers",
base::BindRepeating(
&ProtocolHandlersHandler::HandleObserveAppProtocolHandlers,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"removeAppAllowedHandler",
base::BindRepeating(
&ProtocolHandlersHandler::HandleRemoveAllowedAppHandler,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"removeAppDisallowedHandler",
base::BindRepeating(
&ProtocolHandlersHandler::HandleRemoveDisallowedAppHandler,
base::Unretained(this)));
}
void ProtocolHandlersHandler::OnProtocolHandlerRegistryChanged() {
SendHandlersEnabledValue();
UpdateHandlerList();
}
void ProtocolHandlersHandler::OnWebAppProtocolSettingsChanged() {
UpdateAllAllowedLaunchProtocols();
UpdateAllDisallowedLaunchProtocols();
}
void ProtocolHandlersHandler::OnWebAppUninstalled(
const web_app::AppId& app_id) {
OnWebAppProtocolSettingsChanged();
}
void ProtocolHandlersHandler::GetHandlersForProtocol(
const std::string& protocol,
base::DictionaryValue* handlers_value) {
ProtocolHandlerRegistry* registry = GetProtocolHandlerRegistry();
handlers_value->SetString("protocol_display_name",
ProtocolHandler::GetProtocolDisplayName(protocol));
handlers_value->SetString("protocol", protocol);
base::ListValue handlers_list;
GetHandlersAsListValue(registry, registry->GetHandlersFor(protocol),
&handlers_list);
handlers_value->SetKey("handlers", std::move(handlers_list));
}
void ProtocolHandlersHandler::GetIgnoredHandlers(base::ListValue* handlers) {
ProtocolHandlerRegistry* registry = GetProtocolHandlerRegistry();
ProtocolHandlerRegistry::ProtocolHandlerList ignored_handlers =
registry->GetIgnoredHandlers();
return GetHandlersAsListValue(registry, ignored_handlers, handlers);
}
void ProtocolHandlersHandler::UpdateHandlerList() {
ProtocolHandlerRegistry* registry = GetProtocolHandlerRegistry();
std::vector<std::string> protocols;
registry->GetRegisteredProtocols(&protocols);
base::ListValue handlers;
for (auto protocol = protocols.begin(); protocol != protocols.end();
protocol++) {
std::unique_ptr<base::DictionaryValue> handler_value(
new base::DictionaryValue());
GetHandlersForProtocol(*protocol, handler_value.get());
handlers.Append(std::move(handler_value));
}
std::unique_ptr<base::ListValue> ignored_handlers(new base::ListValue());
GetIgnoredHandlers(ignored_handlers.get());
FireWebUIListener("setProtocolHandlers", handlers);
FireWebUIListener("setIgnoredProtocolHandlers", *ignored_handlers);
}
void ProtocolHandlersHandler::HandleObserveProtocolHandlers(
const base::ListValue* args) {
AllowJavascript();
SendHandlersEnabledValue();
UpdateHandlerList();
}
void ProtocolHandlersHandler::HandleObserveProtocolHandlersEnabledState(
const base::ListValue* args) {
AllowJavascript();
SendHandlersEnabledValue();
}
void ProtocolHandlersHandler::SendHandlersEnabledValue() {
FireWebUIListener("setHandlersEnabled",
base::Value(GetProtocolHandlerRegistry()->enabled()));
}
void ProtocolHandlersHandler::HandleRemoveHandler(const base::ListValue* args) {
ProtocolHandler handler(ParseHandlerFromArgs(args));
CHECK(!handler.IsEmpty());
GetProtocolHandlerRegistry()->RemoveHandler(handler);
// No need to call UpdateHandlerList() - we should receive a notification
// that the ProtocolHandlerRegistry has changed and we will update the view
// then.
}
void ProtocolHandlersHandler::HandleSetHandlersEnabled(
const base::ListValue* args) {
bool enabled = true;
CHECK(args->GetList()[0].is_bool());
enabled = args->GetList()[0].GetBool();
if (enabled)
GetProtocolHandlerRegistry()->Enable();
else
GetProtocolHandlerRegistry()->Disable();
}
void ProtocolHandlersHandler::HandleSetDefault(const base::ListValue* args) {
const ProtocolHandler& handler(ParseHandlerFromArgs(args));
CHECK(!handler.IsEmpty());
GetProtocolHandlerRegistry()->OnAcceptRegisterProtocolHandler(handler);
}
ProtocolHandler ProtocolHandlersHandler::ParseHandlerFromArgs(
const base::ListValue* args) const {
std::u16string protocol;
std::u16string url;
bool ok = args->GetString(0, &protocol) && args->GetString(1, &url);
if (!ok)
return ProtocolHandler::EmptyProtocolHandler();
return ProtocolHandler::CreateProtocolHandler(base::UTF16ToUTF8(protocol),
GURL(base::UTF16ToUTF8(url)));
}
ProtocolHandlerRegistry* ProtocolHandlersHandler::GetProtocolHandlerRegistry() {
return ProtocolHandlerRegistryFactory::GetForBrowserContext(
Profile::FromWebUI(web_ui()));
}
// App Protocol Handler specific functions
std::unique_ptr<base::DictionaryValue>
ProtocolHandlersHandler::GetAppHandlersForProtocol(
const std::string& protocol,
ProtocolHandlerRegistry::ProtocolHandlerList handlers) {
auto handlers_value = std::make_unique<base::DictionaryValue>();
if (!handlers.empty()) {
handlers_value->SetStringPath(
"protocol_display_name",
content::ProtocolHandler::GetProtocolDisplayName(protocol));
handlers_value->SetStringPath("protocol", protocol);
base::ListValue handlers_list;
GetHandlersAsListValue(nullptr, handlers, &handlers_list);
handlers_value->SetKey("handlers", std::move(handlers_list));
}
return handlers_value;
}
void ProtocolHandlersHandler::UpdateAllAllowedLaunchProtocols() {
base::flat_set<std::string> protocols(
GetWebAppProvider()->registrar().GetAllAllowedLaunchProtocols());
web_app::OsIntegrationManager& os_integration_manager =
GetWebAppProvider()->os_integration_manager();
base::Value handlers(base::Value::Type::LIST);
for (auto& protocol : protocols) {
ProtocolHandlerRegistry::ProtocolHandlerList protocol_handlers =
os_integration_manager.GetAllowedHandlersForProtocol(protocol);
auto handler_value(GetAppHandlersForProtocol(protocol, protocol_handlers));
handlers.Append(std::move(*handler_value));
}
FireWebUIListener("setAppAllowedProtocolHandlers", handlers);
}
void ProtocolHandlersHandler::UpdateAllDisallowedLaunchProtocols() {
base::flat_set<std::string> protocols(
GetWebAppProvider()->registrar().GetAllDisallowedLaunchProtocols());
web_app::OsIntegrationManager& os_integration_manager =
GetWebAppProvider()->os_integration_manager();
base::Value handlers(base::Value::Type::LIST);
for (auto& protocol : protocols) {
ProtocolHandlerRegistry::ProtocolHandlerList protocol_handlers =
os_integration_manager.GetDisallowedHandlersForProtocol(protocol);
auto handler_value(GetAppHandlersForProtocol(protocol, protocol_handlers));
handlers.Append(std::move(*handler_value));
}
FireWebUIListener("setAppDisallowedProtocolHandlers", handlers);
}
void ProtocolHandlersHandler::HandleObserveAppProtocolHandlers(
base::Value::ConstListView args) {
AllowJavascript();
UpdateAllAllowedLaunchProtocols();
UpdateAllDisallowedLaunchProtocols();
}
void ProtocolHandlersHandler::HandleRemoveAllowedAppHandler(
base::Value::ConstListView args) {
content::ProtocolHandler handler(ParseAppHandlerFromArgs(args));
CHECK(!handler.IsEmpty());
GetWebAppProvider()->sync_bridge().RemoveAllowedLaunchProtocol(
handler.web_app_id().value(), handler.protocol());
// No need to call UpdateAllAllowedLaunchProtocols() - we should receive a
// notification that the Web App Protocol Settings has changed and we will
// update the view then.
}
void ProtocolHandlersHandler::HandleRemoveDisallowedAppHandler(
base::Value::ConstListView args) {
content::ProtocolHandler handler(ParseAppHandlerFromArgs(args));
CHECK(!handler.IsEmpty());
GetWebAppProvider()->sync_bridge().RemoveDisallowedLaunchProtocol(
handler.web_app_id().value(), handler.protocol());
// Update registration with the OS.
GetWebAppProvider()->os_integration_manager().UpdateProtocolHandlers(
handler.web_app_id().value(), /*force_shortcut_updates_if_needed=*/true,
base::DoNothing());
// No need to call HandleRemoveDisallowedAppHandler() - we should receive a
// notification that the Web App Protocol Settings has changed and we will
// update the view then.
}
content::ProtocolHandler ProtocolHandlersHandler::ParseAppHandlerFromArgs(
base::Value::ConstListView args) const {
const std::string* protocol = args[0].GetIfString();
const std::string* url = args[1].GetIfString();
const std::string* app_id = args[2].GetIfString();
if (!protocol || !url || !app_id)
return content::ProtocolHandler::EmptyProtocolHandler();
return content::ProtocolHandler::CreateWebAppProtocolHandler(
*protocol, GURL(*url), *app_id);
}
web_app::WebAppProvider* ProtocolHandlersHandler::GetWebAppProvider() {
return web_app::WebAppProvider::GetForWebApps(Profile::FromWebUI(web_ui()));
}
} // namespace settings