Commit db72457e authored by Alexey Baskakov's avatar Alexey Baskakov Committed by Commit Bot

WebApp: Implement basic Copy-On-Write for the batched writes API.

Previously we did in-place immediate updates for existing apps.
This was not consistent with the commit ideology since effects were becoming
visible immediately. It didn't match the creation and deletion of apps: they
were deferred until commit.

With this CL we implement basic copy-on-write for WebApp object updates.
We commit the created copy into the original for real, during the commit call.

In next CLs: Fix UpdateSync algorithm to avoid excessive change_processor
Put and Delete calls.

TBR=alancutter@chromium.org

Bug: 860583
Change-Id: I26eb53b668f74ce0c606a60af9016aa8a4cca1c6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1851792
Commit-Queue: Alexey Baskakov <loyso@chromium.org>
Reviewed-by: default avatarMarc Treib <treib@chromium.org>
Cr-Commit-Position: refs/heads/master@{#705007}
parent cfeb33f1
...@@ -19,6 +19,10 @@ WebApp::WebApp(const AppId& app_id) ...@@ -19,6 +19,10 @@ WebApp::WebApp(const AppId& app_id)
WebApp::~WebApp() = default; WebApp::~WebApp() = default;
WebApp::WebApp(const WebApp& web_app) = default;
WebApp& WebApp::operator=(WebApp&& web_app) = default;
void WebApp::AddSource(Source::Type source) { void WebApp::AddSource(Source::Type source) {
sources_[source] = true; sources_[source] = true;
} }
...@@ -74,14 +78,18 @@ void WebApp::SetIcons(Icons icons) { ...@@ -74,14 +78,18 @@ void WebApp::SetIcons(Icons icons) {
icons_ = std::move(icons); icons_ = std::move(icons);
} }
void WebApp::SetSyncData(const SyncData& sync_data) { void WebApp::SetSyncData(SyncData sync_data) {
sync_data_ = sync_data; sync_data_ = std::move(sync_data);
} }
WebApp::SyncData::SyncData() = default; WebApp::SyncData::SyncData() = default;
WebApp::SyncData::~SyncData() = default; WebApp::SyncData::~SyncData() = default;
WebApp::SyncData::SyncData(const SyncData& sync_data) = default;
WebApp::SyncData& WebApp::SyncData::operator=(SyncData&& sync_data) = default;
std::ostream& operator<<(std::ostream& out, const WebApp& app) { std::ostream& operator<<(std::ostream& out, const WebApp& app) {
const std::string theme_color = const std::string theme_color =
app.theme_color_.has_value() app.theme_color_.has_value()
......
...@@ -10,7 +10,6 @@ ...@@ -10,7 +10,6 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "base/macros.h"
#include "base/optional.h" #include "base/optional.h"
#include "chrome/browser/web_applications/components/web_app_constants.h" #include "chrome/browser/web_applications/components/web_app_constants.h"
#include "chrome/browser/web_applications/components/web_app_helpers.h" #include "chrome/browser/web_applications/components/web_app_helpers.h"
...@@ -25,6 +24,14 @@ class WebApp { ...@@ -25,6 +24,14 @@ class WebApp {
explicit WebApp(const AppId& app_id); explicit WebApp(const AppId& app_id);
~WebApp(); ~WebApp();
// Copyable and move-assignable to support Copy-on-Write with Commit.
WebApp(const WebApp& web_app);
WebApp& operator=(WebApp&& web_app);
// Explicitly disallow other copy ctors and assign operators.
WebApp(WebApp&&) = delete;
WebApp& operator=(const WebApp&) = delete;
const AppId& app_id() const { return app_id_; } const AppId& app_id() const { return app_id_; }
const std::string& name() const { return name_; } const std::string& name() const { return name_; }
...@@ -58,6 +65,10 @@ class WebApp { ...@@ -58,6 +65,10 @@ class WebApp {
struct SyncData { struct SyncData {
SyncData(); SyncData();
~SyncData(); ~SyncData();
// Copyable and move-assignable to support Copy-on-Write with Commit.
SyncData(const SyncData& sync_data);
SyncData& operator=(SyncData&& sync_data);
std::string name; std::string name;
base::Optional<SkColor> theme_color; base::Optional<SkColor> theme_color;
}; };
...@@ -81,14 +92,14 @@ class WebApp { ...@@ -81,14 +92,14 @@ class WebApp {
void SetIsSyncPlaceholder(bool is_sync_placeholder); void SetIsSyncPlaceholder(bool is_sync_placeholder);
void SetIcons(Icons icons); void SetIcons(Icons icons);
void SetSyncData(const SyncData& sync_data); void SetSyncData(SyncData sync_data);
private: private:
friend class WebAppDatabase; friend class WebAppDatabase;
friend bool operator==(const WebApp&, const WebApp&); friend bool operator==(const WebApp&, const WebApp&);
friend std::ostream& operator<<(std::ostream&, const WebApp&); friend std::ostream& operator<<(std::ostream&, const WebApp&);
const AppId app_id_; AppId app_id_;
// This set always contains at least one source. // This set always contains at least one source.
using Sources = std::bitset<Source::kMaxValue>; using Sources = std::bitset<Source::kMaxValue>;
...@@ -107,8 +118,6 @@ class WebApp { ...@@ -107,8 +118,6 @@ class WebApp {
Icons icons_; Icons icons_;
SyncData sync_data_; SyncData sync_data_;
DISALLOW_COPY_AND_ASSIGN(WebApp);
}; };
// For logging and debug purposes. // For logging and debug purposes.
......
...@@ -62,14 +62,14 @@ void WebAppDatabase::Write( ...@@ -62,14 +62,14 @@ void WebAppDatabase::Write(
write_batch->WriteData(web_app->app_id(), proto->SerializeAsString()); write_batch->WriteData(web_app->app_id(), proto->SerializeAsString());
} }
for (const AppId& app_id : update_data.apps_to_delete) for (const std::unique_ptr<WebApp>& web_app : update_data.apps_to_update) {
write_batch->DeleteData(app_id);
for (const WebApp* web_app : update_data.apps_to_update) {
auto proto = CreateWebAppProto(*web_app); auto proto = CreateWebAppProto(*web_app);
write_batch->WriteData(web_app->app_id(), proto->SerializeAsString()); write_batch->WriteData(web_app->app_id(), proto->SerializeAsString());
} }
for (const AppId& app_id : update_data.apps_to_delete)
write_batch->DeleteData(app_id);
store_->CommitWriteBatch( store_->CommitWriteBatch(
std::move(write_batch), std::move(write_batch),
base::BindOnce(&WebAppDatabase::OnDataWritten, base::BindOnce(&WebAppDatabase::OnDataWritten,
...@@ -216,7 +216,7 @@ std::unique_ptr<WebApp> WebAppDatabase::CreateWebApp( ...@@ -216,7 +216,7 @@ std::unique_ptr<WebApp> WebAppDatabase::CreateWebApp(
parsed_sync_data.name = sync_data.name(); parsed_sync_data.name = sync_data.name();
if (sync_data.has_theme_color()) if (sync_data.has_theme_color())
parsed_sync_data.theme_color = sync_data.theme_color(); parsed_sync_data.theme_color = sync_data.theme_color();
web_app->SetSyncData(parsed_sync_data); web_app->SetSyncData(std::move(parsed_sync_data));
WebApp::Icons icons; WebApp::Icons icons;
for (int i = 0; i < local_data.icons_size(); ++i) { for (int i = 0; i < local_data.icons_size(); ++i) {
......
...@@ -87,7 +87,7 @@ class WebAppDatabaseTest : public WebAppTest { ...@@ -87,7 +87,7 @@ class WebAppDatabaseTest : public WebAppTest {
WebApp::SyncData sync_data; WebApp::SyncData sync_data;
sync_data.name = "Sync" + name; sync_data.name = "Sync" + name;
sync_data.theme_color = synced_theme_color; sync_data.theme_color = synced_theme_color;
app->SetSyncData(sync_data); app->SetSyncData(std::move(sync_data));
return app; return app;
} }
...@@ -185,8 +185,8 @@ TEST_F(WebAppDatabaseTest, WriteAndDeleteAppsWithCallbacks) { ...@@ -185,8 +185,8 @@ TEST_F(WebAppDatabaseTest, WriteAndDeleteAppsWithCallbacks) {
const int num_apps = 10; const int num_apps = 10;
const std::string base_url = "https://example.com/path"; const std::string base_url = "https://example.com/path";
RegistryUpdateData::AppsToCreate apps_to_create; RegistryUpdateData::Apps apps_to_create;
RegistryUpdateData::AppsToDelete apps_to_delete; std::vector<AppId> apps_to_delete;
Registry expected_registry; Registry expected_registry;
for (int i = 0; i < num_apps; ++i) { for (int i = 0; i < num_apps; ++i) {
......
...@@ -134,7 +134,7 @@ void WebAppInstallFinalizer::FinalizeInstall( ...@@ -134,7 +134,7 @@ void WebAppInstallFinalizer::FinalizeInstall(
WebApp::SyncData sync_data; WebApp::SyncData sync_data;
sync_data.name = base::UTF16ToUTF8(web_app_info.title); sync_data.name = base::UTF16ToUTF8(web_app_info.title);
sync_data.theme_color = web_app_info.theme_color; sync_data.theme_color = web_app_info.theme_color;
web_app->SetSyncData(sync_data); web_app->SetSyncData(std::move(sync_data));
icon_manager_->WriteData( icon_manager_->WriteData(
std::move(app_id), std::make_unique<WebApplicationInfo>(web_app_info), std::move(app_id), std::make_unique<WebApplicationInfo>(web_app_info),
......
...@@ -629,4 +629,47 @@ TEST_F(WebAppRegistrarTest, ScopedRegistryUpdate) { ...@@ -629,4 +629,47 @@ TEST_F(WebAppRegistrarTest, ScopedRegistryUpdate) {
EXPECT_TRUE(ids.empty()); EXPECT_TRUE(ids.empty());
} }
TEST_F(WebAppRegistrarTest, CopyOnWrite) {
controller().Init();
const GURL launch_url("https://example.com");
const AppId app_id = GenerateAppIdFromURL(launch_url);
const WebApp* app = nullptr;
{
auto new_app = CreateWebApp(launch_url.spec());
app = new_app.get();
RegisterApp(std::move(new_app));
}
{
std::unique_ptr<WebAppRegistryUpdate> update = sync_bridge().BeginUpdate();
WebApp* app_copy = update->UpdateApp(app_id);
EXPECT_TRUE(app_copy);
EXPECT_NE(app_copy, app);
app_copy->SetName("New Name");
EXPECT_EQ(app_copy->name(), "New Name");
EXPECT_EQ(app->name(), "Name");
app_copy->AddSource(Source::kPolicy);
app_copy->RemoveSource(Source::kSync);
EXPECT_FALSE(app_copy->IsSynced());
EXPECT_TRUE(app_copy->HasAnySources());
EXPECT_TRUE(app->IsSynced());
EXPECT_TRUE(app->HasAnySources());
SyncBridgeCommitUpdate(std::move(update));
}
// Pointer value stays the same.
EXPECT_EQ(app, registrar().GetAppById(app_id));
EXPECT_EQ(app->name(), "New Name");
EXPECT_FALSE(app->IsSynced());
EXPECT_TRUE(app->HasAnySources());
}
} // namespace web_app } // namespace web_app
...@@ -20,10 +20,10 @@ bool RegistryUpdateData::IsEmpty() const { ...@@ -20,10 +20,10 @@ bool RegistryUpdateData::IsEmpty() const {
apps_to_update.empty(); apps_to_update.empty();
} }
WebAppRegistryUpdate::WebAppRegistryUpdate( WebAppRegistryUpdate::WebAppRegistryUpdate(const WebAppRegistrar* registrar,
WebAppRegistrarMutable* mutable_registrar) util::PassKey<WebAppSyncBridge>)
: mutable_registrar_(mutable_registrar) { : registrar_(registrar) {
DCHECK(mutable_registrar_); DCHECK(registrar_);
update_data_ = std::make_unique<RegistryUpdateData>(); update_data_ = std::make_unique<RegistryUpdateData>();
} }
...@@ -32,7 +32,7 @@ WebAppRegistryUpdate::~WebAppRegistryUpdate() = default; ...@@ -32,7 +32,7 @@ WebAppRegistryUpdate::~WebAppRegistryUpdate() = default;
void WebAppRegistryUpdate::CreateApp(std::unique_ptr<WebApp> web_app) { void WebAppRegistryUpdate::CreateApp(std::unique_ptr<WebApp> web_app) {
DCHECK(update_data_); DCHECK(update_data_);
DCHECK(!web_app->app_id().empty()); DCHECK(!web_app->app_id().empty());
DCHECK(!mutable_registrar_->GetAppById(web_app->app_id())); DCHECK(!registrar_->GetAppById(web_app->app_id()));
DCHECK(!base::Contains(update_data_->apps_to_create, web_app)); DCHECK(!base::Contains(update_data_->apps_to_create, web_app));
update_data_->apps_to_create.push_back(std::move(web_app)); update_data_->apps_to_create.push_back(std::move(web_app));
...@@ -41,7 +41,7 @@ void WebAppRegistryUpdate::CreateApp(std::unique_ptr<WebApp> web_app) { ...@@ -41,7 +41,7 @@ void WebAppRegistryUpdate::CreateApp(std::unique_ptr<WebApp> web_app) {
void WebAppRegistryUpdate::DeleteApp(const AppId& app_id) { void WebAppRegistryUpdate::DeleteApp(const AppId& app_id) {
DCHECK(update_data_); DCHECK(update_data_);
DCHECK(!app_id.empty()); DCHECK(!app_id.empty());
DCHECK(mutable_registrar_->GetAppById(app_id)); DCHECK(registrar_->GetAppById(app_id));
DCHECK(!base::Contains(update_data_->apps_to_delete, app_id)); DCHECK(!base::Contains(update_data_->apps_to_delete, app_id));
update_data_->apps_to_delete.push_back(app_id); update_data_->apps_to_delete.push_back(app_id);
...@@ -49,11 +49,21 @@ void WebAppRegistryUpdate::DeleteApp(const AppId& app_id) { ...@@ -49,11 +49,21 @@ void WebAppRegistryUpdate::DeleteApp(const AppId& app_id) {
WebApp* WebAppRegistryUpdate::UpdateApp(const AppId& app_id) { WebApp* WebAppRegistryUpdate::UpdateApp(const AppId& app_id) {
DCHECK(update_data_); DCHECK(update_data_);
WebApp* app = mutable_registrar_->GetAppByIdMutable(app_id); const WebApp* original_app = registrar_->GetAppById(app_id);
if (app) if (!original_app)
update_data_->apps_to_update.insert(app); return nullptr;
return app; for (auto& app_to_update : update_data_->apps_to_update) {
if (app_to_update->app_id() == app_id)
return app_to_update.get();
}
// Make a copy on write.
auto app_copy = std::make_unique<WebApp>(*original_app);
WebApp* app_copy_ptr = app_copy.get();
update_data_->apps_to_update.push_back(std::move(app_copy));
return app_copy_ptr;
} }
std::unique_ptr<RegistryUpdateData> WebAppRegistryUpdate::TakeUpdateData() { std::unique_ptr<RegistryUpdateData> WebAppRegistryUpdate::TakeUpdateData() {
......
...@@ -9,14 +9,14 @@ ...@@ -9,14 +9,14 @@
#include <vector> #include <vector>
#include "base/callback.h" #include "base/callback.h"
#include "base/containers/flat_set.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/util/type_safety/pass_key.h"
#include "chrome/browser/web_applications/components/web_app_helpers.h" #include "chrome/browser/web_applications/components/web_app_helpers.h"
namespace web_app { namespace web_app {
class WebApp; class WebApp;
class WebAppRegistrarMutable; class WebAppRegistrar;
class WebAppSyncBridge; class WebAppSyncBridge;
// A raw registry update data. // A raw registry update data.
...@@ -24,14 +24,11 @@ struct RegistryUpdateData { ...@@ -24,14 +24,11 @@ struct RegistryUpdateData {
RegistryUpdateData(); RegistryUpdateData();
~RegistryUpdateData(); ~RegistryUpdateData();
using AppsToCreate = std::vector<std::unique_ptr<WebApp>>; using Apps = std::vector<std::unique_ptr<WebApp>>;
AppsToCreate apps_to_create; Apps apps_to_create;
Apps apps_to_update;
using AppsToDelete = std::vector<AppId>; std::vector<AppId> apps_to_delete;
AppsToDelete apps_to_delete;
using AppsToUpdate = base::flat_set<const WebApp*>;
AppsToUpdate apps_to_update;
bool IsEmpty() const; bool IsEmpty() const;
...@@ -43,7 +40,8 @@ struct RegistryUpdateData { ...@@ -43,7 +40,8 @@ struct RegistryUpdateData {
// WebAppRegistryUpdate is a part of WebAppSyncBridge class. // WebAppRegistryUpdate is a part of WebAppSyncBridge class.
class WebAppRegistryUpdate { class WebAppRegistryUpdate {
public: public:
explicit WebAppRegistryUpdate(WebAppRegistrarMutable* mutable_registrar); WebAppRegistryUpdate(const WebAppRegistrar* registrar,
util::PassKey<WebAppSyncBridge>);
~WebAppRegistryUpdate(); ~WebAppRegistryUpdate();
// Register a new app. // Register a new app.
...@@ -58,7 +56,7 @@ class WebAppRegistryUpdate { ...@@ -58,7 +56,7 @@ class WebAppRegistryUpdate {
private: private:
std::unique_ptr<RegistryUpdateData> update_data_; std::unique_ptr<RegistryUpdateData> update_data_;
WebAppRegistrarMutable* const mutable_registrar_; const WebAppRegistrar* const registrar_;
DISALLOW_COPY_AND_ASSIGN(WebAppRegistryUpdate); DISALLOW_COPY_AND_ASSIGN(WebAppRegistryUpdate);
}; };
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "base/containers/flat_set.h" #include "base/containers/flat_set.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/optional.h" #include "base/optional.h"
#include "base/util/type_safety/pass_key.h"
#include "chrome/browser/web_applications/components/web_app_helpers.h" #include "chrome/browser/web_applications/components/web_app_helpers.h"
#include "chrome/browser/web_applications/web_app.h" #include "chrome/browser/web_applications/web_app.h"
#include "chrome/browser/web_applications/web_app_database.h" #include "chrome/browser/web_applications/web_app_database.h"
...@@ -81,7 +82,7 @@ void ApplySyncDataToApp(const sync_pb::WebAppSpecifics& sync_data, ...@@ -81,7 +82,7 @@ void ApplySyncDataToApp(const sync_pb::WebAppSpecifics& sync_data,
parsed_sync_data.name = sync_data.name(); parsed_sync_data.name = sync_data.name();
if (sync_data.has_theme_color()) if (sync_data.has_theme_color())
parsed_sync_data.theme_color = sync_data.theme_color(); parsed_sync_data.theme_color = sync_data.theme_color();
app->SetSyncData(parsed_sync_data); app->SetSyncData(std::move(parsed_sync_data));
} }
bool AreAppsLocallyInstalledByDefault() { bool AreAppsLocallyInstalledByDefault() {
...@@ -133,7 +134,8 @@ WebAppSyncBridge::~WebAppSyncBridge() = default; ...@@ -133,7 +134,8 @@ WebAppSyncBridge::~WebAppSyncBridge() = default;
std::unique_ptr<WebAppRegistryUpdate> WebAppSyncBridge::BeginUpdate() { std::unique_ptr<WebAppRegistryUpdate> WebAppSyncBridge::BeginUpdate() {
DCHECK(!is_in_update_); DCHECK(!is_in_update_);
is_in_update_ = true; is_in_update_ = true;
return std::make_unique<WebAppRegistryUpdate>(registrar_); return std::make_unique<WebAppRegistryUpdate>(
registrar_, util::PassKey<WebAppSyncBridge>());
} }
void WebAppSyncBridge::CommitUpdate( void WebAppSyncBridge::CommitUpdate(
...@@ -197,11 +199,11 @@ void WebAppSyncBridge::CheckRegistryUpdateData( ...@@ -197,11 +199,11 @@ void WebAppSyncBridge::CheckRegistryUpdateData(
for (const std::unique_ptr<WebApp>& web_app : update_data.apps_to_create) for (const std::unique_ptr<WebApp>& web_app : update_data.apps_to_create)
DCHECK(!registrar_->GetAppById(web_app->app_id())); DCHECK(!registrar_->GetAppById(web_app->app_id()));
for (const std::unique_ptr<WebApp>& web_app : update_data.apps_to_update)
DCHECK(registrar_->GetAppById(web_app->app_id()));
for (const AppId& app_id : update_data.apps_to_delete) for (const AppId& app_id : update_data.apps_to_delete)
DCHECK(registrar_->GetAppById(app_id)); DCHECK(registrar_->GetAppById(app_id));
for (const WebApp* web_app : update_data.apps_to_update)
DCHECK(registrar_->GetAppById(web_app->app_id()));
#endif #endif
} }
...@@ -217,8 +219,13 @@ std::vector<std::unique_ptr<WebApp>> WebAppSyncBridge::UpdateRegistrar( ...@@ -217,8 +219,13 @@ std::vector<std::unique_ptr<WebApp>> WebAppSyncBridge::UpdateRegistrar(
registrar_->registry().emplace(std::move(app_id), std::move(web_app)); registrar_->registry().emplace(std::move(app_id), std::move(web_app));
} }
// update_data->apps_to_update are ignored here because we do in-place for (std::unique_ptr<WebApp>& web_app : update_data->apps_to_update) {
// updates. WebApp* original_web_app = registrar_->GetAppByIdMutable(web_app->app_id());
DCHECK(original_web_app);
// Commit previously created copy into original. Preserve original web_app
// object pointer value (the object's identity) to support stored pointers.
*original_web_app = std::move(*web_app);
}
for (const AppId& app_id : update_data->apps_to_delete) { for (const AppId& app_id : update_data->apps_to_delete) {
auto it = registrar_->registry().find(app_id); auto it = registrar_->registry().find(app_id);
...@@ -252,11 +259,12 @@ void WebAppSyncBridge::UpdateSync( ...@@ -252,11 +259,12 @@ void WebAppSyncBridge::UpdateSync(
// An app may obtain or may loose IsSynced flag without being deleted. We // An app may obtain or may loose IsSynced flag without being deleted. We
// should conservatively include or exclude the app from the synced apps // should conservatively include or exclude the app from the synced apps
// subset. // subset. TODO(loyso): Use previous state of the app (and CoW) to detect
// if IsSynced flag changed.
// //
// TODO(loyso): Send an update to sync server only if any sync-specific // TODO(loyso): Send an update to sync server only if any sync-specific
// data was changed. Implement some dirty flags in WebApp setter methods. // data was changed. Implement some dirty flags in WebApp setter methods.
for (const WebApp* app : update_data.apps_to_update) { for (const std::unique_ptr<WebApp>& app : update_data.apps_to_update) {
if (app->IsSynced()) { if (app->IsSynced()) {
change_processor()->Put(app->app_id(), CreateSyncEntityData(*app), change_processor()->Put(app->app_id(), CreateSyncEntityData(*app),
metadata_change_list); metadata_change_list);
...@@ -266,7 +274,8 @@ void WebAppSyncBridge::UpdateSync( ...@@ -266,7 +274,8 @@ void WebAppSyncBridge::UpdateSync(
} }
// We should unconditionally delete from sync (in case IsSynced flag was // We should unconditionally delete from sync (in case IsSynced flag was
// removed during the update). // removed during the update). TODO(loyso): Use previous state of the app (and
// CoW) to detect if IsSynced flag changed.
for (const AppId& app_id : update_data.apps_to_delete) { for (const AppId& app_id : update_data.apps_to_delete) {
const WebApp* app = registrar_->GetAppById(app_id); const WebApp* app = registrar_->GetAppById(app_id);
DCHECK(app); DCHECK(app);
...@@ -328,7 +337,7 @@ void WebAppSyncBridge::ApplySyncDataChange( ...@@ -328,7 +337,7 @@ void WebAppSyncBridge::ApplySyncDataChange(
// app_id is storage key. // app_id is storage key.
const AppId& app_id = change.storage_key(); const AppId& app_id = change.storage_key();
WebApp* existing_web_app = registrar_->GetAppByIdMutable(app_id); const WebApp* existing_web_app = registrar_->GetAppByIdMutable(app_id);
// Handle deletion first. // Handle deletion first.
if (change.type() == syncer::EntityChange::ACTION_DELETE) { if (change.type() == syncer::EntityChange::ACTION_DELETE) {
...@@ -336,10 +345,12 @@ void WebAppSyncBridge::ApplySyncDataChange( ...@@ -336,10 +345,12 @@ void WebAppSyncBridge::ApplySyncDataChange(
DLOG(ERROR) << "ApplySyncDataChange error: no app to delete"; DLOG(ERROR) << "ApplySyncDataChange error: no app to delete";
return; return;
} }
existing_web_app->RemoveSource(Source::kSync); // Do copy on write:
auto app_copy = std::make_unique<WebApp>(*existing_web_app);
app_copy->RemoveSource(Source::kSync);
if (existing_web_app->HasAnySources()) if (app_copy->HasAnySources())
update_local_data->apps_to_update.insert(existing_web_app); update_local_data->apps_to_update.push_back(std::move(app_copy));
else else
update_local_data->apps_to_delete.push_back(app_id); update_local_data->apps_to_delete.push_back(app_id);
...@@ -352,10 +363,12 @@ void WebAppSyncBridge::ApplySyncDataChange( ...@@ -352,10 +363,12 @@ void WebAppSyncBridge::ApplySyncDataChange(
if (existing_web_app) { if (existing_web_app) {
// Any entities that appear in both sets must be merged. // Any entities that appear in both sets must be merged.
ApplySyncDataToApp(specifics, existing_web_app); // Do copy on write:
auto app_copy = std::make_unique<WebApp>(*existing_web_app);
ApplySyncDataToApp(specifics, app_copy.get());
// Preserve web_app->is_locally_installed user's choice here. // Preserve web_app->is_locally_installed user's choice here.
update_local_data->apps_to_update.insert(existing_web_app); update_local_data->apps_to_update.push_back(std::move(app_copy));
} else { } else {
// Any remote entities that don’t exist locally must be written to local // Any remote entities that don’t exist locally must be written to local
// storage. // storage.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment