| // Copyright 2021 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/lacros/lacros_extension_apps_publisher.h" |
| |
| #include <optional> |
| |
| #include "base/run_loop.h" |
| #include "chrome/browser/apps/app_service/extension_apps_utils.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/extensions/chrome_test_extension_loader.h" |
| #include "chrome/browser/extensions/extension_browsertest.h" |
| #include "chrome/browser/extensions/extension_keeplist_chromeos.h" |
| #include "chrome/browser/lacros/for_which_extension_type.h" |
| #include "chrome/browser/lacros/lacros_extensions_util.h" |
| #include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h" |
| #include "chrome/browser/media/webrtc/media_stream_capture_indicator.h" |
| #include "chrome/browser/policy/system_features_disable_list_policy_handler.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/lacros/window_utility.h" |
| #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h" |
| #include "chrome/browser/web_applications/test/web_app_install_test_utils.h" |
| #include "chrome/test/base/in_process_browser_test.h" |
| #include "chrome/test/base/testing_browser_process.h" |
| #include "chrome/test/base/ui_test_utils.h" |
| #include "chromeos/crosapi/mojom/app_service_types.mojom.h" |
| #include "components/policy/core/common/policy_pref_names.h" |
| #include "components/prefs/scoped_user_pref_update.h" |
| #include "components/services/app_service/public/cpp/app_capability_access_cache.h" |
| #include "components/services/app_service/public/cpp/app_types.h" |
| #include "content/public/test/browser_test.h" |
| #include "extensions/browser/app_window/app_window.h" |
| #include "extensions/browser/app_window/app_window_registry.h" |
| #include "extensions/browser/app_window/native_app_window.h" |
| #include "extensions/common/constants.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/blink/public/common/mediastream/media_stream_request.h" |
| #include "third_party/blink/public/mojom/mediastream/media_stream.mojom.h" |
| #include "ui/aura/window.h" |
| |
| namespace { |
| |
| using Apps = std::vector<apps::AppPtr>; |
| using Accesses = std::vector<apps::CapabilityAccessPtr>; |
| |
| // This fake intercepts and tracks all calls to Publish(). |
| class LacrosExtensionAppsPublisherFake : public LacrosExtensionAppsPublisher { |
| public: |
| LacrosExtensionAppsPublisherFake() |
| : LacrosExtensionAppsPublisher(InitForChromeApps()) { |
| // Since LacrosExtensionAppsPublisherTest run without Ash, Lacros won't get |
| // the Ash extension keeplist data from Ash (passed via crosapi). Therefore, |
| // set empty ash keeplist for test. |
| extensions::SetEmptyAshKeeplistForTest(); |
| apps::EnableHostedAppsInLacrosForTesting(); |
| } |
| ~LacrosExtensionAppsPublisherFake() override = default; |
| |
| LacrosExtensionAppsPublisherFake(const LacrosExtensionAppsPublisherFake&) = |
| delete; |
| LacrosExtensionAppsPublisherFake& operator=( |
| const LacrosExtensionAppsPublisherFake&) = delete; |
| |
| void VerifyAppCapabilityAccess(const std::string& app_id, |
| size_t count, |
| std::optional<bool> accessing_camera, |
| std::optional<bool> accessing_microphone) { |
| ASSERT_EQ(count, accesses_history().size()); |
| Accesses& accesses = accesses_history().back(); |
| ASSERT_EQ(1u, accesses.size()); |
| EXPECT_EQ(app_id, accesses[0]->app_id); |
| |
| if (accessing_camera.has_value()) { |
| ASSERT_TRUE(accesses[0]->camera.has_value()); |
| EXPECT_EQ(accessing_camera.value(), accesses[0]->camera.value()); |
| } else { |
| ASSERT_FALSE(accesses[0]->camera.has_value()); |
| } |
| |
| if (accessing_microphone.has_value()) { |
| ASSERT_TRUE(accesses[0]->microphone.has_value()); |
| EXPECT_EQ(accessing_microphone.value(), accesses[0]->microphone.value()); |
| } else { |
| ASSERT_FALSE(accesses[0]->microphone.has_value()); |
| } |
| } |
| |
| std::vector<Apps>& apps_history() { return apps_history_; } |
| |
| std::vector<Accesses>& accesses_history() { return accesses_history_; } |
| |
| std::map<std::string, std::string>& app_windows() { return app_windows_; } |
| |
| const apps::App* GetLastPublishedChangeForExtensionId() { |
| for (size_t i = apps_history_.size() - 1; i >= 0; --i) { |
| if (auto it = std::find_if( |
| apps_history_[i].begin(), apps_history_[i].end(), |
| [](const apps::AppPtr& app) { |
| Profile* profile = nullptr; |
| const extensions::Extension* extension = nullptr; |
| if (lacros_extensions_util::GetProfileAndExtension( |
| app->app_id, &profile, &extension)) { |
| return extension->id() == extensions::kWebStoreAppId; |
| } |
| return false; |
| }); |
| it != apps_history_[i].end()) { |
| return (*it).get(); |
| } |
| } |
| return nullptr; |
| } |
| |
| private: |
| // Override to intercept calls to Publish(). |
| void Publish(Apps apps) override { apps_history_.push_back(std::move(apps)); } |
| |
| // Override to intercept calls to PublishCapabilityAccesses(). |
| void PublishCapabilityAccesses(Accesses accesses) override { |
| accesses_history_.push_back(std::move(accesses)); |
| } |
| |
| // Override to intercept calls to OnAppWindowAdded(). |
| void OnAppWindowAdded(const std::string& app_id, |
| const std::string& window_id) override { |
| app_windows_[window_id] = app_id; |
| } |
| |
| // Override to intercept calls to OnAppWindowRemoved(). |
| void OnAppWindowRemoved(const std::string& app_id, |
| const std::string& window_id) override { |
| EXPECT_TRUE(app_windows_.find(window_id) != app_windows_.end()); |
| EXPECT_EQ(app_windows_[window_id], app_id); |
| app_windows_.erase(window_id); |
| } |
| |
| // Override to pretend that crosapi is initialized. |
| bool InitializeCrosapi() override { return true; } |
| |
| // Holds the contents of all calls to Publish() in chronological order. |
| std::vector<Apps> apps_history_; |
| |
| // Holds the contents of all calls to PublishCapabilityAccesses() in |
| // chronological order. |
| std::vector<Accesses> accesses_history_; |
| |
| // Holds the list of currently showing app windows, as seen by |
| // OnAppWindowAdded() and OnAppWindowRemoved(). The key is the window_id and |
| // the value is the app_id. |
| std::map<std::string, std::string> app_windows_; |
| }; |
| |
| const size_t kDefaultAppsSize = 1u; |
| |
| // Verify that only default apps have been published. Web store app |
| // (hosted app) is the default app that is always loaded by chrome component |
| // extension loader. |
| void VerifyOnlyDefaultAppsPublished( |
| LacrosExtensionAppsPublisherFake* publisher) { |
| ASSERT_GE(publisher->apps_history().size(), 1u); |
| |
| Apps& default_apps = publisher->apps_history()[0]; |
| ASSERT_EQ(kDefaultAppsSize, default_apps.size()); |
| |
| auto& default_app = default_apps[0]; |
| Profile* profile = nullptr; |
| const extensions::Extension* extension = nullptr; |
| bool success = lacros_extensions_util::GetProfileAndExtension( |
| default_app->app_id, &profile, &extension); |
| ASSERT_TRUE(success); |
| ASSERT_TRUE(extension->is_hosted_app()); |
| ASSERT_EQ(extensions::kWebStoreAppId, extension->id()); |
| ASSERT_TRUE(default_app->is_platform_app.has_value()); |
| ASSERT_FALSE(default_app->is_platform_app.value()); |
| } |
| |
| // Adds a fake media device with the specified `stream_type` and starts |
| // capturing. Returns a closure to stop the capturing. |
| base::OnceClosure StartMediaCapture(content::WebContents* web_contents, |
| blink::mojom::MediaStreamType stream_type) { |
| blink::mojom::StreamDevices fake_devices; |
| blink::MediaStreamDevice device(stream_type, "fake_device", "fake_device"); |
| |
| if (blink::IsAudioInputMediaType(stream_type)) { |
| fake_devices.audio_device = device; |
| } else { |
| fake_devices.video_device = device; |
| } |
| |
| std::unique_ptr<content::MediaStreamUI> ui = |
| MediaCaptureDevicesDispatcher::GetInstance() |
| ->GetMediaStreamCaptureIndicator() |
| ->RegisterMediaStream(web_contents, fake_devices); |
| |
| ui->OnStarted(base::RepeatingClosure(), |
| content::MediaStreamUI::SourceCallback(), |
| /*label=*/std::string(), /*screen_capture_ids=*/{}, |
| content::MediaStreamUI::StateChangeCallback()); |
| |
| return base::BindOnce( |
| [](std::unique_ptr<content::MediaStreamUI> ui) { ui.reset(); }, |
| std::move(ui)); |
| } |
| |
| using LacrosExtensionAppsPublisherTest = extensions::ExtensionBrowserTest; |
| |
| // When publisher is created and initialized, only chrome default apps |
| // should be published. |
| IN_PROC_BROWSER_TEST_F(LacrosExtensionAppsPublisherTest, DefaultApps) { |
| LoadExtension(test_data_dir_.AppendASCII("simple_with_file")); |
| std::unique_ptr<LacrosExtensionAppsPublisherFake> publisher = |
| std::make_unique<LacrosExtensionAppsPublisherFake>(); |
| ASSERT_TRUE(publisher->apps_history().empty()); |
| publisher->Initialize(); |
| VerifyOnlyDefaultAppsPublished(publisher.get()); |
| } |
| |
| // When publisher is created and initialized, only chrome default apps |
| // should be published. |
| IN_PROC_BROWSER_TEST_F(LacrosExtensionAppsPublisherTest, |
| SystemFeaturesPoliciesWorkAsExpected) { |
| base::Value::List system_features; |
| system_features.Append(static_cast<int>(policy::SystemFeature::kWebStore)); |
| g_browser_process->local_state()->SetList( |
| policy::policy_prefs::kSystemFeaturesDisableList, |
| std::move(system_features)); |
| g_browser_process->local_state()->SetString( |
| policy::policy_prefs::kSystemFeaturesDisableMode, |
| policy::kBlockedDisableMode); |
| |
| LoadExtension(test_data_dir_.AppendASCII("simple_with_file")); |
| std::unique_ptr<LacrosExtensionAppsPublisherFake> publisher = |
| std::make_unique<LacrosExtensionAppsPublisherFake>(); |
| ASSERT_TRUE(publisher->apps_history().empty()); |
| publisher->Initialize(); |
| |
| // Verify that the Web Store has been published at least one time |
| ASSERT_GE(publisher->apps_history().size(), 1u); |
| |
| // Set the policy to disable the Web Store app. |
| const apps::App* latest_web_store_app = |
| publisher->GetLastPublishedChangeForExtensionId(); |
| EXPECT_EQ(latest_web_store_app->readiness, |
| apps::Readiness::kDisabledByPolicy); |
| ASSERT_TRUE(latest_web_store_app->show_in_launcher.has_value()); |
| EXPECT_TRUE(latest_web_store_app->show_in_launcher.value()); |
| ASSERT_TRUE(latest_web_store_app->show_in_shelf.has_value()); |
| EXPECT_TRUE(latest_web_store_app->show_in_shelf.value()); |
| ASSERT_TRUE(latest_web_store_app->show_in_search.has_value()); |
| EXPECT_TRUE(latest_web_store_app->show_in_search.value()); |
| ASSERT_TRUE(latest_web_store_app->show_in_management.has_value()); |
| EXPECT_TRUE(latest_web_store_app->show_in_management.value()); |
| |
| // Set the policy to hide apps, Web Store should be republished as disable and |
| // hidden. |
| g_browser_process->local_state()->SetString( |
| policy::policy_prefs::kSystemFeaturesDisableMode, |
| policy::kHiddenDisableMode); |
| |
| latest_web_store_app = publisher->GetLastPublishedChangeForExtensionId(); |
| EXPECT_EQ(latest_web_store_app->readiness, |
| apps::Readiness::kDisabledByPolicy); |
| ASSERT_TRUE(latest_web_store_app->show_in_launcher.has_value()); |
| EXPECT_FALSE(latest_web_store_app->show_in_launcher.value()); |
| ASSERT_TRUE(latest_web_store_app->show_in_shelf.has_value()); |
| EXPECT_FALSE(latest_web_store_app->show_in_shelf.value()); |
| ASSERT_TRUE(latest_web_store_app->show_in_search.has_value()); |
| EXPECT_FALSE(latest_web_store_app->show_in_search.value()); |
| ASSERT_TRUE(latest_web_store_app->show_in_management.has_value()); |
| EXPECT_FALSE(latest_web_store_app->show_in_management.value()); |
| |
| // Reset the policy, Web Store should be republished as enabled. |
| g_browser_process->local_state()->SetList( |
| policy::policy_prefs::kSystemFeaturesDisableList, base::Value::List()); |
| latest_web_store_app = publisher->GetLastPublishedChangeForExtensionId(); |
| EXPECT_EQ(latest_web_store_app->readiness, apps::Readiness::kReady); |
| ASSERT_TRUE(latest_web_store_app->show_in_launcher.has_value()); |
| EXPECT_TRUE(latest_web_store_app->show_in_launcher.value()); |
| ASSERT_TRUE(latest_web_store_app->show_in_shelf.has_value()); |
| EXPECT_TRUE(latest_web_store_app->show_in_shelf.value()); |
| ASSERT_TRUE(latest_web_store_app->show_in_search.has_value()); |
| EXPECT_TRUE(latest_web_store_app->show_in_search.value()); |
| ASSERT_TRUE(latest_web_store_app->show_in_management.has_value()); |
| EXPECT_TRUE(latest_web_store_app->show_in_management.value()); |
| } |
| |
| // If the profile has one app installed, then creating a publisher should |
| // immediately result in a call to Publish() with 1 entry. |
| IN_PROC_BROWSER_TEST_F(LacrosExtensionAppsPublisherTest, OneApp) { |
| LoadExtension(test_data_dir_.AppendASCII("simple_with_file")); |
| LoadExtension(test_data_dir_.AppendASCII("platform_apps/minimal")); |
| std::unique_ptr<LacrosExtensionAppsPublisherFake> publisher = |
| std::make_unique<LacrosExtensionAppsPublisherFake>(); |
| publisher->Initialize(); |
| ASSERT_GE(publisher->apps_history().size(), 1u); |
| Apps& apps = publisher->apps_history()[0]; |
| // The platform app is added after the default apps. |
| ASSERT_EQ(kDefaultAppsSize + 1u, apps.size()); |
| auto& platform_app = apps.back(); |
| ASSERT_TRUE(platform_app->is_platform_app.has_value()); |
| ASSERT_TRUE(platform_app->is_platform_app.value()); |
| } |
| |
| // Same as OneApp, but with two pre-installed apps. |
| IN_PROC_BROWSER_TEST_F(LacrosExtensionAppsPublisherTest, TwoApps) { |
| LoadExtension(test_data_dir_.AppendASCII("simple_with_file")); |
| LoadExtension(test_data_dir_.AppendASCII("platform_apps/minimal")); |
| LoadExtension(test_data_dir_.AppendASCII("platform_apps/minimal_id")); |
| std::unique_ptr<LacrosExtensionAppsPublisherFake> publisher = |
| std::make_unique<LacrosExtensionAppsPublisherFake>(); |
| publisher->Initialize(); |
| ASSERT_GE(publisher->apps_history().size(), 1u); |
| Apps& apps = publisher->apps_history()[0]; |
| // The platform apps are added after the default apps. |
| ASSERT_EQ(kDefaultAppsSize + 2u, apps.size()); |
| auto& platform_app_1 = apps[kDefaultAppsSize]; |
| ASSERT_TRUE(platform_app_1->is_platform_app.has_value()); |
| ASSERT_TRUE(platform_app_1->is_platform_app.value()); |
| auto& platform_app_2 = apps[kDefaultAppsSize + 1]; |
| ASSERT_TRUE(platform_app_2->is_platform_app.has_value()); |
| ASSERT_TRUE(platform_app_2->is_platform_app.value()); |
| } |
| |
| // If an app is installed after the AppsPublisher is created, there should be a |
| // corresponding event. |
| IN_PROC_BROWSER_TEST_F(LacrosExtensionAppsPublisherTest, |
| InstallAppAfterCreate) { |
| std::unique_ptr<LacrosExtensionAppsPublisherFake> publisher = |
| std::make_unique<LacrosExtensionAppsPublisherFake>(); |
| ASSERT_TRUE(publisher->apps_history().empty()); |
| publisher->Initialize(); |
| VerifyOnlyDefaultAppsPublished(publisher.get()); |
| ASSERT_GE(publisher->apps_history().size(), 1u); |
| |
| LoadExtension(test_data_dir_.AppendASCII("platform_apps/minimal")); |
| ASSERT_GE(publisher->apps_history().size(), 2u); |
| Apps& apps = publisher->apps_history().back(); |
| ASSERT_EQ(1u, apps.size()); |
| auto& platform_app = apps.back(); |
| ASSERT_TRUE(platform_app->is_platform_app.has_value()); |
| ASSERT_TRUE(platform_app->is_platform_app.value()); |
| } |
| |
| // If an app is unloaded, there should be a corresponding unload event. |
| IN_PROC_BROWSER_TEST_F(LacrosExtensionAppsPublisherTest, Unload) { |
| const extensions::Extension* extension = |
| LoadExtension(test_data_dir_.AppendASCII("platform_apps/minimal")); |
| std::unique_ptr<LacrosExtensionAppsPublisherFake> publisher = |
| std::make_unique<LacrosExtensionAppsPublisherFake>(); |
| publisher->Initialize(); |
| UnloadExtension(extension->id()); |
| |
| ASSERT_GE(publisher->apps_history().size(), 2u); |
| |
| // The first event should be a ready event. |
| { |
| Apps& apps = publisher->apps_history()[0]; |
| ASSERT_EQ(kDefaultAppsSize + 1u, apps.size()); |
| ASSERT_EQ(apps.back()->readiness, apps::Readiness::kReady); |
| } |
| |
| // The last event should be an unload event. |
| { |
| Apps& apps = publisher->apps_history().back(); |
| ASSERT_EQ(1u, apps.size()); |
| ASSERT_EQ(apps[0]->readiness, apps::Readiness::kDisabledByUser); |
| } |
| } |
| |
| // If an app is uninstalled, there should be a corresponding uninstall event. |
| IN_PROC_BROWSER_TEST_F(LacrosExtensionAppsPublisherTest, Uninstall) { |
| const extensions::Extension* extension = |
| LoadExtension(test_data_dir_.AppendASCII("platform_apps/minimal")); |
| std::unique_ptr<LacrosExtensionAppsPublisherFake> publisher = |
| std::make_unique<LacrosExtensionAppsPublisherFake>(); |
| publisher->Initialize(); |
| UninstallExtension(extension->id()); |
| |
| ASSERT_GE(publisher->apps_history().size(), 2u); |
| |
| // The first event should be a ready event. |
| { |
| Apps& apps = publisher->apps_history()[0]; |
| ASSERT_EQ(2u, apps.size()); |
| ASSERT_EQ(apps[1]->readiness, apps::Readiness::kReady); |
| } |
| |
| // The last event should be an uninstall event. |
| { |
| Apps& apps = publisher->apps_history().back(); |
| ASSERT_EQ(1u, apps.size()); |
| ASSERT_EQ(apps[0]->readiness, apps::Readiness::kUninstalledByUser); |
| } |
| } |
| |
| // If the app window is loaded after to creating the publisher, everything |
| // should still work. |
| IN_PROC_BROWSER_TEST_F(LacrosExtensionAppsPublisherTest, LaunchAppWindow) { |
| std::unique_ptr<LacrosExtensionAppsPublisherFake> publisher = |
| std::make_unique<LacrosExtensionAppsPublisherFake>(); |
| publisher->Initialize(); |
| |
| // There should be no windows tracked. |
| { |
| auto& app_windows = publisher->app_windows(); |
| ASSERT_EQ(0u, app_windows.size()); |
| } |
| |
| // Load and launch the app. |
| const extensions::Extension* extension = |
| LoadAndLaunchApp(test_data_dir_.AppendASCII("platform_apps/minimal")); |
| auto* registry = extensions::AppWindowRegistry::Get(profile()); |
| extensions::AppWindow* app_window = |
| registry->GetCurrentAppWindowForApp(extension->id()); |
| ASSERT_TRUE(app_window); |
| |
| // Check that the window is tracked correctly. |
| { |
| auto& app_windows = publisher->app_windows(); |
| ASSERT_EQ(1u, app_windows.size()); |
| EXPECT_EQ(app_windows.begin()->second, extension->id()); |
| EXPECT_EQ(app_windows.begin()->first, |
| lacros_window_utility::GetRootWindowUniqueId( |
| app_window->GetNativeWindow())); |
| } |
| |
| // Check that the window is no longer tracked. This process is asynchronous. |
| app_window->GetBaseWindow()->Close(); |
| base::RunLoop run_loop; |
| run_loop.RunUntilIdle(); |
| |
| { |
| auto& app_windows = publisher->app_windows(); |
| ASSERT_EQ(0u, app_windows.size()); |
| } |
| } |
| |
| // If the app window is loaded prior to creating the publisher, everything |
| // should still work. |
| IN_PROC_BROWSER_TEST_F(LacrosExtensionAppsPublisherTest, PreLaunchAppWindow) { |
| const extensions::Extension* extension = |
| LoadAndLaunchApp(test_data_dir_.AppendASCII("platform_apps/minimal")); |
| auto* registry = extensions::AppWindowRegistry::Get(profile()); |
| extensions::AppWindow* app_window = |
| registry->GetCurrentAppWindowForApp(extension->id()); |
| ASSERT_TRUE(app_window); |
| |
| std::unique_ptr<LacrosExtensionAppsPublisherFake> publisher = |
| std::make_unique<LacrosExtensionAppsPublisherFake>(); |
| publisher->Initialize(); |
| |
| // Check that the window is tracked correctly. |
| { |
| auto& app_windows = publisher->app_windows(); |
| ASSERT_EQ(1u, app_windows.size()); |
| EXPECT_EQ(app_windows.begin()->second, extension->id()); |
| EXPECT_EQ(app_windows.begin()->first, |
| lacros_window_utility::GetRootWindowUniqueId( |
| app_window->GetNativeWindow())); |
| } |
| |
| // Check that the window is no longer tracked. This process is asynchronous. |
| app_window->GetBaseWindow()->Close(); |
| base::RunLoop run_loop; |
| run_loop.RunUntilIdle(); |
| |
| { |
| auto& app_windows = publisher->app_windows(); |
| ASSERT_EQ(0u, app_windows.size()); |
| } |
| } |
| |
| // Verify AppCapabilityAccess is modified for Chrome apps. |
| IN_PROC_BROWSER_TEST_F(LacrosExtensionAppsPublisherTest, |
| RequestAccessingForPlatformApp) { |
| const extensions::Extension* extension = |
| LoadAndLaunchApp(test_data_dir_.AppendASCII("platform_apps/minimal")); |
| ASSERT_TRUE(extension); |
| |
| std::unique_ptr<LacrosExtensionAppsPublisherFake> publisher = |
| std::make_unique<LacrosExtensionAppsPublisherFake>(); |
| publisher->Initialize(); |
| |
| auto* registry = extensions::AppWindowRegistry::Get(profile()); |
| extensions::AppWindow* app_window = |
| registry->GetCurrentAppWindowForApp(extension->id()); |
| ASSERT_TRUE(app_window); |
| content::WebContents* web_contents = app_window->web_contents(); |
| ASSERT_TRUE(web_contents); |
| |
| // Request accessing the camera for `web_contents`. |
| base::OnceClosure video_closure = StartMediaCapture( |
| web_contents, blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE); |
| publisher->VerifyAppCapabilityAccess(extension->id(), 1u, true, std::nullopt); |
| |
| // Request accessing the microphone for `web_contents`. |
| base::OnceClosure audio_closure = StartMediaCapture( |
| web_contents, blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE); |
| publisher->VerifyAppCapabilityAccess(extension->id(), 2u, std::nullopt, true); |
| |
| // Stop accessing the microphone for `web_contents`. |
| std::move(audio_closure).Run(); |
| publisher->VerifyAppCapabilityAccess(extension->id(), 3u, std::nullopt, |
| false); |
| |
| // Stop accessing the camera for `web_contents`. |
| std::move(video_closure).Run(); |
| publisher->VerifyAppCapabilityAccess(extension->id(), 4u, false, |
| std::nullopt); |
| } |
| |
| // Verify AppCapabilityAccess for web apps is not handled by |
| // LacrosExtensionAppsPublisher. |
| IN_PROC_BROWSER_TEST_F(LacrosExtensionAppsPublisherTest, NoAccessingForWebApp) { |
| std::unique_ptr<LacrosExtensionAppsPublisherFake> publisher = |
| std::make_unique<LacrosExtensionAppsPublisherFake>(); |
| publisher->Initialize(); |
| |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = embedded_test_server()->GetURL("app.com", "/ssl/google.html"); |
| auto web_app_info = |
| web_app::WebAppInstallInfo::CreateWithStartUrlForTesting(url); |
| web_app_info->scope = url; |
| auto app_id = web_app::test::InstallWebApp(browser()->profile(), |
| std::move(web_app_info)); |
| |
| // Launch `app_id` for the web app. |
| web_app::LaunchWebAppBrowser(browser()->profile(), app_id); |
| web_app::NavigateViaLinkClickToURLAndWait(browser(), url); |
| content::WebContents* web_content = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| ASSERT_TRUE(web_content); |
| |
| // Request accessing the camera and microphone for `web_contents`. |
| base::OnceClosure video_closure1 = StartMediaCapture( |
| web_content, blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE); |
| base::OnceClosure audio_closure1 = StartMediaCapture( |
| web_content, blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE); |
| |
| // Verify the publisher does not handle the access for the web app. |
| ASSERT_TRUE(publisher->accesses_history().empty()); |
| } |
| |
| // Verify AppCapabilityAccess for browser tabs is not handled by |
| // LacrosExtensionAppsPublisher. |
| IN_PROC_BROWSER_TEST_F(LacrosExtensionAppsPublisherTest, NoAccessingForTab) { |
| std::unique_ptr<LacrosExtensionAppsPublisherFake> publisher = |
| std::make_unique<LacrosExtensionAppsPublisherFake>(); |
| publisher->Initialize(); |
| |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL( |
| browser(), |
| embedded_test_server()->GetURL("app.com", "/ssl/google.html"))); |
| |
| content::WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| |
| // Request accessing the camera and microphone for `web_contents`. |
| base::OnceClosure video_closure = StartMediaCapture( |
| web_contents, blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE); |
| base::OnceClosure audio_closure = StartMediaCapture( |
| web_contents, blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE); |
| |
| // Verify the publisher does not handle the access for the tab. |
| ASSERT_TRUE(publisher->accesses_history().empty()); |
| } |
| |
| } // namespace |