[Shortcut] Create shortcut updater.
This CL creates the shortcut updater to handle updates of shortcuts.
BUG=1412708
Change-Id: I8c04e58d28f7400997ce4654a0952d7bc678879e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4556245
Reviewed-by: Tim Sergeant <tsergeant@chromium.org>
Auto-Submit: Maggie Cai <mxcai@chromium.org>
Commit-Queue: Tim Sergeant <tsergeant@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1149445}
diff --git a/components/services/app_service/public/cpp/shortcut/BUILD.gn b/components/services/app_service/public/cpp/shortcut/BUILD.gn
index dd77ffc..7f05d2e 100644
--- a/components/services/app_service/public/cpp/shortcut/BUILD.gn
+++ b/components/services/app_service/public/cpp/shortcut/BUILD.gn
@@ -11,6 +11,8 @@
"shortcut.h",
"shortcut_registry_cache.cc",
"shortcut_registry_cache.h",
+ "shortcut_update.cc",
+ "shortcut_update.h",
]
defines = [ "IS_SHORTCUT_IMPL" ]
@@ -19,6 +21,7 @@
"//base",
"//components/crx_file",
"//components/services/app_service/public/cpp:macros",
+ "//third_party/abseil-cpp:absl",
]
}
@@ -28,6 +31,7 @@
sources = [
"shortcut_registry_cache_unittest.cc",
"shortcut_unittest.cc",
+ "shortcut_update_unittest.cc",
]
deps = [
diff --git a/components/services/app_service/public/cpp/shortcut/shortcut.cc b/components/services/app_service/public/cpp/shortcut/shortcut.cc
index f139eab..61cb91f 100644
--- a/components/services/app_service/public/cpp/shortcut/shortcut.cc
+++ b/components/services/app_service/public/cpp/shortcut/shortcut.cc
@@ -34,7 +34,9 @@
std::string Shortcut::ToString() const {
std::stringstream out;
out << "shortcut_id: " << shortcut_id << std::endl;
- out << "- name: " << name << std::endl;
+ if (name.has_value()) {
+ out << "- name: " << name.value() << std::endl;
+ }
out << "- shortcut_source: " << EnumToString(shortcut_source) << std::endl;
out << "- host_app_id: " << host_app_id << std::endl;
out << "- local_id: " << local_id << std::endl;
diff --git a/components/services/app_service/public/cpp/shortcut/shortcut.h b/components/services/app_service/public/cpp/shortcut/shortcut.h
index 5622d593..3020da43 100644
--- a/components/services/app_service/public/cpp/shortcut/shortcut.h
+++ b/components/services/app_service/public/cpp/shortcut/shortcut.h
@@ -13,6 +13,7 @@
#include "base/component_export.h"
#include "base/types/strong_alias.h"
#include "components/services/app_service/public/cpp/macros.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
namespace apps {
@@ -47,7 +48,7 @@
// - local_id: shortcut_1
std::string ToString() const;
// Name of the shortcut.
- std::string name;
+ absl::optional<std::string> name;
// Shortcut creation source.
ShortcutSource shortcut_source;
diff --git a/components/services/app_service/public/cpp/shortcut/shortcut_registry_cache.cc b/components/services/app_service/public/cpp/shortcut/shortcut_registry_cache.cc
index b6789d44..c03d77c 100644
--- a/components/services/app_service/public/cpp/shortcut/shortcut_registry_cache.cc
+++ b/components/services/app_service/public/cpp/shortcut/shortcut_registry_cache.cc
@@ -9,6 +9,7 @@
#include "base/check.h"
#include "base/containers/contains.h"
+#include "components/services/app_service/public/cpp/shortcut/shortcut_update.h"
namespace apps {
@@ -23,8 +24,11 @@
is_updating_ = true;
const ShortcutId shortcut_id = delta->shortcut_id;
- states_.emplace(shortcut_id, delta->Clone());
- // TODO(crbug.com/1412708): Handle delta merge.
+ if (HasShortcut(shortcut_id)) {
+ ShortcutUpdate::Merge(states_[shortcut_id].get(), delta.get());
+ } else {
+ states_.emplace(shortcut_id, delta->Clone());
+ }
// TODO(crbug.com/1412708): Update observer.
is_updating_ = false;
}
diff --git a/components/services/app_service/public/cpp/shortcut/shortcut_registry_cache_unittest.cc b/components/services/app_service/public/cpp/shortcut/shortcut_registry_cache_unittest.cc
index 34fd67cdf..e192fec 100644
--- a/components/services/app_service/public/cpp/shortcut/shortcut_registry_cache_unittest.cc
+++ b/components/services/app_service/public/cpp/shortcut/shortcut_registry_cache_unittest.cc
@@ -44,4 +44,35 @@
EXPECT_EQ(cache().GetAllShortcuts().size(), 1u);
}
+TEST_F(ShortcutRegistryCacheTest, UpdateShortcut) {
+ std::string host_app_id = "host_app_id";
+ std::string local_id = "local_id";
+ auto shortcut = std::make_unique<Shortcut>(host_app_id, local_id);
+ ShortcutId shortcut_id = shortcut->shortcut_id;
+ shortcut->name = "name";
+ shortcut->shortcut_source = ShortcutSource::kUser;
+
+ EXPECT_FALSE(cache().HasShortcut(shortcut_id));
+ cache().UpdateShortcut(std::move(shortcut));
+ ASSERT_TRUE(cache().HasShortcut(shortcut_id));
+
+ EXPECT_EQ(cache().GetAllShortcuts().size(), 1u);
+
+ auto shortcut_delta = std::make_unique<Shortcut>(host_app_id, local_id);
+ shortcut_delta->name = "new name";
+ shortcut_delta->shortcut_source = ShortcutSource::kDeveloper;
+
+ cache().UpdateShortcut(std::move(shortcut_delta));
+
+ EXPECT_EQ(cache().GetAllShortcuts().size(), 1u);
+
+ ShortcutView stored_shortcut = cache().GetShortcut(shortcut_id);
+
+ ASSERT_TRUE(stored_shortcut);
+ EXPECT_EQ(stored_shortcut->shortcut_id, shortcut_id);
+ EXPECT_EQ(stored_shortcut->name, "new name");
+ EXPECT_EQ(stored_shortcut->shortcut_source, ShortcutSource::kDeveloper);
+ EXPECT_EQ(stored_shortcut->host_app_id, host_app_id);
+ EXPECT_EQ(stored_shortcut->local_id, local_id);
+}
} // namespace apps
diff --git a/components/services/app_service/public/cpp/shortcut/shortcut_update.cc b/components/services/app_service/public/cpp/shortcut/shortcut_update.cc
new file mode 100644
index 0000000..7f9a6ad
--- /dev/null
+++ b/components/services/app_service/public/cpp/shortcut/shortcut_update.cc
@@ -0,0 +1,86 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/services/app_service/public/cpp/shortcut/shortcut_update.h"
+
+#include "base/check.h"
+#include "base/check_op.h"
+#include "base/logging.h"
+#include "base/strings/string_util.h"
+#include "components/services/app_service/public/cpp/macros.h"
+#include "components/services/app_service/public/cpp/shortcut/shortcut.h"
+
+namespace apps {
+
+ShortcutUpdate::ShortcutUpdate(const Shortcut* state, const Shortcut* delta)
+ : state_(state), delta_(delta) {
+ CHECK(state_ || delta_);
+ if (state_ && delta_) {
+ CHECK_EQ(state_->shortcut_id, delta->shortcut_id);
+ CHECK_EQ(state_->host_app_id, delta->host_app_id);
+ CHECK_EQ(state_->local_id, delta->local_id);
+ }
+}
+
+void ShortcutUpdate::Merge(Shortcut* state, const Shortcut* delta) {
+ CHECK(state);
+ if (!delta) {
+ return;
+ }
+
+ if (delta->shortcut_id != state->shortcut_id) {
+ LOG(ERROR) << "inconsistent shortcut_id: " << delta->shortcut_id << " vs "
+ << state->shortcut_id;
+ return;
+ }
+
+ if (delta->host_app_id != state->host_app_id) {
+ LOG(ERROR) << "inconsistent host_app_id: " << delta->host_app_id << " vs "
+ << state->host_app_id;
+ return;
+ }
+
+ if (delta->local_id != state->local_id) {
+ LOG(ERROR) << "inconsistent local_id: " << delta->local_id << " vs "
+ << state->local_id;
+ return;
+ }
+
+ SET_OPTIONAL_VALUE(name);
+ SET_ENUM_VALUE(shortcut_source, ShortcutSource::kUnknown);
+
+ // When adding new fields to the Shortcut struct, this function should also
+ // be updated.
+}
+
+const ShortcutId& ShortcutUpdate::ShortcutId() const {
+ return delta_ ? delta_->shortcut_id : state_->shortcut_id;
+}
+
+const std::string& ShortcutUpdate::HostAppId() const {
+ return delta_ ? delta_->host_app_id : state_->host_app_id;
+}
+
+const std::string& ShortcutUpdate::LocalId() const {
+ return delta_ ? delta_->local_id : state_->local_id;
+}
+
+const std::string& ShortcutUpdate::Name() const {
+ GET_VALUE_WITH_FALLBACK(name, base::EmptyString())
+}
+
+bool ShortcutUpdate::NameChanged() const {
+ RETURN_OPTIONAL_VALUE_CHANGED(name);
+}
+
+ShortcutSource ShortcutUpdate::ShortcutSource() const {
+ GET_VALUE_WITH_DEFAULT_VALUE(shortcut_source, ShortcutSource::kUnknown);
+}
+
+bool ShortcutUpdate::ShortcutSourceChanged() const {
+ IS_VALUE_CHANGED_WITH_DEFAULT_VALUE(shortcut_source,
+ ShortcutSource::kUnknown);
+}
+
+} // namespace apps
diff --git a/components/services/app_service/public/cpp/shortcut/shortcut_update.h b/components/services/app_service/public/cpp/shortcut/shortcut_update.h
new file mode 100644
index 0000000..4007607
--- /dev/null
+++ b/components/services/app_service/public/cpp/shortcut/shortcut_update.h
@@ -0,0 +1,51 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SERVICES_APP_SERVICE_PUBLIC_CPP_SHORTCUT_SHORTCUT_UPDATE_H_
+#define COMPONENTS_SERVICES_APP_SERVICE_PUBLIC_CPP_SHORTCUT_SHORTCUT_UPDATE_H_
+
+#include <memory>
+#include <string>
+
+#include "base/allocator/partition_allocator/pointers/raw_ptr.h"
+#include "base/component_export.h"
+#include "components/services/app_service/public/cpp/macros.h"
+#include "components/services/app_service/public/cpp/shortcut/shortcut.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace apps {
+
+class COMPONENT_EXPORT(SHORTCUT) ShortcutUpdate {
+ public:
+ static void Merge(Shortcut* state, const Shortcut* delta);
+
+ // At most one of |state| or |delta| may be nullptr.
+ ShortcutUpdate(const Shortcut* state, const Shortcut* delta);
+
+ ShortcutUpdate(const ShortcutUpdate&) = delete;
+ ShortcutUpdate& operator=(const ShortcutUpdate&) = delete;
+
+ const ShortcutId& ShortcutId() const;
+ const std::string& HostAppId() const;
+ const std::string& LocalId() const;
+
+ const std::string& Name() const;
+ bool NameChanged() const;
+
+ ShortcutSource ShortcutSource() const;
+ bool ShortcutSourceChanged() const;
+
+ private:
+ raw_ptr<const Shortcut> state_ = nullptr;
+ raw_ptr<const Shortcut> delta_ = nullptr;
+};
+
+// For logging and debug purposes.
+COMPONENT_EXPORT(SHORTCUT)
+std::ostream& operator<<(std::ostream& out,
+ const ShortcutUpdate& shortcut_update);
+
+} // namespace apps
+
+#endif // COMPONENTS_SERVICES_APP_SERVICE_PUBLIC_CPP_SHORTCUT_SHORTCUT_UPDATE_H_
diff --git a/components/services/app_service/public/cpp/shortcut/shortcut_update_unittest.cc b/components/services/app_service/public/cpp/shortcut/shortcut_update_unittest.cc
new file mode 100644
index 0000000..64e853f1
--- /dev/null
+++ b/components/services/app_service/public/cpp/shortcut/shortcut_update_unittest.cc
@@ -0,0 +1,94 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/services/app_service/public/cpp/shortcut/shortcut_update.h"
+
+#include "components/services/app_service/public/cpp/shortcut/shortcut.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace apps {
+
+class ShortcutUpdateTest : public testing::Test {
+ protected:
+ std::string host_app_id_ = "host_app_id";
+ std::string local_id_ = "local_id";
+ ShortcutId shortcut_id_ = GenerateShortcutId(host_app_id_, local_id_);
+};
+
+TEST_F(ShortcutUpdateTest, StateIsNonNull) {
+ Shortcut shortcut = Shortcut(host_app_id_, local_id_);
+ shortcut.name = "Name";
+ shortcut.shortcut_source = ShortcutSource::kDeveloper;
+ ShortcutUpdate u(&shortcut, nullptr);
+
+ EXPECT_EQ(u.HostAppId(), host_app_id_);
+ EXPECT_EQ(u.LocalId(), local_id_);
+ EXPECT_EQ(u.ShortcutId(), shortcut_id_);
+
+ EXPECT_EQ(u.Name(), "Name");
+ EXPECT_EQ(u.NameChanged(), false);
+
+ EXPECT_EQ(u.ShortcutSource(), ShortcutSource::kDeveloper);
+ EXPECT_EQ(u.ShortcutSourceChanged(), false);
+}
+
+TEST_F(ShortcutUpdateTest, DeltaIsNonNull) {
+ Shortcut shortcut = Shortcut(host_app_id_, local_id_);
+ shortcut.name = "Name";
+ shortcut.shortcut_source = ShortcutSource::kDeveloper;
+ ShortcutUpdate u(nullptr, &shortcut);
+
+ EXPECT_EQ(u.HostAppId(), host_app_id_);
+ EXPECT_EQ(u.LocalId(), local_id_);
+ EXPECT_EQ(u.ShortcutId(), shortcut_id_);
+
+ EXPECT_EQ(u.Name(), "Name");
+ EXPECT_EQ(u.NameChanged(), true);
+
+ EXPECT_EQ(u.ShortcutSource(), ShortcutSource::kDeveloper);
+ EXPECT_EQ(u.ShortcutSourceChanged(), true);
+}
+
+TEST_F(ShortcutUpdateTest, StateAndDeltaAreNonNull) {
+ Shortcut shortcut_state = Shortcut(host_app_id_, local_id_);
+ shortcut_state.name = "Name";
+ shortcut_state.shortcut_source = ShortcutSource::kDeveloper;
+
+ Shortcut shortcut_delta = Shortcut(host_app_id_, local_id_);
+ shortcut_delta.name = "New name";
+ shortcut_delta.shortcut_source = ShortcutSource::kUser;
+
+ ShortcutUpdate u(&shortcut_state, &shortcut_delta);
+
+ EXPECT_EQ(u.HostAppId(), host_app_id_);
+ EXPECT_EQ(u.LocalId(), local_id_);
+ EXPECT_EQ(u.ShortcutId(), shortcut_id_);
+
+ EXPECT_EQ(u.Name(), "New name");
+ EXPECT_EQ(u.NameChanged(), true);
+
+ EXPECT_EQ(u.ShortcutSource(), ShortcutSource::kUser);
+ EXPECT_EQ(u.ShortcutSourceChanged(), true);
+}
+
+TEST_F(ShortcutUpdateTest, Merge) {
+ Shortcut shortcut_state = Shortcut(host_app_id_, local_id_);
+ shortcut_state.name = "Name";
+ shortcut_state.shortcut_source = ShortcutSource::kDeveloper;
+
+ Shortcut shortcut_delta = Shortcut(host_app_id_, local_id_);
+ shortcut_delta.name = "New name";
+ shortcut_delta.shortcut_source = ShortcutSource::kUser;
+
+ ShortcutUpdate::Merge(&shortcut_state, &shortcut_delta);
+
+ EXPECT_EQ(shortcut_state.shortcut_id,
+ GenerateShortcutId(host_app_id_, local_id_));
+ EXPECT_EQ(shortcut_state.name, "New name");
+ EXPECT_EQ(shortcut_state.shortcut_source, ShortcutSource::kUser);
+ EXPECT_EQ(shortcut_state.host_app_id, host_app_id_);
+ EXPECT_EQ(shortcut_state.local_id, local_id_);
+}
+
+} // namespace apps