blob: 0fc9a1afa56d332af1ef7b334b2387036fe95170 [file] [log] [blame]
// Copyright 2019 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/chromeos/kiosk_next_home/intent_config_helper.h"
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/callback.h"
#include "base/json/json_value_converter.h"
#include "base/logging.h"
#include "base/stl_util.h"
#include "base/task/post_task.h"
#include "base/task/task_traits.h"
#include "content/public/common/service_manager_connection.h"
#include "services/data_decoder/public/cpp/safe_json_parser.h"
#include "url/url_constants.h"
#if defined(GOOGLE_CHROME_BUILD)
#include "chrome/grit/component_extension_resources.h"
#include "ui/base/resource/resource_bundle.h"
#endif
namespace chromeos {
namespace kiosk_next_home {
namespace {
struct CustomScheme {
std::string scheme;
std::string path;
static void RegisterJSONConverter(
base::JSONValueConverter<CustomScheme>* converter) {
converter->RegisterStringField("scheme", &CustomScheme::scheme);
converter->RegisterStringField("path", &CustomScheme::path);
}
};
struct IntentConfig {
std::vector<std::unique_ptr<std::string>> allowed_hosts;
std::vector<std::unique_ptr<CustomScheme>> allowed_custom_schemes;
static void RegisterJSONConverter(
base::JSONValueConverter<IntentConfig>* converter) {
converter->RegisterRepeatedString("allowed_hosts",
&IntentConfig::allowed_hosts);
converter->RegisterRepeatedMessage("allowed_custom_schemes",
&IntentConfig::allowed_custom_schemes);
}
};
class ReadJsonConfigResourceDelegate : public IntentConfigHelper::Delegate {
public:
ReadJsonConfigResourceDelegate() = default;
~ReadJsonConfigResourceDelegate() override = default;
// IntentConfigHelper::Delegate:
std::string GetJsonConfig() const override {
#if defined(GOOGLE_CHROME_BUILD)
return ui::ResourceBundle::GetSharedInstance()
.GetRawDataResource(IDR_KIOSK_NEXT_INTENT_CONFIG_JSON)
.as_string();
#else
return std::string();
#endif
}
DISALLOW_COPY_AND_ASSIGN(ReadJsonConfigResourceDelegate);
};
} // namespace
// static
std::unique_ptr<IntentConfigHelper> IntentConfigHelper::GetInstance() {
return std::make_unique<IntentConfigHelper>(
std::make_unique<ReadJsonConfigResourceDelegate>());
}
IntentConfigHelper::IntentConfigHelper(std::unique_ptr<Delegate> delegate)
: ready_(false) {
base::PostTaskWithTraitsAndReplyWithResult(
FROM_HERE,
{base::MayBlock(), base::TaskPriority::USER_VISIBLE,
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
base::BindOnce(
[](std::unique_ptr<Delegate> read_config_delegate) {
return read_config_delegate->GetJsonConfig();
},
std::move(delegate)),
base::BindOnce(&IntentConfigHelper::ParseConfig,
weak_factory_.GetWeakPtr()));
}
IntentConfigHelper::IntentConfigHelper() = default;
IntentConfigHelper::~IntentConfigHelper() = default;
bool IntentConfigHelper::IsIntentAllowed(const GURL& intent_uri) const {
if (!ready_ || !intent_uri.is_valid())
return false;
if (intent_uri.IsStandard()) {
if (intent_uri.SchemeIs(url::kHttpsScheme))
return base::ContainsKey(allowed_hosts_, intent_uri.host());
// Other standard schemes besides https are not allowed.
return false;
}
auto allowed_custom_scheme =
allowed_custom_schemes_.find(intent_uri.scheme());
if (allowed_custom_scheme == allowed_custom_schemes_.end())
return false;
// Scheme is allowed, so intent will be allowed if path matches the config.
std::string path = allowed_custom_scheme->second;
return path == intent_uri.path();
}
void IntentConfigHelper::ParseConfig(const std::string& json_config) {
auto* connection = content::ServiceManagerConnection::GetForProcess();
// Service manager connection may not be bound in tests.
if (!connection)
return;
// Parse JSON in utility process via Data Decoder Service.
data_decoder::SafeJsonParser::Parse(
connection->GetConnector(), json_config,
base::BindRepeating(&IntentConfigHelper::ParseConfigDone,
weak_factory_.GetWeakPtr()),
base::BindRepeating(&IntentConfigHelper::ParseConfigFailed,
weak_factory_.GetWeakPtr()));
}
void IntentConfigHelper::ParseConfigDone(
std::unique_ptr<base::Value> config_value) {
base::JSONValueConverter<IntentConfig> converter;
IntentConfig parsed_config;
if (converter.Convert(*config_value, &parsed_config)) {
for (const auto& allowed_host_ptr : parsed_config.allowed_hosts)
allowed_hosts_.emplace(std::move(*allowed_host_ptr));
for (const auto& custom_scheme_ptr : parsed_config.allowed_custom_schemes) {
allowed_custom_schemes_[std::move(custom_scheme_ptr->scheme)] =
std::move(custom_scheme_ptr->path);
}
ready_ = true;
}
}
void IntentConfigHelper::ParseConfigFailed(const std::string& error) {
DLOG(ERROR) << "Failed to parse intent JSON config: " << error;
}
} // namespace kiosk_next_home
} // namespace chromeos