| // Copyright 2018 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <set> |
| #include <sstream> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/bind.h" |
| #include "base/callback.h" |
| #include "base/files/scoped_temp_dir.h" |
| #include "base/run_loop.h" |
| #include "components/services/app_service/app_service_mojom_impl.h" |
| #include "components/services/app_service/public/cpp/app_capability_access_cache.h" |
| #include "components/services/app_service/public/cpp/intent_filter_util.h" |
| #include "components/services/app_service/public/cpp/intent_test_util.h" |
| #include "components/services/app_service/public/cpp/intent_util.h" |
| #include "components/services/app_service/public/cpp/preferred_apps_list.h" |
| #include "components/services/app_service/public/cpp/publisher_base.h" |
| #include "components/services/app_service/public/mojom/types.mojom.h" |
| #include "content/public/test/browser_task_environment.h" |
| #include "mojo/public/cpp/bindings/clone_traits.h" |
| #include "mojo/public/cpp/bindings/pending_receiver.h" |
| #include "mojo/public/cpp/bindings/pending_remote.h" |
| #include "mojo/public/cpp/bindings/receiver_set.h" |
| #include "mojo/public/cpp/bindings/remote.h" |
| #include "mojo/public/cpp/bindings/remote_set.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/abseil-cpp/absl/types/optional.h" |
| |
| namespace apps { |
| |
| class FakePublisher : public apps::PublisherBase { |
| public: |
| FakePublisher(AppServiceMojomImpl* impl, |
| apps::mojom::AppType app_type, |
| std::vector<std::string> initial_app_ids) |
| : app_type_(app_type), known_app_ids_(std::move(initial_app_ids)) { |
| mojo::PendingRemote<apps::mojom::Publisher> remote; |
| receivers_.Add(this, remote.InitWithNewPipeAndPassReceiver()); |
| impl->RegisterPublisher(std::move(remote), app_type_); |
| } |
| |
| void PublishMoreApps(std::vector<std::string> app_ids) { |
| for (auto& subscriber : subscribers_) { |
| CallOnApps(subscriber.get(), app_ids, /*uninstall=*/false); |
| } |
| for (const auto& app_id : app_ids) { |
| known_app_ids_.push_back(app_id); |
| } |
| } |
| |
| void ModifyCapabilityAccess(const std::string& app_id, |
| absl::optional<bool> accessing_camera, |
| absl::optional<bool> accessing_microphone) { |
| if (accessing_camera.has_value()) { |
| if (accessing_camera.value()) { |
| apps_accessing_camera_.insert(app_id); |
| } else { |
| apps_accessing_camera_.erase(app_id); |
| } |
| } |
| |
| if (accessing_microphone.has_value()) { |
| if (accessing_microphone.value()) { |
| apps_accessing_microphone_.insert(app_id); |
| } else { |
| apps_accessing_microphone_.erase(app_id); |
| } |
| } |
| |
| PublisherBase::ModifyCapabilityAccess( |
| subscribers_, app_id, accessing_camera, accessing_microphone); |
| } |
| |
| void UninstallApps(std::vector<std::string> app_ids, |
| AppServiceMojomImpl* impl) { |
| for (auto& subscriber : subscribers_) { |
| CallOnApps(subscriber.get(), app_ids, /*uninstall=*/true); |
| } |
| for (const auto& app_id : app_ids) { |
| known_app_ids_.push_back(app_id); |
| impl->RemovePreferredApp(app_type_, app_id); |
| } |
| } |
| |
| bool AppHasSupportedLinksPreference(const std::string& app_id) { |
| return supported_link_apps_.find(app_id) != supported_link_apps_.end(); |
| } |
| |
| std::string load_icon_app_id; |
| |
| private: |
| void Connect(mojo::PendingRemote<apps::mojom::Subscriber> subscriber_remote, |
| apps::mojom::ConnectOptionsPtr opts) override { |
| mojo::Remote<apps::mojom::Subscriber> subscriber( |
| std::move(subscriber_remote)); |
| CallOnApps(subscriber.get(), known_app_ids_, /*uninstall=*/false); |
| CallOnCapabilityAccesses(subscriber.get(), known_app_ids_); |
| subscribers_.Add(std::move(subscriber)); |
| } |
| |
| void LoadIcon(const std::string& app_id, |
| apps::mojom::IconKeyPtr icon_key, |
| apps::mojom::IconType icon_type, |
| int32_t size_hint_in_dip, |
| bool allow_placeholder_icon, |
| LoadIconCallback callback) override { |
| load_icon_app_id = app_id; |
| std::move(callback).Run(apps::mojom::IconValue::New()); |
| } |
| |
| void Launch(const std::string& app_id, |
| int32_t event_flags, |
| apps::mojom::LaunchSource launch_source, |
| apps::mojom::WindowInfoPtr window_info) override {} |
| |
| void OnSupportedLinksPreferenceChanged(const std::string& app_id, |
| bool open_in_app) override { |
| if (open_in_app) { |
| supported_link_apps_.insert(app_id); |
| } else { |
| supported_link_apps_.erase(app_id); |
| } |
| } |
| |
| void CallOnApps(apps::mojom::Subscriber* subscriber, |
| std::vector<std::string>& app_ids, |
| bool uninstall) { |
| std::vector<apps::mojom::AppPtr> apps; |
| for (const auto& app_id : app_ids) { |
| auto app = apps::mojom::App::New(); |
| app->app_type = app_type_; |
| app->app_id = app_id; |
| if (uninstall) { |
| app->readiness = apps::mojom::Readiness::kUninstalledByUser; |
| } |
| apps.push_back(std::move(app)); |
| } |
| subscriber->OnApps(std::move(apps), app_type_, |
| false /* should_notify_initialized */); |
| } |
| |
| void CallOnCapabilityAccesses(apps::mojom::Subscriber* subscriber, |
| std::vector<std::string>& app_ids) { |
| std::vector<apps::mojom::CapabilityAccessPtr> capability_accesses; |
| for (const auto& app_id : app_ids) { |
| auto capability_access = apps::mojom::CapabilityAccess::New(); |
| capability_access->app_id = app_id; |
| if (apps_accessing_camera_.find(app_id) != apps_accessing_camera_.end()) { |
| capability_access->camera = apps::mojom::OptionalBool::kTrue; |
| } |
| if (apps_accessing_microphone_.find(app_id) != |
| apps_accessing_microphone_.end()) { |
| capability_access->microphone = apps::mojom::OptionalBool::kTrue; |
| } |
| capability_accesses.push_back(std::move(capability_access)); |
| } |
| subscriber->OnCapabilityAccesses(std::move(capability_accesses)); |
| } |
| |
| apps::mojom::AppType app_type_; |
| std::vector<std::string> known_app_ids_; |
| std::set<std::string> apps_accessing_camera_; |
| std::set<std::string> apps_accessing_microphone_; |
| std::set<std::string> supported_link_apps_; |
| mojo::ReceiverSet<apps::mojom::Publisher> receivers_; |
| mojo::RemoteSet<apps::mojom::Subscriber> subscribers_; |
| }; |
| |
| class FakeSubscriber : public apps::mojom::Subscriber { |
| public: |
| explicit FakeSubscriber(AppServiceMojomImpl* impl) { |
| mojo::PendingRemote<apps::mojom::Subscriber> remote; |
| receivers_.Add(this, remote.InitWithNewPipeAndPassReceiver()); |
| impl->RegisterSubscriber(std::move(remote), nullptr); |
| } |
| |
| std::string AppIdsSeen() { |
| std::stringstream ss; |
| for (const auto& app_id : app_ids_seen_) { |
| ss << app_id; |
| } |
| return ss.str(); |
| } |
| |
| std::string AppIdsAccessingCamera() { |
| std::stringstream ss; |
| for (const auto& app_id : cache_.GetAppsAccessingCamera()) { |
| ss << app_id; |
| } |
| return ss.str(); |
| } |
| |
| std::string AppIdsAccessingMicrophone() { |
| std::stringstream ss; |
| for (const auto& app_id : cache_.GetAppsAccessingMicrophone()) { |
| ss << app_id; |
| } |
| return ss.str(); |
| } |
| |
| PreferredAppsList& PreferredApps() { return preferred_apps_; } |
| |
| private: |
| void OnApps(std::vector<apps::mojom::AppPtr> deltas, |
| apps::mojom::AppType app_type, |
| bool should_notify_initialized) override { |
| for (const auto& delta : deltas) { |
| app_ids_seen_.insert(delta->app_id); |
| if (delta->readiness == apps::mojom::Readiness::kUninstalledByUser) { |
| preferred_apps_.DeleteAppId(delta->app_id); |
| } |
| } |
| } |
| |
| void OnCapabilityAccesses( |
| std::vector<apps::mojom::CapabilityAccessPtr> deltas) override { |
| cache_.OnCapabilityAccesses(std::move(deltas)); |
| } |
| |
| void Clone(mojo::PendingReceiver<apps::mojom::Subscriber> receiver) override { |
| receivers_.Add(this, std::move(receiver)); |
| } |
| |
| void OnPreferredAppsChanged( |
| apps::mojom::PreferredAppChangesPtr changes) override { |
| preferred_apps_.ApplyBulkUpdate(std::move(changes)); |
| } |
| |
| void InitializePreferredApps( |
| PreferredAppsList::PreferredApps preferred_apps) override { |
| preferred_apps_.Init(preferred_apps); |
| } |
| |
| mojo::ReceiverSet<apps::mojom::Subscriber> receivers_; |
| std::set<std::string> app_ids_seen_; |
| AppCapabilityAccessCache cache_; |
| apps::PreferredAppsList preferred_apps_; |
| }; |
| |
| class AppServiceMojomImplTest : public testing::Test { |
| protected: |
| content::BrowserTaskEnvironment task_environment_; |
| base::ScopedTempDir temp_dir_; |
| }; |
| |
| TEST_F(AppServiceMojomImplTest, PubSub) { |
| const int size_hint_in_dip = 64; |
| |
| ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); |
| AppServiceMojomImpl impl(temp_dir_.GetPath()); |
| |
| // Start with one subscriber. |
| FakeSubscriber sub0(&impl); |
| impl.FlushMojoCallsForTesting(); |
| EXPECT_EQ("", sub0.AppIdsSeen()); |
| EXPECT_EQ("", sub0.AppIdsAccessingCamera()); |
| EXPECT_EQ("", sub0.AppIdsAccessingMicrophone()); |
| |
| // Add one publisher. |
| FakePublisher pub0(&impl, apps::mojom::AppType::kArc, |
| std::vector<std::string>{"A", "B"}); |
| impl.FlushMojoCallsForTesting(); |
| EXPECT_EQ("AB", sub0.AppIdsSeen()); |
| EXPECT_EQ("", sub0.AppIdsAccessingCamera()); |
| EXPECT_EQ("", sub0.AppIdsAccessingMicrophone()); |
| |
| pub0.ModifyCapabilityAccess("B", absl::nullopt, absl::nullopt); |
| impl.FlushMojoCallsForTesting(); |
| EXPECT_EQ("", sub0.AppIdsAccessingCamera()); |
| EXPECT_EQ("", sub0.AppIdsAccessingMicrophone()); |
| |
| pub0.ModifyCapabilityAccess("B", true, absl::nullopt); |
| impl.FlushMojoCallsForTesting(); |
| EXPECT_EQ("B", sub0.AppIdsAccessingCamera()); |
| EXPECT_EQ("", sub0.AppIdsAccessingMicrophone()); |
| |
| // Have that publisher publish more apps. |
| pub0.PublishMoreApps(std::vector<std::string>{"C", "D", "E"}); |
| impl.FlushMojoCallsForTesting(); |
| EXPECT_EQ("ABCDE", sub0.AppIdsSeen()); |
| |
| pub0.ModifyCapabilityAccess("D", true, true); |
| impl.FlushMojoCallsForTesting(); |
| EXPECT_EQ("BD", sub0.AppIdsAccessingCamera()); |
| EXPECT_EQ("D", sub0.AppIdsAccessingMicrophone()); |
| |
| // Add a second publisher. |
| FakePublisher pub1(&impl, apps::mojom::AppType::kBuiltIn, |
| std::vector<std::string>{"m"}); |
| impl.FlushMojoCallsForTesting(); |
| EXPECT_EQ("ABCDEm", sub0.AppIdsSeen()); |
| |
| // Have both publishers publish more apps. |
| pub0.PublishMoreApps(std::vector<std::string>{"F"}); |
| pub1.PublishMoreApps(std::vector<std::string>{"n"}); |
| pub0.ModifyCapabilityAccess("B", false, absl::nullopt); |
| pub1.ModifyCapabilityAccess("n", absl::nullopt, true); |
| impl.FlushMojoCallsForTesting(); |
| EXPECT_EQ("ABCDEFmn", sub0.AppIdsSeen()); |
| EXPECT_EQ("D", sub0.AppIdsAccessingCamera()); |
| EXPECT_EQ("Dn", sub0.AppIdsAccessingMicrophone()); |
| |
| // Add a second subscriber. |
| FakeSubscriber sub1(&impl); |
| impl.FlushMojoCallsForTesting(); |
| EXPECT_EQ("ABCDEFmn", sub0.AppIdsSeen()); |
| EXPECT_EQ("ABCDEFmn", sub1.AppIdsSeen()); |
| EXPECT_EQ("D", sub0.AppIdsAccessingCamera()); |
| EXPECT_EQ("Dn", sub0.AppIdsAccessingMicrophone()); |
| EXPECT_EQ("D", sub1.AppIdsAccessingCamera()); |
| EXPECT_EQ("Dn", sub1.AppIdsAccessingMicrophone()); |
| |
| // Publish more apps. |
| pub0.ModifyCapabilityAccess("D", false, false); |
| pub1.PublishMoreApps(std::vector<std::string>{"o", "p", "q"}); |
| pub1.ModifyCapabilityAccess("n", true, absl::nullopt); |
| impl.FlushMojoCallsForTesting(); |
| EXPECT_EQ("ABCDEFmnopq", sub0.AppIdsSeen()); |
| EXPECT_EQ("ABCDEFmnopq", sub1.AppIdsSeen()); |
| EXPECT_EQ("n", sub0.AppIdsAccessingCamera()); |
| EXPECT_EQ("n", sub0.AppIdsAccessingMicrophone()); |
| EXPECT_EQ("n", sub1.AppIdsAccessingCamera()); |
| EXPECT_EQ("n", sub1.AppIdsAccessingMicrophone()); |
| |
| // Add a third publisher. |
| FakePublisher pub2(&impl, apps::mojom::AppType::kCrostini, |
| std::vector<std::string>{"$"}); |
| impl.FlushMojoCallsForTesting(); |
| EXPECT_EQ("$ABCDEFmnopq", sub0.AppIdsSeen()); |
| EXPECT_EQ("$ABCDEFmnopq", sub1.AppIdsSeen()); |
| |
| // Publish more apps. |
| pub2.PublishMoreApps(std::vector<std::string>{"&"}); |
| pub1.PublishMoreApps(std::vector<std::string>{"r"}); |
| pub0.PublishMoreApps(std::vector<std::string>{"G"}); |
| pub1.ModifyCapabilityAccess("n", false, false); |
| pub2.ModifyCapabilityAccess("&", true, false); |
| impl.FlushMojoCallsForTesting(); |
| EXPECT_EQ("$&ABCDEFGmnopqr", sub0.AppIdsSeen()); |
| EXPECT_EQ("$&ABCDEFGmnopqr", sub1.AppIdsSeen()); |
| EXPECT_EQ("&", sub0.AppIdsAccessingCamera()); |
| EXPECT_EQ("", sub0.AppIdsAccessingMicrophone()); |
| EXPECT_EQ("&", sub1.AppIdsAccessingCamera()); |
| EXPECT_EQ("", sub1.AppIdsAccessingMicrophone()); |
| |
| // Call LoadIcon on the impl twice. |
| // |
| // The first time (i == 0), it should be forwarded onto the AppType::kBuiltIn |
| // publisher (which is pub1) and no other publisher. |
| // |
| // The second time (i == 1), passing AppType::kUnknown, none of the |
| // publishers' LoadIcon's should fire, but the callback should still be run. |
| for (int i = 0; i < 2; i++) { |
| auto app_type = i == 0 ? apps::mojom::AppType::kBuiltIn |
| : apps::mojom::AppType::kUnknown; |
| |
| bool callback_ran = false; |
| pub0.load_icon_app_id = "-"; |
| pub1.load_icon_app_id = "-"; |
| pub2.load_icon_app_id = "-"; |
| auto icon_key = apps::mojom::IconKey::New(0, 0, 0); |
| constexpr bool allow_placeholder_icon = false; |
| impl.LoadIcon( |
| app_type, "o", std::move(icon_key), |
| apps::mojom::IconType::kUncompressed, size_hint_in_dip, |
| allow_placeholder_icon, |
| base::BindOnce( |
| [](bool* ran, apps::mojom::IconValuePtr iv) { *ran = true; }, |
| &callback_ran)); |
| impl.FlushMojoCallsForTesting(); |
| EXPECT_TRUE(callback_ran); |
| EXPECT_EQ("-", pub0.load_icon_app_id); |
| EXPECT_EQ(i == 0 ? "o" : "-", pub1.load_icon_app_id); |
| EXPECT_EQ("-", pub2.load_icon_app_id); |
| } |
| } |
| |
| TEST_F(AppServiceMojomImplTest, PreferredApps) { |
| // Test Initialize. |
| ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); |
| AppServiceMojomImpl impl(temp_dir_.GetPath()); |
| impl.GetPreferredAppsForTesting().Init(); |
| |
| const char kAppId1[] = "abcdefg"; |
| const char kAppId2[] = "aaaaaaa"; |
| GURL filter_url = GURL("https://www.google.com/abc"); |
| auto intent_filter = apps_util::CreateIntentFilterForUrlScope(filter_url); |
| |
| impl.GetPreferredAppsForTesting().AddPreferredApp(kAppId1, intent_filter); |
| |
| // Add one subscriber. |
| FakeSubscriber sub0(&impl); |
| task_environment_.RunUntilIdle(); |
| EXPECT_EQ(sub0.PreferredApps().GetValue(), |
| impl.GetPreferredAppsForTesting().GetValue()); |
| |
| // Add another subscriber. |
| FakeSubscriber sub1(&impl); |
| task_environment_.RunUntilIdle(); |
| EXPECT_EQ(sub1.PreferredApps().GetValue(), |
| impl.GetPreferredAppsForTesting().GetValue()); |
| |
| FakePublisher pub0(&impl, apps::mojom::AppType::kArc, |
| std::vector<std::string>{kAppId1, kAppId2}); |
| task_environment_.RunUntilIdle(); |
| |
| // Test sync preferred app to all subscribers. |
| filter_url = GURL("https://www.abc.com/"); |
| GURL another_filter_url = GURL("https://www.test.com/"); |
| intent_filter = apps_util::CreateIntentFilterForUrlScope(filter_url); |
| auto another_intent_filter = |
| apps_util::CreateIntentFilterForUrlScope(another_filter_url); |
| |
| task_environment_.RunUntilIdle(); |
| EXPECT_EQ(absl::nullopt, |
| sub0.PreferredApps().FindPreferredAppForUrl(filter_url)); |
| EXPECT_EQ(absl::nullopt, |
| sub1.PreferredApps().FindPreferredAppForUrl(filter_url)); |
| EXPECT_EQ(absl::nullopt, |
| sub0.PreferredApps().FindPreferredAppForUrl(another_filter_url)); |
| EXPECT_EQ(absl::nullopt, |
| sub1.PreferredApps().FindPreferredAppForUrl(another_filter_url)); |
| |
| impl.AddPreferredApp( |
| apps::mojom::AppType::kUnknown, kAppId2, intent_filter->Clone(), |
| apps_util::CreateIntentFromUrl(filter_url), /*from_publisher=*/true); |
| impl.AddPreferredApp(apps::mojom::AppType::kUnknown, kAppId2, |
| another_intent_filter->Clone(), |
| apps_util::CreateIntentFromUrl(another_filter_url), |
| /*from_publisher=*/true); |
| task_environment_.RunUntilIdle(); |
| EXPECT_EQ(kAppId2, sub0.PreferredApps().FindPreferredAppForUrl(filter_url)); |
| EXPECT_EQ(kAppId2, sub1.PreferredApps().FindPreferredAppForUrl(filter_url)); |
| EXPECT_EQ(kAppId2, |
| sub0.PreferredApps().FindPreferredAppForUrl(another_filter_url)); |
| EXPECT_EQ(kAppId2, |
| sub1.PreferredApps().FindPreferredAppForUrl(another_filter_url)); |
| |
| // Test that uninstall removes all the settings for the app. |
| pub0.UninstallApps(std::vector<std::string>{kAppId2}, &impl); |
| task_environment_.RunUntilIdle(); |
| EXPECT_EQ(absl::nullopt, |
| sub0.PreferredApps().FindPreferredAppForUrl(filter_url)); |
| EXPECT_EQ(absl::nullopt, |
| sub1.PreferredApps().FindPreferredAppForUrl(filter_url)); |
| EXPECT_EQ(absl::nullopt, |
| sub0.PreferredApps().FindPreferredAppForUrl(another_filter_url)); |
| EXPECT_EQ(absl::nullopt, |
| sub1.PreferredApps().FindPreferredAppForUrl(another_filter_url)); |
| |
| impl.AddPreferredApp( |
| apps::mojom::AppType::kUnknown, kAppId2, intent_filter->Clone(), |
| apps_util::CreateIntentFromUrl(filter_url), /*from_publisher=*/true); |
| impl.AddPreferredApp(apps::mojom::AppType::kUnknown, kAppId2, |
| another_intent_filter->Clone(), |
| apps_util::CreateIntentFromUrl(another_filter_url), |
| /*from_publisher=*/true); |
| task_environment_.RunUntilIdle(); |
| |
| EXPECT_EQ(kAppId2, sub0.PreferredApps().FindPreferredAppForUrl(filter_url)); |
| EXPECT_EQ(kAppId2, sub1.PreferredApps().FindPreferredAppForUrl(filter_url)); |
| EXPECT_EQ(kAppId2, |
| sub0.PreferredApps().FindPreferredAppForUrl(another_filter_url)); |
| EXPECT_EQ(kAppId2, |
| sub1.PreferredApps().FindPreferredAppForUrl(another_filter_url)); |
| |
| // Test that remove setting for one filter. |
| impl.RemovePreferredAppForFilter(apps::mojom::AppType::kUnknown, kAppId2, |
| intent_filter->Clone()); |
| task_environment_.RunUntilIdle(); |
| EXPECT_EQ(absl::nullopt, |
| sub0.PreferredApps().FindPreferredAppForUrl(filter_url)); |
| EXPECT_EQ(absl::nullopt, |
| sub1.PreferredApps().FindPreferredAppForUrl(filter_url)); |
| EXPECT_EQ(kAppId2, |
| sub0.PreferredApps().FindPreferredAppForUrl(another_filter_url)); |
| EXPECT_EQ(kAppId2, |
| sub1.PreferredApps().FindPreferredAppForUrl(another_filter_url)); |
| } |
| |
| TEST_F(AppServiceMojomImplTest, PreferredAppsPersistency) { |
| ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); |
| |
| const char kAppId1[] = "abcdefg"; |
| GURL filter_url = GURL("https://www.google.com/abc"); |
| auto intent_filter = apps_util::CreateIntentFilterForUrlScope(filter_url); |
| { |
| base::RunLoop run_loop_read; |
| base::RunLoop run_loop_write; |
| AppServiceMojomImpl impl(temp_dir_.GetPath(), run_loop_read.QuitClosure(), |
| run_loop_write.QuitClosure()); |
| impl.FlushMojoCallsForTesting(); |
| run_loop_read.Run(); |
| impl.AddPreferredApp(apps::mojom::AppType::kUnknown, kAppId1, |
| intent_filter->Clone(), |
| apps_util::CreateIntentFromUrl(filter_url), |
| /*from_publisher=*/false); |
| run_loop_write.Run(); |
| impl.FlushMojoCallsForTesting(); |
| } |
| // Create a new impl to initialize preferred apps from the disk. |
| { |
| base::RunLoop run_loop_read; |
| AppServiceMojomImpl impl(temp_dir_.GetPath(), run_loop_read.QuitClosure()); |
| impl.FlushMojoCallsForTesting(); |
| run_loop_read.Run(); |
| EXPECT_EQ(kAppId1, impl.GetPreferredAppsForTesting().FindPreferredAppForUrl( |
| filter_url)); |
| } |
| } |
| |
| TEST_F(AppServiceMojomImplTest, PreferredAppsSetSupportedLinks) { |
| // Test Initialize. |
| ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); |
| AppServiceMojomImpl impl(temp_dir_.GetPath()); |
| impl.GetPreferredAppsForTesting().Init(); |
| |
| const char kAppId1[] = "abcdefg"; |
| const char kAppId2[] = "hijklmn"; |
| const char kAppId3[] = "opqrstu"; |
| |
| auto intent_filter_a = |
| apps_util::CreateIntentFilterForUrlScope(GURL("https://www.a.com/")); |
| auto intent_filter_b = |
| apps_util::CreateIntentFilterForUrlScope(GURL("https://www.b.com/")); |
| auto intent_filter_c = |
| apps_util::CreateIntentFilterForUrlScope(GURL("https://www.c.com/")); |
| |
| FakeSubscriber sub0(&impl); |
| task_environment_.RunUntilIdle(); |
| |
| FakePublisher pub0(&impl, apps::mojom::AppType::kArc, |
| std::vector<std::string>{kAppId1, kAppId2, kAppId3}); |
| task_environment_.RunUntilIdle(); |
| |
| std::vector<apps::mojom::IntentFilterPtr> app_1_filters; |
| app_1_filters.push_back(intent_filter_a.Clone()); |
| app_1_filters.push_back(intent_filter_b.Clone()); |
| impl.SetSupportedLinksPreference(apps::mojom::AppType::kArc, kAppId1, |
| std::move(app_1_filters)); |
| |
| std::vector<apps::mojom::IntentFilterPtr> app_2_filters; |
| app_2_filters.push_back(intent_filter_c.Clone()); |
| impl.SetSupportedLinksPreference(apps::mojom::AppType::kArc, kAppId2, |
| std::move(app_2_filters)); |
| |
| task_environment_.RunUntilIdle(); |
| |
| EXPECT_TRUE(pub0.AppHasSupportedLinksPreference(kAppId1)); |
| EXPECT_TRUE(pub0.AppHasSupportedLinksPreference(kAppId2)); |
| EXPECT_FALSE(pub0.AppHasSupportedLinksPreference(kAppId3)); |
| |
| EXPECT_EQ(kAppId1, sub0.PreferredApps().FindPreferredAppForUrl( |
| GURL("https://www.a.com/"))); |
| EXPECT_EQ(kAppId1, sub0.PreferredApps().FindPreferredAppForUrl( |
| GURL("https://www.b.com/"))); |
| EXPECT_EQ(kAppId2, sub0.PreferredApps().FindPreferredAppForUrl( |
| GURL("https://www.c.com/"))); |
| |
| // App 3 overlaps with both App 1 and 2. Both previous apps should have all |
| // their supported link filters removed. |
| std::vector<apps::mojom::IntentFilterPtr> app_3_filters; |
| app_3_filters.push_back(intent_filter_b.Clone()); |
| app_3_filters.push_back(intent_filter_c.Clone()); |
| impl.SetSupportedLinksPreference(apps::mojom::AppType::kArc, kAppId3, |
| std::move(app_3_filters)); |
| |
| task_environment_.RunUntilIdle(); |
| EXPECT_FALSE(pub0.AppHasSupportedLinksPreference(kAppId1)); |
| EXPECT_FALSE(pub0.AppHasSupportedLinksPreference(kAppId2)); |
| EXPECT_TRUE(pub0.AppHasSupportedLinksPreference(kAppId3)); |
| |
| EXPECT_EQ(absl::nullopt, sub0.PreferredApps().FindPreferredAppForUrl( |
| GURL("https://www.a.com/"))); |
| EXPECT_EQ(kAppId3, sub0.PreferredApps().FindPreferredAppForUrl( |
| GURL("https://www.b.com/"))); |
| EXPECT_EQ(kAppId3, sub0.PreferredApps().FindPreferredAppForUrl( |
| GURL("https://www.c.com/"))); |
| |
| // Setting App 3 as preferred again should not change anything. |
| app_3_filters = std::vector<apps::mojom::IntentFilterPtr>(); |
| app_3_filters.push_back(intent_filter_b.Clone()); |
| app_3_filters.push_back(intent_filter_c.Clone()); |
| impl.SetSupportedLinksPreference(apps::mojom::AppType::kArc, kAppId3, |
| std::move(app_3_filters)); |
| task_environment_.RunUntilIdle(); |
| |
| EXPECT_TRUE(pub0.AppHasSupportedLinksPreference(kAppId3)); |
| EXPECT_EQ(kAppId3, sub0.PreferredApps().FindPreferredAppForUrl( |
| GURL("https://www.c.com/"))); |
| |
| impl.RemoveSupportedLinksPreference(apps::mojom::AppType::kArc, kAppId3); |
| task_environment_.RunUntilIdle(); |
| |
| EXPECT_FALSE(pub0.AppHasSupportedLinksPreference(kAppId3)); |
| EXPECT_EQ(absl::nullopt, sub0.PreferredApps().FindPreferredAppForUrl( |
| GURL("https://www.c.com/"))); |
| } |
| |
| // Test that app with overlapped works properly. |
| TEST_F(AppServiceMojomImplTest, PreferredAppsOverlap) { |
| // Test Initialize. |
| ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); |
| AppServiceMojomImpl impl(temp_dir_.GetPath()); |
| impl.GetPreferredAppsForTesting().Init(); |
| |
| const char kAppId1[] = "abcdefg"; |
| const char kAppId2[] = "hijklmn"; |
| |
| GURL filter_url_1 = GURL("https://www.google.com/abc"); |
| GURL filter_url_2 = GURL("http://www.google.com.au/abc"); |
| GURL filter_url_3 = GURL("https://www.abc.com/abc"); |
| |
| auto intent_filter_1 = apps_util::CreateIntentFilterForUrlScope(filter_url_1); |
| apps_util::AddConditionValue( |
| apps::mojom::ConditionType::kScheme, filter_url_2.scheme(), |
| apps::mojom::PatternMatchType::kNone, intent_filter_1); |
| apps_util::AddConditionValue( |
| apps::mojom::ConditionType::kHost, filter_url_2.host(), |
| apps::mojom::PatternMatchType::kNone, intent_filter_1); |
| |
| auto intent_filter_2 = apps_util::CreateIntentFilterForUrlScope(filter_url_3); |
| apps_util::AddConditionValue( |
| apps::mojom::ConditionType::kScheme, filter_url_2.scheme(), |
| apps::mojom::PatternMatchType::kNone, intent_filter_2); |
| apps_util::AddConditionValue( |
| apps::mojom::ConditionType::kHost, filter_url_2.host(), |
| apps::mojom::PatternMatchType::kNone, intent_filter_2); |
| |
| auto intent_filter_3 = apps_util::CreateIntentFilterForUrlScope(filter_url_1); |
| |
| FakeSubscriber sub0(&impl); |
| task_environment_.RunUntilIdle(); |
| |
| EXPECT_EQ(absl::nullopt, |
| sub0.PreferredApps().FindPreferredAppForUrl(filter_url_1)); |
| EXPECT_EQ(absl::nullopt, |
| sub0.PreferredApps().FindPreferredAppForUrl(filter_url_2)); |
| EXPECT_EQ(absl::nullopt, |
| sub0.PreferredApps().FindPreferredAppForUrl(filter_url_3)); |
| EXPECT_EQ(0U, impl.GetPreferredAppsForTesting().GetEntrySize()); |
| EXPECT_EQ(0U, sub0.PreferredApps().GetEntrySize()); |
| |
| impl.AddPreferredApp( |
| apps::mojom::AppType::kArc, kAppId1, intent_filter_1->Clone(), |
| apps_util::CreateIntentFromUrl(filter_url_1), /*from_publisher=*/true); |
| task_environment_.RunUntilIdle(); |
| EXPECT_EQ(kAppId1, sub0.PreferredApps().FindPreferredAppForUrl(filter_url_1)); |
| EXPECT_EQ(kAppId1, sub0.PreferredApps().FindPreferredAppForUrl(filter_url_2)); |
| EXPECT_EQ(absl::nullopt, |
| sub0.PreferredApps().FindPreferredAppForUrl(filter_url_3)); |
| EXPECT_EQ(1U, impl.GetPreferredAppsForTesting().GetEntrySize()); |
| EXPECT_EQ(1U, sub0.PreferredApps().GetEntrySize()); |
| |
| // Add preferred app with intent filter overlap with existing entry for |
| // another app will reset the preferred app setting for the other app. |
| impl.AddPreferredApp( |
| apps::mojom::AppType::kArc, kAppId2, intent_filter_2->Clone(), |
| apps_util::CreateIntentFromUrl(filter_url_1), /*from_publisher=*/true); |
| task_environment_.RunUntilIdle(); |
| EXPECT_EQ(absl::nullopt, |
| sub0.PreferredApps().FindPreferredAppForUrl(filter_url_1)); |
| EXPECT_EQ(kAppId2, sub0.PreferredApps().FindPreferredAppForUrl(filter_url_2)); |
| EXPECT_EQ(kAppId2, sub0.PreferredApps().FindPreferredAppForUrl(filter_url_3)); |
| EXPECT_EQ(1U, impl.GetPreferredAppsForTesting().GetEntrySize()); |
| EXPECT_EQ(1U, sub0.PreferredApps().GetEntrySize()); |
| |
| // Test that can remove entry with overlapped filter. |
| impl.RemovePreferredAppForFilter(apps::mojom::AppType::kArc, kAppId2, |
| intent_filter_1->Clone()); |
| task_environment_.RunUntilIdle(); |
| EXPECT_EQ(absl::nullopt, |
| sub0.PreferredApps().FindPreferredAppForUrl(filter_url_1)); |
| EXPECT_EQ(absl::nullopt, |
| sub0.PreferredApps().FindPreferredAppForUrl(filter_url_2)); |
| EXPECT_EQ(absl::nullopt, |
| sub0.PreferredApps().FindPreferredAppForUrl(filter_url_3)); |
| EXPECT_EQ(0U, impl.GetPreferredAppsForTesting().GetEntrySize()); |
| EXPECT_EQ(0U, sub0.PreferredApps().GetEntrySize()); |
| } |
| |
| // Test that app with overlapped supported links works properly. |
| TEST_F(AppServiceMojomImplTest, PreferredAppsOverlapSupportedLink) { |
| // Test Initialize. |
| ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); |
| AppServiceMojomImpl impl(temp_dir_.GetPath()); |
| impl.GetPreferredAppsForTesting().Init(); |
| |
| const char kAppId1[] = "abcdefg"; |
| const char kAppId2[] = "hijklmn"; |
| |
| GURL filter_url_1 = GURL("https://www.google.com/abc"); |
| GURL filter_url_2 = GURL("http://www.google.com.au/abc"); |
| GURL filter_url_3 = GURL("https://www.abc.com/abc"); |
| |
| auto intent_filter_1 = apps_util::CreateIntentFilterForUrlScope(filter_url_1); |
| apps_util::AddConditionValue( |
| apps::mojom::ConditionType::kScheme, filter_url_2.scheme(), |
| apps::mojom::PatternMatchType::kNone, intent_filter_1); |
| apps_util::AddConditionValue( |
| apps::mojom::ConditionType::kHost, filter_url_2.host(), |
| apps::mojom::PatternMatchType::kNone, intent_filter_1); |
| |
| auto intent_filter_2 = apps_util::CreateIntentFilterForUrlScope(filter_url_3); |
| apps_util::AddConditionValue( |
| apps::mojom::ConditionType::kScheme, filter_url_2.scheme(), |
| apps::mojom::PatternMatchType::kNone, intent_filter_2); |
| apps_util::AddConditionValue( |
| apps::mojom::ConditionType::kHost, filter_url_2.host(), |
| apps::mojom::PatternMatchType::kNone, intent_filter_2); |
| |
| auto intent_filter_3 = apps_util::CreateIntentFilterForUrlScope(filter_url_1); |
| |
| std::vector<apps::mojom::IntentFilterPtr> app_1_filters; |
| app_1_filters.push_back(std::move(intent_filter_1)); |
| app_1_filters.push_back(std::move(intent_filter_2)); |
| std::vector<apps::mojom::IntentFilterPtr> app_2_filters; |
| app_2_filters.push_back(std::move(intent_filter_3)); |
| |
| FakeSubscriber sub0(&impl); |
| task_environment_.RunUntilIdle(); |
| |
| FakePublisher pub0(&impl, apps::mojom::AppType::kArc, |
| std::vector<std::string>{kAppId1, kAppId2}); |
| task_environment_.RunUntilIdle(); |
| |
| EXPECT_EQ(absl::nullopt, |
| sub0.PreferredApps().FindPreferredAppForUrl(filter_url_1)); |
| EXPECT_EQ(absl::nullopt, |
| sub0.PreferredApps().FindPreferredAppForUrl(filter_url_2)); |
| EXPECT_EQ(absl::nullopt, |
| sub0.PreferredApps().FindPreferredAppForUrl(filter_url_3)); |
| EXPECT_EQ(0U, impl.GetPreferredAppsForTesting().GetEntrySize()); |
| EXPECT_EQ(0U, sub0.PreferredApps().GetEntrySize()); |
| |
| // Test that add preferred app with overlapped filters for same app will |
| // add all entries. |
| impl.SetSupportedLinksPreference(apps::mojom::AppType::kArc, kAppId1, |
| mojo::Clone(app_1_filters)); |
| task_environment_.RunUntilIdle(); |
| EXPECT_EQ(kAppId1, sub0.PreferredApps().FindPreferredAppForUrl(filter_url_1)); |
| EXPECT_EQ(kAppId1, sub0.PreferredApps().FindPreferredAppForUrl(filter_url_2)); |
| EXPECT_EQ(kAppId1, sub0.PreferredApps().FindPreferredAppForUrl(filter_url_3)); |
| EXPECT_TRUE(pub0.AppHasSupportedLinksPreference(kAppId1)); |
| EXPECT_FALSE(pub0.AppHasSupportedLinksPreference(kAppId2)); |
| EXPECT_EQ(2U, impl.GetPreferredAppsForTesting().GetEntrySize()); |
| EXPECT_EQ(2U, sub0.PreferredApps().GetEntrySize()); |
| |
| // Test that add preferred app with another app that has overlapped filter |
| // will clear all entries from the original app. |
| impl.SetSupportedLinksPreference(apps::mojom::AppType::kArc, kAppId2, |
| mojo::Clone(app_2_filters)); |
| task_environment_.RunUntilIdle(); |
| EXPECT_EQ(kAppId2, sub0.PreferredApps().FindPreferredAppForUrl(filter_url_1)); |
| EXPECT_EQ(absl::nullopt, |
| sub0.PreferredApps().FindPreferredAppForUrl(filter_url_2)); |
| EXPECT_EQ(absl::nullopt, |
| sub0.PreferredApps().FindPreferredAppForUrl(filter_url_3)); |
| EXPECT_FALSE(pub0.AppHasSupportedLinksPreference(kAppId1)); |
| EXPECT_TRUE(pub0.AppHasSupportedLinksPreference(kAppId2)); |
| EXPECT_EQ(1U, impl.GetPreferredAppsForTesting().GetEntrySize()); |
| EXPECT_EQ(1U, sub0.PreferredApps().GetEntrySize()); |
| |
| // Test that setting back to app 1 works. |
| impl.SetSupportedLinksPreference(apps::mojom::AppType::kArc, kAppId1, |
| mojo::Clone(app_1_filters)); |
| task_environment_.RunUntilIdle(); |
| EXPECT_EQ(kAppId1, sub0.PreferredApps().FindPreferredAppForUrl(filter_url_1)); |
| EXPECT_EQ(kAppId1, sub0.PreferredApps().FindPreferredAppForUrl(filter_url_2)); |
| EXPECT_EQ(kAppId1, sub0.PreferredApps().FindPreferredAppForUrl(filter_url_3)); |
| EXPECT_TRUE(pub0.AppHasSupportedLinksPreference(kAppId1)); |
| EXPECT_FALSE(pub0.AppHasSupportedLinksPreference(kAppId2)); |
| EXPECT_EQ(2U, impl.GetPreferredAppsForTesting().GetEntrySize()); |
| EXPECT_EQ(2U, sub0.PreferredApps().GetEntrySize()); |
| } |
| |
| // Test that duplicated entry will not be added. |
| TEST_F(AppServiceMojomImplTest, PreferredAppsDuplicated) { |
| // Test Initialize. |
| ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); |
| AppServiceMojomImpl impl(temp_dir_.GetPath()); |
| impl.GetPreferredAppsForTesting().Init(); |
| |
| const char kAppId1[] = "abcdefg"; |
| |
| GURL filter_url = GURL("https://www.google.com/abc"); |
| |
| auto intent_filter = apps_util::CreateIntentFilterForUrlScope(filter_url); |
| |
| FakeSubscriber sub0(&impl); |
| task_environment_.RunUntilIdle(); |
| |
| EXPECT_EQ(absl::nullopt, |
| sub0.PreferredApps().FindPreferredAppForUrl(filter_url)); |
| EXPECT_EQ(0U, impl.GetPreferredAppsForTesting().GetEntrySize()); |
| EXPECT_EQ(0U, sub0.PreferredApps().GetEntrySize()); |
| |
| impl.AddPreferredApp( |
| apps::mojom::AppType::kArc, kAppId1, intent_filter->Clone(), |
| apps_util::CreateIntentFromUrl(filter_url), /*from_publisher=*/true); |
| task_environment_.RunUntilIdle(); |
| EXPECT_EQ(kAppId1, sub0.PreferredApps().FindPreferredAppForUrl(filter_url)); |
| EXPECT_EQ(1U, impl.GetPreferredAppsForTesting().GetEntrySize()); |
| EXPECT_EQ(1U, sub0.PreferredApps().GetEntrySize()); |
| |
| impl.AddPreferredApp( |
| apps::mojom::AppType::kArc, kAppId1, intent_filter->Clone(), |
| apps_util::CreateIntentFromUrl(filter_url), /*from_publisher=*/true); |
| task_environment_.RunUntilIdle(); |
| EXPECT_EQ(kAppId1, sub0.PreferredApps().FindPreferredAppForUrl(filter_url)); |
| EXPECT_EQ(1U, impl.GetPreferredAppsForTesting().GetEntrySize()); |
| EXPECT_EQ(1U, sub0.PreferredApps().GetEntrySize()); |
| } |
| |
| // Test that duplicated entry will not be added for supported links. |
| TEST_F(AppServiceMojomImplTest, PreferredAppsDuplicatedSupportedLink) { |
| // Test Initialize. |
| ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); |
| AppServiceMojomImpl impl(temp_dir_.GetPath()); |
| impl.GetPreferredAppsForTesting().Init(); |
| |
| const char kAppId1[] = "abcdefg"; |
| |
| GURL filter_url_1 = GURL("https://www.google.com/abc"); |
| GURL filter_url_2 = GURL("http://www.google.com.au/abc"); |
| GURL filter_url_3 = GURL("https://www.abc.com/abc"); |
| |
| auto intent_filter_1 = apps_util::CreateIntentFilterForUrlScope(filter_url_1); |
| |
| auto intent_filter_2 = apps_util::CreateIntentFilterForUrlScope(filter_url_2); |
| |
| auto intent_filter_3 = apps_util::CreateIntentFilterForUrlScope(filter_url_3); |
| |
| std::vector<apps::mojom::IntentFilterPtr> app_1_filters; |
| app_1_filters.push_back(std::move(intent_filter_1)); |
| app_1_filters.push_back(std::move(intent_filter_2)); |
| app_1_filters.push_back(std::move(intent_filter_3)); |
| |
| FakeSubscriber sub0(&impl); |
| task_environment_.RunUntilIdle(); |
| |
| FakePublisher pub0(&impl, apps::mojom::AppType::kArc, |
| std::vector<std::string>{kAppId1}); |
| task_environment_.RunUntilIdle(); |
| |
| EXPECT_EQ(absl::nullopt, |
| sub0.PreferredApps().FindPreferredAppForUrl(filter_url_1)); |
| EXPECT_EQ(absl::nullopt, |
| sub0.PreferredApps().FindPreferredAppForUrl(filter_url_2)); |
| EXPECT_EQ(absl::nullopt, |
| sub0.PreferredApps().FindPreferredAppForUrl(filter_url_3)); |
| EXPECT_EQ(0U, impl.GetPreferredAppsForTesting().GetEntrySize()); |
| EXPECT_EQ(0U, sub0.PreferredApps().GetEntrySize()); |
| |
| impl.SetSupportedLinksPreference(apps::mojom::AppType::kArc, kAppId1, |
| mojo::Clone(app_1_filters)); |
| task_environment_.RunUntilIdle(); |
| EXPECT_EQ(kAppId1, sub0.PreferredApps().FindPreferredAppForUrl(filter_url_1)); |
| EXPECT_EQ(kAppId1, sub0.PreferredApps().FindPreferredAppForUrl(filter_url_2)); |
| EXPECT_EQ(kAppId1, sub0.PreferredApps().FindPreferredAppForUrl(filter_url_3)); |
| EXPECT_TRUE(pub0.AppHasSupportedLinksPreference(kAppId1)); |
| |
| EXPECT_EQ(3U, impl.GetPreferredAppsForTesting().GetEntrySize()); |
| EXPECT_EQ(3U, sub0.PreferredApps().GetEntrySize()); |
| |
| impl.SetSupportedLinksPreference(apps::mojom::AppType::kArc, kAppId1, |
| mojo::Clone(app_1_filters)); |
| task_environment_.RunUntilIdle(); |
| EXPECT_EQ(kAppId1, sub0.PreferredApps().FindPreferredAppForUrl(filter_url_1)); |
| EXPECT_EQ(kAppId1, sub0.PreferredApps().FindPreferredAppForUrl(filter_url_2)); |
| EXPECT_EQ(kAppId1, sub0.PreferredApps().FindPreferredAppForUrl(filter_url_3)); |
| EXPECT_TRUE(pub0.AppHasSupportedLinksPreference(kAppId1)); |
| |
| EXPECT_EQ(3U, impl.GetPreferredAppsForTesting().GetEntrySize()); |
| EXPECT_EQ(3U, sub0.PreferredApps().GetEntrySize()); |
| } |
| |
| } // namespace apps |