blob: 5c3c2469d2eb5154b66b797728457c313ec9a6fc [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/web_applications/web_app_sync_bridge.h"
#include <memory>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/callback.h"
#include "base/logging.h"
#include "base/optional.h"
#include "chrome/browser/web_applications/components/web_app_helpers.h"
#include "chrome/browser/web_applications/web_app_database.h"
#include "chrome/browser/web_applications/web_app_database_factory.h"
#include "chrome/browser/web_applications/web_app_registry_update.h"
#include "chrome/common/channel_info.h"
#include "components/sync/base/model_type.h"
#include "components/sync/base/report_unrecoverable_error.h"
#include "components/sync/model/entity_data.h"
#include "components/sync/model/metadata_batch.h"
#include "components/sync/model/metadata_change_list.h"
#include "components/sync/model/model_type_store.h"
#include "components/sync/model/mutable_data_batch.h"
#include "components/sync/model_impl/client_tag_based_model_type_processor.h"
#include "components/sync/protocol/web_app_specifics.pb.h"
#include "third_party/blink/public/mojom/manifest/display_mode.mojom.h"
#include "url/gurl.h"
namespace web_app {
namespace {
std::unique_ptr<syncer::EntityData> CreateSyncEntityData(const WebApp& app) {
auto entity_data = std::make_unique<syncer::EntityData>();
entity_data->name = app.name();
sync_pb::WebAppSpecifics* specifics =
entity_data->specifics.mutable_web_app();
specifics->set_launch_url(app.launch_url().spec());
specifics->set_name(app.name());
specifics->set_display_mode(ToWebAppSpecificsDisplayMode(app.display_mode()));
if (app.theme_color().has_value())
specifics->set_theme_color(app.theme_color().value());
return entity_data;
}
} // namespace
WebAppSyncBridge::WebAppSyncBridge(
Profile* profile,
AbstractWebAppDatabaseFactory* database_factory,
WebAppRegistrarMutable* registrar)
: WebAppSyncBridge(
profile,
database_factory,
registrar,
std::make_unique<syncer::ClientTagBasedModelTypeProcessor>(
syncer::WEB_APPS,
base::BindRepeating(&syncer::ReportUnrecoverableError,
chrome::GetChannel()))) {}
WebAppSyncBridge::WebAppSyncBridge(
Profile* profile,
AbstractWebAppDatabaseFactory* database_factory,
WebAppRegistrarMutable* registrar,
std::unique_ptr<syncer::ModelTypeChangeProcessor> change_processor)
: AppRegistryController(profile),
syncer::ModelTypeSyncBridge(std::move(change_processor)),
registrar_(registrar) {
DCHECK(database_factory);
DCHECK(registrar_);
database_ = std::make_unique<WebAppDatabase>(
database_factory,
base::BindRepeating(&WebAppSyncBridge::ReportErrorToChangeProcessor,
base::Unretained(this)));
}
WebAppSyncBridge::~WebAppSyncBridge() = default;
std::unique_ptr<WebAppRegistryUpdate> WebAppSyncBridge::BeginUpdate() {
DCHECK(!is_in_update_);
is_in_update_ = true;
database_->BeginTransaction();
return std::make_unique<WebAppRegistryUpdate>(registrar_);
}
void WebAppSyncBridge::CommitUpdate(
std::unique_ptr<WebAppRegistryUpdate> update,
CommitCallback callback) {
DCHECK(is_in_update_);
is_in_update_ = false;
if (update == nullptr || update->update_data().IsEmpty()) {
database_->CancelTransaction();
std::move(callback).Run(/*success*/ true);
return;
}
CheckRegistryUpdateData(update->update_data());
registrar_->CountMutation();
std::unique_ptr<RegistryUpdateData> update_data = update->TakeUpdateData();
database_->CommitTransaction(
*update_data,
base::BindOnce(&WebAppSyncBridge::OnDataWritten,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
UpdateRegistrar(std::move(update_data));
}
void WebAppSyncBridge::Init(base::OnceClosure callback) {
database_->OpenDatabase(base::BindOnce(&WebAppSyncBridge::OnDatabaseOpened,
weak_ptr_factory_.GetWeakPtr(),
std::move(callback)));
}
void WebAppSyncBridge::SetAppDisplayMode(
const AppId& app_id,
blink::mojom::DisplayMode display_mode) {
ScopedRegistryUpdate update(this);
WebApp* web_app = update->UpdateApp(app_id);
if (web_app)
web_app->SetDisplayMode(display_mode);
}
void WebAppSyncBridge::SetAppIsLocallyInstalledForTesting(
const AppId& app_id,
bool is_locally_installed) {
ScopedRegistryUpdate update(this);
WebApp* web_app = update->UpdateApp(app_id);
if (web_app)
web_app->SetIsLocallyInstalled(is_locally_installed);
}
WebAppSyncBridge* WebAppSyncBridge::AsWebAppSyncBridge() {
return this;
}
void WebAppSyncBridge::CheckRegistryUpdateData(
const RegistryUpdateData& update_data) const {
#if DCHECK_IS_ON()
for (const std::unique_ptr<WebApp>& web_app : update_data.apps_to_create)
DCHECK(!registrar_->GetAppById(web_app->app_id()));
for (const AppId& app_id : update_data.apps_to_delete)
DCHECK(registrar_->GetAppById(app_id));
for (const WebApp* web_app : update_data.apps_to_update)
DCHECK(registrar_->GetAppById(web_app->app_id()));
#endif
}
void WebAppSyncBridge::UpdateRegistrar(
std::unique_ptr<RegistryUpdateData> update_data) {
for (std::unique_ptr<WebApp>& web_app : update_data->apps_to_create) {
AppId app_id = web_app->app_id();
DCHECK(!registrar_->GetAppById(app_id));
registrar_->registry().emplace(std::move(app_id), std::move(web_app));
}
for (const AppId& app_id : update_data->apps_to_delete) {
auto it = registrar_->registry().find(app_id);
DCHECK(it != registrar_->registry().end());
registrar_->registry().erase(it);
}
}
void WebAppSyncBridge::OnDatabaseOpened(
base::OnceClosure callback,
Registry registry,
std::unique_ptr<syncer::MetadataBatch> metadata_batch) {
// Provide sync metadata to the processor _before_ any local changes occur.
change_processor()->ModelReadyToSync(std::move(metadata_batch));
registrar_->InitRegistry(std::move(registry));
std::move(callback).Run();
}
void WebAppSyncBridge::OnDataWritten(CommitCallback callback, bool success) {
if (!success)
DLOG(ERROR) << "WebAppSyncBridge commit failed";
std::move(callback).Run(success);
}
void WebAppSyncBridge::ReportErrorToChangeProcessor(
const syncer::ModelError& error) {
change_processor()->ReportError(error);
}
std::unique_ptr<syncer::MetadataChangeList>
WebAppSyncBridge::CreateMetadataChangeList() {
return syncer::ModelTypeStore::WriteBatch::CreateMetadataChangeList();
}
base::Optional<syncer::ModelError> WebAppSyncBridge::MergeSyncData(
std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
syncer::EntityChangeList entity_data) {
NOTIMPLEMENTED();
return base::nullopt;
}
base::Optional<syncer::ModelError> WebAppSyncBridge::ApplySyncChanges(
std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
syncer::EntityChangeList entity_changes) {
NOTIMPLEMENTED();
return base::nullopt;
}
void WebAppSyncBridge::GetData(StorageKeyList storage_keys,
DataCallback callback) {
auto data_batch = std::make_unique<syncer::MutableDataBatch>();
for (const AppId& app_id : storage_keys) {
const WebApp* app = registrar_->GetAppById(app_id);
if (app && app->IsSynced())
data_batch->Put(app->app_id(), CreateSyncEntityData(*app));
}
std::move(callback).Run(std::move(data_batch));
}
void WebAppSyncBridge::GetAllDataForDebugging(DataCallback callback) {
auto data_batch = std::make_unique<syncer::MutableDataBatch>();
for (const WebApp& app : registrar_->AllApps()) {
if (app.IsSynced())
data_batch->Put(app.app_id(), CreateSyncEntityData(app));
}
std::move(callback).Run(std::move(data_batch));
}
std::string WebAppSyncBridge::GetClientTag(
const syncer::EntityData& entity_data) {
DCHECK(entity_data.specifics.has_web_app());
const GURL launch_url(entity_data.specifics.web_app().launch_url());
DCHECK(!launch_url.is_empty());
DCHECK(launch_url.is_valid());
return GenerateAppIdFromURL(launch_url);
}
std::string WebAppSyncBridge::GetStorageKey(
const syncer::EntityData& entity_data) {
return GetClientTag(entity_data);
}
} // namespace web_app