blob: 17011169c4a5f4361505d39458ab185756b28ac0 [file] [log] [blame]
// Copyright 2018 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/login/demo_mode/demo_extensions_external_loader.h"
#include <utility>
#include "base/bind.h"
#include "base/files/file_util.h"
#include "base/json/json_reader.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/optional.h"
#include "base/task/post_task.h"
#include "base/task/task_traits.h"
#include "base/values.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/extensions/external_cache_impl.h"
#include "chrome/browser/chromeos/login/demo_mode/demo_resources.h"
#include "chrome/browser/chromeos/login/demo_mode/demo_session.h"
#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/browser/extensions/external_provider_impl.h"
#include "extensions/browser/extension_file_task_runner.h"
#include "extensions/common/extension_urls.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
namespace chromeos {
namespace {
// Arbitrary, but reasonable size limit in bytes for prefs file.
constexpr size_t kPrefsSizeLimit = 1024 * 1024;
base::Optional<base::Value> LoadPrefsFromDisk(
const base::FilePath& prefs_path) {
if (!base::PathExists(prefs_path)) {
LOG(WARNING) << "Demo extensions prefs not found " << prefs_path.value();
return base::nullopt;
}
std::string prefs_str;
if (!base::ReadFileToStringWithMaxSize(prefs_path, &prefs_str,
kPrefsSizeLimit)) {
LOG(ERROR) << "Failed to read prefs " << prefs_path.value() << "; "
<< "failed after reading " << prefs_str.size() << " bytes";
return base::nullopt;
}
std::unique_ptr<base::Value> prefs_value =
base::JSONReader::ReadDeprecated(prefs_str);
if (!prefs_value) {
LOG(ERROR) << "Unable to parse demo extensions prefs.";
return base::nullopt;
}
if (!prefs_value->is_dict()) {
LOG(ERROR) << "Demo extensions prefs not a dictionary.";
return base::nullopt;
}
return base::Value::FromUniquePtrValue(std::move(prefs_value));
}
} // namespace
// static
bool DemoExtensionsExternalLoader::SupportedForProfile(Profile* profile) {
if (!chromeos::ProfileHelper::IsPrimaryProfile(profile))
return false;
DemoSession* demo_session = DemoSession::Get();
return demo_session && demo_session->started();
}
DemoExtensionsExternalLoader::DemoExtensionsExternalLoader(
const base::FilePath& cache_dir)
: cache_dir_(cache_dir), weak_ptr_factory_(this) {
DCHECK(DemoSession::Get() && DemoSession::Get()->started());
}
DemoExtensionsExternalLoader::~DemoExtensionsExternalLoader() = default;
void DemoExtensionsExternalLoader::LoadApp(const std::string& app_id) {
app_ids_.push_back(app_id);
base::DictionaryValue prefs;
for (const std::string& app_id : app_ids_) {
base::DictionaryValue app_dict;
app_dict.SetKey(extensions::ExternalProviderImpl::kExternalUpdateUrl,
base::Value(extension_urls::kChromeWebstoreUpdateURL));
prefs.SetKey(app_id, std::move(app_dict));
}
if (!external_cache_) {
external_cache_ = std::make_unique<ExternalCacheImpl>(
cache_dir_, g_browser_process->shared_url_loader_factory(),
extensions::GetExtensionFileTaskRunner(), this,
true /* always_check_updates */,
false /* wait_for_cache_initialization */);
}
external_cache_->UpdateExtensionsList(base::DictionaryValue::From(
base::Value::ToUniquePtrValue(std::move(prefs))));
}
void DemoExtensionsExternalLoader::StartLoading() {
DemoSession::Get()->EnsureOfflineResourcesLoaded(base::BindOnce(
&DemoExtensionsExternalLoader::StartLoadingFromOfflineDemoResources,
weak_ptr_factory_.GetWeakPtr()));
}
void DemoExtensionsExternalLoader::OnExtensionListsUpdated(
const base::DictionaryValue* prefs) {
DCHECK(external_cache_);
// Notifies the provider that the extensions have either been downloaded or
// found in cache, and are ready to be installed.
LoadFinished(prefs->CreateDeepCopy());
}
void DemoExtensionsExternalLoader::OnExtensionLoadedInCache(
const std::string& id) {}
void DemoExtensionsExternalLoader::OnExtensionDownloadFailed(
const std::string& id) {}
std::string DemoExtensionsExternalLoader::GetInstalledExtensionVersion(
const std::string& id) {
return std::string();
}
void DemoExtensionsExternalLoader::StartLoadingFromOfflineDemoResources() {
DemoSession* demo_session = DemoSession::Get();
DCHECK(demo_session->resources()->loaded());
base::FilePath demo_extension_list =
demo_session->resources()->GetExternalExtensionsPrefsPath();
if (demo_extension_list.empty()) {
LoadFinished(std::make_unique<base::DictionaryValue>());
return;
}
base::PostTaskWithTraitsAndReplyWithResult(
FROM_HERE,
{base::MayBlock(), base::TaskPriority::USER_VISIBLE,
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
base::BindOnce(&LoadPrefsFromDisk, demo_extension_list),
base::BindOnce(
&DemoExtensionsExternalLoader::DemoExternalExtensionsPrefsLoaded,
weak_ptr_factory_.GetWeakPtr()));
}
void DemoExtensionsExternalLoader::DemoExternalExtensionsPrefsLoaded(
base::Optional<base::Value> prefs) {
if (!prefs.has_value()) {
LoadFinished(std::make_unique<base::DictionaryValue>());
return;
}
DCHECK(prefs.value().is_dict());
DemoSession* demo_session = DemoSession::Get();
DCHECK(demo_session);
// Adjust CRX paths in the prefs. Prefs on disk contains paths relative to
// the offline demo resources root - they have to be changed to absolute paths
// so extensions service knows from where to load them.
for (auto&& dict_item : prefs.value().DictItems()) {
if (!dict_item.second.is_dict())
continue;
const base::Value* path = dict_item.second.FindKeyOfType(
extensions::ExternalProviderImpl::kExternalCrx,
base::Value::Type::STRING);
if (!path || !path->is_string())
continue;
base::FilePath relative_path = base::FilePath(path->GetString());
if (relative_path.IsAbsolute()) {
LOG(ERROR) << "Ignoring demo extension with an absolute path "
<< dict_item.first;
dict_item.second.RemoveKey(
extensions::ExternalProviderImpl::kExternalCrx);
continue;
}
dict_item.second.SetKey(
extensions::ExternalProviderImpl::kExternalCrx,
base::Value(
demo_session->resources()->GetAbsolutePath(relative_path).value()));
}
LoadFinished(base::DictionaryValue::From(
base::Value::ToUniquePtrValue(std::move(prefs.value()))));
}
} // namespace chromeos