blob: 7043237b9a9cec36c628b72520ee7136e827b034 [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/web_applications/web_app_database.h"
#include "base/bind.h"
#include "base/callback.h"
#include "chrome/browser/web_applications/proto/web_app.pb.h"
#include "chrome/browser/web_applications/web_app.h"
#include "chrome/browser/web_applications/web_app_database_factory.h"
#include "components/sync/base/model_type.h"
#include "components/sync/model/model_error.h"
namespace web_app {
WebAppDatabase::WebAppDatabase(AbstractWebAppDatabaseFactory* database_factory)
: database_factory_(database_factory) {
DCHECK(database_factory_);
}
WebAppDatabase::~WebAppDatabase() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
void WebAppDatabase::OpenDatabase(OnceRegistryOpenedCallback callback) {
DCHECK(!store_);
syncer::OnceModelTypeStoreFactory store_factory =
database_factory_->GetStoreFactory();
// CreateStore then ReadRegistry.
CreateStore(
std::move(store_factory),
base::BindOnce(&WebAppDatabase::ReadRegistry,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void WebAppDatabase::WriteWebApp(const WebApp& web_app) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(opened_);
BeginTransaction();
auto proto = CreateWebAppProto(web_app);
write_batch_->WriteData(proto->app_id(), proto->SerializeAsString());
CommitTransaction();
}
void WebAppDatabase::DeleteWebApps(std::vector<AppId> app_ids) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(opened_);
BeginTransaction();
for (auto& app_id : app_ids)
write_batch_->DeleteData(app_id);
CommitTransaction();
}
// static
std::unique_ptr<WebAppProto> WebAppDatabase::CreateWebAppProto(
const WebApp& web_app) {
auto proto = std::make_unique<WebAppProto>();
proto->set_app_id(web_app.app_id());
proto->set_name(web_app.name());
proto->set_description(web_app.description());
proto->set_launch_url(web_app.launch_url().spec());
if (!web_app.scope().is_empty())
proto->set_scope(web_app.scope().spec());
if (web_app.theme_color())
proto->set_theme_color(web_app.theme_color().value());
for (const WebApp::IconInfo& icon : web_app.icons()) {
WebAppIconInfoProto* icon_proto = proto->add_icons();
icon_proto->set_url(icon.url.spec());
icon_proto->set_size_in_px(icon.size_in_px);
}
return proto;
}
// static
std::unique_ptr<WebApp> WebAppDatabase::CreateWebApp(const WebAppProto& proto) {
auto web_app = std::make_unique<WebApp>(proto.app_id());
GURL launch_url(proto.launch_url());
if (launch_url.is_empty() || !launch_url.is_valid()) {
LOG(ERROR) << "WebApp proto launch_url parse error: "
<< launch_url.possibly_invalid_spec();
return nullptr;
}
web_app->SetLaunchUrl(launch_url);
web_app->SetName(proto.name());
web_app->SetDescription(proto.description());
if (proto.has_scope()) {
GURL scope(proto.scope());
if (scope.is_empty() || !scope.is_valid()) {
LOG(ERROR) << "WebApp proto scope parse error: "
<< scope.possibly_invalid_spec();
return nullptr;
}
web_app->SetScope(scope);
}
if (proto.has_theme_color())
web_app->SetThemeColor(proto.theme_color());
if (proto.icons_size() == 0) {
LOG(ERROR) << "WebApp proto parse icons error: no icons";
return nullptr;
}
WebApp::Icons icons;
for (int i = 0; i < proto.icons_size(); ++i) {
const WebAppIconInfoProto& icon_proto = proto.icons(i);
GURL icon_url(icon_proto.url());
if (icon_url.is_empty() || !icon_url.is_valid()) {
LOG(ERROR) << "WebApp IconInfo proto url parse error: "
<< icon_url.possibly_invalid_spec();
return nullptr;
}
icons.push_back({icon_url, icon_proto.size_in_px()});
}
web_app->SetIcons(std::move(icons));
return web_app;
}
void WebAppDatabase::ReadRegistry(OnceRegistryOpenedCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(store_);
store_->ReadAllData(base::BindOnce(&WebAppDatabase::OnAllDataRead,
weak_ptr_factory_.GetWeakPtr(),
std::move(callback)));
}
void WebAppDatabase::CreateStore(
syncer::OnceModelTypeStoreFactory store_factory,
base::OnceClosure closure) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// For now we use syncer:APPS prefix within local isolated LevelDB, no sync.
// TODO(loyso): Create separate ModelType::WEB_APPS before implementing sync.
// Otherwise it may interfere with existing APPS data.
std::move(store_factory)
.Run(syncer::APPS,
base::BindOnce(&WebAppDatabase::OnStoreCreated,
weak_ptr_factory_.GetWeakPtr(), std::move(closure)));
}
void WebAppDatabase::OnStoreCreated(
base::OnceClosure closure,
const base::Optional<syncer::ModelError>& error,
std::unique_ptr<syncer::ModelTypeStore> store) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (error) {
LOG(ERROR) << "WebApps LevelDB opening error: " << error->ToString();
return;
}
store_ = std::move(store);
std::move(closure).Run();
}
void WebAppDatabase::OnAllDataRead(
OnceRegistryOpenedCallback callback,
const base::Optional<syncer::ModelError>& error,
std::unique_ptr<syncer::ModelTypeStore::RecordList> data_records) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (error) {
LOG(ERROR) << "WebApps LevelDB read error: " << error->ToString();
return;
}
Registry registry;
for (const syncer::ModelTypeStore::Record& record : *data_records) {
const AppId app_id = record.id;
std::unique_ptr<WebApp> web_app = ParseWebApp(app_id, record.value);
if (web_app)
registry.emplace(app_id, std::move(web_app));
}
std::move(callback).Run(std::move(registry));
opened_ = true;
}
void WebAppDatabase::OnDataWritten(
const base::Optional<syncer::ModelError>& error) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (error)
LOG(ERROR) << "WebApps LevelDB write error: " << error->ToString();
}
// static
std::unique_ptr<WebApp> WebAppDatabase::ParseWebApp(const AppId& app_id,
const std::string& value) {
WebAppProto proto;
const bool parsed = proto.ParseFromString(value);
if (!parsed || proto.app_id() != app_id) {
LOG(ERROR) << "WebApps LevelDB parse error (unknown).";
return nullptr;
}
return CreateWebApp(proto);
}
void WebAppDatabase::BeginTransaction() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!write_batch_);
write_batch_ = store_->CreateWriteBatch();
}
void WebAppDatabase::CommitTransaction() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(write_batch_);
store_->CommitWriteBatch(std::move(write_batch_),
base::BindOnce(&WebAppDatabase::OnDataWritten,
weak_ptr_factory_.GetWeakPtr()));
write_batch_.reset();
}
} // namespace web_app