summaryrefslogtreecommitdiff
path: root/chromium/components/services
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/components/services')
-rw-r--r--chromium/components/services/app_service/BUILD.gn1
-rw-r--r--chromium/components/services/app_service/app_service_mojom_impl.cc11
-rw-r--r--chromium/components/services/app_service/app_service_mojom_impl.h4
-rw-r--r--chromium/components/services/app_service/public/cpp/BUILD.gn27
-rw-r--r--chromium/components/services/app_service/public/cpp/app_registry_cache.cc95
-rw-r--r--chromium/components/services/app_service/public/cpp/app_registry_cache.h31
-rw-r--r--chromium/components/services/app_service/public/cpp/app_registry_cache_mojom_unittest.cc63
-rw-r--r--chromium/components/services/app_service/public/cpp/app_registry_cache_unittest.cc681
-rw-r--r--chromium/components/services/app_service/public/cpp/app_types.cc326
-rw-r--r--chromium/components/services/app_service/public/cpp/app_types.h163
-rw-r--r--chromium/components/services/app_service/public/cpp/app_update.cc265
-rw-r--r--chromium/components/services/app_service/public/cpp/app_update.h28
-rw-r--r--chromium/components/services/app_service/public/cpp/app_update_mojom_unittest.cc203
-rw-r--r--chromium/components/services/app_service/public/cpp/app_update_unittest.cc722
-rw-r--r--chromium/components/services/app_service/public/cpp/browser_app_instance_update.h6
-rw-r--r--chromium/components/services/app_service/public/cpp/browser_window_instance_update.h4
-rw-r--r--chromium/components/services/app_service/public/cpp/features.cc16
-rw-r--r--chromium/components/services/app_service/public/cpp/features.h19
-rw-r--r--chromium/components/services/app_service/public/cpp/icon_loader.h6
-rw-r--r--chromium/components/services/app_service/public/cpp/icon_types.h3
-rw-r--r--chromium/components/services/app_service/public/cpp/instance_update.cc7
-rw-r--r--chromium/components/services/app_service/public/cpp/intent_filter.cc318
-rw-r--r--chromium/components/services/app_service/public/cpp/intent_filter.h167
-rw-r--r--chromium/components/services/app_service/public/cpp/intent_filter_util.cc11
-rw-r--r--chromium/components/services/app_service/public/cpp/intent_filter_util.h7
-rw-r--r--chromium/components/services/app_service/public/cpp/intent_filter_util_unittest.cc106
-rw-r--r--chromium/components/services/app_service/public/cpp/intent_util.cc32
-rw-r--r--chromium/components/services/app_service/public/cpp/macros.h33
-rw-r--r--chromium/components/services/app_service/public/cpp/permission.cc213
-rw-r--r--chromium/components/services/app_service/public/cpp/permission.h117
-rw-r--r--chromium/components/services/app_service/public/cpp/preferred_apps_converter.cc6
-rw-r--r--chromium/components/services/app_service/public/cpp/preferred_apps_converter_unittest.cc11
-rw-r--r--chromium/components/services/app_service/public/cpp/publisher_base.cc6
-rw-r--r--chromium/components/services/app_service/public/cpp/publisher_base.h3
-rw-r--r--chromium/components/services/app_service/public/cpp/run_on_os_login_types.cc62
-rw-r--r--chromium/components/services/app_service/public/cpp/run_on_os_login_types.h64
-rw-r--r--chromium/components/services/app_service/public/mojom/BUILD.gn3
-rw-r--r--chromium/components/services/app_service/public/mojom/app_service.mojom15
-rw-r--r--chromium/components/services/app_service/public/mojom/types.mojom27
-rw-r--r--chromium/components/services/filesystem/directory_impl.cc2
-rw-r--r--chromium/components/services/filesystem/file_impl.cc6
-rw-r--r--chromium/components/services/filesystem/file_impl.h4
-rw-r--r--chromium/components/services/filesystem/lock_table.cc8
-rw-r--r--chromium/components/services/filesystem/util.cc6
-rw-r--r--chromium/components/services/font/BUILD.gn1
-rw-r--r--chromium/components/services/font/font_service_app.cc93
-rw-r--r--chromium/components/services/font/font_service_app.h33
-rw-r--r--chromium/components/services/heap_profiling/heap_profiling_service.cc1
-rw-r--r--chromium/components/services/heap_profiling/json_exporter_unittest.cc54
-rw-r--r--chromium/components/services/heap_profiling/public/cpp/heap_profiling_trace_source.cc1
-rw-r--r--chromium/components/services/heap_profiling/public/cpp/profiling_client.cc14
-rw-r--r--chromium/components/services/paint_preview_compositor/paint_preview_compositor_collection_impl.cc16
-rw-r--r--chromium/components/services/paint_preview_compositor/paint_preview_compositor_collection_impl.h4
-rw-r--r--chromium/components/services/patch/content/patch_service.cc1
-rw-r--r--chromium/components/services/print_compositor/print_compositor_impl.cc14
-rw-r--r--chromium/components/services/quarantine/BUILD.gn2
-rw-r--r--chromium/components/services/quarantine/OWNERS1
-rw-r--r--chromium/components/services/quarantine/README.md6
-rw-r--r--chromium/components/services/quarantine/public/cpp/BUILD.gn16
-rw-r--r--chromium/components/services/quarantine/public/cpp/quarantine_features_win.cc15
-rw-r--r--chromium/components/services/quarantine/public/cpp/quarantine_features_win.h16
-rw-r--r--chromium/components/services/quarantine/quarantine.cc2
-rw-r--r--chromium/components/services/quarantine/quarantine.h2
-rw-r--r--chromium/components/services/quarantine/quarantine_impl.cc7
-rw-r--r--chromium/components/services/quarantine/quarantine_impl.h8
-rw-r--r--chromium/components/services/quarantine/quarantine_unittest.cc6
-rw-r--r--chromium/components/services/quarantine/quarantine_win.cc1
-rw-r--r--chromium/components/services/quarantine/quarantine_win_unittest.cc1
-rw-r--r--chromium/components/services/quarantine/test_support.cc2
-rw-r--r--chromium/components/services/storage/BUILD.gn31
-rw-r--r--chromium/components/services/storage/DEPS2
-rw-r--r--chromium/components/services/storage/dom_storage/local_storage_impl.cc4
-rw-r--r--chromium/components/services/storage/dom_storage/local_storage_impl_unittest.cc5
-rw-r--r--chromium/components/services/storage/dom_storage/session_storage_data_map_unittest.cc4
-rw-r--r--chromium/components/services/storage/dom_storage/session_storage_impl.cc2
-rw-r--r--chromium/components/services/storage/dom_storage/storage_area_impl_unittest.cc6
-rw-r--r--chromium/components/services/storage/dom_storage/testing_legacy_session_storage_database.cc2
-rw-r--r--chromium/components/services/storage/indexed_db/leveldb/leveldb_factory.cc7
-rw-r--r--chromium/components/services/storage/indexed_db/scopes/leveldb_scope.h20
-rw-r--r--chromium/components/services/storage/indexed_db/scopes/leveldb_scopes.cc10
-rw-r--r--chromium/components/services/storage/indexed_db/scopes/leveldb_scopes_tasks.h31
-rw-r--r--chromium/components/services/storage/indexed_db/scopes/leveldb_scopes_test_utils.cc8
-rw-r--r--chromium/components/services/storage/indexed_db/scopes/varint_coding.h2
-rw-r--r--chromium/components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_database.cc2
-rw-r--r--chromium/components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_iterator.cc8
-rw-r--r--chromium/components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_iterator.h8
-rw-r--r--chromium/components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_transaction.h25
-rw-r--r--chromium/components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_transaction_unittest.cc1
-rw-r--r--chromium/components/services/storage/partition_impl.cc2
-rw-r--r--chromium/components/services/storage/public/cpp/BUILD.gn1
-rw-r--r--chromium/components/services/storage/public/cpp/constants.cc4
-rw-r--r--chromium/components/services/storage/public/cpp/constants.h3
-rw-r--r--chromium/components/services/storage/public/cpp/filesystem/filesystem_impl.cc17
-rw-r--r--chromium/components/services/storage/public/cpp/filesystem/filesystem_proxy.cc13
-rw-r--r--chromium/components/services/storage/public/cpp/quota_client_callback_wrapper.cc35
-rw-r--r--chromium/components/services/storage/public/cpp/quota_client_callback_wrapper.h19
-rw-r--r--chromium/components/services/storage/public/cpp/storage_key_quota_client.h29
-rw-r--r--chromium/components/services/storage/public/mojom/quota_client.mojom30
-rw-r--r--chromium/components/services/storage/service_worker/service_worker_database.cc51
-rw-r--r--chromium/components/services/storage/service_worker/service_worker_storage.cc7
-rw-r--r--chromium/components/services/storage/service_worker/service_worker_storage_control_impl.cc5
-rw-r--r--chromium/components/services/storage/service_worker/service_worker_storage_control_impl_unittest.cc106
-rw-r--r--chromium/components/services/storage/service_worker/service_worker_storage_unittest.cc2
-rw-r--r--chromium/components/services/storage/shared_storage/DIR_METADATA12
-rw-r--r--chromium/components/services/storage/shared_storage/OWNERS6
-rw-r--r--chromium/components/services/storage/shared_storage/async_shared_storage_database.cc196
-rw-r--r--chromium/components/services/storage/shared_storage/async_shared_storage_database.h237
-rw-r--r--chromium/components/services/storage/shared_storage/async_shared_storage_database_unittest.cc1648
-rw-r--r--chromium/components/services/storage/shared_storage/shared_storage_database.cc900
-rw-r--r--chromium/components/services/storage/shared_storage/shared_storage_database.h388
-rw-r--r--chromium/components/services/storage/shared_storage/shared_storage_database_unittest.cc835
-rw-r--r--chromium/components/services/storage/shared_storage/shared_storage_options.cc80
-rw-r--r--chromium/components/services/storage/shared_storage/shared_storage_options.h104
-rw-r--r--chromium/components/services/storage/shared_storage/shared_storage_test_utils.cc369
-rw-r--r--chromium/components/services/storage/shared_storage/shared_storage_test_utils.h210
-rw-r--r--chromium/components/services/storage/storage_service_impl.cc8
-rw-r--r--chromium/components/services/storage/storage_service_impl.h6
-rw-r--r--chromium/components/services/unzip/BUILD.gn16
-rw-r--r--chromium/components/services/unzip/DEPS1
-rw-r--r--chromium/components/services/unzip/OWNERS3
-rw-r--r--chromium/components/services/unzip/public/cpp/unzip.cc133
-rw-r--r--chromium/components/services/unzip/public/cpp/unzip.h6
-rw-r--r--chromium/components/services/unzip/public/cpp/unzip_unittest.cc145
-rw-r--r--chromium/components/services/unzip/public/mojom/unzipper.mojom19
-rw-r--r--chromium/components/services/unzip/unzipper_impl.cc131
-rw-r--r--chromium/components/services/unzip/unzipper_impl.h8
126 files changed, 9417 insertions, 771 deletions
diff --git a/chromium/components/services/app_service/BUILD.gn b/chromium/components/services/app_service/BUILD.gn
index a860306ee5b..d6e390233a2 100644
--- a/chromium/components/services/app_service/BUILD.gn
+++ b/chromium/components/services/app_service/BUILD.gn
@@ -35,6 +35,7 @@ source_set("unit_tests") {
"//components/services/app_service/public/cpp:intents",
"//components/services/app_service/public/cpp:preferred_apps",
"//components/services/app_service/public/cpp:publisher",
+ "//components/services/app_service/public/cpp:run_on_os_login",
"//components/services/app_service/public/cpp:test_support",
"//content/test:test_support",
"//testing/gtest",
diff --git a/chromium/components/services/app_service/app_service_mojom_impl.cc b/chromium/components/services/app_service/app_service_mojom_impl.cc
index dab153dcd2f..23623e84c2b 100644
--- a/chromium/components/services/app_service/app_service_mojom_impl.cc
+++ b/chromium/components/services/app_service/app_service_mojom_impl.cc
@@ -539,6 +539,17 @@ void AppServiceMojomImpl::SetWindowMode(apps::mojom::AppType app_type,
iter->second->SetWindowMode(app_id, window_mode);
}
+void AppServiceMojomImpl::SetRunOnOsLoginMode(
+ apps::mojom::AppType app_type,
+ const std::string& app_id,
+ apps::mojom::RunOnOsLoginMode run_on_os_login_mode) {
+ auto iter = publishers_.find(app_type);
+ if (iter == publishers_.end()) {
+ return;
+ }
+ iter->second->SetRunOnOsLoginMode(app_id, run_on_os_login_mode);
+}
+
PreferredAppsList& AppServiceMojomImpl::GetPreferredAppsForTesting() {
return preferred_apps_;
}
diff --git a/chromium/components/services/app_service/app_service_mojom_impl.h b/chromium/components/services/app_service/app_service_mojom_impl.h
index 32a402deb8e..c58a2ea5d4d 100644
--- a/chromium/components/services/app_service/app_service_mojom_impl.h
+++ b/chromium/components/services/app_service/app_service_mojom_impl.h
@@ -121,6 +121,10 @@ class AppServiceMojomImpl : public apps::mojom::AppService {
void SetWindowMode(apps::mojom::AppType app_type,
const std::string& app_id,
apps::mojom::WindowMode window_mode) override;
+ void SetRunOnOsLoginMode(
+ apps::mojom::AppType app_type,
+ const std::string& app_id,
+ apps::mojom::RunOnOsLoginMode run_on_os_login_mode) override;
// Retern the preferred_apps_ for testing.
PreferredAppsList& GetPreferredAppsForTesting();
diff --git a/chromium/components/services/app_service/public/cpp/BUILD.gn b/chromium/components/services/app_service/public/cpp/BUILD.gn
index 504e7576c47..b461dd8b633 100644
--- a/chromium/components/services/app_service/public/cpp/BUILD.gn
+++ b/chromium/components/services/app_service/public/cpp/BUILD.gn
@@ -49,13 +49,20 @@ component("app_types") {
sources = [
"app_types.cc",
"app_types.h",
+ "intent_filter.cc",
+ "intent_filter.h",
+ "permission.cc",
+ "permission.h",
]
defines = [ "IS_APP_TYPES_IMPL" ]
public_deps = [ "//components/services/app_service/public/mojom" ]
- deps = [ ":icon_types" ]
+ deps = [
+ ":icon_types",
+ ":run_on_os_login",
+ ]
}
component("app_update") {
@@ -73,6 +80,9 @@ component("app_update") {
"app_update.h",
"capability_access_update.cc",
"capability_access_update.h",
+ "features.cc",
+ "features.h",
+ "macros.h",
]
defines = [ "IS_APP_UPDATE_IMPL" ]
@@ -86,6 +96,7 @@ component("app_update") {
":app_types",
":icon_types",
":intents",
+ ":run_on_os_login",
"//ui/gfx",
]
}
@@ -173,6 +184,18 @@ component("icon_types") {
deps = [ "//ui/gfx" ]
}
+component("run_on_os_login") {
+ output_name = "LOGIN_MODE"
+ sources = [
+ "run_on_os_login_types.cc",
+ "run_on_os_login_types.h",
+ ]
+
+ defines = [ "IS_LOGIN_MODE_IMPL" ]
+
+ public_deps = [ "//components/services/app_service/public/mojom" ]
+}
+
source_set("intents") {
sources = [
"intent_constants.cc",
@@ -184,6 +207,7 @@ source_set("intents") {
]
deps = [
+ ":app_types",
"//base",
"//components/services/app_service/public/mojom",
"//third_party/blink/public/common",
@@ -295,6 +319,7 @@ source_set("unit_tests") {
":intents",
":preferred_apps",
":publisher",
+ ":run_on_os_login",
":test_support",
":types",
"//content/test:test_support",
diff --git a/chromium/components/services/app_service/public/cpp/app_registry_cache.cc b/chromium/components/services/app_service/public/cpp/app_registry_cache.cc
index c0effb42c76..ef505a87806 100644
--- a/chromium/components/services/app_service/public/cpp/app_registry_cache.cc
+++ b/chromium/components/services/app_service/public/cpp/app_registry_cache.cc
@@ -5,17 +5,12 @@
#include "components/services/app_service/public/cpp/app_registry_cache.h"
#include "base/containers/contains.h"
+#include "components/services/app_service/public/cpp/features.h"
#include <utility>
namespace apps {
-namespace {
-
-constexpr uint32_t kUAFSentinelInitial = 0xabcd1234;
-
-} // namespace
-
AppRegistryCache::Observer::Observer(AppRegistryCache* cache) {
Observe(cache);
}
@@ -42,15 +37,13 @@ void AppRegistryCache::Observer::Observe(AppRegistryCache* cache) {
}
}
-AppRegistryCache::AppRegistryCache()
- : account_id_(EmptyAccountId()), uaf_sentinel_(kUAFSentinelInitial) {}
+AppRegistryCache::AppRegistryCache() : account_id_(EmptyAccountId()) {}
AppRegistryCache::~AppRegistryCache() {
for (auto& obs : observers_) {
obs.OnAppRegistryCacheWillBeDestroyed(this);
}
DCHECK(observers_.empty());
- uaf_sentinel_ = 0;
}
void AppRegistryCache::AddObserver(Observer* observer) {
@@ -69,8 +62,8 @@ void AppRegistryCache::OnApps(std::vector<apps::mojom::AppPtr> deltas,
if (should_notify_initialized) {
DCHECK_NE(apps::mojom::AppType::kUnknown, app_type);
- if (initialized_app_types_.find(app_type) == initialized_app_types_.end()) {
- in_progress_initialized_app_types_.insert(app_type);
+ if (!IsAppTypeInitialized(ConvertMojomAppTypToAppType(app_type))) {
+ in_progress_initialized_mojom_app_types_.insert(app_type);
}
}
@@ -90,15 +83,17 @@ void AppRegistryCache::OnApps(std::vector<apps::mojom::AppPtr> deltas,
OnAppTypeInitialized();
}
-void AppRegistryCache::OnApps(std::vector<std::unique_ptr<App>> deltas,
+void AppRegistryCache::OnApps(std::vector<AppPtr> deltas,
apps::AppType app_type,
bool should_notify_initialized) {
DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
- // TODO(crbug.com/1253250): Modify in_progress_initialized_app_types_ based on
- // the `App` struct when the `App` struct replaces the mojom `App` struct for
- // all publishers, to prevent OnAppTypeInitialized to be called twice for both
- // the mojom struct and non-mojom struct.
+ if (should_notify_initialized) {
+ DCHECK_NE(apps::AppType::kUnknown, app_type);
+ if (!IsAppTypeInitialized(app_type)) {
+ in_progress_initialized_app_types_.insert(app_type);
+ }
+ }
if (!deltas_in_progress_.empty()) {
std::move(deltas.begin(), deltas.end(),
@@ -108,15 +103,15 @@ void AppRegistryCache::OnApps(std::vector<std::unique_ptr<App>> deltas,
DoOnApps(std::move(deltas));
while (!deltas_pending_.empty()) {
- std::vector<std::unique_ptr<App>> pending;
+ std::vector<AppPtr> pending;
pending.swap(deltas_pending_);
DoOnApps(std::move(pending));
}
- // TODO(crbug.com/1253250): Modify and add OnAppTypeInitialized for the `App`
- // struct when the `App` struct replaces the mojom `App` struct for all
- // publishers, to prevent OnAppTypeInitialized to be called twice for both the
- // mojom struct and non-mojom struct.
+ if (base::FeatureList::IsEnabled(
+ kAppServiceOnAppTypeInitializedWithoutMojom)) {
+ OnAppTypeInitialized();
+ }
}
void AppRegistryCache::DoOnApps(std::vector<apps::mojom::AppPtr> deltas) {
@@ -179,7 +174,7 @@ void AppRegistryCache::DoOnApps(std::vector<apps::mojom::AppPtr> deltas) {
mojom_deltas_in_progress_.clear();
}
-void AppRegistryCache::DoOnApps(std::vector<std::unique_ptr<App>> deltas) {
+void AppRegistryCache::DoOnApps(std::vector<AppPtr> deltas) {
// Merge any deltas elements that have the same app_id. If an observer's
// OnAppUpdate calls back into this AppRegistryCache then we can therefore
// present a single delta for any given app_id.
@@ -242,30 +237,68 @@ void AppRegistryCache::SetAccountId(const AccountId& account_id) {
account_id_ = account_id;
}
-const std::set<apps::mojom::AppType>& AppRegistryCache::GetInitializedAppTypes()
- const {
+const std::set<AppType>& AppRegistryCache::InitializedAppTypes() const {
return initialized_app_types_;
}
-bool AppRegistryCache::IsAppTypeInitialized(
- apps::mojom::AppType app_type) const {
+bool AppRegistryCache::IsAppTypeInitialized(apps::AppType app_type) const {
return base::Contains(initialized_app_types_, app_type);
}
+void AppRegistryCache::OnMojomAppTypeInitialized() {
+ if (in_progress_initialized_mojom_app_types_.empty()) {
+ return;
+ }
+
+ auto in_progress_initialized_app_types =
+ in_progress_initialized_mojom_app_types_;
+ in_progress_initialized_mojom_app_types_.clear();
+
+ for (auto app_type : in_progress_initialized_app_types) {
+ for (auto& obs : observers_) {
+ obs.OnAppTypeInitialized(ConvertMojomAppTypToAppType(app_type));
+ }
+ initialized_app_types_.insert(ConvertMojomAppTypToAppType(app_type));
+ }
+}
+
void AppRegistryCache::OnAppTypeInitialized() {
- CHECK_EQ(kUAFSentinelInitial, uaf_sentinel_);
- if (in_progress_initialized_app_types_.empty()) {
+ if (!base::FeatureList::IsEnabled(
+ kAppServiceOnAppTypeInitializedWithoutMojom)) {
+ OnMojomAppTypeInitialized();
+ return;
+ }
+
+ // Check both the non mojom and mojom initialized status. Only when they are
+ // not initialized, call OnAppTypeInitialized to notify observers, because
+ // observers might use the non mojom or mojom App struct.
+ //
+ // TODO(crbug.com/1253250): Remove the mojom initialized checking when all
+ // observers use the non mojom App struct only.
+ if (in_progress_initialized_mojom_app_types_.empty() ||
+ in_progress_initialized_app_types_.empty()) {
return;
}
- auto in_progress_initialized_app_types = in_progress_initialized_app_types_;
- in_progress_initialized_app_types_.clear();
+ // In observer's OnAppTypeInitialized callback, `OnApp` might be call to
+ // update the app, then this OnAppTypeInitialized might be called again. So we
+ // need to check the initialized `app_type` first, and remove it from
+ // `in_progress_initialized_app_types_` to prevent the dead loop.
+ std::set<AppType> in_progress_initialized_app_types;
+ for (auto app_type : in_progress_initialized_app_types_) {
+ if (base::Contains(in_progress_initialized_mojom_app_types_,
+ ConvertAppTypeToMojomAppType(app_type))) {
+ in_progress_initialized_app_types.insert(app_type);
+ }
+ }
for (auto app_type : in_progress_initialized_app_types) {
+ auto mojom_app_type = ConvertAppTypeToMojomAppType(app_type);
+ in_progress_initialized_app_types_.erase(app_type);
+ in_progress_initialized_mojom_app_types_.erase(mojom_app_type);
for (auto& obs : observers_) {
obs.OnAppTypeInitialized(app_type);
}
- CHECK_EQ(kUAFSentinelInitial, uaf_sentinel_);
initialized_app_types_.insert(app_type);
}
}
diff --git a/chromium/components/services/app_service/public/cpp/app_registry_cache.h b/chromium/components/services/app_service/public/cpp/app_registry_cache.h
index 799a93a8af1..14f52557977 100644
--- a/chromium/components/services/app_service/public/cpp/app_registry_cache.h
+++ b/chromium/components/services/app_service/public/cpp/app_registry_cache.h
@@ -53,9 +53,9 @@ class COMPONENT_EXPORT(APP_UPDATE) AppRegistryCache {
// Called when the publisher for |app_type| has finished initiating apps.
// Note that this will not be called for app types initialized prior to this
// observer being registered. Observers should call
- // AppRegistryCache::GetInitializedAppTypes() at the time of starting
+ // AppRegistryCache::InitializedAppTypes() at the time of starting
// observation to get a set of the app types which have been initialized.
- virtual void OnAppTypeInitialized(apps::mojom::AppType app_type) {}
+ virtual void OnAppTypeInitialized(apps::AppType app_type) {}
// Called when the AppRegistryCache object (the thing that this observer
// observes) will be destroyed. In response, the observer, |this|, should
@@ -115,7 +115,7 @@ class COMPONENT_EXPORT(APP_UPDATE) AppRegistryCache {
void OnApps(std::vector<apps::mojom::AppPtr> deltas,
apps::mojom::AppType app_type,
bool should_notify_initialized);
- void OnApps(std::vector<std::unique_ptr<App>> deltas,
+ void OnApps(std::vector<AppPtr> deltas,
apps::AppType app_type,
bool should_notify_initialized);
@@ -240,26 +240,26 @@ class COMPONENT_EXPORT(APP_UPDATE) AppRegistryCache {
}
// Returns the set of app types that have so far been initialized.
- const std::set<apps::mojom::AppType>& GetInitializedAppTypes() const;
+ const std::set<AppType>& InitializedAppTypes() const;
- bool IsAppTypeInitialized(apps::mojom::AppType app_type) const;
+ bool IsAppTypeInitialized(AppType app_type) const;
private:
friend class AppRegistryCacheTest;
friend class PublisherTest;
void DoOnApps(std::vector<apps::mojom::AppPtr> deltas);
- void DoOnApps(std::vector<std::unique_ptr<App>> deltas);
+ void DoOnApps(std::vector<AppPtr> deltas);
- // NOINLINE should force this function to appear on the stack in crash dumps.
- // https://crbug.com/1237267.
- void NOINLINE OnAppTypeInitialized();
+ void OnMojomAppTypeInitialized();
+
+ void OnAppTypeInitialized();
base::ObserverList<Observer> observers_;
// Maps from app_id to the latest state: the "sum" of all previous deltas.
std::map<std::string, apps::mojom::AppPtr> mojom_states_;
- std::map<std::string, std::unique_ptr<App>> states_;
+ std::map<std::string, AppPtr> states_;
// Track the deltas being processed or are about to be processed by OnApps.
// They are separate to manage the "notification and merging might be delayed
@@ -280,23 +280,20 @@ class COMPONENT_EXPORT(APP_UPDATE) AppRegistryCache {
std::map<std::string, apps::mojom::App*> mojom_deltas_in_progress_;
std::vector<apps::mojom::AppPtr> mojom_deltas_pending_;
std::map<std::string, App*> deltas_in_progress_;
- std::vector<std::unique_ptr<App>> deltas_pending_;
+ std::vector<AppPtr> deltas_pending_;
// Saves app types which will finish initialization, and OnAppTypeInitialized
// will be called to notify observers.
- std::set<apps::mojom::AppType> in_progress_initialized_app_types_;
+ std::set<apps::mojom::AppType> in_progress_initialized_mojom_app_types_;
+ std::set<AppType> in_progress_initialized_app_types_;
// Saves app types which have finished initialization, and
// OnAppTypeInitialized has be called to notify observers.
- std::set<apps::mojom::AppType> initialized_app_types_;
+ std::set<AppType> initialized_app_types_;
AccountId account_id_;
SEQUENCE_CHECKER(my_sequence_checker_);
-
- // A sentinel value checking for a UAF in https://crbug.com/1237267. Should be
- // removed after https://crbug.com/1237267 is fixed.
- uint32_t uaf_sentinel_;
};
} // namespace apps
diff --git a/chromium/components/services/app_service/public/cpp/app_registry_cache_mojom_unittest.cc b/chromium/components/services/app_service/public/cpp/app_registry_cache_mojom_unittest.cc
index 1e52d6ad846..04612f78995 100644
--- a/chromium/components/services/app_service/public/cpp/app_registry_cache_mojom_unittest.cc
+++ b/chromium/components/services/app_service/public/cpp/app_registry_cache_mojom_unittest.cc
@@ -5,7 +5,10 @@
#include <map>
#include "base/memory/raw_ptr.h"
+#include "base/test/scoped_feature_list.h"
#include "components/services/app_service/public/cpp/app_registry_cache.h"
+#include "components/services/app_service/public/cpp/app_types.h"
+#include "components/services/app_service/public/cpp/features.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -30,6 +33,11 @@ MATCHER_P(HasAppId, app_id, "Has the correct app id") {
class AppRegistryCacheMojomTest : public testing::Test,
public apps::AppRegistryCache::Observer {
protected:
+ AppRegistryCacheMojomTest() {
+ scoped_feature_list_.InitAndDisableFeature(
+ apps::kAppServiceOnAppTypeInitializedWithoutMojom);
+ }
+
static apps::mojom::AppPtr MakeApp(
const char* app_id,
const char* name,
@@ -68,7 +76,7 @@ class AppRegistryCacheMojomTest : public testing::Test,
updated_names_.insert(update.Name());
}
- void OnAppTypeInitialized(apps::mojom::AppType app_type) override {
+ void OnAppTypeInitialized(apps::AppType app_type) override {
app_type_ = app_type;
}
@@ -78,17 +86,18 @@ class AppRegistryCacheMojomTest : public testing::Test,
NOTREACHED();
}
- void SetAppType(apps::mojom::AppType app_type) { app_type_ = app_type; }
+ void SetAppType(apps::AppType app_type) { app_type_ = app_type; }
const AccountId& account_id() const { return account_id_; }
- apps::mojom::AppType app_type() const { return app_type_; }
+ apps::AppType app_type() const { return app_type_; }
int num_freshly_installed_ = 0;
std::set<std::string> updated_ids_;
std::set<std::string> updated_names_;
AccountId account_id_ = AccountId::FromUserEmail("test@gmail.com");
- apps::mojom::AppType app_type_ = apps::mojom::AppType::kUnknown;
+ apps::AppType app_type_ = apps::AppType::kUnknown;
+ base::test::ScopedFeatureList scoped_feature_list_;
};
// Responds to a cache's OnAppUpdate to call back into the cache, checking that
@@ -126,7 +135,7 @@ class RecursiveObserver : public apps::AppRegistryCache::Observer {
int NumAppsSeenOnAppUpdate() { return num_apps_seen_on_app_update_; }
- apps::mojom::AppType app_type() const { return app_type_; }
+ apps::AppType app_type() const { return app_type_; }
protected:
// apps::AppRegistryCache::Observer overrides.
@@ -199,7 +208,7 @@ class RecursiveObserver : public apps::AppRegistryCache::Observer {
num_apps_seen_on_app_update_++;
}
- void OnAppTypeInitialized(apps::mojom::AppType app_type) override {
+ void OnAppTypeInitialized(apps::AppType app_type) override {
app_type_ = app_type;
}
@@ -222,7 +231,7 @@ class RecursiveObserver : public apps::AppRegistryCache::Observer {
int expected_num_apps_;
int num_apps_seen_on_app_update_;
AccountId account_id_ = AccountId::FromUserEmail("test@gmail.com");
- apps::mojom::AppType app_type_ = apps::mojom::AppType::kUnknown;
+ apps::AppType app_type_ = apps::AppType::kUnknown;
// Records previously seen app names, keyed by app_id's, so we can check
// that, for these tests, a given app's name is always increasing (in string
@@ -261,7 +270,7 @@ class InitializedObserver : public apps::AppRegistryCache::Observer {
updated_ids_.insert(update.AppId());
}
- void OnAppTypeInitialized(apps::mojom::AppType app_type) override {
+ void OnAppTypeInitialized(apps::AppType app_type) override {
app_type_ = app_type;
++count_;
app_count_ = updated_ids_.size();
@@ -272,9 +281,9 @@ class InitializedObserver : public apps::AppRegistryCache::Observer {
Observe(nullptr);
}
- void SetAppType(apps::mojom::AppType app_type) { app_type_ = app_type; }
+ void SetAppType(apps::AppType app_type) { app_type_ = app_type; }
- apps::mojom::AppType app_type() const { return app_type_; }
+ apps::AppType app_type() const { return app_type_; }
int count() const { return count_; }
@@ -282,7 +291,7 @@ class InitializedObserver : public apps::AppRegistryCache::Observer {
private:
std::set<std::string> updated_ids_;
- apps::mojom::AppType app_type_ = apps::mojom::AppType::kUnknown;
+ apps::AppType app_type_ = apps::AppType::kUnknown;
int count_ = 0;
int app_count_ = 0;
};
@@ -410,10 +419,10 @@ TEST_F(AppRegistryCacheMojomTest, Observer) {
EXPECT_NE(updated_ids_.end(), updated_ids_.find("a"));
EXPECT_NE(updated_ids_.end(), updated_ids_.find("c"));
EXPECT_NE(updated_ids_.end(), updated_ids_.find("e"));
- EXPECT_EQ(apps::mojom::AppType::kArc, app_type());
- EXPECT_TRUE(cache.IsAppTypeInitialized(apps::mojom::AppType::kArc));
+ EXPECT_EQ(apps::AppType::kArc, app_type());
+ EXPECT_TRUE(cache.IsAppTypeInitialized(apps::AppType::kArc));
- SetAppType(apps::mojom::AppType::kUnknown);
+ SetAppType(apps::AppType::kUnknown);
num_freshly_installed_ = 0;
updated_ids_.clear();
deltas.clear();
@@ -426,7 +435,7 @@ TEST_F(AppRegistryCacheMojomTest, Observer) {
EXPECT_EQ(2u, updated_ids_.size());
EXPECT_NE(updated_ids_.end(), updated_ids_.find("b"));
EXPECT_NE(updated_ids_.end(), updated_ids_.find("c"));
- EXPECT_EQ(apps::mojom::AppType::kUnknown, app_type());
+ EXPECT_EQ(apps::AppType::kUnknown, app_type());
cache.RemoveObserver(this);
@@ -439,8 +448,8 @@ TEST_F(AppRegistryCacheMojomTest, Observer) {
EXPECT_EQ(0, num_freshly_installed_);
EXPECT_EQ(0u, updated_ids_.size());
- EXPECT_EQ(apps::mojom::AppType::kUnknown, app_type());
- EXPECT_TRUE(cache.IsAppTypeInitialized(apps::mojom::AppType::kArc));
+ EXPECT_EQ(apps::AppType::kUnknown, app_type());
+ EXPECT_TRUE(cache.IsAppTypeInitialized(apps::AppType::kArc));
}
TEST_F(AppRegistryCacheMojomTest, Recursive) {
@@ -473,8 +482,8 @@ TEST_F(AppRegistryCacheMojomTest, Recursive) {
cache.OnApps(std::move(deltas), apps::mojom::AppType::kUnknown,
false /* should_notify_initialized */);
EXPECT_EQ(1, observer.NumAppsSeenOnAppUpdate());
- EXPECT_EQ(apps::mojom::AppType::kArc, observer.app_type());
- EXPECT_TRUE(cache.IsAppTypeInitialized(apps::mojom::AppType::kArc));
+ EXPECT_EQ(apps::AppType::kArc, observer.app_type());
+ EXPECT_TRUE(cache.IsAppTypeInitialized(apps::AppType::kArc));
}
TEST_F(AppRegistryCacheMojomTest, SuperRecursive) {
@@ -521,8 +530,8 @@ TEST_F(AppRegistryCacheMojomTest, SuperRecursive) {
EXPECT_EQ("avocado", GetName(cache, "a"));
EXPECT_EQ("boysenberry", GetName(cache, "b"));
EXPECT_EQ("coconut", GetName(cache, "c"));
- EXPECT_EQ(apps::mojom::AppType::kArc, observer.app_type());
- EXPECT_TRUE(cache.IsAppTypeInitialized(apps::mojom::AppType::kArc));
+ EXPECT_EQ(apps::AppType::kArc, observer.app_type());
+ EXPECT_TRUE(cache.IsAppTypeInitialized(apps::AppType::kArc));
}
TEST_F(AppRegistryCacheMojomTest, OnAppTypeInitialized) {
@@ -541,16 +550,16 @@ TEST_F(AppRegistryCacheMojomTest, OnAppTypeInitialized) {
cache.OnApps(std::move(deltas), apps::mojom::AppType::kArc,
true /* should_notify_initialized */);
- EXPECT_EQ(apps::mojom::AppType::kArc, observer1.app_type());
+ EXPECT_EQ(apps::AppType::kArc, observer1.app_type());
EXPECT_EQ(1, observer1.count());
EXPECT_EQ(3, observer1.app_count());
- EXPECT_EQ(1u, cache.GetInitializedAppTypes().size());
- EXPECT_TRUE(cache.IsAppTypeInitialized(apps::mojom::AppType::kArc));
+ EXPECT_EQ(1u, cache.InitializedAppTypes().size());
+ EXPECT_TRUE(cache.IsAppTypeInitialized(apps::AppType::kArc));
// New observers should not have OnAppTypeInitialized called.
InitializedObserver observer2(&cache);
- EXPECT_EQ(apps::mojom::AppType::kUnknown, observer2.app_type());
+ EXPECT_EQ(apps::AppType::kUnknown, observer2.app_type());
EXPECT_EQ(0, observer2.count());
- EXPECT_EQ(1u, cache.GetInitializedAppTypes().size());
- EXPECT_TRUE(cache.IsAppTypeInitialized(apps::mojom::AppType::kArc));
+ EXPECT_EQ(1u, cache.InitializedAppTypes().size());
+ EXPECT_TRUE(cache.IsAppTypeInitialized(apps::AppType::kArc));
}
diff --git a/chromium/components/services/app_service/public/cpp/app_registry_cache_unittest.cc b/chromium/components/services/app_service/public/cpp/app_registry_cache_unittest.cc
index c6c81461ff4..ebaa29ce7fb 100644
--- a/chromium/components/services/app_service/public/cpp/app_registry_cache_unittest.cc
+++ b/chromium/components/services/app_service/public/cpp/app_registry_cache_unittest.cc
@@ -2,29 +2,112 @@
// 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/app_registry_cache.h"
+
#include <map>
+#include <set>
#include <utility>
#include <vector>
-#include "components/services/app_service/public/cpp/app_registry_cache.h"
+#include "base/containers/contains.h"
+#include "base/test/scoped_feature_list.h"
+#include "components/services/app_service/public/cpp/app_types.h"
+#include "components/services/app_service/public/cpp/features.h"
#include "components/services/app_service/public/cpp/types_util.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace apps {
-class AppRegistryCacheTest : public testing::Test {
+namespace {
+
+apps::AppPtr MakeApp(const char* app_id,
+ const char* name,
+ apps::AppType app_type = apps::AppType::kArc,
+ apps::Readiness readiness = apps::Readiness::kUnknown,
+ uint64_t timeline = 0) {
+ auto app = std::make_unique<apps::App>(app_type, app_id);
+ app->readiness = readiness;
+ app->name = name;
+ app->icon_key =
+ apps::IconKey(timeline, /*resource_id=*/0, /*icon_effects=*/0);
+ return app;
+}
+
+apps::mojom::AppPtr MakeMojomApp(
+ const char* app_id,
+ const char* name,
+ apps::mojom::AppType app_type = apps::mojom::AppType::kArc) {
+ apps::mojom::AppPtr app = apps::mojom::App::New();
+ app->app_type = app_type;
+ app->app_id = app_id;
+ app->name = name;
+ return app;
+}
+
+// InitializedObserver is used to test the OnAppTypeInitialized interface for
+// AppRegistryCache::Observer.
+class InitializedObserver : public apps::AppRegistryCache::Observer {
public:
- std::unique_ptr<App> MakeApp(const char* app_id,
- const char* name,
- Readiness readiness = Readiness::kUnknown,
- uint64_t timeline = 0) {
- std::unique_ptr<App> app = std::make_unique<App>(AppType::kArc, app_id);
- app->readiness = readiness;
- app->name = name;
- app->icon_key = IconKey(timeline, /*resource_id=*/0, /*icon_effects=*/0);
- return app;
+ explicit InitializedObserver(apps::AppRegistryCache* cache) {
+ cache_ = cache;
+ Observe(cache);
+ }
+
+ ~InitializedObserver() override = default;
+
+ // apps::AppRegistryCache::Observer overrides.
+ void OnAppUpdate(const apps::AppUpdate& update) override {
+ updated_ids_.insert(update.AppId());
+ }
+
+ void UpdateApps() {
+ std::vector<AppPtr> deltas;
+ deltas.push_back(MakeApp("n", "noodle", AppType::kArc));
+ deltas.push_back(MakeApp("s", "salmon", AppType::kChromeApp));
+ cache_->OnApps(std::move(deltas), AppType::kUnknown,
+ false /* should_notify_initialized */);
+
+ std::vector<apps::mojom::AppPtr> mojom_deltas;
+ mojom_deltas.push_back(
+ MakeMojomApp("n", "noodle", apps::mojom::AppType::kArc));
+ mojom_deltas.push_back(
+ MakeMojomApp("s", "salmon", apps::mojom::AppType::kChromeApp));
+ cache_->OnApps(std::move(mojom_deltas), apps::mojom::AppType::kUnknown,
+ false /* should_notify_initialized */);
+ }
+
+ void OnAppTypeInitialized(apps::AppType app_type) override {
+ app_types_.insert(app_type);
+ ++initialized_app_type_count_;
+ app_count_at_initialization_ = updated_ids_.size();
+ UpdateApps();
+ }
+
+ void OnAppRegistryCacheWillBeDestroyed(
+ apps::AppRegistryCache* cache) override {
+ Observe(nullptr);
+ }
+
+ std::set<apps::AppType> app_types() const { return app_types_; }
+
+ int initialized_app_type_count() const { return initialized_app_type_count_; }
+
+ int app_count_at_initialization() const {
+ return app_count_at_initialization_;
}
+ private:
+ std::set<std::string> updated_ids_;
+ std::set<apps::AppType> app_types_;
+ int initialized_app_type_count_ = 0;
+ int app_count_at_initialization_ = 0;
+ apps::AppRegistryCache* cache_ = nullptr;
+};
+
+} // namespace
+
+class AppRegistryCacheTest : public testing::Test {
+ public:
void CallForAllApps(AppRegistryCache& cache) {
cache.ForAllApps([this](const AppUpdate& update) { OnAppUpdate(update); });
}
@@ -68,16 +151,30 @@ class AppRegistryCacheTest : public testing::Test {
updated_names_.clear();
}
+ void DisableOnAppTypeInitializedFlag() {
+ scoped_feature_list_.InitAndDisableFeature(
+ kAppServiceOnAppTypeInitializedWithoutMojom);
+ }
+
+ void EnableOnAppTypeInitializedFlag() {
+ scoped_feature_list_.InitAndEnableFeature(
+ kAppServiceOnAppTypeInitializedWithoutMojom);
+ }
+
std::set<std::string> updated_ids_;
std::set<std::string> updated_names_;
+
+ private:
+ base::test::ScopedFeatureList scoped_feature_list_;
};
TEST_F(AppRegistryCacheTest, OnApps) {
AppRegistryCache cache;
- std::vector<std::unique_ptr<App>> deltas;
+ std::vector<AppPtr> deltas;
deltas.push_back(MakeApp("a", "apple"));
- deltas.push_back(MakeApp("b", "banana", Readiness::kReady));
- deltas.push_back(MakeApp("c", "cherry", Readiness::kDisabledByPolicy,
+ deltas.push_back(MakeApp("b", "banana", AppType::kArc, Readiness::kReady));
+ deltas.push_back(MakeApp("c", "cherry", AppType::kArc,
+ Readiness::kDisabledByPolicy,
/*timeline=*/10));
cache.OnApps(std::move(deltas), AppType::kUnknown,
false /* should_notify_initialized */);
@@ -98,7 +195,7 @@ TEST_F(AppRegistryCacheTest, OnApps) {
Clear();
deltas.clear();
- deltas.push_back(MakeApp("a", "apricot", Readiness::kReady));
+ deltas.push_back(MakeApp("a", "apricot", AppType::kArc, Readiness::kReady));
deltas.push_back(MakeApp("d", "durian"));
cache.OnApps(std::move(deltas), AppType::kUnknown,
false /* should_notify_initialized */);
@@ -121,8 +218,8 @@ TEST_F(AppRegistryCacheTest, OnApps) {
TEST_F(AppRegistryCacheTest, Removed) {
AppRegistryCache cache;
- std::vector<std::unique_ptr<App>> apps;
- apps.push_back(MakeApp("app", "app", Readiness::kReady));
+ std::vector<AppPtr> apps;
+ apps.push_back(MakeApp("app", "app", AppType::kArc, Readiness::kReady));
cache.OnApps(std::move(apps), AppType::kUnknown,
false /* should_notify_initialized */);
@@ -135,8 +232,9 @@ TEST_F(AppRegistryCacheTest, Removed) {
// Uninstall the app, then remove it.
apps.clear();
- apps.push_back(MakeApp("app", "app", Readiness::kUninstalledByUser));
- apps.push_back(MakeApp("app", "app", Readiness::kRemoved));
+ apps.push_back(
+ MakeApp("app", "app", AppType::kArc, Readiness::kUninstalledByUser));
+ apps.push_back(MakeApp("app", "app", AppType::kArc, Readiness::kRemoved));
cache.OnApps(std::move(apps), AppType::kUnknown,
false /* should_notify_initialized */);
@@ -148,4 +246,549 @@ TEST_F(AppRegistryCacheTest, Removed) {
Clear();
}
+// Verify the OnAppTypeInitialized callback when OnApps is called for the non
+// mojom App type first, with the disabled flag.
+TEST_F(AppRegistryCacheTest,
+ OnAppTypeInitializedWithDisableFlagNonMojomUpdateFirst) {
+ DisableOnAppTypeInitializedFlag();
+
+ AppRegistryCache cache;
+ InitializedObserver observer1(&cache);
+
+ std::vector<AppPtr> deltas1;
+ deltas1.push_back(MakeApp("a", "avocado"));
+ deltas1.push_back(MakeApp("c", "cucumber"));
+ cache.OnApps(std::move(deltas1), AppType::kArc,
+ true /* should_notify_initialized */);
+
+ // Verify OnAppTypeInitialized is not called when the non mojom Apps are
+ // added.
+ EXPECT_TRUE(observer1.app_types().empty());
+ EXPECT_EQ(0, observer1.initialized_app_type_count());
+ EXPECT_EQ(0, observer1.app_count_at_initialization());
+ EXPECT_TRUE(cache.InitializedAppTypes().empty());
+ EXPECT_FALSE(cache.IsAppTypeInitialized(AppType::kArc));
+
+ std::vector<apps::mojom::AppPtr> mojom_deltas1;
+ mojom_deltas1.push_back(MakeMojomApp("a", "avocado"));
+ mojom_deltas1.push_back(MakeMojomApp("c", "cucumber"));
+ cache.OnApps(std::move(mojom_deltas1), apps::mojom::AppType::kArc,
+ true /* should_notify_initialized */);
+
+ // Verify OnAppTypeInitialized is called when the mojom Apps are added.
+ EXPECT_TRUE(base::Contains(observer1.app_types(), apps::AppType::kArc));
+ EXPECT_EQ(1, observer1.initialized_app_type_count());
+ EXPECT_EQ(2, observer1.app_count_at_initialization());
+ EXPECT_EQ(1u, cache.InitializedAppTypes().size());
+ EXPECT_TRUE(cache.IsAppTypeInitialized(AppType::kArc));
+
+ std::vector<AppPtr> deltas2;
+ deltas2.push_back(MakeApp("d", "durian"));
+ cache.OnApps(std::move(deltas2), AppType::kArc,
+ true /* should_notify_initialized */);
+
+ // Verify OnAppTypeInitialized is not called when the non mojom Apps are
+ // added.
+ EXPECT_EQ(1, observer1.initialized_app_type_count());
+ EXPECT_EQ(2, observer1.app_count_at_initialization());
+ EXPECT_EQ(1u, cache.InitializedAppTypes().size());
+
+ std::vector<apps::mojom::AppPtr> mojom_deltas2;
+ mojom_deltas2.push_back(MakeMojomApp("d", "durian"));
+ cache.OnApps(std::move(mojom_deltas2), apps::mojom::AppType::kArc,
+ true /* should_notify_initialized */);
+
+ // Verify OnAppTypeInitialized is not called when the mojom Apps are
+ // initialized again.
+ EXPECT_EQ(1, observer1.initialized_app_type_count());
+ EXPECT_EQ(2, observer1.app_count_at_initialization());
+ EXPECT_EQ(1u, cache.InitializedAppTypes().size());
+
+ // Verify the new observers should not have OnAppTypeInitialized called.
+ InitializedObserver observer2(&cache);
+ EXPECT_TRUE(observer2.app_types().empty());
+ EXPECT_EQ(0, observer2.initialized_app_type_count());
+ EXPECT_EQ(0, observer2.app_count_at_initialization());
+}
+
+// Verify the OnAppTypeInitialized callback when OnApps is called for the mojom
+// App type first, with the disabled flag.
+TEST_F(AppRegistryCacheTest,
+ OnAppTypeInitializedWithDisableFlagMojomUpdateFirst) {
+ DisableOnAppTypeInitializedFlag();
+
+ AppRegistryCache cache;
+ InitializedObserver observer1(&cache);
+
+ std::vector<apps::mojom::AppPtr> mojom_deltas1;
+ mojom_deltas1.push_back(MakeMojomApp("a", "avocado"));
+ mojom_deltas1.push_back(MakeMojomApp("c", "cucumber"));
+ cache.OnApps(std::move(mojom_deltas1), apps::mojom::AppType::kArc,
+ true /* should_notify_initialized */);
+
+ // Verify OnAppTypeInitialized is called when the mojom Apps are added.
+ EXPECT_TRUE(base::Contains(observer1.app_types(), apps::AppType::kArc));
+ EXPECT_EQ(1, observer1.initialized_app_type_count());
+ EXPECT_EQ(2, observer1.app_count_at_initialization());
+ EXPECT_EQ(1u, cache.InitializedAppTypes().size());
+ EXPECT_TRUE(cache.IsAppTypeInitialized(AppType::kArc));
+
+ std::vector<AppPtr> deltas1;
+ deltas1.push_back(MakeApp("a", "avocado"));
+ deltas1.push_back(MakeApp("c", "cucumber"));
+ cache.OnApps(std::move(deltas1), AppType::kArc,
+ true /* should_notify_initialized */);
+
+ // Verify OnAppTypeInitialized is not called when the non mojom Apps are
+ // added.
+ EXPECT_EQ(1, observer1.initialized_app_type_count());
+ EXPECT_EQ(2, observer1.app_count_at_initialization());
+
+ std::vector<apps::mojom::AppPtr> mojom_deltas2;
+ mojom_deltas2.push_back(MakeMojomApp("d", "durian"));
+ cache.OnApps(std::move(mojom_deltas2), apps::mojom::AppType::kArc,
+ true /* should_notify_initialized */);
+
+ std::vector<AppPtr> deltas2;
+ deltas2.push_back(MakeApp("d", "durian"));
+ cache.OnApps(std::move(deltas2), AppType::kArc,
+ true /* should_notify_initialized */);
+
+ // Verify OnAppTypeInitialized is not called when the Apps are added.
+ EXPECT_EQ(1, observer1.initialized_app_type_count());
+ EXPECT_EQ(2, observer1.app_count_at_initialization());
+ EXPECT_EQ(1u, cache.InitializedAppTypes().size());
+
+ // Verify the new observers should not have OnAppTypeInitialized called.
+ InitializedObserver observer2(&cache);
+ EXPECT_TRUE(observer2.app_types().empty());
+ EXPECT_EQ(0, observer2.initialized_app_type_count());
+ EXPECT_EQ(0, observer2.app_count_at_initialization());
+}
+
+// Verify the OnAppTypeInitialized callback when OnApps is called for multiple
+// App types, with the disabled flag.
+TEST_F(AppRegistryCacheTest,
+ OnAppTypeInitializedWithDisableFlagMultipleAppTypes) {
+ DisableOnAppTypeInitializedFlag();
+
+ AppRegistryCache cache;
+ InitializedObserver observer1(&cache);
+
+ std::vector<AppPtr> deltas1;
+ deltas1.push_back(MakeApp("a", "avocado"));
+ deltas1.push_back(MakeApp("c", "cucumber"));
+ cache.OnApps(std::move(deltas1), AppType::kArc,
+ true /* should_notify_initialized */);
+
+ // Verify OnAppTypeInitialized is not called when the non mojom Apps are
+ // added.
+ EXPECT_TRUE(observer1.app_types().empty());
+ EXPECT_EQ(0, observer1.initialized_app_type_count());
+ EXPECT_EQ(0, observer1.app_count_at_initialization());
+ EXPECT_TRUE(cache.InitializedAppTypes().empty());
+
+ std::vector<apps::mojom::AppPtr> mojom_deltas1;
+ mojom_deltas1.push_back(MakeMojomApp("a", "avocado"));
+ mojom_deltas1.push_back(MakeMojomApp("c", "cucumber"));
+ cache.OnApps(std::move(mojom_deltas1), apps::mojom::AppType::kArc,
+ true /* should_notify_initialized */);
+
+ // Verify OnAppTypeInitialized is called when the mojom Apps are added.
+ EXPECT_TRUE(base::Contains(observer1.app_types(), apps::AppType::kArc));
+ EXPECT_EQ(1, observer1.initialized_app_type_count());
+ EXPECT_EQ(2, observer1.app_count_at_initialization());
+ EXPECT_EQ(1u, cache.InitializedAppTypes().size());
+ EXPECT_TRUE(cache.IsAppTypeInitialized(AppType::kArc));
+
+ std::vector<apps::mojom::AppPtr> mojom_deltas2;
+ mojom_deltas2.push_back(
+ MakeMojomApp("d", "durian", apps::mojom::AppType::kChromeApp));
+ cache.OnApps(std::move(mojom_deltas2), apps::mojom::AppType::kChromeApp,
+ true /* should_notify_initialized */);
+
+ // Verify OnAppTypeInitialized is called when the mojom Apps are added.
+ EXPECT_EQ(2u, observer1.app_types().size());
+ EXPECT_TRUE(base::Contains(observer1.app_types(), apps::AppType::kChromeApp));
+ EXPECT_EQ(2, observer1.initialized_app_type_count());
+ EXPECT_EQ(5, observer1.app_count_at_initialization());
+ EXPECT_EQ(2u, cache.InitializedAppTypes().size());
+ EXPECT_TRUE(cache.IsAppTypeInitialized(AppType::kChromeApp));
+
+ std::vector<AppPtr> deltas2;
+ deltas2.push_back(MakeApp("d", "durian", AppType::kChromeApp));
+ cache.OnApps(std::move(deltas2), AppType::kChromeApp,
+ true /* should_notify_initialized */);
+
+ // Verify OnAppTypeInitialized is not called when the non mojom Apps are
+ // added.
+ EXPECT_EQ(2, observer1.initialized_app_type_count());
+ EXPECT_EQ(5, observer1.app_count_at_initialization());
+ EXPECT_EQ(2u, cache.InitializedAppTypes().size());
+
+ // Verify the new observers should not have OnAppTypeInitialized called.
+ InitializedObserver observer2(&cache);
+ EXPECT_TRUE(observer2.app_types().empty());
+ EXPECT_EQ(0, observer2.initialized_app_type_count());
+ EXPECT_EQ(0, observer2.app_count_at_initialization());
+}
+
+// Verify the OnAppTypeInitialized callback when OnApps is called for empty apps
+// vector, with the disabled flag.
+TEST_F(AppRegistryCacheTest, OnAppTypeInitializedWithDisableFlagEmptyUpdate) {
+ DisableOnAppTypeInitializedFlag();
+
+ AppRegistryCache cache;
+ InitializedObserver observer1(&cache);
+
+ std::vector<AppPtr> deltas1;
+ cache.OnApps(std::move(deltas1), AppType::kStandaloneBrowserChromeApp,
+ true /* should_notify_initialized */);
+
+ // Verify OnAppTypeInitialized is not called when the non mojom Apps are
+ // initialized.
+ EXPECT_TRUE(observer1.app_types().empty());
+ EXPECT_EQ(0, observer1.initialized_app_type_count());
+ EXPECT_EQ(0, observer1.app_count_at_initialization());
+ EXPECT_TRUE(cache.InitializedAppTypes().empty());
+ EXPECT_FALSE(
+ cache.IsAppTypeInitialized(AppType::kStandaloneBrowserChromeApp));
+
+ std::vector<apps::mojom::AppPtr> mojom_deltas1;
+ cache.OnApps(std::move(mojom_deltas1),
+ apps::mojom::AppType::kStandaloneBrowserChromeApp,
+ true /* should_notify_initialized */);
+
+ // Verify OnAppTypeInitialized is called when the mojom Apps are initialized.
+ EXPECT_TRUE(base::Contains(observer1.app_types(),
+ apps::AppType::kStandaloneBrowserChromeApp));
+ EXPECT_EQ(1, observer1.initialized_app_type_count());
+ EXPECT_EQ(0, observer1.app_count_at_initialization());
+ EXPECT_EQ(1u, cache.InitializedAppTypes().size());
+ EXPECT_TRUE(cache.IsAppTypeInitialized(AppType::kStandaloneBrowserChromeApp));
+
+ std::vector<AppPtr> deltas2;
+ deltas2.push_back(MakeApp("d", "durian"));
+ cache.OnApps(std::move(deltas2), AppType::kStandaloneBrowserChromeApp,
+ true /* should_notify_initialized */);
+
+ // Verify OnAppTypeInitialized is not called when the non mojom Apps are
+ // initialized again.
+ EXPECT_EQ(1, observer1.initialized_app_type_count());
+ EXPECT_EQ(0, observer1.app_count_at_initialization());
+ EXPECT_EQ(1u, cache.InitializedAppTypes().size());
+
+ std::vector<apps::mojom::AppPtr> mojom_deltas2;
+ mojom_deltas2.push_back(MakeMojomApp("d", "durian"));
+ cache.OnApps(std::move(mojom_deltas2),
+ apps::mojom::AppType::kStandaloneBrowserChromeApp,
+ true /* should_notify_initialized */);
+
+ // Verify OnAppTypeInitialized is not called when the mojom Apps are
+ // initialized again.
+ EXPECT_EQ(1, observer1.initialized_app_type_count());
+ EXPECT_EQ(0, observer1.app_count_at_initialization());
+ EXPECT_EQ(1u, cache.InitializedAppTypes().size());
+
+ std::vector<apps::mojom::AppPtr> mojom_deltas3;
+ cache.OnApps(std::move(mojom_deltas3), apps::mojom::AppType::kRemote,
+ true /* should_notify_initialized */);
+
+ // Verify OnAppTypeInitialized is called when the mojom Apps are initialized.
+ EXPECT_EQ(2u, observer1.app_types().size());
+ EXPECT_TRUE(base::Contains(observer1.app_types(), apps::AppType::kRemote));
+ EXPECT_EQ(2, observer1.initialized_app_type_count());
+ EXPECT_EQ(2u, cache.InitializedAppTypes().size());
+ EXPECT_TRUE(cache.IsAppTypeInitialized(AppType::kRemote));
+
+ std::vector<AppPtr> deltas3;
+ cache.OnApps(std::move(deltas3), AppType::kRemote,
+ true /* should_notify_initialized */);
+
+ // Verify OnAppTypeInitialized is not called when the non mojom Apps are
+ // initialized.
+ EXPECT_EQ(2, observer1.initialized_app_type_count());
+ EXPECT_EQ(2u, cache.InitializedAppTypes().size());
+
+ // Verify the new observers should not have OnAppTypeInitialized called.
+ InitializedObserver observer2(&cache);
+ EXPECT_TRUE(observer2.app_types().empty());
+ EXPECT_EQ(0, observer2.initialized_app_type_count());
+ EXPECT_EQ(0, observer2.app_count_at_initialization());
+}
+
+// Verify the OnAppTypeInitialized callback when OnApps is called for the non
+// mojom App type first, with the enabled flag.
+TEST_F(AppRegistryCacheTest,
+ OnAppTypeInitializedWithEnableFlagNonMojomUpdateFirst) {
+ EnableOnAppTypeInitializedFlag();
+
+ AppRegistryCache cache;
+ InitializedObserver observer1(&cache);
+
+ std::vector<AppPtr> deltas1;
+ deltas1.push_back(MakeApp("a", "avocado"));
+ deltas1.push_back(MakeApp("c", "cucumber"));
+ cache.OnApps(std::move(deltas1), AppType::kArc,
+ true /* should_notify_initialized */);
+
+ // Verify OnAppTypeInitialized is not called when the non mojom Apps are
+ // added.
+ EXPECT_TRUE(observer1.app_types().empty());
+ EXPECT_EQ(0, observer1.initialized_app_type_count());
+ EXPECT_EQ(0, observer1.app_count_at_initialization());
+ EXPECT_EQ(0u, cache.InitializedAppTypes().size());
+ EXPECT_FALSE(cache.IsAppTypeInitialized(AppType::kArc));
+
+ std::vector<apps::mojom::AppPtr> mojom_deltas1;
+ mojom_deltas1.push_back(MakeMojomApp("a", "avocado"));
+ mojom_deltas1.push_back(MakeMojomApp("c", "cucumber"));
+ cache.OnApps(std::move(mojom_deltas1), apps::mojom::AppType::kArc,
+ true /* should_notify_initialized */);
+
+ // Verify OnAppTypeInitialized is called when both the non mojom and mojom
+ // Apps are added.
+ EXPECT_TRUE(base::Contains(observer1.app_types(), AppType::kArc));
+ EXPECT_EQ(1, observer1.initialized_app_type_count());
+ EXPECT_EQ(2, observer1.app_count_at_initialization());
+ EXPECT_EQ(1u, cache.InitializedAppTypes().size());
+ EXPECT_TRUE(cache.IsAppTypeInitialized(AppType::kArc));
+
+ std::vector<AppPtr> deltas2;
+ deltas2.push_back(MakeApp("d", "durian"));
+ cache.OnApps(std::move(deltas2), AppType::kArc,
+ true /* should_notify_initialized */);
+
+ std::vector<apps::mojom::AppPtr> mojom_deltas2;
+ mojom_deltas2.push_back(MakeMojomApp("d", "durian"));
+ cache.OnApps(std::move(mojom_deltas2), apps::mojom::AppType::kArc,
+ true /* should_notify_initialized */);
+
+ // Verify OnAppTypeInitialized is not called when more Apps are
+ // added.
+ EXPECT_TRUE(base::Contains(observer1.app_types(), AppType::kArc));
+ EXPECT_EQ(1, observer1.initialized_app_type_count());
+ EXPECT_EQ(2, observer1.app_count_at_initialization());
+ EXPECT_EQ(1u, cache.InitializedAppTypes().size());
+
+ // Verify the new observers should not have OnAppTypeInitialized called.
+ InitializedObserver observer2(&cache);
+ EXPECT_TRUE(observer2.app_types().empty());
+ EXPECT_EQ(0, observer2.initialized_app_type_count());
+ EXPECT_EQ(0, observer2.app_count_at_initialization());
+}
+
+// Verify the OnAppTypeInitialized callback when OnApps is called for the mojom
+// App type first, with the enabled flag.
+TEST_F(AppRegistryCacheTest,
+ OnAppTypeInitializedWithEnableFlagMojomUpdateFirst) {
+ EnableOnAppTypeInitializedFlag();
+
+ AppRegistryCache cache;
+ InitializedObserver observer1(&cache);
+
+ std::vector<apps::mojom::AppPtr> mojom_deltas1;
+ mojom_deltas1.push_back(MakeMojomApp("a", "avocado"));
+ mojom_deltas1.push_back(MakeMojomApp("c", "cucumber"));
+ cache.OnApps(std::move(mojom_deltas1), apps::mojom::AppType::kArc,
+ true /* should_notify_initialized */);
+
+ // Verify OnAppTypeInitialized is not called when the mojom Apps are added.
+ EXPECT_TRUE(observer1.app_types().empty());
+ EXPECT_EQ(0, observer1.initialized_app_type_count());
+ EXPECT_EQ(0, observer1.app_count_at_initialization());
+ EXPECT_EQ(0u, cache.InitializedAppTypes().size());
+ EXPECT_FALSE(cache.IsAppTypeInitialized(AppType::kArc));
+
+ std::vector<AppPtr> deltas1;
+ deltas1.push_back(MakeApp("a", "avocado"));
+ deltas1.push_back(MakeApp("c", "cucumber"));
+ cache.OnApps(std::move(deltas1), AppType::kArc,
+ true /* should_notify_initialized */);
+
+ // Verify OnAppTypeInitialized is called when both the non mojom and mojom
+ // Apps are added.
+ EXPECT_TRUE(base::Contains(observer1.app_types(), AppType::kArc));
+ EXPECT_EQ(1, observer1.initialized_app_type_count());
+ EXPECT_EQ(2, observer1.app_count_at_initialization());
+ EXPECT_EQ(1u, cache.InitializedAppTypes().size());
+ EXPECT_TRUE(cache.IsAppTypeInitialized(AppType::kArc));
+
+ std::vector<apps::mojom::AppPtr> mojom_deltas2;
+ mojom_deltas2.push_back(MakeMojomApp("d", "durian"));
+ cache.OnApps(std::move(mojom_deltas2), apps::mojom::AppType::kArc,
+ true /* should_notify_initialized */);
+
+ std::vector<AppPtr> deltas2;
+ deltas2.push_back(MakeApp("d", "durian"));
+ cache.OnApps(std::move(deltas2), AppType::kArc,
+ true /* should_notify_initialized */);
+
+ // Verify OnAppTypeInitialized is not called when the Apps are added.
+ EXPECT_EQ(1, observer1.initialized_app_type_count());
+ EXPECT_EQ(2, observer1.app_count_at_initialization());
+ EXPECT_EQ(1u, cache.InitializedAppTypes().size());
+
+ // Verify the new observers should not have OnAppTypeInitialized called.
+ InitializedObserver observer2(&cache);
+ EXPECT_TRUE(observer2.app_types().empty());
+ EXPECT_EQ(0, observer2.initialized_app_type_count());
+ EXPECT_EQ(0, observer2.app_count_at_initialization());
+}
+
+// Verify the OnAppTypeInitialized callback when OnApps is called for multiple
+// App types, with the enabled flag.
+TEST_F(AppRegistryCacheTest,
+ OnAppTypeInitializedWithEnableFlagMultipleAppTypes) {
+ EnableOnAppTypeInitializedFlag();
+
+ AppRegistryCache cache;
+ InitializedObserver observer1(&cache);
+
+ std::vector<AppPtr> deltas1;
+ deltas1.push_back(MakeApp("a", "avocado"));
+ deltas1.push_back(MakeApp("c", "cucumber"));
+ cache.OnApps(std::move(deltas1), AppType::kArc,
+ true /* should_notify_initialized */);
+
+ // Verify OnAppTypeInitialized is not called when the non mojom Apps are
+ // added.
+ EXPECT_TRUE(observer1.app_types().empty());
+ EXPECT_EQ(0, observer1.initialized_app_type_count());
+ EXPECT_EQ(0, observer1.app_count_at_initialization());
+ EXPECT_TRUE(cache.InitializedAppTypes().empty());
+
+ std::vector<apps::mojom::AppPtr> mojom_deltas1;
+ mojom_deltas1.push_back(MakeMojomApp("a", "avocado"));
+ mojom_deltas1.push_back(MakeMojomApp("c", "cucumber"));
+ cache.OnApps(std::move(mojom_deltas1), apps::mojom::AppType::kArc,
+ true /* should_notify_initialized */);
+
+ // Verify OnAppTypeInitialized is called when both the non mojom and mojom
+ // Apps are added.
+ EXPECT_TRUE(base::Contains(observer1.app_types(), AppType::kArc));
+ EXPECT_EQ(1, observer1.initialized_app_type_count());
+ EXPECT_EQ(2, observer1.app_count_at_initialization());
+ EXPECT_EQ(1u, cache.InitializedAppTypes().size());
+ EXPECT_TRUE(cache.IsAppTypeInitialized(AppType::kArc));
+
+ std::vector<apps::mojom::AppPtr> mojom_deltas2;
+ mojom_deltas2.push_back(
+ MakeMojomApp("d", "durian", apps::mojom::AppType::kChromeApp));
+ cache.OnApps(std::move(mojom_deltas2), apps::mojom::AppType::kChromeApp,
+ true /* should_notify_initialized */);
+
+ // Verify OnAppTypeInitialized is not called when the mojom Apps are added.
+ EXPECT_EQ(1u, observer1.app_types().size());
+ EXPECT_FALSE(base::Contains(observer1.app_types(), AppType::kChromeApp));
+ EXPECT_EQ(1, observer1.initialized_app_type_count());
+ EXPECT_EQ(2, observer1.app_count_at_initialization());
+ EXPECT_EQ(1u, cache.InitializedAppTypes().size());
+ EXPECT_FALSE(cache.IsAppTypeInitialized(AppType::kChromeApp));
+
+ std::vector<AppPtr> deltas2;
+ deltas2.push_back(MakeApp("d", "durian", AppType::kChromeApp));
+ cache.OnApps(std::move(deltas2), AppType::kChromeApp,
+ true /* should_notify_initialized */);
+
+ // Verify OnAppTypeInitialized is called when both the non mojom and mojom
+ // Apps are added.
+ EXPECT_EQ(2u, observer1.app_types().size());
+ EXPECT_TRUE(base::Contains(observer1.app_types(), AppType::kChromeApp));
+ EXPECT_EQ(2, observer1.initialized_app_type_count());
+ EXPECT_EQ(5, observer1.app_count_at_initialization());
+ EXPECT_EQ(2u, cache.InitializedAppTypes().size());
+ EXPECT_TRUE(cache.IsAppTypeInitialized(AppType::kChromeApp));
+
+ // Verify the new observers should not have OnAppTypeInitialized called.
+ InitializedObserver observer2(&cache);
+ EXPECT_TRUE(observer2.app_types().empty());
+ EXPECT_EQ(0, observer2.initialized_app_type_count());
+ EXPECT_EQ(0, observer2.app_count_at_initialization());
+}
+
+// Verify the OnAppTypeInitialized callback when OnApps is called for empty apps
+// vector, with the enabled flag.
+TEST_F(AppRegistryCacheTest, OnAppTypeInitializedWithEnableFlagEmptyUpdate) {
+ EnableOnAppTypeInitializedFlag();
+
+ AppRegistryCache cache;
+ InitializedObserver observer1(&cache);
+
+ std::vector<AppPtr> deltas1;
+ cache.OnApps(std::move(deltas1), AppType::kStandaloneBrowserChromeApp,
+ true /* should_notify_initialized */);
+
+ // Verify OnAppTypeInitialized is not called when the non mojom Apps are
+ // initialized.
+ EXPECT_TRUE(observer1.app_types().empty());
+ EXPECT_EQ(0, observer1.initialized_app_type_count());
+ EXPECT_EQ(0, observer1.app_count_at_initialization());
+ EXPECT_TRUE(cache.InitializedAppTypes().empty());
+
+ std::vector<apps::mojom::AppPtr> mojom_deltas1;
+ cache.OnApps(std::move(mojom_deltas1),
+ apps::mojom::AppType::kStandaloneBrowserChromeApp,
+ true /* should_notify_initialized */);
+
+ // Verify OnAppTypeInitialized is called when both the mojom and non mojom
+ // Apps are initialized.
+ EXPECT_TRUE(base::Contains(observer1.app_types(),
+ AppType::kStandaloneBrowserChromeApp));
+ EXPECT_EQ(1, observer1.initialized_app_type_count());
+ EXPECT_EQ(0, observer1.app_count_at_initialization());
+ EXPECT_EQ(1u, cache.InitializedAppTypes().size());
+ EXPECT_TRUE(cache.IsAppTypeInitialized(AppType::kStandaloneBrowserChromeApp));
+
+ std::vector<AppPtr> deltas2;
+ deltas2.push_back(MakeApp("d", "durian"));
+ cache.OnApps(std::move(deltas2), AppType::kStandaloneBrowserChromeApp,
+ true /* should_notify_initialized */);
+
+ // Verify OnAppTypeInitialized is not called when the non mojom Apps are
+ // initialized again.
+ EXPECT_EQ(1, observer1.initialized_app_type_count());
+ EXPECT_EQ(0, observer1.app_count_at_initialization());
+ EXPECT_EQ(1u, cache.InitializedAppTypes().size());
+
+ std::vector<apps::mojom::AppPtr> mojom_deltas2;
+ mojom_deltas2.push_back(MakeMojomApp("d", "durian"));
+ cache.OnApps(std::move(mojom_deltas2),
+ apps::mojom::AppType::kStandaloneBrowserChromeApp,
+ true /* should_notify_initialized */);
+
+ // Verify OnAppTypeInitialized is not called when the mojom Apps are
+ // initialized again.
+ EXPECT_EQ(1, observer1.initialized_app_type_count());
+ EXPECT_EQ(0, observer1.app_count_at_initialization());
+ EXPECT_EQ(1u, cache.InitializedAppTypes().size());
+
+ std::vector<apps::mojom::AppPtr> mojom_deltas3;
+ cache.OnApps(std::move(mojom_deltas3), apps::mojom::AppType::kRemote,
+ true /* should_notify_initialized */);
+
+ // Verify OnAppTypeInitialized is not called when the mojom Apps are
+ // initialized.
+ EXPECT_EQ(1u, observer1.app_types().size());
+ EXPECT_EQ(1u, cache.InitializedAppTypes().size());
+
+ std::vector<AppPtr> deltas3;
+ cache.OnApps(std::move(deltas3), AppType::kRemote,
+ true /* should_notify_initialized */);
+
+ // Verify OnAppTypeInitialized is called when both the mojom and non mojom
+ // Apps are initialized.
+ EXPECT_EQ(2u, observer1.app_types().size());
+ EXPECT_TRUE(base::Contains(observer1.app_types(), AppType::kRemote));
+ EXPECT_EQ(2, observer1.initialized_app_type_count());
+ EXPECT_EQ(2u, cache.InitializedAppTypes().size());
+ EXPECT_TRUE(cache.IsAppTypeInitialized(AppType::kRemote));
+
+ // Verify the new observers should not have OnAppTypeInitialized called.
+ InitializedObserver observer2(&cache);
+ EXPECT_TRUE(observer2.app_types().empty());
+ EXPECT_EQ(0, observer2.initialized_app_type_count());
+ EXPECT_EQ(0, observer2.app_count_at_initialization());
+}
+
} // namespace apps
diff --git a/chromium/components/services/app_service/public/cpp/app_types.cc b/chromium/components/services/app_service/public/cpp/app_types.cc
index 4a6d3ed29c9..abacfc3c8a1 100644
--- a/chromium/components/services/app_service/public/cpp/app_types.cc
+++ b/chromium/components/services/app_service/public/cpp/app_types.cc
@@ -11,8 +11,8 @@ App::App(AppType app_type, const std::string& app_id)
App::~App() = default;
-std::unique_ptr<App> App::Clone() const {
- std::unique_ptr<App> app = std::make_unique<App>(app_type, app_id);
+AppPtr App::Clone() const {
+ auto app = std::make_unique<App>(app_type, app_id);
app->readiness = readiness;
app->name = name;
@@ -20,11 +20,39 @@ std::unique_ptr<App> App::Clone() const {
app->publisher_id = publisher_id;
app->description = description;
app->version = version;
+ app->additional_search_terms = additional_search_terms;
if (icon_key.has_value()) {
app->icon_key = apps::IconKey(icon_key->timeline, icon_key->resource_id,
icon_key->icon_effects);
}
+
+ app->last_launch_time = last_launch_time;
+ app->install_time = install_time;
+ app->permissions = ClonePermissions(permissions);
+ app->install_reason = install_reason;
+ app->install_source = install_source;
+ app->policy_id = policy_id;
+ app->is_platform_app = is_platform_app;
+ app->recommendable = recommendable;
+ app->searchable = searchable;
+ app->show_in_launcher = show_in_launcher;
+ app->show_in_shelf = show_in_shelf;
+ app->show_in_search = show_in_search;
+ app->show_in_management = show_in_management;
+ app->handles_intents = handles_intents;
+ app->allow_uninstall = allow_uninstall;
+ app->has_badge = has_badge;
+ app->paused = paused;
+ app->intent_filters = CloneIntentFilters(intent_filters);
+ app->resize_locked = resize_locked;
+ app->window_mode = window_mode;
+
+ if (run_on_os_login.has_value()) {
+ app->run_on_os_login = apps::RunOnOsLogin(run_on_os_login->login_mode,
+ run_on_os_login->is_managed);
+ }
+
return app;
}
@@ -61,6 +89,39 @@ AppType ConvertMojomAppTypToAppType(apps::mojom::AppType mojom_app_type) {
}
}
+mojom::AppType ConvertAppTypeToMojomAppType(AppType app_type) {
+ switch (app_type) {
+ case AppType::kUnknown:
+ return apps::mojom::AppType::kUnknown;
+ case AppType::kArc:
+ return apps::mojom::AppType::kArc;
+ case AppType::kBuiltIn:
+ return apps::mojom::AppType::kBuiltIn;
+ case AppType::kCrostini:
+ return apps::mojom::AppType::kCrostini;
+ case AppType::kChromeApp:
+ return apps::mojom::AppType::kChromeApp;
+ case AppType::kWeb:
+ return apps::mojom::AppType::kWeb;
+ case AppType::kMacOs:
+ return apps::mojom::AppType::kMacOs;
+ case AppType::kPluginVm:
+ return apps::mojom::AppType::kPluginVm;
+ case AppType::kStandaloneBrowser:
+ return apps::mojom::AppType::kStandaloneBrowser;
+ case AppType::kRemote:
+ return apps::mojom::AppType::kRemote;
+ case AppType::kBorealis:
+ return apps::mojom::AppType::kBorealis;
+ case AppType::kSystemWeb:
+ return apps::mojom::AppType::kSystemWeb;
+ case AppType::kStandaloneBrowserChromeApp:
+ return apps::mojom::AppType::kStandaloneBrowserChromeApp;
+ case AppType::kExtension:
+ return apps::mojom::AppType::kExtension;
+ }
+}
+
Readiness ConvertMojomReadinessToReadiness(
apps::mojom::Readiness mojom_readiness) {
switch (mojom_readiness) {
@@ -85,10 +146,157 @@ Readiness ConvertMojomReadinessToReadiness(
}
}
-std::unique_ptr<App> ConvertMojomAppToApp(
- const apps::mojom::AppPtr& mojom_app) {
+apps::mojom::Readiness ConvertReadinessToMojomReadiness(Readiness readiness) {
+ switch (readiness) {
+ case Readiness::kUnknown:
+ return apps::mojom::Readiness::kUnknown;
+ case Readiness::kReady:
+ return apps::mojom::Readiness::kReady;
+ case Readiness::kDisabledByBlocklist:
+ return apps::mojom::Readiness::kDisabledByBlocklist;
+ case Readiness::kDisabledByPolicy:
+ return apps::mojom::Readiness::kDisabledByPolicy;
+ case Readiness::kDisabledByUser:
+ return apps::mojom::Readiness::kDisabledByUser;
+ case Readiness::kTerminated:
+ return apps::mojom::Readiness::kTerminated;
+ case Readiness::kUninstalledByUser:
+ return apps::mojom::Readiness::kUninstalledByUser;
+ case Readiness::kRemoved:
+ return apps::mojom::Readiness::kRemoved;
+ case Readiness::kUninstalledByMigration:
+ return apps::mojom::Readiness::kUninstalledByMigration;
+ }
+}
+
+InstallReason ConvertMojomInstallReasonToInstallReason(
+ apps::mojom::InstallReason mojom_install_reason) {
+ switch (mojom_install_reason) {
+ case apps::mojom::InstallReason::kUnknown:
+ return InstallReason::kUnknown;
+ case apps::mojom::InstallReason::kSystem:
+ return InstallReason::kSystem;
+ case apps::mojom::InstallReason::kPolicy:
+ return InstallReason::kPolicy;
+ case apps::mojom::InstallReason::kOem:
+ return InstallReason::kOem;
+ case apps::mojom::InstallReason::kDefault:
+ return InstallReason::kDefault;
+ case apps::mojom::InstallReason::kSync:
+ return InstallReason::kSync;
+ case apps::mojom::InstallReason::kUser:
+ return InstallReason::kUser;
+ case apps::mojom::InstallReason::kSubApp:
+ return InstallReason::kSubApp;
+ }
+}
+
+apps::mojom::InstallReason ConvertInstallReasonToMojomInstallReason(
+ InstallReason install_reason) {
+ switch (install_reason) {
+ case InstallReason::kUnknown:
+ return apps::mojom::InstallReason::kUnknown;
+ case InstallReason::kSystem:
+ return apps::mojom::InstallReason::kSystem;
+ case InstallReason::kPolicy:
+ return apps::mojom::InstallReason::kPolicy;
+ case InstallReason::kOem:
+ return apps::mojom::InstallReason::kOem;
+ case InstallReason::kDefault:
+ return apps::mojom::InstallReason::kDefault;
+ case InstallReason::kSync:
+ return apps::mojom::InstallReason::kSync;
+ case InstallReason::kUser:
+ return apps::mojom::InstallReason::kUser;
+ case InstallReason::kSubApp:
+ return apps::mojom::InstallReason::kSubApp;
+ }
+}
+
+InstallSource ConvertMojomInstallSourceToInstallSource(
+ apps::mojom::InstallSource mojom_install_source) {
+ switch (mojom_install_source) {
+ case apps::mojom::InstallSource::kUnknown:
+ return InstallSource::kUnknown;
+ case apps::mojom::InstallSource::kSystem:
+ return InstallSource::kSystem;
+ case apps::mojom::InstallSource::kSync:
+ return InstallSource::kSync;
+ case apps::mojom::InstallSource::kPlayStore:
+ return InstallSource::kPlayStore;
+ case apps::mojom::InstallSource::kChromeWebStore:
+ return InstallSource::kChromeWebStore;
+ case apps::mojom::InstallSource::kBrowser:
+ return InstallSource::kBrowser;
+ }
+}
+
+apps::mojom::InstallSource ConvertInstallSourceToMojomInstallSource(
+ InstallSource install_source) {
+ switch (install_source) {
+ case InstallSource::kUnknown:
+ return apps::mojom::InstallSource::kUnknown;
+ case InstallSource::kSystem:
+ return apps::mojom::InstallSource::kSystem;
+ case InstallSource::kSync:
+ return apps::mojom::InstallSource::kSync;
+ case InstallSource::kPlayStore:
+ return apps::mojom::InstallSource::kPlayStore;
+ case InstallSource::kChromeWebStore:
+ return apps::mojom::InstallSource::kChromeWebStore;
+ case InstallSource::kBrowser:
+ return apps::mojom::InstallSource::kBrowser;
+ }
+}
+
+WindowMode ConvertMojomWindowModeToWindowMode(
+ apps::mojom::WindowMode mojom_window_mode) {
+ switch (mojom_window_mode) {
+ case apps::mojom::WindowMode::kUnknown:
+ return WindowMode::kUnknown;
+ case apps::mojom::WindowMode::kWindow:
+ return WindowMode::kWindow;
+ case apps::mojom::WindowMode::kBrowser:
+ return WindowMode::kBrowser;
+ case apps::mojom::WindowMode::kTabbedWindow:
+ return WindowMode::kTabbedWindow;
+ }
+}
+
+apps::mojom::WindowMode ConvertWindowModeToMojomWindowMode(
+ WindowMode window_mode) {
+ switch (window_mode) {
+ case WindowMode::kUnknown:
+ return apps::mojom::WindowMode::kUnknown;
+ case WindowMode::kWindow:
+ return apps::mojom::WindowMode::kWindow;
+ case WindowMode::kBrowser:
+ return apps::mojom::WindowMode::kBrowser;
+ case WindowMode::kTabbedWindow:
+ return apps::mojom::WindowMode::kTabbedWindow;
+ }
+}
+
+absl::optional<bool> GetOptionalBool(
+ const apps::mojom::OptionalBool& mojom_optional_bool) {
+ absl::optional<bool> optional_bool;
+ if (mojom_optional_bool != apps::mojom::OptionalBool::kUnknown) {
+ optional_bool = mojom_optional_bool == apps::mojom::OptionalBool::kTrue;
+ }
+ return optional_bool;
+}
+
+apps::mojom::OptionalBool GetMojomOptionalBool(
+ const absl::optional<bool>& optional_bool) {
+ return optional_bool.has_value()
+ ? (optional_bool.value() ? apps::mojom::OptionalBool::kTrue
+ : apps::mojom::OptionalBool::kFalse)
+ : apps::mojom::OptionalBool::kUnknown;
+}
+
+AppPtr ConvertMojomAppToApp(const apps::mojom::AppPtr& mojom_app) {
DCHECK(mojom_app);
- std::unique_ptr<App> app = std::make_unique<App>(
+ auto app = std::make_unique<App>(
ConvertMojomAppTypToAppType(mojom_app->app_type), mojom_app->app_id);
app->readiness = ConvertMojomReadinessToReadiness(mojom_app->readiness);
@@ -97,13 +305,121 @@ std::unique_ptr<App> ConvertMojomAppToApp(
app->publisher_id = mojom_app->publisher_id;
app->description = mojom_app->description;
app->version = mojom_app->version;
+ app->additional_search_terms = mojom_app->additional_search_terms;
if (mojom_app->icon_key) {
app->icon_key = apps::IconKey(mojom_app->icon_key->timeline,
mojom_app->icon_key->resource_id,
mojom_app->icon_key->icon_effects);
}
+
+ app->last_launch_time = mojom_app->last_launch_time;
+ app->install_time = mojom_app->install_time;
+
+ for (const auto& mojom_permission : mojom_app->permissions) {
+ auto permission = ConvertMojomPermissionToPermission(mojom_permission);
+ if (permission) {
+ app->permissions.push_back(std::move(permission));
+ }
+ }
+
+ app->install_reason =
+ ConvertMojomInstallReasonToInstallReason(mojom_app->install_reason);
+ app->install_source =
+ ConvertMojomInstallSourceToInstallSource(mojom_app->install_source);
+
+ app->policy_id = mojom_app->policy_id;
+
+ app->is_platform_app = GetOptionalBool(mojom_app->is_platform_app);
+ app->recommendable = GetOptionalBool(mojom_app->recommendable);
+ app->searchable = GetOptionalBool(mojom_app->searchable);
+ app->show_in_launcher = GetOptionalBool(mojom_app->show_in_launcher);
+ app->show_in_shelf = GetOptionalBool(mojom_app->show_in_shelf);
+ app->show_in_search = GetOptionalBool(mojom_app->show_in_search);
+ app->show_in_management = GetOptionalBool(mojom_app->show_in_management);
+ app->handles_intents = GetOptionalBool(mojom_app->handles_intents);
+ app->allow_uninstall = GetOptionalBool(mojom_app->allow_uninstall);
+ app->has_badge = GetOptionalBool(mojom_app->has_badge);
+ app->paused = GetOptionalBool(mojom_app->paused);
+
+ for (const auto& mojom_intent_filter : mojom_app->intent_filters) {
+ auto intent_filter =
+ ConvertMojomIntentFilterToIntentFilter(mojom_intent_filter);
+ if (intent_filter) {
+ app->intent_filters.push_back(std::move(intent_filter));
+ }
+ }
+
+ app->resize_locked = GetOptionalBool(mojom_app->resize_locked);
+ app->window_mode = ConvertMojomWindowModeToWindowMode(mojom_app->window_mode);
+ if (mojom_app->run_on_os_login) {
+ app->run_on_os_login =
+ apps::RunOnOsLogin(ConvertMojomRunOnOsLoginModeToRunOnOsLoginMode(
+ mojom_app->run_on_os_login->login_mode),
+ mojom_app->run_on_os_login->is_managed);
+ }
+
return app;
}
+apps::mojom::AppPtr ConvertAppToMojomApp(const AppPtr& app) {
+ auto mojom_app = apps::mojom::App::New();
+ mojom_app->app_type = ConvertAppTypeToMojomAppType(app->app_type);
+ mojom_app->app_id = app->app_id;
+ mojom_app->readiness = ConvertReadinessToMojomReadiness(app->readiness);
+ mojom_app->name = app->name;
+ mojom_app->short_name = app->short_name;
+ mojom_app->publisher_id = app->publisher_id;
+ mojom_app->description = app->description;
+ mojom_app->version = app->version;
+ mojom_app->additional_search_terms = app->additional_search_terms;
+
+ if (app->icon_key.has_value()) {
+ mojom_app->icon_key = ConvertIconKeyToMojomIconKey(app->icon_key.value());
+ }
+
+ mojom_app->last_launch_time = app->last_launch_time;
+ mojom_app->install_time = app->install_time;
+
+ for (const auto& permission : app->permissions) {
+ if (permission) {
+ mojom_app->permissions.push_back(
+ ConvertPermissionToMojomPermission(permission));
+ }
+ }
+
+ mojom_app->install_reason =
+ ConvertInstallReasonToMojomInstallReason(app->install_reason);
+ mojom_app->install_source =
+ ConvertInstallSourceToMojomInstallSource(app->install_source);
+ mojom_app->policy_id = app->policy_id;
+ mojom_app->is_platform_app = GetMojomOptionalBool(app->is_platform_app);
+ mojom_app->recommendable = GetMojomOptionalBool(app->recommendable);
+ mojom_app->searchable = GetMojomOptionalBool(app->searchable);
+ mojom_app->show_in_launcher = GetMojomOptionalBool(app->show_in_launcher);
+ mojom_app->show_in_shelf = GetMojomOptionalBool(app->show_in_shelf);
+ mojom_app->show_in_search = GetMojomOptionalBool(app->show_in_search);
+ mojom_app->show_in_management = GetMojomOptionalBool(app->show_in_management);
+ mojom_app->handles_intents = GetMojomOptionalBool(app->handles_intents);
+ mojom_app->allow_uninstall = GetMojomOptionalBool(app->allow_uninstall);
+ mojom_app->has_badge = GetMojomOptionalBool(app->has_badge);
+ mojom_app->paused = GetMojomOptionalBool(app->paused);
+
+ for (const auto& intent_filter : app->intent_filters) {
+ if (intent_filter) {
+ mojom_app->intent_filters.push_back(
+ ConvertIntentFilterToMojomIntentFilter(intent_filter));
+ }
+ }
+
+ mojom_app->resize_locked = GetMojomOptionalBool(app->resize_locked);
+ mojom_app->window_mode = ConvertWindowModeToMojomWindowMode(app->window_mode);
+
+ if (app->run_on_os_login.has_value()) {
+ mojom_app->run_on_os_login =
+ ConvertRunOnOsLoginToMojomRunOnOsLogin(app->run_on_os_login.value());
+ }
+ return mojom_app;
+}
+
} // namespace apps
diff --git a/chromium/components/services/app_service/public/cpp/app_types.h b/chromium/components/services/app_service/public/cpp/app_types.h
index d22de50352a..0e7fdc2ac43 100644
--- a/chromium/components/services/app_service/public/cpp/app_types.h
+++ b/chromium/components/services/app_service/public/cpp/app_types.h
@@ -7,8 +7,14 @@
#include <string>
#include <utility>
+#include <vector>
+#include "base/component_export.h"
+#include "base/time/time.h"
#include "components/services/app_service/public/cpp/icon_types.h"
+#include "components/services/app_service/public/cpp/intent_filter.h"
+#include "components/services/app_service/public/cpp/permission.h"
+#include "components/services/app_service/public/cpp/run_on_os_login_types.h"
#include "components/services/app_service/public/mojom/types.mojom.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
@@ -54,6 +60,55 @@ enum class Readiness {
kMaxValue = kUninstalledByMigration,
};
+// How the app was installed.
+// This should be kept in sync with histograms.xml, and InstallReason in
+// enums.xml.
+// Note the enumeration is used in UMA histogram so entries should not be
+// re-ordered or removed. New entries should be added at the bottom.
+enum class InstallReason {
+ kUnknown = 0,
+ kSystem, // Installed with the system and is considered a part of the OS.
+ kPolicy, // Installed by policy.
+ kOem, // Installed by an OEM.
+ kDefault, // Preinstalled by default, but is not considered a system app.
+ kSync, // Installed by sync.
+ kUser, // Installed by user action.
+ kSubApp, // Installed by the SubApp API call.
+
+ // Add any new values above this one, and update kMaxValue to the highest
+ // enumerator value.
+ kMaxValue = kSubApp,
+};
+
+// Where the app was installed from.
+// This should be kept in sync with histograms.xml, and InstallSource in
+// enums.xml.
+// Note the enumeration is used in UMA histogram so entries should not be
+// re-ordered or removed. New entries should be added at the bottom.
+enum class InstallSource {
+ kUnknown = 0,
+ kSystem, // Installed as part of Chrome OS.
+ kSync, // Installed from sync.
+ kPlayStore, // Installed from Play store.
+ kChromeWebStore, // Installed from Chrome web store.
+ kBrowser, // Installed from browser.
+
+ // Add any new values above this one, and update kMaxValue to the highest
+ // enumerator value.
+ kMaxValue = kBrowser,
+};
+
+// The window mode that each app will open in.
+enum class WindowMode {
+ kUnknown = 0,
+ // Opens in a standalone window
+ kWindow,
+ // Opens in the default web browser
+ kBrowser,
+ // Opens in a tabbed app window
+ kTabbedWindow,
+};
+
struct COMPONENT_EXPORT(APP_TYPES) App {
App(AppType app_type, const std::string& app_id);
@@ -78,26 +133,130 @@ struct COMPONENT_EXPORT(APP_TYPES) App {
absl::optional<std::string> description;
absl::optional<std::string> version;
+ std::vector<std::string> additional_search_terms;
absl::optional<IconKey> icon_key;
- // TODO(crbug.com/1253250): Add other App struct fields.
+ absl::optional<base::Time> last_launch_time;
+ absl::optional<base::Time> install_time;
+
+ // This vector must be treated atomically, if there is a permission
+ // change, the publisher must send through the entire list of permissions.
+ // Should contain no duplicate IDs.
+ // If empty during updates, Subscriber can assume no changes.
+ // There is no guarantee that this is sorted by any criteria.
+ Permissions permissions;
+
+ // Whether the app was installed by sync, policy or as a default app.
+ InstallReason install_reason = InstallReason::kUnknown;
+
+ // Where the app was installed from, e.g. from Play Store, from Chrome Web
+ // Store, etc.
+ InstallSource install_source = InstallSource::kUnknown;
+
+ // An optional ID used for policy to identify the app.
+ // For web apps, it contains the install URL.
+ absl::optional<std::string> policy_id;
+
+ // Whether the app is an extensions::Extensions where is_platform_app()
+ // returns true.
+ absl::optional<bool> is_platform_app;
+
+ absl::optional<bool> recommendable;
+ absl::optional<bool> searchable;
+ absl::optional<bool> show_in_launcher;
+ absl::optional<bool> show_in_shelf;
+ absl::optional<bool> show_in_search;
+ absl::optional<bool> show_in_management;
+
+ // True if the app is able to handle intents and should be shown in intent
+ // surfaces.
+ absl::optional<bool> handles_intents;
+
+ // Whether the app publisher allows the app to be uninstalled.
+ absl::optional<bool> allow_uninstall;
+
+ // Whether the app icon should add the notification badging.
+ absl::optional<bool> has_badge;
+
+ // Paused apps cannot be launched, and any running apps that become paused
+ // will be stopped. This is independent of whether or not the app is ready to
+ // be launched (defined by the Readiness field).
+ absl::optional<bool> paused;
+
+ // This vector stores all the intent filters defined in this app. Each
+ // intent filter defines a matching criteria for whether an intent can
+ // be handled by this app. One app can have multiple intent filters.
+ IntentFilters intent_filters;
+
+ // Whether the app can be free resized. If this is true, various resizing
+ // operations will be restricted.
+ absl::optional<bool> resize_locked;
+
+ // Whether the app's display mode is in the browser or otherwise.
+ WindowMode window_mode = WindowMode::kUnknown;
+
+ // Whether the app runs on os login in a new window or not.
+ absl::optional<RunOnOsLogin> run_on_os_login;
// When adding new fields to the App type, the `Clone` function and the
// `AppUpdate` class should also be updated.
};
+using AppPtr = std::unique_ptr<App>;
+
// TODO(crbug.com/1253250): Remove these functions after migrating to non-mojo
// AppService.
COMPONENT_EXPORT(APP_TYPES)
AppType ConvertMojomAppTypToAppType(apps::mojom::AppType mojom_app_type);
COMPONENT_EXPORT(APP_TYPES)
+mojom::AppType ConvertAppTypeToMojomAppType(AppType mojom_app_type);
+
+COMPONENT_EXPORT(APP_TYPES)
Readiness ConvertMojomReadinessToReadiness(
apps::mojom::Readiness mojom_readiness);
COMPONENT_EXPORT(APP_TYPES)
-std::unique_ptr<App> ConvertMojomAppToApp(const apps::mojom::AppPtr& mojom_app);
+apps::mojom::Readiness ConvertReadinessToMojomReadiness(Readiness readiness);
+
+COMPONENT_EXPORT(APP_TYPES)
+InstallReason ConvertMojomInstallReasonToInstallReason(
+ apps::mojom::InstallReason mojom_install_reason);
+
+COMPONENT_EXPORT(APP_TYPES)
+apps::mojom::InstallReason ConvertInstallReasonToMojomInstallReason(
+ InstallReason install_reason);
+
+COMPONENT_EXPORT(APP_TYPES)
+InstallSource ConvertMojomInstallSourceToInstallSource(
+ apps::mojom::InstallSource mojom_install_source);
+
+COMPONENT_EXPORT(APP_TYPES)
+apps::mojom::InstallSource ConvertInstallSourceToMojomInstallSource(
+ InstallSource install_source);
+
+COMPONENT_EXPORT(APP_TYPES)
+WindowMode ConvertMojomWindowModeToWindowMode(
+ apps::mojom::WindowMode mojom_window_mode);
+
+COMPONENT_EXPORT(APP_TYPES)
+apps::mojom::WindowMode ConvertWindowModeToMojomWindowMode(
+ WindowMode mojom_window_mode);
+
+COMPONENT_EXPORT(APP_TYPES)
+absl::optional<bool> GetOptionalBool(
+ const apps::mojom::OptionalBool& mojom_optional_bool);
+
+COMPONENT_EXPORT(APP_TYPES)
+absl::optional<bool> GetMojomOptionalBool(
+ const apps::mojom::OptionalBool& mojom_optional_bool);
+
+COMPONENT_EXPORT(APP_TYPES)
+AppPtr ConvertMojomAppToApp(const apps::mojom::AppPtr& mojom_app);
+
+COMPONENT_EXPORT(APP_TYPES)
+apps::mojom::AppPtr ConvertAppToMojomApp(const AppPtr& app);
} // namespace apps
diff --git a/chromium/components/services/app_service/public/cpp/app_update.cc b/chromium/components/services/app_service/public/cpp/app_update.cc
index 71a696f89cf..2950815e592 100644
--- a/chromium/components/services/app_service/public/cpp/app_update.cc
+++ b/chromium/components/services/app_service/public/cpp/app_update.cc
@@ -9,6 +9,7 @@
#include "base/time/time.h"
#include "components/services/app_service/public/cpp/icon_types.h"
#include "components/services/app_service/public/cpp/intent_filter_util.h"
+#include "components/services/app_service/public/cpp/macros.h"
namespace {
@@ -39,6 +40,11 @@ absl::optional<apps::IconKey> CloneIconKey(const apps::IconKey& icon_key) {
icon_key.icon_effects);
}
+absl::optional<apps::RunOnOsLogin> CloneRunOnOsLogin(
+ const apps::RunOnOsLogin& login_data) {
+ return apps::RunOnOsLogin(login_data.login_mode, login_data.is_managed);
+}
+
} // namespace
namespace apps {
@@ -99,7 +105,7 @@ void AppUpdate::Merge(apps::mojom::App* state, const apps::mojom::App* delta) {
DCHECK(state->permissions.empty() ||
(delta->permissions.size() == state->permissions.size()));
state->permissions.clear();
- ClonePermissions(delta->permissions, &state->permissions);
+ ::ClonePermissions(delta->permissions, &state->permissions);
}
if (delta->install_reason != apps::mojom::InstallReason::kUnknown) {
state->install_reason = delta->install_reason;
@@ -145,7 +151,7 @@ void AppUpdate::Merge(apps::mojom::App* state, const apps::mojom::App* delta) {
}
if (!delta->intent_filters.empty()) {
state->intent_filters.clear();
- CloneIntentFilters(delta->intent_filters, &state->intent_filters);
+ ::CloneIntentFilters(delta->intent_filters, &state->intent_filters);
}
if (delta->resize_locked != apps::mojom::OptionalBool::kUnknown) {
state->resize_locked = delta->resize_locked;
@@ -153,6 +159,9 @@ void AppUpdate::Merge(apps::mojom::App* state, const apps::mojom::App* delta) {
if (delta->window_mode != apps::mojom::WindowMode::kUnknown) {
state->window_mode = delta->window_mode;
}
+ if (!delta->run_on_os_login.is_null()) {
+ state->run_on_os_login = delta->run_on_os_login.Clone();
+ }
// When adding new fields to the App Mojo type, this function should also be
// updated.
@@ -175,28 +184,59 @@ void AppUpdate::Merge(App* state, const App* delta) {
DCHECK_NE(state->readiness, Readiness::kRemoved);
DCHECK_NE(delta->readiness, Readiness::kRemoved);
- if (delta->readiness != apps::Readiness::kUnknown) {
- state->readiness = delta->readiness;
- }
- if (delta->name.has_value()) {
- state->name = delta->name;
- }
- if (delta->short_name.has_value()) {
- state->short_name = delta->short_name;
- }
- if (delta->publisher_id.has_value()) {
- state->publisher_id = delta->publisher_id;
- }
- if (delta->description.has_value()) {
- state->description = delta->description;
- }
- if (delta->version.has_value()) {
- state->version = delta->version;
+ SET_ENUM_VALUE(readiness, apps::Readiness::kUnknown);
+ SET_OPTIONAL_VALUE(name)
+ SET_OPTIONAL_VALUE(short_name)
+ SET_OPTIONAL_VALUE(publisher_id)
+ SET_OPTIONAL_VALUE(description)
+ SET_OPTIONAL_VALUE(version)
+
+ if (!delta->additional_search_terms.empty()) {
+ state->additional_search_terms.clear();
+ state->additional_search_terms = delta->additional_search_terms;
}
+
if (delta->icon_key.has_value()) {
state->icon_key = CloneIconKey(delta->icon_key.value());
}
+ SET_OPTIONAL_VALUE(last_launch_time);
+ SET_OPTIONAL_VALUE(install_time);
+
+ if (!delta->permissions.empty()) {
+ DCHECK(state->permissions.empty() ||
+ (delta->permissions.size() == state->permissions.size()));
+ state->permissions.clear();
+ state->permissions = ClonePermissions(delta->permissions);
+ }
+
+ SET_ENUM_VALUE(install_reason, InstallReason::kUnknown);
+ SET_ENUM_VALUE(install_source, InstallSource::kUnknown);
+ SET_OPTIONAL_VALUE(policy_id);
+ SET_OPTIONAL_VALUE(is_platform_app);
+ SET_OPTIONAL_VALUE(recommendable);
+ SET_OPTIONAL_VALUE(searchable);
+ SET_OPTIONAL_VALUE(show_in_launcher);
+ SET_OPTIONAL_VALUE(show_in_shelf);
+ SET_OPTIONAL_VALUE(show_in_search);
+ SET_OPTIONAL_VALUE(show_in_management);
+ SET_OPTIONAL_VALUE(handles_intents);
+ SET_OPTIONAL_VALUE(allow_uninstall);
+ SET_OPTIONAL_VALUE(has_badge);
+ SET_OPTIONAL_VALUE(paused);
+
+ if (!delta->intent_filters.empty()) {
+ state->intent_filters.clear();
+ state->intent_filters = CloneIntentFilters(delta->intent_filters);
+ }
+
+ SET_OPTIONAL_VALUE(resize_locked)
+ SET_ENUM_VALUE(window_mode, WindowMode::kUnknown)
+
+ if (delta->run_on_os_login.has_value()) {
+ state->run_on_os_login = CloneRunOnOsLogin(delta->run_on_os_login.value());
+ }
+
// When adding new fields to the App type, this function should also be
// updated.
}
@@ -260,14 +300,7 @@ apps::mojom::Readiness AppUpdate::PriorReadiness() const {
}
apps::Readiness AppUpdate::GetReadiness() const {
- if (delta_ && (delta_->readiness != apps::Readiness::kUnknown)) {
- return delta_->readiness;
- }
- if (state_) {
- return state_->readiness;
- }
- return apps::Readiness::kUnknown;
-}
+ GET_VALUE_WITH_DEFAULT_VALUE(readiness, apps::Readiness::kUnknown)}
apps::Readiness AppUpdate::GetPriorReadiness() const {
return state_ ? state_->readiness : apps::Readiness::kUnknown;
@@ -291,13 +324,7 @@ const std::string& AppUpdate::Name() const {
}
const std::string& AppUpdate::GetName() const {
- if (delta_ && delta_->name.has_value()) {
- return delta_->name.value();
- }
- if (state_ && state_->name.has_value()) {
- return state_->name.value();
- }
- return base::EmptyString();
+ GET_VALUE_WITH_FALLBACK(name, base::EmptyString())
}
bool AppUpdate::NameChanged() const {
@@ -316,13 +343,7 @@ const std::string& AppUpdate::ShortName() const {
}
const std::string& AppUpdate::GetShortName() const {
- if (delta_ && delta_->short_name.has_value()) {
- return delta_->short_name.value();
- }
- if (state_ && state_->short_name.has_value()) {
- return state_->short_name.value();
- }
- return base::EmptyString();
+ GET_VALUE_WITH_FALLBACK(short_name, base::EmptyString())
}
bool AppUpdate::ShortNameChanged() const {
@@ -342,13 +363,7 @@ const std::string& AppUpdate::PublisherId() const {
}
const std::string& AppUpdate::GetPublisherId() const {
- if (delta_ && delta_->publisher_id.has_value()) {
- return delta_->publisher_id.value();
- }
- if (state_ && state_->publisher_id.has_value()) {
- return state_->publisher_id.value();
- }
- return base::EmptyString();
+ GET_VALUE_WITH_FALLBACK(publisher_id, base::EmptyString())
}
bool AppUpdate::PublisherIdChanged() const {
@@ -368,13 +383,7 @@ const std::string& AppUpdate::Description() const {
}
const std::string& AppUpdate::GetDescription() const {
- if (delta_ && delta_->description.has_value()) {
- return delta_->description.value();
- }
- if (state_ && state_->description.has_value()) {
- return state_->description.value();
- }
- return base::EmptyString();
+ GET_VALUE_WITH_FALLBACK(description, base::EmptyString())
}
bool AppUpdate::DescriptionChanged() const {
@@ -394,13 +403,7 @@ const std::string& AppUpdate::Version() const {
}
const std::string& AppUpdate::GetVersion() const {
- if (delta_ && delta_->version.has_value()) {
- return delta_->version.value();
- }
- if (state_ && state_->version.has_value()) {
- return state_->version.value();
- }
- return base::EmptyString();
+ GET_VALUE_WITH_FALLBACK(version, base::EmptyString())
}
bool AppUpdate::VersionChanged() const {
@@ -422,6 +425,11 @@ std::vector<std::string> AppUpdate::AdditionalSearchTerms() const {
return additional_search_terms;
}
+std::vector<std::string> AppUpdate::GetAdditionalSearchTerms() const {
+ GET_VALUE_WITH_CHECK_AND_DEFAULT_RETURN(additional_search_terms, empty,
+ std::vector<std::string>{})
+}
+
bool AppUpdate::AdditionalSearchTermsChanged() const {
return mojom_delta_ && !mojom_delta_->additional_search_terms.empty() &&
(!mojom_state_ || (mojom_delta_->additional_search_terms !=
@@ -464,6 +472,10 @@ base::Time AppUpdate::LastLaunchTime() const {
return base::Time();
}
+base::Time AppUpdate::GetLastLaunchTime() const {
+ GET_VALUE_WITH_FALLBACK(last_launch_time, base::Time())
+}
+
bool AppUpdate::LastLaunchTimeChanged() const {
return mojom_delta_ && mojom_delta_->last_launch_time.has_value() &&
(!mojom_state_ ||
@@ -480,6 +492,10 @@ base::Time AppUpdate::InstallTime() const {
return base::Time();
}
+base::Time AppUpdate::GetInstallTime() const {
+ GET_VALUE_WITH_FALLBACK(install_time, base::Time())
+}
+
bool AppUpdate::InstallTimeChanged() const {
return mojom_delta_ && mojom_delta_->install_time.has_value() &&
(!mojom_state_ ||
@@ -490,9 +506,21 @@ std::vector<apps::mojom::PermissionPtr> AppUpdate::Permissions() const {
std::vector<apps::mojom::PermissionPtr> permissions;
if (mojom_delta_ && !mojom_delta_->permissions.empty()) {
- ClonePermissions(mojom_delta_->permissions, &permissions);
+ ::ClonePermissions(mojom_delta_->permissions, &permissions);
} else if (mojom_state_ && !mojom_state_->permissions.empty()) {
- ClonePermissions(mojom_state_->permissions, &permissions);
+ ::ClonePermissions(mojom_state_->permissions, &permissions);
+ }
+
+ return permissions;
+}
+
+apps::Permissions AppUpdate::GetPermissions() const {
+ apps::Permissions permissions;
+
+ if (delta_ && !delta_->permissions.empty()) {
+ permissions = ClonePermissions(delta_->permissions);
+ } else if (state_ && !state_->permissions.empty()) {
+ permissions = ClonePermissions(state_->permissions);
}
return permissions;
@@ -515,6 +543,10 @@ apps::mojom::InstallReason AppUpdate::InstallReason() const {
return apps::mojom::InstallReason::kUnknown;
}
+apps::InstallReason AppUpdate::GetInstallReason() const {
+ GET_VALUE_WITH_DEFAULT_VALUE(install_reason, InstallReason::kUnknown)
+}
+
bool AppUpdate::InstallReasonChanged() const {
return mojom_delta_ &&
(mojom_delta_->install_reason !=
@@ -534,6 +566,10 @@ apps::mojom::InstallSource AppUpdate::InstallSource() const {
return apps::mojom::InstallSource::kUnknown;
}
+apps::InstallSource AppUpdate::GetInstallSource() const {
+ GET_VALUE_WITH_DEFAULT_VALUE(install_source, InstallSource::kUnknown)
+}
+
bool AppUpdate::InstallSourceChanged() const {
return mojom_delta_ &&
(mojom_delta_->install_source !=
@@ -552,6 +588,10 @@ const std::string& AppUpdate::PolicyId() const {
return base::EmptyString();
}
+const std::string& AppUpdate::GetPolicyId() const {
+ GET_VALUE_WITH_FALLBACK(policy_id, base::EmptyString())
+}
+
bool AppUpdate::PolicyIdChanged() const {
return mojom_delta_ && mojom_delta_->policy_id.has_value() &&
(!mojom_state_ ||
@@ -583,6 +623,10 @@ apps::mojom::OptionalBool AppUpdate::IsPlatformApp() const {
return apps::mojom::OptionalBool::kUnknown;
}
+absl::optional<bool> AppUpdate::GetIsPlatformApp() const {
+ GET_VALUE_WITH_FALLBACK(is_platform_app, absl::nullopt)
+}
+
bool AppUpdate::IsPlatformAppChanged() const {
return mojom_delta_ &&
(mojom_delta_->is_platform_app !=
@@ -602,6 +646,10 @@ apps::mojom::OptionalBool AppUpdate::Recommendable() const {
return apps::mojom::OptionalBool::kUnknown;
}
+absl::optional<bool> AppUpdate::GetRecommendable() const {
+ GET_VALUE_WITH_FALLBACK(recommendable, absl::nullopt)
+}
+
bool AppUpdate::RecommendableChanged() const {
return mojom_delta_ &&
(mojom_delta_->recommendable != apps::mojom::OptionalBool::kUnknown) &&
@@ -620,6 +668,10 @@ apps::mojom::OptionalBool AppUpdate::Searchable() const {
return apps::mojom::OptionalBool::kUnknown;
}
+absl::optional<bool> AppUpdate::GetSearchable() const {
+ GET_VALUE_WITH_FALLBACK(searchable, absl::nullopt)
+}
+
bool AppUpdate::SearchableChanged() const {
return mojom_delta_ &&
(mojom_delta_->searchable != apps::mojom::OptionalBool::kUnknown) &&
@@ -638,6 +690,10 @@ apps::mojom::OptionalBool AppUpdate::ShowInLauncher() const {
return apps::mojom::OptionalBool::kUnknown;
}
+absl::optional<bool> AppUpdate::GetShowInLauncher() const {
+ GET_VALUE_WITH_FALLBACK(show_in_launcher, absl::nullopt)
+}
+
bool AppUpdate::ShowInLauncherChanged() const {
return mojom_delta_ &&
(mojom_delta_->show_in_launcher !=
@@ -657,6 +713,10 @@ apps::mojom::OptionalBool AppUpdate::ShowInShelf() const {
return apps::mojom::OptionalBool::kUnknown;
}
+absl::optional<bool> AppUpdate::GetShowInShelf() const {
+ GET_VALUE_WITH_FALLBACK(show_in_shelf, absl::nullopt)
+}
+
bool AppUpdate::ShowInShelfChanged() const {
return mojom_delta_ &&
(mojom_delta_->show_in_shelf != apps::mojom::OptionalBool::kUnknown) &&
@@ -675,6 +735,10 @@ apps::mojom::OptionalBool AppUpdate::ShowInSearch() const {
return apps::mojom::OptionalBool::kUnknown;
}
+absl::optional<bool> AppUpdate::GetShowInSearch() const {
+ GET_VALUE_WITH_FALLBACK(show_in_search, absl::nullopt)
+}
+
bool AppUpdate::ShowInSearchChanged() const {
return mojom_delta_ &&
(mojom_delta_->show_in_search !=
@@ -694,6 +758,10 @@ apps::mojom::OptionalBool AppUpdate::ShowInManagement() const {
return apps::mojom::OptionalBool::kUnknown;
}
+absl::optional<bool> AppUpdate::GetShowInManagement() const {
+ GET_VALUE_WITH_FALLBACK(show_in_management, absl::nullopt)
+}
+
bool AppUpdate::ShowInManagementChanged() const {
return mojom_delta_ &&
(mojom_delta_->show_in_management !=
@@ -713,6 +781,10 @@ apps::mojom::OptionalBool AppUpdate::HandlesIntents() const {
return apps::mojom::OptionalBool::kUnknown;
}
+absl::optional<bool> AppUpdate::GetHandlesIntents() const {
+ GET_VALUE_WITH_FALLBACK(handles_intents, absl::nullopt)
+}
+
bool AppUpdate::HandlesIntentsChanged() const {
return mojom_delta_ &&
(mojom_delta_->handles_intents !=
@@ -732,6 +804,10 @@ apps::mojom::OptionalBool AppUpdate::AllowUninstall() const {
return apps::mojom::OptionalBool::kUnknown;
}
+absl::optional<bool> AppUpdate::GetAllowUninstall() const {
+ GET_VALUE_WITH_FALLBACK(allow_uninstall, absl::nullopt)
+}
+
bool AppUpdate::AllowUninstallChanged() const {
return mojom_delta_ &&
(mojom_delta_->allow_uninstall !=
@@ -751,6 +827,10 @@ apps::mojom::OptionalBool AppUpdate::HasBadge() const {
return apps::mojom::OptionalBool::kUnknown;
}
+absl::optional<bool> AppUpdate::GetHasBadge() const {
+ GET_VALUE_WITH_FALLBACK(has_badge, absl::nullopt);
+}
+
bool AppUpdate::HasBadgeChanged() const {
return mojom_delta_ &&
(mojom_delta_->has_badge != apps::mojom::OptionalBool::kUnknown) &&
@@ -769,6 +849,10 @@ apps::mojom::OptionalBool AppUpdate::Paused() const {
return apps::mojom::OptionalBool::kUnknown;
}
+absl::optional<bool> AppUpdate::GetPaused() const {
+ GET_VALUE_WITH_FALLBACK(paused, absl::nullopt);
+}
+
bool AppUpdate::PausedChanged() const {
return mojom_delta_ &&
(mojom_delta_->paused != apps::mojom::OptionalBool::kUnknown) &&
@@ -779,9 +863,21 @@ std::vector<apps::mojom::IntentFilterPtr> AppUpdate::IntentFilters() const {
std::vector<apps::mojom::IntentFilterPtr> intent_filters;
if (mojom_delta_ && !mojom_delta_->intent_filters.empty()) {
- CloneIntentFilters(mojom_delta_->intent_filters, &intent_filters);
+ ::CloneIntentFilters(mojom_delta_->intent_filters, &intent_filters);
} else if (mojom_state_ && !mojom_state_->intent_filters.empty()) {
- CloneIntentFilters(mojom_state_->intent_filters, &intent_filters);
+ ::CloneIntentFilters(mojom_state_->intent_filters, &intent_filters);
+ }
+
+ return intent_filters;
+}
+
+apps::IntentFilters AppUpdate::GetIntentFilters() const {
+ apps::IntentFilters intent_filters;
+
+ if (delta_ && !delta_->intent_filters.empty()) {
+ intent_filters = CloneIntentFilters(delta_->intent_filters);
+ } else if (state_ && !state_->intent_filters.empty()) {
+ intent_filters = CloneIntentFilters(state_->intent_filters);
}
return intent_filters;
@@ -803,6 +899,10 @@ apps::mojom::OptionalBool AppUpdate::ResizeLocked() const {
return apps::mojom::OptionalBool::kUnknown;
}
+absl::optional<bool> AppUpdate::GetResizeLocked() const {
+ GET_VALUE_WITH_FALLBACK(resize_locked, absl::nullopt);
+}
+
bool AppUpdate::ResizeLockedChanged() const {
return mojom_delta_ &&
(mojom_delta_->resize_locked != apps::mojom::OptionalBool::kUnknown) &&
@@ -821,6 +921,10 @@ apps::mojom::WindowMode AppUpdate::WindowMode() const {
return apps::mojom::WindowMode::kUnknown;
}
+apps::WindowMode AppUpdate::GetWindowMode() const {
+ GET_VALUE_WITH_DEFAULT_VALUE(window_mode, WindowMode::kUnknown)
+}
+
bool AppUpdate::WindowModeChanged() const {
return mojom_delta_ &&
(mojom_delta_->window_mode != apps::mojom::WindowMode::kUnknown) &&
@@ -828,6 +932,32 @@ bool AppUpdate::WindowModeChanged() const {
(mojom_delta_->window_mode != mojom_state_->window_mode));
}
+apps::mojom::RunOnOsLoginPtr AppUpdate::RunOnOsLogin() const {
+ if (mojom_delta_ && !mojom_delta_->run_on_os_login.is_null()) {
+ return mojom_delta_->run_on_os_login.Clone();
+ }
+ if (mojom_state_ && !mojom_state_->run_on_os_login.is_null()) {
+ return mojom_state_->run_on_os_login.Clone();
+ }
+ return apps::mojom::RunOnOsLoginPtr();
+}
+
+absl::optional<apps::RunOnOsLogin> AppUpdate::GetRunOnOsLogin() const {
+ if (delta_ && delta_->run_on_os_login.has_value()) {
+ return CloneRunOnOsLogin(delta_->run_on_os_login.value());
+ }
+ if (state_ && state_->run_on_os_login.has_value()) {
+ return CloneRunOnOsLogin(state_->run_on_os_login.value());
+ }
+ return absl::nullopt;
+}
+
+bool AppUpdate::RunOnOsLoginChanged() const {
+ return mojom_delta_ && !mojom_delta_->run_on_os_login.is_null() &&
+ (!mojom_state_ ||
+ !mojom_delta_->run_on_os_login.Equals(mojom_state_->run_on_os_login));
+}
+
const ::AccountId& AppUpdate::AccountId() const {
return account_id_;
}
@@ -885,6 +1015,9 @@ std::ostream& operator<<(std::ostream& out, const AppUpdate& app) {
out << "ResizeLocked: " << app.ResizeLocked() << std::endl;
out << "WindowMode: " << app.WindowMode() << std::endl;
+ if (app.RunOnOsLogin()) {
+ out << "RunOnOsLoginMode: " << app.RunOnOsLogin()->login_mode << std::endl;
+ }
return out;
}
diff --git a/chromium/components/services/app_service/public/cpp/app_update.h b/chromium/components/services/app_service/public/cpp/app_update.h
index 9070c6b3aca..4c6ccc87bc6 100644
--- a/chromium/components/services/app_service/public/cpp/app_update.h
+++ b/chromium/components/services/app_service/public/cpp/app_update.h
@@ -13,12 +13,15 @@
#include "base/memory/raw_ptr.h"
#include "components/account_id/account_id.h"
#include "components/services/app_service/public/cpp/app_types.h"
+#include "components/services/app_service/public/cpp/intent_filter.h"
+#include "components/services/app_service/public/cpp/permission.h"
#include "components/services/app_service/public/mojom/types.mojom.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace apps {
struct IconKey;
+struct RunOnOsLogin;
// Wraps two apps::mojom::AppPtr's, a prior state and a delta on top of that
// state. The state is conceptually the "sum" of all of the previous deltas,
@@ -108,6 +111,7 @@ class COMPONENT_EXPORT(APP_UPDATE) AppUpdate {
bool VersionChanged() const;
std::vector<std::string> AdditionalSearchTerms() const;
+ std::vector<std::string> GetAdditionalSearchTerms() const;
bool AdditionalSearchTermsChanged() const;
apps::mojom::IconKeyPtr IconKey() const;
@@ -115,69 +119,93 @@ class COMPONENT_EXPORT(APP_UPDATE) AppUpdate {
bool IconKeyChanged() const;
base::Time LastLaunchTime() const;
+ base::Time GetLastLaunchTime() const;
bool LastLaunchTimeChanged() const;
base::Time InstallTime() const;
+ base::Time GetInstallTime() const;
bool InstallTimeChanged() const;
std::vector<apps::mojom::PermissionPtr> Permissions() const;
+ apps::Permissions GetPermissions() const;
bool PermissionsChanged() const;
apps::mojom::InstallReason InstallReason() const;
+ apps::InstallReason GetInstallReason() const;
bool InstallReasonChanged() const;
apps::mojom::InstallSource InstallSource() const;
+ apps::InstallSource GetInstallSource() const;
bool InstallSourceChanged() const;
// An optional ID used for policy to identify the app.
// For web apps, it contains the install URL.
const std::string& PolicyId() const;
+ const std::string& GetPolicyId() const;
bool PolicyIdChanged() const;
apps::mojom::OptionalBool InstalledInternally() const;
apps::mojom::OptionalBool IsPlatformApp() const;
+ absl::optional<bool> GetIsPlatformApp() const;
bool IsPlatformAppChanged() const;
apps::mojom::OptionalBool Recommendable() const;
+ absl::optional<bool> GetRecommendable() const;
bool RecommendableChanged() const;
apps::mojom::OptionalBool Searchable() const;
+ absl::optional<bool> GetSearchable() const;
bool SearchableChanged() const;
apps::mojom::OptionalBool ShowInLauncher() const;
+ absl::optional<bool> GetShowInLauncher() const;
bool ShowInLauncherChanged() const;
apps::mojom::OptionalBool ShowInShelf() const;
+ absl::optional<bool> GetShowInShelf() const;
bool ShowInShelfChanged() const;
apps::mojom::OptionalBool ShowInSearch() const;
+ absl::optional<bool> GetShowInSearch() const;
bool ShowInSearchChanged() const;
apps::mojom::OptionalBool ShowInManagement() const;
+ absl::optional<bool> GetShowInManagement() const;
bool ShowInManagementChanged() const;
apps::mojom::OptionalBool HandlesIntents() const;
+ absl::optional<bool> GetHandlesIntents() const;
bool HandlesIntentsChanged() const;
apps::mojom::OptionalBool AllowUninstall() const;
+ absl::optional<bool> GetAllowUninstall() const;
bool AllowUninstallChanged() const;
apps::mojom::OptionalBool HasBadge() const;
+ absl::optional<bool> GetHasBadge() const;
bool HasBadgeChanged() const;
apps::mojom::OptionalBool Paused() const;
+ absl::optional<bool> GetPaused() const;
bool PausedChanged() const;
std::vector<apps::mojom::IntentFilterPtr> IntentFilters() const;
+ apps::IntentFilters GetIntentFilters() const;
bool IntentFiltersChanged() const;
apps::mojom::OptionalBool ResizeLocked() const;
+ absl::optional<bool> GetResizeLocked() const;
bool ResizeLockedChanged() const;
apps::mojom::WindowMode WindowMode() const;
+ apps::WindowMode GetWindowMode() const;
bool WindowModeChanged() const;
+ apps::mojom::RunOnOsLoginPtr RunOnOsLogin() const;
+ absl::optional<apps::RunOnOsLogin> GetRunOnOsLogin() const;
+ bool RunOnOsLoginChanged() const;
+
const ::AccountId& AccountId() const;
private:
diff --git a/chromium/components/services/app_service/public/cpp/app_update_mojom_unittest.cc b/chromium/components/services/app_service/public/cpp/app_update_mojom_unittest.cc
index 87c933f790a..3e2bf019056 100644
--- a/chromium/components/services/app_service/public/cpp/app_update_mojom_unittest.cc
+++ b/chromium/components/services/app_service/public/cpp/app_update_mojom_unittest.cc
@@ -2,8 +2,12 @@
// 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/app_types.h"
#include "components/services/app_service/public/cpp/app_update.h"
+#include "components/services/app_service/public/cpp/icon_types.h"
+#include "components/services/app_service/public/cpp/intent_filter.h"
#include "components/services/app_service/public/cpp/intent_filter_util.h"
+#include "components/services/app_service/public/cpp/permission.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
@@ -100,6 +104,9 @@ class AppUpdateMojomTest : public testing::Test {
apps::mojom::WindowMode expect_window_mode_;
bool expect_window_mode_changed_;
+ apps::mojom::RunOnOsLoginPtr expect_run_on_os_login_;
+ bool expect_run_on_os_login_changed_;
+
AccountId account_id_ = AccountId::FromUserEmail("test@gmail.com");
apps::mojom::PermissionPtr MakePermission(
@@ -141,6 +148,7 @@ class AppUpdateMojomTest : public testing::Test {
expect_intent_filters_changed_ = false;
expect_resize_locked_changed_ = false;
expect_window_mode_changed_ = false;
+ expect_run_on_os_login_changed_ = false;
}
void CheckExpects(const apps::AppUpdate& u) {
@@ -230,6 +238,9 @@ class AppUpdateMojomTest : public testing::Test {
EXPECT_EQ(expect_window_mode_, u.WindowMode());
EXPECT_EQ(expect_window_mode_changed_, u.WindowModeChanged());
+ EXPECT_EQ(expect_run_on_os_login_, u.RunOnOsLogin());
+ EXPECT_EQ(expect_run_on_os_login_changed_, u.RunOnOsLoginChanged());
+
EXPECT_EQ(account_id_, u.AccountId());
}
@@ -269,6 +280,7 @@ class AppUpdateMojomTest : public testing::Test {
expect_intent_filters_.clear();
expect_resize_locked_ = apps::mojom::OptionalBool::kUnknown;
expect_window_mode_ = apps::mojom::WindowMode::kUnknown;
+ expect_run_on_os_login_ = nullptr;
ExpectNoChange();
CheckExpects(u);
@@ -939,6 +951,32 @@ class AppUpdateMojomTest : public testing::Test {
ExpectNoChange();
CheckExpects(u);
}
+
+ // RunOnOsLogin tests.
+
+ if (state) {
+ auto runOnOsLoginTestPtr = apps::mojom::RunOnOsLogin::New(
+ apps::mojom::RunOnOsLoginMode::kNotRun, false);
+ state->run_on_os_login = runOnOsLoginTestPtr.Clone();
+ expect_run_on_os_login_ = runOnOsLoginTestPtr.Clone();
+ expect_run_on_os_login_changed_ = false;
+ CheckExpects(u);
+ }
+
+ if (delta) {
+ auto runOnOsLoginTestPtr = apps::mojom::RunOnOsLogin::New(
+ apps::mojom::RunOnOsLoginMode::kWindowed, false);
+ delta->run_on_os_login = runOnOsLoginTestPtr.Clone();
+ expect_run_on_os_login_ = runOnOsLoginTestPtr.Clone();
+ expect_run_on_os_login_changed_ = true;
+ CheckExpects(u);
+ }
+
+ if (state) {
+ apps::AppUpdate::Merge(state, delta);
+ ExpectNoChange();
+ CheckExpects(u);
+ }
}
};
@@ -969,3 +1007,168 @@ TEST_F(AppUpdateMojomTest, BothAreNonNull) {
TestAppUpdate(state.get(), delta.get());
}
+
+TEST_F(AppUpdateMojomTest, AppConvert) {
+ apps::mojom::AppPtr input = apps::mojom::App::New();
+ input->app_type = apps::mojom::AppType::kWeb;
+ input->app_id = "abcdefg";
+ input->readiness = apps::mojom::Readiness::kReady;
+ input->name = "lacros test name";
+ input->short_name = "lacros test name";
+ input->publisher_id = "publisher_id";
+ input->description = "description";
+ input->version = "version";
+ input->additional_search_terms = {"1", "2"};
+
+ auto icon_key = apps::mojom::IconKey::New();
+ icon_key->timeline = 1;
+ icon_key->icon_effects = 2;
+ input->icon_key = std::move(icon_key);
+
+ input->last_launch_time = base::Time() + base::Days(1);
+ input->install_time = base::Time() + base::Days(2);
+
+ input->install_reason = apps::mojom::InstallReason::kUser;
+ input->policy_id = "https://app.site/alpha";
+ input->is_platform_app = apps::mojom::OptionalBool::kFalse;
+ input->recommendable = apps::mojom::OptionalBool::kTrue;
+ input->searchable = apps::mojom::OptionalBool::kTrue;
+ input->paused = apps::mojom::OptionalBool::kFalse;
+ input->show_in_launcher = apps::mojom::OptionalBool::kTrue;
+ input->show_in_shelf = apps::mojom::OptionalBool::kTrue;
+ input->show_in_search = apps::mojom::OptionalBool::kTrue;
+ input->show_in_management = apps::mojom::OptionalBool::kTrue;
+ input->has_badge = apps::mojom::OptionalBool::kUnknown;
+ input->paused = apps::mojom::OptionalBool::kFalse;
+
+ auto intent_filter = apps::mojom::IntentFilter::New();
+ apps_util::AddSingleValueCondition(
+ apps::mojom::ConditionType::kScheme, "https",
+ apps::mojom::PatternMatchType::kNone, intent_filter);
+ intent_filter->activity_name = "activity_name";
+ intent_filter->activity_label = "activity_label";
+ input->intent_filters.push_back(std::move(intent_filter));
+
+ input->window_mode = apps::mojom::WindowMode::kWindow;
+
+ auto permission = apps::mojom::Permission::New();
+ permission->permission_type = apps::mojom::PermissionType::kCamera;
+ permission->value = apps::mojom::PermissionValue::New();
+ permission->value->set_bool_value(true);
+ permission->is_managed = true;
+ input->permissions.push_back(std::move(permission));
+
+ input->allow_uninstall = apps::mojom::OptionalBool::kTrue;
+ input->handles_intents = apps::mojom::OptionalBool::kTrue;
+
+ auto output = apps::ConvertMojomAppToApp(input);
+
+ EXPECT_EQ(output->app_type, apps::AppType::kWeb);
+ EXPECT_EQ(output->app_id, "abcdefg");
+ EXPECT_EQ(output->readiness, apps::Readiness::kReady);
+ EXPECT_EQ(output->name, "lacros test name");
+ EXPECT_EQ(output->short_name, "lacros test name");
+ EXPECT_EQ(output->publisher_id, "publisher_id");
+ EXPECT_EQ(output->description, "description");
+ EXPECT_EQ(output->version, "version");
+ EXPECT_EQ(output->additional_search_terms, input->additional_search_terms);
+
+ EXPECT_EQ(output->icon_key->timeline, 1U);
+ EXPECT_EQ(output->icon_key->icon_effects, 2U);
+
+ EXPECT_EQ(output->last_launch_time, base::Time() + base::Days(1));
+ EXPECT_EQ(output->install_time, base::Time() + base::Days(2));
+
+ EXPECT_EQ(output->install_reason, apps::InstallReason::kUser);
+ EXPECT_EQ(output->policy_id, "https://app.site/alpha");
+ EXPECT_FALSE(output->is_platform_app.value());
+ EXPECT_TRUE(output->recommendable.value());
+ EXPECT_TRUE(output->searchable.value());
+ EXPECT_FALSE(output->paused.value());
+ EXPECT_TRUE(output->show_in_launcher.value());
+ EXPECT_TRUE(output->show_in_shelf.value());
+ EXPECT_TRUE(output->show_in_search.value());
+ EXPECT_TRUE(output->show_in_management.value());
+ EXPECT_FALSE(output->has_badge.has_value());
+ EXPECT_FALSE(output->paused.value());
+
+ ASSERT_EQ(output->intent_filters.size(), 1U);
+ auto& filter = output->intent_filters[0];
+ ASSERT_EQ(filter->conditions.size(), 1U);
+ auto& condition = filter->conditions[0];
+ EXPECT_EQ(condition->condition_type, apps::ConditionType::kScheme);
+ ASSERT_EQ(condition->condition_values.size(), 1U);
+ EXPECT_EQ(condition->condition_values[0]->value, "https");
+ EXPECT_EQ(condition->condition_values[0]->match_type,
+ apps::PatternMatchType::kNone);
+ EXPECT_EQ(filter->activity_name, "activity_name");
+ EXPECT_EQ(filter->activity_label, "activity_label");
+
+ EXPECT_EQ(output->window_mode, apps::WindowMode::kWindow);
+
+ ASSERT_EQ(output->permissions.size(), 1U);
+ auto& out_permission = output->permissions[0];
+ EXPECT_EQ(out_permission->permission_type, apps::PermissionType::kCamera);
+ ASSERT_TRUE(out_permission->value->bool_value.has_value());
+ EXPECT_TRUE(out_permission->value->bool_value.value());
+ EXPECT_TRUE(out_permission->is_managed);
+
+ EXPECT_TRUE(output->allow_uninstall.value());
+ EXPECT_TRUE(output->handles_intents.value());
+
+ auto mojom_app = apps::ConvertAppToMojomApp(output);
+
+ EXPECT_EQ(mojom_app->app_type, apps::mojom::AppType::kWeb);
+ EXPECT_EQ(mojom_app->app_id, "abcdefg");
+ EXPECT_EQ(mojom_app->readiness, apps::mojom::Readiness::kReady);
+ EXPECT_EQ(mojom_app->name, "lacros test name");
+ EXPECT_EQ(mojom_app->short_name, "lacros test name");
+ EXPECT_EQ(mojom_app->publisher_id, "publisher_id");
+ EXPECT_EQ(mojom_app->description, "description");
+ EXPECT_EQ(mojom_app->version, "version");
+ EXPECT_EQ(mojom_app->additional_search_terms, input->additional_search_terms);
+
+ EXPECT_EQ(mojom_app->icon_key->timeline, 1U);
+ EXPECT_EQ(mojom_app->icon_key->icon_effects, 2U);
+
+ EXPECT_EQ(mojom_app->last_launch_time, base::Time() + base::Days(1));
+ EXPECT_EQ(mojom_app->install_time, base::Time() + base::Days(2));
+
+ EXPECT_EQ(mojom_app->install_reason, apps::mojom::InstallReason::kUser);
+ EXPECT_EQ(mojom_app->policy_id, "https://app.site/alpha");
+ EXPECT_EQ(mojom_app->recommendable, apps::mojom::OptionalBool::kTrue);
+ EXPECT_EQ(mojom_app->searchable, apps::mojom::OptionalBool::kTrue);
+ EXPECT_EQ(mojom_app->paused, apps::mojom::OptionalBool::kFalse);
+ EXPECT_EQ(mojom_app->show_in_launcher, apps::mojom::OptionalBool::kTrue);
+ EXPECT_EQ(mojom_app->show_in_shelf, apps::mojom::OptionalBool::kTrue);
+ EXPECT_EQ(mojom_app->show_in_search, apps::mojom::OptionalBool::kTrue);
+ EXPECT_EQ(mojom_app->show_in_management, apps::mojom::OptionalBool::kTrue);
+ EXPECT_EQ(mojom_app->has_badge, apps::mojom::OptionalBool::kUnknown);
+ EXPECT_EQ(mojom_app->paused, apps::mojom::OptionalBool::kFalse);
+
+ ASSERT_EQ(mojom_app->intent_filters.size(), 1U);
+ auto& mojom_filter = mojom_app->intent_filters[0];
+ ASSERT_EQ(mojom_filter->conditions.size(), 1U);
+ auto& mojom_condition = mojom_filter->conditions[0];
+ EXPECT_EQ(mojom_condition->condition_type,
+ apps::mojom::ConditionType::kScheme);
+ ASSERT_EQ(mojom_condition->condition_values.size(), 1U);
+ EXPECT_EQ(mojom_condition->condition_values[0]->value, "https");
+ EXPECT_EQ(mojom_condition->condition_values[0]->match_type,
+ apps::mojom::PatternMatchType::kNone);
+ EXPECT_EQ(mojom_filter->activity_name, "activity_name");
+ EXPECT_EQ(mojom_filter->activity_label, "activity_label");
+
+ EXPECT_EQ(mojom_app->window_mode, apps::mojom::WindowMode::kWindow);
+
+ ASSERT_EQ(mojom_app->permissions.size(), 1U);
+ auto& mojom_permission = mojom_app->permissions[0];
+ EXPECT_EQ(mojom_permission->permission_type,
+ apps::mojom::PermissionType::kCamera);
+ ASSERT_TRUE(mojom_permission->value->is_bool_value());
+ EXPECT_TRUE(mojom_permission->value->get_bool_value());
+ EXPECT_TRUE(mojom_permission->is_managed);
+
+ EXPECT_EQ(mojom_app->allow_uninstall, apps::mojom::OptionalBool::kTrue);
+ EXPECT_EQ(mojom_app->handles_intents, apps::mojom::OptionalBool::kTrue);
+}
diff --git a/chromium/components/services/app_service/public/cpp/app_update_unittest.cc b/chromium/components/services/app_service/public/cpp/app_update_unittest.cc
index 00f4613d62f..a969a8d995d 100644
--- a/chromium/components/services/app_service/public/cpp/app_update_unittest.cc
+++ b/chromium/components/services/app_service/public/cpp/app_update_unittest.cc
@@ -3,20 +3,69 @@
// found in the LICENSE file.
#include "components/services/app_service/public/cpp/app_update.h"
-#include "components/services/app_service/public/cpp/intent_filter_util.h"
+
+#include "components/services/app_service/public/cpp/app_update.h"
+#include "components/services/app_service/public/cpp/intent_filter.h"
+#include "components/services/app_service/public/cpp/permission.h"
+#include "components/services/app_service/public/cpp/run_on_os_login_types.h"
#include "testing/gtest/include/gtest/gtest.h"
+namespace apps {
+
namespace {
-const apps::AppType app_type = apps::AppType::kArc;
+const AppType app_type = AppType::kArc;
const char app_id[] = "abcdefgh";
const char test_name_0[] = "Inigo Montoya";
const char test_name_1[] = "Dread Pirate Roberts";
+
+PermissionPtr MakePermission(PermissionType permission_type,
+ TriState tri_state,
+ bool is_managed) {
+ return std::make_unique<Permission>(
+ permission_type, std::make_unique<PermissionValue>(tri_state),
+ is_managed);
+}
+
+PermissionPtr MakePermission(PermissionType permission_type,
+ bool bool_value,
+ bool is_managed) {
+ return std::make_unique<Permission>(
+ permission_type, std::make_unique<PermissionValue>(bool_value),
+ is_managed);
+}
+
+bool IsEqual(const Permissions& source, const Permissions& target) {
+ if (source.size() != target.size()) {
+ return false;
+ }
+
+ for (int i = 0; i < static_cast<int>(source.size()); i++) {
+ if (*source[i] != *target[i]) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool IsEqual(const IntentFilters& source, const IntentFilters& target) {
+ if (source.size() != target.size()) {
+ return false;
+ }
+
+ for (int i = 0; i < static_cast<int>(source.size()); i++) {
+ if (*source[i] != *target[i]) {
+ return false;
+ }
+ }
+ return true;
+}
+
} // namespace
class AppUpdateTest : public testing::Test {
protected:
- apps::Readiness expect_readiness_;
- apps::Readiness expect_prior_readiness_;
+ Readiness expect_readiness_;
+ Readiness expect_prior_readiness_;
std::string expect_name_;
bool expect_name_changed_;
@@ -29,11 +78,55 @@ class AppUpdateTest : public testing::Test {
std::string expect_version_;
- absl::optional<apps::IconKey> expect_icon_key_;
+ std::vector<std::string> expect_additional_search_terms_;
+
+ absl::optional<IconKey> expect_icon_key_;
+
+ base::Time expect_last_launch_time_;
+
+ base::Time expect_install_time_;
+
+ Permissions expect_permissions_;
+
+ InstallReason expect_install_reason_;
+
+ InstallSource expect_install_source_;
+
+ std::string expect_policy_id_;
+
+ absl::optional<bool> expect_is_platform_app_;
+
+ absl::optional<bool> expect_recommendable_;
+
+ absl::optional<bool> expect_searchable_;
+
+ absl::optional<bool> expect_show_in_launcher_;
+
+ absl::optional<bool> expect_show_in_shelf_;
+
+ absl::optional<bool> expect_show_in_search_;
+
+ absl::optional<bool> expect_show_in_management_;
+
+ absl::optional<bool> expect_handles_intents_;
+
+ absl::optional<bool> expect_allow_uninstall_;
+
+ absl::optional<bool> expect_has_badge_;
+
+ absl::optional<bool> expect_paused_;
+
+ IntentFilters expect_intent_filters_;
+
+ absl::optional<bool> expect_resize_locked_;
+
+ WindowMode expect_window_mode_;
+
+ absl::optional<RunOnOsLogin> expect_run_on_os_login_;
AccountId account_id_ = AccountId::FromUserEmail("test@gmail.com");
- void CheckExpects(const apps::AppUpdate& u) {
+ void CheckExpects(const AppUpdate& u) {
EXPECT_EQ(expect_readiness_, u.GetReadiness());
EXPECT_EQ(expect_prior_readiness_, u.GetPriorReadiness());
@@ -47,6 +140,8 @@ class AppUpdateTest : public testing::Test {
EXPECT_EQ(expect_version_, u.GetVersion());
+ EXPECT_EQ(expect_additional_search_terms_, u.GetAdditionalSearchTerms());
+
if (expect_icon_key_.has_value()) {
ASSERT_TRUE(u.GetIconKey().has_value());
EXPECT_EQ(expect_icon_key_.value(), u.GetIconKey().value());
@@ -54,23 +149,88 @@ class AppUpdateTest : public testing::Test {
ASSERT_FALSE(u.GetIconKey().has_value());
}
+ EXPECT_EQ(expect_last_launch_time_, u.GetLastLaunchTime());
+
+ EXPECT_EQ(expect_install_time_, u.GetInstallTime());
+
+ EXPECT_TRUE(IsEqual(expect_permissions_, u.GetPermissions()));
+
+ EXPECT_EQ(expect_install_reason_, u.GetInstallReason());
+
+ EXPECT_EQ(expect_install_source_, u.GetInstallSource());
+
+ EXPECT_EQ(expect_policy_id_, u.GetPolicyId());
+
+ EXPECT_EQ(expect_is_platform_app_, u.GetIsPlatformApp());
+
+ EXPECT_EQ(expect_recommendable_, u.GetRecommendable());
+
+ EXPECT_EQ(expect_searchable_, u.GetSearchable());
+
+ EXPECT_EQ(expect_show_in_launcher_, u.GetShowInLauncher());
+
+ EXPECT_EQ(expect_show_in_shelf_, u.GetShowInShelf());
+
+ EXPECT_EQ(expect_show_in_search_, u.GetShowInSearch());
+
+ EXPECT_EQ(expect_show_in_management_, u.GetShowInManagement());
+
+ EXPECT_EQ(expect_handles_intents_, u.GetHandlesIntents());
+
+ EXPECT_EQ(expect_has_badge_, u.GetHasBadge());
+
+ EXPECT_EQ(expect_paused_, u.GetPaused());
+
+ EXPECT_TRUE(IsEqual(expect_intent_filters_, u.GetIntentFilters()));
+
+ EXPECT_EQ(expect_resize_locked_, u.GetResizeLocked());
+
+ EXPECT_EQ(expect_window_mode_, u.GetWindowMode());
+ if (expect_run_on_os_login_.has_value()) {
+ ASSERT_TRUE(u.GetRunOnOsLogin().has_value());
+ EXPECT_EQ(expect_run_on_os_login_.value(), u.GetRunOnOsLogin().value());
+ } else {
+ ASSERT_FALSE(u.GetRunOnOsLogin().has_value());
+ }
+
EXPECT_EQ(account_id_, u.AccountId());
}
- void TestAppUpdate(apps::App* state, apps::App* delta) {
- apps::AppUpdate u(state, delta, account_id_);
+ void TestAppUpdate(App* state, App* delta) {
+ AppUpdate u(state, delta, account_id_);
EXPECT_EQ(app_type, u.GetAppType());
EXPECT_EQ(app_id, u.GetAppId());
- expect_readiness_ = apps::Readiness::kUnknown;
- expect_prior_readiness_ = apps::Readiness::kUnknown;
+ expect_readiness_ = Readiness::kUnknown;
+ expect_prior_readiness_ = Readiness::kUnknown;
expect_name_ = "";
expect_short_name_ = "";
expect_publisher_id_ = "";
expect_description_ = "";
expect_version_ = "";
+ expect_additional_search_terms_.clear();
expect_icon_key_ = absl::nullopt;
+ expect_last_launch_time_ = base::Time();
+ expect_install_time_ = base::Time();
+ expect_permissions_.clear();
+ expect_install_reason_ = InstallReason::kUnknown;
+ expect_install_source_ = InstallSource::kUnknown;
+ expect_policy_id_ = "";
+ expect_is_platform_app_ = absl::nullopt;
+ expect_recommendable_ = absl::nullopt;
+ expect_searchable_ = absl::nullopt;
+ expect_show_in_launcher_ = absl::nullopt;
+ expect_show_in_shelf_ = absl::nullopt;
+ expect_show_in_search_ = absl::nullopt;
+ expect_show_in_management_ = absl::nullopt;
+ expect_handles_intents_ = absl::nullopt;
+ expect_has_badge_ = absl::nullopt;
+ expect_paused_ = absl::nullopt;
+ expect_intent_filters_.clear();
+ expect_resize_locked_ = absl::nullopt;
+ expect_window_mode_ = WindowMode::kUnknown;
+ expect_run_on_os_login_ = absl::nullopt;
CheckExpects(u);
if (delta) {
@@ -88,8 +248,8 @@ class AppUpdateTest : public testing::Test {
}
if (delta) {
- delta->readiness = apps::Readiness::kReady;
- expect_readiness_ = apps::Readiness::kReady;
+ delta->readiness = Readiness::kReady;
+ expect_readiness_ = Readiness::kReady;
CheckExpects(u);
delta->name = absl::nullopt;
@@ -99,14 +259,16 @@ class AppUpdateTest : public testing::Test {
}
if (state) {
- apps::AppUpdate::Merge(state, delta);
+ AppUpdate::Merge(state, delta);
expect_prior_readiness_ = state->readiness;
+ EXPECT_EQ(expect_name_, state->name);
+ EXPECT_EQ(expect_readiness_, state->readiness);
CheckExpects(u);
}
if (delta) {
- delta->readiness = apps::Readiness::kDisabledByPolicy;
- expect_readiness_ = apps::Readiness::kDisabledByPolicy;
+ delta->readiness = Readiness::kDisabledByPolicy;
+ expect_readiness_ = Readiness::kDisabledByPolicy;
delta->name = test_name_1;
expect_name_ = test_name_1;
expect_name_changed_ = true;
@@ -128,8 +290,9 @@ class AppUpdateTest : public testing::Test {
}
if (state) {
- apps::AppUpdate::Merge(state, delta);
+ AppUpdate::Merge(state, delta);
expect_prior_readiness_ = state->readiness;
+ EXPECT_EQ(expect_short_name_, state->short_name);
CheckExpects(u);
}
@@ -148,7 +311,8 @@ class AppUpdateTest : public testing::Test {
}
if (state) {
- apps::AppUpdate::Merge(state, delta);
+ AppUpdate::Merge(state, delta);
+ EXPECT_EQ(expect_publisher_id_, state->publisher_id);
CheckExpects(u);
}
@@ -167,7 +331,8 @@ class AppUpdateTest : public testing::Test {
}
if (state) {
- apps::AppUpdate::Merge(state, delta);
+ AppUpdate::Merge(state, delta);
+ EXPECT_EQ(expect_description_, state->description);
CheckExpects(u);
}
@@ -186,43 +351,548 @@ class AppUpdateTest : public testing::Test {
}
if (state) {
+ AppUpdate::Merge(state, delta);
+ EXPECT_EQ(expect_version_, state->version);
+ CheckExpects(u);
+ }
+
+ // AdditionalSearchTerms tests.
+
+ if (state) {
+ state->additional_search_terms.push_back("cat");
+ state->additional_search_terms.push_back("dog");
+ expect_additional_search_terms_.push_back("cat");
+ expect_additional_search_terms_.push_back("dog");
+ CheckExpects(u);
+ }
+
+ if (delta) {
+ expect_additional_search_terms_.clear();
+ delta->additional_search_terms.push_back("horse");
+ delta->additional_search_terms.push_back("mouse");
+ expect_additional_search_terms_.push_back("horse");
+ expect_additional_search_terms_.push_back("mouse");
+ CheckExpects(u);
+ }
+
+ if (state) {
apps::AppUpdate::Merge(state, delta);
+ EXPECT_EQ(expect_additional_search_terms_,
+ state->additional_search_terms);
CheckExpects(u);
}
// IconKey tests.
if (state) {
- state->icon_key = apps::IconKey(100, 0, 0);
- expect_icon_key_ = apps::IconKey(100, 0, 0);
+ state->icon_key = IconKey(100, 0, 0);
+ expect_icon_key_ = IconKey(100, 0, 0);
+ CheckExpects(u);
+ }
+
+ if (delta) {
+ delta->icon_key = IconKey(200, 0, 0);
+ expect_icon_key_ = IconKey(200, 0, 0);
+ CheckExpects(u);
+ }
+
+ if (state) {
+ AppUpdate::Merge(state, delta);
+ EXPECT_EQ(expect_icon_key_.value(), state->icon_key.value());
+ CheckExpects(u);
+ }
+
+ // LastLaunchTime tests.
+
+ if (state) {
+ state->last_launch_time = base::Time::FromDoubleT(1000.0);
+ expect_last_launch_time_ = base::Time::FromDoubleT(1000.0);
CheckExpects(u);
}
if (delta) {
- delta->icon_key = apps::IconKey(200, 0, 0);
- expect_icon_key_ = apps::IconKey(200, 0, 0);
+ delta->last_launch_time = base::Time::FromDoubleT(1001.0);
+ expect_last_launch_time_ = base::Time::FromDoubleT(1001.0);
CheckExpects(u);
}
if (state) {
apps::AppUpdate::Merge(state, delta);
+ EXPECT_EQ(expect_last_launch_time_, state->last_launch_time);
+ CheckExpects(u);
+ }
+
+ // InstallTime tests.
+
+ if (state) {
+ state->install_time = base::Time::FromDoubleT(2000.0);
+ expect_install_time_ = base::Time::FromDoubleT(2000.0);
+ CheckExpects(u);
+ }
+
+ if (delta) {
+ delta->install_time = base::Time::FromDoubleT(2001.0);
+ expect_install_time_ = base::Time::FromDoubleT(2001.0);
+ CheckExpects(u);
+ }
+
+ if (state) {
+ apps::AppUpdate::Merge(state, delta);
+ EXPECT_EQ(expect_install_time_, state->install_time);
+ CheckExpects(u);
+ }
+
+ // Permission tests.
+
+ if (state) {
+ auto p0 = MakePermission(PermissionType::kLocation, TriState::kAllow,
+ /*is_managed=*/true);
+ auto p1 = MakePermission(PermissionType::kNotifications, TriState::kBlock,
+ /*is_managed=*/false);
+ state->permissions.push_back(p0->Clone());
+ state->permissions.push_back(p1->Clone());
+ expect_permissions_.push_back(p0->Clone());
+ expect_permissions_.push_back(p1->Clone());
+ CheckExpects(u);
+ }
+
+ if (delta) {
+ expect_permissions_.clear();
+ auto p0 = MakePermission(PermissionType::kNotifications,
+ /*bool_value=*/true,
+ /*is_managed=*/false);
+ auto p1 = MakePermission(PermissionType::kLocation,
+ /*bool_value=*/false,
+ /*is_managed=*/true);
+ delta->permissions.push_back(p0->Clone());
+ delta->permissions.push_back(p1->Clone());
+ expect_permissions_.push_back(p0->Clone());
+ expect_permissions_.push_back(p1->Clone());
+ CheckExpects(u);
+ }
+
+ if (state) {
+ apps::AppUpdate::Merge(state, delta);
+ EXPECT_TRUE(IsEqual(expect_permissions_, state->permissions));
+ CheckExpects(u);
+ }
+
+ // InstallReason tests.
+
+ if (state) {
+ state->install_reason = InstallReason::kUser;
+ expect_install_reason_ = InstallReason::kUser;
+ CheckExpects(u);
+ }
+
+ if (delta) {
+ delta->install_reason = InstallReason::kPolicy;
+ expect_install_reason_ = InstallReason::kPolicy;
+ CheckExpects(u);
+ }
+
+ if (state) {
+ AppUpdate::Merge(state, delta);
+ EXPECT_EQ(expect_install_reason_, state->install_reason);
+ CheckExpects(u);
+ }
+
+ // InstallSource tests.
+
+ if (state) {
+ state->install_source = InstallSource::kPlayStore;
+ expect_install_source_ = InstallSource::kPlayStore;
+ CheckExpects(u);
+ }
+
+ if (delta) {
+ delta->install_source = InstallSource::kSync;
+ expect_install_source_ = InstallSource::kSync;
+ CheckExpects(u);
+ }
+
+ if (state) {
+ AppUpdate::Merge(state, delta);
+ EXPECT_EQ(expect_install_source_, state->install_source);
+ CheckExpects(u);
+ }
+
+ // PolicyId tests.
+
+ if (state) {
+ state->policy_id = "https://app.site/alpha";
+ expect_policy_id_ = "https://app.site/alpha";
+ CheckExpects(u);
+ }
+
+ if (delta) {
+ delta->policy_id = "https://app.site/delta";
+ expect_policy_id_ = "https://app.site/delta";
+ CheckExpects(u);
+ }
+
+ if (state) {
+ apps::AppUpdate::Merge(state, delta);
+ EXPECT_EQ(expect_policy_id_, state->policy_id);
+ CheckExpects(u);
+ }
+
+ // IsPlatformApp tests.
+
+ if (state) {
+ state->is_platform_app = false;
+ expect_is_platform_app_ = false;
+ CheckExpects(u);
+ }
+
+ if (delta) {
+ delta->is_platform_app = true;
+ expect_is_platform_app_ = true;
+ CheckExpects(u);
+ }
+
+ if (state) {
+ apps::AppUpdate::Merge(state, delta);
+ EXPECT_EQ(expect_is_platform_app_, state->is_platform_app);
+ CheckExpects(u);
+ }
+
+ // Recommendable tests.
+
+ if (state) {
+ state->recommendable = false;
+ expect_recommendable_ = false;
+ CheckExpects(u);
+ }
+
+ if (delta) {
+ delta->recommendable = true;
+ expect_recommendable_ = true;
+ CheckExpects(u);
+ }
+
+ if (state) {
+ apps::AppUpdate::Merge(state, delta);
+ EXPECT_EQ(expect_recommendable_, state->recommendable);
+ CheckExpects(u);
+ }
+
+ // Searchable tests.
+
+ if (state) {
+ state->searchable = false;
+ expect_searchable_ = false;
+ CheckExpects(u);
+ }
+
+ if (delta) {
+ delta->searchable = true;
+ expect_searchable_ = true;
+ CheckExpects(u);
+ }
+
+ if (state) {
+ apps::AppUpdate::Merge(state, delta);
+ EXPECT_EQ(expect_searchable_, state->searchable);
+ CheckExpects(u);
+ }
+
+ // ShowInLauncher tests.
+
+ if (state) {
+ state->show_in_launcher = false;
+ expect_show_in_launcher_ = false;
+ CheckExpects(u);
+ }
+
+ if (delta) {
+ delta->show_in_launcher = true;
+ expect_show_in_launcher_ = true;
+ CheckExpects(u);
+ }
+
+ if (state) {
+ apps::AppUpdate::Merge(state, delta);
+ EXPECT_EQ(expect_show_in_launcher_, state->show_in_launcher);
+ CheckExpects(u);
+ }
+
+ // ShowInShelf tests.
+
+ if (state) {
+ state->show_in_shelf = false;
+ expect_show_in_shelf_ = false;
+ CheckExpects(u);
+ }
+
+ if (delta) {
+ delta->show_in_shelf = true;
+ expect_show_in_shelf_ = true;
+ CheckExpects(u);
+ }
+
+ if (state) {
+ apps::AppUpdate::Merge(state, delta);
+ EXPECT_EQ(expect_show_in_shelf_, state->show_in_shelf);
+ CheckExpects(u);
+ }
+
+ // ShowInSearch tests.
+
+ if (state) {
+ state->show_in_search = false;
+ expect_show_in_search_ = false;
+ CheckExpects(u);
+ }
+
+ if (delta) {
+ delta->show_in_search = true;
+ expect_show_in_search_ = true;
+ CheckExpects(u);
+ }
+
+ if (state) {
+ apps::AppUpdate::Merge(state, delta);
+ EXPECT_EQ(expect_show_in_search_, state->show_in_search);
+ CheckExpects(u);
+ }
+
+ // ShowInManagement tests.
+
+ if (state) {
+ state->show_in_management = false;
+ expect_show_in_management_ = false;
+ CheckExpects(u);
+ }
+
+ if (delta) {
+ delta->show_in_management = true;
+ expect_show_in_management_ = true;
+ CheckExpects(u);
+ }
+
+ if (state) {
+ apps::AppUpdate::Merge(state, delta);
+ EXPECT_EQ(expect_show_in_management_, state->show_in_management);
+ CheckExpects(u);
+ }
+
+ // HandlesIntents tests.
+
+ if (state) {
+ state->handles_intents = false;
+ expect_handles_intents_ = false;
+ CheckExpects(u);
+ }
+
+ if (delta) {
+ delta->handles_intents = true;
+ expect_handles_intents_ = true;
+ CheckExpects(u);
+ }
+
+ if (state) {
+ apps::AppUpdate::Merge(state, delta);
+ EXPECT_EQ(expect_handles_intents_, state->handles_intents);
+ CheckExpects(u);
+ }
+
+ // AllowUninstall tests
+
+ if (state) {
+ state->allow_uninstall = false;
+ expect_allow_uninstall_ = false;
+ CheckExpects(u);
+ }
+
+ if (delta) {
+ delta->allow_uninstall = true;
+ expect_allow_uninstall_ = true;
+ CheckExpects(u);
+ }
+
+ if (state) {
+ apps::AppUpdate::Merge(state, delta);
+ EXPECT_EQ(expect_allow_uninstall_, state->allow_uninstall);
+ CheckExpects(u);
+ }
+
+ // HasBadge tests.
+
+ if (state) {
+ state->has_badge = false;
+ expect_has_badge_ = false;
+ CheckExpects(u);
+ }
+
+ if (delta) {
+ delta->has_badge = true;
+ expect_has_badge_ = true;
+ CheckExpects(u);
+ }
+
+ if (state) {
+ apps::AppUpdate::Merge(state, delta);
+ EXPECT_EQ(expect_has_badge_, state->has_badge);
+ CheckExpects(u);
+ }
+
+ // Pause tests.
+
+ if (state) {
+ state->paused = false;
+ expect_paused_ = false;
+ CheckExpects(u);
+ }
+
+ if (delta) {
+ delta->paused = true;
+ expect_paused_ = true;
+ CheckExpects(u);
+ }
+
+ if (state) {
+ apps::AppUpdate::Merge(state, delta);
+ EXPECT_EQ(expect_paused_, state->paused);
+ CheckExpects(u);
+ }
+
+ // Intent Filter tests.
+
+ if (state) {
+ IntentFilterPtr intent_filter = std::make_unique<IntentFilter>();
+
+ ConditionValues scheme_condition_values;
+ scheme_condition_values.push_back(
+ std::make_unique<ConditionValue>("https", PatternMatchType::kNone));
+ ConditionPtr scheme_condition = std::make_unique<Condition>(
+ ConditionType::kScheme, std::move(scheme_condition_values));
+
+ ConditionValues host_condition_values;
+ host_condition_values.push_back(std::make_unique<ConditionValue>(
+ "www.google.com", PatternMatchType::kNone));
+ auto host_condition = std::make_unique<Condition>(
+ ConditionType::kHost, std::move(host_condition_values));
+
+ intent_filter->conditions.push_back(std::move(scheme_condition));
+ intent_filter->conditions.push_back(std::move(host_condition));
+
+ state->intent_filters.push_back(intent_filter->Clone());
+ expect_intent_filters_.push_back(intent_filter->Clone());
+ CheckExpects(u);
+ }
+
+ if (delta) {
+ expect_intent_filters_.clear();
+
+ IntentFilterPtr intent_filter = std::make_unique<IntentFilter>();
+
+ ConditionValues scheme_condition_values;
+ scheme_condition_values.push_back(
+ std::make_unique<ConditionValue>("https", PatternMatchType::kNone));
+ ConditionPtr scheme_condition = std::make_unique<Condition>(
+ ConditionType::kScheme, std::move(scheme_condition_values));
+ intent_filter->conditions.push_back(scheme_condition->Clone());
+
+ ConditionValues host_condition_values;
+ host_condition_values.push_back(std::make_unique<ConditionValue>(
+ "www.abc.com", PatternMatchType::kNone));
+ auto host_condition = std::make_unique<Condition>(
+ ConditionType::kHost, std::move(host_condition_values));
+ intent_filter->conditions.push_back(host_condition->Clone());
+
+ intent_filter->conditions.push_back(std::move(scheme_condition));
+ intent_filter->conditions.push_back(std::move(host_condition));
+
+ delta->intent_filters.push_back(intent_filter->Clone());
+ expect_intent_filters_.push_back(intent_filter->Clone());
+ CheckExpects(u);
+ }
+
+ if (state) {
+ apps::AppUpdate::Merge(state, delta);
+ EXPECT_TRUE(IsEqual(expect_intent_filters_, state->intent_filters));
+ CheckExpects(u);
+ }
+
+ // ResizeLocked tests.
+
+ if (state) {
+ state->resize_locked = false;
+ expect_resize_locked_ = false;
+ CheckExpects(u);
+ }
+
+ if (delta) {
+ delta->resize_locked = true;
+ expect_resize_locked_ = true;
+ CheckExpects(u);
+ }
+
+ if (state) {
+ apps::AppUpdate::Merge(state, delta);
+ EXPECT_EQ(expect_resize_locked_, state->resize_locked);
+ CheckExpects(u);
+ }
+
+ // WindowMode tests.
+
+ if (state) {
+ state->window_mode = WindowMode::kBrowser;
+ expect_window_mode_ = WindowMode::kBrowser;
+ CheckExpects(u);
+ }
+
+ if (delta) {
+ delta->window_mode = WindowMode::kWindow;
+ expect_window_mode_ = WindowMode::kWindow;
+ CheckExpects(u);
+ }
+
+ if (state) {
+ apps::AppUpdate::Merge(state, delta);
+ EXPECT_EQ(expect_window_mode_, state->window_mode);
+ CheckExpects(u);
+ }
+
+ // RunOnOsLogin tests.
+
+ if (state) {
+ state->run_on_os_login = RunOnOsLogin(RunOnOsLoginMode::kNotRun, false);
+ expect_run_on_os_login_ = RunOnOsLogin(RunOnOsLoginMode::kNotRun, false);
+ CheckExpects(u);
+ }
+
+ if (delta) {
+ delta->run_on_os_login = RunOnOsLogin(RunOnOsLoginMode::kWindowed, false);
+ expect_run_on_os_login_ =
+ RunOnOsLogin(RunOnOsLoginMode::kWindowed, false);
+ CheckExpects(u);
+ }
+
+ if (state) {
+ AppUpdate::Merge(state, delta);
+ EXPECT_EQ(expect_run_on_os_login_.value(),
+ state->run_on_os_login.value());
CheckExpects(u);
}
}
};
TEST_F(AppUpdateTest, StateIsNonNull) {
- apps::App state(app_type, app_id);
+ App state(app_type, app_id);
TestAppUpdate(&state, nullptr);
}
TEST_F(AppUpdateTest, DeltaIsNonNull) {
- apps::App delta(app_type, app_id);
+ App delta(app_type, app_id);
TestAppUpdate(nullptr, &delta);
}
TEST_F(AppUpdateTest, BothAreNonNull) {
- apps::App state(app_type, app_id);
- apps::App delta(app_type, app_id);
+ App state(app_type, app_id);
+ App delta(app_type, app_id);
TestAppUpdate(&state, &delta);
}
+
+} // namespace apps
diff --git a/chromium/components/services/app_service/public/cpp/browser_app_instance_update.h b/chromium/components/services/app_service/public/cpp/browser_app_instance_update.h
index 37f7c25c493..dfaa7dd3783 100644
--- a/chromium/components/services/app_service/public/cpp/browser_app_instance_update.h
+++ b/chromium/components/services/app_service/public/cpp/browser_app_instance_update.h
@@ -27,8 +27,10 @@ struct BrowserAppInstanceUpdate {
std::string app_id;
std::string window_id;
std::string title;
- bool is_browser_active;
- bool is_web_contents_active;
+ bool is_browser_active = false;
+ bool is_web_contents_active = false;
+ uint32_t browser_session_id = 0;
+ uint32_t restored_browser_session_id = 0;
};
} // namespace apps
diff --git a/chromium/components/services/app_service/public/cpp/browser_window_instance_update.h b/chromium/components/services/app_service/public/cpp/browser_window_instance_update.h
index 4f0d3c1b504..83402c01707 100644
--- a/chromium/components/services/app_service/public/cpp/browser_window_instance_update.h
+++ b/chromium/components/services/app_service/public/cpp/browser_window_instance_update.h
@@ -12,7 +12,9 @@ namespace apps {
struct BrowserWindowInstanceUpdate {
base::UnguessableToken id;
std::string window_id;
- bool is_active;
+ bool is_active = false;
+ uint32_t browser_session_id = 0;
+ uint32_t restored_browser_session_id = 0;
};
} // namespace apps
diff --git a/chromium/components/services/app_service/public/cpp/features.cc b/chromium/components/services/app_service/public/cpp/features.cc
new file mode 100644
index 00000000000..af71190a101
--- /dev/null
+++ b/chromium/components/services/app_service/public/cpp/features.cc
@@ -0,0 +1,16 @@
+// Copyright 2022 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 "components/services/app_service/public/cpp/features.h"
+
+namespace apps {
+
+const base::Feature kAppServiceOnAppTypeInitializedWithoutMojom{
+ "AppServiceOnAppTypeInitializedWithoutMojom",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+
+const base::Feature kAppServiceOnAppUpdateWithoutMojom{
+ "AppServiceOnAppUpdateWithoutMojom", base::FEATURE_DISABLED_BY_DEFAULT};
+
+} // namespace apps
diff --git a/chromium/components/services/app_service/public/cpp/features.h b/chromium/components/services/app_service/public/cpp/features.h
new file mode 100644
index 00000000000..dcc3bbc7c41
--- /dev/null
+++ b/chromium/components/services/app_service/public/cpp/features.h
@@ -0,0 +1,19 @@
+// Copyright 2022 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.
+
+#ifndef COMPONENTS_SERVICES_APP_SERVICE_PUBLIC_CPP_FEATURES_H_
+#define COMPONENTS_SERVICES_APP_SERVICE_PUBLIC_CPP_FEATURES_H_
+
+#include "base/component_export.h"
+#include "base/feature_list.h"
+
+namespace apps {
+
+COMPONENT_EXPORT(APP_UPDATE)
+extern const base::Feature kAppServiceOnAppTypeInitializedWithoutMojom;
+extern const base::Feature kAppServiceOnAppUpdateWithoutMojom;
+
+} // namespace apps
+
+#endif // COMPONENTS_SERVICES_APP_SERVICE_PUBLIC_CPP_FEATURES_H_
diff --git a/chromium/components/services/app_service/public/cpp/icon_loader.h b/chromium/components/services/app_service/public/cpp/icon_loader.h
index 8da5708cc0e..37f7e89b49d 100644
--- a/chromium/components/services/app_service/public/cpp/icon_loader.h
+++ b/chromium/components/services/app_service/public/cpp/icon_loader.h
@@ -58,7 +58,8 @@ class IconLoader {
virtual absl::optional<IconKey> GetIconKey(const std::string& app_id);
// This can return nullptr, meaning that the IconLoader does not track when
- // the icon is no longer actively used by the caller.
+ // the icon is no longer actively used by the caller. `callback` may be
+ // dispatched synchronously if it's possible to quickly return a result.
virtual std::unique_ptr<Releaser> LoadIconFromIconKey(
AppType app_type,
const std::string& app_id,
@@ -69,7 +70,8 @@ class IconLoader {
apps::LoadIconCallback callback) = 0;
// Convenience method that calls "LoadIconFromIconKey(app_type, app_id,
- // GetIconKey(app_id), etc)".
+ // GetIconKey(app_id), etc)". `callback` may be dispatched synchronously if
+ // it's possible to quickly return a result.
std::unique_ptr<Releaser> LoadIcon(AppType app_type,
const std::string& app_id,
const IconType& icon_type,
diff --git a/chromium/components/services/app_service/public/cpp/icon_types.h b/chromium/components/services/app_service/public/cpp/icon_types.h
index 5391b835c4f..7d89bdd2b22 100644
--- a/chromium/components/services/app_service/public/cpp/icon_types.h
+++ b/chromium/components/services/app_service/public/cpp/icon_types.h
@@ -7,6 +7,7 @@
#include <vector>
+#include "base/component_export.h"
#include "components/services/app_service/public/mojom/types.mojom.h"
#include "ui/gfx/image/image_skia.h"
@@ -60,6 +61,8 @@ struct COMPONENT_EXPORT(ICON_TYPES) IconKey {
// components/services/app_service/public/cpp/icon_loader.*
};
+using IconKeyPtr = std::unique_ptr<IconKey>;
+
enum class IconType {
// Sentinel value used in error cases.
kUnknown,
diff --git a/chromium/components/services/app_service/public/cpp/instance_update.cc b/chromium/components/services/app_service/public/cpp/instance_update.cc
index 028b19cb5e5..1bc5e1b53c2 100644
--- a/chromium/components/services/app_service/public/cpp/instance_update.cc
+++ b/chromium/components/services/app_service/public/cpp/instance_update.cc
@@ -125,7 +125,8 @@ bool InstanceUpdate::WindowChanged() const {
}
const std::string& InstanceUpdate::LaunchId() const {
- GET_VALUE_WITH_CHECK_AND_DEFAULT_RETURN(LaunchId, empty, base::EmptyString());
+ GET_VALUE_WITH_CHECK_AND_DEFAULT_RETURN(LaunchId(), empty,
+ base::EmptyString());
}
bool InstanceUpdate::LaunchIdChanged() const {
@@ -133,7 +134,7 @@ bool InstanceUpdate::LaunchIdChanged() const {
}
InstanceState InstanceUpdate::State() const {
- GET_VALUE_WITH_DEFAULT_VALUE(State, InstanceState::kUnknown);
+ GET_VALUE_WITH_DEFAULT_VALUE(State(), InstanceState::kUnknown);
}
bool InstanceUpdate::StateChanged() const {
@@ -141,7 +142,7 @@ bool InstanceUpdate::StateChanged() const {
}
base::Time InstanceUpdate::LastUpdatedTime() const {
- GET_VALUE_WITH_CHECK_AND_DEFAULT_RETURN(LastUpdatedTime, is_null,
+ GET_VALUE_WITH_CHECK_AND_DEFAULT_RETURN(LastUpdatedTime(), is_null,
base::Time());
}
diff --git a/chromium/components/services/app_service/public/cpp/intent_filter.cc b/chromium/components/services/app_service/public/cpp/intent_filter.cc
new file mode 100644
index 00000000000..c58fbbac959
--- /dev/null
+++ b/chromium/components/services/app_service/public/cpp/intent_filter.cc
@@ -0,0 +1,318 @@
+// Copyright 2022 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 "components/services/app_service/public/cpp/intent_filter.h"
+
+namespace apps {
+
+ConditionValue::ConditionValue(const std::string& value,
+ PatternMatchType match_type)
+ : value(value), match_type(match_type) {}
+
+ConditionValue::~ConditionValue() = default;
+
+bool ConditionValue::operator==(const ConditionValue& other) const {
+ return value == other.value && match_type == other.match_type;
+}
+
+bool ConditionValue::operator!=(const ConditionValue& other) const {
+ return !(*this == other);
+}
+
+Condition::Condition(ConditionType condition_type,
+ ConditionValues condition_values)
+ : condition_type(condition_type),
+ condition_values(std::move(condition_values)) {}
+
+Condition::~Condition() = default;
+
+bool Condition::operator==(const Condition& other) const {
+ if (condition_values.size() != other.condition_values.size()) {
+ return false;
+ }
+
+ for (int i = 0; i < static_cast<int>(condition_values.size()); i++) {
+ if (*condition_values[i] != *other.condition_values[i]) {
+ return false;
+ }
+ }
+
+ return condition_type == other.condition_type;
+}
+
+bool Condition::operator!=(const Condition& other) const {
+ return !(*this == other);
+}
+
+ConditionPtr Condition::Clone() const {
+ ConditionValues values;
+ for (const auto& condition_value : condition_values) {
+ values.push_back(std::make_unique<ConditionValue>(
+ condition_value->value, condition_value->match_type));
+ }
+
+ return std::make_unique<Condition>(condition_type, std::move(values));
+}
+
+IntentFilter::IntentFilter() = default;
+IntentFilter::~IntentFilter() = default;
+
+bool IntentFilter::operator==(const IntentFilter& other) const {
+ if (conditions.size() != other.conditions.size()) {
+ return false;
+ }
+
+ for (int i = 0; i < static_cast<int>(conditions.size()); i++) {
+ if (*conditions[i] != *other.conditions[i]) {
+ return false;
+ }
+ }
+
+ return activity_name == other.activity_name &&
+ activity_label == other.activity_label;
+}
+
+bool IntentFilter::operator!=(const IntentFilter& other) const {
+ return !(*this == other);
+}
+
+IntentFilterPtr IntentFilter::Clone() const {
+ IntentFilterPtr intent_filter = std::make_unique<IntentFilter>();
+
+ for (const auto& condition : conditions) {
+ intent_filter->conditions.push_back(condition->Clone());
+ }
+
+ if (activity_name.has_value())
+ intent_filter->activity_name = activity_name.value();
+
+ if (activity_label.has_value())
+ intent_filter->activity_label = activity_label.value();
+
+ return intent_filter;
+}
+
+IntentFilters CloneIntentFilters(const IntentFilters& intent_filters) {
+ IntentFilters ret;
+ for (const auto& intent_filter : intent_filters) {
+ ret.push_back(intent_filter->Clone());
+ }
+ return ret;
+}
+
+ConditionType ConvertMojomConditionTypeToConditionType(
+ const apps::mojom::ConditionType& mojom_condition_type) {
+ switch (mojom_condition_type) {
+ case apps::mojom::ConditionType::kScheme:
+ return ConditionType::kScheme;
+ case apps::mojom::ConditionType::kHost:
+ return ConditionType::kHost;
+ case apps::mojom::ConditionType::kPattern:
+ return ConditionType::kPattern;
+ case apps::mojom::ConditionType::kAction:
+ return ConditionType::kAction;
+ case apps::mojom::ConditionType::kMimeType:
+ return ConditionType::kMimeType;
+ case apps::mojom::ConditionType::kFile:
+ return ConditionType::kFile;
+ }
+}
+
+apps::mojom::ConditionType ConvertConditionTypeToMojomConditionType(
+ const ConditionType& condition_type) {
+ switch (condition_type) {
+ case ConditionType::kScheme:
+ return apps::mojom::ConditionType::kScheme;
+ case ConditionType::kHost:
+ return apps::mojom::ConditionType::kHost;
+ case ConditionType::kPattern:
+ return apps::mojom::ConditionType::kPattern;
+ case ConditionType::kAction:
+ return apps::mojom::ConditionType::kAction;
+ case ConditionType::kMimeType:
+ return apps::mojom::ConditionType::kMimeType;
+ case ConditionType::kFile:
+ return apps::mojom::ConditionType::kFile;
+ }
+}
+
+PatternMatchType ConvertMojomPatternMatchTypeToPatternMatchType(
+ const apps::mojom::PatternMatchType& mojom_pattern_match_type) {
+ switch (mojom_pattern_match_type) {
+ case apps::mojom::PatternMatchType::kNone:
+ return PatternMatchType::kNone;
+ case apps::mojom::PatternMatchType::kLiteral:
+ return PatternMatchType::kLiteral;
+ case apps::mojom::PatternMatchType::kPrefix:
+ return PatternMatchType::kPrefix;
+ case apps::mojom::PatternMatchType::kGlob:
+ return PatternMatchType::kGlob;
+ case apps::mojom::PatternMatchType::kMimeType:
+ return PatternMatchType::kMimeType;
+ case apps::mojom::PatternMatchType::kFileExtension:
+ return PatternMatchType::kFileExtension;
+ case apps::mojom::PatternMatchType::kIsDirectory:
+ return PatternMatchType::kIsDirectory;
+ }
+}
+
+apps::mojom::PatternMatchType ConvertPatternMatchTypeToMojomPatternMatchType(
+ const PatternMatchType& pattern_match_type) {
+ switch (pattern_match_type) {
+ case PatternMatchType::kNone:
+ return apps::mojom::PatternMatchType::kNone;
+ case PatternMatchType::kLiteral:
+ return apps::mojom::PatternMatchType::kLiteral;
+ case PatternMatchType::kPrefix:
+ return apps::mojom::PatternMatchType::kPrefix;
+ case PatternMatchType::kGlob:
+ return apps::mojom::PatternMatchType::kGlob;
+ case PatternMatchType::kMimeType:
+ return apps::mojom::PatternMatchType::kMimeType;
+ case PatternMatchType::kFileExtension:
+ return apps::mojom::PatternMatchType::kFileExtension;
+ case PatternMatchType::kIsDirectory:
+ return apps::mojom::PatternMatchType::kIsDirectory;
+ }
+}
+
+ConditionValuePtr ConvertMojomConditionValueToConditionValue(
+ const apps::mojom::ConditionValuePtr& mojom_condition_value) {
+ if (!mojom_condition_value) {
+ return nullptr;
+ }
+
+ ConditionValuePtr condition_value = std::make_unique<ConditionValue>(
+ mojom_condition_value->value,
+ ConvertMojomPatternMatchTypeToPatternMatchType(
+ mojom_condition_value->match_type));
+ return condition_value;
+}
+
+apps::mojom::ConditionValuePtr ConvertConditionValueToMojomConditionValue(
+ const ConditionValuePtr& condition_value) {
+ auto mojom_condition_value = apps::mojom::ConditionValue::New();
+ if (!condition_value) {
+ return mojom_condition_value;
+ }
+
+ mojom_condition_value->value = condition_value->value;
+ mojom_condition_value->match_type =
+ ConvertPatternMatchTypeToMojomPatternMatchType(
+ condition_value->match_type);
+ return mojom_condition_value;
+}
+
+ConditionPtr ConvertMojomConditionToCondition(
+ const apps::mojom::ConditionPtr& mojom_condition) {
+ if (!mojom_condition) {
+ return nullptr;
+ }
+
+ ConditionValues values;
+ for (const auto& condition_value : mojom_condition->condition_values) {
+ values.push_back(
+ ConvertMojomConditionValueToConditionValue(condition_value));
+ }
+ return std::make_unique<Condition>(
+ ConvertMojomConditionTypeToConditionType(mojom_condition->condition_type),
+ std::move(values));
+}
+
+apps::mojom::ConditionPtr ConvertConditionToMojomCondition(
+ const ConditionPtr& condition) {
+ auto mojom_condition = apps::mojom::Condition::New();
+ if (!condition) {
+ return mojom_condition;
+ }
+
+ mojom_condition->condition_type =
+ ConvertConditionTypeToMojomConditionType(condition->condition_type);
+
+ for (const auto& condition_value : condition->condition_values) {
+ if (condition_value) {
+ mojom_condition->condition_values.push_back(
+ ConvertConditionValueToMojomConditionValue(condition_value));
+ }
+ }
+ return mojom_condition;
+}
+
+IntentFilterPtr ConvertMojomIntentFilterToIntentFilter(
+ const apps::mojom::IntentFilterPtr& mojom_intent_filter) {
+ if (!mojom_intent_filter) {
+ return nullptr;
+ }
+
+ IntentFilterPtr intent_filter = std::make_unique<IntentFilter>();
+ for (const auto& condition : mojom_intent_filter->conditions) {
+ if (condition) {
+ intent_filter->conditions.push_back(
+ ConvertMojomConditionToCondition(condition));
+ }
+ }
+
+ if (mojom_intent_filter->activity_name.has_value())
+ intent_filter->activity_name = mojom_intent_filter->activity_name.value();
+
+ if (mojom_intent_filter->activity_label.has_value())
+ intent_filter->activity_label = mojom_intent_filter->activity_label.value();
+
+ return intent_filter;
+}
+
+apps::mojom::IntentFilterPtr ConvertIntentFilterToMojomIntentFilter(
+ const IntentFilterPtr& intent_filter) {
+ auto mojom_intent_filter = apps::mojom::IntentFilter::New();
+ if (!intent_filter) {
+ return mojom_intent_filter;
+ }
+
+ for (const auto& condition : intent_filter->conditions) {
+ if (condition) {
+ mojom_intent_filter->conditions.push_back(
+ ConvertConditionToMojomCondition(condition));
+ }
+ }
+
+ mojom_intent_filter->activity_name = intent_filter->activity_name;
+ mojom_intent_filter->activity_label = intent_filter->activity_label;
+ return mojom_intent_filter;
+}
+
+base::flat_map<std::string, std::vector<apps::mojom::IntentFilterPtr>>
+ConvertIntentFiltersToMojomIntentFilters(
+ const base::flat_map<std::string, apps::IntentFilters>& intent_filter) {
+ base::flat_map<std::string, std::vector<apps::mojom::IntentFilterPtr>> ret;
+ for (const auto& it : intent_filter) {
+ std::vector<apps::mojom::IntentFilterPtr> mojom_filters;
+ for (const auto& filter_it : it.second) {
+ if (filter_it) {
+ mojom_filters.push_back(
+ ConvertIntentFilterToMojomIntentFilter(filter_it));
+ }
+ }
+ ret[it.first] = std::move(mojom_filters);
+ }
+ return ret;
+}
+
+base::flat_map<std::string, apps::IntentFilters>
+ConvertMojomIntentFiltersToIntentFilters(
+ const base::flat_map<std::string,
+ std::vector<apps::mojom::IntentFilterPtr>>&
+ mojom_intent_filter) {
+ base::flat_map<std::string, apps::IntentFilters> ret;
+ for (const auto& it : mojom_intent_filter) {
+ apps::IntentFilters filters;
+ for (const auto& filter_it : it.second) {
+ if (filter_it)
+ filters.push_back(ConvertMojomIntentFilterToIntentFilter(filter_it));
+ }
+ ret[it.first] = std::move(filters);
+ }
+ return ret;
+}
+
+} // namespace apps
diff --git a/chromium/components/services/app_service/public/cpp/intent_filter.h b/chromium/components/services/app_service/public/cpp/intent_filter.h
new file mode 100644
index 00000000000..7b1ba9bf476
--- /dev/null
+++ b/chromium/components/services/app_service/public/cpp/intent_filter.h
@@ -0,0 +1,167 @@
+// Copyright 2022 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.
+
+#ifndef COMPONENTS_SERVICES_APP_SERVICE_PUBLIC_CPP_INTENT_FILTER_H_
+#define COMPONENTS_SERVICES_APP_SERVICE_PUBLIC_CPP_INTENT_FILTER_H_
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/component_export.h"
+#include "base/containers/flat_map.h"
+#include "components/services/app_service/public/mojom/types.mojom.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace apps {
+
+// The intent filter matching condition types.
+enum class ConditionType {
+ kScheme, // Matches the URL scheme (e.g. https, tel).
+ kHost, // Matches the URL host (e.g. www.google.com).
+ kPattern, // Matches the URL pattern (e.g. /abc/*).
+ kAction, // Matches the action type (e.g. view, send).
+ kMimeType, // Matches the top-level mime type (e.g. text/plain).
+ kFile, // Matches against all files.
+};
+
+// The pattern match type for intent filter pattern condition.
+enum class PatternMatchType {
+ kNone = 0,
+ kLiteral,
+ kPrefix,
+ kGlob,
+ kMimeType,
+ kFileExtension,
+ kIsDirectory,
+};
+
+// For pattern type of condition, the value match will be based on the pattern
+// match type. If the match_type is kNone, then an exact match with the value
+// will be required.
+struct COMPONENT_EXPORT(APP_TYPES) ConditionValue {
+ ConditionValue(const std::string& value, PatternMatchType match_type);
+ ConditionValue(const ConditionValue&) = delete;
+ ConditionValue& operator=(const ConditionValue&) = delete;
+ ~ConditionValue();
+
+ bool operator==(const ConditionValue& other) const;
+ bool operator!=(const ConditionValue& other) const;
+
+ std::string value;
+ PatternMatchType match_type; // This will be None for non pattern conditions.
+};
+
+using ConditionValuePtr = std::unique_ptr<ConditionValue>;
+using ConditionValues = std::vector<ConditionValuePtr>;
+
+// The condition for an intent filter. It matches if the intent contains this
+// condition type and the corresponding value matches with any of the
+// condition_values.
+struct COMPONENT_EXPORT(APP_TYPES) Condition {
+ Condition(ConditionType condition_type, ConditionValues condition_values);
+ Condition(const Condition&) = delete;
+ Condition& operator=(const Condition&) = delete;
+ ~Condition();
+
+ bool operator==(const Condition& other) const;
+ bool operator!=(const Condition& other) const;
+
+ std::unique_ptr<Condition> Clone() const;
+
+ ConditionType condition_type;
+ ConditionValues condition_values;
+};
+
+using ConditionPtr = std::unique_ptr<Condition>;
+using Conditions = std::vector<ConditionPtr>;
+
+// An intent filter is defined by an app, and contains a list of conditions that
+// an intent needs to match. If all conditions match, then this intent filter
+// matches against an intent.
+struct COMPONENT_EXPORT(APP_TYPES) IntentFilter {
+ IntentFilter();
+ IntentFilter(const IntentFilter&) = delete;
+ IntentFilter& operator=(const IntentFilter&) = delete;
+ ~IntentFilter();
+
+ bool operator==(const IntentFilter& other) const;
+ bool operator!=(const IntentFilter& other) const;
+
+ std::unique_ptr<IntentFilter> Clone() const;
+
+ Conditions conditions;
+
+ // Activity which registered this filter. We only fill this field for ARC
+ // share intent filters and Web App file_handlers.
+ absl::optional<std::string> activity_name;
+
+ // The label shown to the user for this activity.
+ absl::optional<std::string> activity_label;
+};
+
+using IntentFilterPtr = std::unique_ptr<IntentFilter>;
+using IntentFilters = std::vector<IntentFilterPtr>;
+
+// Creates a deep copy of `intent_filters`.
+COMPONENT_EXPORT(APP_TYPES)
+IntentFilters CloneIntentFilters(const IntentFilters& intent_filters);
+
+// TODO(crbug.com/1253250): Remove these functions after migrating to non-mojo
+// AppService.
+COMPONENT_EXPORT(APP_TYPES)
+ConditionType ConvertMojomConditionTypeToConditionType(
+ const apps::mojom::ConditionType& mojom_condition_type);
+
+COMPONENT_EXPORT(APP_TYPES)
+apps::mojom::ConditionType ConvertConditionTypeToMojomConditionType(
+ const ConditionType& condition_type);
+
+COMPONENT_EXPORT(APP_TYPES)
+PatternMatchType ConvertMojomPatternMatchTypeToPatternMatchType(
+ const apps::mojom::PatternMatchType& mojom_pattern_match_type);
+
+COMPONENT_EXPORT(APP_TYPES)
+apps::mojom::PatternMatchType ConvertPatternMatchTypeToMojomPatternMatchType(
+ const PatternMatchType& pattern_match_type);
+
+COMPONENT_EXPORT(APP_TYPES)
+ConditionValuePtr ConvertMojomConditionValueToConditionValue(
+ const apps::mojom::ConditionValuePtr& mojom_condition_value);
+
+COMPONENT_EXPORT(APP_TYPES)
+apps::mojom::ConditionValuePtr ConvertConditionValueToMojomConditionValue(
+ const ConditionValuePtr& condition_value);
+
+COMPONENT_EXPORT(APP_TYPES)
+ConditionPtr ConvertMojomConditionToCondition(
+ const apps::mojom::ConditionPtr& mojom_condition);
+
+COMPONENT_EXPORT(APP_TYPES)
+apps::mojom::ConditionPtr ConvertConditionToMojomCondition(
+ const ConditionPtr& condition);
+
+COMPONENT_EXPORT(APP_TYPES)
+IntentFilterPtr ConvertMojomIntentFilterToIntentFilter(
+ const apps::mojom::IntentFilterPtr& mojom_intent_filter);
+
+COMPONENT_EXPORT(APP_TYPES)
+apps::mojom::IntentFilterPtr ConvertIntentFilterToMojomIntentFilter(
+ const IntentFilterPtr& intent_filter);
+
+COMPONENT_EXPORT(APP_TYPES)
+base::flat_map<std::string, std::vector<apps::mojom::IntentFilterPtr>>
+ConvertIntentFiltersToMojomIntentFilters(
+ const base::flat_map<std::string, apps::IntentFilters>& intent_filter);
+
+COMPONENT_EXPORT(APP_TYPES)
+base::flat_map<std::string, apps::IntentFilters>
+ConvertMojomIntentFiltersToIntentFilters(
+ const base::flat_map<std::string,
+ std::vector<apps::mojom::IntentFilterPtr>>&
+ mojom_intent_filter);
+
+} // namespace apps
+
+#endif // COMPONENTS_SERVICES_APP_SERVICE_PUBLIC_CPP_INTENT_FILTER_H_
diff --git a/chromium/components/services/app_service/public/cpp/intent_filter_util.cc b/chromium/components/services/app_service/public/cpp/intent_filter_util.cc
index ca849f67af7..811cb6fb04c 100644
--- a/chromium/components/services/app_service/public/cpp/intent_filter_util.cc
+++ b/chromium/components/services/app_service/public/cpp/intent_filter_util.cc
@@ -84,6 +84,17 @@ apps::mojom::ConditionPtr MakeCondition(
return condition;
}
+void AddSingleValueCondition(apps::ConditionType condition_type,
+ const std::string& value,
+ apps::PatternMatchType pattern_match_type,
+ apps::IntentFilterPtr& intent_filter) {
+ apps::ConditionValues condition_values;
+ condition_values.push_back(
+ std::make_unique<apps::ConditionValue>(value, pattern_match_type));
+ intent_filter->conditions.push_back(std::make_unique<apps::Condition>(
+ condition_type, std::move(condition_values)));
+}
+
void AddSingleValueCondition(apps::mojom::ConditionType condition_type,
const std::string& value,
apps::mojom::PatternMatchType pattern_match_type,
diff --git a/chromium/components/services/app_service/public/cpp/intent_filter_util.h b/chromium/components/services/app_service/public/cpp/intent_filter_util.h
index 8624079f7a6..e4ec59576ad 100644
--- a/chromium/components/services/app_service/public/cpp/intent_filter_util.h
+++ b/chromium/components/services/app_service/public/cpp/intent_filter_util.h
@@ -9,6 +9,7 @@
#include <string>
+#include "components/services/app_service/public/cpp/intent_filter.h"
#include "components/services/app_service/public/mojom/types.mojom.h"
#include "url/gurl.h"
@@ -47,6 +48,12 @@ apps::mojom::ConditionPtr MakeCondition(
// Creates condition that only contain one value and add the condition to
// the intent filter.
+void AddSingleValueCondition(apps::ConditionType condition_type,
+ const std::string& value,
+ apps::PatternMatchType pattern_match_type,
+ apps::IntentFilterPtr& intent_filter);
+
+// TODO(crbug.com/1253250): Remove after migrating to non-mojo AppService.
void AddSingleValueCondition(apps::mojom::ConditionType condition_type,
const std::string& value,
apps::mojom::PatternMatchType pattern_match_type,
diff --git a/chromium/components/services/app_service/public/cpp/intent_filter_util_unittest.cc b/chromium/components/services/app_service/public/cpp/intent_filter_util_unittest.cc
index 190ad7d3570..bb6e1140105 100644
--- a/chromium/components/services/app_service/public/cpp/intent_filter_util_unittest.cc
+++ b/chromium/components/services/app_service/public/cpp/intent_filter_util_unittest.cc
@@ -5,6 +5,7 @@
#include "components/services/app_service/public/cpp/intent_filter_util.h"
#include "base/values.h"
+#include "components/services/app_service/public/cpp/intent_filter.h"
#include "components/services/app_service/public/cpp/intent_test_util.h"
#include "components/services/app_service/public/cpp/intent_util.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -370,3 +371,108 @@ TEST_F(IntentFilterUtilTest, PatternGlobAndLiteralOverlap) {
ASSERT_FALSE(apps_util::FiltersHaveOverlap(literal_pattern_filter2,
glob_pattern_filter));
}
+
+TEST_F(IntentFilterUtilTest, IntentFiltersConvert) {
+ base::flat_map<std::string, std::vector<apps::IntentFilterPtr>> filters;
+
+ auto intent_filter1 = std::make_unique<apps::IntentFilter>();
+ apps_util::AddSingleValueCondition(apps::ConditionType::kScheme, "1",
+ apps::PatternMatchType::kNone,
+ intent_filter1);
+ filters["1"].push_back(std::move(intent_filter1));
+
+ auto intent_filter2 = std::make_unique<apps::IntentFilter>();
+ apps_util::AddSingleValueCondition(apps::ConditionType::kHost, "2",
+ apps::PatternMatchType::kLiteral,
+ intent_filter2);
+ apps_util::AddSingleValueCondition(apps::ConditionType::kPattern, "3",
+ apps::PatternMatchType::kPrefix,
+ intent_filter2);
+ filters["1"].push_back(std::move(intent_filter2));
+
+ apps::IntentFilters intent_filters2;
+ auto intent_filter3 = std::make_unique<apps::IntentFilter>();
+ apps_util::AddSingleValueCondition(apps::ConditionType::kAction, "4",
+ apps::PatternMatchType::kGlob,
+ intent_filter3);
+ apps_util::AddSingleValueCondition(apps::ConditionType::kMimeType, "5",
+ apps::PatternMatchType::kMimeType,
+ intent_filter3);
+ filters["2"].push_back(std::move(intent_filter3));
+
+ auto intent_filter4 = std::make_unique<apps::IntentFilter>();
+ apps_util::AddSingleValueCondition(apps::ConditionType::kFile, "6",
+ apps::PatternMatchType::kMimeType,
+ intent_filter4);
+ apps_util::AddSingleValueCondition(apps::ConditionType::kFile, "7",
+ apps::PatternMatchType::kFileExtension,
+ intent_filter4);
+ filters["2"].push_back(std::move(intent_filter4));
+
+ auto output = apps::ConvertMojomIntentFiltersToIntentFilters(
+ apps::ConvertIntentFiltersToMojomIntentFilters(filters));
+
+ ASSERT_EQ(output.size(), 2U);
+ EXPECT_EQ(*filters["1"][0], *output["1"][0]);
+ EXPECT_EQ(*filters["1"][1], *output["1"][1]);
+
+ EXPECT_EQ(*filters["2"][0], *output["2"][0]);
+ EXPECT_EQ(*filters["2"][1], *output["2"][1]);
+
+ {
+ auto& condition = output["1"][0]->conditions[0];
+ EXPECT_EQ(condition->condition_type, apps::ConditionType::kScheme);
+ ASSERT_EQ(condition->condition_values.size(), 1U);
+ EXPECT_EQ(condition->condition_values[0]->match_type,
+ apps::PatternMatchType::kNone);
+ EXPECT_EQ(condition->condition_values[0]->value, "1");
+ }
+ {
+ auto& condition = output["1"][1]->conditions[0];
+ EXPECT_EQ(condition->condition_type, apps::ConditionType::kHost);
+ ASSERT_EQ(condition->condition_values.size(), 1U);
+ EXPECT_EQ(condition->condition_values[0]->match_type,
+ apps::PatternMatchType::kLiteral);
+ EXPECT_EQ(condition->condition_values[0]->value, "2");
+ }
+ {
+ auto& condition = output["1"][1]->conditions[1];
+ EXPECT_EQ(condition->condition_type, apps::ConditionType::kPattern);
+ ASSERT_EQ(condition->condition_values.size(), 1U);
+ EXPECT_EQ(condition->condition_values[0]->match_type,
+ apps::PatternMatchType::kPrefix);
+ EXPECT_EQ(condition->condition_values[0]->value, "3");
+ }
+ {
+ auto& condition = output["2"][0]->conditions[0];
+ EXPECT_EQ(condition->condition_type, apps::ConditionType::kAction);
+ ASSERT_EQ(condition->condition_values.size(), 1U);
+ EXPECT_EQ(condition->condition_values[0]->match_type,
+ apps::PatternMatchType::kGlob);
+ EXPECT_EQ(condition->condition_values[0]->value, "4");
+ }
+ {
+ auto& condition = output["2"][0]->conditions[1];
+ EXPECT_EQ(condition->condition_type, apps::ConditionType::kMimeType);
+ ASSERT_EQ(condition->condition_values.size(), 1U);
+ EXPECT_EQ(condition->condition_values[0]->match_type,
+ apps::PatternMatchType::kMimeType);
+ EXPECT_EQ(condition->condition_values[0]->value, "5");
+ }
+ {
+ auto& condition = output["2"][1]->conditions[0];
+ EXPECT_EQ(condition->condition_type, apps::ConditionType::kFile);
+ ASSERT_EQ(condition->condition_values.size(), 1U);
+ EXPECT_EQ(condition->condition_values[0]->match_type,
+ apps::PatternMatchType::kMimeType);
+ EXPECT_EQ(condition->condition_values[0]->value, "6");
+ }
+ {
+ auto& condition = output["2"][1]->conditions[1];
+ EXPECT_EQ(condition->condition_type, apps::ConditionType::kFile);
+ ASSERT_EQ(condition->condition_values.size(), 1U);
+ EXPECT_EQ(condition->condition_values[0]->match_type,
+ apps::PatternMatchType::kFileExtension);
+ EXPECT_EQ(condition->condition_values[0]->value, "7");
+ }
+}
diff --git a/chromium/components/services/app_service/public/cpp/intent_util.cc b/chromium/components/services/app_service/public/cpp/intent_util.cc
index d08a4483572..57d426c8753 100644
--- a/chromium/components/services/app_service/public/cpp/intent_util.cc
+++ b/chromium/components/services/app_service/public/cpp/intent_util.cc
@@ -610,22 +610,20 @@ base::Value ConvertIntentToValue(const apps::mojom::IntentPtr& intent) {
absl::optional<std::string> GetStringValueFromDict(
const base::DictionaryValue& dict,
const std::string& key_name) {
- if (!dict.HasKey(key_name))
+ const base::Value* value = dict.FindKey(key_name);
+ if (!value)
return absl::nullopt;
- const std::string* value = dict.FindStringKey(key_name);
- if (!value || value->empty())
+ const std::string* string_value = value->GetIfString();
+ if (!string_value || string_value->empty())
return absl::nullopt;
- return *value;
+ return *string_value;
}
apps::mojom::OptionalBool GetBoolValueFromDict(
const base::DictionaryValue& dict,
const std::string& key_name) {
- if (!dict.HasKey(key_name))
- return apps::mojom::OptionalBool::kUnknown;
-
absl::optional<bool> value = dict.FindBoolKey(key_name);
if (!value.has_value())
return apps::mojom::OptionalBool::kUnknown;
@@ -636,9 +634,6 @@ apps::mojom::OptionalBool GetBoolValueFromDict(
absl::optional<GURL> GetGurlValueFromDict(const base::DictionaryValue& dict,
const std::string& key_name) {
- if (!dict.HasKey(key_name))
- return absl::nullopt;
-
const std::string* url_spec = dict.FindStringKey(key_name);
if (!url_spec)
return absl::nullopt;
@@ -653,15 +648,12 @@ absl::optional<GURL> GetGurlValueFromDict(const base::DictionaryValue& dict,
absl::optional<std::vector<apps::mojom::IntentFilePtr>> GetFilesFromDict(
const base::DictionaryValue& dict,
const std::string& key_name) {
- if (!dict.HasKey(key_name))
- return absl::nullopt;
-
const base::Value* value = dict.FindListKey(key_name);
- if (!value || !value->is_list() || value->GetList().empty())
+ if (!value || !value->is_list() || value->GetListDeprecated().empty())
return absl::nullopt;
std::vector<apps::mojom::IntentFilePtr> files;
- for (const auto& item : value->GetList()) {
+ for (const auto& item : value->GetListDeprecated()) {
GURL url(item.GetString());
if (url.is_valid()) {
auto file = apps::mojom::IntentFile::New();
@@ -675,15 +667,12 @@ absl::optional<std::vector<apps::mojom::IntentFilePtr>> GetFilesFromDict(
absl::optional<std::vector<std::string>> GetCategoriesFromDict(
const base::DictionaryValue& dict,
const std::string& key_name) {
- if (!dict.HasKey(key_name))
- return absl::nullopt;
-
const base::Value* value = dict.FindListKey(key_name);
- if (!value || !value->is_list() || value->GetList().empty())
+ if (!value || !value->is_list() || value->GetListDeprecated().empty())
return absl::nullopt;
std::vector<std::string> categories;
- for (const auto& item : value->GetList())
+ for (const auto& item : value->GetListDeprecated())
categories.push_back(item.GetString());
return categories;
@@ -692,9 +681,6 @@ absl::optional<std::vector<std::string>> GetCategoriesFromDict(
absl::optional<base::flat_map<std::string, std::string>> GetExtrasFromDict(
const base::DictionaryValue& dict,
const std::string& key_name) {
- if (!dict.HasKey(key_name))
- return absl::nullopt;
-
const base::Value* value = dict.FindDictKey(key_name);
if (!value || !value->is_dict())
return absl::nullopt;
diff --git a/chromium/components/services/app_service/public/cpp/macros.h b/chromium/components/services/app_service/public/cpp/macros.h
index 9cef7b39213..6e7d30d9844 100644
--- a/chromium/components/services/app_service/public/cpp/macros.h
+++ b/chromium/components/services/app_service/public/cpp/macros.h
@@ -7,6 +7,16 @@
namespace apps {
+#define SET_OPTIONAL_VALUE(VALUE) \
+ if (delta->VALUE.has_value()) { \
+ state->VALUE = delta->VALUE; \
+ }
+
+#define SET_ENUM_VALUE(VALUE, DEFAULT_VALUE) \
+ if (delta->VALUE != DEFAULT_VALUE) { \
+ state->VALUE = delta->VALUE; \
+ }
+
#define GET_VALUE(VALUE) \
if (delta_ && delta_->VALUE()) { \
return delta_->VALUE(); \
@@ -20,12 +30,21 @@ namespace apps {
return delta_ && delta_->VALUE() && \
(!state_ || (delta_->VALUE() != state_->VALUE()));
+#define GET_VALUE_WITH_FALLBACK(VALUE, FALLBACK_VALUE) \
+ if (delta_ && delta_->VALUE.has_value()) { \
+ return delta_->VALUE.value(); \
+ } \
+ if (state_ && state_->VALUE.has_value()) { \
+ return state_->VALUE.value(); \
+ } \
+ return FALLBACK_VALUE;
+
#define GET_VALUE_WITH_DEFAULT_VALUE(VALUE, DEFAULT_VALUE) \
- if (delta_ && delta_->VALUE() != (DEFAULT_VALUE)) { \
- return delta_->VALUE(); \
+ if (delta_ && delta_->VALUE != (DEFAULT_VALUE)) { \
+ return delta_->VALUE; \
} \
if (state_) { \
- return state_->VALUE(); \
+ return state_->VALUE; \
} \
return DEFAULT_VALUE;
@@ -34,11 +53,11 @@ namespace apps {
(!state_ || (delta_->VALUE() != state_->VALUE()));
#define GET_VALUE_WITH_CHECK_AND_DEFAULT_RETURN(VALUE, CHECK, DEFAULT_RETURN) \
- if (delta_ && !delta_->VALUE().CHECK()) { \
- return delta_->VALUE(); \
+ if (delta_ && !delta_->VALUE.CHECK()) { \
+ return delta_->VALUE; \
} \
- if (state_ && !state_->VALUE().CHECK()) { \
- return state_->VALUE(); \
+ if (state_ && !state_->VALUE.CHECK()) { \
+ return state_->VALUE; \
} \
return DEFAULT_RETURN;
diff --git a/chromium/components/services/app_service/public/cpp/permission.cc b/chromium/components/services/app_service/public/cpp/permission.cc
new file mode 100644
index 00000000000..7e91f5ab680
--- /dev/null
+++ b/chromium/components/services/app_service/public/cpp/permission.cc
@@ -0,0 +1,213 @@
+// Copyright 2022 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 "components/services/app_service/public/cpp/permission.h"
+
+namespace apps {
+
+PermissionValue::PermissionValue(bool bool_value) : bool_value(bool_value) {}
+
+PermissionValue::PermissionValue(TriState tristate_value)
+ : tristate_value(tristate_value) {}
+
+PermissionValue::~PermissionValue() = default;
+
+bool PermissionValue::operator==(const PermissionValue& other) const {
+ if (tristate_value.has_value() && other.tristate_value.has_value()) {
+ return tristate_value.value() == other.tristate_value.value();
+ }
+
+ if (bool_value.has_value() && other.bool_value.has_value()) {
+ return bool_value.value() == other.bool_value.value();
+ }
+
+ return false;
+}
+
+std::unique_ptr<PermissionValue> PermissionValue::Clone() const {
+ if (tristate_value.has_value()) {
+ return std::make_unique<PermissionValue>(tristate_value.value());
+ }
+
+ if (bool_value.has_value()) {
+ return std::make_unique<PermissionValue>(bool_value.value());
+ }
+
+ return nullptr;
+}
+
+bool PermissionValue::IsPermissionEnabled() {
+ if (tristate_value.has_value()) {
+ return tristate_value.value() == TriState::kAllow;
+ } else if (bool_value.has_value()) {
+ return bool_value.value();
+ }
+ return false;
+}
+
+Permission::Permission(PermissionType permission_type,
+ PermissionValuePtr value,
+ bool is_managed)
+ : permission_type(permission_type),
+ value(std::move(value)),
+ is_managed(is_managed) {}
+
+Permission::~Permission() = default;
+
+bool Permission::operator==(const Permission& other) const {
+ return permission_type == other.permission_type &&
+ ((!value && !other.value) || (*value == *other.value)) &&
+ is_managed == other.is_managed;
+}
+
+bool Permission::operator!=(const Permission& other) const {
+ return !(*this == other);
+}
+
+PermissionPtr Permission::Clone() const {
+ if (!value) {
+ return nullptr;
+ }
+
+ return std::make_unique<Permission>(permission_type, value->Clone(),
+ is_managed);
+}
+
+Permissions ClonePermissions(const Permissions& source_permissions) {
+ Permissions permissions;
+ for (const auto& permission : source_permissions) {
+ permissions.push_back(permission->Clone());
+ }
+ return permissions;
+}
+
+PermissionType ConvertMojomPermissionTypeToPermissionType(
+ apps::mojom::PermissionType mojom_permission_type) {
+ switch (mojom_permission_type) {
+ case apps::mojom::PermissionType::kUnknown:
+ return PermissionType::kUnknown;
+ case apps::mojom::PermissionType::kCamera:
+ return PermissionType::kCamera;
+ case apps::mojom::PermissionType::kLocation:
+ return PermissionType::kLocation;
+ case apps::mojom::PermissionType::kMicrophone:
+ return PermissionType::kMicrophone;
+ case apps::mojom::PermissionType::kNotifications:
+ return PermissionType::kNotifications;
+ case apps::mojom::PermissionType::kContacts:
+ return PermissionType::kContacts;
+ case apps::mojom::PermissionType::kStorage:
+ return PermissionType::kStorage;
+ case apps::mojom::PermissionType::kPrinting:
+ return PermissionType::kPrinting;
+ }
+}
+
+apps::mojom::PermissionType ConvertPermissionTypeToMojomPermissionType(
+ PermissionType permission_type) {
+ switch (permission_type) {
+ case PermissionType::kUnknown:
+ return apps::mojom::PermissionType::kUnknown;
+ case PermissionType::kCamera:
+ return apps::mojom::PermissionType::kCamera;
+ case PermissionType::kLocation:
+ return apps::mojom::PermissionType::kLocation;
+ case PermissionType::kMicrophone:
+ return apps::mojom::PermissionType::kMicrophone;
+ case PermissionType::kNotifications:
+ return apps::mojom::PermissionType::kNotifications;
+ case PermissionType::kContacts:
+ return apps::mojom::PermissionType::kContacts;
+ case PermissionType::kStorage:
+ return apps::mojom::PermissionType::kStorage;
+ case PermissionType::kPrinting:
+ return apps::mojom::PermissionType::kPrinting;
+ }
+}
+
+TriState ConvertMojomTriStateToTriState(apps::mojom::TriState mojom_tri_state) {
+ switch (mojom_tri_state) {
+ case apps::mojom::TriState::kAllow:
+ return TriState::kAllow;
+ case apps::mojom::TriState::kBlock:
+ return TriState::kBlock;
+ case apps::mojom::TriState::kAsk:
+ return TriState::kAsk;
+ }
+}
+
+apps::mojom::TriState ConvertTriStateToMojomTriState(TriState tri_state) {
+ switch (tri_state) {
+ case TriState::kAllow:
+ return apps::mojom::TriState::kAllow;
+ case TriState::kBlock:
+ return apps::mojom::TriState::kBlock;
+ case TriState::kAsk:
+ return apps::mojom::TriState::kAsk;
+ }
+}
+
+PermissionValuePtr ConvertMojomPermissionValueToPermissionValue(
+ const apps::mojom::PermissionValuePtr& mojom_permission_value) {
+ if (!mojom_permission_value) {
+ return nullptr;
+ }
+
+ if (mojom_permission_value->is_tristate_value()) {
+ return std::make_unique<PermissionValue>(ConvertMojomTriStateToTriState(
+ mojom_permission_value->get_tristate_value()));
+ } else if (mojom_permission_value->is_bool_value()) {
+ return std::make_unique<PermissionValue>(
+ mojom_permission_value->get_bool_value());
+ }
+ return nullptr;
+}
+
+apps::mojom::PermissionValuePtr ConvertPermissionValueToMojomPermissionValue(
+ const PermissionValuePtr& permission_value) {
+ auto mojom_permission_value = apps::mojom::PermissionValue::New();
+ if (!permission_value) {
+ return mojom_permission_value;
+ }
+
+ if (permission_value->bool_value.has_value()) {
+ mojom_permission_value->set_bool_value(
+ permission_value->bool_value.value());
+ }
+ if (permission_value->tristate_value.has_value()) {
+ mojom_permission_value->set_tristate_value(ConvertTriStateToMojomTriState(
+ permission_value->tristate_value.value()));
+ }
+ return mojom_permission_value;
+}
+
+PermissionPtr ConvertMojomPermissionToPermission(
+ const apps::mojom::PermissionPtr& mojom_permission) {
+ if (!mojom_permission) {
+ return nullptr;
+ }
+
+ return std::make_unique<Permission>(
+ ConvertMojomPermissionTypeToPermissionType(
+ mojom_permission->permission_type),
+ ConvertMojomPermissionValueToPermissionValue(mojom_permission->value),
+ mojom_permission->is_managed);
+}
+
+apps::mojom::PermissionPtr ConvertPermissionToMojomPermission(
+ const PermissionPtr& permission) {
+ auto mojom_permission = apps::mojom::Permission::New();
+ if (!permission) {
+ return mojom_permission;
+ }
+
+ mojom_permission->permission_type =
+ ConvertPermissionTypeToMojomPermissionType(permission->permission_type);
+ mojom_permission->value =
+ ConvertPermissionValueToMojomPermissionValue(permission->value);
+ mojom_permission->is_managed = permission->is_managed;
+ return mojom_permission;
+}
+
+} // namespace apps
diff --git a/chromium/components/services/app_service/public/cpp/permission.h b/chromium/components/services/app_service/public/cpp/permission.h
new file mode 100644
index 00000000000..1483cf52b8a
--- /dev/null
+++ b/chromium/components/services/app_service/public/cpp/permission.h
@@ -0,0 +1,117 @@
+// Copyright 2022 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.
+
+#ifndef COMPONENTS_SERVICES_APP_SERVICE_PUBLIC_CPP_PERMISSION_H_
+#define COMPONENTS_SERVICES_APP_SERVICE_PUBLIC_CPP_PERMISSION_H_
+
+#include <utility>
+#include <vector>
+
+#include "base/component_export.h"
+#include "components/services/app_service/public/mojom/types.mojom.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace apps {
+
+// The types of permissions in App Service.
+enum class PermissionType {
+ kUnknown = 0,
+ kCamera = 1,
+ kLocation = 2,
+ kMicrophone = 3,
+ kNotifications = 4,
+ kContacts = 5,
+ kStorage = 6,
+ kPrinting = 7,
+};
+
+enum class TriState {
+ kAllow,
+ kBlock,
+ kAsk,
+};
+
+// The permission value could be a TriState or a bool
+struct COMPONENT_EXPORT(APP_TYPES) PermissionValue {
+ explicit PermissionValue(bool bool_value);
+ explicit PermissionValue(TriState tristate_value);
+ PermissionValue(const PermissionValue&) = delete;
+ PermissionValue& operator=(const PermissionValue&) = delete;
+ ~PermissionValue();
+
+ bool operator==(const PermissionValue& other) const;
+
+ std::unique_ptr<PermissionValue> Clone() const;
+
+ // Checks whether this is equal to permission enabled. If it is TriState, only
+ // Allow represent permission enabled.
+ bool IsPermissionEnabled();
+
+ absl::optional<bool> bool_value;
+ absl::optional<TriState> tristate_value;
+};
+
+using PermissionValuePtr = std::unique_ptr<PermissionValue>;
+
+struct COMPONENT_EXPORT(APP_TYPES) Permission {
+ Permission(PermissionType permission_type,
+ PermissionValuePtr value,
+ bool is_managed);
+ Permission(const Permission&) = delete;
+ Permission& operator=(const Permission&) = delete;
+ ~Permission();
+
+ bool operator==(const Permission& other) const;
+ bool operator!=(const Permission& other) const;
+
+ std::unique_ptr<Permission> Clone() const;
+
+ PermissionType permission_type;
+ std::unique_ptr<PermissionValue> value;
+ // If the permission is managed by an enterprise policy.
+ bool is_managed;
+};
+
+using PermissionPtr = std::unique_ptr<Permission>;
+using Permissions = std::vector<PermissionPtr>;
+
+// Creates a deep copy of `source_permissions`.
+COMPONENT_EXPORT(APP_TYPES)
+Permissions ClonePermissions(const Permissions& source_permissions);
+
+// TODO(crbug.com/1253250): Remove these functions after migrating to non-mojo
+// AppService.
+COMPONENT_EXPORT(APP_TYPES)
+PermissionType ConvertMojomPermissionTypeToPermissionType(
+ apps::mojom::PermissionType mojom_permission_type);
+
+COMPONENT_EXPORT(APP_TYPES)
+apps::mojom::PermissionType ConvertPermissionTypeToMojomPermissionType(
+ PermissionType permission_type);
+
+COMPONENT_EXPORT(APP_TYPES)
+TriState ConvertMojomTriStateToTriState(apps::mojom::TriState mojom_tri_state);
+
+COMPONENT_EXPORT(APP_TYPES)
+apps::mojom::TriState ConvertTriStateToMojomTriState(TriState tri_state);
+
+COMPONENT_EXPORT(APP_TYPES)
+PermissionValuePtr ConvertMojomPermissionValueToPermissionValue(
+ const apps::mojom::PermissionValuePtr& mojom_permission_value);
+
+COMPONENT_EXPORT(APP_TYPES)
+apps::mojom::PermissionValuePtr ConvertPermissionValueToMojomPermissionValue(
+ const PermissionValuePtr& permission_value);
+
+COMPONENT_EXPORT(APP_TYPES)
+PermissionPtr ConvertMojomPermissionToPermission(
+ const apps::mojom::PermissionPtr& mojom_permission);
+
+COMPONENT_EXPORT(APP_TYPES)
+apps::mojom::PermissionPtr ConvertPermissionToMojomPermission(
+ const PermissionPtr& permission);
+
+} // namespace apps
+
+#endif // COMPONENTS_SERVICES_APP_SERVICE_PUBLIC_CPP_PERMISSION_H_
diff --git a/chromium/components/services/app_service/public/cpp/preferred_apps_converter.cc b/chromium/components/services/app_service/public/cpp/preferred_apps_converter.cc
index 0114a7944e2..e83d2165923 100644
--- a/chromium/components/services/app_service/public/cpp/preferred_apps_converter.cc
+++ b/chromium/components/services/app_service/public/cpp/preferred_apps_converter.cc
@@ -84,7 +84,7 @@ apps::mojom::ConditionPtr ParseValueToCondition(const base::Value& value) {
<< apps::kConditionValuesKey << "\" key with list value.";
return nullptr;
}
- for (auto& condition_value : condition_values->GetList()) {
+ for (auto& condition_value : condition_values->GetListDeprecated()) {
auto parsed_condition_value = ParseValueToConditionValue(condition_value);
if (!parsed_condition_value) {
DVLOG(0) << "Fail to parse condition. Cannot parse condition values";
@@ -102,7 +102,7 @@ apps::mojom::IntentFilterPtr ParseValueToIntentFilter(
return nullptr;
}
auto intent_filter = apps::mojom::IntentFilter::New();
- for (auto& condition : value->GetList()) {
+ for (auto& condition : value->GetListDeprecated()) {
auto parsed_condition = ParseValueToCondition(condition);
if (!parsed_condition) {
DVLOG(0) << "Fail to parse intent filter. Cannot parse conditions.";
@@ -160,7 +160,7 @@ PreferredAppsList::PreferredApps ParseValueToPreferredApps(
}
PreferredAppsList::PreferredApps preferred_apps;
- for (auto& entry : preferred_apps_list->GetList()) {
+ for (auto& entry : preferred_apps_list->GetListDeprecated()) {
auto* app_id = entry.FindStringKey(kAppIdKey);
if (!app_id) {
DVLOG(0) << "Fail to parse condition value. Cannot find \""
diff --git a/chromium/components/services/app_service/public/cpp/preferred_apps_converter_unittest.cc b/chromium/components/services/app_service/public/cpp/preferred_apps_converter_unittest.cc
index acc8599f795..b40d83862db 100644
--- a/chromium/components/services/app_service/public/cpp/preferred_apps_converter_unittest.cc
+++ b/chromium/components/services/app_service/public/cpp/preferred_apps_converter_unittest.cc
@@ -33,20 +33,21 @@ TEST_F(PreferredAppsConverterTest, ConvertSimpleEntry) {
auto* converted_preferred_apps =
converted_value.FindKey(apps::kPreferredAppsKey);
// Check that each entry is correct.
- ASSERT_EQ(1u, converted_preferred_apps->GetList().size());
- auto& entry = converted_preferred_apps->GetList()[0];
+ ASSERT_EQ(1u, converted_preferred_apps->GetListDeprecated().size());
+ auto& entry = converted_preferred_apps->GetListDeprecated()[0];
EXPECT_EQ(kAppId1, *entry.FindStringKey(apps::kAppIdKey));
auto* converted_intent_filter = entry.FindKey(apps::kIntentFilterKey);
ASSERT_EQ(intent_filter->conditions.size(),
- converted_intent_filter->GetList().size());
+ converted_intent_filter->GetListDeprecated().size());
for (size_t i = 0; i < intent_filter->conditions.size(); i++) {
auto& condition = intent_filter->conditions[i];
- auto& converted_condition = converted_intent_filter->GetList()[i];
+ auto& converted_condition = converted_intent_filter->GetListDeprecated()[i];
auto& condition_values = condition->condition_values;
auto converted_condition_values =
- converted_condition.FindKey(apps::kConditionValuesKey)->GetList();
+ converted_condition.FindKey(apps::kConditionValuesKey)
+ ->GetListDeprecated();
EXPECT_EQ(static_cast<int>(condition->condition_type),
converted_condition.FindIntKey(apps::kConditionTypeKey));
diff --git a/chromium/components/services/app_service/public/cpp/publisher_base.cc b/chromium/components/services/app_service/public/cpp/publisher_base.cc
index 2fb5657d845..9f1d5aa4e28 100644
--- a/chromium/components/services/app_service/public/cpp/publisher_base.cc
+++ b/chromium/components/services/app_service/public/cpp/publisher_base.cc
@@ -181,4 +181,10 @@ void PublisherBase::SetWindowMode(const std::string& app_id,
NOTIMPLEMENTED();
}
+void PublisherBase::SetRunOnOsLoginMode(
+ const std::string& app_id,
+ apps::mojom::RunOnOsLoginMode run_on_os_login_mode) {
+ NOTIMPLEMENTED();
+}
+
} // namespace apps
diff --git a/chromium/components/services/app_service/public/cpp/publisher_base.h b/chromium/components/services/app_service/public/cpp/publisher_base.h
index 20f8955123a..d5e4951902a 100644
--- a/chromium/components/services/app_service/public/cpp/publisher_base.h
+++ b/chromium/components/services/app_service/public/cpp/publisher_base.h
@@ -101,6 +101,9 @@ class PublisherBase : public apps::mojom::Publisher {
apps::mojom::OptionalBool locked) override;
void SetWindowMode(const std::string& app_id,
apps::mojom::WindowMode window_mode) override;
+ void SetRunOnOsLoginMode(
+ const std::string& app_id,
+ apps::mojom::RunOnOsLoginMode run_on_os_login_mode) override;
mojo::Receiver<apps::mojom::Publisher> receiver_{this};
};
diff --git a/chromium/components/services/app_service/public/cpp/run_on_os_login_types.cc b/chromium/components/services/app_service/public/cpp/run_on_os_login_types.cc
new file mode 100644
index 00000000000..0cf4bacdd45
--- /dev/null
+++ b/chromium/components/services/app_service/public/cpp/run_on_os_login_types.cc
@@ -0,0 +1,62 @@
+// Copyright 2022 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 "components/services/app_service/public/cpp/run_on_os_login_types.h"
+
+namespace apps {
+
+RunOnOsLogin::RunOnOsLogin() = default;
+
+RunOnOsLogin::RunOnOsLogin(RunOnOsLoginMode login_mode, bool is_managed)
+ : login_mode(login_mode), is_managed(is_managed) {}
+
+RunOnOsLogin::~RunOnOsLogin() = default;
+
+bool RunOnOsLogin::operator==(const RunOnOsLogin& other) const {
+ return login_mode == other.login_mode && is_managed == other.is_managed;
+}
+
+apps::mojom::RunOnOsLoginPtr ConvertRunOnOsLoginToMojomRunOnOsLogin(
+ const RunOnOsLogin& run_on_os_login) {
+ auto run_on_os_login_mojom = apps::mojom::RunOnOsLogin::New();
+ run_on_os_login_mojom->login_mode =
+ ConvertRunOnOsLoginModeToMojomRunOnOsLoginMode(
+ run_on_os_login.login_mode);
+ run_on_os_login_mojom->is_managed = run_on_os_login.is_managed;
+ return run_on_os_login_mojom;
+}
+
+std::unique_ptr<RunOnOsLogin> ConvertMojomRunOnOsLoginToRunOnOsLogin(
+ const apps::mojom::RunOnOsLoginPtr& run_on_os_login) {
+ DCHECK(run_on_os_login);
+ return std::make_unique<RunOnOsLogin>(
+ ConvertMojomRunOnOsLoginModeToRunOnOsLoginMode(
+ run_on_os_login->login_mode),
+ run_on_os_login->is_managed);
+}
+
+apps::mojom::RunOnOsLoginMode ConvertRunOnOsLoginModeToMojomRunOnOsLoginMode(
+ RunOnOsLoginMode login_mode) {
+ switch (login_mode) {
+ case RunOnOsLoginMode::kUnknown:
+ return apps::mojom::RunOnOsLoginMode::kUnknown;
+ case RunOnOsLoginMode::kNotRun:
+ return apps::mojom::RunOnOsLoginMode::kNotRun;
+ case RunOnOsLoginMode::kWindowed:
+ return apps::mojom::RunOnOsLoginMode::kWindowed;
+ }
+}
+
+RunOnOsLoginMode ConvertMojomRunOnOsLoginModeToRunOnOsLoginMode(
+ apps::mojom::RunOnOsLoginMode login_mode) {
+ switch (login_mode) {
+ case apps::mojom::RunOnOsLoginMode::kUnknown:
+ return RunOnOsLoginMode::kUnknown;
+ case apps::mojom::RunOnOsLoginMode::kNotRun:
+ return RunOnOsLoginMode::kNotRun;
+ case apps::mojom::RunOnOsLoginMode::kWindowed:
+ return RunOnOsLoginMode::kWindowed;
+ }
+}
+} // namespace apps \ No newline at end of file
diff --git a/chromium/components/services/app_service/public/cpp/run_on_os_login_types.h b/chromium/components/services/app_service/public/cpp/run_on_os_login_types.h
new file mode 100644
index 00000000000..fccf6d8a239
--- /dev/null
+++ b/chromium/components/services/app_service/public/cpp/run_on_os_login_types.h
@@ -0,0 +1,64 @@
+// Copyright 2022 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.
+
+#ifndef COMPONENTS_SERVICES_APP_SERVICE_PUBLIC_CPP_RUN_ON_OS_LOGIN_TYPES_H_
+#define COMPONENTS_SERVICES_APP_SERVICE_PUBLIC_CPP_RUN_ON_OS_LOGIN_TYPES_H_
+
+#include <vector>
+
+#include "base/component_export.h"
+#include "components/services/app_service/public/mojom/types.mojom.h"
+
+namespace apps {
+
+enum class RunOnOsLoginMode {
+ // kUnknown to be used for app_update.cc.
+ kUnknown,
+ // App won't run on OS Login.
+ kNotRun,
+ // App runs in windowed mode on OS Login.
+ kWindowed,
+};
+
+struct COMPONENT_EXPORT(LOGIN_MODE) RunOnOsLogin {
+ RunOnOsLogin();
+ RunOnOsLogin(RunOnOsLoginMode login_mode, bool is_managed);
+
+ RunOnOsLogin(const RunOnOsLogin&) = delete;
+ RunOnOsLogin& operator=(const RunOnOsLogin&) = delete;
+ RunOnOsLogin(RunOnOsLogin&&) = default;
+ RunOnOsLogin& operator=(RunOnOsLogin&&) = default;
+
+ bool operator==(const RunOnOsLogin& other) const;
+
+ ~RunOnOsLogin();
+
+ // RunOnOsLoginMode struct to be used
+ // to verify if the mode is set by policy
+ // or not.
+ RunOnOsLoginMode login_mode;
+ // If the run on os login mode is policy
+ // controlled or not.
+ bool is_managed;
+};
+
+COMPONENT_EXPORT(LOGIN_MODE)
+apps::mojom::RunOnOsLoginPtr ConvertRunOnOsLoginToMojomRunOnOsLogin(
+ const RunOnOsLogin& run_on_os_login);
+
+COMPONENT_EXPORT(LOGIN_MODE)
+std::unique_ptr<RunOnOsLogin> ConvertMojomRunOnOsLoginToRunOnOsLogin(
+ const apps::mojom::RunOnOsLoginPtr& run_on_os_login);
+
+COMPONENT_EXPORT(LOGIN_MODE)
+apps::mojom::RunOnOsLoginMode ConvertRunOnOsLoginModeToMojomRunOnOsLoginMode(
+ RunOnOsLoginMode login_mode);
+
+COMPONENT_EXPORT(LOGIN_MODE)
+RunOnOsLoginMode ConvertMojomRunOnOsLoginModeToRunOnOsLoginMode(
+ apps::mojom::RunOnOsLoginMode login_mode);
+
+} // namespace apps
+
+#endif // COMPONENTS_SERVICES_APP_SERVICE_PUBLIC_CPP_RUN_ON_OS_LOGIN_TYPES_H_ \ No newline at end of file
diff --git a/chromium/components/services/app_service/public/mojom/BUILD.gn b/chromium/components/services/app_service/public/mojom/BUILD.gn
index b2a043d80dd..76718d3985c 100644
--- a/chromium/components/services/app_service/public/mojom/BUILD.gn
+++ b/chromium/components/services/app_service/public/mojom/BUILD.gn
@@ -6,13 +6,13 @@ import("//mojo/public/tools/bindings/mojom.gni")
mojom("types") {
sources = [ "types.mojom" ]
+ webui_module_path = "/"
public_deps = [
"//mojo/public/mojom/base",
"//skia/public/mojom",
"//ui/gfx/geometry/mojom",
"//ui/gfx/image/mojom",
- "//ui/gfx/image/mojom",
"//ui/gfx/mojom",
"//ui/gfx/range/mojom",
"//url/mojom:url_mojom_gurl",
@@ -21,6 +21,7 @@ mojom("types") {
mojom("mojom") {
sources = [ "app_service.mojom" ]
+ webui_module_path = "/"
public_deps = [ ":types" ]
}
diff --git a/chromium/components/services/app_service/public/mojom/app_service.mojom b/chromium/components/services/app_service/public/mojom/app_service.mojom
index 7fcc26c101b..199129ed973 100644
--- a/chromium/components/services/app_service/public/mojom/app_service.mojom
+++ b/chromium/components/services/app_service/public/mojom/app_service.mojom
@@ -13,6 +13,9 @@ import "components/services/app_service/public/mojom/types.mojom";
// app's name and icon) that are satisfied by the appropriate provider.
//
// See components/services/app_service/README.md.
+//
+// Mojom AppService is DEPRECATED. When adding new interfaces, use AppPublisher
+// in chrome/browser/apps/app_service/publishers/app_publisher.h.
interface AppService {
// Called by a publisher of apps to register itself and its apps with the App
// Service.
@@ -166,6 +169,12 @@ interface AppService {
AppType app_type,
string app_id,
WindowMode window_mode);
+
+ // Set the mode for the app to be run on os login identified by |app_id|.
+ SetRunOnOsLoginMode(
+ AppType app_type,
+ string app_id,
+ RunOnOsLoginMode run_on_os_login_mode);
};
interface Publisher {
@@ -333,6 +342,12 @@ interface Publisher {
SetWindowMode(
string app_id,
WindowMode window_mode);
+
+ // Set the mode to run on OS Login for the app identified by |app_id|.
+ // Implemented if the publisher supports setting this mode for OS Login,
+ // otherwise should do nothing.
+ SetRunOnOsLoginMode(string app_id,
+ RunOnOsLoginMode run_on_os_login_mode);
};
// Subscriber works as a proxy, to receive a stream of apps from publishers,
diff --git a/chromium/components/services/app_service/public/mojom/types.mojom b/chromium/components/services/app_service/public/mojom/types.mojom
index b419afc1580..626bb17c7c9 100644
--- a/chromium/components/services/app_service/public/mojom/types.mojom
+++ b/chromium/components/services/app_service/public/mojom/types.mojom
@@ -91,7 +91,11 @@ struct App {
// Whether the app's display mode is in the browser or otherwise.
WindowMode window_mode;
- // When adding new fields, also update the Merge method and other helpers in
+ // Whether the app runs on os login.
+ RunOnOsLogin? run_on_os_login;
+ // When adding new fields, also update the App struct in
+ // components/services/app_service/public/cpp/app_types.* and the Merge method
+ // and other helpers in
// components/services/app_service/public/cpp/app_update.*
};
@@ -533,3 +537,24 @@ enum WindowMode {
// Opens in a tabbed app window
kTabbedWindow,
};
+
+// The RunOnOsLoginModes must be kept in sync
+// with RunOnOsLoginMode in
+// chrome/browser/web_applications/web_app_constants.h
+enum RunOnOsLoginMode {
+ kUnknown = 0,
+ // App won't run on OS Login.
+ kNotRun,
+ // App will run in windowed mode on OS Login.
+ kWindowed,
+};
+
+// RunOnOsLoginMode struct to be used
+// to verify if the mode is set by policy
+// or not.
+struct RunOnOsLogin {
+ RunOnOsLoginMode login_mode;
+ // If the run on os login mode is policy
+ // controlled or not.
+ bool is_managed;
+};
diff --git a/chromium/components/services/filesystem/directory_impl.cc b/chromium/components/services/filesystem/directory_impl.cc
index 82b49267de7..e484cc0eb29 100644
--- a/chromium/components/services/filesystem/directory_impl.cc
+++ b/chromium/components/services/filesystem/directory_impl.cc
@@ -259,7 +259,7 @@ void DirectoryImpl::IsWritable(const std::string& raw_path,
void DirectoryImpl::Flush(FlushCallback callback) {
// On Windows no need to sync directories. Their metadata will be updated when
// files are created, without an explicit sync.
-#if !defined(OS_WIN)
+#if !BUILDFLAG(IS_WIN)
base::File file(directory_path_,
base::File::FLAG_OPEN | base::File::FLAG_READ);
if (!file.IsValid()) {
diff --git a/chromium/components/services/filesystem/file_impl.cc b/chromium/components/services/filesystem/file_impl.cc
index 29e51005498..a8417ea7367 100644
--- a/chromium/components/services/filesystem/file_impl.cc
+++ b/chromium/components/services/filesystem/file_impl.cc
@@ -65,7 +65,7 @@ bool FileImpl::IsValid() const {
return file_.IsValid();
}
-#if !defined(OS_FUCHSIA)
+#if !BUILDFLAG(IS_FUCHSIA)
base::File::Error FileImpl::RawLockFile() {
return file_.Lock(base::File::LockMode::kExclusive);
}
@@ -73,7 +73,7 @@ base::File::Error FileImpl::RawLockFile() {
base::File::Error FileImpl::RawUnlockFile() {
return file_.Unlock();
}
-#endif // !OS_FUCHSIA
+#endif // !BUILDFLAG(IS_FUCHSIA)
void FileImpl::Close(CloseCallback callback) {
if (!file_.IsValid()) {
@@ -143,7 +143,7 @@ void FileImpl::Write(const std::vector<uint8_t>& bytes_to_write,
// Who knows what |write()| would return if the size is that big (and it
// actually wrote that much).
if (bytes_to_write.size() >
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
static_cast<size_t>(std::numeric_limits<int>::max())) {
#else
static_cast<size_t>(std::numeric_limits<ssize_t>::max())) {
diff --git a/chromium/components/services/filesystem/file_impl.h b/chromium/components/services/filesystem/file_impl.h
index 4a3d4a0f70e..8ae90799d29 100644
--- a/chromium/components/services/filesystem/file_impl.h
+++ b/chromium/components/services/filesystem/file_impl.h
@@ -41,12 +41,12 @@ class FileImpl : public mojom::File {
// Returns whether the underlying file handle is valid.
bool IsValid() const;
-#if !defined(OS_FUCHSIA)
+#if !BUILDFLAG(IS_FUCHSIA)
// Attempts to perform the native operating system's locking operations on
// the internal mojom::File handle. Not supported on Fuchsia.
base::File::Error RawLockFile();
base::File::Error RawUnlockFile();
-#endif // !OS_FUCHSIA
+#endif // !BUILDFLAG(IS_FUCHSIA)
const base::FilePath& path() const { return path_; }
diff --git a/chromium/components/services/filesystem/lock_table.cc b/chromium/components/services/filesystem/lock_table.cc
index a3402a9ef54..e1bdf6b71fa 100644
--- a/chromium/components/services/filesystem/lock_table.cc
+++ b/chromium/components/services/filesystem/lock_table.cc
@@ -23,7 +23,7 @@ base::File::Error LockTable::LockFile(FileImpl* file) {
return base::File::FILE_ERROR_FAILED;
}
-#if !defined(OS_FUCHSIA)
+#if !BUILDFLAG(IS_FUCHSIA)
// Fuchsia doesn't provide a file locking mechanism, so file locks work only
// within a single process. File locking is used only by LevelDB which stores
// all files in the profile directory and normally there shouldn't be more
@@ -36,7 +36,7 @@ base::File::Error LockTable::LockFile(FileImpl* file) {
// Locking failed for some reason.
return lock_err;
}
-#endif // !OS_FUCHSIA
+#endif // !BUILDFLAG(IS_FUCHSIA)
locked_files_.insert(file->path());
return base::File::FILE_OK;
@@ -45,14 +45,14 @@ base::File::Error LockTable::LockFile(FileImpl* file) {
base::File::Error LockTable::UnlockFile(FileImpl* file) {
auto it = locked_files_.find(file->path());
if (it != locked_files_.end()) {
-#if !defined(OS_FUCHSIA)
+#if !BUILDFLAG(IS_FUCHSIA)
base::File::Error lock_err = file->RawUnlockFile();
if (lock_err != base::File::FILE_OK) {
// TODO(erg): When can we fail to release a lock?
NOTREACHED();
return lock_err;
}
-#endif // !OS_FUCHSIA
+#endif // !BUILDFLAG(IS_FUCHSIA)
locked_files_.erase(it);
}
diff --git a/chromium/components/services/filesystem/util.cc b/chromium/components/services/filesystem/util.cc
index 1aef24d61b7..ddfdc58bfdb 100644
--- a/chromium/components/services/filesystem/util.cc
+++ b/chromium/components/services/filesystem/util.cc
@@ -15,7 +15,7 @@
#include "base/strings/string_util.h"
#include "build/build_config.h"
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
#include "base/strings/utf_string_conversions.h"
#endif
@@ -97,9 +97,9 @@ base::File::Error ValidatePath(const std::string& raw_path,
if (!base::IsStringUTF8(raw_path))
return base::File::Error::FILE_ERROR_INVALID_OPERATION;
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
base::FilePath::StringType path = base::UTF8ToWide(raw_path);
-#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
base::FilePath::StringType path = raw_path;
#endif
diff --git a/chromium/components/services/font/BUILD.gn b/chromium/components/services/font/BUILD.gn
index b065131e035..1d4965449a5 100644
--- a/chromium/components/services/font/BUILD.gn
+++ b/chromium/components/services/font/BUILD.gn
@@ -16,6 +16,7 @@ source_set("lib") {
deps = [
"//base",
+ "//build:chromeos_buildflags",
"//components/services/font/public/mojom",
"//mojo/public/cpp/bindings",
"//mojo/public/cpp/system",
diff --git a/chromium/components/services/font/font_service_app.cc b/chromium/components/services/font/font_service_app.cc
index 4bbeebcb52c..32adf6f3c5c 100644
--- a/chromium/components/services/font/font_service_app.cc
+++ b/chromium/components/services/font/font_service_app.cc
@@ -8,10 +8,12 @@
#include "base/bind.h"
#include "base/command_line.h"
+#include "base/feature_list.h"
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
+#include "build/chromeos_buildflags.h"
#include "components/services/font/fontconfig_matching.h"
#include "mojo/public/cpp/system/platform_handle.h"
#include "ppapi/buildflags/buildflags.h"
@@ -77,11 +79,24 @@ font_service::mojom::RenderStyleSwitch ConvertSubpixelRendering(
return font_service::mojom::RenderStyleSwitch::NO_PREFERENCE;
}
+// A feature that controls whether we use a cache for font family matching.
+const base::Feature kCacheFontFamilyMatching {
+ "CacheFontFamilyMatching",
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+ base::FEATURE_ENABLED_BY_DEFAULT
+#else
+ base::FEATURE_DISABLED_BY_DEFAULT
+#endif
+};
+
+// The maximum number of entries to keep in the font family matching cache.
+constexpr int kCacheFontFamilyMaxSize = 10000;
+
} // namespace
namespace font_service {
-FontServiceApp::FontServiceApp() = default;
+FontServiceApp::FontServiceApp() : match_cache_(kCacheFontFamilyMaxSize) {}
FontServiceApp::~FontServiceApp() = default;
@@ -100,37 +115,59 @@ void FontServiceApp::MatchFamilyName(const std::string& family_name,
SkFontStyle result_style;
SkFontConfigInterface* fc =
SkFontConfigInterface::GetSingletonDirectInterface();
- const bool r = fc->matchFamilyName(
- family_name.data(),
- SkFontStyle(requested_style->weight, requested_style->width,
- static_cast<SkFontStyle::Slant>(requested_style->slant)),
- &result_identity, &result_family, &result_style);
-
- if (!r) {
- mojom::TypefaceStylePtr style(mojom::TypefaceStyle::New());
+ SkFontStyle font_style(
+ requested_style->weight, requested_style->width,
+ static_cast<SkFontStyle::Slant>(requested_style->slant));
+
+ MatchCacheKey key;
+ if (base::FeatureList::IsEnabled(kCacheFontFamilyMatching)) {
+ key.family_name = family_name;
+ key.font_style = font_style;
+ auto it = match_cache_.Get(key);
+ if (it != match_cache_.end()) {
+ std::move(callback).Run(
+ it->second.identity ? it->second.identity.Clone() : nullptr,
+ it->second.family_name, it->second.style.Clone());
+ return;
+ }
+ }
+ const bool r =
+ fc->matchFamilyName(family_name.data(), font_style, &result_identity,
+ &result_family, &result_style);
+
+ mojom::FontIdentityPtr identity = nullptr;
+ mojom::TypefaceStylePtr style(mojom::TypefaceStyle::New());
+ std::string result_family_cppstring = result_family.c_str();
+
+ if (r) {
+ // Stash away the returned path, so we can give it an ID (index)
+ // which will later be given to us in a request to open the file.
+ base::FilePath path(result_identity.fString.c_str());
+ size_t index = FindOrAddPath(path);
+
+ identity = mojom::FontIdentity::New();
+ identity->id = static_cast<uint32_t>(index);
+ identity->ttc_index = result_identity.fTTCIndex;
+ identity->filepath = path;
+
+ style->weight = result_style.weight();
+ style->width = result_style.width();
+ style->slant = static_cast<mojom::TypefaceSlant>(result_style.slant());
+ } else {
style->weight = SkFontStyle().weight();
style->width = SkFontStyle().width();
style->slant = static_cast<mojom::TypefaceSlant>(SkFontStyle().slant());
- std::move(callback).Run(nullptr, "", std::move(style));
- return;
}
- // Stash away the returned path, so we can give it an ID (index)
- // which will later be given to us in a request to open the file.
- base::FilePath path(result_identity.fString.c_str());
- size_t index = FindOrAddPath(path);
-
- mojom::FontIdentityPtr identity(mojom::FontIdentity::New());
- identity->id = static_cast<uint32_t>(index);
- identity->ttc_index = result_identity.fTTCIndex;
- identity->filepath = path;
-
- mojom::TypefaceStylePtr style(mojom::TypefaceStyle::New());
- style->weight = result_style.weight();
- style->width = result_style.width();
- style->slant = static_cast<mojom::TypefaceSlant>(result_style.slant());
+ if (base::FeatureList::IsEnabled(kCacheFontFamilyMatching)) {
+ MatchCacheValue value;
+ value.family_name = result_family_cppstring;
+ value.identity = identity ? identity.Clone() : nullptr;
+ value.style = style.Clone();
+ match_cache_.Put(key, std::move(value));
+ }
- std::move(callback).Run(std::move(identity), result_family.c_str(),
+ std::move(callback).Run(std::move(identity), result_family_cppstring,
std::move(style));
}
@@ -256,4 +293,8 @@ size_t FontServiceApp::FindOrAddPath(const base::FilePath& path) {
return count;
}
+FontServiceApp::MatchCacheValue::MatchCacheValue() = default;
+FontServiceApp::MatchCacheValue::~MatchCacheValue() = default;
+FontServiceApp::MatchCacheValue::MatchCacheValue(MatchCacheValue&&) = default;
+
} // namespace font_service
diff --git a/chromium/components/services/font/font_service_app.h b/chromium/components/services/font/font_service_app.h
index 949551b6949..2b2aac722a4 100644
--- a/chromium/components/services/font/font_service_app.h
+++ b/chromium/components/services/font/font_service_app.h
@@ -6,14 +6,18 @@
#define COMPONENTS_SERVICES_FONT_FONT_SERVICE_APP_H_
#include <stdint.h>
+#include <tuple>
#include <vector>
+#include "base/containers/lru_cache.h"
#include "base/files/file_path.h"
#include "components/services/font/public/mojom/font_service.mojom.h"
#include "mojo/public/cpp/bindings/receiver_set.h"
+#include "third_party/skia/include/core/SkFontStyle.h"
namespace font_service {
+// This class is instantiated in the browser process.
class FontServiceApp : public mojom::FontService {
public:
FontServiceApp();
@@ -58,6 +62,35 @@ class FontServiceApp : public mojom::FontService {
// We don't want to leak paths to our callers; we thus enumerate the paths of
// fonts.
std::vector<base::FilePath> paths_;
+
+ // On some platforms, font matching is very expensive, ranging from 2-30+ms.
+ // We keep a cache of results to speed this up.
+ struct MatchCacheKey {
+ std::string family_name;
+ SkFontStyle font_style;
+ bool operator==(const MatchCacheKey& other) const {
+ return family_name == other.family_name && font_style == other.font_style;
+ }
+ };
+ struct MatchCacheKeyCompare {
+ bool operator()(const MatchCacheKey& lhs, const MatchCacheKey& rhs) const {
+ return std::make_tuple(lhs.family_name, lhs.font_style.weight(),
+ lhs.font_style.width(), lhs.font_style.slant()) <
+ std::make_tuple(rhs.family_name, rhs.font_style.weight(),
+ rhs.font_style.width(), rhs.font_style.slant());
+ }
+ };
+ struct MatchCacheValue {
+ MatchCacheValue();
+ ~MatchCacheValue();
+ MatchCacheValue(MatchCacheValue&&);
+ std::string family_name;
+ mojom::FontIdentityPtr identity;
+ mojom::TypefaceStylePtr style;
+ };
+
+ base::LRUCache<MatchCacheKey, MatchCacheValue, MatchCacheKeyCompare>
+ match_cache_;
};
} // namespace font_service
diff --git a/chromium/components/services/heap_profiling/heap_profiling_service.cc b/chromium/components/services/heap_profiling/heap_profiling_service.cc
index ede6ca04444..b21009e1106 100644
--- a/chromium/components/services/heap_profiling/heap_profiling_service.cc
+++ b/chromium/components/services/heap_profiling/heap_profiling_service.cc
@@ -9,7 +9,6 @@
#include "base/bind.h"
#include "base/memory/weak_ptr.h"
-#include "base/no_destructor.h"
#include "base/task/post_task.h"
#include "base/task/thread_pool.h"
#include "components/services/heap_profiling/connection_manager.h"
diff --git a/chromium/components/services/heap_profiling/json_exporter_unittest.cc b/chromium/components/services/heap_profiling/json_exporter_unittest.cc
index e8a17017bb4..f6ba7ceb574 100644
--- a/chromium/components/services/heap_profiling/json_exporter_unittest.cc
+++ b/chromium/components/services/heap_profiling/json_exporter_unittest.cc
@@ -37,7 +37,7 @@ const base::Value* FindFirstRegionWithAnyName(
if (!found_regions)
return nullptr;
- for (const base::Value& cur : found_regions->GetList()) {
+ for (const base::Value& cur : found_regions->GetListDeprecated()) {
const base::Value* found_name =
cur.FindKeyOfType("mf", base::Value::Type::STRING);
if (!found_name)
@@ -51,7 +51,7 @@ const base::Value* FindFirstRegionWithAnyName(
// Looks up a given string id from the string table. Returns -1 if not found.
int GetIdFromStringTable(const base::Value* strings, const char* text) {
- for (const auto& string : strings->GetList()) {
+ for (const auto& string : strings->GetListDeprecated()) {
const base::Value* string_id =
string.FindKeyOfType("id", base::Value::Type::INTEGER);
const base::Value* string_text =
@@ -66,7 +66,7 @@ int GetIdFromStringTable(const base::Value* strings, const char* text) {
// Looks up a given string from the string table. Returns empty string if not
// found.
std::string GetStringFromStringTable(const base::Value* strings, int sid) {
- for (const auto& string : strings->GetList()) {
+ for (const auto& string : strings->GetListDeprecated()) {
const base::Value* string_id =
string.FindKeyOfType("id", base::Value::Type::INTEGER);
if (string_id->GetInt() == sid) {
@@ -81,7 +81,7 @@ std::string GetStringFromStringTable(const base::Value* strings, int sid) {
}
int GetNodeWithNameID(const base::Value* nodes, int sid) {
- for (const auto& node : nodes->GetList()) {
+ for (const auto& node : nodes->GetListDeprecated()) {
const base::Value* node_id =
node.FindKeyOfType("id", base::Value::Type::INTEGER);
const base::Value* node_name_sid =
@@ -95,7 +95,7 @@ int GetNodeWithNameID(const base::Value* nodes, int sid) {
int GetOffsetForBacktraceID(const base::Value* nodes, int id) {
int offset = 0;
- for (const auto& node : nodes->GetList()) {
+ for (const auto& node : nodes->GetListDeprecated()) {
if (node.GetInt() == id)
return offset;
offset++;
@@ -104,7 +104,7 @@ int GetOffsetForBacktraceID(const base::Value* nodes, int id) {
}
bool IsBacktraceInList(const base::Value* backtraces, int id, int parent) {
- for (const auto& backtrace : backtraces->GetList()) {
+ for (const auto& backtrace : backtraces->GetListDeprecated()) {
const base::Value* backtrace_id =
backtrace.FindKeyOfType("id", base::Value::Type::INTEGER);
if (backtrace_id == nullptr)
@@ -192,7 +192,7 @@ TEST(ProfilingJsonExporterTest, Simple) {
ASSERT_TRUE(strings);
// Validate the strings table.
- EXPECT_EQ(5u, strings->GetList().size());
+ EXPECT_EQ(5u, strings->GetListDeprecated().size());
int sid_unknown = GetIdFromStringTable(strings, "[unknown]");
int sid_1234 = GetIdFromStringTable(strings, "pc:1234");
int sid_5678 = GetIdFromStringTable(strings, "pc:5678");
@@ -210,7 +210,7 @@ TEST(ProfilingJsonExporterTest, Simple) {
// [1] => address: 5678 parent: 0
// [2] => address: 9012 parent: 0
// [3] => address: 9013 parent: 2
- EXPECT_EQ(4u, nodes->GetList().size());
+ EXPECT_EQ(4u, nodes->GetListDeprecated().size());
int id0 = GetNodeWithNameID(nodes, sid_1234);
int id1 = GetNodeWithNameID(nodes, sid_5678);
int id2 = GetNodeWithNameID(nodes, sid_9012);
@@ -241,9 +241,9 @@ TEST(ProfilingJsonExporterTest, Simple) {
// Counts should be a list of two items, a 1 and a 2. The two matching 20-byte
// allocations should be coalesced to produce the 2.
- EXPECT_EQ(2u, counts->GetList().size());
- EXPECT_EQ(2u, types->GetList().size());
- EXPECT_EQ(2u, sizes->GetList().size());
+ EXPECT_EQ(2u, counts->GetListDeprecated().size());
+ EXPECT_EQ(2u, types->GetListDeprecated().size());
+ EXPECT_EQ(2u, sizes->GetListDeprecated().size());
int node1 = GetOffsetForBacktraceID(backtraces, id1);
int node3 = GetOffsetForBacktraceID(backtraces, id3);
@@ -251,16 +251,16 @@ TEST(ProfilingJsonExporterTest, Simple) {
EXPECT_NE(-1, node3);
// Validate node allocated with |stack1|.
- EXPECT_EQ(2, counts->GetList()[node1].GetInt());
- EXPECT_EQ(0, types->GetList()[node1].GetInt());
- EXPECT_EQ(40, sizes->GetList()[node1].GetInt());
- EXPECT_EQ(id1, backtraces->GetList()[node1].GetInt());
+ EXPECT_EQ(2, counts->GetListDeprecated()[node1].GetInt());
+ EXPECT_EQ(0, types->GetListDeprecated()[node1].GetInt());
+ EXPECT_EQ(40, sizes->GetListDeprecated()[node1].GetInt());
+ EXPECT_EQ(id1, backtraces->GetListDeprecated()[node1].GetInt());
// Validate node allocated with |stack2|.
- EXPECT_EQ(2, counts->GetList()[node3].GetInt());
- EXPECT_EQ(0, types->GetList()[node3].GetInt());
- EXPECT_EQ(44, sizes->GetList()[node3].GetInt());
- EXPECT_EQ(id3, backtraces->GetList()[node3].GetInt());
+ EXPECT_EQ(2, counts->GetListDeprecated()[node3].GetInt());
+ EXPECT_EQ(0, types->GetListDeprecated()[node3].GetInt());
+ EXPECT_EQ(44, sizes->GetListDeprecated()[node3].GetInt());
+ EXPECT_EQ(id3, backtraces->GetListDeprecated()[node3].GetInt());
// Validate that the partition alloc one got through.
counts = heaps_v2->FindPath({"allocators", "partition_alloc", "counts"});
@@ -274,9 +274,9 @@ TEST(ProfilingJsonExporterTest, Simple) {
ASSERT_TRUE(backtraces);
// There should just be one entry for the partition_alloc allocation.
- EXPECT_EQ(1u, counts->GetList().size());
- EXPECT_EQ(1u, types->GetList().size());
- EXPECT_EQ(1u, sizes->GetList().size());
+ EXPECT_EQ(1u, counts->GetListDeprecated().size());
+ EXPECT_EQ(1u, types->GetListDeprecated().size());
+ EXPECT_EQ(1u, sizes->GetListDeprecated().size());
}
// GetProcessMemoryMaps iterates through every memory region, making allocations
@@ -354,8 +354,8 @@ TEST(ProfilingJsonExporterTest, Context) {
heaps_v2->FindPath({"allocators", "partition_alloc", "types"});
ASSERT_TRUE(types);
- const auto& counts_list = counts->GetList();
- const auto& types_list = types->GetList();
+ const auto& counts_list = counts->GetListDeprecated();
+ const auto& types_list = types->GetListDeprecated();
// There should be three allocations, two coalesced ones, one with unique
// context, and one with no context.
@@ -369,7 +369,7 @@ TEST(ProfilingJsonExporterTest, Context) {
// Reconstruct the map from type id to string.
std::map<int, std::string> type_to_string;
- for (const auto& type : types_map->GetList()) {
+ for (const auto& type : types_map->GetListDeprecated()) {
const base::Value* id =
type.FindKeyOfType("id", base::Value::Type::INTEGER);
ASSERT_TRUE(id);
@@ -447,8 +447,8 @@ TEST(ProfilingJsonExporterTest, LargeAllocation) {
const base::Value* malloc =
parsed_json.value->FindPath({"heaps_v2", "allocators", "malloc"});
const base::Value* malloc_sizes = malloc->FindKey("sizes");
- EXPECT_EQ(1u, malloc_sizes->GetList().size());
- EXPECT_EQ(0x9876543210ul, malloc_sizes->GetList()[0].GetDouble());
+ EXPECT_EQ(1u, malloc_sizes->GetListDeprecated().size());
+ EXPECT_EQ(0x9876543210ul, malloc_sizes->GetListDeprecated()[0].GetDouble());
}
#endif
diff --git a/chromium/components/services/heap_profiling/public/cpp/heap_profiling_trace_source.cc b/chromium/components/services/heap_profiling/public/cpp/heap_profiling_trace_source.cc
index 31391d6b0d5..1860fe83ded 100644
--- a/chromium/components/services/heap_profiling/public/cpp/heap_profiling_trace_source.cc
+++ b/chromium/components/services/heap_profiling/public/cpp/heap_profiling_trace_source.cc
@@ -4,6 +4,7 @@
#include "components/services/heap_profiling/public/cpp/heap_profiling_trace_source.h"
+#include "base/no_destructor.h"
#include "base/profiler/frame.h"
#include "base/profiler/module_cache.h"
#include "base/trace_event/trace_event.h"
diff --git a/chromium/components/services/heap_profiling/public/cpp/profiling_client.cc b/chromium/components/services/heap_profiling/public/cpp/profiling_client.cc
index 72d2d2016be..964890d3773 100644
--- a/chromium/components/services/heap_profiling/public/cpp/profiling_client.cc
+++ b/chromium/components/services/heap_profiling/public/cpp/profiling_client.cc
@@ -21,16 +21,16 @@
#include "base/trace_event/memory_dump_manager.h"
#include "build/build_config.h"
-#if !defined(OS_IOS)
+#if !BUILDFLAG(IS_IOS)
#include "components/services/heap_profiling/public/cpp/heap_profiling_trace_source.h"
#endif
-#if defined(OS_ANDROID) && BUILDFLAG(CAN_UNWIND_WITH_CFI_TABLE) && \
+#if BUILDFLAG(IS_ANDROID) && BUILDFLAG(CAN_UNWIND_WITH_CFI_TABLE) && \
defined(OFFICIAL_BUILD)
#include "base/trace_event/cfi_backtrace_android.h"
#endif
-#if defined(OS_APPLE)
+#if BUILDFLAG(IS_APPLE)
#include "base/allocator/allocator_interception_mac.h"
#endif
@@ -51,14 +51,14 @@ void ProfilingClient::StartProfiling(mojom::ProfilingParamsPtr params,
started_profiling_ = true;
base::trace_event::MallocDumpProvider::GetInstance()->DisableMetrics();
-#if defined(OS_APPLE)
+#if BUILDFLAG(IS_APPLE)
// On macOS, this call is necessary to shim malloc zones that were created
// after startup. This cannot be done during shim initialization because the
// task scheduler has not yet been initialized.
base::allocator::PeriodicallyShimNewMallocZones();
#endif
-#if defined(OS_ANDROID) && BUILDFLAG(CAN_UNWIND_WITH_CFI_TABLE) && \
+#if BUILDFLAG(IS_ANDROID) && BUILDFLAG(CAN_UNWIND_WITH_CFI_TABLE) && \
defined(OFFICIAL_BUILD)
// On Android the unwinder initialization requires file reading before
// initializing shim. So, post task on background thread.
@@ -78,7 +78,7 @@ void ProfilingClient::StartProfiling(mojom::ProfilingParamsPtr params,
StartProfilingInternal(std::move(params), std::move(callback));
#endif
-#if !defined(OS_IOS)
+#if !BUILDFLAG(IS_IOS)
// Create trace source so that it registers itself to the tracing system.
HeapProfilingTraceSource::GetInstance();
#endif
@@ -215,7 +215,7 @@ void ProfilingClient::AddHeapProfileToTrace(
std::vector<base::SamplingHeapProfiler::Sample> samples =
profiler->GetSamples(/*profile_id=*/0);
-#if !defined(OS_IOS)
+#if !BUILDFLAG(IS_IOS)
bool success =
HeapProfilingTraceSource::GetInstance()->AddToTraceIfEnabled(samples);
#else
diff --git a/chromium/components/services/paint_preview_compositor/paint_preview_compositor_collection_impl.cc b/chromium/components/services/paint_preview_compositor/paint_preview_compositor_collection_impl.cc
index cfae6933194..7b54280f3c3 100644
--- a/chromium/components/services/paint_preview_compositor/paint_preview_compositor_collection_impl.cc
+++ b/chromium/components/services/paint_preview_compositor/paint_preview_compositor_collection_impl.cc
@@ -17,9 +17,9 @@
#include "third_party/skia/include/core/SkGraphics.h"
#include "third_party/skia/include/ports/SkFontConfigInterface.h"
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
#include "content/public/child/dwrite_font_proxy_init_win.h"
-#elif defined(OS_LINUX) || defined(OS_CHROMEOS)
+#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
#include "components/services/font/public/cpp/font_loader.h"
#endif
@@ -48,7 +48,7 @@ PaintPreviewCompositorCollectionImpl::PaintPreviewCompositorCollectionImpl(
// Adapted from content::InitializeSkia().
// TODO(crbug/1199857): Tune these limits.
constexpr int kMB = 1024 * 1024;
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
SkGraphics::SetFontCacheLimit(base::SysInfo::IsLowEndDevice() ? kMB
: 8 * kMB);
SkGraphics::SetResourceCacheTotalByteLimit(
@@ -56,15 +56,15 @@ PaintPreviewCompositorCollectionImpl::PaintPreviewCompositorCollectionImpl(
SkGraphics::SetResourceCacheSingleAllocationByteLimit(16 * kMB);
#else
SkGraphics::SetResourceCacheSingleAllocationByteLimit(64 * kMB);
-#endif // defined(OS_ANDROID)
+#endif // BUILDFLAG(IS_ANDROID)
if (!initialize_environment_)
return;
// Initialize font access for Skia.
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
content::InitializeDWriteFontProxy();
-#elif defined(OS_LINUX) || defined(OS_CHROMEOS)
+#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
mojo::PendingRemote<font_service::mojom::FontService> font_service;
content::UtilityThread::Get()->BindHostReceiver(
font_service.InitWithNewPipeAndPassReceiver());
@@ -87,7 +87,7 @@ PaintPreviewCompositorCollectionImpl::PaintPreviewCompositorCollectionImpl(
base::BindOnce([] { SkFontMgr::RefDefault(); }));
// Sanity check that fonts are working.
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
// No WebSandbox is provided on Linux so the local fonts aren't accessible.
// This is fine since since the subsetted fonts are provided in the SkPicture.
// However, we still need to check that the SkFontMgr starts as it is used by
@@ -100,7 +100,7 @@ PaintPreviewCompositorCollectionImpl::PaintPreviewCompositorCollectionImpl(
PaintPreviewCompositorCollectionImpl::~PaintPreviewCompositorCollectionImpl() {
g_in_shutdown_key.Set("true");
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
content::UninitializeDWriteFontProxy();
#endif
}
diff --git a/chromium/components/services/paint_preview_compositor/paint_preview_compositor_collection_impl.h b/chromium/components/services/paint_preview_compositor/paint_preview_compositor_collection_impl.h
index 7c09acbe61e..a7f8fe5d35b 100644
--- a/chromium/components/services/paint_preview_compositor/paint_preview_compositor_collection_impl.h
+++ b/chromium/components/services/paint_preview_compositor/paint_preview_compositor_collection_impl.h
@@ -21,7 +21,7 @@
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver.h"
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
#include "components/services/font/public/cpp/font_loader.h"
#include "third_party/skia/include/core/SkRefCnt.h"
#endif
@@ -73,7 +73,7 @@ class PaintPreviewCompositorCollectionImpl
std::unique_ptr<PaintPreviewCompositorImpl>>
compositors_;
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
sk_sp<font_service::FontLoader> font_loader_;
#endif
diff --git a/chromium/components/services/patch/content/patch_service.cc b/chromium/components/services/patch/content/patch_service.cc
index 1c8ff4fc446..d384c3cb559 100644
--- a/chromium/components/services/patch/content/patch_service.cc
+++ b/chromium/components/services/patch/content/patch_service.cc
@@ -4,7 +4,6 @@
#include "components/services/patch/content/patch_service.h"
-#include "base/no_destructor.h"
#include "components/services/patch/public/mojom/file_patcher.mojom.h"
#include "components/strings/grit/components_strings.h"
#include "content/public/browser/service_process_host.h"
diff --git a/chromium/components/services/print_compositor/print_compositor_impl.cc b/chromium/components/services/print_compositor/print_compositor_impl.cc
index 8f6f77381fb..e3739baf49f 100644
--- a/chromium/components/services/print_compositor/print_compositor_impl.cc
+++ b/chromium/components/services/print_compositor/print_compositor_impl.cc
@@ -29,12 +29,12 @@
#include "third_party/skia/src/utils/SkMultiPictureDocument.h"
#include "ui/accessibility/ax_tree_update.h"
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
#include "content/public/child/dwrite_font_proxy_init_win.h"
-#elif defined(OS_APPLE)
+#elif BUILDFLAG(IS_APPLE)
#include "third_party/blink/public/platform/platform.h"
#include "third_party/skia/include/core/SkFontMgr.h"
-#elif defined(OS_POSIX) && !defined(OS_ANDROID)
+#elif BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID)
#include "third_party/blink/public/platform/platform.h"
#endif
@@ -51,7 +51,7 @@ PrintCompositorImpl::PrintCompositorImpl(
if (!initialize_environment)
return;
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
// Initialize direct write font proxy so skia can use it.
content::InitializeDWriteFontProxy();
#endif
@@ -60,7 +60,7 @@ PrintCompositorImpl::PrintCompositorImpl(
SkGraphics::SetImageGeneratorFromEncodedDataFactory(
blink::WebImageGenerator::CreateAsSkImageGenerator);
-#if defined(OS_POSIX) && !defined(OS_ANDROID)
+#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID)
content::UtilityThread::Get()->EnsureBlinkInitializedWithSandboxSupport();
// Check that we have sandbox support on this platform.
DCHECK(blink::Platform::Current()->GetSandboxSupport());
@@ -68,7 +68,7 @@ PrintCompositorImpl::PrintCompositorImpl(
content::UtilityThread::Get()->EnsureBlinkInitialized();
#endif
-#if defined(OS_APPLE)
+#if BUILDFLAG(IS_APPLE)
// Check that font access is granted.
// This doesn't do comprehensive tests to make sure fonts can work properly.
// It is just a quick and simple check to catch things like improper sandbox
@@ -78,7 +78,7 @@ PrintCompositorImpl::PrintCompositorImpl(
}
PrintCompositorImpl::~PrintCompositorImpl() {
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
content::UninitializeDWriteFontProxy();
#endif
}
diff --git a/chromium/components/services/quarantine/BUILD.gn b/chromium/components/services/quarantine/BUILD.gn
index 4411ebbb4a4..d8d69e696ba 100644
--- a/chromium/components/services/quarantine/BUILD.gn
+++ b/chromium/components/services/quarantine/BUILD.gn
@@ -27,7 +27,6 @@ static_library("quarantine") {
if (is_win) {
sources += [ "quarantine_win.cc" ]
- deps += [ "//components/services/quarantine/public/cpp:features" ]
}
if (is_mac) {
@@ -136,7 +135,6 @@ source_set("unit_tests") {
if (is_win) {
sources += [ "quarantine_win_unittest.cc" ]
- deps += [ "//components/services/quarantine/public/cpp:features" ]
}
if (is_mac) {
diff --git a/chromium/components/services/quarantine/OWNERS b/chromium/components/services/quarantine/OWNERS
index 15eb1078310..2a42cbefb8f 100644
--- a/chromium/components/services/quarantine/OWNERS
+++ b/chromium/components/services/quarantine/OWNERS
@@ -1,2 +1 @@
-asanka@chromium.org
wfh@chromium.org
diff --git a/chromium/components/services/quarantine/README.md b/chromium/components/services/quarantine/README.md
index 8caf099c5e2..b23584c478e 100644
--- a/chromium/components/services/quarantine/README.md
+++ b/chromium/components/services/quarantine/README.md
@@ -1,6 +1,4 @@
Quarantine service to scan/mark a downloaded file with a mark-of-the-web.
-The service will run in browser process except for Windows
-with kOutOfProcessQuarantine flag set, where will run in a utility process
-
-TODO: Implement, add call sites, and refactor components/download/quarantine.
+The service will run in browser process except for Windows, where will run in a
+utility process
diff --git a/chromium/components/services/quarantine/public/cpp/BUILD.gn b/chromium/components/services/quarantine/public/cpp/BUILD.gn
deleted file mode 100644
index 40fab13c662..00000000000
--- a/chromium/components/services/quarantine/public/cpp/BUILD.gn
+++ /dev/null
@@ -1,16 +0,0 @@
-# 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.
-
-import("//mojo/public/tools/bindings/mojom.gni")
-
-if (is_win) {
- source_set("features") {
- sources = [
- "quarantine_features_win.cc",
- "quarantine_features_win.h",
- ]
-
- public_deps = [ "//base" ]
- }
-}
diff --git a/chromium/components/services/quarantine/public/cpp/quarantine_features_win.cc b/chromium/components/services/quarantine/public/cpp/quarantine_features_win.cc
deleted file mode 100644
index 47bf5a470f0..00000000000
--- a/chromium/components/services/quarantine/public/cpp/quarantine_features_win.cc
+++ /dev/null
@@ -1,15 +0,0 @@
-// 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 "components/services/quarantine/public/cpp/quarantine_features_win.h"
-
-namespace quarantine {
-
-// This feature controls whether the quarantine service should run in
-// the browser process or a new utility process.
-// Unused until quarantine service is fully implemented.
-const base::Feature kOutOfProcessQuarantine{"OutOfProcessQuarantine",
- base::FEATURE_ENABLED_BY_DEFAULT};
-
-} // namespace quarantine
diff --git a/chromium/components/services/quarantine/public/cpp/quarantine_features_win.h b/chromium/components/services/quarantine/public/cpp/quarantine_features_win.h
deleted file mode 100644
index b97fd1e6535..00000000000
--- a/chromium/components/services/quarantine/public/cpp/quarantine_features_win.h
+++ /dev/null
@@ -1,16 +0,0 @@
-// 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.
-
-#ifndef COMPONENTS_SERVICES_QUARANTINE_PUBLIC_CPP_QUARANTINE_FEATURES_WIN_H_
-#define COMPONENTS_SERVICES_QUARANTINE_PUBLIC_CPP_QUARANTINE_FEATURES_WIN_H_
-
-#include "base/feature_list.h"
-
-namespace quarantine {
-
-extern const base::Feature kOutOfProcessQuarantine;
-
-} // namespace quarantine
-
-#endif // COMPONENTS_SERVICES_QUARANTINE_PUBLIC_CPP_QUARANTINE_FEATURES_WIN_H_
diff --git a/chromium/components/services/quarantine/quarantine.cc b/chromium/components/services/quarantine/quarantine.cc
index 83e907cbfb4..84215bc6fa0 100644
--- a/chromium/components/services/quarantine/quarantine.cc
+++ b/chromium/components/services/quarantine/quarantine.cc
@@ -6,7 +6,7 @@
#include "build/build_config.h"
-#if !defined(OS_WIN) && !defined(OS_APPLE) && !defined(OS_CHROMEOS)
+#if !BUILDFLAG(IS_WIN) && !BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_CHROMEOS)
namespace quarantine {
diff --git a/chromium/components/services/quarantine/quarantine.h b/chromium/components/services/quarantine/quarantine.h
index 949a17921ba..be1b49a881b 100644
--- a/chromium/components/services/quarantine/quarantine.h
+++ b/chromium/components/services/quarantine/quarantine.h
@@ -73,7 +73,7 @@ void QuarantineFile(const base::FilePath& file,
const std::string& client_guid,
mojom::Quarantine::QuarantineFileCallback callback);
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
QuarantineFileResult SetInternetZoneIdentifierDirectly(
const base::FilePath& full_path,
const GURL& source_url,
diff --git a/chromium/components/services/quarantine/quarantine_impl.cc b/chromium/components/services/quarantine/quarantine_impl.cc
index 5fcd99151c8..63339384418 100644
--- a/chromium/components/services/quarantine/quarantine_impl.cc
+++ b/chromium/components/services/quarantine/quarantine_impl.cc
@@ -7,6 +7,7 @@
#include "base/bind.h"
#include "base/task/post_task.h"
#include "base/task/thread_pool.h"
+#include "build/build_config.h"
#include "components/services/quarantine/quarantine.h"
namespace quarantine {
@@ -35,16 +36,16 @@ void QuarantineImpl::QuarantineFile(
const GURL& referrer_url,
const std::string& client_guid,
mojom::Quarantine::QuarantineFileCallback callback) {
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
// On Mac posting to a new task runner to do the potentially blocking
// quarantine work.
scoped_refptr<base::TaskRunner> task_runner =
base::ThreadPool::CreateTaskRunner(
{base::MayBlock(), base::TaskPriority::USER_VISIBLE});
-#else // OS_MAC
+#else // BUILDFLAG(IS_MAC)
scoped_refptr<base::TaskRunner> task_runner =
base::ThreadTaskRunnerHandle::Get();
-#endif // OS_MAC
+#endif // BUILDFLAG(IS_MAC)
task_runner->PostTask(
FROM_HERE,
base::BindOnce(
diff --git a/chromium/components/services/quarantine/quarantine_impl.h b/chromium/components/services/quarantine/quarantine_impl.h
index 877414bb3c1..87dbc72b11a 100644
--- a/chromium/components/services/quarantine/quarantine_impl.h
+++ b/chromium/components/services/quarantine/quarantine_impl.h
@@ -12,9 +12,9 @@
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver.h"
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
#include "base/win/scoped_com_initializer.h"
-#endif // OS_WIN
+#endif // BUILDFLAG(IS_WIN)
namespace quarantine {
@@ -39,10 +39,10 @@ class QuarantineImpl : public mojom::Quarantine {
private:
mojo::Receiver<mojom::Quarantine> receiver_{this};
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
base::win::ScopedCOMInitializer com_initializer_{
base::win::ScopedCOMInitializer::Uninitialization::kBlockPremature};
-#endif // OS_WIN
+#endif // BUILDFLAG(IS_WIN)
};
} // namespace quarantine
diff --git a/chromium/components/services/quarantine/quarantine_unittest.cc b/chromium/components/services/quarantine/quarantine_unittest.cc
index 052430ab8f9..4701858bcb7 100644
--- a/chromium/components/services/quarantine/quarantine_unittest.cc
+++ b/chromium/components/services/quarantine/quarantine_unittest.cc
@@ -18,7 +18,7 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
#include "base/win/scoped_com_initializer.h"
#endif
@@ -39,7 +39,7 @@ void CheckQuarantineResult(QuarantineFileResult result,
class QuarantineTest : public testing::Test {
public:
void SetUp() override {
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
ASSERT_TRUE(com_initializer_.Succeeded());
#endif
ASSERT_TRUE(test_dir_.CreateUniqueTempDir());
@@ -54,7 +54,7 @@ class QuarantineTest : public testing::Test {
}
private:
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
base::win::ScopedCOMInitializer com_initializer_;
#endif
base::test::SingleThreadTaskEnvironment task_environment_;
diff --git a/chromium/components/services/quarantine/quarantine_win.cc b/chromium/components/services/quarantine/quarantine_win.cc
index e4cd1da3d70..010bb335d5e 100644
--- a/chromium/components/services/quarantine/quarantine_win.cc
+++ b/chromium/components/services/quarantine/quarantine_win.cc
@@ -29,7 +29,6 @@
#include "base/win/windows_version.h"
#include "components/services/quarantine/common.h"
#include "components/services/quarantine/common_win.h"
-#include "components/services/quarantine/public/cpp/quarantine_features_win.h"
#include "url/gurl.h"
namespace quarantine {
diff --git a/chromium/components/services/quarantine/quarantine_win_unittest.cc b/chromium/components/services/quarantine/quarantine_win_unittest.cc
index 3c38a6c2220..12540ae6899 100644
--- a/chromium/components/services/quarantine/quarantine_win_unittest.cc
+++ b/chromium/components/services/quarantine/quarantine_win_unittest.cc
@@ -19,7 +19,6 @@
#include "base/win/scoped_com_initializer.h"
#include "base/win/win_util.h"
#include "base/win/windows_version.h"
-#include "components/services/quarantine/public/cpp/quarantine_features_win.h"
#include "components/services/quarantine/quarantine.h"
#include "components/services/quarantine/test_support.h"
#include "net/base/filename_util.h"
diff --git a/chromium/components/services/quarantine/test_support.cc b/chromium/components/services/quarantine/test_support.cc
index 1bd7bc3b4f4..a2a09fbee61 100644
--- a/chromium/components/services/quarantine/test_support.cc
+++ b/chromium/components/services/quarantine/test_support.cc
@@ -6,7 +6,7 @@
#include "build/build_config.h"
-#if !defined(OS_WIN) && !defined(OS_APPLE)
+#if !BUILDFLAG(IS_WIN) && !BUILDFLAG(IS_APPLE)
namespace quarantine {
diff --git a/chromium/components/services/storage/BUILD.gn b/chromium/components/services/storage/BUILD.gn
index 007edf013a8..00ed503c1d1 100644
--- a/chromium/components/services/storage/BUILD.gn
+++ b/chromium/components/services/storage/BUILD.gn
@@ -77,6 +77,12 @@ source_set("storage") {
"service_worker/service_worker_storage.h",
"service_worker/service_worker_storage_control_impl.cc",
"service_worker/service_worker_storage_control_impl.h",
+ "shared_storage/async_shared_storage_database.cc",
+ "shared_storage/async_shared_storage_database.h",
+ "shared_storage/shared_storage_database.cc",
+ "shared_storage/shared_storage_database.h",
+ "shared_storage/shared_storage_options.cc",
+ "shared_storage/shared_storage_options.h",
"storage_service_impl.cc",
"storage_service_impl.h",
]
@@ -90,6 +96,7 @@ source_set("storage") {
"//components/services/storage/service_worker:service_worker_proto",
"//mojo/public/cpp/bindings",
"//sql",
+ "//third_party/abseil-cpp:absl",
"//third_party/blink/public/common",
"//third_party/leveldatabase",
"//url",
@@ -101,6 +108,7 @@ source_set("storage") {
"//net",
"//services/network/public/cpp",
"//services/network/public/mojom",
+ "//storage/browser",
"//storage/common",
]
}
@@ -136,6 +144,18 @@ component("test_api_stubs") {
defines = [ "IS_STORAGE_SERVICE_TEST_API_STUBS_IMPL" ]
}
+bundle_data("tests_bundle_data") {
+ visibility = [ ":tests" ]
+ testonly = true
+ sources = [
+ "//components/test/data/storage/shared_storage.v0.init_too_old.sql",
+ "//components/test/data/storage/shared_storage.v1.init_too_new.sql",
+ "//components/test/data/storage/shared_storage.v1.sql",
+ ]
+ outputs = [ "{{bundle_resources_dir}}/" +
+ "{{source_root_relative_dir}}/{{source_file_part}}" ]
+}
+
source_set("tests") {
testonly = true
@@ -164,12 +184,15 @@ source_set("tests") {
"service_worker/service_worker_storage_test_utils.cc",
"service_worker/service_worker_storage_test_utils.h",
"service_worker/service_worker_storage_unittest.cc",
+ "shared_storage/async_shared_storage_database_unittest.cc",
+ "shared_storage/shared_storage_database_unittest.cc",
"storage_service_impl_unittest.cc",
]
deps = [
":storage",
":test_support",
+ ":tests_bundle_data",
"//base",
"//base/test:test_support",
"//components/services/storage/public/cpp",
@@ -180,8 +203,13 @@ source_set("tests") {
"//mojo/public/cpp/system",
"//net",
"//net:test_support",
+ "//sql",
+ "//sql:test_support",
+ "//storage/browser:browser",
+ "//storage/browser:test_support",
"//testing/gmock",
"//testing/gtest",
+ "//third_party/sqlite",
]
data = [ "//components/services/storage/test_data/" ]
@@ -201,11 +229,14 @@ source_set("test_support") {
"indexed_db/leveldb/mock_level_db.h",
"indexed_db/scopes/leveldb_scopes_test_utils.cc",
"indexed_db/scopes/leveldb_scopes_test_utils.h",
+ "shared_storage/shared_storage_test_utils.cc",
+ "shared_storage/shared_storage_test_utils.h",
]
deps = [
":storage",
"//base/test:test_support",
+ "//sql:test_support",
"//testing/gmock",
"//testing/gtest",
"//third_party/leveldatabase",
diff --git a/chromium/components/services/storage/DEPS b/chromium/components/services/storage/DEPS
index fadaaf00f3c..8c47aae7a79 100644
--- a/chromium/components/services/storage/DEPS
+++ b/chromium/components/services/storage/DEPS
@@ -4,4 +4,6 @@ include_rules = [
"+third_party/blink/public/mojom",
"+third_party/leveldatabase",
"+sql",
+ "+storage/browser/quota/special_storage_policy.h",
+ "+storage/browser/test/mock_special_storage_policy.h",
]
diff --git a/chromium/components/services/storage/dom_storage/local_storage_impl.cc b/chromium/components/services/storage/dom_storage/local_storage_impl.cc
index 749ba0bb361..82a90a61105 100644
--- a/chromium/components/services/storage/dom_storage/local_storage_impl.cc
+++ b/chromium/components/services/storage/dom_storage/local_storage_impl.cc
@@ -79,7 +79,7 @@ const int kCommitErrorThreshold = 8;
// Limits on the cache size and number of areas in memory, over which the areas
// are purged.
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
const unsigned kMaxLocalStorageAreaCount = 10;
const size_t kMaxLocalStorageCacheSize = 2 * 1024 * 1024;
#else
@@ -213,7 +213,7 @@ class LocalStorageImpl::StorageAreaHolder final
options.default_commit_delay = kCommitDefaultDelaySecs;
options.max_bytes_per_hour = kMaxBytesPerHour;
options.max_commits_per_hour = kMaxCommitsPerHour;
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
options.cache_mode = StorageAreaImpl::CacheMode::KEYS_ONLY_WHEN_POSSIBLE;
#else
options.cache_mode = StorageAreaImpl::CacheMode::KEYS_AND_VALUES;
diff --git a/chromium/components/services/storage/dom_storage/local_storage_impl_unittest.cc b/chromium/components/services/storage/dom_storage/local_storage_impl_unittest.cc
index 89ddf64633f..3b617b6302c 100644
--- a/chromium/components/services/storage/dom_storage/local_storage_impl_unittest.cc
+++ b/chromium/components/services/storage/dom_storage/local_storage_impl_unittest.cc
@@ -4,13 +4,14 @@
#include "components/services/storage/dom_storage/local_storage_impl.h"
+#include <tuple>
+
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/containers/span.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
-#include "base/ignore_result.h"
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
@@ -256,7 +257,7 @@ class LocalStorageImplTest : public testing::Test {
base::RunLoop run_loop;
std::vector<blink::mojom::KeyValuePtr> data;
mojo::PendingRemote<blink::mojom::StorageAreaObserver> unused_observer;
- ignore_result(unused_observer.InitWithNewPipeAndPassReceiver());
+ std::ignore = unused_observer.InitWithNewPipeAndPassReceiver();
area->GetAll(std::move(unused_observer),
test::MakeGetAllCallback(run_loop.QuitClosure(), &data));
run_loop.Run();
diff --git a/chromium/components/services/storage/dom_storage/session_storage_data_map_unittest.cc b/chromium/components/services/storage/dom_storage/session_storage_data_map_unittest.cc
index b798967fd47..5d3362c5473 100644
--- a/chromium/components/services/storage/dom_storage/session_storage_data_map_unittest.cc
+++ b/chromium/components/services/storage/dom_storage/session_storage_data_map_unittest.cc
@@ -5,11 +5,11 @@
#include "components/services/storage/dom_storage/session_storage_data_map.h"
#include <map>
+#include <tuple>
#include <vector>
#include "base/bind.h"
#include "base/containers/span.h"
-#include "base/ignore_result.h"
#include "base/memory/ptr_util.h"
#include "base/run_loop.h"
#include "base/task/post_task.h"
@@ -41,7 +41,7 @@ base::span<const uint8_t> MakeBytes(base::StringPiece str) {
mojo::PendingRemote<blink::mojom::StorageAreaObserver> MakeStubObserver() {
mojo::PendingRemote<blink::mojom::StorageAreaObserver> observer;
- ignore_result(observer.InitWithNewPipeAndPassReceiver());
+ std::ignore = observer.InitWithNewPipeAndPassReceiver();
return observer;
}
diff --git a/chromium/components/services/storage/dom_storage/session_storage_impl.cc b/chromium/components/services/storage/dom_storage/session_storage_impl.cc
index 70bd2345f7c..80aecec0563 100644
--- a/chromium/components/services/storage/dom_storage/session_storage_impl.cc
+++ b/chromium/components/services/storage/dom_storage/session_storage_impl.cc
@@ -41,7 +41,7 @@ const int kSessionStorageCommitErrorThreshold = 8;
// Limits on the cache size and number of areas in memory, over which the areas
// are purged.
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
const unsigned kMaxSessionStorageAreaCount = 10;
const size_t kMaxSessionStorageCacheSize = 2 * 1024 * 1024;
#else
diff --git a/chromium/components/services/storage/dom_storage/storage_area_impl_unittest.cc b/chromium/components/services/storage/dom_storage/storage_area_impl_unittest.cc
index 502c7b980bc..299d5380ab0 100644
--- a/chromium/components/services/storage/dom_storage/storage_area_impl_unittest.cc
+++ b/chromium/components/services/storage/dom_storage/storage_area_impl_unittest.cc
@@ -7,13 +7,13 @@
#include <list>
#include <memory>
#include <string>
+#include <tuple>
#include <vector>
#include "base/atomic_ref_count.h"
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/containers/span.h"
-#include "base/ignore_result.h"
#include "base/memory/ptr_util.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
@@ -903,7 +903,7 @@ TEST_F(StorageAreaImplTest, GetAllWhenCacheOnlyKeys) {
MakeSuccessCallback(barrier.AddClosure(), &put_result1));
mojo::PendingRemote<blink::mojom::StorageAreaObserver> unused_observer;
- ignore_result(unused_observer.InitWithNewPipeAndPassReceiver());
+ std::ignore = unused_observer.InitWithNewPipeAndPassReceiver();
storage_area()->GetAll(std::move(unused_observer),
MakeGetAllCallback(barrier.AddClosure(), &data));
storage_area()->Put(
@@ -974,7 +974,7 @@ TEST_F(StorageAreaImplTest, GetAllAfterSetCacheMode) {
upgrade_loop.Run();
mojo::PendingRemote<blink::mojom::StorageAreaObserver> unused_observer;
- ignore_result(unused_observer.InitWithNewPipeAndPassReceiver());
+ std::ignore = unused_observer.InitWithNewPipeAndPassReceiver();
storage_area()->GetAll(
std::move(unused_observer),
MakeGetAllCallback(upgrade_loop.QuitClosure(), &data));
diff --git a/chromium/components/services/storage/dom_storage/testing_legacy_session_storage_database.cc b/chromium/components/services/storage/dom_storage/testing_legacy_session_storage_database.cc
index 29cd8d1609f..ecd979bc3e1 100644
--- a/chromium/components/services/storage/dom_storage/testing_legacy_session_storage_database.cc
+++ b/chromium/components/services/storage/dom_storage/testing_legacy_session_storage_database.cc
@@ -499,7 +499,7 @@ leveldb::Status TestingLegacySessionStorageDatabase::TryToOpen(
options.block_cache = leveldb_chrome::GetSharedWebBlockCache();
std::string db_name = file_path_.AsUTF8Unsafe();
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
// On Android there is no support for session storage restoring, and since
// the restoring code is responsible for database cleanup, we must manually
// delete the old database here before we open it.
diff --git a/chromium/components/services/storage/indexed_db/leveldb/leveldb_factory.cc b/chromium/components/services/storage/indexed_db/leveldb/leveldb_factory.cc
index c4082447fb4..dd3646d6e61 100644
--- a/chromium/components/services/storage/indexed_db/leveldb/leveldb_factory.cc
+++ b/chromium/components/services/storage/indexed_db/leveldb/leveldb_factory.cc
@@ -50,16 +50,13 @@ std::tuple<scoped_refptr<LevelDBState>,
DefaultLevelDBFactory::OpenLevelDBState(const base::FilePath& file_name,
bool create_if_missing,
size_t write_buffer_size) {
- leveldb::Status status;
- std::unique_ptr<leveldb::DB> db;
-
if (file_name.empty()) {
if (!create_if_missing)
return {nullptr, leveldb::Status::NotFound("", ""), false};
std::unique_ptr<leveldb::Env> in_memory_env =
leveldb_chrome::NewMemEnv(in_memory_db_name_, options_.env);
- std::tie(db, status) = OpenInMemoryDB(in_memory_env.get());
+ auto [db, status] = OpenInMemoryDB(in_memory_env.get());
if (UNLIKELY(!status.ok())) {
LOG(ERROR) << "Failed to open in-memory LevelDB database: "
<< status.ToString();
@@ -73,7 +70,7 @@ DefaultLevelDBFactory::OpenLevelDBState(const base::FilePath& file_name,
}
// ChromiumEnv assumes UTF8, converts back to FilePath before using.
- std::tie(db, status) =
+ auto [db, status] =
OpenDB(file_name.AsUTF8Unsafe(), create_if_missing, write_buffer_size);
if (UNLIKELY(!status.ok())) {
if (!create_if_missing && status.IsInvalidArgument())
diff --git a/chromium/components/services/storage/indexed_db/scopes/leveldb_scope.h b/chromium/components/services/storage/indexed_db/scopes/leveldb_scope.h
index 26f70a76dfc..13f5d1723f6 100644
--- a/chromium/components/services/storage/indexed_db/scopes/leveldb_scope.h
+++ b/chromium/components/services/storage/indexed_db/scopes/leveldb_scope.h
@@ -13,7 +13,6 @@
#include "base/callback.h"
#include "base/check_op.h"
-#include "base/compiler_specific.h"
#include "base/containers/flat_map.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
@@ -81,19 +80,19 @@ class LevelDBScope {
return scope_id_;
}
- leveldb::Status Put(const leveldb::Slice& key,
- const leveldb::Slice& value) WARN_UNUSED_RESULT;
- leveldb::Status Delete(const leveldb::Slice& key) WARN_UNUSED_RESULT;
+ [[nodiscard]] leveldb::Status Put(const leveldb::Slice& key,
+ const leveldb::Slice& value);
+ [[nodiscard]] leveldb::Status Delete(const leveldb::Slice& key);
// Deletes the range. |begin| is always inclusive. See
// |LevelDBScopeDeletionMode| for the different types of range deletion.
- leveldb::Status DeleteRange(const leveldb::Slice& begin,
- const leveldb::Slice& end,
- LevelDBScopeDeletionMode mode) WARN_UNUSED_RESULT;
+ [[nodiscard]] leveldb::Status DeleteRange(const leveldb::Slice& begin,
+ const leveldb::Slice& end,
+ LevelDBScopeDeletionMode mode);
// Submits pending changes & the undo log to LevelDB. Required to be able to
// read any keys that have been submitted to |Put|, |Delete|, or
// |DeleteRange|.
- leveldb::Status WriteChangesAndUndoLog() WARN_UNUSED_RESULT;
+ [[nodiscard]] leveldb::Status WriteChangesAndUndoLog();
// In the case of LevelDBScopes being in the mode
// TaskRunnerMode::kUseCurrentSequence, rollbacks happen synchronously. The
@@ -148,8 +147,7 @@ class LevelDBScope {
// status & the mode of this scope. The caller (LevelDBScopes) is expected to
// queue up a cleanup task if the mode is kUndoLogOnDisk. This instance should
// not be used after this call.
- std::pair<leveldb::Status, Mode> Commit(bool sync_on_commit)
- WARN_UNUSED_RESULT;
+ [[nodiscard]] std::pair<leveldb::Status, Mode> Commit(bool sync_on_commit);
// Submits pending changes & the undo log to LevelDB. Required to be able to
// read any keys that have been submitted to Put, Delete, or
@@ -181,7 +179,7 @@ class LevelDBScope {
bool CanSkipWritingUndoEntry(const leveldb::Slice& key);
void AddCommitPoint();
- leveldb::Status WriteBufferBatch(bool sync) WARN_UNUSED_RESULT;
+ [[nodiscard]] leveldb::Status WriteBufferBatch(bool sync);
#if DCHECK_IS_ON()
std::vector<std::pair<std::string, std::string>> deferred_delete_ranges_;
diff --git a/chromium/components/services/storage/indexed_db/scopes/leveldb_scopes.cc b/chromium/components/services/storage/indexed_db/scopes/leveldb_scopes.cc
index e45257801f0..e2d1299ba13 100644
--- a/chromium/components/services/storage/indexed_db/scopes/leveldb_scopes.cc
+++ b/chromium/components/services/storage/indexed_db/scopes/leveldb_scopes.cc
@@ -111,9 +111,7 @@ leveldb::Status LevelDBScopes::Initialize() {
for (; iterator->Valid() && iterator->key().starts_with(prefix_key);
iterator->Next()) {
// Parse the key & value.
- int64_t scope_id;
- bool success;
- std::tie(success, scope_id) = leveldb_scopes::ParseScopeMetadataId(
+ auto [success, scope_id] = leveldb_scopes::ParseScopeMetadataId(
iterator->key(), metadata_key_prefix_);
if (UNLIKELY(!success)) {
return leveldb::Status::Corruption(base::StrCat(
@@ -274,9 +272,7 @@ leveldb::Status LevelDBScopes::Commit(std::unique_ptr<LevelDBScope> scope,
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(recovery_finished_);
DCHECK(cleanup_runner_);
- LevelDBScope::Mode scopes_mode;
- leveldb::Status s;
- std::tie(s, scopes_mode) = scope->Commit(sync_on_commit);
+ auto [status, scopes_mode] = scope->Commit(sync_on_commit);
if (scopes_mode == LevelDBScope::Mode::kUndoLogOnDisk) {
auto task = std::make_unique<CleanupScopeTask>(
level_db_, metadata_key_prefix_, scope->scope_id(),
@@ -288,7 +284,7 @@ leveldb::Status LevelDBScopes::Commit(std::unique_ptr<LevelDBScope> scope,
base::BindOnce(&LevelDBScopes::OnCleanupTaskResult,
weak_factory_.GetWeakPtr(), std::move(on_complete)));
}
- return s;
+ return status;
}
leveldb::Status LevelDBScopes::Rollback(int64_t scope_id,
diff --git a/chromium/components/services/storage/indexed_db/scopes/leveldb_scopes_tasks.h b/chromium/components/services/storage/indexed_db/scopes/leveldb_scopes_tasks.h
index 97c02f337c9..f8fca7817c0 100644
--- a/chromium/components/services/storage/indexed_db/scopes/leveldb_scopes_tasks.h
+++ b/chromium/components/services/storage/indexed_db/scopes/leveldb_scopes_tasks.h
@@ -9,7 +9,6 @@
#include <vector>
#include "base/callback.h"
-#include "base/compiler_specific.h"
#include "base/memory/scoped_refptr.h"
#include "base/sequence_checker.h"
#include "components/services/storage/indexed_db/leveldb/leveldb_state.h"
@@ -38,18 +37,18 @@ class LevelDBScopesTask {
protected:
// Submits the in-progress WriteBatch to LevelDB, no matter what size the
// batch is.
- leveldb::Status SubmitWriteBatch(const leveldb::WriteOptions& options)
- WARN_UNUSED_RESULT;
+ [[nodiscard]] leveldb::Status SubmitWriteBatch(
+ const leveldb::WriteOptions& options);
// Submits thein-progress WriteBatch to LevelDB only if the approximate size
// of the batch is > |max_write_batch_size_|.
- leveldb::Status MaybeSubmitWriteBatch(const leveldb::WriteOptions& options)
- WARN_UNUSED_RESULT;
+ [[nodiscard]] leveldb::Status MaybeSubmitWriteBatch(
+ const leveldb::WriteOptions& options);
- leveldb::Status DeleteRange(leveldb::Slice range_start,
- leveldb::Slice range_end,
- const leveldb::ReadOptions& read_options,
- const leveldb::WriteOptions& write_options)
- WARN_UNUSED_RESULT;
+ [[nodiscard]] leveldb::Status DeleteRange(
+ leveldb::Slice range_start,
+ leveldb::Slice range_end,
+ const leveldb::ReadOptions& read_options,
+ const leveldb::WriteOptions& write_options);
SEQUENCE_CHECKER(sequence_checker_);
@@ -79,16 +78,16 @@ class CleanupScopeTask : private LevelDBScopesTask {
size_t max_write_batch_size_bytes);
~CleanupScopeTask();
- leveldb::Status Run() WARN_UNUSED_RESULT;
+ [[nodiscard]] leveldb::Status Run();
private:
- leveldb::Status ExecuteAndDeleteCleanupTasks(
+ [[nodiscard]] leveldb::Status ExecuteAndDeleteCleanupTasks(
const leveldb::ReadOptions& read_options,
- const leveldb::WriteOptions& write_options) WARN_UNUSED_RESULT;
- leveldb::Status DeletePrefixedRange(
+ const leveldb::WriteOptions& write_options);
+ [[nodiscard]] leveldb::Status DeletePrefixedRange(
leveldb::Slice prefix,
const leveldb::ReadOptions& read_options,
- const leveldb::WriteOptions& write_options) WARN_UNUSED_RESULT;
+ const leveldb::WriteOptions& write_options);
const std::vector<uint8_t> metadata_prefix_;
const int64_t scope_number_;
@@ -109,7 +108,7 @@ class RevertScopeTask : private LevelDBScopesTask {
size_t max_write_batch_size_bytes);
~RevertScopeTask();
- leveldb::Status Run() WARN_UNUSED_RESULT;
+ [[nodiscard]] leveldb::Status Run();
private:
const std::vector<uint8_t> metadata_prefix_;
diff --git a/chromium/components/services/storage/indexed_db/scopes/leveldb_scopes_test_utils.cc b/chromium/components/services/storage/indexed_db/scopes/leveldb_scopes_test_utils.cc
index 9486bf3a1b0..cf380e68105 100644
--- a/chromium/components/services/storage/indexed_db/scopes/leveldb_scopes_test_utils.cc
+++ b/chromium/components/services/storage/indexed_db/scopes/leveldb_scopes_test_utils.cc
@@ -101,9 +101,7 @@ void LevelDBScopesTestBase::SetUpBreakableDB(
TearDown();
ASSERT_TRUE(temp_directory_.CreateUniqueTempDir());
- leveldb::Status status;
- std::unique_ptr<leveldb::DB> temp_real_db;
- std::tie(temp_real_db, status) =
+ auto [temp_real_db, status] =
leveldb_factory_->OpenDB(temp_directory_.GetPath().AsUTF8Unsafe(),
/*create_if_missing=*/true, kWriteBufferSize);
ASSERT_TRUE(status.ok());
@@ -124,10 +122,8 @@ void LevelDBScopesTestBase::SetUpFlakyDB(
if (leveldb_)
TearDown();
ASSERT_TRUE(temp_directory_.CreateUniqueTempDir());
- leveldb::Status status;
- std::unique_ptr<leveldb::DB> temp_db;
- std::tie(temp_db, status) =
+ auto [temp_db, status] =
leveldb_factory_->OpenDB(temp_directory_.GetPath().AsUTF8Unsafe(),
/*create_if_missing=*/true, kWriteBufferSize);
ASSERT_TRUE(status.ok());
diff --git a/chromium/components/services/storage/indexed_db/scopes/varint_coding.h b/chromium/components/services/storage/indexed_db/scopes/varint_coding.h
index 640061229cb..eca8f086a92 100644
--- a/chromium/components/services/storage/indexed_db/scopes/varint_coding.h
+++ b/chromium/components/services/storage/indexed_db/scopes/varint_coding.h
@@ -22,7 +22,7 @@ void EncodeVarInt(int64_t from, std::string* into);
// Decodes a varint from the given string piece into the given int64_t. Returns
// if the string had a valid varint (where a byte was found with it's top bit
// set). This function does NOT check to see if move than 64 bits were read.
-WARN_UNUSED_RESULT bool DecodeVarInt(base::StringPiece* from, int64_t* into);
+[[nodiscard]] bool DecodeVarInt(base::StringPiece* from, int64_t* into);
} // namespace content
diff --git a/chromium/components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_database.cc b/chromium/components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_database.cc
index 0c8aa710d02..473a0b1d85a 100644
--- a/chromium/components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_database.cc
+++ b/chromium/components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_database.cc
@@ -53,7 +53,7 @@ namespace {
//
// Sync writes are necessary on Windows for quota calculations; POSIX
// calculates file sizes correctly even when not synced to disk.
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
const bool kSyncWrites = true;
#else
// TODO(dgrogan): Either remove the #if block or change this back to false.
diff --git a/chromium/components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_iterator.cc b/chromium/components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_iterator.cc
index c9d2b0a5bb0..8b2932c34dd 100644
--- a/chromium/components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_iterator.cc
+++ b/chromium/components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_iterator.cc
@@ -113,11 +113,9 @@ leveldb::Status TransactionalLevelDBIterator::Next() {
CheckState();
bool iterator_is_loaded = (iterator_ != nullptr);
- std::string key_before_eviction;
- leveldb::Status s;
- std::tie(key_before_eviction, s) = WillUseDBIterator(/*perform_seek=*/true);
- if (!s.ok())
- return s;
+ auto [key_before_eviction, status] = WillUseDBIterator(/*perform_seek=*/true);
+ if (!status.ok())
+ return status;
DCHECK(iterator_);
// Exit early if not valid.
diff --git a/chromium/components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_iterator.h b/chromium/components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_iterator.h
index aa497672c5d..293ce558002 100644
--- a/chromium/components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_iterator.h
+++ b/chromium/components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_iterator.h
@@ -72,16 +72,16 @@ class TransactionalLevelDBIterator {
enum class Direction { kNext, kPrev };
enum class IteratorState { kActive, kEvictedAndValid, kEvictedAndInvalid };
- leveldb::Status WrappedIteratorStatus() WARN_UNUSED_RESULT;
+ [[nodiscard]] leveldb::Status WrappedIteratorStatus();
// Notifies the database of iterator usage and recreates iterator if needed.
// If the iterator was previously evicted, this method returns the key that
// was used, the status of reloading the iterator.
- std::tuple<std::string, leveldb::Status> WillUseDBIterator(bool perform_seek)
- WARN_UNUSED_RESULT;
+ [[nodiscard]] std::tuple<std::string, leveldb::Status> WillUseDBIterator(
+ bool perform_seek);
// If this method fails, then iterator_ will be nullptr.
- leveldb::Status ReloadIterator() WARN_UNUSED_RESULT;
+ [[nodiscard]] leveldb::Status ReloadIterator();
void NextPastScopesMetadata();
void PrevPastScopesMetadata();
diff --git a/chromium/components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_transaction.h b/chromium/components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_transaction.h
index a3a5c752fcc..2c4652fc481 100644
--- a/chromium/components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_transaction.h
+++ b/chromium/components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_transaction.h
@@ -10,7 +10,6 @@
#include <string>
#include "base/callback.h"
-#include "base/compiler_specific.h"
#include "base/containers/flat_set.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
@@ -50,24 +49,24 @@ class TransactionalLevelDBTransaction
TransactionalLevelDBTransaction& operator=(
const TransactionalLevelDBTransaction&) = delete;
- leveldb::Status Put(const base::StringPiece& key,
- std::string* value) WARN_UNUSED_RESULT;
+ [[nodiscard]] leveldb::Status Put(const base::StringPiece& key,
+ std::string* value);
- leveldb::Status Remove(const base::StringPiece& key) WARN_UNUSED_RESULT;
+ [[nodiscard]] leveldb::Status Remove(const base::StringPiece& key);
- leveldb::Status RemoveRange(const base::StringPiece& begin,
- const base::StringPiece& end,
- LevelDBScopeDeletionMode deletion_mode)
- WARN_UNUSED_RESULT;
+ [[nodiscard]] leveldb::Status RemoveRange(
+ const base::StringPiece& begin,
+ const base::StringPiece& end,
+ LevelDBScopeDeletionMode deletion_mode);
- virtual leveldb::Status Get(const base::StringPiece& key,
- std::string* value,
- bool* found) WARN_UNUSED_RESULT;
- virtual leveldb::Status Commit(bool sync_on_commit) WARN_UNUSED_RESULT;
+ [[nodiscard]] virtual leveldb::Status Get(const base::StringPiece& key,
+ std::string* value,
+ bool* found);
+ [[nodiscard]] virtual leveldb::Status Commit(bool sync_on_commit);
// If the underlying scopes system is in single-sequence mode, then this
// method will return the result of the rollback task.
- leveldb::Status Rollback() WARN_UNUSED_RESULT;
+ [[nodiscard]] leveldb::Status Rollback();
// The returned iterator must be destroyed before the destruction of this
// transaction. This may return null, if it does, status will explain why.
diff --git a/chromium/components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_transaction_unittest.cc b/chromium/components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_transaction_unittest.cc
index 644beeed548..f2602dae6a6 100644
--- a/chromium/components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_transaction_unittest.cc
+++ b/chromium/components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_transaction_unittest.cc
@@ -15,7 +15,6 @@
#include "base/files/file.h"
#include "base/files/file_path.h"
-#include "base/no_destructor.h"
#include "base/strings/string_piece.h"
#include "base/test/bind.h"
#include "base/threading/sequenced_task_runner_handle.h"
diff --git a/chromium/components/services/storage/partition_impl.cc b/chromium/components/services/storage/partition_impl.cc
index 88d5a877127..d77cb004a94 100644
--- a/chromium/components/services/storage/partition_impl.cc
+++ b/chromium/components/services/storage/partition_impl.cc
@@ -88,7 +88,7 @@ void PartitionImpl::BindSessionStorageControl(
{base::MayBlock(), base::WithBaseSyncPrimitives(),
base::TaskShutdownBehavior::BLOCK_SHUTDOWN}),
base::SequencedTaskRunnerHandle::Get(),
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
// On Android there is no support for session storage restoring, and since
// the restoring code is responsible for database cleanup, we must
// manually delete the old database here before we open a new one.
diff --git a/chromium/components/services/storage/public/cpp/BUILD.gn b/chromium/components/services/storage/public/cpp/BUILD.gn
index 3b259d0b6cf..867ebf97832 100644
--- a/chromium/components/services/storage/public/cpp/BUILD.gn
+++ b/chromium/components/services/storage/public/cpp/BUILD.gn
@@ -10,7 +10,6 @@ component("cpp") {
"constants.h",
"quota_client_callback_wrapper.h",
"quota_error_or.h",
- "storage_key_quota_client.h",
]
sources = [
diff --git a/chromium/components/services/storage/public/cpp/constants.cc b/chromium/components/services/storage/public/cpp/constants.cc
index c79decd3187..0ca6aa5ba1f 100644
--- a/chromium/components/services/storage/public/cpp/constants.cc
+++ b/chromium/components/services/storage/public/cpp/constants.cc
@@ -24,4 +24,8 @@ const char kLocalStorageLeveldbName[] = "leveldb";
const base::FilePath::CharType kServiceWorkerDirectory[] =
FILE_PATH_LITERAL("Service Worker");
+// The file name of the database storing media license data.
+const base::FilePath::CharType kMediaLicenseDatabaseFileName[] =
+ FILE_PATH_LITERAL("Media Licenses.db");
+
} // namespace storage
diff --git a/chromium/components/services/storage/public/cpp/constants.h b/chromium/components/services/storage/public/cpp/constants.h
index 3897d6b73b4..b2cdcf60758 100644
--- a/chromium/components/services/storage/public/cpp/constants.h
+++ b/chromium/components/services/storage/public/cpp/constants.h
@@ -22,6 +22,9 @@ extern const char kLocalStorageLeveldbName[];
COMPONENT_EXPORT(STORAGE_SERVICE_PUBLIC)
extern const base::FilePath::CharType kServiceWorkerDirectory[];
+COMPONENT_EXPORT(STORAGE_SERVICE_PUBLIC)
+extern const base::FilePath::CharType kMediaLicenseDatabaseFileName[];
+
} // namespace storage
#endif // COMPONENTS_SERVICES_STORAGE_PUBLIC_CPP_CONSTANTS_H_
diff --git a/chromium/components/services/storage/public/cpp/filesystem/filesystem_impl.cc b/chromium/components/services/storage/public/cpp/filesystem/filesystem_impl.cc
index fab8aa8f523..fbe4ab5b280 100644
--- a/chromium/components/services/storage/public/cpp/filesystem/filesystem_impl.cc
+++ b/chromium/components/services/storage/public/cpp/filesystem/filesystem_impl.cc
@@ -19,7 +19,7 @@
#include "build/build_config.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
#include <windows.h>
#endif
@@ -81,7 +81,7 @@ class FileLockImpl : public mojom::FileLock {
return;
}
-#if defined(OS_FUCHSIA)
+#if BUILDFLAG(IS_FUCHSIA)
std::move(callback).Run(base::File::FILE_OK);
#else
std::move(callback).Run(file_.Unlock());
@@ -124,12 +124,11 @@ void FilesystemImpl::GetEntries(const base::FilePath& path,
// Fix up the absolute paths to be relative to |path|.
std::vector<base::FilePath> entries;
- std::vector<base::FilePath::StringType> root_components;
- full_path.GetComponents(&root_components);
+ std::vector<base::FilePath::StringType> root_components =
+ full_path.GetComponents();
const size_t num_components_to_strip = root_components.size();
for (const auto& entry : result.value()) {
- std::vector<base::FilePath::StringType> components;
- entry.GetComponents(&components);
+ std::vector<base::FilePath::StringType> components = entry.GetComponents();
base::FilePath relative_path;
for (size_t i = num_components_to_strip; i < components.size(); ++i)
relative_path = relative_path.Append(components[i]);
@@ -288,7 +287,7 @@ base::FileErrorOr<base::File> FilesystemImpl::LockFileLocal(
if (!GetLockTable().AddLock(path))
return base::File::FILE_ERROR_IN_USE;
-#if !defined(OS_FUCHSIA)
+#if !BUILDFLAG(IS_FUCHSIA)
base::File::Error error = file.Lock(base::File::LockMode::kExclusive);
if (error != base::File::FILE_OK)
return error;
@@ -306,7 +305,7 @@ void FilesystemImpl::UnlockFileLocal(const base::FilePath& path) {
mojom::PathAccessInfoPtr FilesystemImpl::GetPathAccessLocal(
const base::FilePath& path) {
mojom::PathAccessInfoPtr info;
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
uint32_t attributes = ::GetFileAttributes(path.value().c_str());
if (attributes != INVALID_FILE_ATTRIBUTES) {
info = mojom::PathAccessInfo::New();
@@ -314,7 +313,7 @@ mojom::PathAccessInfoPtr FilesystemImpl::GetPathAccessLocal(
if ((attributes & FILE_ATTRIBUTE_READONLY) == 0)
info->can_write = true;
}
-#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
const char* const c_path = path.value().c_str();
if (!access(c_path, F_OK)) {
info = mojom::PathAccessInfo::New();
diff --git a/chromium/components/services/storage/public/cpp/filesystem/filesystem_proxy.cc b/chromium/components/services/storage/public/cpp/filesystem/filesystem_proxy.cc
index 67a934a7c40..c2d7895529f 100644
--- a/chromium/components/services/storage/public/cpp/filesystem/filesystem_proxy.cc
+++ b/chromium/components/services/storage/public/cpp/filesystem/filesystem_proxy.cc
@@ -23,12 +23,6 @@ namespace storage {
namespace {
-size_t GetNumPathComponents(const base::FilePath& path) {
- std::vector<base::FilePath::StringType> components;
- path.GetComponents(&components);
- return components.size();
-}
-
class LocalFileLockImpl : public FilesystemProxy::FileLock {
public:
LocalFileLockImpl(base::FilePath path, base::File lock)
@@ -41,7 +35,7 @@ class LocalFileLockImpl : public FilesystemProxy::FileLock {
// FilesystemProxy::FileLock implementation:
base::File::Error Release() override {
base::File::Error error = base::File::FILE_OK;
-#if !defined(OS_FUCHSIA)
+#if !BUILDFLAG(IS_FUCHSIA)
error = lock_.Unlock();
#endif
lock_.Close();
@@ -89,7 +83,7 @@ FilesystemProxy::FilesystemProxy(
mojo::PendingRemote<mojom::Directory> directory,
scoped_refptr<base::SequencedTaskRunner> ipc_task_runner)
: root_(root),
- num_root_components_(GetNumPathComponents(root_)),
+ num_root_components_(root_.GetComponents().size()),
remote_directory_(std::move(directory), ipc_task_runner) {
DCHECK(root_.IsAbsolute());
}
@@ -380,8 +374,7 @@ base::FilePath FilesystemProxy::MakeRelative(const base::FilePath& path) const {
return base::FilePath();
// Absolute paths need to be rebased onto |root_|.
- std::vector<base::FilePath::StringType> components;
- path.GetComponents(&components);
+ std::vector<base::FilePath::StringType> components = path.GetComponents();
base::FilePath relative_path;
for (size_t i = num_root_components_; i < components.size(); ++i)
relative_path = relative_path.Append(components[i]);
diff --git a/chromium/components/services/storage/public/cpp/quota_client_callback_wrapper.cc b/chromium/components/services/storage/public/cpp/quota_client_callback_wrapper.cc
index 883a17d9f81..32256f4166b 100644
--- a/chromium/components/services/storage/public/cpp/quota_client_callback_wrapper.cc
+++ b/chromium/components/services/storage/public/cpp/quota_client_callback_wrapper.cc
@@ -8,6 +8,7 @@
#include "base/check_op.h"
#include "base/sequence_checker.h"
+#include "components/services/storage/public/cpp/buckets/bucket_locator.h"
#include "components/services/storage/public/mojom/quota_client.mojom.h"
#include "mojo/public/cpp/bindings/callback_helpers.h"
#include "third_party/blink/public/common/storage_key/storage_key.h"
@@ -26,14 +27,13 @@ QuotaClientCallbackWrapper::~QuotaClientCallbackWrapper() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
-void QuotaClientCallbackWrapper::GetStorageKeyUsage(
- const blink::StorageKey& storage_key,
- blink::mojom::StorageType type,
- GetStorageKeyUsageCallback callback) {
+void QuotaClientCallbackWrapper::GetBucketUsage(
+ const BucketLocator& bucket,
+ GetBucketUsageCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- wrapped_client_->GetStorageKeyUsage(
- storage_key, type,
+ wrapped_client_->GetBucketUsage(
+ bucket,
mojo::WrapCallbackWithDefaultInvokeIfNotRun(std::move(callback), 0));
}
@@ -47,26 +47,13 @@ void QuotaClientCallbackWrapper::GetStorageKeysForType(
std::move(callback), std::vector<blink::StorageKey>()));
}
-void QuotaClientCallbackWrapper::GetStorageKeysForHost(
- blink::mojom::StorageType type,
- const std::string& host,
- GetStorageKeysForHostCallback callback) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
- wrapped_client_->GetStorageKeysForHost(
- type, host,
- mojo::WrapCallbackWithDefaultInvokeIfNotRun(
- std::move(callback), std::vector<blink::StorageKey>()));
-}
-
-void QuotaClientCallbackWrapper::DeleteStorageKeyData(
- const blink::StorageKey& storage_key,
- blink::mojom::StorageType type,
- DeleteStorageKeyDataCallback callback) {
+void QuotaClientCallbackWrapper::DeleteBucketData(
+ const BucketLocator& bucket,
+ DeleteBucketDataCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- wrapped_client_->DeleteStorageKeyData(
- storage_key, type,
+ wrapped_client_->DeleteBucketData(
+ bucket,
mojo::WrapCallbackWithDefaultInvokeIfNotRun(
std::move(callback), blink::mojom::QuotaStatusCode::kErrorAbort));
}
diff --git a/chromium/components/services/storage/public/cpp/quota_client_callback_wrapper.h b/chromium/components/services/storage/public/cpp/quota_client_callback_wrapper.h
index f964baf1528..5935e56d02a 100644
--- a/chromium/components/services/storage/public/cpp/quota_client_callback_wrapper.h
+++ b/chromium/components/services/storage/public/cpp/quota_client_callback_wrapper.h
@@ -10,12 +10,10 @@
#include "base/thread_annotations.h"
#include "components/services/storage/public/mojom/quota_client.mojom.h"
-namespace blink {
-class StorageKey;
-} // namespace blink
-
namespace storage {
+struct BucketLocator;
+
// Stopgap for QuotaClients in systems with an unclear ownership graph.
//
// Implements the QuotaClient interface by proxying to a "real" implementation.
@@ -54,17 +52,12 @@ class COMPONENT_EXPORT(STORAGE_SERVICE_PUBLIC) QuotaClientCallbackWrapper
~QuotaClientCallbackWrapper() override;
// mojom::QuotaClient.
- void GetStorageKeyUsage(const blink::StorageKey& storage_key,
- blink::mojom::StorageType type,
- GetStorageKeyUsageCallback callback) override;
+ void GetBucketUsage(const BucketLocator& bucket,
+ GetBucketUsageCallback callback) override;
void GetStorageKeysForType(blink::mojom::StorageType type,
GetStorageKeysForTypeCallback callback) override;
- void GetStorageKeysForHost(blink::mojom::StorageType type,
- const std::string& host,
- GetStorageKeysForHostCallback callback) override;
- void DeleteStorageKeyData(const blink::StorageKey& storage_key,
- blink::mojom::StorageType type,
- DeleteStorageKeyDataCallback callback) override;
+ void DeleteBucketData(const BucketLocator& bucket,
+ DeleteBucketDataCallback callback) override;
void PerformStorageCleanup(blink::mojom::StorageType type,
PerformStorageCleanupCallback callback) override;
diff --git a/chromium/components/services/storage/public/cpp/storage_key_quota_client.h b/chromium/components/services/storage/public/cpp/storage_key_quota_client.h
deleted file mode 100644
index 7c0ac11520c..00000000000
--- a/chromium/components/services/storage/public/cpp/storage_key_quota_client.h
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2021 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.
-
-#ifndef COMPONENTS_SERVICES_STORAGE_PUBLIC_CPP_STORAGE_KEY_QUOTA_CLIENT_H_
-#define COMPONENTS_SERVICES_STORAGE_PUBLIC_CPP_STORAGE_KEY_QUOTA_CLIENT_H_
-
-#include "components/services/storage/public/mojom/quota_client.mojom.h"
-
-namespace storage {
-
-// Interface for storage key based QuotaClient to be inherited by quota managed
-// storage APIs that have not implemented Storage Buckets support yet. As
-// Storage APIs migrate their implementation to support Storage Buckets, they
-// should use the bucket base QuotaClient to be added as part of
-// crbug.com/1199417.
-//
-// TODO(crbug.com/1214066): Once all storage API's have migrated off storage key
-// based QuotaClient. This class should be removed and all API's should inherit
-// from bucket based QuotaClient.
-class COMPONENT_EXPORT(STORAGE_SERVICE_PUBLIC) StorageKeyQuotaClient
- : public mojom::QuotaClient {
- protected:
- ~StorageKeyQuotaClient() override = default;
-};
-
-} // namespace storage
-
-#endif // COMPONENTS_SERVICES_STORAGE_PUBLIC_CPP_STORAGE_KEY_QUOTA_CLIENT_H_
diff --git a/chromium/components/services/storage/public/mojom/quota_client.mojom b/chromium/components/services/storage/public/mojom/quota_client.mojom
index e64fb788eab..52f9aea8cde 100644
--- a/chromium/components/services/storage/public/mojom/quota_client.mojom
+++ b/chromium/components/services/storage/public/mojom/quota_client.mojom
@@ -6,6 +6,7 @@ module storage.mojom;
import "third_party/blink/public/mojom/quota/quota_types.mojom";
import "third_party/blink/public/mojom/storage_key/storage_key.mojom";
+import "components/services/storage/public/mojom/buckets/bucket_locator.mojom";
// Interface between each storage API and the quota manager.
//
@@ -17,25 +18,24 @@ import "third_party/blink/public/mojom/storage_key/storage_key.mojom";
// currently lives in the browser process, and will eventually move to the
// Storage Service process.
interface QuotaClient {
- // Returns the amount of data stored in the storage specified by `storage_key`
- // and `type`.
- GetStorageKeyUsage(blink.mojom.StorageKey storage_key,
- blink.mojom.StorageType type)
- => (int64 usage);
+ // Returns the amount of data stored in the storage specified by `bucket`.
+ // The quota manager should not assume that 0 `usage` means the
+ // storage API has no record of the `bucket`'s existence.
+ // DeleteBucketData() is the only way to guarantee that storage APIs erase
+ // all tracks of a `bucket`.
+ GetBucketUsage(BucketLocator bucket) => (int64 usage);
- // Returns a list of storage keys that have data in the `type` storage.
- GetStorageKeysForType(blink.mojom.StorageType type)
- => (array<blink.mojom.StorageKey> storage_keys);
-
- // Returns a list of storage keys that match the `host` and have data in the
+ // Returns a list of storage keys that have data in the default bucket for
// `type` storage.
- GetStorageKeysForHost(blink.mojom.StorageType type, string host)
+ //
+ // This method is currently used to bootstrap the buckets table in the quota
+ // database with data produced by old code. No other uses should be added.
+ // We're planning to remove this around 2024.
+ GetStorageKeysForType(blink.mojom.StorageType type)
=> (array<blink.mojom.StorageKey> storage_keys);
- // Returns after all data belonging to `storage_key` in the `type` storage
- // has been deleted.
- DeleteStorageKeyData(blink.mojom.StorageKey storage_key,
- blink.mojom.StorageType type)
+ // Returns after all data belonging to `bucket` has been deleted.
+ DeleteBucketData(BucketLocator bucket)
=> (blink.mojom.QuotaStatusCode status);
// An opportunity to perform a cleanup step after major deletions.
diff --git a/chromium/components/services/storage/service_worker/service_worker_database.cc b/chromium/components/services/storage/service_worker/service_worker_database.cc
index c539b19c5c8..dbf4ba5d433 100644
--- a/chromium/components/services/storage/service_worker/service_worker_database.cc
+++ b/chromium/components/services/storage/service_worker/service_worker_database.cc
@@ -19,6 +19,7 @@
#include "components/services/storage/filesystem_proxy_factory.h"
#include "components/services/storage/service_worker/service_worker_database.pb.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/blink/public/common/storage_key/storage_key.h"
#include "third_party/blink/public/mojom/service_worker/service_worker_database.mojom.h"
#include "third_party/leveldatabase/env_chromium.h"
#include "third_party/leveldatabase/leveldb_chrome.h"
@@ -63,7 +64,8 @@
// be updated in the future to avoid a migration.
// TODO(crbug.com/1199077): Update name during a migration to Version 3.
// key: "REG:" + <StorageKey 'key'.origin> + "/" + [ "^0" + <StorageKey
-// `key`.top_level_site> ] + '\x00' + <int64_t 'registration_id'>
+// `key`.top_level_site> + "^3" + <StorageKey `key`.ancestor_chain_bit> ] +
+// '\x00' + <int64_t 'registration_id'>
// - or -
// key: "REG:" + <StorageKey 'key'.origin> + "/" + "^1" + <StorageKey
// 'nonce'.High64Bits> + "^2" + <StorageKey 'nonce'.Low64Bits> + '\x00' +
@@ -95,7 +97,7 @@
// TODO(crbug.com/1199077): Update name during a migration to Version 3.
// key: "REGID_TO_ORIGIN:" + <int64_t 'registration_id'>
// value: <StorageKey 'key'.origin> + "/" + [ "^0" + <StorageKey
-// `key`.top_level_site> ]
+// `key`.top_level_site> + "^3" + <StorageKey `key`.ancestor_chain_bit>]
// - or -
// value: <StorageKey 'key'.origin> + "/" + "^1" + <StorageKey
// 'nonce'.High64Bits> + "^2" + <StorageKey 'nonce'.Low64Bits>
@@ -115,45 +117,6 @@
// OBSOLETE: https://crbug.com/788604
// key: "INITDATA_FOREIGN_FETCH_ORIGIN:" + <GURL 'origin'>
// value: <empty>
-namespace {
-
-// Returns true if the registration key string is partitioned by top-level site
-// but storage partitioning is currently disabled. Returns false if the key
-// string contains a serialized nonce.
-bool ShouldSkipKeyDueToPartitioning(const std::string& reg_key_string) {
- // Don't skip anything if storage partitioning is enabled.
- if (blink::StorageKey::IsThirdPartyStoragePartitioningEnabled())
- return false;
-
- // TODO(crbug.com/1246549) : This currently counts carets to tell the
- // difference between nonce and top-level site schemes. When the ancestor bit
- // is implemented this will need to be modified to handle that case (since it
- // will also use 2 carets).
- int number_of_carets =
- std::count(reg_key_string.begin(), reg_key_string.end(), '^');
-
- switch (number_of_carets) {
- case 2: {
- // Don't skip if a nonce serialization scheme is found.
- return false;
- }
- case 1: {
- // Do skip if partitioning is disabled and we detect a top-level site
- // serialization scheme.
- return true;
- }
- case 0: {
- // Don't skip for a 1p context key.
- return false;
- }
- default: {
- NOTREACHED();
- return true;
- }
- }
-}
-
-} // namespace
namespace storage {
@@ -431,7 +394,7 @@ ServiceWorkerDatabase::GetStorageKeysWithRegistrations(
service_worker_internals::kUniqueOriginKey, &key_str))
break;
- if (ShouldSkipKeyDueToPartitioning(key_str))
+ if (blink::StorageKey::ShouldSkipKeyDueToPartitioning(key_str))
continue;
absl::optional<blink::StorageKey> key =
@@ -623,7 +586,7 @@ ServiceWorkerDatabase::Status ServiceWorkerDatabase::GetAllRegistrations(
// Get only the sub-string before the separator.
std::string reg_key_string = prefix_string.substr(0, separator_pos);
- if (ShouldSkipKeyDueToPartitioning(reg_key_string))
+ if (blink::StorageKey::ShouldSkipKeyDueToPartitioning(reg_key_string))
continue;
absl::optional<blink::StorageKey> key =
@@ -700,7 +663,7 @@ ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadRegistrationStorageKey(
// If storage partitioning is disabled we shouldn't have any handles to
// registration IDs associated with partitioned entries.
- DCHECK(!ShouldSkipKeyDueToPartitioning(value));
+ DCHECK(!blink::StorageKey::ShouldSkipKeyDueToPartitioning(value));
absl::optional<blink::StorageKey> parsed =
blink::StorageKey::Deserialize(value);
diff --git a/chromium/components/services/storage/service_worker/service_worker_storage.cc b/chromium/components/services/storage/service_worker/service_worker_storage.cc
index 59a32d7e89a..3dbac654f20 100644
--- a/chromium/components/services/storage/service_worker/service_worker_storage.cc
+++ b/chromium/components/services/storage/service_worker/service_worker_storage.cc
@@ -20,6 +20,7 @@
#include "base/task/sequenced_task_runner.h"
#include "base/task/task_runner_util.h"
#include "base/task/thread_pool.h"
+#include "base/time/time.h"
#include "base/trace_event/trace_event.h"
#include "components/services/storage/public/cpp/constants.h"
#include "components/services/storage/service_worker/service_worker_disk_cache.h"
@@ -1580,6 +1581,8 @@ void ServiceWorkerStorage::ReadInitialDataFromDB(
ServiceWorkerDatabase* database,
scoped_refptr<base::SequencedTaskRunner> original_task_runner,
InitializeCallback callback) {
+ base::TimeTicks now = base::TimeTicks::Now();
+
DCHECK(database);
std::unique_ptr<ServiceWorkerStorage::InitialData> data(
new ServiceWorkerStorage::InitialData());
@@ -1604,6 +1607,10 @@ void ServiceWorkerStorage::ReadInitialDataFromDB(
original_task_runner->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), std::move(data), status));
+
+ base::UmaHistogramMediumTimes(
+ "ServiceWorker.Storage.ReadInitialDataFromDB.Time",
+ base::TimeTicks::Now() - now);
}
void ServiceWorkerStorage::DeleteRegistrationFromDB(
diff --git a/chromium/components/services/storage/service_worker/service_worker_storage_control_impl.cc b/chromium/components/services/storage/service_worker/service_worker_storage_control_impl.cc
index d5d1ccb754f..9001b00dc84 100644
--- a/chromium/components/services/storage/service_worker/service_worker_storage_control_impl.cc
+++ b/chromium/components/services/storage/service_worker/service_worker_storage_control_impl.cc
@@ -5,6 +5,7 @@
#include "components/services/storage/service_worker/service_worker_storage_control_impl.h"
#include "base/containers/contains.h"
+#include "base/debug/alias.h"
#include "components/services/storage/service_worker/service_worker_resource_ops.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver_set.h"
@@ -483,6 +484,10 @@ ServiceWorkerStorageControlImpl::CreateLiveVersionReferenceRemote(
reference->Add(remote_reference.InitWithNewPipeAndPassReceiver());
live_versions_[version_id] = std::move(reference);
} else {
+ // TODO(https://crbug.com/1277263): Remove the following CHECK() once the
+ // cause is identified.
+ base::debug::Alias(&version_id);
+ CHECK(it->second.get()) << "Invalid version id: " << version_id;
it->second->Add(remote_reference.InitWithNewPipeAndPassReceiver());
}
return remote_reference;
diff --git a/chromium/components/services/storage/service_worker/service_worker_storage_control_impl_unittest.cc b/chromium/components/services/storage/service_worker/service_worker_storage_control_impl_unittest.cc
index 4ba4aaa8093..a17552bdbee 100644
--- a/chromium/components/services/storage/service_worker/service_worker_storage_control_impl_unittest.cc
+++ b/chromium/components/services/storage/service_worker/service_worker_storage_control_impl_unittest.cc
@@ -88,6 +88,11 @@ struct GetUserDataForAllRegistrationsResult {
std::vector<mojom::ServiceWorkerUserDataPtr> values;
};
+struct GetUsageForStorageKeyResult {
+ DatabaseStatus status;
+ int64_t usage;
+};
+
ReadResponseHeadResult ReadResponseHead(
mojom::ServiceWorkerResourceReader* reader) {
ReadResponseHeadResult result;
@@ -570,6 +575,21 @@ class ServiceWorkerStorageControlImplTest : public testing::Test {
return return_value;
}
+ GetUsageForStorageKeyResult GetUsageForStorageKey(
+ const blink::StorageKey& key) {
+ GetUsageForStorageKeyResult result;
+ base::RunLoop loop;
+ storage()->GetUsageForStorageKey(
+ key,
+ base::BindLambdaForTesting([&](DatabaseStatus status, int64_t usage) {
+ result.status = status;
+ result.usage = usage;
+ loop.Quit();
+ }));
+ loop.Run();
+ return result;
+ }
+
// Creates a registration with the given resource records.
RegistrationData CreateRegistrationData(
int64_t registration_id,
@@ -1423,6 +1443,92 @@ TEST_F(ServiceWorkerStorageControlImplTest,
EXPECT_EQ(result.values.size(), 0UL);
}
+// Tests that getting usage for a storage key works at different stages of
+// registration resource update.
+TEST_F(ServiceWorkerStorageControlImplTest, GetUsageForStorageKey) {
+ const GURL kScope("https://www.example.com/");
+ const blink::StorageKey kKey(url::Origin::Create(kScope));
+ const GURL kScriptUrl("https://www.example.com/sw.js");
+ const GURL kImportedScriptUrl("https://www.example.com/imported.js");
+
+ LazyInitializeForTest();
+
+ // No data has written yet for a given storage key.
+ {
+ GetUsageForStorageKeyResult result = GetUsageForStorageKey(kKey);
+ ASSERT_EQ(result.status, DatabaseStatus::kOk);
+ ASSERT_EQ(result.usage, 0);
+ }
+
+ // Preparation: Create a registration with two resources. These aren't written
+ // to storage yet.
+ std::vector<ResourceRecord> resources;
+ const int64_t resource_id1 = GetNewResourceId();
+ const std::string resource_data1 = "main script data";
+ resources.push_back(mojom::ServiceWorkerResourceRecord::New(
+ resource_id1, kScriptUrl, resource_data1.size()));
+
+ const int64_t resource_id2 = GetNewResourceId();
+ const std::string resource_data2 = "imported script data";
+ resources.push_back(mojom::ServiceWorkerResourceRecord::New(
+ resource_id2, kImportedScriptUrl, resource_data2.size()));
+
+ const int64_t registration_id = GetNewRegistrationId();
+ const int64_t version_id = GetNewVersionId().version_id;
+ RegistrationData registration_data = CreateRegistrationData(
+ registration_id, version_id, kScope, kKey, kScriptUrl, resources);
+
+ // Put these resources ids on the uncommitted list in storage.
+ DatabaseStatus status;
+ status = StoreUncommittedResourceId(resource_id1);
+ ASSERT_EQ(status, DatabaseStatus::kOk);
+ status = StoreUncommittedResourceId(resource_id2);
+ ASSERT_EQ(status, DatabaseStatus::kOk);
+
+ {
+ GetUsageForStorageKeyResult result = GetUsageForStorageKey(kKey);
+ ASSERT_EQ(result.status, DatabaseStatus::kOk);
+ ASSERT_EQ(result.usage, 0)
+ << "Resources that aren't stored with the registration "
+ << "don't use quota.";
+ }
+
+ // Write responses and the registration data.
+ {
+ int result = WriteResource(resource_id1, resource_data1);
+ ASSERT_GT(result, 0);
+ }
+ {
+ int result = WriteResource(resource_id2, resource_data2);
+ ASSERT_GT(result, 0);
+ }
+ status =
+ StoreRegistration(std::move(registration_data), std::move(resources));
+ ASSERT_EQ(status, DatabaseStatus::kOk);
+
+ // Expect the storage usage for resources stored.
+ {
+ GetUsageForStorageKeyResult result = GetUsageForStorageKey(kKey);
+ ASSERT_EQ(result.status, DatabaseStatus::kOk);
+ const int64_t expected_usage =
+ resource_data1.size() + resource_data2.size();
+ ASSERT_EQ(result.usage, expected_usage);
+ }
+
+ // Delete the registration.
+ {
+ DeleteRegistrationResult result = DeleteRegistration(registration_id, kKey);
+ ASSERT_EQ(result.status, DatabaseStatus::kOk);
+ }
+
+ // Expect no storage usage.
+ {
+ GetUsageForStorageKeyResult result = GetUsageForStorageKey(kKey);
+ ASSERT_EQ(result.status, DatabaseStatus::kOk);
+ ASSERT_EQ(result.usage, 0);
+ }
+}
+
// Tests that apply policy updates work.
TEST_F(ServiceWorkerStorageControlImplTest, ApplyPolicyUpdates) {
const GURL kScope1("https://foo.example.com/");
diff --git a/chromium/components/services/storage/service_worker/service_worker_storage_unittest.cc b/chromium/components/services/storage/service_worker/service_worker_storage_unittest.cc
index be27d1faa0e..8b325727c14 100644
--- a/chromium/components/services/storage/service_worker/service_worker_storage_unittest.cc
+++ b/chromium/components/services/storage/service_worker/service_worker_storage_unittest.cc
@@ -913,7 +913,7 @@ TEST_F(ServiceWorkerStorageDiskTest, DeleteAndStartOver_OpenedFileExists) {
base::BindOnce(&DatabaseStatusCallback, run_loop.QuitClosure(), &status));
run_loop.Run();
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
// On Windows, deleting the directory containing an opened file should fail.
EXPECT_EQ(ServiceWorkerDatabase::Status::kErrorIOError, *status);
EXPECT_TRUE(storage()->IsDisabled());
diff --git a/chromium/components/services/storage/shared_storage/DIR_METADATA b/chromium/components/services/storage/shared_storage/DIR_METADATA
new file mode 100644
index 00000000000..95ccec4a089
--- /dev/null
+++ b/chromium/components/services/storage/shared_storage/DIR_METADATA
@@ -0,0 +1,12 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+# https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+# https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+ component: "Blink>Storage>SharedStorage"
+}
+team_email: "chrome-ads-core@google.com"
diff --git a/chromium/components/services/storage/shared_storage/OWNERS b/chromium/components/services/storage/shared_storage/OWNERS
new file mode 100644
index 00000000000..6ab87565cd2
--- /dev/null
+++ b/chromium/components/services/storage/shared_storage/OWNERS
@@ -0,0 +1,6 @@
+# Primary:
+cammie@chromium.org
+
+# Secondary:
+yaoxia@chromium.org
+jkarlin@chromium.org
diff --git a/chromium/components/services/storage/shared_storage/async_shared_storage_database.cc b/chromium/components/services/storage/shared_storage/async_shared_storage_database.cc
new file mode 100644
index 00000000000..3a6d9f438e6
--- /dev/null
+++ b/chromium/components/services/storage/shared_storage/async_shared_storage_database.cc
@@ -0,0 +1,196 @@
+// Copyright 2021 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 "components/services/storage/shared_storage/async_shared_storage_database.h"
+
+#include <inttypes.h>
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/files/file_util.h"
+#include "base/time/time.h"
+#include "components/services/storage/public/mojom/storage_usage_info.mojom.h"
+#include "components/services/storage/shared_storage/shared_storage_options.h"
+#include "storage/browser/quota/special_storage_policy.h"
+#include "url/origin.h"
+
+namespace storage {
+
+// static
+std::unique_ptr<AsyncSharedStorageDatabase> AsyncSharedStorageDatabase::Create(
+ base::FilePath db_path,
+ scoped_refptr<base::SequencedTaskRunner> blocking_task_runner,
+ scoped_refptr<storage::SpecialStoragePolicy> special_storage_policy,
+ std::unique_ptr<SharedStorageDatabaseOptions> options) {
+ return absl::WrapUnique(new AsyncSharedStorageDatabase(
+ std::move(db_path), std::move(blocking_task_runner),
+ std::move(special_storage_policy), std::move(options)));
+}
+
+AsyncSharedStorageDatabase::AsyncSharedStorageDatabase(
+ base::FilePath db_path,
+ scoped_refptr<base::SequencedTaskRunner> blocking_task_runner,
+ scoped_refptr<storage::SpecialStoragePolicy> special_storage_policy,
+ std::unique_ptr<SharedStorageDatabaseOptions> options)
+ : database_(base::SequenceBound<SharedStorageDatabase>(
+ std::move(blocking_task_runner),
+ std::move(db_path),
+ std::move(special_storage_policy),
+ std::move(options))) {}
+
+AsyncSharedStorageDatabase::~AsyncSharedStorageDatabase() = default;
+
+void AsyncSharedStorageDatabase::Destroy(
+ base::OnceCallback<void(bool)> callback) {
+ DCHECK(database_);
+ database_.AsyncCall(&SharedStorageDatabase::Destroy)
+ .Then(std::move(callback));
+}
+
+void AsyncSharedStorageDatabase::TrimMemory(base::OnceClosure callback) {
+ DCHECK(database_);
+ database_.AsyncCall(&SharedStorageDatabase::TrimMemory)
+ .Then(std::move(callback));
+}
+
+void AsyncSharedStorageDatabase::Get(
+ url::Origin context_origin,
+ std::u16string key,
+ base::OnceCallback<void(GetResult)> callback) {
+ DCHECK(database_);
+ database_.AsyncCall(&SharedStorageDatabase::Get)
+ .WithArgs(std::move(context_origin), std::move(key))
+ .Then(std::move(callback));
+}
+
+void AsyncSharedStorageDatabase::Set(
+ url::Origin context_origin,
+ std::u16string key,
+ std::u16string value,
+ base::OnceCallback<void(OperationResult)> callback,
+ SetBehavior behavior) {
+ DCHECK(database_);
+ database_.AsyncCall(&SharedStorageDatabase::Set)
+ .WithArgs(std::move(context_origin), std::move(key), std::move(value),
+ behavior)
+ .Then(std::move(callback));
+}
+
+void AsyncSharedStorageDatabase::Append(
+ url::Origin context_origin,
+ std::u16string key,
+ std::u16string value,
+ base::OnceCallback<void(OperationResult)> callback) {
+ DCHECK(database_);
+ database_.AsyncCall(&SharedStorageDatabase::Append)
+ .WithArgs(std::move(context_origin), std::move(key), std::move(value))
+ .Then(std::move(callback));
+}
+
+void AsyncSharedStorageDatabase::Delete(
+ url::Origin context_origin,
+ std::u16string key,
+ base::OnceCallback<void(OperationResult)> callback) {
+ DCHECK(database_);
+ database_.AsyncCall(&SharedStorageDatabase::Delete)
+ .WithArgs(std::move(context_origin), std::move(key))
+ .Then(std::move(callback));
+}
+
+void AsyncSharedStorageDatabase::Clear(
+ url::Origin context_origin,
+ base::OnceCallback<void(OperationResult)> callback) {
+ DCHECK(database_);
+ database_.AsyncCall(&SharedStorageDatabase::Clear)
+ .WithArgs(std::move(context_origin))
+ .Then(std::move(callback));
+}
+
+void AsyncSharedStorageDatabase::Length(
+ url::Origin context_origin,
+ base::OnceCallback<void(int)> callback) {
+ DCHECK(database_);
+ database_.AsyncCall(&SharedStorageDatabase::Length)
+ .WithArgs(std::move(context_origin))
+ .Then(std::move(callback));
+}
+
+void AsyncSharedStorageDatabase::Key(
+ url::Origin context_origin,
+ int index,
+ base::OnceCallback<void(GetResult)> callback) {
+ DCHECK_GE(index, 0);
+ DCHECK(database_);
+ database_.AsyncCall(&SharedStorageDatabase::Key)
+ .WithArgs(std::move(context_origin), static_cast<uint64_t>(index))
+ .Then(std::move(callback));
+}
+
+void AsyncSharedStorageDatabase::PurgeMatchingOrigins(
+ OriginMatcherFunction origin_matcher,
+ base::Time begin,
+ base::Time end,
+ base::OnceCallback<void(OperationResult)> callback,
+ bool perform_storage_cleanup) {
+ DCHECK(database_);
+ database_.AsyncCall(&SharedStorageDatabase::PurgeMatchingOrigins)
+ .WithArgs(std::move(origin_matcher), begin, end, perform_storage_cleanup)
+ .Then(std::move(callback));
+}
+
+void AsyncSharedStorageDatabase::PurgeStaleOrigins(
+ base::TimeDelta window_to_be_deemed_active,
+ base::OnceCallback<void(OperationResult)> callback) {
+ DCHECK(database_);
+ database_.AsyncCall(&SharedStorageDatabase::PurgeStaleOrigins)
+ .WithArgs(window_to_be_deemed_active)
+ .Then(std::move(callback));
+}
+
+void AsyncSharedStorageDatabase::FetchOrigins(
+ base::OnceCallback<void(std::vector<mojom::StorageUsageInfoPtr>)>
+ callback) {
+ DCHECK(database_);
+ database_.AsyncCall(&SharedStorageDatabase::FetchOrigins)
+ .Then(std::move(callback));
+}
+
+void AsyncSharedStorageDatabase::IsOpenForTesting(
+ base::OnceCallback<void(bool)> callback) {
+ DCHECK(database_);
+ database_.AsyncCall(&SharedStorageDatabase::IsOpenForTesting)
+ .Then(std::move(callback));
+}
+
+void AsyncSharedStorageDatabase::DBStatusForTesting(
+ base::OnceCallback<void(InitStatus)> callback) {
+ DCHECK(database_);
+ database_.AsyncCall(&SharedStorageDatabase::DBStatusForTesting)
+ .Then(std::move(callback));
+}
+
+void AsyncSharedStorageDatabase::OverrideLastUsedTimeForTesting(
+ url::Origin context_origin,
+ base::Time override_last_used_time,
+ base::OnceCallback<void(bool)> callback) {
+ DCHECK(database_);
+ database_.AsyncCall(&SharedStorageDatabase::OverrideLastUsedTimeForTesting)
+ .WithArgs(std::move(context_origin), override_last_used_time)
+ .Then(std::move(callback));
+}
+
+void AsyncSharedStorageDatabase::OverrideSpecialStoragePolicyForTesting(
+ scoped_refptr<storage::SpecialStoragePolicy> special_storage_policy,
+ base::OnceCallback<void(bool)> callback) {
+ DCHECK(database_);
+ database_
+ .AsyncCall(&SharedStorageDatabase::OverrideSpecialStoragePolicyForTesting)
+ .WithArgs(std::move(special_storage_policy))
+ .Then(std::move(callback));
+}
+
+} // namespace storage
diff --git a/chromium/components/services/storage/shared_storage/async_shared_storage_database.h b/chromium/components/services/storage/shared_storage/async_shared_storage_database.h
new file mode 100644
index 00000000000..1771c4ff3d8
--- /dev/null
+++ b/chromium/components/services/storage/shared_storage/async_shared_storage_database.h
@@ -0,0 +1,237 @@
+// Copyright 2021 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.
+
+#ifndef COMPONENTS_SERVICES_STORAGE_SHARED_STORAGE_ASYNC_SHARED_STORAGE_DATABASE_H_
+#define COMPONENTS_SERVICES_STORAGE_SHARED_STORAGE_ASYNC_SHARED_STORAGE_DATABASE_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/callback_forward.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/task/sequenced_task_runner.h"
+#include "base/threading/sequence_bound.h"
+#include "components/services/storage/shared_storage/shared_storage_database.h"
+
+namespace base {
+class FilePath;
+class Time;
+class TimeDelta;
+} // namespace base
+
+namespace url {
+class Origin;
+} // namespace url
+
+namespace storage {
+struct SharedStorageDatabaseOptions;
+class SpecialStoragePolicy;
+
+// A wrapper around SharedStorageDatabase which makes the operations
+// asynchronous.
+class AsyncSharedStorageDatabase {
+ public:
+ using InitStatus = SharedStorageDatabase::InitStatus;
+ using SetBehavior = SharedStorageDatabase::SetBehavior;
+ using OperationResult = SharedStorageDatabase::OperationResult;
+ using GetResult = SharedStorageDatabase::GetResult;
+
+ // A callback type to check if a given origin matches a storage policy.
+ // Can be passed empty/null where used, which means the origin will always
+ // match.
+ using OriginMatcherFunction = SharedStorageDatabase::OriginMatcherFunction;
+
+ // Creates an `AsyncSharedStorageDatabase` instance. If `db_path` is empty,
+ // creates a temporary, in-memory database; otherwise creates a persistent
+ // database within a filesystem directory given by `db_path`, which must be an
+ // absolute path. If file-backed, the database may or may not already exist at
+ // `db_path`, and if it doesn't, it will be created.
+ //
+ // The instance will be bound to and perform all operations on
+ // `blocking_task_runner`, which must support blocking operations.
+ static std::unique_ptr<AsyncSharedStorageDatabase> Create(
+ base::FilePath db_path,
+ scoped_refptr<base::SequencedTaskRunner> blocking_task_runner,
+ scoped_refptr<storage::SpecialStoragePolicy> special_storage_policy,
+ std::unique_ptr<SharedStorageDatabaseOptions> options);
+
+ AsyncSharedStorageDatabase(const AsyncSharedStorageDatabase&) = delete;
+ AsyncSharedStorageDatabase& operator=(const AsyncSharedStorageDatabase&) =
+ delete;
+ AsyncSharedStorageDatabase(const AsyncSharedStorageDatabase&&) = delete;
+ AsyncSharedStorageDatabase& operator=(const AsyncSharedStorageDatabase&&) =
+ delete;
+
+ ~AsyncSharedStorageDatabase();
+
+ base::SequenceBound<SharedStorageDatabase>&
+ GetSequenceBoundDatabaseForTesting() {
+ return database_;
+ }
+
+ // Destroys the database.
+ //
+ // If filebacked, deletes the persistent database within the filesystem
+ // directory.
+ //
+ // It is OK to call `Destroy()` regardless of whether database initialization
+ // was successful.
+ void Destroy(base::OnceCallback<void(bool)> callback);
+
+ // `TrimMemory()`, `Get()`, `Set()`, `Append()`, `Delete()`, `Clear()`,
+ // `Length()`, `Key()`, `PurgeMatchingOrigins()`, `PurgeStaleOrigins()`, and
+ // `FetchOrigins()` are all async versions of the corresponding methods in
+ // `storage::SharedStorageDatabase`, with the modification that `Set()` and
+ // `Append()` take a boolean callback to indicate that a value was set or
+ // appended, rather than a long integer callback with the row number for the
+ // next available row.
+ //
+ // It is OK to call these async methods even if the database has failed to
+ // initialize, as there is an alternate code path to handle this case that
+ // skips accessing `database_` (as it will be null) and hence performing the
+ // intending operation, logs the occurrence of the missing database to UMA,
+ // and runs the callback with a trivial instance of its expected result type).
+
+ // Releases all non-essential memory associated with this database connection.
+ // `callback` runs once the operation is finished.
+ void TrimMemory(base::OnceClosure callback);
+
+ // Retrieves the `value` for `context_origin` and `key`. `callback` is called
+ // with a struct bundling a string `value` in its data field if one is found,
+ // `absl::nullopt` otherwise, and a OperationResult to indicate whether the
+ // transaction was free of database errors.
+ //
+ // `key` must be of length at most
+ // `SharedStorageDatabaseOptions::max_string_length`, with the burden on the
+ // caller to handle errors for strings that exceed this length.
+ void Get(url::Origin context_origin,
+ std::u16string key,
+ base::OnceCallback<void(GetResult)> callback);
+
+ // Sets an entry for `context_origin` and `key` to have `value`.
+ // If `behavior` is `kIgnoreIfPresent` and an entry already exists for
+ // `context_origin` and `key`, then the database table is not modified.
+ // The parameter of `callback` reports whether or not any entry is added, the
+ // request is ignored, or if there is an error.
+ //
+ // `key` and `value` must be each of length at most
+ // `SharedStorageDatabaseOptions::max_string_length`, with the burden on the
+ // caller to handle errors for strings that exceed this length. Moreover, if
+ // the length retrieved by `Length(context_origin, callback)` equals
+ // `SharedStorageDatabaseOptions::max_entries_per_origin_`, `Set()` will fail
+ // and the table will not be modified.
+ void Set(url::Origin context_origin,
+ std::u16string key,
+ std::u16string value,
+ base::OnceCallback<void(OperationResult)> callback,
+ SetBehavior behavior = SetBehavior::kDefault);
+
+ // Appends `value` to the end of the current `value` for `context_origin` and
+ // `key`, if `key` exists. If `key` does not exist, creates an entry for `key`
+ // with value `value`. The parameter of `callback` reports whether or not any
+ // entry is added or modified or if there is an error.
+ //
+ // `key` and `value` must be each of length at most
+ // `SharedStorageDatabaseOptions::max_string_length`, with the burden on the
+ // caller to handle errors for strings that exceed this length. Moreover, if
+ // the length of the string obtained by concatening the current `script_value`
+ // (if one exists) and `value` exceeds
+ // `SharedStorageDatabaseOptions::max_string_length`, or if the length
+ // retrieved by `Length(context_origin, callback)` equals
+ // `SharedStorageDatabaseOptions::max_entries_per_origin_`, `Append()` will
+ // fail and the database table will not be modified.
+ void Append(url::Origin context_origin,
+ std::u16string key,
+ std::u16string value,
+ base::OnceCallback<void(OperationResult)> callback);
+
+ // Deletes the entry for `context_origin` and `key`. The parameter of
+ // `callback` reports whether the deletion is successful.
+ //
+ // `key` must be of length at most
+ // `SharedStorageDatabaseOptions::max_string_length`, with the burden on the
+ // caller to handle errors for strings that exceed this length.
+ void Delete(url::Origin context_origin,
+ std::u16string key,
+ base::OnceCallback<void(OperationResult)> callback);
+
+ // Clears all entries for `context_origin`. The parameter of `callback`
+ // reports whether the operation is successful.
+ void Clear(url::Origin context_origin,
+ base::OnceCallback<void(OperationResult)> callback);
+
+ // The parameter of `callback` reports the number of entries for
+ // `context_origin`, 0 if there are none, or -1 on operation failure.
+ void Length(url::Origin context_origin,
+ base::OnceCallback<void(int)> callback);
+
+ // If a list of all the keys for `context_origin` are taken in lexicographic
+ // order, retrieves the `key` at `index` of the list and calls `callback` with
+ // a struct bundling it as a parameter (along with a OperationResult to
+ // indicate whether the transaction was free of database errors); otherwise
+ // calls `callback` with `absl::nullopt` in the data field of the struct.
+ // `index` must be non-negative.
+ //
+ // TODO(crbug.com/1247861): Replace with an async iterator.
+ void Key(url::Origin context_origin,
+ int index,
+ base::OnceCallback<void(GetResult)> callback);
+
+ // Clears all origins that match `origin_matcher` run on the owning
+ // StoragePartition's `SpecialStoragePolicy` and have `last_used_time` between
+ // the times `begin` and `end`. If `perform_storage_cleanup` is true, vacuums
+ // the database afterwards. The parameter of `callback` reports whether the
+ // transaction was successful.
+ //
+ // Note that `origin_matcher` is accessed on a different sequence than where
+ // it was created.
+ void PurgeMatchingOrigins(OriginMatcherFunction origin_matcher,
+ base::Time begin,
+ base::Time end,
+ base::OnceCallback<void(OperationResult)> callback,
+ bool perform_storage_cleanup = false);
+
+ // Clear all entries for all origins whose `last_read_time` falls before
+ // `base::Time::Now() - window_to_be_deemed_active`.
+ void PurgeStaleOrigins(base::TimeDelta window_to_be_deemed_active,
+ base::OnceCallback<void(OperationResult)> callback);
+
+ // Fetches a vector of `mojom::StorageUsageInfoPtr`, with one
+ // `mojom::StorageUsageInfoPtr` for each origin currently using shared storage
+ // in this profile.
+ void FetchOrigins(
+ base::OnceCallback<void(std::vector<mojom::StorageUsageInfoPtr>)>
+ callback);
+
+ // Asynchronously determines whether the database is open. Useful for testing.
+ void IsOpenForTesting(base::OnceCallback<void(bool)> callback);
+
+ // Asynchronously determines the database `InitStatus`. Useful for testing.
+ void DBStatusForTesting(base::OnceCallback<void(InitStatus)> callback);
+
+ // Changes `last_used_time` to `override_last_used_time` for `context_origin`.
+ void OverrideLastUsedTimeForTesting(url::Origin context_origin,
+ base::Time override_last_used_time,
+ base::OnceCallback<void(bool)> callback);
+
+ // Overrides the `SpecialStoragePolicy` for tests.
+ void OverrideSpecialStoragePolicyForTesting(
+ scoped_refptr<storage::SpecialStoragePolicy> special_storage_policy,
+ base::OnceCallback<void(bool)> callback);
+
+ private:
+ // Instances should be obtained from the `Create()` factory method.
+ AsyncSharedStorageDatabase(
+ base::FilePath db_path,
+ scoped_refptr<base::SequencedTaskRunner> blocking_task_runner,
+ scoped_refptr<storage::SpecialStoragePolicy> special_storage_policy,
+ std::unique_ptr<SharedStorageDatabaseOptions> options);
+
+ base::SequenceBound<SharedStorageDatabase> database_;
+};
+
+} // namespace storage
+
+#endif // COMPONENTS_SERVICES_STORAGE_SHARED_STORAGE_ASYNC_SHARED_STORAGE_DATABASE_H_
diff --git a/chromium/components/services/storage/shared_storage/async_shared_storage_database_unittest.cc b/chromium/components/services/storage/shared_storage/async_shared_storage_database_unittest.cc
new file mode 100644
index 00000000000..fdf3041a271
--- /dev/null
+++ b/chromium/components/services/storage/shared_storage/async_shared_storage_database_unittest.cc
@@ -0,0 +1,1648 @@
+// Copyright 2021 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 "components/services/storage/shared_storage/async_shared_storage_database.h"
+
+#include <memory>
+#include <queue>
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/files/file.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/run_loop.h"
+#include "base/strings/strcat.h"
+#include "base/task/sequenced_task_runner.h"
+#include "base/task/thread_pool.h"
+#include "base/test/bind.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/test/task_environment.h"
+#include "base/test/test_future.h"
+#include "base/threading/sequence_bound.h"
+#include "base/threading/thread.h"
+#include "components/services/storage/public/mojom/storage_usage_info.mojom.h"
+#include "components/services/storage/shared_storage/shared_storage_database.h"
+#include "components/services/storage/shared_storage/shared_storage_options.h"
+#include "components/services/storage/shared_storage/shared_storage_test_utils.h"
+#include "sql/database.h"
+#include "storage/browser/quota/special_storage_policy.h"
+#include "storage/browser/test/mock_special_storage_policy.h"
+#include "testing/gmock/include/gmock/gmock-matchers.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/features.h"
+#include "url/gurl.h"
+#include "url/origin.h"
+
+namespace storage {
+
+namespace {
+
+using ::testing::ElementsAre;
+using InitStatus = SharedStorageDatabase::InitStatus;
+using SetBehavior = SharedStorageDatabase::SetBehavior;
+using OperationResult = SharedStorageDatabase::OperationResult;
+using GetResult = SharedStorageDatabase::GetResult;
+using DBOperation = TestDatabaseOperationReceiver::DBOperation;
+using Type = DBOperation::Type;
+
+const int kMaxEntriesPerOrigin = 5;
+const int kMaxStringLength = 100;
+
+} // namespace
+
+class AsyncSharedStorageDatabaseTest : public testing::Test {
+ public:
+ AsyncSharedStorageDatabaseTest()
+ : task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
+ {base::MayBlock(), base::WithBaseSyncPrimitives(),
+ base::TaskShutdownBehavior::BLOCK_SHUTDOWN})),
+ special_storage_policy_(
+ base::MakeRefCounted<MockSpecialStoragePolicy>()),
+ receiver_(std::make_unique<TestDatabaseOperationReceiver>()) {}
+
+ ~AsyncSharedStorageDatabaseTest() override = default;
+
+ void SetUp() override {
+ InitSharedStorageFeature();
+
+ // Get a temporary directory for the test DB files.
+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+
+ file_name_ = temp_dir_.GetPath().AppendASCII("TestSharedStorage.db");
+ }
+
+ void TearDown() override {
+ task_environment_.RunUntilIdle();
+
+ // It's not strictly necessary as part of test cleanup, but here we test
+ // the code path that force razes the database, and, if file-backed,
+ // force deletes the database file.
+ EXPECT_TRUE(DestroySync());
+ EXPECT_FALSE(base::PathExists(file_name_));
+ EXPECT_TRUE(temp_dir_.Delete());
+ }
+
+ virtual void InitSharedStorageFeature() {
+ scoped_feature_list_.InitAndEnableFeatureWithParameters(
+ {blink::features::kSharedStorageAPI},
+ {{"MaxSharedStorageInitTries", "1"}});
+ }
+
+ // Initialize an async shared storage database instance from the SQL file at
+ // `relative_file_path` in the "storage/" subdirectory of test data.
+ void LoadFromFileSync(const char* relative_file_path) {
+ DCHECK(!file_name_.empty());
+
+ ASSERT_TRUE(CreateDatabaseFromSQL(file_name_, relative_file_path));
+
+ async_database_ = AsyncSharedStorageDatabase::Create(
+ file_name_, task_runner_, special_storage_policy_,
+ SharedStorageOptions::Create()->GetDatabaseOptions());
+ }
+
+ void CreateSync(const base::FilePath& db_path,
+ std::unique_ptr<SharedStorageDatabaseOptions> options) {
+ async_database_ = AsyncSharedStorageDatabase::Create(
+ db_path, task_runner_, special_storage_policy_, std::move(options));
+ }
+
+ void Destroy(bool* out_success) {
+ DCHECK(out_success);
+ DCHECK(async_database_);
+ DCHECK(receiver_);
+
+ DBOperation operation(Type::DB_DESTROY);
+ auto callback =
+ receiver_->MakeBoolCallback(std::move(operation), out_success);
+ async_database_->Destroy(std::move(callback));
+ }
+
+ bool DestroySync() {
+ if (!async_database_)
+ return true;
+
+ base::test::TestFuture<bool> future;
+ async_database_->Destroy(future.GetCallback());
+ EXPECT_TRUE(future.Wait());
+ return future.Get();
+ }
+
+ bool is_finished() const {
+ DCHECK(receiver_);
+ return receiver_->is_finished();
+ }
+
+ void SetExpectedOperationList(std::queue<DBOperation> expected_operations) {
+ DCHECK(receiver_);
+ receiver_->set_expected_operations(std::move(expected_operations));
+ }
+
+ void WaitForOperations() {
+ DCHECK(receiver_);
+ receiver_->WaitForOperations();
+ }
+
+ void VerifySharedStorageTablesAndColumnsSync() {
+ DCHECK(async_database_);
+
+ auto task = base::BindOnce([](SharedStorageDatabase* db) -> bool {
+ auto* sql_db = db->db();
+ EXPECT_TRUE(sql_db);
+ VerifySharedStorageTablesAndColumns(*sql_db);
+ return true;
+ });
+
+ base::test::TestFuture<bool> future;
+
+ auto wrapped_task = base::BindOnce(
+ [](base::OnceCallback<bool(SharedStorageDatabase*)> task,
+ base::OnceCallback<void(bool)> callback,
+ scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
+ SharedStorageDatabase* db) {
+ callback_task_runner->PostTask(
+ FROM_HERE,
+ base::BindOnce(std::move(callback), std::move(task).Run(db)));
+ },
+ std::move(task), future.GetCallback(),
+ base::SequencedTaskRunnerHandle::Get());
+
+ async_database_->GetSequenceBoundDatabaseForTesting()
+ .PostTaskWithThisObject(std::move(wrapped_task));
+
+ EXPECT_TRUE(future.Wait());
+ EXPECT_TRUE(future.Get());
+ }
+
+ void IsOpen(bool* out_boolean) {
+ DCHECK(out_boolean);
+ DCHECK(async_database_);
+ DCHECK(receiver_);
+
+ DBOperation operation(Type::DB_IS_OPEN);
+ auto callback =
+ receiver_->MakeBoolCallback(std::move(operation), out_boolean);
+ async_database_->IsOpenForTesting(std::move(callback));
+ }
+
+ bool IsOpenSync() {
+ DCHECK(async_database_);
+
+ base::test::TestFuture<bool> future;
+ async_database_->IsOpenForTesting(future.GetCallback());
+ EXPECT_TRUE(future.Wait());
+ return future.Get();
+ }
+
+ void DBStatus(InitStatus* out_status) {
+ DCHECK(out_status);
+ DCHECK(async_database_);
+ DCHECK(receiver_);
+
+ DBOperation operation(Type::DB_STATUS);
+ auto callback =
+ receiver_->MakeStatusCallback(std::move(operation), out_status);
+ async_database_->DBStatusForTesting(std::move(callback));
+ }
+
+ InitStatus DBStatusSync() {
+ DCHECK(async_database_);
+
+ base::test::TestFuture<InitStatus> future;
+ async_database_->DBStatusForTesting(future.GetCallback());
+ EXPECT_TRUE(future.Wait());
+ return future.Get();
+ }
+
+ void TrimMemory() {
+ DCHECK(async_database_);
+ DCHECK(receiver_);
+
+ DBOperation operation(Type::DB_TRIM_MEMORY);
+ auto callback = receiver_->MakeOnceClosure(std::move(operation));
+ async_database_->TrimMemory(std::move(callback));
+ }
+
+ void OverrideLastUsedTime(url::Origin context_origin,
+ base::Time override_last_used_time,
+ bool* out_success) {
+ DCHECK(out_success);
+ DCHECK(async_database_);
+ DCHECK(receiver_);
+
+ DBOperation operation(Type::DB_OVERRIDE_TIME, context_origin,
+ {TestDatabaseOperationReceiver::SerializeTime(
+ override_last_used_time)});
+ auto callback =
+ receiver_->MakeBoolCallback(std::move(operation), out_success);
+ async_database_->OverrideLastUsedTimeForTesting(std::move(context_origin),
+ override_last_used_time,
+ std::move(callback));
+ }
+
+ void Get(url::Origin context_origin,
+ std::u16string key,
+ GetResult* out_value) {
+ DCHECK(out_value);
+ DCHECK(async_database_);
+ DCHECK(receiver_);
+
+ DBOperation operation(Type::DB_GET, context_origin, {key});
+ auto callback =
+ receiver_->MakeGetResultCallback(std::move(operation), out_value);
+ async_database_->Get(std::move(context_origin), std::move(key),
+ std::move(callback));
+ }
+
+ GetResult GetSync(url::Origin context_origin, std::u16string key) {
+ DCHECK(async_database_);
+
+ base::test::TestFuture<GetResult> future;
+ async_database_->Get(std::move(context_origin), std::move(key),
+ future.GetCallback());
+ EXPECT_TRUE(future.Wait());
+ return future.Get();
+ }
+
+ void Set(url::Origin context_origin,
+ std::u16string key,
+ std::u16string value,
+ OperationResult* out_result,
+ SetBehavior behavior = SetBehavior::kDefault) {
+ DCHECK(out_result);
+ DCHECK(async_database_);
+ DCHECK(receiver_);
+
+ *out_result = OperationResult::kSqlError;
+ DBOperation operation(
+ Type::DB_SET, context_origin,
+ {key, value, base::NumberToString16(static_cast<int>(behavior))});
+ auto callback = receiver_->MakeOperationResultCallback(std::move(operation),
+ out_result);
+ async_database_->Set(std::move(context_origin), std::move(key),
+ std::move(value), std::move(callback), behavior);
+ }
+
+ OperationResult SetSync(url::Origin context_origin,
+ std::u16string key,
+ std::u16string value,
+ SetBehavior behavior = SetBehavior::kDefault) {
+ DCHECK(async_database_);
+
+ base::test::TestFuture<OperationResult> future;
+ async_database_->Set(std::move(context_origin), std::move(key),
+ std::move(value), future.GetCallback(), behavior);
+ EXPECT_TRUE(future.Wait());
+ return future.Get();
+ }
+
+ void Append(url::Origin context_origin,
+ std::u16string key,
+ std::u16string value,
+ OperationResult* out_result) {
+ DCHECK(out_result);
+ DCHECK(async_database_);
+ DCHECK(receiver_);
+
+ *out_result = OperationResult::kSqlError;
+ DBOperation operation(Type::DB_APPEND, context_origin, {key, value});
+ auto callback = receiver_->MakeOperationResultCallback(std::move(operation),
+ out_result);
+ async_database_->Append(std::move(context_origin), std::move(key),
+ std::move(value), std::move(callback));
+ }
+
+ OperationResult AppendSync(url::Origin context_origin,
+ std::u16string key,
+ std::u16string value) {
+ DCHECK(async_database_);
+
+ base::test::TestFuture<OperationResult> future;
+ async_database_->Append(std::move(context_origin), std::move(key),
+ std::move(value), future.GetCallback());
+ EXPECT_TRUE(future.Wait());
+ return future.Get();
+ }
+
+ void Delete(url::Origin context_origin,
+ std::u16string key,
+ OperationResult* out_result) {
+ DCHECK(out_result);
+ DCHECK(async_database_);
+ DCHECK(receiver_);
+
+ *out_result = OperationResult::kSqlError;
+ DBOperation operation(Type::DB_DELETE, context_origin, {key});
+ auto callback = receiver_->MakeOperationResultCallback(std::move(operation),
+ out_result);
+ async_database_->Delete(std::move(context_origin), std::move(key),
+ std::move(callback));
+ }
+
+ OperationResult DeleteSync(url::Origin context_origin, std::u16string key) {
+ DCHECK(async_database_);
+
+ base::test::TestFuture<OperationResult> future;
+ async_database_->Delete(std::move(context_origin), std::move(key),
+ future.GetCallback());
+ EXPECT_TRUE(future.Wait());
+ return future.Get();
+ }
+
+ void Length(url::Origin context_origin, int* out_length) {
+ DCHECK(out_length);
+ DCHECK(async_database_);
+ DCHECK(receiver_);
+
+ *out_length = -1;
+ DBOperation operation(Type::DB_LENGTH, context_origin);
+ auto callback =
+ receiver_->MakeIntCallback(std::move(operation), out_length);
+ async_database_->Length(std::move(context_origin), std::move(callback));
+ }
+
+ int LengthSync(url::Origin context_origin) {
+ DCHECK(async_database_);
+
+ base::test::TestFuture<int> future;
+ async_database_->Length(std::move(context_origin), future.GetCallback());
+ EXPECT_TRUE(future.Wait());
+ return future.Get();
+ }
+
+ void Key(url::Origin context_origin, int index, GetResult* out_key) {
+ DCHECK(out_key);
+ DCHECK(async_database_);
+ DCHECK(receiver_);
+
+ DBOperation operation(Type::DB_KEY, context_origin,
+ {base::NumberToString16(index)});
+ auto callback =
+ receiver_->MakeGetResultCallback(std::move(operation), out_key);
+ async_database_->Key(std::move(context_origin), index, std::move(callback));
+ }
+
+ GetResult KeySync(url::Origin context_origin, int index) {
+ DCHECK(async_database_);
+
+ base::test::TestFuture<GetResult> future;
+ async_database_->Key(std::move(context_origin), index,
+ future.GetCallback());
+ EXPECT_TRUE(future.Wait());
+ return future.Get();
+ }
+
+ void Clear(url::Origin context_origin, OperationResult* out_result) {
+ DCHECK(out_result);
+ DCHECK(async_database_);
+ DCHECK(receiver_);
+
+ *out_result = OperationResult::kSqlError;
+ DBOperation operation(Type::DB_CLEAR, context_origin);
+ auto callback = receiver_->MakeOperationResultCallback(std::move(operation),
+ out_result);
+ async_database_->Clear(std::move(context_origin), std::move(callback));
+ }
+
+ OperationResult ClearSync(url::Origin context_origin) {
+ DCHECK(async_database_);
+
+ base::test::TestFuture<OperationResult> future;
+ async_database_->Clear(std::move(context_origin), future.GetCallback());
+ EXPECT_TRUE(future.Wait());
+ return future.Get();
+ }
+
+ void FetchOrigins(std::vector<mojom::StorageUsageInfoPtr>* out_result) {
+ DCHECK(out_result);
+ DCHECK(async_database_);
+ DCHECK(receiver_);
+
+ DBOperation operation(Type::DB_FETCH_ORIGINS);
+ auto callback =
+ receiver_->MakeInfosCallback(std::move(operation), out_result);
+ async_database_->FetchOrigins(std::move(callback));
+ }
+
+ std::vector<mojom::StorageUsageInfoPtr> FetchOriginsSync() {
+ DCHECK(async_database_);
+
+ base::test::TestFuture<std::vector<mojom::StorageUsageInfoPtr>> future;
+ async_database_->FetchOrigins(future.GetCallback());
+ EXPECT_TRUE(future.Wait());
+ return future.Take();
+ }
+
+ void PurgeMatchingOrigins(OriginMatcherFunctionUtility* matcher_utility,
+ size_t matcher_id,
+ base::Time begin,
+ base::Time end,
+ OperationResult* out_result,
+ bool perform_storage_cleanup = false) {
+ DCHECK(out_result);
+ DCHECK(async_database_);
+ DCHECK(receiver_);
+ DCHECK(matcher_utility);
+ DCHECK(!(*matcher_utility).is_empty());
+ DCHECK_LT(matcher_id, (*matcher_utility).size());
+
+ std::vector<std::u16string> params(
+ {base::NumberToString16(matcher_id),
+ TestDatabaseOperationReceiver::SerializeTime(begin),
+ TestDatabaseOperationReceiver::SerializeTime(end),
+ TestDatabaseOperationReceiver::SerializeBool(
+ perform_storage_cleanup)});
+ DBOperation operation(Type::DB_PURGE_MATCHING, std::move(params));
+ auto callback = receiver_->MakeOperationResultCallback(std::move(operation),
+ out_result);
+ async_database_->PurgeMatchingOrigins(
+ matcher_utility->TakeMatcherFunctionForId(matcher_id), begin, end,
+ std::move(callback), perform_storage_cleanup);
+ }
+
+ void PurgeStaleOrigins(base::TimeDelta window_to_be_deemed_active,
+ OperationResult* out_result) {
+ DCHECK(out_result);
+ DCHECK(async_database_);
+ DCHECK(receiver_);
+
+ DBOperation operation(Type::DB_PURGE_STALE,
+ {TestDatabaseOperationReceiver::SerializeTimeDelta(
+ window_to_be_deemed_active)});
+ auto callback = receiver_->MakeOperationResultCallback(std::move(operation),
+ out_result);
+ async_database_->PurgeStaleOrigins(window_to_be_deemed_active,
+ std::move(callback));
+ }
+
+ protected:
+ base::test::TaskEnvironment task_environment_;
+ scoped_refptr<base::SequencedTaskRunner> task_runner_;
+ scoped_refptr<storage::MockSpecialStoragePolicy> special_storage_policy_;
+ std::unique_ptr<AsyncSharedStorageDatabase> async_database_;
+ std::unique_ptr<TestDatabaseOperationReceiver> receiver_;
+ base::test::ScopedFeatureList scoped_feature_list_;
+ base::ScopedTempDir temp_dir_;
+ base::FilePath file_name_;
+};
+
+// Test loading version 1 database.
+TEST_F(AsyncSharedStorageDatabaseTest, Version1_LoadFromFile) {
+ LoadFromFileSync("shared_storage.v1.sql");
+ ASSERT_TRUE(async_database_);
+
+ url::Origin google_com = url::Origin::Create(GURL("http://google.com/"));
+ EXPECT_EQ(GetSync(google_com, u"key1").data, u"value1");
+ EXPECT_EQ(GetSync(google_com, u"key2").data, u"value2");
+
+ // Because the SQL database is lazy-initialized, wait to verify tables and
+ // columns until after the first call to `GetSync()`.
+ VerifySharedStorageTablesAndColumnsSync();
+
+ std::vector<url::Origin> origins;
+ for (const auto& info : FetchOriginsSync())
+ origins.push_back(info->origin);
+ EXPECT_THAT(
+ origins,
+ ElementsAre(url::Origin::Create(GURL("http://abc.xyz")),
+ url::Origin::Create(GURL("http://chromium.org")), google_com,
+ url::Origin::Create(GURL("http://google.org")),
+ url::Origin::Create(GURL("http://growwithgoogle.com")),
+ url::Origin::Create(GURL("http://gv.com")),
+ url::Origin::Create(GURL("http://waymo.com")),
+ url::Origin::Create(GURL("http://withgoogle.com")),
+ url::Origin::Create(GURL("http://youtube.com"))));
+}
+
+TEST_F(AsyncSharedStorageDatabaseTest, Version1_DestroyTooNew) {
+ // Initialization should fail, since the last compatible version number
+ // is too high.
+ LoadFromFileSync("shared_storage.v1.init_too_new.sql");
+ ASSERT_TRUE(async_database_);
+
+ // Call an operation so that the database will attempt to be lazy-initialized.
+ EXPECT_EQ(
+ OperationResult::kInitFailure,
+ SetSync(url::Origin::Create(GURL("http://www.a.com")), u"key", u"value"));
+ ASSERT_FALSE(IsOpenSync());
+ EXPECT_EQ(InitStatus::kTooNew, DBStatusSync());
+
+ // Test that it is still OK to `Destroy()` the database.
+ EXPECT_TRUE(DestroySync());
+}
+
+TEST_F(AsyncSharedStorageDatabaseTest, Version0_DestroyTooOld) {
+ // Initialization should fail, since the current version number
+ // is too low and we're forcing there not to be a retry attempt.
+ LoadFromFileSync("shared_storage.v0.init_too_old.sql");
+ ASSERT_TRUE(async_database_);
+
+ // Call an operation so that the database will attempt to be lazy-initialized.
+ EXPECT_EQ(
+ OperationResult::kInitFailure,
+ SetSync(url::Origin::Create(GURL("http://www.a.com")), u"key", u"value"));
+ ASSERT_FALSE(IsOpenSync());
+ EXPECT_EQ(InitStatus::kTooOld, DBStatusSync());
+
+ // Test that it is still OK to `Destroy()` the database.
+ EXPECT_TRUE(DestroySync());
+}
+
+class AsyncSharedStorageDatabaseParamTest
+ : public AsyncSharedStorageDatabaseTest,
+ public testing::WithParamInterface<SharedStorageWrappedBool> {
+ public:
+ void SetUp() override {
+ AsyncSharedStorageDatabaseTest::SetUp();
+
+ auto options = SharedStorageOptions::Create()->GetDatabaseOptions();
+
+ if (GetParam().in_memory_only)
+ CreateSync(base::FilePath(), std::move(options));
+ else
+ CreateSync(file_name_, std::move(options));
+ }
+
+ void TearDown() override {
+ if (!GetParam().in_memory_only) {
+ // `TearDown()` will call `DestroySync()`. First verify that the file
+ // exists, so that when the we verify after destruction in `TearDown()`
+ // that the file no longer exists, we know that `Destroy()` was indeed
+ // successful.
+ EXPECT_TRUE(base::PathExists(file_name_));
+ }
+
+ AsyncSharedStorageDatabaseTest::TearDown();
+ }
+
+ void InitSharedStorageFeature() override {
+ scoped_feature_list_.InitAndEnableFeatureWithParameters(
+ {blink::features::kSharedStorageAPI},
+ {{"MaxSharedStorageEntriesPerOrigin",
+ base::NumberToString(kMaxEntriesPerOrigin)},
+ {"MaxSharedStorageStringLength",
+ base::NumberToString(kMaxStringLength)}});
+ }
+};
+
+INSTANTIATE_TEST_SUITE_P(All,
+ AsyncSharedStorageDatabaseParamTest,
+ testing::ValuesIn(GetSharedStorageWrappedBools()),
+ testing::PrintToStringParamName());
+
+// Operations are tested more thoroughly in shared_storage_database_unittest.cc.
+TEST_P(AsyncSharedStorageDatabaseParamTest, SyncOperations) {
+ url::Origin kOrigin1 = url::Origin::Create(GURL("http://www.example1.test"));
+ EXPECT_EQ(OperationResult::kSet, SetSync(kOrigin1, u"key1", u"value1"));
+ EXPECT_EQ(GetSync(kOrigin1, u"key1").data, u"value1");
+
+ EXPECT_EQ(OperationResult::kSet, SetSync(kOrigin1, u"key1", u"value2"));
+ EXPECT_EQ(GetSync(kOrigin1, u"key1").data, u"value2");
+
+ EXPECT_EQ(OperationResult::kSet, SetSync(kOrigin1, u"key2", u"value1"));
+ EXPECT_EQ(GetSync(kOrigin1, u"key2").data, u"value1");
+ EXPECT_EQ(2, LengthSync(kOrigin1));
+
+ EXPECT_EQ(OperationResult::kSuccess, DeleteSync(kOrigin1, u"key1"));
+ EXPECT_FALSE(GetSync(kOrigin1, u"key1").data);
+ EXPECT_EQ(1, LengthSync(kOrigin1));
+
+ EXPECT_EQ(OperationResult::kSet, AppendSync(kOrigin1, u"key1", u"value1"));
+ EXPECT_EQ(GetSync(kOrigin1, u"key1").data, u"value1");
+ EXPECT_EQ(2, LengthSync(kOrigin1));
+
+ EXPECT_EQ(OperationResult::kSet, AppendSync(kOrigin1, u"key1", u"value1"));
+ EXPECT_EQ(GetSync(kOrigin1, u"key1").data,
+ base::StrCat({u"value1", u"value1"}));
+ EXPECT_EQ(2, LengthSync(kOrigin1));
+
+ EXPECT_EQ(KeySync(kOrigin1, 0).data, u"key1");
+ EXPECT_EQ(KeySync(kOrigin1, 1).data, u"key2");
+
+ EXPECT_EQ(OperationResult::kSuccess, ClearSync(kOrigin1));
+ EXPECT_EQ(0, LengthSync(kOrigin1));
+}
+
+// Verifies that the async operations are executed in order and without races.
+TEST_P(AsyncSharedStorageDatabaseParamTest, AsyncOperations) {
+ url::Origin kOrigin1 = url::Origin::Create(GURL("http://www.example1.test"));
+
+ std::queue<DBOperation> operation_list(
+ {{Type::DB_SET,
+ kOrigin1,
+ {u"key1", u"value1",
+ TestDatabaseOperationReceiver::SerializeSetBehavior(
+ SetBehavior::kDefault)}},
+ {Type::DB_GET, kOrigin1, {u"key1"}},
+ {Type::DB_SET,
+ kOrigin1,
+ {u"key1", u"value2",
+ TestDatabaseOperationReceiver::SerializeSetBehavior(
+ SetBehavior::kDefault)}},
+ {Type::DB_GET, kOrigin1, {u"key1"}},
+ {Type::DB_SET,
+ kOrigin1,
+ {u"key2", u"value1",
+ TestDatabaseOperationReceiver::SerializeSetBehavior(
+ SetBehavior::kDefault)}},
+ {Type::DB_GET, kOrigin1, {u"key2"}},
+ {Type::DB_LENGTH, kOrigin1},
+ {Type::DB_DELETE, kOrigin1, {u"key1"}},
+ {Type::DB_LENGTH, kOrigin1},
+ {Type::DB_APPEND, kOrigin1, {u"key1", u"value1"}},
+ {Type::DB_GET, kOrigin1, {u"key1"}},
+ {Type::DB_LENGTH, kOrigin1},
+ {Type::DB_APPEND, kOrigin1, {u"key1", u"value1"}},
+ {Type::DB_GET, kOrigin1, {u"key1"}},
+ {Type::DB_LENGTH, kOrigin1},
+ {Type::DB_KEY, kOrigin1, {base::NumberToString16(0)}},
+ {Type::DB_KEY, kOrigin1, {base::NumberToString16(1)}},
+ {Type::DB_CLEAR, kOrigin1},
+ {Type::DB_LENGTH, kOrigin1}});
+
+ SetExpectedOperationList(std::move(operation_list));
+
+ OperationResult result1 = OperationResult::kSqlError;
+ Set(kOrigin1, u"key1", u"value1", &result1);
+ GetResult value1;
+ Get(kOrigin1, u"key1", &value1);
+ OperationResult result2 = OperationResult::kSqlError;
+ Set(kOrigin1, u"key1", u"value2", &result2);
+ GetResult value2;
+ Get(kOrigin1, u"key1", &value2);
+ OperationResult result3 = OperationResult::kSqlError;
+ Set(kOrigin1, u"key2", u"value1", &result3);
+ GetResult value3;
+ Get(kOrigin1, u"key2", &value3);
+ int length1 = -1;
+ Length(kOrigin1, &length1);
+
+ OperationResult result4 = OperationResult::kSqlError;
+ Delete(kOrigin1, u"key1", &result4);
+ int length2 = -1;
+ Length(kOrigin1, &length2);
+
+ OperationResult result5 = OperationResult::kSqlError;
+ Append(kOrigin1, u"key1", u"value1", &result5);
+ GetResult value4;
+ Get(kOrigin1, u"key1", &value4);
+ int length3 = -1;
+ Length(kOrigin1, &length3);
+
+ OperationResult result6 = OperationResult::kSqlError;
+ Append(kOrigin1, u"key1", u"value1", &result6);
+ GetResult value5;
+ Get(kOrigin1, u"key1", &value5);
+ int length4 = -1;
+ Length(kOrigin1, &length4);
+
+ GetResult key1;
+ Key(kOrigin1, 0, &key1);
+ GetResult key2;
+ Key(kOrigin1, 1, &key2);
+
+ OperationResult result7 = OperationResult::kSqlError;
+ Clear(kOrigin1, &result7);
+ int length5 = -1;
+ Length(kOrigin1, &length5);
+
+ WaitForOperations();
+ EXPECT_TRUE(is_finished());
+
+ EXPECT_EQ(OperationResult::kSet, result1);
+ EXPECT_EQ(value1.data, u"value1");
+ EXPECT_EQ(OperationResult::kSet, result2);
+ EXPECT_EQ(value2.data, u"value2");
+ EXPECT_EQ(OperationResult::kSet, result3);
+ EXPECT_EQ(value3.data, u"value1");
+ EXPECT_EQ(2, length1);
+
+ EXPECT_EQ(OperationResult::kSuccess, result4);
+ EXPECT_EQ(1, length2);
+
+ EXPECT_EQ(OperationResult::kSet, result5);
+ EXPECT_EQ(value4.data, u"value1");
+ EXPECT_EQ(2, length3);
+
+ EXPECT_EQ(OperationResult::kSet, result6);
+ EXPECT_EQ(value5.data, u"value1value1");
+ EXPECT_EQ(2, length4);
+
+ EXPECT_EQ(key1.data, u"key1");
+ EXPECT_EQ(key2.data, u"key2");
+
+ EXPECT_EQ(OperationResult::kSuccess, result7);
+ EXPECT_EQ(0, length5);
+}
+
+TEST_P(AsyncSharedStorageDatabaseParamTest,
+ LazyInit_IgnoreForGet_CreateForSet) {
+ url::Origin kOrigin1 = url::Origin::Create(GURL("http://www.example1.test"));
+
+ std::queue<DBOperation> operation_list;
+ operation_list.push(DBOperation(Type::DB_IS_OPEN));
+ operation_list.push(DBOperation(Type::DB_STATUS));
+ operation_list.push(DBOperation(Type::DB_GET, kOrigin1, {u"key1"}));
+ operation_list.push(DBOperation(Type::DB_IS_OPEN));
+ operation_list.push(DBOperation(Type::DB_STATUS));
+ operation_list.push(
+ DBOperation(Type::DB_SET, kOrigin1,
+ {u"key1", u"value1",
+ TestDatabaseOperationReceiver::SerializeSetBehavior(
+ SetBehavior::kDefault)}));
+ operation_list.push(DBOperation(Type::DB_IS_OPEN));
+ operation_list.push(DBOperation(Type::DB_STATUS));
+ operation_list.push(DBOperation(Type::DB_GET, kOrigin1, {u"key1"}));
+ operation_list.push(DBOperation(Type::DB_LENGTH, kOrigin1));
+
+ SetExpectedOperationList(std::move(operation_list));
+
+ bool open1 = false;
+ IsOpen(&open1);
+ InitStatus status1 = InitStatus::kUnattempted;
+ DBStatus(&status1);
+
+ // Test that we can successfully call `Get()` on a nonexistent key before the
+ // database is initialized.
+ GetResult value1;
+ Get(kOrigin1, u"key1", &value1);
+ bool open2 = false;
+ IsOpen(&open2);
+ InitStatus status2 = InitStatus::kUnattempted;
+ DBStatus(&status2);
+
+ // Call an operation that initializes the database.
+ OperationResult result1 = OperationResult::kSqlError;
+ Set(kOrigin1, u"key1", u"value1", &result1);
+ bool open3 = false;
+ IsOpen(&open3);
+ InitStatus status3 = InitStatus::kUnattempted;
+ DBStatus(&status3);
+
+ GetResult value2;
+ Get(kOrigin1, u"key1", &value2);
+ int length1 = -1;
+ Length(kOrigin1, &length1);
+
+ WaitForOperations();
+ EXPECT_TRUE(is_finished());
+
+ EXPECT_FALSE(open1);
+ EXPECT_EQ(InitStatus::kUnattempted, status1);
+
+ EXPECT_FALSE(value1.data);
+ EXPECT_EQ(OperationResult::kSuccess, value1.result);
+ EXPECT_EQ(!GetParam().in_memory_only, open2);
+ EXPECT_EQ(InitStatus::kUnattempted, status2);
+
+ EXPECT_EQ(OperationResult::kSet, result1);
+ EXPECT_TRUE(open3);
+ EXPECT_EQ(InitStatus::kSuccess, status3);
+
+ EXPECT_EQ(value2.data, u"value1");
+ EXPECT_EQ(OperationResult::kSuccess, value2.result);
+ EXPECT_EQ(1, length1);
+}
+
+TEST_P(AsyncSharedStorageDatabaseParamTest,
+ LazyInit_IgnoreForDelete_CreateForAppend) {
+ url::Origin kOrigin1 = url::Origin::Create(GURL("http://www.example1.test"));
+
+ std::queue<DBOperation> operation_list;
+ operation_list.push(DBOperation(Type::DB_IS_OPEN));
+ operation_list.push(DBOperation(Type::DB_STATUS));
+ operation_list.push(DBOperation(Type::DB_DELETE, kOrigin1, {u"key1"}));
+ operation_list.push(DBOperation(Type::DB_IS_OPEN));
+ operation_list.push(DBOperation(Type::DB_STATUS));
+ operation_list.push(
+ DBOperation(Type::DB_APPEND, kOrigin1, {u"key1", u"value1"}));
+ operation_list.push(DBOperation(Type::DB_IS_OPEN));
+ operation_list.push(DBOperation(Type::DB_STATUS));
+ operation_list.push(DBOperation(Type::DB_DELETE, kOrigin1, {u"key2"}));
+
+ SetExpectedOperationList(std::move(operation_list));
+
+ bool open1 = false;
+ IsOpen(&open1);
+ InitStatus status1 = InitStatus::kUnattempted;
+ DBStatus(&status1);
+
+ // Test that we can successfully call `Delete()` on a nonexistent key before
+ // the database is initialized.
+ OperationResult result1 = OperationResult::kSqlError;
+ Delete(kOrigin1, u"key1", &result1);
+ bool open2 = false;
+ IsOpen(&open2);
+ InitStatus status2 = InitStatus::kUnattempted;
+ DBStatus(&status2);
+
+ // Call an operation that initializes the database.
+ OperationResult result2 = OperationResult::kSqlError;
+ Append(kOrigin1, u"key1", u"value1", &result2);
+ bool open3 = false;
+ IsOpen(&open3);
+ InitStatus status3 = InitStatus::kUnattempted;
+ DBStatus(&status3);
+
+ // Test that we can successfully call `Delete()` on a nonexistent key after
+ // the database is initialized.
+ OperationResult result3 = OperationResult::kSqlError;
+ Delete(kOrigin1, u"key2", &result3);
+
+ WaitForOperations();
+ EXPECT_TRUE(is_finished());
+
+ EXPECT_FALSE(open1);
+ EXPECT_EQ(InitStatus::kUnattempted, status1);
+
+ EXPECT_EQ(OperationResult::kSuccess, result1);
+ EXPECT_EQ(!GetParam().in_memory_only, open2);
+ EXPECT_EQ(InitStatus::kUnattempted, status2);
+
+ EXPECT_EQ(OperationResult::kSet, result2);
+ EXPECT_TRUE(open3);
+ EXPECT_EQ(InitStatus::kSuccess, status3);
+
+ EXPECT_EQ(OperationResult::kSuccess, result3);
+}
+
+TEST_P(AsyncSharedStorageDatabaseParamTest,
+ LazyInit_IgnoreForClear_CreateForAppend) {
+ url::Origin kOrigin1 = url::Origin::Create(GURL("http://www.example1.test"));
+ url::Origin kOrigin2 = url::Origin::Create(GURL("http://www.example2.test"));
+
+ std::queue<DBOperation> operation_list;
+ operation_list.push(DBOperation(Type::DB_IS_OPEN));
+ operation_list.push(DBOperation(Type::DB_STATUS));
+ operation_list.push(DBOperation(Type::DB_CLEAR, kOrigin1));
+ operation_list.push(DBOperation(Type::DB_IS_OPEN));
+ operation_list.push(DBOperation(Type::DB_STATUS));
+ operation_list.push(
+ DBOperation(Type::DB_APPEND, kOrigin1, {u"key1", u"value1"}));
+ operation_list.push(DBOperation(Type::DB_IS_OPEN));
+ operation_list.push(DBOperation(Type::DB_STATUS));
+ operation_list.push(DBOperation(Type::DB_CLEAR, kOrigin2));
+
+ SetExpectedOperationList(std::move(operation_list));
+
+ bool open1 = false;
+ IsOpen(&open1);
+ InitStatus status1 = InitStatus::kUnattempted;
+ DBStatus(&status1);
+
+ // Test that we can successfully call `Clear()` on a nonexistent origin before
+ // the database is initialized.
+ OperationResult result1 = OperationResult::kSqlError;
+ Clear(kOrigin1, &result1);
+ bool open2 = false;
+ IsOpen(&open2);
+ InitStatus status2 = InitStatus::kUnattempted;
+ DBStatus(&status2);
+
+ // Call an operation that initializes the database.
+ OperationResult result2 = OperationResult::kSqlError;
+ Append(kOrigin1, u"key1", u"value1", &result2);
+ bool open3 = false;
+ IsOpen(&open3);
+ InitStatus status3 = InitStatus::kUnattempted;
+ DBStatus(&status3);
+
+ // Test that we can successfully call `Clear()` on a nonexistent origin after
+ // the database is initialized.
+ OperationResult result3 = OperationResult::kSqlError;
+ Clear(kOrigin2, &result3);
+
+ WaitForOperations();
+ EXPECT_TRUE(is_finished());
+
+ EXPECT_FALSE(open1);
+ EXPECT_EQ(InitStatus::kUnattempted, status1);
+
+ EXPECT_EQ(OperationResult::kSuccess, result1);
+ EXPECT_EQ(!GetParam().in_memory_only, open2);
+ EXPECT_EQ(InitStatus::kUnattempted, status2);
+
+ EXPECT_EQ(OperationResult::kSet, result2);
+ EXPECT_TRUE(open3);
+ EXPECT_EQ(InitStatus::kSuccess, status3);
+
+ EXPECT_EQ(OperationResult::kSuccess, result3);
+}
+
+TEST_P(AsyncSharedStorageDatabaseParamTest, PurgeStaleOrigins) {
+ url::Origin kOrigin1 = url::Origin::Create(GURL("http://www.example1.test"));
+ url::Origin kOrigin2 = url::Origin::Create(GURL("http://www.example2.test"));
+ url::Origin kOrigin3 = url::Origin::Create(GURL("http://www.example3.test"));
+ url::Origin kOrigin4 = url::Origin::Create(GURL("http://www.example4.test"));
+
+ std::queue<DBOperation> operation_list;
+ operation_list.push(DBOperation(Type::DB_FETCH_ORIGINS));
+ operation_list.push(DBOperation(Type::DB_IS_OPEN));
+ operation_list.push(DBOperation(Type::DB_STATUS));
+
+ operation_list.push(DBOperation(
+ Type::DB_PURGE_STALE,
+ {TestDatabaseOperationReceiver::SerializeTimeDelta(base::Days(1))}));
+
+ operation_list.push(
+ DBOperation(Type::DB_SET, kOrigin1,
+ {u"key1", u"value1",
+ TestDatabaseOperationReceiver::SerializeSetBehavior(
+ SetBehavior::kDefault)}));
+ operation_list.push(DBOperation(Type::DB_IS_OPEN));
+ operation_list.push(DBOperation(Type::DB_STATUS));
+
+ operation_list.push(
+ DBOperation(Type::DB_SET, kOrigin1,
+ {u"key2", u"value2",
+ TestDatabaseOperationReceiver::SerializeSetBehavior(
+ SetBehavior::kDefault)}));
+ operation_list.push(DBOperation(Type::DB_LENGTH, kOrigin1));
+
+ operation_list.push(
+ DBOperation(Type::DB_SET, kOrigin2,
+ {u"key1", u"value1",
+ TestDatabaseOperationReceiver::SerializeSetBehavior(
+ SetBehavior::kDefault)}));
+ operation_list.push(DBOperation(Type::DB_LENGTH, kOrigin2));
+
+ operation_list.push(
+ DBOperation(Type::DB_SET, kOrigin3,
+ {u"key1", u"value1",
+ TestDatabaseOperationReceiver::SerializeSetBehavior(
+ SetBehavior::kDefault)}));
+ operation_list.push(
+ DBOperation(Type::DB_SET, kOrigin3,
+ {u"key2", u"value2",
+ TestDatabaseOperationReceiver::SerializeSetBehavior(
+ SetBehavior::kDefault)}));
+ operation_list.push(
+ DBOperation(Type::DB_SET, kOrigin3,
+ {u"key3", u"value3",
+ TestDatabaseOperationReceiver::SerializeSetBehavior(
+ SetBehavior::kDefault)}));
+ operation_list.push(DBOperation(Type::DB_LENGTH, kOrigin3));
+
+ operation_list.push(
+ DBOperation(Type::DB_SET, kOrigin4,
+ {u"key1", u"value1",
+ TestDatabaseOperationReceiver::SerializeSetBehavior(
+ SetBehavior::kDefault)}));
+ operation_list.push(
+ DBOperation(Type::DB_SET, kOrigin4,
+ {u"key2", u"value2",
+ TestDatabaseOperationReceiver::SerializeSetBehavior(
+ SetBehavior::kDefault)}));
+ operation_list.push(
+ DBOperation(Type::DB_SET, kOrigin4,
+ {u"key3", u"value3",
+ TestDatabaseOperationReceiver::SerializeSetBehavior(
+ SetBehavior::kDefault)}));
+ operation_list.push(
+ DBOperation(Type::DB_SET, kOrigin4,
+ {u"key4", u"value4",
+ TestDatabaseOperationReceiver::SerializeSetBehavior(
+ SetBehavior::kDefault)}));
+ operation_list.push(DBOperation(Type::DB_LENGTH, kOrigin4));
+
+ operation_list.push(DBOperation(Type::DB_FETCH_ORIGINS));
+
+ base::Time override_time1 = base::Time::Now() - base::Days(2);
+ operation_list.push(DBOperation(
+ Type::DB_OVERRIDE_TIME, kOrigin1,
+ {TestDatabaseOperationReceiver::SerializeTime(override_time1)}));
+ operation_list.push(DBOperation(
+ Type::DB_PURGE_STALE,
+ {TestDatabaseOperationReceiver::SerializeTimeDelta(base::Days(1))}));
+
+ operation_list.push(DBOperation(Type::DB_LENGTH, kOrigin1));
+ operation_list.push(DBOperation(Type::DB_LENGTH, kOrigin2));
+ operation_list.push(DBOperation(Type::DB_LENGTH, kOrigin3));
+ operation_list.push(DBOperation(Type::DB_LENGTH, kOrigin4));
+ operation_list.push(DBOperation(Type::DB_FETCH_ORIGINS));
+
+ base::Time override_time2 = base::Time::Now() - base::Hours(2);
+ operation_list.push(DBOperation(
+ Type::DB_OVERRIDE_TIME, kOrigin3,
+ {TestDatabaseOperationReceiver::SerializeTime(override_time2)}));
+ operation_list.push(DBOperation(
+ Type::DB_PURGE_STALE,
+ {TestDatabaseOperationReceiver::SerializeTimeDelta(base::Hours(1))}));
+
+ operation_list.push(DBOperation(Type::DB_LENGTH, kOrigin1));
+ operation_list.push(DBOperation(Type::DB_LENGTH, kOrigin2));
+ operation_list.push(DBOperation(Type::DB_LENGTH, kOrigin3));
+ operation_list.push(DBOperation(Type::DB_LENGTH, kOrigin4));
+
+ operation_list.push(DBOperation(Type::DB_TRIM_MEMORY));
+ operation_list.push(DBOperation(Type::DB_FETCH_ORIGINS));
+
+ operation_list.push(DBOperation(Type::DB_GET, kOrigin2, {u"key1"}));
+ operation_list.push(DBOperation(Type::DB_GET, kOrigin4, {u"key1"}));
+ operation_list.push(DBOperation(Type::DB_GET, kOrigin4, {u"key2"}));
+ operation_list.push(DBOperation(Type::DB_GET, kOrigin4, {u"key3"}));
+ operation_list.push(DBOperation(Type::DB_GET, kOrigin4, {u"key4"}));
+
+ SetExpectedOperationList(std::move(operation_list));
+
+ // Check that origin list is initially empty due to the database not being
+ // initialized.
+ std::vector<mojom::StorageUsageInfoPtr> infos1;
+ FetchOrigins(&infos1);
+
+ // Check that calling `PurgeStaleOrigins()` on the uninitialized database
+ // doesn't give an error.
+ bool open1 = false;
+ IsOpen(&open1);
+ InitStatus status1 = InitStatus::kUnattempted;
+ DBStatus(&status1);
+
+ OperationResult result1 = OperationResult::kSqlError;
+ PurgeStaleOrigins(base::Days(1), &result1);
+
+ OperationResult result2 = OperationResult::kSqlError;
+ Set(kOrigin1, u"key1", u"value1", &result2);
+
+ bool open2 = false;
+ IsOpen(&open2);
+ InitStatus status2 = InitStatus::kUnattempted;
+ DBStatus(&status2);
+
+ OperationResult result3 = OperationResult::kSqlError;
+ Set(kOrigin1, u"key2", u"value2", &result3);
+ int length1 = -1;
+ Length(kOrigin1, &length1);
+
+ OperationResult result4 = OperationResult::kSqlError;
+ Set(kOrigin2, u"key1", u"value1", &result4);
+ int length2 = -1;
+ Length(kOrigin2, &length2);
+
+ OperationResult result5 = OperationResult::kSqlError;
+ Set(kOrigin3, u"key1", u"value1", &result5);
+ OperationResult result6 = OperationResult::kSqlError;
+ Set(kOrigin3, u"key2", u"value2", &result6);
+ OperationResult result7 = OperationResult::kSqlError;
+ Set(kOrigin3, u"key3", u"value3", &result7);
+ int length3 = -1;
+ Length(kOrigin3, &length3);
+
+ OperationResult result8 = OperationResult::kSqlError;
+ Set(kOrigin4, u"key1", u"value1", &result8);
+ OperationResult result9 = OperationResult::kSqlError;
+ Set(kOrigin4, u"key2", u"value2", &result9);
+ OperationResult result10 = OperationResult::kSqlError;
+ Set(kOrigin4, u"key3", u"value3", &result10);
+ OperationResult result11 = OperationResult::kSqlError;
+ Set(kOrigin4, u"key4", u"value4", &result11);
+ int length4 = -1;
+ Length(kOrigin4, &length4);
+
+ std::vector<mojom::StorageUsageInfoPtr> infos2;
+ FetchOrigins(&infos2);
+
+ bool success1 = false;
+ OverrideLastUsedTime(kOrigin1, override_time1, &success1);
+
+ OperationResult result12 = OperationResult::kSqlError;
+ PurgeStaleOrigins(base::Days(1), &result12);
+
+ int length5 = -1;
+ Length(kOrigin1, &length5);
+ int length6 = -1;
+ Length(kOrigin2, &length6);
+ int length7 = -1;
+ Length(kOrigin3, &length7);
+ int length8 = -1;
+ Length(kOrigin4, &length8);
+
+ std::vector<mojom::StorageUsageInfoPtr> infos3;
+ FetchOrigins(&infos3);
+
+ bool success2 = false;
+ OverrideLastUsedTime(kOrigin3, override_time2, &success2);
+
+ OperationResult result13 = OperationResult::kSqlError;
+ PurgeStaleOrigins(base::Hours(1), &result13);
+
+ int length9 = -1;
+ Length(kOrigin1, &length9);
+ int length10 = -1;
+ Length(kOrigin2, &length10);
+ int length11 = -1;
+ Length(kOrigin3, &length11);
+ int length12 = -1;
+ Length(kOrigin4, &length12);
+
+ TrimMemory();
+
+ std::vector<mojom::StorageUsageInfoPtr> infos4;
+ FetchOrigins(&infos4);
+
+ GetResult value1;
+ Get(kOrigin2, u"key1", &value1);
+ GetResult value2;
+ Get(kOrigin4, u"key1", &value2);
+ GetResult value3;
+ Get(kOrigin4, u"key2", &value3);
+ GetResult value4;
+ Get(kOrigin4, u"key3", &value4);
+ GetResult value5;
+ Get(kOrigin4, u"key4", &value5);
+
+ WaitForOperations();
+ EXPECT_TRUE(is_finished());
+
+ // Database is not yet initialized. `FetchOrigins()` returns an empty vector.
+ EXPECT_TRUE(infos1.empty());
+ EXPECT_EQ(!GetParam().in_memory_only, open1);
+ EXPECT_EQ(InitStatus::kUnattempted, status1);
+
+ // No error from calling `PurgeStaleOrigins()` on an uninitialized
+ // database.
+ EXPECT_EQ(OperationResult::kSuccess, result1);
+
+ // The call to `Set()` initializes the database.
+ EXPECT_EQ(OperationResult::kSet, result2);
+ EXPECT_TRUE(open2);
+ EXPECT_EQ(InitStatus::kSuccess, status2);
+
+ EXPECT_EQ(OperationResult::kSet, result3);
+ EXPECT_EQ(2, length1);
+
+ EXPECT_EQ(OperationResult::kSet, result4);
+ EXPECT_EQ(1, length2);
+
+ EXPECT_EQ(OperationResult::kSet, result5);
+ EXPECT_EQ(OperationResult::kSet, result6);
+ EXPECT_EQ(OperationResult::kSet, result7);
+ EXPECT_EQ(3, length3);
+
+ EXPECT_EQ(OperationResult::kSet, result8);
+ EXPECT_EQ(OperationResult::kSet, result9);
+ EXPECT_EQ(OperationResult::kSet, result10);
+ EXPECT_EQ(OperationResult::kSet, result11);
+ EXPECT_EQ(4, length4);
+
+ std::vector<url::Origin> origins;
+ for (const auto& info : infos2)
+ origins.push_back(info->origin);
+ EXPECT_THAT(origins, ElementsAre(kOrigin1, kOrigin2, kOrigin3, kOrigin4));
+
+ EXPECT_TRUE(success1);
+ EXPECT_EQ(OperationResult::kSuccess, result12);
+
+ // `kOrigin1` is cleared. The other origins are not.
+ EXPECT_EQ(0, length5);
+ EXPECT_EQ(1, length6);
+ EXPECT_EQ(3, length7);
+ EXPECT_EQ(4, length8);
+
+ origins.clear();
+ for (const auto& info : infos3)
+ origins.push_back(info->origin);
+ EXPECT_THAT(origins, ElementsAre(kOrigin2, kOrigin3, kOrigin4));
+
+ EXPECT_TRUE(success2);
+ EXPECT_EQ(OperationResult::kSuccess, result13);
+
+ // `kOrigin3` is cleared. The other remaining ones are not.
+ EXPECT_EQ(0, length9);
+ EXPECT_EQ(1, length10);
+ EXPECT_EQ(0, length11);
+ EXPECT_EQ(4, length12);
+
+ origins.clear();
+ for (const auto& info : infos4)
+ origins.push_back(info->origin);
+ EXPECT_THAT(origins, ElementsAre(kOrigin2, kOrigin4));
+
+ // Database is still intact after trimming memory.
+ EXPECT_EQ(value1.data, u"value1");
+ EXPECT_EQ(OperationResult::kSuccess, value1.result);
+ EXPECT_EQ(value2.data, u"value1");
+ EXPECT_EQ(OperationResult::kSuccess, value2.result);
+ EXPECT_EQ(value3.data, u"value2");
+ EXPECT_EQ(OperationResult::kSuccess, value3.result);
+ EXPECT_EQ(value4.data, u"value3");
+ EXPECT_EQ(OperationResult::kSuccess, value4.result);
+ EXPECT_EQ(value5.data, u"value4");
+ EXPECT_EQ(OperationResult::kSuccess, value5.result);
+}
+
+class AsyncSharedStorageDatabasePurgeMatchingOriginsParamTest
+ : public AsyncSharedStorageDatabaseTest,
+ public testing::WithParamInterface<PurgeMatchingOriginsParams> {
+ public:
+ void SetUp() override {
+ AsyncSharedStorageDatabaseTest::SetUp();
+
+ auto options = SharedStorageOptions::Create()->GetDatabaseOptions();
+
+ if (GetParam().in_memory_only)
+ CreateSync(base::FilePath(), std::move(options));
+ else
+ CreateSync(file_name_, std::move(options));
+ }
+
+ void InitSharedStorageFeature() override {
+ scoped_feature_list_.InitAndEnableFeatureWithParameters(
+ {blink::features::kSharedStorageAPI},
+ {{"MaxSharedStorageEntriesPerOrigin",
+ base::NumberToString(kMaxEntriesPerOrigin)},
+ {"MaxSharedStorageStringLength",
+ base::NumberToString(kMaxStringLength)}});
+ }
+};
+
+INSTANTIATE_TEST_SUITE_P(
+ All,
+ AsyncSharedStorageDatabasePurgeMatchingOriginsParamTest,
+ testing::ValuesIn(GetPurgeMatchingOriginsParams()),
+ testing::PrintToStringParamName());
+
+TEST_P(AsyncSharedStorageDatabasePurgeMatchingOriginsParamTest,
+ SinceThreshold) {
+ url::Origin kOrigin1 = url::Origin::Create(GURL("http://www.example1.test"));
+ url::Origin kOrigin2 = url::Origin::Create(GURL("http://www.example2.test"));
+ url::Origin kOrigin3 = url::Origin::Create(GURL("http://www.example3.test"));
+ url::Origin kOrigin4 = url::Origin::Create(GURL("http://www.example4.test"));
+ url::Origin kOrigin5 = url::Origin::Create(GURL("http://www.example5.test"));
+
+ std::queue<DBOperation> operation_list;
+ operation_list.push(DBOperation(Type::DB_FETCH_ORIGINS));
+ operation_list.push(DBOperation(Type::DB_IS_OPEN));
+ operation_list.push(DBOperation(Type::DB_STATUS));
+
+ base::Time threshold1 = base::Time::Now();
+ OriginMatcherFunctionUtility matcher_utility;
+ size_t matcher_id1 = matcher_utility.RegisterMatcherFunction({kOrigin1});
+
+ operation_list.push(DBOperation(
+ Type::DB_PURGE_MATCHING,
+ {base::NumberToString16(matcher_id1),
+ TestDatabaseOperationReceiver::SerializeTime(threshold1),
+ TestDatabaseOperationReceiver::SerializeTime(base::Time::Max()),
+ TestDatabaseOperationReceiver::SerializeBool(
+ GetParam().perform_storage_cleanup)}));
+
+ operation_list.push(
+ DBOperation(Type::DB_SET, kOrigin1,
+ {u"key1", u"value1",
+ TestDatabaseOperationReceiver::SerializeSetBehavior(
+ SetBehavior::kDefault)}));
+ operation_list.push(DBOperation(Type::DB_IS_OPEN));
+ operation_list.push(DBOperation(Type::DB_STATUS));
+
+ operation_list.push(
+ DBOperation(Type::DB_SET, kOrigin1,
+ {u"key2", u"value2",
+ TestDatabaseOperationReceiver::SerializeSetBehavior(
+ SetBehavior::kDefault)}));
+ operation_list.push(DBOperation(Type::DB_LENGTH, kOrigin1));
+
+ operation_list.push(
+ DBOperation(Type::DB_SET, kOrigin2,
+ {u"key1", u"value1",
+ TestDatabaseOperationReceiver::SerializeSetBehavior(
+ SetBehavior::kDefault)}));
+ operation_list.push(DBOperation(Type::DB_LENGTH, kOrigin2));
+
+ operation_list.push(
+ DBOperation(Type::DB_SET, kOrigin3,
+ {u"key1", u"value1",
+ TestDatabaseOperationReceiver::SerializeSetBehavior(
+ SetBehavior::kDefault)}));
+ operation_list.push(
+ DBOperation(Type::DB_SET, kOrigin3,
+ {u"key2", u"value2",
+ TestDatabaseOperationReceiver::SerializeSetBehavior(
+ SetBehavior::kDefault)}));
+ operation_list.push(
+ DBOperation(Type::DB_SET, kOrigin3,
+ {u"key3", u"value3",
+ TestDatabaseOperationReceiver::SerializeSetBehavior(
+ SetBehavior::kDefault)}));
+ operation_list.push(DBOperation(Type::DB_LENGTH, kOrigin3));
+
+ operation_list.push(
+ DBOperation(Type::DB_SET, kOrigin4,
+ {u"key1", u"value1",
+ TestDatabaseOperationReceiver::SerializeSetBehavior(
+ SetBehavior::kDefault)}));
+ operation_list.push(
+ DBOperation(Type::DB_SET, kOrigin4,
+ {u"key2", u"value2",
+ TestDatabaseOperationReceiver::SerializeSetBehavior(
+ SetBehavior::kDefault)}));
+ operation_list.push(
+ DBOperation(Type::DB_SET, kOrigin4,
+ {u"key3", u"value3",
+ TestDatabaseOperationReceiver::SerializeSetBehavior(
+ SetBehavior::kDefault)}));
+ operation_list.push(
+ DBOperation(Type::DB_SET, kOrigin4,
+ {u"key4", u"value4",
+ TestDatabaseOperationReceiver::SerializeSetBehavior(
+ SetBehavior::kDefault)}));
+ operation_list.push(DBOperation(Type::DB_LENGTH, kOrigin4));
+
+ operation_list.push(
+ DBOperation(Type::DB_SET, kOrigin5,
+ {u"key1", u"value1",
+ TestDatabaseOperationReceiver::SerializeSetBehavior(
+ SetBehavior::kDefault)}));
+ operation_list.push(DBOperation(Type::DB_LENGTH, kOrigin5));
+
+ operation_list.push(DBOperation(Type::DB_FETCH_ORIGINS));
+
+ base::Time threshold2 = base::Time::Now() + base::Seconds(100);
+ base::Time override_time1 = threshold2 + base::Milliseconds(5);
+ operation_list.push(DBOperation(
+ Type::DB_OVERRIDE_TIME, kOrigin1,
+ {TestDatabaseOperationReceiver::SerializeTime(override_time1)}));
+
+ size_t matcher_id2 =
+ matcher_utility.RegisterMatcherFunction({kOrigin1, kOrigin2, kOrigin5});
+ operation_list.push(DBOperation(
+ Type::DB_PURGE_MATCHING,
+ {base::NumberToString16(matcher_id2),
+ TestDatabaseOperationReceiver::SerializeTime(threshold2),
+ TestDatabaseOperationReceiver::SerializeTime(base::Time::Max()),
+ TestDatabaseOperationReceiver::SerializeBool(
+ GetParam().perform_storage_cleanup)}));
+
+ operation_list.push(DBOperation(Type::DB_LENGTH, kOrigin1));
+ operation_list.push(DBOperation(Type::DB_LENGTH, kOrigin2));
+ operation_list.push(DBOperation(Type::DB_LENGTH, kOrigin3));
+ operation_list.push(DBOperation(Type::DB_LENGTH, kOrigin4));
+ operation_list.push(DBOperation(Type::DB_LENGTH, kOrigin5));
+
+ operation_list.push(DBOperation(Type::DB_FETCH_ORIGINS));
+
+ base::Time threshold3 = base::Time::Now() + base::Seconds(200);
+ operation_list.push(
+ DBOperation(Type::DB_OVERRIDE_TIME, kOrigin3,
+ {TestDatabaseOperationReceiver::SerializeTime(threshold3)}));
+
+ base::Time threshold4 = threshold3 + base::Seconds(100);
+ operation_list.push(
+ DBOperation(Type::DB_OVERRIDE_TIME, kOrigin5,
+ {TestDatabaseOperationReceiver::SerializeTime(threshold4)}));
+
+ size_t matcher_id3 = matcher_utility.RegisterMatcherFunction(
+ {kOrigin2, kOrigin3, kOrigin4, kOrigin5});
+ operation_list.push(
+ DBOperation(Type::DB_PURGE_MATCHING,
+ {base::NumberToString16(matcher_id3),
+ TestDatabaseOperationReceiver::SerializeTime(threshold3),
+ TestDatabaseOperationReceiver::SerializeTime(threshold4),
+ TestDatabaseOperationReceiver::SerializeBool(
+ GetParam().perform_storage_cleanup)}));
+
+ operation_list.push(DBOperation(Type::DB_LENGTH, kOrigin1));
+ operation_list.push(DBOperation(Type::DB_LENGTH, kOrigin2));
+ operation_list.push(DBOperation(Type::DB_LENGTH, kOrigin3));
+ operation_list.push(DBOperation(Type::DB_LENGTH, kOrigin4));
+ operation_list.push(DBOperation(Type::DB_LENGTH, kOrigin5));
+
+ operation_list.push(DBOperation(Type::DB_TRIM_MEMORY));
+
+ operation_list.push(DBOperation(Type::DB_FETCH_ORIGINS));
+
+ operation_list.push(DBOperation(Type::DB_GET, kOrigin2, {u"key1"}));
+ operation_list.push(DBOperation(Type::DB_GET, kOrigin4, {u"key1"}));
+ operation_list.push(DBOperation(Type::DB_GET, kOrigin4, {u"key2"}));
+ operation_list.push(DBOperation(Type::DB_GET, kOrigin4, {u"key3"}));
+ operation_list.push(DBOperation(Type::DB_GET, kOrigin4, {u"key4"}));
+
+ operation_list.push(DBOperation(Type::DB_DESTROY));
+
+ SetExpectedOperationList(std::move(operation_list));
+
+ // Check that origin list is initially empty due to the database not being
+ // initialized.
+ std::vector<mojom::StorageUsageInfoPtr> infos1;
+ FetchOrigins(&infos1);
+
+ // Check that calling `PurgeMatchingOrigins()` on the uninitialized database
+ // doesn't give an error.
+ bool open1 = false;
+ IsOpen(&open1);
+ InitStatus status1 = InitStatus::kUnattempted;
+ DBStatus(&status1);
+
+ OperationResult result1 = OperationResult::kSqlError;
+ PurgeMatchingOrigins(&matcher_utility, matcher_id1, threshold1,
+ base::Time::Max(), &result1,
+ GetParam().perform_storage_cleanup);
+
+ OperationResult result2 = OperationResult::kSqlError;
+ Set(kOrigin1, u"key1", u"value1", &result2);
+
+ bool open2 = false;
+ IsOpen(&open2);
+ InitStatus status2 = InitStatus::kUnattempted;
+ DBStatus(&status2);
+
+ OperationResult result3 = OperationResult::kSqlError;
+ Set(kOrigin1, u"key2", u"value2", &result3);
+ int length1 = -1;
+ Length(kOrigin1, &length1);
+
+ OperationResult result4 = OperationResult::kSqlError;
+ Set(kOrigin2, u"key1", u"value1", &result4);
+ int length2 = -1;
+ Length(kOrigin2, &length2);
+
+ OperationResult result5 = OperationResult::kSqlError;
+ Set(kOrigin3, u"key1", u"value1", &result5);
+ OperationResult result6 = OperationResult::kSqlError;
+ Set(kOrigin3, u"key2", u"value2", &result6);
+ OperationResult result7 = OperationResult::kSqlError;
+ Set(kOrigin3, u"key3", u"value3", &result7);
+ int length3 = -1;
+ Length(kOrigin3, &length3);
+
+ OperationResult result8 = OperationResult::kSqlError;
+ Set(kOrigin4, u"key1", u"value1", &result8);
+ OperationResult result9 = OperationResult::kSqlError;
+ Set(kOrigin4, u"key2", u"value2", &result9);
+ OperationResult result10 = OperationResult::kSqlError;
+ Set(kOrigin4, u"key3", u"value3", &result10);
+ OperationResult result11 = OperationResult::kSqlError;
+ Set(kOrigin4, u"key4", u"value4", &result11);
+ int length4 = -1;
+ Length(kOrigin4, &length4);
+
+ OperationResult result12 = OperationResult::kSqlError;
+ Set(kOrigin5, u"key1", u"value1", &result12);
+ int length5 = -1;
+ Length(kOrigin5, &length5);
+
+ std::vector<mojom::StorageUsageInfoPtr> infos2;
+ FetchOrigins(&infos2);
+
+ bool success1 = false;
+ OverrideLastUsedTime(kOrigin1, override_time1, &success1);
+
+ // Verify that the only match we get is for `kOrigin1`, whose `last_used_time`
+ // is between the time parameters.
+ OperationResult result13 = OperationResult::kSqlError;
+ PurgeMatchingOrigins(&matcher_utility, matcher_id2, threshold2,
+ base::Time::Max(), &result13,
+ GetParam().perform_storage_cleanup);
+
+ int length6 = -1;
+ Length(kOrigin1, &length6);
+ int length7 = -1;
+ Length(kOrigin2, &length7);
+ int length8 = -1;
+ Length(kOrigin3, &length8);
+ int length9 = -1;
+ Length(kOrigin4, &length9);
+ int length10 = -1;
+ Length(kOrigin5, &length10);
+
+ std::vector<mojom::StorageUsageInfoPtr> infos3;
+ FetchOrigins(&infos3);
+
+ bool success2 = false;
+ OverrideLastUsedTime(kOrigin3, threshold3, &success2);
+ bool success3 = false;
+ OverrideLastUsedTime(kOrigin5, threshold4, &success3);
+
+ // Verify that we still get matches for `kOrigin3`, whose `last_used_time` is
+ // exactly at the `begin` time, as well as for `kOrigin5`, whose
+ // `last_used_time` is exactly at the `end` time.
+ OperationResult result14 = OperationResult::kSqlError;
+ PurgeMatchingOrigins(&matcher_utility, matcher_id3, threshold3, threshold4,
+ &result14, GetParam().perform_storage_cleanup);
+
+ int length11 = -1;
+ Length(kOrigin1, &length11);
+ int length12 = -1;
+ Length(kOrigin2, &length12);
+ int length13 = -1;
+ Length(kOrigin3, &length13);
+ int length14 = -1;
+ Length(kOrigin4, &length14);
+ int length15 = -1;
+ Length(kOrigin5, &length15);
+
+ TrimMemory();
+
+ std::vector<mojom::StorageUsageInfoPtr> infos4;
+ FetchOrigins(&infos4);
+
+ GetResult value1;
+ Get(kOrigin2, u"key1", &value1);
+ GetResult value2;
+ Get(kOrigin4, u"key1", &value2);
+ GetResult value3;
+ Get(kOrigin4, u"key2", &value3);
+ GetResult value4;
+ Get(kOrigin4, u"key3", &value4);
+ GetResult value5;
+ Get(kOrigin4, u"key4", &value5);
+
+ bool success4 = false;
+ Destroy(&success4);
+
+ WaitForOperations();
+ EXPECT_TRUE(is_finished());
+
+ // Database is not yet initialized. `FetchOrigins()` returns an empty vector.
+ EXPECT_TRUE(infos1.empty());
+ EXPECT_EQ(!GetParam().in_memory_only, open1);
+ EXPECT_EQ(InitStatus::kUnattempted, status1);
+
+ // No error from calling `PurgeMatchingOrigins()` on an uninitialized
+ // database.
+ EXPECT_EQ(OperationResult::kSuccess, result1);
+
+ // The call to `Set()` initializes the database.
+ EXPECT_EQ(OperationResult::kSet, result2);
+ EXPECT_TRUE(open2);
+ EXPECT_EQ(InitStatus::kSuccess, status2);
+
+ EXPECT_EQ(OperationResult::kSet, result3);
+ EXPECT_EQ(2, length1);
+
+ EXPECT_EQ(OperationResult::kSet, result4);
+ EXPECT_EQ(1, length2);
+
+ EXPECT_EQ(OperationResult::kSet, result5);
+ EXPECT_EQ(OperationResult::kSet, result6);
+ EXPECT_EQ(OperationResult::kSet, result7);
+ EXPECT_EQ(3, length3);
+
+ EXPECT_EQ(OperationResult::kSet, result8);
+ EXPECT_EQ(OperationResult::kSet, result9);
+ EXPECT_EQ(OperationResult::kSet, result10);
+ EXPECT_EQ(OperationResult::kSet, result11);
+ EXPECT_EQ(4, length4);
+
+ EXPECT_EQ(OperationResult::kSet, result12);
+ EXPECT_EQ(1, length5);
+
+ std::vector<url::Origin> origins;
+ for (const auto& info : infos2)
+ origins.push_back(info->origin);
+ EXPECT_THAT(origins,
+ ElementsAre(kOrigin1, kOrigin2, kOrigin3, kOrigin4, kOrigin5));
+
+ EXPECT_TRUE(success1);
+ EXPECT_EQ(OperationResult::kSuccess, result13);
+
+ // `kOrigin1` is cleared. The other origins are not.
+ EXPECT_EQ(0, length6);
+ EXPECT_EQ(1, length7);
+ EXPECT_EQ(3, length8);
+ EXPECT_EQ(4, length9);
+ EXPECT_EQ(1, length10);
+
+ origins.clear();
+ for (const auto& info : infos3)
+ origins.push_back(info->origin);
+ EXPECT_THAT(origins, ElementsAre(kOrigin2, kOrigin3, kOrigin4, kOrigin5));
+
+ EXPECT_TRUE(success2);
+ EXPECT_TRUE(success3);
+
+ EXPECT_EQ(OperationResult::kSuccess, result14);
+
+ // `kOrigin3` and `kOrigin5` are cleared. The others weren't modified within
+ // the given time period.
+ EXPECT_EQ(0, length11);
+ EXPECT_EQ(1, length12);
+ EXPECT_EQ(0, length13);
+ EXPECT_EQ(4, length14);
+ EXPECT_EQ(0, length15);
+
+ origins.clear();
+ for (const auto& info : infos4)
+ origins.push_back(info->origin);
+ EXPECT_THAT(origins, ElementsAre(kOrigin2, kOrigin4));
+
+ // Database is still intact after trimming memory (and possibly performing
+ // storage cleanup).
+ EXPECT_EQ(value1.data, u"value1");
+ EXPECT_EQ(OperationResult::kSuccess, value1.result);
+ EXPECT_EQ(value2.data, u"value1");
+ EXPECT_EQ(OperationResult::kSuccess, value2.result);
+ EXPECT_EQ(value3.data, u"value2");
+ EXPECT_EQ(OperationResult::kSuccess, value3.result);
+ EXPECT_EQ(value4.data, u"value3");
+ EXPECT_EQ(OperationResult::kSuccess, value4.result);
+ EXPECT_EQ(value5.data, u"value4");
+ EXPECT_EQ(OperationResult::kSuccess, value5.result);
+
+ EXPECT_TRUE(success4);
+}
+
+} // namespace storage
diff --git a/chromium/components/services/storage/shared_storage/shared_storage_database.cc b/chromium/components/services/storage/shared_storage/shared_storage_database.cc
new file mode 100644
index 00000000000..923717a5467
--- /dev/null
+++ b/chromium/components/services/storage/shared_storage/shared_storage_database.cc
@@ -0,0 +1,900 @@
+// Copyright 2021 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 "components/services/storage/shared_storage/shared_storage_database.h"
+
+#include <inttypes.h>
+
+#include <algorithm>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/files/file_util.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/time/default_clock.h"
+#include "base/time/time.h"
+#include "components/services/storage/public/mojom/storage_usage_info.mojom.h"
+#include "components/services/storage/shared_storage/shared_storage_options.h"
+#include "sql/error_delegate_util.h"
+#include "sql/statement.h"
+#include "sql/transaction.h"
+#include "storage/browser/quota/special_storage_policy.h"
+#include "third_party/blink/public/common/features.h"
+#include "url/gurl.h"
+#include "url/origin.h"
+
+namespace storage {
+
+// Because each entry is a key-value pair, and both keys and values are
+// std::u16strings and bounded by `max_string_length_`, the total bytes used per
+// entry is at most 2 * 2 * `max_string_length_`.
+const int kSharedStorageEntryTotalBytesMultiplier = 4;
+
+namespace {
+
+// Version number of the database.
+const int kCurrentVersionNumber = 1;
+
+[[nodiscard]] std::string SerializeOrigin(const url::Origin& origin) {
+ DCHECK(!origin.opaque());
+ DCHECK_NE(url::kFileScheme, origin.scheme());
+ return origin.Serialize();
+}
+
+[[nodiscard]] bool InitSchema(sql::Database& db) {
+ static constexpr char kValuesMappingSql[] =
+ "CREATE TABLE IF NOT EXISTS values_mapping("
+ "context_origin TEXT NOT NULL,"
+ "key TEXT NOT NULL,"
+ "value TEXT,"
+ "PRIMARY KEY(context_origin,key)) WITHOUT ROWID";
+ if (!db.Execute(kValuesMappingSql))
+ return false;
+
+ static constexpr char kPerOriginMappingSql[] =
+ "CREATE TABLE IF NOT EXISTS per_origin_mapping("
+ "context_origin TEXT NOT NULL PRIMARY KEY,"
+ "last_used_time INTEGER NOT NULL,"
+ "length INTEGER NOT NULL) WITHOUT ROWID";
+ if (!db.Execute(kPerOriginMappingSql))
+ return false;
+
+ static constexpr char kLastUsedTimeIndexSql[] =
+ "CREATE INDEX IF NOT EXISTS per_origin_mapping_last_used_time_idx "
+ "ON per_origin_mapping(last_used_time)";
+ if (!db.Execute(kLastUsedTimeIndexSql))
+ return false;
+
+ return true;
+}
+
+} // namespace
+
+SharedStorageDatabase::GetResult::GetResult() = default;
+
+SharedStorageDatabase::GetResult::GetResult(const GetResult&) = default;
+
+SharedStorageDatabase::GetResult::GetResult(GetResult&&) = default;
+
+SharedStorageDatabase::GetResult::~GetResult() = default;
+
+SharedStorageDatabase::GetResult& SharedStorageDatabase::GetResult::operator=(
+ const GetResult&) = default;
+
+SharedStorageDatabase::GetResult& SharedStorageDatabase::GetResult::operator=(
+ GetResult&&) = default;
+
+SharedStorageDatabase::SharedStorageDatabase(
+ base::FilePath db_path,
+ scoped_refptr<storage::SpecialStoragePolicy> special_storage_policy,
+ std::unique_ptr<SharedStorageDatabaseOptions> options)
+ : db_({// Run the database in exclusive mode. Nobody else should be
+ // accessing the database while we're running, and this will give
+ // somewhat improved perf.
+ .exclusive_locking = true,
+ .page_size = options->max_page_size,
+ .cache_size = options->max_cache_size}),
+ db_path_(std::move(db_path)),
+ special_storage_policy_(std::move(special_storage_policy)),
+ max_entries_per_origin_(int64_t{options->max_entries_per_origin}),
+ clock_(base::DefaultClock::GetInstance()) {
+ DCHECK(db_path_.empty() || db_path_.IsAbsolute());
+ DCHECK_GT(max_entries_per_origin_, 0);
+ DCHECK_GT(options->max_init_tries, 0);
+ DCHECK_GT(options->max_string_length, 0);
+ max_string_length_ = static_cast<size_t>(options->max_string_length);
+ max_init_tries_ = static_cast<size_t>(options->max_init_tries);
+ db_file_status_ = db_path_.empty() ? DBFileStatus::kNoPreexistingFile
+ : DBFileStatus::kNotChecked;
+}
+
+SharedStorageDatabase::~SharedStorageDatabase() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
+
+bool SharedStorageDatabase::Destroy() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ if (db_.is_open() && !db_.RazeAndClose())
+ return false;
+
+ // The file already doesn't exist.
+ if (db_path_.empty())
+ return true;
+
+ return base::DeleteFile(db_path_);
+}
+
+void SharedStorageDatabase::TrimMemory() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK_EQ(InitStatus::kSuccess, db_status_);
+ db_.TrimMemory();
+}
+
+SharedStorageDatabase::GetResult SharedStorageDatabase::Get(
+ url::Origin context_origin,
+ std::u16string key) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK_LE(key.size(), max_string_length_);
+ GetResult result;
+
+ if (LazyInit(DBCreationPolicy::kIgnoreIfAbsent) != InitStatus::kSuccess) {
+ // We do not return an error if the database doesn't exist, but only if it
+ // pre-exists on disk and yet fails to initialize.
+ if (db_status_ == InitStatus::kUnattempted)
+ result.result = OperationResult::kSuccess;
+ else
+ result.result = OperationResult::kInitFailure;
+
+ return result;
+ }
+
+ // In theory, there ought to be at most one entry found. But we make no
+ // assumption about the state of the disk. In the rare case that multiple
+ // entries are found, we return only the value from the first entry found.
+ static constexpr char kSelectSql[] =
+ "SELECT value FROM values_mapping "
+ "WHERE context_origin=? AND key=? "
+ "LIMIT 1";
+
+ sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, kSelectSql));
+ std::string origin_str(SerializeOrigin(context_origin));
+ statement.BindString(0, origin_str);
+ statement.BindString16(1, key);
+
+ if (statement.Step())
+ result.data = statement.ColumnString16(0);
+ if (!statement.Succeeded())
+ return result;
+
+ if (UpdateLastUsedTime(origin_str))
+ result.result = OperationResult::kSuccess;
+
+ return result;
+}
+
+SharedStorageDatabase::OperationResult SharedStorageDatabase::Set(
+ url::Origin context_origin,
+ std::u16string key,
+ std::u16string value,
+ SetBehavior behavior) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(!key.empty());
+ DCHECK_LE(key.size(), max_string_length_);
+ DCHECK_LE(value.size(), max_string_length_);
+
+ if (LazyInit(DBCreationPolicy::kCreateIfAbsent) != InitStatus::kSuccess)
+ return OperationResult::kInitFailure;
+
+ sql::Transaction transaction(&db_);
+ if (!transaction.Begin())
+ return OperationResult::kSqlError;
+
+ std::string origin_str(SerializeOrigin(context_origin));
+ if (HasEntryFor(origin_str, key)) {
+ if (behavior == SharedStorageDatabase::SetBehavior::kIgnoreIfPresent) {
+ // If we are in a nested transaction, we need to commit, even though we
+ // haven't made any changes, so that the failure to set in this case
+ // isn't seen as an error (as then the entire stack of transactions
+ // will be rolled back and the next transaction within the parent
+ // transaction will fail to begin).
+ if (db_.transaction_nesting())
+ transaction.Commit();
+ return OperationResult::kIgnored;
+ }
+
+ if (Delete(context_origin, key) != OperationResult::kSuccess)
+ return OperationResult::kSqlError;
+ } else if (!HasCapacity(origin_str)) {
+ return OperationResult::kNoCapacity;
+ }
+
+ if (!InsertIntoValuesMapping(origin_str, key, value))
+ return OperationResult::kSqlError;
+
+ if (!UpdateLength(origin_str, /*delta=*/1))
+ return OperationResult::kSqlError;
+
+ if (!transaction.Commit())
+ return OperationResult::kSqlError;
+
+ return OperationResult::kSet;
+}
+
+SharedStorageDatabase::OperationResult SharedStorageDatabase::Append(
+ url::Origin context_origin,
+ std::u16string key,
+ std::u16string tail_value) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(!key.empty());
+ DCHECK_LE(key.size(), max_string_length_);
+ DCHECK_LE(tail_value.size(), max_string_length_);
+
+ if (LazyInit(DBCreationPolicy::kCreateIfAbsent) != InitStatus::kSuccess)
+ return OperationResult::kInitFailure;
+
+ sql::Transaction transaction(&db_);
+ if (!transaction.Begin())
+ return OperationResult::kSqlError;
+
+ GetResult get_result = Get(context_origin, key);
+ if (get_result.result != OperationResult::kSuccess)
+ return OperationResult::kSqlError;
+
+ std::u16string new_value;
+ std::string origin_str(SerializeOrigin(context_origin));
+
+ if (get_result.data) {
+ new_value = std::move(*get_result.data);
+ new_value.append(tail_value);
+
+ if (new_value.size() > max_string_length_)
+ return OperationResult::kInvalidAppend;
+
+ if (Delete(context_origin, key) != OperationResult::kSuccess)
+ return OperationResult::kSqlError;
+ } else {
+ new_value = std::move(tail_value);
+
+ if (!HasCapacity(origin_str))
+ return OperationResult::kNoCapacity;
+ }
+
+ if (!InsertIntoValuesMapping(origin_str, key, new_value))
+ return OperationResult::kSqlError;
+
+ if (!UpdateLength(origin_str, /*delta=*/1))
+ return OperationResult::kSqlError;
+
+ if (!transaction.Commit())
+ return OperationResult::kSqlError;
+
+ return OperationResult::kSet;
+}
+
+SharedStorageDatabase::OperationResult SharedStorageDatabase::Delete(
+ url::Origin context_origin,
+ std::u16string key) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK_LE(key.size(), max_string_length_);
+
+ if (LazyInit(DBCreationPolicy::kIgnoreIfAbsent) != InitStatus::kSuccess) {
+ // We do not return an error if the database doesn't exist, but only if it
+ // pre-exists on disk and yet fails to initialize.
+ if (db_status_ == InitStatus::kUnattempted)
+ return OperationResult::kSuccess;
+ else
+ return OperationResult::kInitFailure;
+ }
+
+ std::string origin_str(SerializeOrigin(context_origin));
+ if (!HasEntryFor(origin_str, key))
+ return OperationResult::kSuccess;
+
+ sql::Transaction transaction(&db_);
+ if (!transaction.Begin())
+ return OperationResult::kSqlError;
+
+ static constexpr char kDeleteSql[] =
+ "DELETE FROM values_mapping "
+ "WHERE context_origin=? AND key=?";
+
+ sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, kDeleteSql));
+ statement.BindString(0, origin_str);
+ statement.BindString16(1, key);
+
+ if (!statement.Run())
+ return OperationResult::kSqlError;
+
+ if (!UpdateLength(origin_str, /*delta=*/-1))
+ return OperationResult::kSqlError;
+
+ if (!transaction.Commit())
+ return OperationResult::kSqlError;
+ return OperationResult::kSuccess;
+}
+
+SharedStorageDatabase::OperationResult SharedStorageDatabase::Clear(
+ url::Origin context_origin) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ if (LazyInit(DBCreationPolicy::kIgnoreIfAbsent) != InitStatus::kSuccess) {
+ // We do not return an error if the database doesn't exist, but only if it
+ // pre-exists on disk and yet fails to initialize.
+ if (db_status_ == InitStatus::kUnattempted)
+ return OperationResult::kSuccess;
+ else
+ return OperationResult::kInitFailure;
+ }
+
+ if (!Purge(SerializeOrigin(context_origin)))
+ return OperationResult::kSqlError;
+ return OperationResult::kSuccess;
+}
+
+int64_t SharedStorageDatabase::Length(url::Origin context_origin) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ if (LazyInit(DBCreationPolicy::kIgnoreIfAbsent) != InitStatus::kSuccess) {
+ // We do not return -1 (to signifiy an error) if the database doesn't exist,
+ // but only if it pre-exists on disk and yet fails to initialize.
+ if (db_status_ == InitStatus::kUnattempted)
+ return 0L;
+ else
+ return -1;
+ }
+
+ std::string origin_str(SerializeOrigin(context_origin));
+ int64_t length = NumEntries(origin_str);
+ if (!length)
+ return 0L;
+
+ if (!UpdateLastUsedTime(origin_str))
+ return -1;
+
+ return length;
+}
+
+SharedStorageDatabase::GetResult SharedStorageDatabase::Key(
+ url::Origin context_origin,
+ uint64_t index) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ GetResult result;
+
+ if (LazyInit(DBCreationPolicy::kIgnoreIfAbsent) != InitStatus::kSuccess) {
+ // We do not return an error if the database doesn't exist, but only if it
+ // pre-exists on disk and yet fails to initialize.
+ if (db_status_ == InitStatus::kUnattempted)
+ result.result = OperationResult::kSuccess;
+ else
+ result.result = OperationResult::kInitFailure;
+ return result;
+ }
+
+ static constexpr char kSelectSql[] =
+ "SELECT key FROM values_mapping "
+ "WHERE context_origin=? "
+ "ORDER BY key";
+
+ sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, kSelectSql));
+ std::string origin_str(SerializeOrigin(context_origin));
+ statement.BindString(0, origin_str);
+
+ uint64_t current_index = 0UL;
+
+ while (statement.Step()) {
+ if (!statement.Succeeded())
+ return result;
+ if (current_index == index) {
+ result.data = statement.ColumnString16(0);
+ break;
+ }
+
+ current_index++;
+ }
+
+ if (UpdateLastUsedTime(origin_str))
+ result.result = OperationResult::kSuccess;
+
+ return result;
+}
+
+SharedStorageDatabase::OperationResult
+SharedStorageDatabase::PurgeMatchingOrigins(
+ OriginMatcherFunction origin_matcher,
+ base::Time begin,
+ base::Time end,
+ bool perform_storage_cleanup) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK_LE(begin, end);
+
+ if (LazyInit(DBCreationPolicy::kIgnoreIfAbsent) != InitStatus::kSuccess) {
+ // We do not return an error if the database doesn't exist, but only if it
+ // pre-exists on disk and yet fails to initialize.
+ if (db_status_ == InitStatus::kUnattempted)
+ return OperationResult::kSuccess;
+ else
+ return OperationResult::kInitFailure;
+ }
+
+ static constexpr char kSelectSql[] =
+ "SELECT context_origin FROM per_origin_mapping "
+ "WHERE last_used_time BETWEEN ? AND ? "
+ "ORDER BY last_used_time";
+ sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, kSelectSql));
+ statement.BindTime(0, begin);
+ statement.BindTime(1, end);
+
+ std::vector<std::string> origins;
+
+ while (statement.Step())
+ origins.push_back(statement.ColumnString(0));
+
+ if (!statement.Succeeded())
+ return OperationResult::kSqlError;
+
+ if (origins.empty())
+ return OperationResult::kSuccess;
+
+ sql::Transaction transaction(&db_);
+ if (!transaction.Begin())
+ return OperationResult::kSqlError;
+
+ for (const auto& origin : origins) {
+ if (origin_matcher && !origin_matcher.Run(url::Origin::Create(GURL(origin)),
+ special_storage_policy_.get())) {
+ continue;
+ }
+
+ if (!Purge(origin))
+ return OperationResult::kSqlError;
+ }
+
+ if (!transaction.Commit())
+ return OperationResult::kSqlError;
+
+ if (perform_storage_cleanup && !Vacuum())
+ return OperationResult::kSqlError;
+
+ return OperationResult::kSuccess;
+}
+
+SharedStorageDatabase::OperationResult SharedStorageDatabase::PurgeStaleOrigins(
+ base::TimeDelta window_to_be_deemed_active) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK_GT(window_to_be_deemed_active, base::TimeDelta());
+
+ if (LazyInit(DBCreationPolicy::kIgnoreIfAbsent) != InitStatus::kSuccess) {
+ // We do not return an error if the database doesn't exist, but only if it
+ // pre-exists on disk and yet fails to initialize.
+ if (db_status_ == InitStatus::kUnattempted)
+ return OperationResult::kSuccess;
+ else
+ return OperationResult::kInitFailure;
+ }
+
+ base::Time threshold = clock_->Now() - window_to_be_deemed_active;
+
+ static constexpr char kSelectSql[] =
+ "SELECT context_origin FROM per_origin_mapping "
+ "WHERE last_used_time<? "
+ "ORDER BY last_used_time";
+ sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, kSelectSql));
+ statement.BindTime(0, threshold);
+
+ std::vector<std::string> stale_origins;
+
+ while (statement.Step())
+ stale_origins.push_back(statement.ColumnString(0));
+
+ if (!statement.Succeeded())
+ return OperationResult::kSqlError;
+
+ if (stale_origins.empty())
+ return OperationResult::kSuccess;
+
+ sql::Transaction transaction(&db_);
+ if (!transaction.Begin())
+ return OperationResult::kSqlError;
+
+ for (const auto& origin : stale_origins) {
+ if (!Purge(origin))
+ return OperationResult::kSqlError;
+ }
+
+ if (!transaction.Commit())
+ return OperationResult::kSqlError;
+ return OperationResult::kSuccess;
+}
+
+std::vector<mojom::StorageUsageInfoPtr> SharedStorageDatabase::FetchOrigins() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ if (LazyInit(DBCreationPolicy::kIgnoreIfAbsent) != InitStatus::kSuccess)
+ return {};
+
+ static constexpr char kSelectSql[] =
+ "SELECT context_origin,last_used_time,length FROM per_origin_mapping "
+ "ORDER BY context_origin";
+
+ sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, kSelectSql));
+ std::vector<mojom::StorageUsageInfoPtr> fetched_origin_infos;
+
+ while (statement.Step()) {
+ fetched_origin_infos.emplace_back(mojom::StorageUsageInfo::New(
+ url::Origin::Create(GURL(statement.ColumnString(0))),
+ statement.ColumnInt64(2) * kSharedStorageEntryTotalBytesMultiplier *
+ max_string_length_,
+ statement.ColumnTime(1)));
+ }
+
+ if (!statement.Succeeded())
+ return {};
+
+ return fetched_origin_infos;
+}
+
+bool SharedStorageDatabase::IsOpenForTesting() const {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ return db_.is_open();
+}
+
+SharedStorageDatabase::InitStatus SharedStorageDatabase::DBStatusForTesting()
+ const {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ return db_status_;
+}
+
+bool SharedStorageDatabase::OverrideLastUsedTimeForTesting(
+ url::Origin context_origin,
+ base::Time override_last_used_time) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ if (LazyInit(DBCreationPolicy::kIgnoreIfAbsent) != InitStatus::kSuccess)
+ return false;
+
+ return SetLastUsedTime(SerializeOrigin(context_origin),
+ override_last_used_time);
+}
+
+void SharedStorageDatabase::OverrideClockForTesting(base::Clock* clock) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(clock);
+ clock_ = clock;
+}
+
+bool SharedStorageDatabase::OverrideSpecialStoragePolicyForTesting(
+ scoped_refptr<storage::SpecialStoragePolicy> special_storage_policy) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ special_storage_policy_ = std::move(special_storage_policy);
+ return true;
+}
+
+SharedStorageDatabase::InitStatus SharedStorageDatabase::LazyInit(
+ DBCreationPolicy policy) {
+ // Early return in case of previous failure, to prevent an unbounded
+ // number of re-attempts.
+ if (db_status_ != InitStatus::kUnattempted)
+ return db_status_;
+
+ if (policy == DBCreationPolicy::kIgnoreIfAbsent && !DBExists())
+ return InitStatus::kUnattempted;
+
+ for (size_t i = 0; i < max_init_tries_; ++i) {
+ db_status_ = InitImpl();
+ if (db_status_ == InitStatus::kSuccess)
+ return db_status_;
+
+ meta_table_.Reset();
+ db_.Close();
+ }
+
+ return db_status_;
+}
+
+bool SharedStorageDatabase::DBExists() {
+ DCHECK_EQ(InitStatus::kUnattempted, db_status_);
+
+ if (db_file_status_ == DBFileStatus::kNoPreexistingFile)
+ return false;
+
+ // The in-memory case is included in `DBFileStatus::kNoPreexistingFile`.
+ DCHECK(!db_path_.empty());
+
+ // We do not expect `DBExists()` to be called in the case where
+ // `db_file_status_ == DBFileStatus::kPreexistingFile`, as then
+ // `db_status_ != InitStatus::kUnattempted`, which would force an early return
+ // in `LazyInit()`.
+ DCHECK_EQ(DBFileStatus::kNotChecked, db_file_status_);
+
+ // The histogram tag must be set before opening.
+ db_.set_histogram_tag("SharedStorage");
+
+ if (!db_.Open(db_path_)) {
+ db_file_status_ = DBFileStatus::kNoPreexistingFile;
+ return false;
+ }
+
+ static const char kSelectSql[] =
+ "SELECT COUNT(*) FROM sqlite_schema WHERE type=?";
+ sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, kSelectSql));
+ statement.BindCString(0, "table");
+
+ if (!statement.Step() || statement.ColumnInt(0) == 0) {
+ db_file_status_ = DBFileStatus::kNoPreexistingFile;
+ return false;
+ }
+
+ db_file_status_ = DBFileStatus::kPreexistingFile;
+ return true;
+}
+
+bool SharedStorageDatabase::OpenDatabase() {
+ // If the database is open, the histogram tag will have already been set in
+ // `DBExists()`, since it must be set before opening.
+ if (!db_.is_open())
+ db_.set_histogram_tag("SharedStorage");
+
+ // base::Unretained is safe here because this SharedStorageDatabase owns
+ // the sql::Database instance that stores and uses the callback. So,
+ // `this` is guaranteed to outlive the callback.
+ db_.set_error_callback(base::BindRepeating(
+ &SharedStorageDatabase::DatabaseErrorCallback, base::Unretained(this)));
+
+ if (!db_path_.empty()) {
+ if (!db_.is_open() && !db_.Open(db_path_))
+ return false;
+
+ db_.Preload();
+ } else {
+ if (!db_.OpenInMemory())
+ return false;
+ }
+
+ return true;
+}
+
+void SharedStorageDatabase::DatabaseErrorCallback(int extended_error,
+ sql::Statement* stmt) {
+ base::UmaHistogramSparse("Storage.SharedStorage.Database.Error",
+ extended_error);
+
+ if (sql::IsErrorCatastrophic(extended_error)) {
+ bool success = Destroy();
+ UMA_HISTOGRAM_BOOLEAN("Storage.SharedStorage.Database.Destruction",
+ success);
+ if (!success) {
+ DLOG(FATAL) << "Database destruction failed after catastrophic error:\n"
+ << db_.GetErrorMessage();
+ }
+ }
+
+ // The default handling is to assert on debug and to ignore on release.
+ if (!sql::Database::IsExpectedSqliteError(extended_error))
+ DLOG(FATAL) << db_.GetErrorMessage();
+}
+
+SharedStorageDatabase::InitStatus SharedStorageDatabase::InitImpl() {
+ if (!OpenDatabase())
+ return InitStatus::kError;
+
+ // Database should now be open.
+ DCHECK(db_.is_open());
+
+ // Scope initialization in a transaction so we can't be partially initialized.
+ sql::Transaction transaction(&db_);
+ if (!transaction.Begin()) {
+ LOG(WARNING) << "Shared storage database begin initialization failed.";
+ db_.RazeAndClose();
+ return InitStatus::kError;
+ }
+
+ // Create the tables.
+ if (!meta_table_.Init(&db_, kCurrentVersionNumber, kCurrentVersionNumber) ||
+ !InitSchema(db_)) {
+ return InitStatus::kError;
+ }
+
+ if (meta_table_.GetCompatibleVersionNumber() > kCurrentVersionNumber) {
+ LOG(WARNING) << "Shared storage database is too new.";
+ return InitStatus::kTooNew;
+ }
+
+ int cur_version = meta_table_.GetVersionNumber();
+
+ if (cur_version < kCurrentVersionNumber) {
+ LOG(WARNING) << "Shared storage database is too old to be compatible.";
+ db_.RazeAndClose();
+ return InitStatus::kTooOld;
+ }
+
+ // The initialization is complete.
+ if (!transaction.Commit()) {
+ LOG(WARNING) << "Shared storage database initialization commit failed.";
+ db_.RazeAndClose();
+ return InitStatus::kError;
+ }
+
+ return InitStatus::kSuccess;
+}
+
+bool SharedStorageDatabase::Vacuum() {
+ DCHECK_EQ(InitStatus::kSuccess, db_status_);
+ DCHECK_EQ(0, db_.transaction_nesting())
+ << "Can not have a transaction when vacuuming.";
+ return db_.Execute("VACUUM");
+}
+
+bool SharedStorageDatabase::Purge(const std::string& context_origin) {
+ sql::Transaction transaction(&db_);
+ if (!transaction.Begin())
+ return false;
+
+ static constexpr char kDeleteSql[] =
+ "DELETE FROM values_mapping "
+ "WHERE context_origin=?";
+
+ sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, kDeleteSql));
+ statement.BindString(0, context_origin);
+
+ if (!statement.Run())
+ return false;
+
+ if (!DeleteFromPerOriginMapping(context_origin))
+ return false;
+
+ return transaction.Commit();
+}
+
+int64_t SharedStorageDatabase::NumEntries(const std::string& context_origin) {
+ // In theory, there ought to be at most one entry found. But we make no
+ // assumption about the state of the disk. In the rare case that multiple
+ // entries are found, we return only the `length` from the first entry found.
+ static constexpr char kSelectSql[] =
+ "SELECT length FROM per_origin_mapping "
+ "WHERE context_origin=? "
+ "LIMIT 1";
+
+ sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, kSelectSql));
+ statement.BindString(0, context_origin);
+
+ int64_t length = 0;
+ if (statement.Step())
+ length = statement.ColumnInt64(0);
+
+ return length;
+}
+
+bool SharedStorageDatabase::HasEntryFor(const std::string& context_origin,
+ const std::u16string& key) {
+ static constexpr char kSelectSql[] =
+ "SELECT 1 FROM values_mapping "
+ "WHERE context_origin=? AND key=? "
+ "LIMIT 1";
+
+ sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, kSelectSql));
+ statement.BindString(0, context_origin);
+ statement.BindString16(1, key);
+
+ return statement.Step();
+}
+
+bool SharedStorageDatabase::SetLastUsedTime(const std::string& context_origin,
+ base::Time new_last_used_time) {
+ int64_t length = NumEntries(context_origin);
+
+ // If length is zero, no need to delete, and don't insert the origin into the
+ // `per_origin_mapping`.
+ if (!length)
+ return true;
+
+ sql::Transaction transaction(&db_);
+ if (!transaction.Begin())
+ return false;
+
+ if (!DeleteFromPerOriginMapping(context_origin))
+ return false;
+
+ if (!InsertIntoPerOriginMapping(context_origin, new_last_used_time, length))
+ return false;
+
+ return transaction.Commit();
+}
+
+bool SharedStorageDatabase::UpdateLastUsedTime(
+ const std::string& context_origin) {
+ return SetLastUsedTime(context_origin, clock_->Now());
+}
+
+bool SharedStorageDatabase::UpdateLength(const std::string& context_origin,
+ int64_t delta,
+ bool should_update_time) {
+ // In theory, there ought to be at most one entry found. But we make no
+ // assumption about the state of the disk. In the rare case that multiple
+ // entries are found, we retrieve only the `length` (and possibly the `time`)
+ // from the first entry found.
+ static constexpr char kSelectSql[] =
+ "SELECT length,last_used_time FROM per_origin_mapping "
+ "WHERE context_origin=? "
+ "LIMIT 1";
+
+ sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, kSelectSql));
+ statement.BindString(0, context_origin);
+ int64_t length = 0;
+ base::Time time = clock_->Now();
+
+ if (statement.Step()) {
+ length = statement.ColumnInt64(0);
+ if (!should_update_time)
+ time = statement.ColumnTime(1);
+ }
+
+ sql::Transaction transaction(&db_);
+ if (!transaction.Begin())
+ return false;
+
+ if (!DeleteFromPerOriginMapping(context_origin))
+ return false;
+
+ // If the new length is zero, then don't re-insert the origin into the
+ // `per_origin_mapping`.
+ if (length + delta == 0L)
+ return transaction.Commit();
+
+ if (!InsertIntoPerOriginMapping(context_origin, time, length + delta))
+ return false;
+
+ return transaction.Commit();
+}
+
+bool SharedStorageDatabase::InsertIntoValuesMapping(
+ const std::string& context_origin,
+ const std::u16string& key,
+ const std::u16string& value) {
+ static constexpr char kInsertSql[] =
+ "INSERT INTO values_mapping(context_origin,key,value)"
+ "VALUES(?,?,?)";
+
+ sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, kInsertSql));
+ statement.BindString(0, context_origin);
+ statement.BindString16(1, key);
+ statement.BindString16(2, value);
+
+ return statement.Run();
+}
+
+bool SharedStorageDatabase::DeleteFromPerOriginMapping(
+ const std::string& context_origin) {
+ static constexpr char kDeleteSql[] =
+ "DELETE FROM per_origin_mapping "
+ "WHERE context_origin=?";
+
+ sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, kDeleteSql));
+ statement.BindString(0, context_origin);
+
+ return statement.Run();
+}
+
+bool SharedStorageDatabase::InsertIntoPerOriginMapping(
+ const std::string& context_origin,
+ base::Time last_used_time,
+ uint64_t length) {
+ static constexpr char kInsertSql[] =
+ "INSERT INTO per_origin_mapping(context_origin,last_used_time,length)"
+ "VALUES(?,?,?)";
+
+ sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, kInsertSql));
+ statement.BindString(0, context_origin);
+ statement.BindTime(1, last_used_time);
+ statement.BindInt64(2, static_cast<int64_t>(length));
+
+ return statement.Run();
+}
+
+bool SharedStorageDatabase::HasCapacity(const std::string& context_origin) {
+ return NumEntries(context_origin) < max_entries_per_origin_;
+}
+
+} // namespace storage
diff --git a/chromium/components/services/storage/shared_storage/shared_storage_database.h b/chromium/components/services/storage/shared_storage/shared_storage_database.h
new file mode 100644
index 00000000000..413396a9efa
--- /dev/null
+++ b/chromium/components/services/storage/shared_storage/shared_storage_database.h
@@ -0,0 +1,388 @@
+// Copyright 2021 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.
+
+#ifndef COMPONENTS_SERVICES_STORAGE_SHARED_STORAGE_SHARED_STORAGE_DATABASE_H_
+#define COMPONENTS_SERVICES_STORAGE_SHARED_STORAGE_SHARED_STORAGE_DATABASE_H_
+
+#include <inttypes.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/memory/raw_ptr.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/sequence_checker.h"
+#include "base/thread_annotations.h"
+#include "base/threading/sequence_bound.h"
+#include "base/time/clock.h"
+#include "components/services/storage/public/mojom/storage_usage_info.mojom-forward.h"
+#include "sql/database.h"
+#include "sql/meta_table.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace base {
+class FilePath;
+class Time;
+class TimeDelta;
+} // namespace base
+
+namespace sql {
+class Statement;
+}
+
+namespace url {
+class Origin;
+} // namespace url
+
+namespace storage {
+struct SharedStorageDatabaseOptions;
+class SpecialStoragePolicy;
+
+// Multiplier for determining the padded total size in bytes that an origin
+// is using.
+extern const int kSharedStorageEntryTotalBytesMultiplier;
+
+// Wraps its own `sql::Database` instance on behalf of the Shared Storage
+// backend implementation. This object is not sequence-safe and must be
+// instantiated on a sequence which allows use of blocking file operations.
+class SharedStorageDatabase {
+ public:
+ // A callback type to check if a given origin matches a storage policy.
+ // Can be passed empty/null where used, which means the origin will always
+ // match.
+ using OriginMatcherFunction =
+ base::RepeatingCallback<bool(const url::Origin&, SpecialStoragePolicy*)>;
+
+ enum class InitStatus {
+ kUnattempted =
+ 0, // Status if `LazyInit()` has not yet been called or if `LazyInit()`
+ // has early returned due to `DBCreationPolicy::kIgnoreIfAbsent`.
+ kSuccess = 1, // Status if `LazyInit()` was successful.
+ kError = 2, // Status if `LazyInit()` failed and a more specific error
+ // wasn't diagnosed.
+ kTooNew = 3, // Status if `LazyInit()` failed due to a compatible version
+ // number being too high.
+ kTooOld = 4, // Status if `LazyInit()` failed due to a version number being
+ // too low.
+ };
+
+ enum class DBFileStatus {
+ kNotChecked = 0, // Status if DB is file-backed and there hasn't been an
+ // attempt to open the SQL database for the given FilePath
+ // to see if it exists and contains data.
+ kNoPreexistingFile =
+ 1, // Status if the DB is in-memory or if the DB is file-backed but the
+ // attempt to open it was unsuccessful or any pre-existing file
+ // contained no data.
+ kPreexistingFile =
+ 2, // Status if there was a pre-existing file containing at least one
+ // table that we were able to successfully open.
+ };
+
+ enum class SetBehavior {
+ kDefault = 0, // Sets entry regardless of whether one previously exists.
+ kIgnoreIfPresent = 1, // Does not set an entry if one previously exists.
+ };
+
+ enum class OperationResult {
+ kSuccess = 0, // Result if a non-setting operation is successful.
+ kSet = 1, // Result if value is set.
+ kIgnored = 2, // Result if value was present and ignored; no error.
+ kSqlError = 3, // Result if there is a SQL database error.
+ kInitFailure = 4, // Result if database initialization failed and a
+ // database is required.
+ kNoCapacity = 5, // Result if there was insufficient capacity for the
+ // requesting origin.
+ kInvalidAppend = 6, // Result if the length of the value after appending
+ // would exceed the maximum allowed length.
+ };
+
+ // Bundles a retrieved string from the database along with a field indicating
+ // whether the transaction was free of SQL errors.
+ struct GetResult {
+ absl::optional<std::u16string> data;
+ OperationResult result = OperationResult::kSqlError;
+ GetResult();
+ GetResult(const GetResult&);
+ GetResult(GetResult&&);
+ ~GetResult();
+ GetResult& operator=(const GetResult&);
+ GetResult& operator=(GetResult&&);
+ };
+
+ // When `db_path` is empty, the database will be opened in memory only.
+ SharedStorageDatabase(
+ base::FilePath db_path,
+ scoped_refptr<storage::SpecialStoragePolicy> special_storage_policy,
+ std::unique_ptr<SharedStorageDatabaseOptions> options);
+
+ SharedStorageDatabase(const SharedStorageDatabase&) = delete;
+ SharedStorageDatabase(const SharedStorageDatabase&&) = delete;
+
+ ~SharedStorageDatabase();
+
+ SharedStorageDatabase& operator=(const SharedStorageDatabase&) = delete;
+ SharedStorageDatabase& operator=(const SharedStorageDatabase&&) = delete;
+
+ // Deletes the database and returns whether the operation was successful.
+ //
+ // It is OK to call `Destroy()` regardless of whether `Init()` was successful.
+ [[nodiscard]] bool Destroy();
+
+ // Returns a pointer to the database containing the actual data.
+ [[nodiscard]] sql::Database* db() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ return &db_;
+ }
+
+ // Releases all non-essential memory associated with this database connection.
+ void TrimMemory();
+
+ // Retrieves the `entry` for `context_origin` and `key`. Returns a
+ // struct containing a `value` string if one is found, `absl::nullopt`
+ // otherwise, with a bool `success` indicating whether the transaction was
+ // free of errors.
+ //
+ // Note that `key` is assumed to be of length at most
+ // `max_string_length_`, with the burden on the caller to handle errors for
+ // strings that exceed this length.
+ [[nodiscard]] GetResult Get(url::Origin context_origin, std::u16string key);
+
+ // Sets an entry for `context_origin` and `key` to have `value`.
+ // If `behavior` is `kIgnoreIfPresent` and an entry already exists for
+ // `context_origin` and `key`, then the table is not modified.
+ // Returns an enum indicating whether or not a new entry is added, the request
+ // is ignored, or if there is an error.
+ //
+ // Note that `key` and `value` assumed to be each of length at
+ // most `max_string_length_`, with the burden on the caller to handle errors
+ // for strings that exceed this length. Moreover, if `Length(context_origin)`
+ // equals `max_entries_per_origin_`, `Set()` will return a value of
+ // `OperationResult::kNoCapacity` and the table will not be modified.
+ [[nodiscard]] OperationResult Set(
+ url::Origin context_origin,
+ std::u16string key,
+ std::u16string value,
+ SetBehavior behavior = SetBehavior::kDefault);
+
+ // Appends `tail_value` to the end of the current `value`
+ // for `context_origin` and `key`, if `key` exists. If
+ // `key` does not exist, creates an entry for `key` with value
+ // `tail_value`. Returns an enum indicating whether or not an entry is
+ // added/modified or if there is an error.
+ //
+ // Note that `key` and `value` are assumed to be each of length
+ // at most `max_string_length_`, with the burden on the caller to handle
+ // errors for strings that exceed this length. Moreover, if the length of the
+ // string obtained by concatening the current `value` (if one exists)
+ // and `tail_value` exceeds `max_string_length_`, or if
+ // `Length(context_origin)` equals `max_entries_per_origin_`, `Append()` will
+ // return a value of `OperationResult::kNoCapacity` and the table will not be
+ // modified.
+ [[nodiscard]] OperationResult Append(url::Origin context_origin,
+ std::u16string key,
+ std::u16string tail_value);
+
+ // Deletes the entry for `context_origin` and `key`. Returns
+ // whether the deletion is successful.
+ //
+ // Note that `key` is assumed to be of length at most
+ // `max_string_length_`, with the burden on the caller to handle errors for
+ // strings that exceed this length.
+ [[nodiscard]] OperationResult Delete(url::Origin context_origin,
+ std::u16string key);
+
+ // Clears all entries for `context_origin`. Returns whether the operation is
+ // successful.
+ [[nodiscard]] OperationResult Clear(url::Origin context_origin);
+
+ // Returns the number of entries for `context_origin` in the database, or -1
+ // on error. Note that this call will update the origin's `last_used_time`.
+ // TODO(crbug.com/1277662): Consider renaming to something more descriptive.
+ [[nodiscard]] int64_t Length(url::Origin context_origin);
+
+ // If a list of all the keys for `context_origin` are taken in lexicographic
+ // order, retrieves the `key` at `index` of the list and sets it as
+ // data in the returned struct; otherwise the struct holds `absl::nullopt` if
+ // no such `key` exists. The `GetResult` struct also has a bool
+ // `success` indicating whether the transaction was free of errors.
+ //
+ // TODO(crbug.com/1247861): Replace with an async iterator.
+ [[nodiscard]] GetResult Key(url::Origin context_origin, uint64_t index);
+
+ // Clears all origins that match `origin_matcher` run on the owning
+ // StoragePartition's `SpecialStoragePolicy` and have `last_used_time` between
+ // the times `begin` and `end`. If `perform_storage_cleanup` is true, vacuums
+ // the database afterwards. Returns whether the transaction was successful.
+ [[nodiscard]] OperationResult PurgeMatchingOrigins(
+ OriginMatcherFunction origin_matcher,
+ base::Time begin,
+ base::Time end,
+ bool perform_storage_cleanup = false);
+
+ // Clear all entries for all origins whose `last_read_time` falls before
+ // `base::Time::Now() - window_to_be_deemed_active`. Returns whether the
+ // transaction was successful.
+ [[nodiscard]] OperationResult PurgeStaleOrigins(
+ base::TimeDelta window_to_be_deemed_active);
+
+ // Fetches a vector of `mojom::StorageUsageInfoPtr`, with one
+ // `mojom::StorageUsageInfoPtr` for each origin currently using shared storage
+ // in this profile.
+ [[nodiscard]] std::vector<mojom::StorageUsageInfoPtr> FetchOrigins();
+
+ // Returns whether the SQLite database is open.
+ [[nodiscard]] bool IsOpenForTesting() const;
+
+ // Returns the `db_status_` for tests.
+ [[nodiscard]] InitStatus DBStatusForTesting() const;
+
+ // Changes `last_used_time` to `override_last_used_time` for `context_origin`.
+ [[nodiscard]] bool OverrideLastUsedTimeForTesting(
+ url::Origin context_origin,
+ base::Time override_last_used_time);
+
+ // Overrides the clock used to check the time.
+ void OverrideClockForTesting(base::Clock* clock);
+
+ // Overrides the `SpecialStoragePolicy` for tests. Returns true.
+ [[nodiscard]] bool OverrideSpecialStoragePolicyForTesting(
+ scoped_refptr<storage::SpecialStoragePolicy> special_storage_policy);
+
+ private:
+ // Policy to tell `LazyInit()` whether or not to create a new database if a
+ // pre-existing on-disk database is not found.
+ enum class DBCreationPolicy {
+ kIgnoreIfAbsent = 0,
+ kCreateIfAbsent = 1,
+ };
+
+ // Called at the start of each public operation, and initializes the database
+ // if it isn't already initialized (unless there is no pre-existing on-disk
+ // database to initialize and `policy` is
+ // `DBCreationPolicy::kIgnoreIfAbsent`).
+ [[nodiscard]] InitStatus LazyInit(DBCreationPolicy policy)
+ VALID_CONTEXT_REQUIRED(sequence_checker_);
+
+ // Determines whether or not an uninitialized DB already exists on disk.
+ [[nodiscard]] bool DBExists() VALID_CONTEXT_REQUIRED(sequence_checker_);
+
+ // If `db_path_` is empty, opens a temporary database in memory; otherwise
+ // opens a persistent database with the absolute path `db_path`, creating the
+ // file if it does not yet exist. Returns whether opening was successful.
+ [[nodiscard]] bool OpenDatabase() VALID_CONTEXT_REQUIRED(sequence_checker_);
+
+ // Callback for database errors. Schedules a call to Destroy() if the
+ // error is catastrophic.
+ void DatabaseErrorCallback(int extended_error, sql::Statement* stmt)
+ VALID_CONTEXT_REQUIRED(sequence_checker_);
+
+ // Helper function to implement internals of `Init()`. This allows
+ // Init() to retry in case of failure, since some failures run
+ // recovery code.
+ [[nodiscard]] InitStatus InitImpl() VALID_CONTEXT_REQUIRED(sequence_checker_);
+
+ // Vacuums the database. This will cause sqlite to defragment and collect
+ // unused space in the file. It can be VERY SLOW. Returns whether the
+ // operation was successful.
+ [[nodiscard]] bool Vacuum() VALID_CONTEXT_REQUIRED(sequence_checker_);
+
+ // Clears all entries for `context_origin`. Returns whether deletion is
+ // successful. Not named `Clear()` to distinguish it from the public method
+ // called via `SequenceBound::AsyncCall()`.
+ [[nodiscard]] bool Purge(const std::string& context_origin)
+ VALID_CONTEXT_REQUIRED(sequence_checker_);
+
+ // Returns the number of entries for `context_origin`, i.e. the `length`.
+ // Not named `Length()` to distinguish it from the public method called via
+ // `SequenceBound::AsyncCall()`.
+ [[nodiscard]] int64_t NumEntries(const std::string& context_origin)
+ VALID_CONTEXT_REQUIRED(sequence_checker_);
+
+ // Returns whether an entry exists for `context_origin` and `key`.
+ [[nodiscard]] bool HasEntryFor(const std::string& context_origin,
+ const std::u16string& key)
+ VALID_CONTEXT_REQUIRED(sequence_checker_);
+
+ // Sets `last_used_time` to `new_last_used_time` for `context_origin`.
+ [[nodiscard]] bool SetLastUsedTime(const std::string& context_origin,
+ base::Time new_last_used_time)
+ VALID_CONTEXT_REQUIRED(sequence_checker_);
+
+ // Updates `last_used_time` to `base::Time::Now()` for `context_origin`.
+ [[nodiscard]] bool UpdateLastUsedTime(const std::string& context_origin)
+ VALID_CONTEXT_REQUIRED(sequence_checker_);
+
+ // Updates `length` by `delta` for `context_origin`. If `should_update_time`
+ // is true, also updates `last_used_time` to `base::Time::Now()`.
+ [[nodiscard]] bool UpdateLength(const std::string& context_origin,
+ int64_t delta,
+ bool should_update_time = true)
+ VALID_CONTEXT_REQUIRED(sequence_checker_);
+
+ // Inserts a triple for `(context_origin,key,value)` into
+ // `values_mapping`.
+ [[nodiscard]] bool InsertIntoValuesMapping(const std::string& context_origin,
+ const std::u16string& key,
+ const std::u16string& value)
+ VALID_CONTEXT_REQUIRED(sequence_checker_);
+
+ // Deletes the row for `context_origin` from `per_origin_mapping`.
+ [[nodiscard]] bool DeleteFromPerOriginMapping(
+ const std::string& context_origin)
+ VALID_CONTEXT_REQUIRED(sequence_checker_);
+
+ // Inserts the triple for `(context_origin, last_used_time, length)` into
+ // `per_origin_mapping`.
+ [[nodiscard]] bool InsertIntoPerOriginMapping(
+ const std::string& context_origin,
+ base::Time last_used_time,
+ uint64_t length) VALID_CONTEXT_REQUIRED(sequence_checker_);
+
+ // Returns whether the `length` for `context_origin` is less than
+ // `max_entries_per_origin_`.
+ [[nodiscard]] bool HasCapacity(const std::string& context_origin)
+ VALID_CONTEXT_REQUIRED(sequence_checker_);
+
+ // The database containing the actual data.
+ sql::Database db_ GUARDED_BY_CONTEXT(sequence_checker_);
+
+ // Contains the version information.
+ sql::MetaTable meta_table_ GUARDED_BY_CONTEXT(sequence_checker_);
+
+ // Initialization status of `db_`.
+ GUARDED_BY_CONTEXT(sequence_checker_)
+ InitStatus db_status_ = InitStatus::kUnattempted;
+
+ // Only set to true if `DBExists()
+ DBFileStatus db_file_status_ GUARDED_BY_CONTEXT(sequence_checker_);
+
+ // The path to the database, if file-backed.
+ base::FilePath db_path_ GUARDED_BY_CONTEXT(sequence_checker_);
+
+ // The owning partition's storage policy.
+ scoped_refptr<storage::SpecialStoragePolicy> special_storage_policy_
+ GUARDED_BY_CONTEXT(sequence_checker_);
+
+ // The maximum allowed number of entries per origin.
+ const int64_t max_entries_per_origin_ GUARDED_BY_CONTEXT(sequence_checker_);
+
+ // The maximum size of a string input from any origin's script. Applies
+ // separately to both script keys and script values.
+ size_t max_string_length_ GUARDED_BY_CONTEXT(sequence_checker_);
+
+ // Maxmium number of times that SQL database attempts to initialize.
+ size_t max_init_tries_ GUARDED_BY_CONTEXT(sequence_checker_);
+
+ // Clock used to determine current time. Can be overridden in tests.
+ raw_ptr<base::Clock> clock_ GUARDED_BY_CONTEXT(sequence_checker_);
+
+ SEQUENCE_CHECKER(sequence_checker_);
+};
+
+} // namespace storage
+
+#endif // COMPONENTS_SERVICES_STORAGE_SHARED_STORAGE_SHARED_STORAGE_DATABASE_H_
diff --git a/chromium/components/services/storage/shared_storage/shared_storage_database_unittest.cc b/chromium/components/services/storage/shared_storage/shared_storage_database_unittest.cc
new file mode 100644
index 00000000000..364bead255c
--- /dev/null
+++ b/chromium/components/services/storage/shared_storage/shared_storage_database_unittest.cc
@@ -0,0 +1,835 @@
+// Copyright 2021 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 "components/services/storage/shared_storage/shared_storage_database.h"
+
+#include <algorithm>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/run_loop.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/test/simple_test_clock.h"
+#include "base/test/task_environment.h"
+#include "base/time/time.h"
+#include "components/services/storage/public/mojom/storage_usage_info.mojom.h"
+#include "components/services/storage/shared_storage/shared_storage_options.h"
+#include "components/services/storage/shared_storage/shared_storage_test_utils.h"
+#include "sql/database.h"
+#include "storage/browser/quota/special_storage_policy.h"
+#include "storage/browser/test/mock_special_storage_policy.h"
+#include "testing/gmock/include/gmock/gmock-matchers.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/features.h"
+#include "url/gurl.h"
+#include "url/origin.h"
+
+namespace storage {
+
+namespace {
+
+using ::testing::ElementsAre;
+using OriginMatcherFunction = SharedStorageDatabase::OriginMatcherFunction;
+using InitStatus = SharedStorageDatabase::InitStatus;
+using SetBehavior = SharedStorageDatabase::SetBehavior;
+using OperationResult = SharedStorageDatabase::OperationResult;
+using GetResult = SharedStorageDatabase::GetResult;
+
+const int kMaxEntriesPerOrigin = 5;
+const int kMaxStringLength = 100;
+
+} // namespace
+
+class SharedStorageDatabaseTest : public testing::Test {
+ public:
+ SharedStorageDatabaseTest() {
+ special_storage_policy_ = base::MakeRefCounted<MockSpecialStoragePolicy>();
+ }
+
+ ~SharedStorageDatabaseTest() override = default;
+
+ void SetUp() override {
+ InitSharedStorageFeature();
+
+ // Get a temporary directory for the test DB files.
+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+
+ file_name_ = temp_dir_.GetPath().AppendASCII("TestSharedStorage.db");
+ }
+
+ void TearDown() override {
+ db_.reset();
+ EXPECT_TRUE(temp_dir_.Delete());
+ }
+
+ // Initialize a shared storage database instance from the SQL file at
+ // `relative_file_path` in the "storage/" subdirectory of test data.
+ std::unique_ptr<SharedStorageDatabase> LoadFromFile(
+ const char* relative_file_path) {
+ if (!CreateDatabaseFromSQL(file_name_, relative_file_path)) {
+ ADD_FAILURE() << "Failed loading " << relative_file_path;
+ return nullptr;
+ }
+
+ return std::make_unique<SharedStorageDatabase>(
+ file_name_, special_storage_policy_,
+ SharedStorageOptions::Create()->GetDatabaseOptions());
+ }
+
+ sql::Database* SqlDB() { return db_ ? db_->db() : nullptr; }
+
+ virtual void InitSharedStorageFeature() {
+ scoped_feature_list_.InitAndEnableFeatureWithParameters(
+ {blink::features::kSharedStorageAPI},
+ {{"MaxSharedStorageInitTries", "1"}});
+ }
+
+ protected:
+ base::ScopedTempDir temp_dir_;
+ base::FilePath file_name_;
+ scoped_refptr<storage::MockSpecialStoragePolicy> special_storage_policy_;
+ std::unique_ptr<SharedStorageDatabase> db_;
+ base::test::ScopedFeatureList scoped_feature_list_;
+ base::SimpleTestClock clock_;
+
+ private:
+ base::test::SingleThreadTaskEnvironment task_environment_;
+};
+
+// Test loading version 1 database.
+TEST_F(SharedStorageDatabaseTest, Version1_LoadFromFile) {
+ db_ = LoadFromFile("shared_storage.v1.sql");
+ ASSERT_TRUE(db_);
+
+ url::Origin google_com = url::Origin::Create(GURL("http://google.com/"));
+ EXPECT_EQ(db_->Get(google_com, u"key1").data, u"value1");
+ EXPECT_EQ(db_->Get(google_com, u"key2").data, u"value2");
+
+ // Because the SQL database is lazy-initialized, wait to verify tables and
+ // columns until after the first call to `Get()`.
+ ASSERT_TRUE(SqlDB());
+ VerifySharedStorageTablesAndColumns(*SqlDB());
+
+ url::Origin youtube_com = url::Origin::Create(GURL("http://youtube.com/"));
+ EXPECT_EQ(1L, db_->Length(youtube_com));
+
+ url::Origin chromium_org = url::Origin::Create(GURL("http://chromium.org/"));
+ EXPECT_EQ(db_->Get(chromium_org, u"a").data, u"");
+ EXPECT_EQ(db_->Key(chromium_org, 2UL).data, u"c");
+
+ url::Origin google_org = url::Origin::Create(GURL("http://google.org/"));
+ EXPECT_EQ(
+ db_->Get(google_org, u"1").data,
+ u"fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "fffffffffffffffff");
+ EXPECT_EQ(db_->Get(google_org,
+ u"ffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "ffffffffffffffffffffffffffffffffffffffffffffffffffffffff")
+ .data,
+ u"k");
+
+ std::vector<url::Origin> origins;
+ for (const auto& info : db_->FetchOrigins())
+ origins.push_back(info->origin);
+ EXPECT_THAT(
+ origins,
+ ElementsAre(
+ url::Origin::Create(GURL("http://abc.xyz")), chromium_org, google_com,
+ google_org, url::Origin::Create(GURL("http://growwithgoogle.com")),
+ url::Origin::Create(GURL("http://gv.com")),
+ url::Origin::Create(GURL("http://waymo.com")),
+ url::Origin::Create(GURL("http://withgoogle.com")), youtube_com));
+
+ EXPECT_TRUE(db_->Destroy());
+}
+
+TEST_F(SharedStorageDatabaseTest, Version1_DestroyTooNew) {
+ // Initialization should fail, since the last compatible version number
+ // is too high.
+ db_ = LoadFromFile("shared_storage.v1.init_too_new.sql");
+ ASSERT_TRUE(db_);
+ ASSERT_TRUE(SqlDB());
+
+ // Call an operation so that the database will attempt to be lazy-initialized.
+ const url::Origin kOrigin = url::Origin::Create(GURL("http://www.a.com"));
+ EXPECT_EQ(OperationResult::kInitFailure, db_->Set(kOrigin, u"key", u"value"));
+ ASSERT_FALSE(db_->IsOpenForTesting());
+ EXPECT_EQ(InitStatus::kTooNew, db_->DBStatusForTesting());
+
+ // Test that other operations likewise fail, in order to exercise these code
+ // paths.
+ EXPECT_EQ(OperationResult::kInitFailure, db_->Get(kOrigin, u"key").result);
+ EXPECT_EQ(OperationResult::kInitFailure,
+ db_->Append(kOrigin, u"key", u"value"));
+ EXPECT_EQ(OperationResult::kInitFailure, db_->Delete(kOrigin, u"key"));
+ EXPECT_EQ(OperationResult::kInitFailure, db_->Clear(kOrigin));
+ EXPECT_EQ(-1, db_->Length(kOrigin));
+ EXPECT_EQ(OperationResult::kInitFailure, db_->Key(kOrigin, 0).result);
+ EXPECT_EQ(OperationResult::kInitFailure,
+ db_->PurgeMatchingOrigins(OriginMatcherFunction(),
+ base::Time::Min(), base::Time::Max(),
+ /*perform_storage_cleanup=*/false));
+ EXPECT_EQ(OperationResult::kInitFailure,
+ db_->PurgeStaleOrigins(base::Seconds(1)));
+
+ // Test that it is still OK to Destroy() the database.
+ EXPECT_TRUE(db_->Destroy());
+}
+
+TEST_F(SharedStorageDatabaseTest, Version0_DestroyTooOld) {
+ // Initialization should fail, since the current version number
+ // is too low and we're forcing there not to be a retry attempt.
+ db_ = LoadFromFile("shared_storage.v0.init_too_old.sql");
+ ASSERT_TRUE(db_);
+ ASSERT_TRUE(SqlDB());
+
+ // Call an operation so that the database will attempt to be lazy-initialized.
+ EXPECT_EQ(OperationResult::kInitFailure,
+ db_->Set(url::Origin::Create(GURL("http://www.a.com")), u"key",
+ u"value"));
+ ASSERT_FALSE(db_->IsOpenForTesting());
+ EXPECT_EQ(InitStatus::kTooOld, db_->DBStatusForTesting());
+
+ // Test that it is still OK to Destroy() the database.
+ EXPECT_TRUE(db_->Destroy());
+}
+
+class SharedStorageDatabaseParamTest
+ : public SharedStorageDatabaseTest,
+ public testing::WithParamInterface<SharedStorageWrappedBool> {
+ public:
+ void SetUp() override {
+ SharedStorageDatabaseTest::SetUp();
+
+ auto options = SharedStorageOptions::Create()->GetDatabaseOptions();
+ base::FilePath db_path =
+ (GetParam().in_memory_only) ? base::FilePath() : file_name_;
+ db_ = std::make_unique<SharedStorageDatabase>(
+ db_path, special_storage_policy_, std::move(options));
+ db_->OverrideClockForTesting(&clock_);
+ }
+
+ void InitSharedStorageFeature() override {
+ scoped_feature_list_.InitAndEnableFeatureWithParameters(
+ {blink::features::kSharedStorageAPI},
+ {{"MaxSharedStorageEntriesPerOrigin",
+ base::NumberToString(kMaxEntriesPerOrigin)},
+ {"MaxSharedStorageStringLength",
+ base::NumberToString(kMaxStringLength)}});
+ }
+};
+
+INSTANTIATE_TEST_SUITE_P(All,
+ SharedStorageDatabaseParamTest,
+ testing::ValuesIn(GetSharedStorageWrappedBools()),
+ testing::PrintToStringParamName());
+
+TEST_P(SharedStorageDatabaseParamTest, BasicOperations) {
+ const url::Origin kOrigin1 =
+ url::Origin::Create(GURL("http://www.example1.test"));
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin1, u"key1", u"value1"));
+ EXPECT_EQ(db_->Get(kOrigin1, u"key1").data, u"value1");
+
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin1, u"key1", u"value2"));
+ EXPECT_EQ(db_->Get(kOrigin1, u"key1").data, u"value2");
+
+ EXPECT_EQ(OperationResult::kSuccess, db_->Delete(kOrigin1, u"key1"));
+ EXPECT_FALSE(db_->Get(kOrigin1, u"key1").data);
+
+ // Check that trying to retrieve the empty key doesn't give an error, even
+ // though the input is invalid and no value is found.
+ GetResult result = db_->Get(kOrigin1, u"");
+ EXPECT_EQ(OperationResult::kSuccess, result.result);
+ EXPECT_FALSE(result.data);
+
+ // Check that trying to delete the empty key doesn't give an error, even
+ // though the input is invalid and no value is found to delete.
+ EXPECT_EQ(OperationResult::kSuccess, db_->Delete(kOrigin1, u""));
+}
+
+TEST_P(SharedStorageDatabaseParamTest, IgnoreIfPresent) {
+ const url::Origin kOrigin1 =
+ url::Origin::Create(GURL("http://www.example1.test"));
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin1, u"key1", u"value1"));
+ EXPECT_EQ(db_->Get(kOrigin1, u"key1").data, u"value1");
+
+ // The database does not set a new value for "key1", but retains the
+ // previously set value "value1" because `behavior` is `kIgnoreIfPresent`.
+ EXPECT_EQ(OperationResult::kIgnored,
+ db_->Set(kOrigin1, u"key1", u"value2",
+ /*behavior=*/SetBehavior::kIgnoreIfPresent));
+ EXPECT_EQ(db_->Get(kOrigin1, u"key1").data, u"value1");
+
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin1, u"key2", u"value1"));
+ EXPECT_EQ(db_->Get(kOrigin1, u"key2").data, u"value1");
+
+ // Having `behavior` set to `kDefault` makes `Set()` override any previous
+ // value.
+ EXPECT_EQ(OperationResult::kSet,
+ db_->Set(kOrigin1, u"key2", u"value2",
+ /*behavior=*/SetBehavior::kDefault));
+ EXPECT_EQ(db_->Get(kOrigin1, u"key2").data, u"value2");
+
+ const url::Origin kOrigin2 =
+ url::Origin::Create(GURL("http://www.example2.test"));
+
+ // If no previous value exists, it makes no difference whether
+ // `behavior` is set to `kDefault` or `kIgnoreIfPresent`.
+ EXPECT_EQ(OperationResult::kSet,
+ db_->Set(kOrigin2, u"key1", u"value1",
+ /*behavior=*/SetBehavior::kIgnoreIfPresent));
+ EXPECT_EQ(db_->Get(kOrigin2, u"key1").data, u"value1");
+
+ EXPECT_EQ(OperationResult::kSet,
+ db_->Set(kOrigin2, u"key2", u"value2",
+ /*behavior=*/SetBehavior::kDefault));
+ EXPECT_EQ(db_->Get(kOrigin2, u"key2").data, u"value2");
+}
+
+TEST_P(SharedStorageDatabaseParamTest, Append) {
+ const url::Origin kOrigin1 =
+ url::Origin::Create(GURL("http://www.example1.test"));
+ EXPECT_EQ(OperationResult::kSet, db_->Append(kOrigin1, u"key1", u"value1"));
+ EXPECT_EQ(db_->Get(kOrigin1, u"key1").data, u"value1");
+
+ EXPECT_EQ(OperationResult::kSet, db_->Append(kOrigin1, u"key1", u"value1"));
+ EXPECT_EQ(db_->Get(kOrigin1, u"key1").data, u"value1value1");
+
+ EXPECT_EQ(OperationResult::kSet, db_->Append(kOrigin1, u"key1", u"value1"));
+ EXPECT_EQ(db_->Get(kOrigin1, u"key1").data, u"value1value1value1");
+}
+
+TEST_P(SharedStorageDatabaseParamTest, Length) {
+ const url::Origin kOrigin1 =
+ url::Origin::Create(GURL("http://www.example1.test"));
+ EXPECT_EQ(0L, db_->Length(kOrigin1));
+
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin1, u"key1", u"value1"));
+ EXPECT_EQ(1L, db_->Length(kOrigin1));
+
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin1, u"key2", u"value2"));
+ EXPECT_EQ(2L, db_->Length(kOrigin1));
+
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin1, u"key2", u"value3"));
+ EXPECT_EQ(2L, db_->Length(kOrigin1));
+
+ const url::Origin kOrigin2 =
+ url::Origin::Create(GURL("http://www.example2.test"));
+ EXPECT_EQ(0L, db_->Length(kOrigin2));
+
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin2, u"key1", u"value1"));
+ EXPECT_EQ(1L, db_->Length(kOrigin2));
+ EXPECT_EQ(2L, db_->Length(kOrigin1));
+
+ EXPECT_EQ(OperationResult::kSuccess, db_->Delete(kOrigin2, u"key1"));
+ EXPECT_EQ(0L, db_->Length(kOrigin2));
+ EXPECT_EQ(2L, db_->Length(kOrigin1));
+
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin1, u"key3", u"value3"));
+ EXPECT_EQ(3L, db_->Length(kOrigin1));
+ EXPECT_EQ(0L, db_->Length(kOrigin2));
+}
+
+TEST_P(SharedStorageDatabaseParamTest, Key) {
+ const url::Origin kOrigin1 =
+ url::Origin::Create(GURL("http://www.example1.test"));
+ EXPECT_FALSE(db_->Key(kOrigin1, 0UL).data);
+
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin1, u"key1", u"value1"));
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin1, u"key2", u"value2"));
+ EXPECT_EQ(db_->Key(kOrigin1, 0UL).data, u"key1");
+ EXPECT_EQ(db_->Key(kOrigin1, 1UL).data, u"key2");
+ EXPECT_FALSE(db_->Key(kOrigin1, 2UL).data);
+
+ const url::Origin kOrigin2 =
+ url::Origin::Create(GURL("http://www.example2.test"));
+ EXPECT_FALSE(db_->Key(kOrigin2, 0UL).data);
+
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin2, u"key2", u"value2"));
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin2, u"key1", u"value1"));
+ EXPECT_EQ(db_->Key(kOrigin2, 0UL).data, u"key1");
+ EXPECT_EQ(db_->Key(kOrigin2, 1UL).data, u"key2");
+
+ EXPECT_EQ(OperationResult::kSuccess, db_->Delete(kOrigin2, u"key2"));
+ EXPECT_EQ(db_->Key(kOrigin2, 0UL).data, u"key1");
+
+ // There is no longer a key at this index.
+ EXPECT_FALSE(db_->Key(kOrigin2, 1UL).data);
+}
+
+TEST_P(SharedStorageDatabaseParamTest, Clear) {
+ const url::Origin kOrigin1 =
+ url::Origin::Create(GURL("http://www.example1.test"));
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin1, u"key1", u"value1"));
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin1, u"key2", u"value2"));
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin1, u"key3", u"value3"));
+ EXPECT_EQ(3L, db_->Length(kOrigin1));
+
+ const url::Origin kOrigin2 =
+ url::Origin::Create(GURL("http://www.example2.test"));
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin2, u"key1", u"value1"));
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin2, u"key2", u"value2"));
+ EXPECT_EQ(2L, db_->Length(kOrigin2));
+
+ EXPECT_EQ(OperationResult::kSuccess, db_->Clear(kOrigin1));
+ EXPECT_EQ(0L, db_->Length(kOrigin1));
+ EXPECT_EQ(2L, db_->Length(kOrigin2));
+
+ EXPECT_EQ(OperationResult::kSuccess, db_->Clear(kOrigin2));
+ EXPECT_EQ(0L, db_->Length(kOrigin2));
+}
+
+TEST_P(SharedStorageDatabaseParamTest, FetchOrigins) {
+ EXPECT_TRUE(db_->FetchOrigins().empty());
+
+ const url::Origin kOrigin1 =
+ url::Origin::Create(GURL("http://www.example1.test"));
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin1, u"key1", u"value1"));
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin1, u"key2", u"value2"));
+ EXPECT_EQ(2L, db_->Length(kOrigin1));
+
+ const url::Origin kOrigin2 =
+ url::Origin::Create(GURL("http://www.example2.test"));
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin2, u"key1", u"value1"));
+ EXPECT_EQ(1L, db_->Length(kOrigin2));
+
+ const url::Origin kOrigin3 =
+ url::Origin::Create(GURL("http://www.example3.test"));
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin3, u"key1", u"value1"));
+ EXPECT_EQ(1L, db_->Length(kOrigin3));
+
+ const url::Origin kOrigin4 =
+ url::Origin::Create(GURL("http://www.example4.test"));
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin4, u"key1", u"value1"));
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin4, u"key2", u"value2"));
+ EXPECT_EQ(2L, db_->Length(kOrigin4));
+
+ std::vector<url::Origin> origins;
+ for (const auto& info : db_->FetchOrigins())
+ origins.push_back(info->origin);
+ EXPECT_THAT(origins, ElementsAre(kOrigin1, kOrigin2, kOrigin3, kOrigin4));
+
+ EXPECT_EQ(OperationResult::kSuccess, db_->Clear(kOrigin1));
+ EXPECT_EQ(0L, db_->Length(kOrigin1));
+
+ EXPECT_EQ(OperationResult::kSuccess, db_->Delete(kOrigin2, u"key1"));
+ EXPECT_EQ(0L, db_->Length(kOrigin2));
+
+ origins.clear();
+ EXPECT_TRUE(origins.empty());
+ for (const auto& info : db_->FetchOrigins())
+ origins.push_back(info->origin);
+ EXPECT_THAT(origins, ElementsAre(kOrigin3, kOrigin4));
+}
+
+class SharedStorageDatabasePurgeMatchingOriginsParamTest
+ : public SharedStorageDatabaseTest,
+ public testing::WithParamInterface<PurgeMatchingOriginsParams> {
+ public:
+ void SetUp() override {
+ SharedStorageDatabaseTest::SetUp();
+
+ auto options = SharedStorageOptions::Create()->GetDatabaseOptions();
+ base::FilePath db_path =
+ (GetParam().in_memory_only) ? base::FilePath() : file_name_;
+ db_ = std::make_unique<SharedStorageDatabase>(
+ db_path, special_storage_policy_, std::move(options));
+ db_->OverrideClockForTesting(&clock_);
+ }
+
+ void InitSharedStorageFeature() override {
+ scoped_feature_list_.InitAndEnableFeatureWithParameters(
+ {blink::features::kSharedStorageAPI},
+ {{"MaxSharedStorageEntriesPerOrigin",
+ base::NumberToString(kMaxEntriesPerOrigin)},
+ {"MaxSharedStorageStringLength",
+ base::NumberToString(kMaxStringLength)}});
+ }
+};
+
+INSTANTIATE_TEST_SUITE_P(All,
+ SharedStorageDatabasePurgeMatchingOriginsParamTest,
+ testing::ValuesIn(GetPurgeMatchingOriginsParams()),
+ testing::PrintToStringParamName());
+
+TEST_P(SharedStorageDatabasePurgeMatchingOriginsParamTest, AllTime) {
+ EXPECT_TRUE(db_->FetchOrigins().empty());
+
+ const url::Origin kOrigin1 =
+ url::Origin::Create(GURL("http://www.example1.test"));
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin1, u"key1", u"value1"));
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin1, u"key2", u"value2"));
+ EXPECT_EQ(2L, db_->Length(kOrigin1));
+
+ const url::Origin kOrigin2 =
+ url::Origin::Create(GURL("http://www.example2.test"));
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin2, u"key1", u"value1"));
+ EXPECT_EQ(1L, db_->Length(kOrigin2));
+
+ const url::Origin kOrigin3 =
+ url::Origin::Create(GURL("http://www.example3.test"));
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin3, u"key1", u"value1"));
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin3, u"key2", u"value2"));
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin3, u"key3", u"value3"));
+ EXPECT_EQ(3L, db_->Length(kOrigin3));
+
+ std::vector<url::Origin> origins;
+ for (const auto& info : db_->FetchOrigins())
+ origins.push_back(info->origin);
+ EXPECT_THAT(origins, ElementsAre(kOrigin1, kOrigin2, kOrigin3));
+
+ EXPECT_EQ(
+ OperationResult::kSuccess,
+ db_->PurgeMatchingOrigins(
+ OriginMatcherFunctionUtility::MakeMatcherFunction({kOrigin1}),
+ base::Time(), base::Time::Max(), GetParam().perform_storage_cleanup));
+
+ // `kOrigin1` is cleared. The other origins are not.
+ EXPECT_EQ(0L, db_->Length(kOrigin1));
+ EXPECT_EQ(1L, db_->Length(kOrigin2));
+ EXPECT_EQ(3L, db_->Length(kOrigin3));
+
+ origins.clear();
+ for (const auto& info : db_->FetchOrigins())
+ origins.push_back(info->origin);
+ EXPECT_THAT(origins, ElementsAre(kOrigin2, kOrigin3));
+
+ EXPECT_EQ(
+ OperationResult::kSuccess,
+ db_->PurgeMatchingOrigins(
+ OriginMatcherFunctionUtility::MakeMatcherFunction(
+ {kOrigin2, kOrigin3}),
+ base::Time(), base::Time::Max(), GetParam().perform_storage_cleanup));
+
+ // All three origins should be cleared.
+ EXPECT_EQ(0L, db_->Length(kOrigin1));
+ EXPECT_EQ(0L, db_->Length(kOrigin2));
+ EXPECT_EQ(0L, db_->Length(kOrigin3));
+
+ EXPECT_TRUE(db_->FetchOrigins().empty());
+
+ // There is no error from trying to clear an origin that isn't in the
+ // database.
+ EXPECT_EQ(
+ OperationResult::kSuccess,
+ db_->PurgeMatchingOrigins(
+ OriginMatcherFunctionUtility::MakeMatcherFunction(
+ {"http://www.example4.test"}),
+ base::Time(), base::Time::Max(), GetParam().perform_storage_cleanup));
+}
+
+TEST_P(SharedStorageDatabasePurgeMatchingOriginsParamTest, SinceThreshold) {
+ EXPECT_TRUE(db_->FetchOrigins().empty());
+
+ const url::Origin kOrigin1 =
+ url::Origin::Create(GURL("http://www.example1.test"));
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin1, u"key1", u"value1"));
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin1, u"key2", u"value2"));
+ EXPECT_EQ(2L, db_->Length(kOrigin1));
+
+ const url::Origin kOrigin2 =
+ url::Origin::Create(GURL("http://www.example2.test"));
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin2, u"key1", u"value1"));
+ EXPECT_EQ(1L, db_->Length(kOrigin2));
+
+ const url::Origin kOrigin3 =
+ url::Origin::Create(GURL("http://www.example3.test"));
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin3, u"key1", u"value1"));
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin3, u"key2", u"value2"));
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin3, u"key3", u"value3"));
+ EXPECT_EQ(3L, db_->Length(kOrigin3));
+
+ const url::Origin kOrigin4 =
+ url::Origin::Create(GURL("http://www.example4.test"));
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin4, u"key1", u"value1"));
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin4, u"key2", u"value2"));
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin4, u"key3", u"value3"));
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin4, u"key4", u"value4"));
+ EXPECT_EQ(4L, db_->Length(kOrigin4));
+
+ clock_.SetNow(base::Time::Now());
+ clock_.Advance(base::Milliseconds(50));
+
+ // Time threshold that will be used as a starting point for deletion.
+ base::Time threshold = clock_.Now();
+
+ std::vector<url::Origin> origins;
+ for (const auto& info : db_->FetchOrigins())
+ origins.push_back(info->origin);
+ EXPECT_THAT(origins, ElementsAre(kOrigin1, kOrigin2, kOrigin3, kOrigin4));
+
+ // Read from `kOrigin1`.
+ EXPECT_EQ(db_->Get(kOrigin1, u"key1").data, u"value1");
+
+ EXPECT_EQ(
+ OperationResult::kSuccess,
+ db_->PurgeMatchingOrigins(
+ OriginMatcherFunctionUtility::MakeMatcherFunction(
+ {kOrigin1, kOrigin2}),
+ threshold, base::Time::Max(), GetParam().perform_storage_cleanup));
+
+ // `kOrigin1` is cleared. The other origins are not.
+ EXPECT_EQ(0L, db_->Length(kOrigin1));
+ EXPECT_EQ(1L, db_->Length(kOrigin2));
+ EXPECT_EQ(3L, db_->Length(kOrigin3));
+ EXPECT_EQ(4L, db_->Length(kOrigin4));
+
+ clock_.Advance(base::Milliseconds(50));
+
+ // Time threshold that will be used as a starting point for deletion.
+ threshold = clock_.Now();
+
+ // Write to `kOrigin3`.
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin3, u"key4", u"value4"));
+ EXPECT_EQ(4L, db_->Length(kOrigin3));
+
+ origins.clear();
+ for (const auto& info : db_->FetchOrigins())
+ origins.push_back(info->origin);
+ EXPECT_THAT(origins, ElementsAre(kOrigin2, kOrigin3, kOrigin4));
+
+ EXPECT_EQ(
+ OperationResult::kSuccess,
+ db_->PurgeMatchingOrigins(
+ OriginMatcherFunctionUtility::MakeMatcherFunction(
+ {kOrigin2, kOrigin3, kOrigin4}),
+ threshold, base::Time::Max(), GetParam().perform_storage_cleanup));
+
+ // `kOrigin3` is cleared. The others weren't modified within the given time
+ // period.
+ EXPECT_EQ(0L, db_->Length(kOrigin1));
+ EXPECT_EQ(1L, db_->Length(kOrigin2));
+ EXPECT_EQ(0L, db_->Length(kOrigin3));
+ EXPECT_EQ(4L, db_->Length(kOrigin4));
+
+ origins.clear();
+ for (const auto& info : db_->FetchOrigins())
+ origins.push_back(info->origin);
+ EXPECT_THAT(origins, ElementsAre(kOrigin2, kOrigin4));
+
+ // There is no error from trying to clear an origin that isn't in the
+ // database.
+ EXPECT_EQ(
+ OperationResult::kSuccess,
+ db_->PurgeMatchingOrigins(
+ OriginMatcherFunctionUtility::MakeMatcherFunction(
+ {"http://www.example5.test"}),
+ threshold, base::Time::Max(), GetParam().perform_storage_cleanup));
+}
+
+TEST_P(SharedStorageDatabaseParamTest, PurgeStaleOrigins) {
+ EXPECT_TRUE(db_->FetchOrigins().empty());
+
+ const url::Origin kOrigin1 =
+ url::Origin::Create(GURL("http://www.example1.test"));
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin1, u"key1", u"value1"));
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin1, u"key2", u"value2"));
+ EXPECT_EQ(2L, db_->Length(kOrigin1));
+ EXPECT_EQ(db_->Get(kOrigin1, u"key1").data, u"value1");
+ EXPECT_EQ(db_->Get(kOrigin1, u"key2").data, u"value2");
+
+ const url::Origin kOrigin2 =
+ url::Origin::Create(GURL("http://www.example2.test"));
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin2, u"key1", u"value1"));
+ EXPECT_EQ(1L, db_->Length(kOrigin2));
+ EXPECT_EQ(db_->Get(kOrigin2, u"key1").data, u"value1");
+
+ const url::Origin kOrigin3 =
+ url::Origin::Create(GURL("http://www.example3.test"));
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin3, u"key1", u"value1"));
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin3, u"key2", u"value2"));
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin3, u"key3", u"value3"));
+ EXPECT_EQ(3L, db_->Length(kOrigin3));
+
+ const url::Origin kOrigin4 =
+ url::Origin::Create(GURL("http://www.example4.test"));
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin4, u"key1", u"value1"));
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin4, u"key2", u"value2"));
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin4, u"key3", u"value3"));
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin4, u"key4", u"value4"));
+ EXPECT_EQ(4L, db_->Length(kOrigin4));
+
+ std::vector<url::Origin> origins;
+ for (const auto& info : db_->FetchOrigins())
+ origins.push_back(info->origin);
+ EXPECT_THAT(origins, ElementsAre(kOrigin1, kOrigin2, kOrigin3, kOrigin4));
+
+ clock_.SetNow(base::Time::Now());
+ clock_.Advance(base::Milliseconds(50));
+
+ // Time threshold after which an origin must be read from or written to in
+ // order to be considered active.
+ base::Time threshold = clock_.Now();
+ clock_.Advance(base::Milliseconds(50));
+
+ // Read from `kOrigin1`.
+ EXPECT_EQ(db_->Get(kOrigin1, u"key1").data, u"value1");
+
+ // Write to `kOrigin3`.
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin3, u"key4", u"value4"));
+ EXPECT_EQ(4L, db_->Length(kOrigin3));
+
+ EXPECT_EQ(OperationResult::kSuccess,
+ db_->PurgeStaleOrigins(clock_.Now() - threshold));
+
+ // `kOrigin1` was active.
+ EXPECT_EQ(2L, db_->Length(kOrigin1));
+
+ // `kOrigin2` was inactive.
+ EXPECT_EQ(0L, db_->Length(kOrigin2));
+
+ // `kOrigin3` was active.
+ EXPECT_EQ(4L, db_->Length(kOrigin3));
+
+ // `kOrigin4` was inactive.
+ EXPECT_EQ(0L, db_->Length(kOrigin4));
+
+ origins.clear();
+ for (const auto& info : db_->FetchOrigins())
+ origins.push_back(info->origin);
+ EXPECT_THAT(origins, ElementsAre(kOrigin1, kOrigin3));
+}
+
+TEST_P(SharedStorageDatabaseParamTest, TrimMemory) {
+ EXPECT_TRUE(db_->FetchOrigins().empty());
+
+ const url::Origin kOrigin1 =
+ url::Origin::Create(GURL("http://www.example1.test"));
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin1, u"key1", u"value1"));
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin1, u"key2", u"value2"));
+ EXPECT_EQ(2L, db_->Length(kOrigin1));
+
+ const url::Origin kOrigin2 =
+ url::Origin::Create(GURL("http://www.example2.test"));
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin2, u"key1", u"value1"));
+ EXPECT_EQ(1L, db_->Length(kOrigin2));
+
+ const url::Origin kOrigin3 =
+ url::Origin::Create(GURL("http://www.example3.test"));
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin3, u"key1", u"value1"));
+ EXPECT_EQ(1L, db_->Length(kOrigin3));
+
+ const url::Origin kOrigin4 =
+ url::Origin::Create(GURL("http://www.example4.test"));
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin4, u"key1", u"value1"));
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin4, u"key2", u"value2"));
+ EXPECT_EQ(2L, db_->Length(kOrigin4));
+
+ std::vector<url::Origin> origins;
+ for (const auto& info : db_->FetchOrigins())
+ origins.push_back(info->origin);
+ EXPECT_THAT(origins, ElementsAre(kOrigin1, kOrigin2, kOrigin3, kOrigin4));
+
+ EXPECT_EQ(OperationResult::kSuccess, db_->Clear(kOrigin1));
+ EXPECT_EQ(0L, db_->Length(kOrigin1));
+
+ EXPECT_EQ(OperationResult::kSuccess, db_->Delete(kOrigin2, u"key1"));
+ EXPECT_EQ(0L, db_->Length(kOrigin2));
+
+ origins.clear();
+ for (const auto& info : db_->FetchOrigins())
+ origins.push_back(info->origin);
+ EXPECT_THAT(origins, ElementsAre(kOrigin3, kOrigin4));
+
+ // Release nonessential memory.
+ db_->TrimMemory();
+
+ // Check that the database is still intact.
+ origins.clear();
+ for (const auto& info : db_->FetchOrigins())
+ origins.push_back(info->origin);
+ EXPECT_THAT(origins, ElementsAre(kOrigin3, kOrigin4));
+
+ EXPECT_EQ(1L, db_->Length(kOrigin3));
+ EXPECT_EQ(2L, db_->Length(kOrigin4));
+
+ EXPECT_EQ(db_->Get(kOrigin3, u"key1").data, u"value1");
+ EXPECT_EQ(db_->Get(kOrigin4, u"key1").data, u"value1");
+ EXPECT_EQ(db_->Get(kOrigin4, u"key2").data, u"value2");
+}
+
+TEST_P(SharedStorageDatabaseParamTest, MaxEntriesPerOrigin) {
+ const url::Origin kOrigin1 =
+ url::Origin::Create(GURL("http://www.example1.test"));
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin1, u"key1", u"value1"));
+ EXPECT_EQ(1L, db_->Length(kOrigin1));
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin1, u"key2", u"value2"));
+ EXPECT_EQ(2L, db_->Length(kOrigin1));
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin1, u"key3", u"value3"));
+ EXPECT_EQ(3L, db_->Length(kOrigin1));
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin1, u"key4", u"value4"));
+ EXPECT_EQ(4L, db_->Length(kOrigin1));
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin1, u"key5", u"value5"));
+ EXPECT_EQ(5L, db_->Length(kOrigin1));
+
+ // `kOrigin1` should have hit capacity, and hence this value will not be set.
+ EXPECT_EQ(OperationResult::kNoCapacity,
+ db_->Set(kOrigin1, u"key6", u"value6"));
+
+ EXPECT_EQ(5L, db_->Length(kOrigin1));
+ EXPECT_EQ(OperationResult::kSuccess, db_->Delete(kOrigin1, u"key5"));
+ EXPECT_EQ(4L, db_->Length(kOrigin1));
+
+ // There should now be capacity and the value will be set.
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin1, u"key6", u"value6"));
+ EXPECT_EQ(5L, db_->Length(kOrigin1));
+}
+
+TEST_P(SharedStorageDatabaseParamTest, MaxStringLength) {
+ const url::Origin kOrigin1 =
+ url::Origin::Create(GURL("http://www.example1.test"));
+ const std::u16string kLongString(kMaxStringLength, u'g');
+
+ // This value has the maximum allowed length.
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin1, u"key1", kLongString));
+ EXPECT_EQ(1L, db_->Length(kOrigin1));
+
+ // Appending to the value would exceed the allowed length and so won't
+ // succeed.
+ EXPECT_EQ(OperationResult::kInvalidAppend,
+ db_->Append(kOrigin1, u"key1", u"h"));
+
+ EXPECT_EQ(1L, db_->Length(kOrigin1));
+
+ // This key has the maximum allowed length.
+ EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin1, kLongString, u"value1"));
+ EXPECT_EQ(2L, db_->Length(kOrigin1));
+}
+
+} // namespace storage
diff --git a/chromium/components/services/storage/shared_storage/shared_storage_options.cc b/chromium/components/services/storage/shared_storage/shared_storage_options.cc
new file mode 100644
index 00000000000..fcbb0343a98
--- /dev/null
+++ b/chromium/components/services/storage/shared_storage/shared_storage_options.cc
@@ -0,0 +1,80 @@
+// Copyright 2021 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 "components/services/storage/shared_storage/shared_storage_options.h"
+
+#include "base/bits.h"
+#include "third_party/blink/public/common/features.h"
+
+namespace storage {
+
+namespace {
+
+bool IsValidPageSize(int page_size) {
+ if (page_size < 512 || page_size > 65536)
+ return false;
+ return base::bits::IsPowerOfTwo(page_size);
+}
+
+} // namespace
+
+// static
+std::unique_ptr<SharedStorageOptions> SharedStorageOptions::Create() {
+ return std::make_unique<SharedStorageOptions>(
+ blink::features::kMaxSharedStoragePageSize.Get(),
+ blink::features::kMaxSharedStorageCacheSize.Get(),
+ blink::features::kMaxSharedStorageEntriesPerOrigin.Get(),
+ blink::features::kMaxSharedStorageStringLength.Get(),
+ blink::features::kMaxSharedStorageInitTries.Get(),
+ blink::features::kMaxSharedStorageConsecutiveOperationErrorsAllowed.Get(),
+ blink::features::kSharedStorageStaleOriginPurgeInitialInterval.Get(),
+ blink::features::kSharedStorageStaleOriginPurgeRecurringInterval.Get(),
+ blink::features::kSharedStorageOriginStalenessThreshold.Get());
+}
+
+SharedStorageOptions::SharedStorageOptions(
+ int max_page_size,
+ int max_cache_size,
+ int max_entries_per_origin,
+ int max_string_length,
+ int max_init_tries,
+ int max_allowed_consecutive_errors,
+ base::TimeDelta stale_origin_purge_initial_interval,
+ base::TimeDelta stale_origin_purge_recurring_interval,
+ base::TimeDelta origin_staleness_threshold)
+ : max_page_size(max_page_size),
+ max_cache_size(max_cache_size),
+ max_entries_per_origin(max_entries_per_origin),
+ max_string_length(max_string_length),
+ max_init_tries(max_init_tries),
+ max_allowed_consecutive_errors(max_allowed_consecutive_errors),
+ stale_origin_purge_initial_interval(stale_origin_purge_initial_interval),
+ stale_origin_purge_recurring_interval(
+ stale_origin_purge_recurring_interval),
+ origin_staleness_threshold(origin_staleness_threshold) {
+ DCHECK(IsValidPageSize(max_page_size));
+}
+
+std::unique_ptr<SharedStorageDatabaseOptions>
+SharedStorageOptions::GetDatabaseOptions() {
+ return std::make_unique<SharedStorageDatabaseOptions>(
+ max_page_size, max_cache_size, max_entries_per_origin, max_string_length,
+ max_init_tries);
+}
+
+SharedStorageDatabaseOptions::SharedStorageDatabaseOptions(
+ int max_page_size,
+ int max_cache_size,
+ int max_entries_per_origin,
+ int max_string_length,
+ int max_init_tries)
+ : max_page_size(max_page_size),
+ max_cache_size(max_cache_size),
+ max_entries_per_origin(max_entries_per_origin),
+ max_string_length(max_string_length),
+ max_init_tries(max_init_tries) {
+ DCHECK(IsValidPageSize(max_page_size));
+}
+
+} // namespace storage
diff --git a/chromium/components/services/storage/shared_storage/shared_storage_options.h b/chromium/components/services/storage/shared_storage/shared_storage_options.h
new file mode 100644
index 00000000000..751662e24a2
--- /dev/null
+++ b/chromium/components/services/storage/shared_storage/shared_storage_options.h
@@ -0,0 +1,104 @@
+// Copyright 2021 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.
+
+#ifndef COMPONENTS_SERVICES_STORAGE_SHARED_STORAGE_SHARED_STORAGE_OPTIONS_H_
+#define COMPONENTS_SERVICES_STORAGE_SHARED_STORAGE_SHARED_STORAGE_OPTIONS_H_
+
+#include <memory>
+
+#include "base/time/time.h"
+
+namespace storage {
+
+struct SharedStorageDatabaseOptions;
+
+// Bundles Finch-configurable constants for the `SharedStorageManager`,
+// `AsyncSharedStorageDatabase`, and `SharedStorageDatabase` classes.
+struct SharedStorageOptions {
+ // The static `Create()` method accesses field trial params to populate one or
+ // more attributes, and so must be called on the main thread.
+ static std::unique_ptr<SharedStorageOptions> Create();
+
+ SharedStorageOptions(int max_page_size,
+ int max_cache_size,
+ int max_entries_per_origin,
+ int max_string_length,
+ int max_init_tries,
+ int max_allowed_consecutive_errors,
+ base::TimeDelta stale_origin_purge_initial_interval,
+ base::TimeDelta stale_origin_purge_recurring_interval,
+ base::TimeDelta origin_staleness_threshold);
+
+ // Creates a pointer to a smaller bundle of just the constants that need to
+ // be forwarded to `AsyncSharedStorageDatabase` and `SharedStorageDatabase`.
+ std::unique_ptr<SharedStorageDatabaseOptions> GetDatabaseOptions();
+
+ // The max size of a database page, in bytes. Must be a power of 2 between
+ // 512 and 65536 inclusive.
+ const int max_page_size;
+
+ // The max size of the database cache, in pages.
+ const int max_cache_size;
+
+ // The maximum number of entries allowed per origin.
+ const int max_entries_per_origin;
+
+ // The maximum allowed string length for each script key or script value.
+ const int max_string_length;
+
+ // The maximum number of times that `SharedStorageDatabase` will try to
+ // initialize the SQL database.
+ const int max_init_tries;
+
+ // Maximum number of consecutive operation errors allowed before the database
+ // is deleted and recreated.
+ const int max_allowed_consecutive_errors;
+
+ // The initial interval at which stale origins are purged.
+ const base::TimeDelta stale_origin_purge_initial_interval;
+
+ // The recurring interval at which stale origins are purged. May differ from
+ // the initial interval.
+ const base::TimeDelta stale_origin_purge_recurring_interval;
+
+ // The amount of time that an origin needs to be inactive in order for it to
+ // be deemed stale.
+ const base::TimeDelta origin_staleness_threshold;
+};
+
+// Bundles Finch-configurable constants for the `AsyncSharedStorageDatabase`
+// and `SharedStorageDatabase` classes. This smaller class is separate from the
+// larger `SharedStorageOptions` (which has the ability to create an instance of
+// `SharedStorageDatabaseOptions` from a subset of its members) so that the
+// smaller `SharedStorageDatabaseOptions` bundle can be read on an alternate
+// thread while the larger class's bundle can continue to be accessed on the
+// main thread.
+struct SharedStorageDatabaseOptions {
+ SharedStorageDatabaseOptions(int max_page_size,
+ int max_cache_size,
+ int max_entries_per_origin,
+ int max_string_length,
+ int max_init_tries);
+
+ // The max size of a database page, in bytes. Must be a power of 2 between
+ // 512 and 65536 inclusive.
+ const int max_page_size;
+
+ // The max size of the database cache, in pages.
+ const int max_cache_size;
+
+ // The maximum number of entries allowed per origin.
+ const int max_entries_per_origin;
+
+ // The maximum allowed string length for each script key or script value.
+ const int max_string_length;
+
+ // The maximum number of times that `SharedStorageDatabase` will try to
+ // initialize the SQL database.
+ const int max_init_tries;
+};
+
+} // namespace storage
+
+#endif // COMPONENTS_SERVICES_STORAGE_SHARED_STORAGE_SHARED_STORAGE_OPTIONS_H_
diff --git a/chromium/components/services/storage/shared_storage/shared_storage_test_utils.cc b/chromium/components/services/storage/shared_storage/shared_storage_test_utils.cc
new file mode 100644
index 00000000000..2c13bbf283a
--- /dev/null
+++ b/chromium/components/services/storage/shared_storage/shared_storage_test_utils.cc
@@ -0,0 +1,369 @@
+// Copyright 2021 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 "components/services/storage/shared_storage/shared_storage_test_utils.h"
+
+#include <queue>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/containers/contains.h"
+#include "base/files/file_path.h"
+#include "base/path_service.h"
+#include "base/strings/strcat.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/services/storage/public/mojom/storage_usage_info.mojom.h"
+#include "sql/database.h"
+#include "sql/test/test_helpers.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace storage {
+
+TestDatabaseOperationReceiver::DBOperation::DBOperation(Type type)
+ : type(type) {
+ DCHECK(type == Type::DB_IS_OPEN || type == Type::DB_STATUS ||
+ type == Type::DB_DESTROY || type == Type::DB_TRIM_MEMORY ||
+ type == Type::DB_FETCH_ORIGINS);
+}
+
+TestDatabaseOperationReceiver::DBOperation::DBOperation(Type type,
+ url::Origin origin)
+ : type(type), origin(std::move(origin)) {
+ DCHECK(type == Type::DB_LENGTH || type == Type::DB_CLEAR);
+}
+
+TestDatabaseOperationReceiver::DBOperation::DBOperation(
+ Type type,
+ url::Origin origin,
+ std::vector<std::u16string> params)
+ : type(type), origin(std::move(origin)), params(std::move(params)) {
+ DCHECK(type == Type::DB_GET || type == Type::DB_SET ||
+ type == Type::DB_APPEND || type == Type::DB_DELETE ||
+ type == Type::DB_KEY || type == Type::DB_OVERRIDE_TIME);
+}
+
+TestDatabaseOperationReceiver::DBOperation::DBOperation(
+ Type type,
+ std::vector<std::u16string> params)
+ : type(type), params(std::move(params)) {
+ DCHECK(type == Type::DB_PURGE_MATCHING || type == Type::DB_PURGE_STALE);
+}
+
+TestDatabaseOperationReceiver::DBOperation::~DBOperation() = default;
+
+TestDatabaseOperationReceiver::DBOperation::DBOperation(const DBOperation&) =
+ default;
+
+bool TestDatabaseOperationReceiver::DBOperation::operator==(
+ const DBOperation& operation) const {
+ if (type != operation.type || params != operation.params)
+ return false;
+
+ if (origin.opaque() && operation.origin.opaque())
+ return true;
+
+ return origin == operation.origin;
+}
+
+bool TestDatabaseOperationReceiver::DBOperation::operator!=(
+ const DBOperation& operation) const {
+ if (type != operation.type || params != operation.params)
+ return true;
+
+ if (origin.opaque() && operation.origin.opaque())
+ return false;
+
+ return origin != operation.origin;
+}
+
+std::string TestDatabaseOperationReceiver::DBOperation::Serialize() const {
+ std::string serialization(
+ base::StrCat({"type: ", base::NumberToString(static_cast<int>(type)),
+ "; origin: ", origin.Serialize(), "; params: {"}));
+ for (int i = 0; i < static_cast<int>(params.size()) - 1; i++) {
+ serialization =
+ base::StrCat({serialization, base::UTF16ToUTF8(params[i]), ","});
+ }
+ serialization = params.empty()
+ ? base::StrCat({serialization, "}"})
+ : base::StrCat({serialization,
+ base::UTF16ToUTF8(params.back()), "}"});
+ return serialization;
+}
+
+TestDatabaseOperationReceiver::TestDatabaseOperationReceiver() = default;
+
+TestDatabaseOperationReceiver::~TestDatabaseOperationReceiver() = default;
+
+// static
+std::u16string TestDatabaseOperationReceiver::SerializeTime(base::Time time) {
+ return SerializeTimeDelta(time.ToDeltaSinceWindowsEpoch());
+}
+
+// static
+
+std::u16string TestDatabaseOperationReceiver::SerializeTimeDelta(
+ base::TimeDelta delta) {
+ return base::StrCat({base::NumberToString16(delta.InMicroseconds()), u"us"});
+}
+
+// static
+std::u16string TestDatabaseOperationReceiver::SerializeBool(bool b) {
+ return b ? u"true" : u"false";
+}
+
+// static
+std::u16string TestDatabaseOperationReceiver::SerializeSetBehavior(
+ SetBehavior behavior) {
+ return base::NumberToString16(static_cast<int>(behavior));
+}
+
+void TestDatabaseOperationReceiver::WaitForOperations() {
+ finished_ = false;
+ loop_.Run();
+ if (expected_operations_.empty())
+ Finish();
+}
+
+void TestDatabaseOperationReceiver::GetResultCallbackBase(
+ const DBOperation& current_operation,
+ GetResult* out_result,
+ GetResult result) {
+ DCHECK(out_result);
+ *out_result = std::move(result);
+
+ if (ExpectationsMet(current_operation) && loop_.running())
+ Finish();
+}
+
+base::OnceCallback<void(GetResult)>
+TestDatabaseOperationReceiver::MakeGetResultCallback(
+ const DBOperation& current_operation,
+ GetResult* out_result) {
+ return base::BindOnce(&TestDatabaseOperationReceiver::GetResultCallbackBase,
+ base::Unretained(this), current_operation, out_result);
+}
+
+void TestDatabaseOperationReceiver::OperationResultCallbackBase(
+ const DBOperation& current_operation,
+ OperationResult* out_result,
+ OperationResult result) {
+ DCHECK(out_result);
+ *out_result = result;
+
+ if (ExpectationsMet(current_operation) && loop_.running())
+ Finish();
+}
+
+base::OnceCallback<void(OperationResult)>
+TestDatabaseOperationReceiver::MakeOperationResultCallback(
+ const DBOperation& current_operation,
+ OperationResult* out_result) {
+ return base::BindOnce(
+ &TestDatabaseOperationReceiver::OperationResultCallbackBase,
+ base::Unretained(this), current_operation, out_result);
+}
+
+void TestDatabaseOperationReceiver::IntCallbackBase(
+ const DBOperation& current_operation,
+ int* out_length,
+ int length) {
+ DCHECK(out_length);
+ *out_length = length;
+
+ if (ExpectationsMet(current_operation) && loop_.running())
+ Finish();
+}
+
+base::OnceCallback<void(int)> TestDatabaseOperationReceiver::MakeIntCallback(
+ const DBOperation& current_operation,
+ int* out_length) {
+ return base::BindOnce(&TestDatabaseOperationReceiver::IntCallbackBase,
+ base::Unretained(this), current_operation, out_length);
+}
+
+void TestDatabaseOperationReceiver::BoolCallbackBase(
+ const DBOperation& current_operation,
+ bool* out_boolean,
+ bool boolean) {
+ DCHECK(out_boolean);
+ *out_boolean = boolean;
+
+ if (ExpectationsMet(current_operation) && loop_.running())
+ Finish();
+}
+
+base::OnceCallback<void(bool)> TestDatabaseOperationReceiver::MakeBoolCallback(
+ const DBOperation& current_operation,
+ bool* out_boolean) {
+ return base::BindOnce(&TestDatabaseOperationReceiver::BoolCallbackBase,
+ base::Unretained(this), current_operation, out_boolean);
+}
+
+void TestDatabaseOperationReceiver::StatusCallbackBase(
+ const DBOperation& current_operation,
+ InitStatus* out_status,
+ InitStatus status) {
+ DCHECK(out_status);
+ *out_status = status;
+
+ if (ExpectationsMet(current_operation) && loop_.running())
+ Finish();
+}
+
+base::OnceCallback<void(InitStatus)>
+TestDatabaseOperationReceiver::MakeStatusCallback(
+ const DBOperation& current_operation,
+ InitStatus* out_status) {
+ return base::BindOnce(&TestDatabaseOperationReceiver::StatusCallbackBase,
+ base::Unretained(this), current_operation, out_status);
+}
+
+void TestDatabaseOperationReceiver::InfosCallbackBase(
+ const DBOperation& current_operation,
+ std::vector<mojom::StorageUsageInfoPtr>* out_infos,
+ std::vector<mojom::StorageUsageInfoPtr> infos) {
+ DCHECK(out_infos);
+ *out_infos = std::move(infos);
+
+ if (ExpectationsMet(current_operation) && loop_.running())
+ Finish();
+}
+
+base::OnceCallback<void(std::vector<mojom::StorageUsageInfoPtr>)>
+TestDatabaseOperationReceiver::MakeInfosCallback(
+ const DBOperation& current_operation,
+ std::vector<mojom::StorageUsageInfoPtr>* out_infos) {
+ return base::BindOnce(&TestDatabaseOperationReceiver::InfosCallbackBase,
+ base::Unretained(this), current_operation, out_infos);
+}
+
+void TestDatabaseOperationReceiver::OnceClosureBase(
+ const DBOperation& current_operation) {
+ if (ExpectationsMet(current_operation) && loop_.running())
+ Finish();
+}
+
+base::OnceClosure TestDatabaseOperationReceiver::MakeOnceClosure(
+ const DBOperation& current_operation) {
+ return base::BindOnce(&TestDatabaseOperationReceiver::OnceClosureBase,
+ base::Unretained(this), current_operation);
+}
+
+bool TestDatabaseOperationReceiver::ExpectationsMet(
+ const DBOperation& current_operation) {
+ EXPECT_FALSE(expected_operations_.empty());
+
+ if (expected_operations_.empty())
+ return false;
+
+ EXPECT_EQ(expected_operations_.front(), current_operation)
+ << "expected operation: " << expected_operations_.front().Serialize()
+ << std::endl
+ << "actual operation: " << current_operation.Serialize() << std::endl;
+
+ if (expected_operations_.front() != current_operation) {
+ return false;
+ } else {
+ expected_operations_.pop();
+ return expected_operations_.empty();
+ }
+}
+
+void TestDatabaseOperationReceiver::Finish() {
+ finished_ = true;
+ loop_.Quit();
+}
+
+OriginMatcherFunctionUtility::OriginMatcherFunctionUtility() = default;
+OriginMatcherFunctionUtility::~OriginMatcherFunctionUtility() = default;
+
+OriginMatcherFunction OriginMatcherFunctionUtility::MakeMatcherFunction(
+ std::vector<url::Origin> origins_to_match) {
+ return base::BindRepeating(
+ [](std::vector<url::Origin> origins_to_match, const url::Origin& origin,
+ SpecialStoragePolicy* policy) {
+ return base::Contains(origins_to_match, origin);
+ },
+ origins_to_match);
+}
+
+OriginMatcherFunction OriginMatcherFunctionUtility::MakeMatcherFunction(
+ std::vector<std::string> origin_strs_to_match) {
+ std::vector<url::Origin> origins_to_match;
+ for (const auto& str : origin_strs_to_match)
+ origins_to_match.push_back(url::Origin::Create(GURL(str)));
+ return MakeMatcherFunction(origins_to_match);
+}
+
+size_t OriginMatcherFunctionUtility::RegisterMatcherFunction(
+ std::vector<url::Origin> origins_to_match) {
+ matcher_table_.emplace_back(MakeMatcherFunction(origins_to_match));
+ return matcher_table_.size() - 1;
+}
+
+OriginMatcherFunction OriginMatcherFunctionUtility::TakeMatcherFunctionForId(
+ size_t id) {
+ DCHECK_LT(id, matcher_table_.size());
+ return std::move(matcher_table_[id]);
+}
+
+std::vector<SharedStorageWrappedBool> GetSharedStorageWrappedBools() {
+ return std::vector<SharedStorageWrappedBool>({{true}, {false}});
+}
+
+std::string PrintToString(const SharedStorageWrappedBool& b) {
+ return b.in_memory_only ? "InMemoryOnly" : "FileBacked";
+}
+
+std::vector<PurgeMatchingOriginsParams> GetPurgeMatchingOriginsParams() {
+ return std::vector<PurgeMatchingOriginsParams>(
+ {{true, true}, {true, false}, {false, true}, {false, false}});
+}
+
+std::string PrintToString(const PurgeMatchingOriginsParams& p) {
+ return base::StrCat({(p.in_memory_only ? "InMemoryOnly" : "FileBacked"),
+ "_With", (p.perform_storage_cleanup ? "" : "out"),
+ "Cleanup"});
+}
+
+void VerifySharedStorageTablesAndColumns(sql::Database& db) {
+ // `meta`, `values_mapping`, and `per_origin_mapping`.
+ EXPECT_EQ(3u, sql::test::CountSQLTables(&db));
+
+ // Implicit index on `meta` and `per_origin_mapping_last_used_time_idx`.
+ EXPECT_EQ(2u, sql::test::CountSQLIndices(&db));
+
+ // `key` and `value`.
+ EXPECT_EQ(2u, sql::test::CountTableColumns(&db, "meta"));
+
+ // `context_origin`, `script_key`, and `script_value`.
+ EXPECT_EQ(3u, sql::test::CountTableColumns(&db, "values_mapping"));
+
+ // `context_origin`, `last_used_time`, and `length`.
+ EXPECT_EQ(3u, sql::test::CountTableColumns(&db, "per_origin_mapping"));
+}
+
+bool GetTestDataSharedStorageDir(base::FilePath* dir) {
+ if (!base::PathService::Get(base::DIR_SOURCE_ROOT, dir))
+ return false;
+ *dir = dir->AppendASCII("components");
+ *dir = dir->AppendASCII("test");
+ *dir = dir->AppendASCII("data");
+ *dir = dir->AppendASCII("storage");
+ return true;
+}
+
+bool CreateDatabaseFromSQL(const base::FilePath& db_path,
+ const char* ascii_path) {
+ base::FilePath dir;
+ if (!GetTestDataSharedStorageDir(&dir))
+ return false;
+ return sql::test::CreateDatabaseFromSQL(db_path, dir.AppendASCII(ascii_path));
+}
+
+} // namespace storage
diff --git a/chromium/components/services/storage/shared_storage/shared_storage_test_utils.h b/chromium/components/services/storage/shared_storage/shared_storage_test_utils.h
new file mode 100644
index 00000000000..b518d06321f
--- /dev/null
+++ b/chromium/components/services/storage/shared_storage/shared_storage_test_utils.h
@@ -0,0 +1,210 @@
+// Copyright 2021 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.
+
+#ifndef COMPONENTS_SERVICES_STORAGE_SHARED_STORAGE_SHARED_STORAGE_TEST_UTILS_H_
+#define COMPONENTS_SERVICES_STORAGE_SHARED_STORAGE_SHARED_STORAGE_TEST_UTILS_H_
+
+#include <queue>
+#include <string>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "components/services/storage/public/mojom/storage_usage_info.mojom-forward.h"
+#include "components/services/storage/shared_storage/shared_storage_database.h"
+#include "url/origin.h"
+
+namespace base {
+class FilePath;
+} // namespace base
+
+namespace sql {
+class Database;
+} // namespace sql
+
+namespace storage {
+
+using OriginMatcherFunction = SharedStorageDatabase::OriginMatcherFunction;
+using InitStatus = SharedStorageDatabase::InitStatus;
+using SetBehavior = SharedStorageDatabase::SetBehavior;
+using OperationResult = SharedStorageDatabase::OperationResult;
+using GetResult = SharedStorageDatabase::GetResult;
+
+// Helper class for testing async operations, accessible here for unit tests
+// of both `AsyncSharedStorageDatabase` and `SharedStorageManager`.
+class TestDatabaseOperationReceiver {
+ public:
+ struct DBOperation {
+ enum class Type {
+ DB_DESTROY = 0,
+ DB_TRIM_MEMORY = 1,
+ DB_GET = 2,
+ DB_SET = 3,
+ DB_APPEND = 4,
+ DB_DELETE = 5,
+ DB_CLEAR = 6,
+ DB_LENGTH = 7,
+ DB_KEY = 8,
+ DB_PURGE_MATCHING = 9,
+ DB_PURGE_STALE = 10,
+ DB_FETCH_ORIGINS = 11,
+ DB_IS_OPEN = 12,
+ DB_STATUS = 13,
+ DB_OVERRIDE_TIME = 14,
+ } type;
+ url::Origin origin;
+ std::vector<std::u16string> params;
+ explicit DBOperation(Type type);
+ DBOperation(Type type, url::Origin origin);
+ DBOperation(Type type,
+ url::Origin origin,
+ std::vector<std::u16string> params);
+ DBOperation(Type type, std::vector<std::u16string> params);
+ DBOperation(const DBOperation&);
+ ~DBOperation();
+ bool operator==(const DBOperation& operation) const;
+ bool operator!=(const DBOperation& operation) const;
+ std::string Serialize() const;
+ };
+
+ TestDatabaseOperationReceiver();
+
+ ~TestDatabaseOperationReceiver();
+
+ // For serializing parameters to insert into `params` when creating a
+ // `DBOperation` struct.
+ static std::u16string SerializeTime(base::Time time);
+ static std::u16string SerializeTimeDelta(base::TimeDelta delta);
+ static std::u16string SerializeBool(bool b);
+ static std::u16string SerializeSetBehavior(SetBehavior behavior);
+
+ bool is_finished() const { return finished_; }
+
+ void set_expected_operations(std::queue<DBOperation> expected_operations) {
+ expected_operations_ = std::move(expected_operations);
+ }
+
+ void WaitForOperations();
+
+ void GetResultCallbackBase(const DBOperation& current_operation,
+ GetResult* out_result,
+ GetResult result);
+ base::OnceCallback<void(GetResult)> MakeGetResultCallback(
+ const DBOperation& current_operation,
+ GetResult* out_result);
+
+ void OperationResultCallbackBase(const DBOperation& current_operation,
+ OperationResult* out_result,
+ OperationResult result);
+ base::OnceCallback<void(OperationResult)> MakeOperationResultCallback(
+ const DBOperation& current_operation,
+ OperationResult* out_result);
+
+ void IntCallbackBase(const DBOperation& current_operation,
+ int* out_length,
+ int length);
+ base::OnceCallback<void(int)> MakeIntCallback(
+ const DBOperation& current_operation,
+ int* out_length);
+
+ void BoolCallbackBase(const DBOperation& current_operation,
+ bool* out_boolean,
+ bool boolean);
+ base::OnceCallback<void(bool)> MakeBoolCallback(
+ const DBOperation& current_operation,
+ bool* out_boolean);
+
+ void StatusCallbackBase(const DBOperation& current_operation,
+ InitStatus* out_status,
+ InitStatus status);
+ base::OnceCallback<void(InitStatus)> MakeStatusCallback(
+ const DBOperation& current_operation,
+ InitStatus* out_status);
+
+ void InfosCallbackBase(const DBOperation& current_operation,
+ std::vector<mojom::StorageUsageInfoPtr>* out_infos,
+ std::vector<mojom::StorageUsageInfoPtr> infos);
+ base::OnceCallback<void(std::vector<mojom::StorageUsageInfoPtr>)>
+ MakeInfosCallback(const DBOperation& current_operation,
+ std::vector<mojom::StorageUsageInfoPtr>* out_infos);
+
+ void OnceClosureBase(const DBOperation& current_operation);
+ base::OnceClosure MakeOnceClosure(const DBOperation& current_operation);
+
+ private:
+ bool ExpectationsMet(const DBOperation& current_operation);
+ void Finish();
+
+ base::RunLoop loop_;
+ bool finished_ = true;
+ std::queue<DBOperation> expected_operations_;
+};
+
+class OriginMatcherFunctionUtility {
+ public:
+ OriginMatcherFunctionUtility();
+ ~OriginMatcherFunctionUtility();
+
+ [[nodiscard]] static OriginMatcherFunction MakeMatcherFunction(
+ std::vector<url::Origin> origins_to_match);
+
+ [[nodiscard]] static OriginMatcherFunction MakeMatcherFunction(
+ std::vector<std::string> origin_strs_to_match);
+
+ [[nodiscard]] size_t RegisterMatcherFunction(
+ std::vector<url::Origin> origins_to_match);
+
+ [[nodiscard]] OriginMatcherFunction TakeMatcherFunctionForId(size_t id);
+
+ [[nodiscard]] bool is_empty() const { return matcher_table_.empty(); }
+
+ [[nodiscard]] size_t size() const { return matcher_table_.size(); }
+
+ private:
+ std::vector<OriginMatcherFunction> matcher_table_;
+};
+
+// Wraps a bool indicating if the database is in memory only,
+// for the purpose of customizing a `PrintToString()` method below, which will
+// be used in the parameterized test names via
+// `testing::PrintToStringParamName()`.
+struct SharedStorageWrappedBool {
+ bool in_memory_only;
+};
+
+// Wraps bools indicating if the database is in memory only and if storage
+// cleanup should be performed after purging, for the purpose of customizing a
+// `PrintToString()` method below, which will be used in the parameterized test
+// names via `testing::PrintToStringParamName()`.
+struct PurgeMatchingOriginsParams {
+ bool in_memory_only;
+ bool perform_storage_cleanup;
+};
+
+[[nodiscard]] std::vector<SharedStorageWrappedBool>
+GetSharedStorageWrappedBools();
+
+// Used by `testing::PrintToStringParamName()`.
+[[nodiscard]] std::string PrintToString(const SharedStorageWrappedBool& b);
+
+[[nodiscard]] std::vector<PurgeMatchingOriginsParams>
+GetPurgeMatchingOriginsParams();
+
+// Used by testing::PrintToStringParamName().
+[[nodiscard]] std::string PrintToString(const PurgeMatchingOriginsParams& p);
+
+// Verify that the up-to-date SQL Shared Storage database has the expected
+// tables and columns. Functional tests only check whether the things which
+// should be there are, but do not check if extraneous items are
+// present. Any extraneous items have the potential to interact
+// negatively with future schema changes.
+void VerifySharedStorageTablesAndColumns(sql::Database& db);
+
+[[nodiscard]] bool GetTestDataSharedStorageDir(base::FilePath* dir);
+
+[[nodiscard]] bool CreateDatabaseFromSQL(const base::FilePath& db_path,
+ const char* ascii_path);
+
+} // namespace storage
+
+#endif // COMPONENTS_SERVICES_STORAGE_SHARED_STORAGE_SHARED_STORAGE_TEST_UTILS_H_
diff --git a/chromium/components/services/storage/storage_service_impl.cc b/chromium/components/services/storage/storage_service_impl.cc
index d119afafeb4..d848fb3c480 100644
--- a/chromium/components/services/storage/storage_service_impl.cc
+++ b/chromium/components/services/storage/storage_service_impl.cc
@@ -25,7 +25,7 @@ namespace {
// We don't use out-of-process Storage Service on Android, so we can avoid
// pulling all the related code (including Directory mojom) into the build.
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
// The name under which we register our own sandboxed VFS instance when running
// out-of-process.
constexpr char kVfsName[] = "storage_service";
@@ -61,7 +61,7 @@ void StorageServiceImpl::EnableAggressiveDomStorageFlushing() {
StorageAreaImpl::EnableAggressiveCommitDelay();
}
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
void StorageServiceImpl::SetDataDirectory(
const base::FilePath& path,
mojo::PendingRemote<mojom::Directory> directory) {
@@ -91,7 +91,7 @@ void StorageServiceImpl::SetDataDirectory(
kVfsName, std::make_unique<SandboxedVfsDelegate>(CreateFilesystemProxy()),
/*make_default=*/true);
}
-#endif // !defined(OS_ANDROID)
+#endif // !BUILDFLAG(IS_ANDROID)
void StorageServiceImpl::BindPartition(
const absl::optional<base::FilePath>& path,
@@ -132,7 +132,7 @@ void StorageServiceImpl::RemovePartition(PartitionImpl* partition) {
partitions_.erase(iter);
}
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
void StorageServiceImpl::BindDataDirectoryReceiver(
mojo::PendingReceiver<mojom::Directory> receiver) {
DCHECK(remote_data_directory_.is_bound());
diff --git a/chromium/components/services/storage/storage_service_impl.h b/chromium/components/services/storage/storage_service_impl.h
index 76838adc62c..e02fc94c2f8 100644
--- a/chromium/components/services/storage/storage_service_impl.h
+++ b/chromium/components/services/storage/storage_service_impl.h
@@ -45,7 +45,7 @@ class StorageServiceImpl : public mojom::StorageService {
// mojom::StorageService implementation:
void EnableAggressiveDomStorageFlushing() override;
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
void SetDataDirectory(
const base::FilePath& path,
mojo::PendingRemote<mojom::Directory> directory) override;
@@ -60,7 +60,7 @@ class StorageServiceImpl : public mojom::StorageService {
// Removes a partition from the set of tracked partitions.
void RemovePartition(PartitionImpl* partition);
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
// Binds a Directory receiver to the same remote implementation to which
// |remote_data_directory_| is bound. It is invalid to call this when
// |remote_data_directory_| is unbound.
@@ -71,7 +71,7 @@ class StorageServiceImpl : public mojom::StorageService {
const mojo::Receiver<mojom::StorageService> receiver_;
const scoped_refptr<base::SequencedTaskRunner> io_task_runner_;
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
// If bound, the service will assume it should not perform certain filesystem
// operations directly and will instead go through this interface.
base::FilePath remote_data_directory_path_;
diff --git a/chromium/components/services/unzip/BUILD.gn b/chromium/components/services/unzip/BUILD.gn
index d2648aecb43..a21e47cabef 100644
--- a/chromium/components/services/unzip/BUILD.gn
+++ b/chromium/components/services/unzip/BUILD.gn
@@ -11,6 +11,7 @@ source_set("lib") {
deps = [
"//base",
"//mojo/public/cpp/bindings",
+ "//third_party/ced",
"//third_party/zlib/google:zip",
]
@@ -45,6 +46,21 @@ bundle_data("unit_tests_bundle_data") {
visibility = [ ":unit_tests" ]
testonly = true
sources = [
+ "//components/test/data/unzip_service/SJIS 00.zip",
+ "//components/test/data/unzip_service/SJIS 01.zip",
+ "//components/test/data/unzip_service/SJIS 02.zip",
+ "//components/test/data/unzip_service/SJIS 03.zip",
+ "//components/test/data/unzip_service/SJIS 04.zip",
+ "//components/test/data/unzip_service/SJIS 05.zip",
+ "//components/test/data/unzip_service/SJIS 06.zip",
+ "//components/test/data/unzip_service/SJIS 07.zip",
+ "//components/test/data/unzip_service/SJIS 08.zip",
+ "//components/test/data/unzip_service/SJIS 09.zip",
+ "//components/test/data/unzip_service/SJIS 10.zip",
+ "//components/test/data/unzip_service/SJIS 11.zip",
+ "//components/test/data/unzip_service/SJIS 12.zip",
+ "//components/test/data/unzip_service/SJIS 13.zip",
+ "//components/test/data/unzip_service/UTF8 (Bug 903664).zip",
"//components/test/data/unzip_service/bad_archive.zip",
"//components/test/data/unzip_service/good_archive.zip",
]
diff --git a/chromium/components/services/unzip/DEPS b/chromium/components/services/unzip/DEPS
index c182c3ca877..c84c5757b14 100644
--- a/chromium/components/services/unzip/DEPS
+++ b/chromium/components/services/unzip/DEPS
@@ -1,5 +1,6 @@
include_rules = [
"+components/services/filesystem",
"+mojo/public",
+ "+third_party/ced",
"+third_party/zlib/google",
]
diff --git a/chromium/components/services/unzip/OWNERS b/chromium/components/services/unzip/OWNERS
index ec81839d0ad..76d860e1f41 100644
--- a/chromium/components/services/unzip/OWNERS
+++ b/chromium/components/services/unzip/OWNERS
@@ -1,2 +1,5 @@
+adanilo@chromium.org
+fdegros@chromium.org
+noel@chromium.org
sorin@chromium.org
waffles@chromium.org
diff --git a/chromium/components/services/unzip/public/cpp/unzip.cc b/chromium/components/services/unzip/public/cpp/unzip.cc
index b2b929ca7c1..d487f1f2393 100644
--- a/chromium/components/services/unzip/public/cpp/unzip.cc
+++ b/chromium/components/services/unzip/public/cpp/unzip.cc
@@ -11,12 +11,15 @@
#include "base/callback.h"
#include "base/files/file.h"
#include "base/files/file_path.h"
+#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_refptr.h"
+#include "base/metrics/histogram_functions.h"
#include "base/task/post_task.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/thread_pool.h"
#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/time/time.h"
#include "components/services/filesystem/directory_impl.h"
#include "components/services/filesystem/lock_table.h"
#include "components/services/unzip/public/mojom/unzipper.mojom.h"
@@ -25,9 +28,12 @@
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
namespace unzip {
-
namespace {
+std::string Redact(const base::FilePath& path) {
+ return LOG_IS_ON(INFO) ? "'" + path.AsUTF8Unsafe() + "'" : "(redacted)";
+}
+
class UnzipFilter : public unzip::mojom::UnzipFilter {
public:
UnzipFilter(mojo::PendingReceiver<unzip::mojom::UnzipFilter> receiver,
@@ -72,6 +78,8 @@ class UnzipParams : public base::RefCounted<UnzipParams> {
callback_task_runner_->PostTask(
FROM_HERE, base::BindOnce(std::move(callback_), result));
}
+
+ unzipper_.reset();
}
void set_unzip_filter(std::unique_ptr<UnzipFilter> filter) {
@@ -94,12 +102,52 @@ class UnzipParams : public base::RefCounted<UnzipParams> {
scoped_refptr<base::SequencedTaskRunner> background_task_runner_keep_alive_;
};
-void UnzipDone(scoped_refptr<UnzipParams> params, bool success) {
- params->InvokeCallback(success);
- params->unzipper().reset();
-}
+class DetectEncodingParams : public base::RefCounted<DetectEncodingParams> {
+ public:
+ DetectEncodingParams(
+ mojo::PendingRemote<mojom::Unzipper> unzipper,
+ DetectEncodingCallback callback,
+ scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
+ scoped_refptr<base::SequencedTaskRunner> background_task_runner)
+ : unzipper_(std::move(unzipper)),
+ callback_(std::move(callback)),
+ callback_task_runner_(std::move(callback_task_runner)),
+ background_task_runner_(std::move(background_task_runner)) {}
+
+ DetectEncodingParams(const DetectEncodingParams&) = delete;
+ DetectEncodingParams& operator=(const DetectEncodingParams&) = delete;
+
+ mojo::Remote<mojom::Unzipper>& unzipper() { return unzipper_; }
+
+ void InvokeCallback(int encoding) {
+ if (callback_) {
+ base::UmaHistogramEnumeration("Unzipper.DetectEncoding.Result",
+ static_cast<Encoding>(encoding),
+ Encoding::NUM_ENCODINGS);
+ base::UmaHistogramTimes("Unzipper.DetectEncoding.Time",
+ base::TimeTicks::Now() - start_time_);
+ callback_task_runner_->PostTask(
+ FROM_HERE, base::BindOnce(std::move(callback_),
+ static_cast<Encoding>(encoding)));
+ }
+
+ unzipper_.reset();
+ }
+
+ private:
+ friend class base::RefCounted<DetectEncodingParams>;
+
+ ~DetectEncodingParams() = default;
+
+ // The Remote is stored so it does not get deleted before the callback runs.
+ mojo::Remote<mojom::Unzipper> unzipper_;
+ DetectEncodingCallback callback_;
+ scoped_refptr<base::SequencedTaskRunner> callback_task_runner_;
+ scoped_refptr<base::SequencedTaskRunner> background_task_runner_;
+ const base::TimeTicks start_time_ = base::TimeTicks::Now();
+};
-void DoUnzipWithFilter(
+void DoUnzip(
mojo::PendingRemote<mojom::Unzipper> unzipper,
const base::FilePath& zip_path,
const base::FilePath& output_dir,
@@ -129,22 +177,50 @@ void DoUnzipWithFilter(
background_task_runner_keep_alive);
unzip_params->unzipper().set_disconnect_handler(
- base::BindOnce(&UnzipDone, unzip_params, false));
-
- if (filter_callback.is_null()) {
- unzip_params->unzipper()->Unzip(std::move(zip_file),
- std::move(directory_remote),
- base::BindOnce(&UnzipDone, unzip_params));
- return;
- }
+ base::BindOnce(&UnzipParams::InvokeCallback, unzip_params, false));
mojo::PendingRemote<unzip::mojom::UnzipFilter> unzip_filter_remote;
- unzip_params->set_unzip_filter(std::make_unique<UnzipFilter>(
- unzip_filter_remote.InitWithNewPipeAndPassReceiver(), filter_callback));
+ if (filter_callback) {
+ unzip_params->set_unzip_filter(std::make_unique<UnzipFilter>(
+ unzip_filter_remote.InitWithNewPipeAndPassReceiver(),
+ std::move(filter_callback)));
+ }
- unzip_params->unzipper()->UnzipWithFilter(
+ unzip_params->unzipper()->Unzip(
std::move(zip_file), std::move(directory_remote),
- std::move(unzip_filter_remote), base::BindOnce(&UnzipDone, unzip_params));
+ std::move(unzip_filter_remote),
+ base::BindOnce(&UnzipParams::InvokeCallback, unzip_params));
+}
+
+void DoDetectEncoding(
+ mojo::PendingRemote<mojom::Unzipper> unzipper,
+ const base::FilePath& zip_path,
+ DetectEncodingCallback result_callback,
+ scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
+ scoped_refptr<base::SequencedTaskRunner> background_task_runner) {
+ base::File zip_file(zip_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
+ if (!zip_file.IsValid()) {
+ LOG(ERROR) << "Cannot open ZIP archive " << Redact(zip_path) << ": "
+ << base::File::ErrorToString(zip_file.error_details());
+ callback_task_runner->PostTask(
+ FROM_HERE,
+ base::BindOnce(std::move(result_callback), UNKNOWN_ENCODING));
+ return;
+ }
+
+ // |result_callback| is shared between the connection error handler and the
+ // DetectEncoding call using a refcounted DetectEncodingParams object that
+ // owns |result_callback|.
+ auto params = base::MakeRefCounted<DetectEncodingParams>(
+ std::move(unzipper), std::move(result_callback),
+ std::move(callback_task_runner), std::move(background_task_runner));
+
+ params->unzipper().set_disconnect_handler(base::BindOnce(
+ &DetectEncodingParams::InvokeCallback, params, UNKNOWN_ENCODING));
+
+ params->unzipper()->DetectEncoding(
+ std::move(zip_file),
+ base::BindOnce(&DetectEncodingParams::InvokeCallback, params));
}
} // namespace
@@ -170,10 +246,23 @@ void UnzipWithFilter(mojo::PendingRemote<mojom::Unzipper> unzipper,
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN});
background_task_runner->PostTask(
FROM_HERE,
- base::BindOnce(&DoUnzipWithFilter, std::move(unzipper), zip_path,
- output_dir, base::SequencedTaskRunnerHandle::Get(),
- filter_callback, std::move(result_callback),
- background_task_runner));
+ base::BindOnce(&DoUnzip, std::move(unzipper), zip_path, output_dir,
+ base::SequencedTaskRunnerHandle::Get(), filter_callback,
+ std::move(result_callback), background_task_runner));
+}
+
+void DetectEncoding(mojo::PendingRemote<mojom::Unzipper> unzipper,
+ const base::FilePath& zip_path,
+ DetectEncodingCallback result_callback) {
+ scoped_refptr<base::SequencedTaskRunner> background_task_runner =
+ base::ThreadPool::CreateSequencedTaskRunner(
+ {base::TaskPriority::USER_VISIBLE, base::MayBlock(),
+ base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN});
+ background_task_runner->PostTask(
+ FROM_HERE, base::BindOnce(&DoDetectEncoding, std::move(unzipper),
+ zip_path, std::move(result_callback),
+ base::SequencedTaskRunnerHandle::Get(),
+ background_task_runner));
}
} // namespace unzip
diff --git a/chromium/components/services/unzip/public/cpp/unzip.h b/chromium/components/services/unzip/public/cpp/unzip.h
index e62a4ac524d..1f4bbb0f203 100644
--- a/chromium/components/services/unzip/public/cpp/unzip.h
+++ b/chromium/components/services/unzip/public/cpp/unzip.h
@@ -8,6 +8,7 @@
#include "base/callback_forward.h"
#include "components/services/unzip/public/mojom/unzipper.mojom.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "third_party/ced/src/util/encodings/encodings.h"
namespace base {
class FilePath;
@@ -32,6 +33,11 @@ void UnzipWithFilter(mojo::PendingRemote<mojom::Unzipper> unzipper,
UnzipFilterCallback filter_callback,
UnzipCallback result_callback);
+using DetectEncodingCallback = base::OnceCallback<void(Encoding)>;
+void DetectEncoding(mojo::PendingRemote<mojom::Unzipper> unzipper,
+ const base::FilePath& zip_file,
+ DetectEncodingCallback result_callback);
+
} // namespace unzip
#endif // COMPONENTS_SERVICES_UNZIP_PUBLIC_CPP_UNZIP_H_
diff --git a/chromium/components/services/unzip/public/cpp/unzip_unittest.cc b/chromium/components/services/unzip/public/cpp/unzip_unittest.cc
index ccb9053207a..658301c4b44 100644
--- a/chromium/components/services/unzip/public/cpp/unzip_unittest.cc
+++ b/chromium/components/services/unzip/public/cpp/unzip_unittest.cc
@@ -4,33 +4,33 @@
#include "components/services/unzip/public/cpp/unzip.h"
+#include <utility>
+
#include "base/base_paths.h"
-#include "base/bind.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/path_service.h"
#include "base/run_loop.h"
+#include "base/test/bind.h"
#include "base/test/task_environment.h"
#include "components/services/unzip/unzipper_impl.h"
#include "mojo/public/cpp/bindings/receiver_set.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace unzip {
-
namespace {
-// Note: this method has to return void for the ASSERTION_* to compile.
-void GetArchivePath(const base::FilePath::StringPieceType& archive_name,
- base::FilePath* path) {
- ASSERT_TRUE(base::PathService::Get(base::DIR_SOURCE_ROOT, path));
- *path = path->Append(FILE_PATH_LITERAL("components"))
- .Append(FILE_PATH_LITERAL("test"))
- .Append(FILE_PATH_LITERAL("data"))
- .Append(FILE_PATH_LITERAL("unzip_service"))
- .Append(archive_name);
- ASSERT_TRUE(base::PathExists(*path));
+base::FilePath GetArchivePath(
+ const base::FilePath::StringPieceType archive_name) {
+ base::FilePath path;
+ EXPECT_TRUE(base::PathService::Get(base::DIR_SOURCE_ROOT, &path));
+ return path.Append(FILE_PATH_LITERAL("components"))
+ .Append(FILE_PATH_LITERAL("test"))
+ .Append(FILE_PATH_LITERAL("data"))
+ .Append(FILE_PATH_LITERAL("unzip_service"))
+ .Append(archive_name);
}
// Sets the number of files under |dir| in |file_count| and if
@@ -63,37 +63,44 @@ class UnzipTest : public testing::Test {
~UnzipTest() override = default;
// Unzips |zip_file| into |output_dir| and returns true if the unzip was
- // successful.
+ // successful. Only extract files for which |filter_callback| returns true, if
+ // it is provided.
bool DoUnzip(const base::FilePath& zip_file,
- const base::FilePath& output_dir) {
- return DoUnzipWithFilter(zip_file, output_dir, UnzipFilterCallback());
+ const base::FilePath& output_dir,
+ UnzipFilterCallback filter_callback = {}) {
+ mojo::PendingRemote<mojom::Unzipper> unzipper;
+ receivers_.Add(&unzipper_, unzipper.InitWithNewPipeAndPassReceiver());
+
+ base::RunLoop run_loop;
+ bool result = false;
+
+ UnzipCallback result_callback =
+ base::BindLambdaForTesting([&](const bool success) {
+ result = success;
+ run_loop.QuitClosure().Run();
+ });
+
+ UnzipWithFilter(std::move(unzipper), zip_file, output_dir,
+ std::move(filter_callback), std::move(result_callback));
+
+ run_loop.Run();
+ return result;
}
- // Same as DoUnzip() but only extract files for which |filter_callback|
- // returns true.
- bool DoUnzipWithFilter(const base::FilePath& zip_file,
- const base::FilePath& output_dir,
- UnzipFilterCallback filter_callback) {
+ Encoding DoDetectEncoding(const base::FilePath& zip_file) {
mojo::PendingRemote<mojom::Unzipper> unzipper;
receivers_.Add(&unzipper_, unzipper.InitWithNewPipeAndPassReceiver());
base::RunLoop run_loop;
- bool result = false;
+ Encoding result = UNKNOWN_ENCODING;
- UnzipCallback result_callback = base::BindOnce(
- [](base::OnceClosure quit_closure, bool* out_result, bool result) {
- *out_result = result;
- std::move(quit_closure).Run();
- },
- run_loop.QuitClosure(), &result);
-
- if (filter_callback) {
- UnzipWithFilter(std::move(unzipper), zip_file, output_dir,
- std::move(filter_callback), std::move(result_callback));
- } else {
- Unzip(std::move(unzipper), zip_file, output_dir,
- std::move(result_callback));
- }
+ DetectEncodingCallback result_callback =
+ base::BindLambdaForTesting([&](const Encoding encoding) {
+ result = encoding;
+ run_loop.QuitClosure().Run();
+ });
+
+ DetectEncoding(std::move(unzipper), zip_file, std::move(result_callback));
run_loop.Run();
return result;
}
@@ -114,9 +121,8 @@ class UnzipTest : public testing::Test {
};
TEST_F(UnzipTest, UnzipBadArchive) {
- base::FilePath bad_archive;
- GetArchivePath(FILE_PATH_LITERAL("bad_archive.zip"), &bad_archive);
- EXPECT_FALSE(DoUnzip(bad_archive, unzip_dir_));
+ EXPECT_FALSE(DoUnzip(GetArchivePath(FILE_PATH_LITERAL("bad_archive.zip")),
+ unzip_dir_));
// No files should have been extracted.
int64_t file_count = -1;
@@ -125,9 +131,8 @@ TEST_F(UnzipTest, UnzipBadArchive) {
}
TEST_F(UnzipTest, UnzipGoodArchive) {
- base::FilePath archive;
- GetArchivePath(FILE_PATH_LITERAL("good_archive.zip"), &archive);
- EXPECT_TRUE(DoUnzip(archive, unzip_dir_));
+ EXPECT_TRUE(DoUnzip(GetArchivePath(FILE_PATH_LITERAL("good_archive.zip")),
+ unzip_dir_));
// Sanity check that the right number of files have been extracted and that
// they are not empty.
@@ -139,13 +144,11 @@ TEST_F(UnzipTest, UnzipGoodArchive) {
}
TEST_F(UnzipTest, UnzipWithFilter) {
- base::FilePath archive;
- GetArchivePath(FILE_PATH_LITERAL("good_archive.zip"), &archive);
- EXPECT_TRUE(DoUnzipWithFilter(
- archive, unzip_dir_,
- base::BindRepeating([](const base::FilePath& path) -> bool {
- return path.MatchesExtension(FILE_PATH_LITERAL(".txt"));
- })));
+ EXPECT_TRUE(DoUnzip(GetArchivePath(FILE_PATH_LITERAL("good_archive.zip")),
+ unzip_dir_,
+ base::BindRepeating([](const base::FilePath& path) {
+ return path.MatchesExtension(FILE_PATH_LITERAL(".txt"));
+ })));
// It should only have kept the 2 text files from the archive.
int64_t file_count = -1;
@@ -155,5 +158,51 @@ TEST_F(UnzipTest, UnzipWithFilter) {
EXPECT_FALSE(some_files_empty);
}
+TEST_F(UnzipTest, DetectEncodingAbsentArchive) {
+ EXPECT_EQ(UNKNOWN_ENCODING, DoDetectEncoding(GetArchivePath(
+ FILE_PATH_LITERAL("absent_archive.zip"))));
+}
+
+TEST_F(UnzipTest, DetectEncodingBadArchive) {
+ EXPECT_EQ(
+ UNKNOWN_ENCODING,
+ DoDetectEncoding(GetArchivePath(FILE_PATH_LITERAL("bad_archive.zip"))));
+}
+
+TEST_F(UnzipTest, DetectEncodingAscii) {
+ EXPECT_EQ(
+ Encoding::ASCII_7BIT,
+ DoDetectEncoding(GetArchivePath(FILE_PATH_LITERAL("good_archive.zip"))));
+}
+
+// See https://crbug.com/903664
+TEST_F(UnzipTest, DetectEncodingUtf8) {
+ EXPECT_EQ(Encoding::UTF8, DoDetectEncoding(GetArchivePath(
+ FILE_PATH_LITERAL("UTF8 (Bug 903664).zip"))));
+}
+
+// See https://crbug.com/1287893
+TEST_F(UnzipTest, DetectEncodingSjis) {
+ for (const base::FilePath::StringPieceType name : {
+ FILE_PATH_LITERAL("SJIS 00.zip"),
+ FILE_PATH_LITERAL("SJIS 01.zip"),
+ FILE_PATH_LITERAL("SJIS 02.zip"),
+ FILE_PATH_LITERAL("SJIS 03.zip"),
+ FILE_PATH_LITERAL("SJIS 04.zip"),
+ FILE_PATH_LITERAL("SJIS 05.zip"),
+ FILE_PATH_LITERAL("SJIS 06.zip"),
+ FILE_PATH_LITERAL("SJIS 07.zip"),
+ FILE_PATH_LITERAL("SJIS 08.zip"),
+ FILE_PATH_LITERAL("SJIS 09.zip"),
+ FILE_PATH_LITERAL("SJIS 10.zip"),
+ FILE_PATH_LITERAL("SJIS 11.zip"),
+ FILE_PATH_LITERAL("SJIS 12.zip"),
+ FILE_PATH_LITERAL("SJIS 13.zip"),
+ }) {
+ EXPECT_EQ(Encoding::JAPANESE_SHIFT_JIS,
+ DoDetectEncoding(GetArchivePath(name)));
+ }
+}
+
} // namespace
} // namespace unzip
diff --git a/chromium/components/services/unzip/public/mojom/unzipper.mojom b/chromium/components/services/unzip/public/mojom/unzipper.mojom
index 00273934dd9..a16f891cd85 100644
--- a/chromium/components/services/unzip/public/mojom/unzipper.mojom
+++ b/chromium/components/services/unzip/public/mojom/unzipper.mojom
@@ -20,14 +20,17 @@ interface UnzipFilter {
interface Unzipper {
// Unzip |zip_file| into |output_dir|.
// Returns true on success, false otherwise.
- Unzip(mojo_base.mojom.ReadOnlyFile zip_file,
- pending_remote<filesystem.mojom.Directory> output_dir)
- => (bool result);
-
- // Same as |unzip| but only includes the files for which |filter| returns
- // true. Note that this incurs one IPC for each file of the archive.
- UnzipWithFilter(
+ // If provided, |filter| is called for each entry of the archive (which incurs
+ // one IPC for each entry) and only the entries for which it returns true are
+ // extracted.
+ Unzip(
mojo_base.mojom.ReadOnlyFile zip_file,
pending_remote<filesystem.mojom.Directory> output_dir,
- pending_remote<UnzipFilter> filter) => (bool result);
+ pending_remote<UnzipFilter>? filter) => (bool result);
+
+ // Detects the encoding of filenames stored in the ZIP archive.
+ // Returns an Encoding as defined in
+ // third_party/ced/src/util/encodings/encodings.pb.h
+ // or UNKNOWN_ENCODING in case of error.
+ DetectEncoding(mojo_base.mojom.ReadOnlyFile zip_file) => (int32 encoding);
};
diff --git a/chromium/components/services/unzip/unzipper_impl.cc b/chromium/components/services/unzip/unzipper_impl.cc
index b710ab8b2bf..10e1a68bfef 100644
--- a/chromium/components/services/unzip/unzipper_impl.cc
+++ b/chromium/components/services/unzip/unzipper_impl.cc
@@ -9,10 +9,13 @@
#include "base/bind.h"
#include "base/compiler_specific.h"
+#include "base/files/file.h"
+#include "base/logging.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "components/services/filesystem/public/mojom/directory.mojom.h"
#include "mojo/public/cpp/bindings/remote.h"
+#include "third_party/ced/src/compact_enc_det/compact_enc_det.h"
#include "third_party/zlib/google/zip.h"
#include "third_party/zlib/google/zip_reader.h"
@@ -20,24 +23,8 @@ namespace unzip {
namespace {
-// A writer delegate that reports errors instead of writing.
-class DudWriterDelegate : public zip::WriterDelegate {
- public:
- DudWriterDelegate() {}
-
- DudWriterDelegate(const DudWriterDelegate&) = delete;
- DudWriterDelegate& operator=(const DudWriterDelegate&) = delete;
-
- ~DudWriterDelegate() override {}
-
- // WriterDelegate methods:
- bool PrepareOutput() override { return false; }
- bool WriteBytes(const char* data, int num_bytes) override { return false; }
- void SetTimeModified(const base::Time& time) override {}
-};
-
std::string PathToMojoString(const base::FilePath& path) {
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
return base::WideToUTF8(path.value());
#else
return path.value();
@@ -64,7 +51,7 @@ std::unique_ptr<zip::WriterDelegate> MakeFileWriterDelegateNoParent(
filesystem::mojom::kFlagWriteAttributes,
&err, file.get()) ||
err != base::File::Error::FILE_OK) {
- return std::make_unique<DudWriterDelegate>();
+ return nullptr;
}
return std::make_unique<zip::FileWriterDelegate>(std::move(file));
}
@@ -80,22 +67,51 @@ std::unique_ptr<zip::WriterDelegate> MakeFileWriterDelegate(
parent.BindNewPipeAndPassReceiver(),
filesystem::mojom::kFlagOpenAlways, &err) ||
err != base::File::Error::FILE_OK) {
- return std::make_unique<DudWriterDelegate>();
+ return nullptr;
}
return MakeFileWriterDelegateNoParent(parent.get(), path.BaseName());
}
-bool FilterNoFiles(const base::FilePath& unused) {
- return true;
-}
-
-bool FilterWithFilterRemote(mojom::UnzipFilter* filter,
- const base::FilePath& path) {
+bool Filter(const mojo::Remote<mojom::UnzipFilter>& filter,
+ const base::FilePath& path) {
bool result = false;
filter->ShouldUnzipFile(path, &result);
return result;
}
+// Reads the given ZIP archive, and returns all the filenames concatenated
+// together in one long string capped at ~100KB, without any separator, and in
+// the encoding used by the ZIP archive itself. Returns an empty string if the
+// ZIP cannot be read.
+std::string GetRawFileNamesFromZip(const base::File& zip_file) {
+ std::string result;
+
+ // Open ZIP archive for reading.
+ zip::ZipReader reader;
+ if (!reader.OpenFromPlatformFile(zip_file.GetPlatformFile())) {
+ LOG(ERROR) << "Cannot decode ZIP archive";
+ return result;
+ }
+
+ // Reserve a ~100KB buffer.
+ result.reserve(100000);
+
+ // Iterate over file entries of the ZIP archive.
+ while (const zip::ZipReader::Entry* const entry = reader.Next()) {
+ const std::string& path = entry->path_in_original_encoding;
+
+ // Stop if we have enough data in |result|.
+ if (path.size() > (result.capacity() - result.size()))
+ break;
+
+ // Accumulate data in |result|.
+ result += path;
+ }
+
+ LOG_IF(ERROR, result.empty()) << "Cannot extract filenames from ZIP archive";
+ return result;
+}
+
} // namespace
UnzipperImpl::UnzipperImpl() = default;
@@ -108,38 +124,53 @@ UnzipperImpl::~UnzipperImpl() = default;
void UnzipperImpl::Unzip(
base::File zip_file,
mojo::PendingRemote<filesystem::mojom::Directory> output_dir_remote,
+ mojo::PendingRemote<mojom::UnzipFilter> filter_remote,
UnzipCallback callback) {
DCHECK(zip_file.IsValid());
mojo::Remote<filesystem::mojom::Directory> output_dir(
std::move(output_dir_remote));
- std::move(callback).Run(zip::UnzipWithFilterAndWriters(
- zip_file.GetPlatformFile(),
- base::BindRepeating(&MakeFileWriterDelegate, output_dir.get()),
- base::BindRepeating(&CreateDirectory, output_dir.get()),
- base::BindRepeating(&FilterNoFiles), /*log_skipped_files=*/false));
+ zip::FilterCallback filter_cb;
+ if (filter_remote) {
+ filter_cb = base::BindRepeating(
+ &Filter, mojo::Remote<mojom::UnzipFilter>(std::move(filter_remote)));
+ }
+
+ std::move(callback).Run(
+ zip::Unzip(zip_file.GetPlatformFile(),
+ base::BindRepeating(&MakeFileWriterDelegate, output_dir.get()),
+ base::BindRepeating(&CreateDirectory, output_dir.get()),
+ {.filter = std::move(filter_cb)}));
}
-void UnzipperImpl::UnzipWithFilter(
- base::File zip_file,
- mojo::PendingRemote<filesystem::mojom::Directory> output_dir_remote,
- mojo::PendingRemote<mojom::UnzipFilter> filter_remote,
- UnzipCallback callback) {
+void UnzipperImpl::DetectEncoding(base::File zip_file,
+ DetectEncodingCallback callback) {
DCHECK(zip_file.IsValid());
- mojo::Remote<filesystem::mojom::Directory> output_dir(
- std::move(output_dir_remote));
- mojo::Remote<mojom::UnzipFilter> filter(std::move(filter_remote));
-
- // Note that we pass a pointer to |filter| below, as it is a repeating
- // callback and transferring its value would cause the callback to fail when
- // called more than once. It is safe to pass a pointer as
- // UnzipWithFilterAndWriters is synchronous, so |filter| won't be used when
- // the method returns.
- std::move(callback).Run(zip::UnzipWithFilterAndWriters(
- zip_file.GetPlatformFile(),
- base::BindRepeating(&MakeFileWriterDelegate, output_dir.get()),
- base::BindRepeating(&CreateDirectory, output_dir.get()),
- base::BindRepeating(&FilterWithFilterRemote, filter.get()),
- /*log_skipped_files=*/false));
+
+ // Accumulate raw filenames.
+ const std::string all_names = GetRawFileNamesFromZip(zip_file);
+ if (all_names.empty()) {
+ std::move(callback).Run(UNKNOWN_ENCODING);
+ return;
+ }
+
+ // Detect encoding.
+ int consumed_bytes = 0;
+ bool is_reliable = false;
+ const Encoding encoding = CompactEncDet::DetectEncoding(
+ all_names.data(), all_names.size(), nullptr, nullptr, nullptr,
+ UNKNOWN_ENCODING, UNKNOWN_LANGUAGE,
+ CompactEncDet::QUERY_CORPUS, // Plain text
+ true, // Exclude 7-bit encodings
+ &consumed_bytes, &is_reliable);
+
+ VLOG(1) << "Detected encoding: " << MimeEncodingName(encoding) << " ("
+ << encoding << "), reliable: " << is_reliable
+ << ", consumed bytes: " << consumed_bytes;
+
+ LOG_IF(ERROR, encoding == UNKNOWN_ENCODING)
+ << "Cannot detect encoding of filenames in ZIP archive";
+
+ std::move(callback).Run(encoding);
}
} // namespace unzip
diff --git a/chromium/components/services/unzip/unzipper_impl.h b/chromium/components/services/unzip/unzipper_impl.h
index 330b716f910..6503523312a 100644
--- a/chromium/components/services/unzip/unzipper_impl.h
+++ b/chromium/components/services/unzip/unzipper_impl.h
@@ -32,13 +32,11 @@ class UnzipperImpl : public mojom::Unzipper {
void Unzip(
base::File zip_file,
mojo::PendingRemote<filesystem::mojom::Directory> output_dir_remote,
+ mojo::PendingRemote<mojom::UnzipFilter> filter_remote,
UnzipCallback callback) override;
- void UnzipWithFilter(
- base::File zip_file,
- mojo::PendingRemote<filesystem::mojom::Directory> output_dir_remote,
- mojo::PendingRemote<mojom::UnzipFilter> filter_remote,
- UnzipWithFilterCallback callback) override;
+ void DetectEncoding(base::File zip_file,
+ DetectEncodingCallback callback) override;
mojo::Receiver<mojom::Unzipper> receiver_{this};
};