chromium/chrome/browser/apps/app_service/media_access_browsertest.cc

// 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 <memory>

#include "base/functional/callback.h"
#include "chrome/browser/apps/app_service/app_service_proxy.h"
#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
#include "chrome/browser/apps/platform_apps/app_browsertest_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/profiles/profile.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
#include "chrome/browser/ui/web_applications/web_app_browsertest_base.h"
#include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
#include "chrome/browser/web_applications/test/web_app_test_observers.h"
#include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
#include "chrome/test/base/ui_test_utils.h"
#include "chromeos/constants/chromeos_features.h"
#include "components/app_constants/constants.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/test/browser_test.h"
#include "extensions/common/extension.h"
#include "third_party/blink/public/common/mediastream/media_stream_request.h"
#include "third_party/blink/public/mojom/mediastream/media_stream.mojom.h"

using extensions::Extension;

namespace {

bool AccessingCamera(Profile* profile, const std::string& app_id) {
  std::optional<bool> accessing_camera;
  auto* proxy = apps::AppServiceProxyFactory::GetForProfile(profile);
  proxy->AppCapabilityAccessCache().ForOneApp(
      app_id, [&accessing_camera](const apps::CapabilityAccessUpdate& update) {
        accessing_camera = update.Camera();
      });
  return accessing_camera.value_or(false);
}

bool AccessingMicrophone(Profile* profile, const std::string& app_id) {
  std::optional<bool> accessing_microphone;
  auto* proxy = apps::AppServiceProxyFactory::GetForProfile(profile);
  proxy->AppCapabilityAccessCache().ForOneApp(
      app_id,
      [&accessing_microphone](const apps::CapabilityAccessUpdate& update) {
        accessing_microphone = update.Microphone();
      });
  return accessing_microphone.value_or(false);
}

// 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));
}

}  // namespace

class MediaAccessExtensionAppsTest : public extensions::PlatformAppBrowserTest {
 public:
  void SetUpOnMainThread() override {
    extensions::PlatformAppBrowserTest::SetUpOnMainThread();

    ASSERT_TRUE(embedded_test_server()->Start());
  }

  void UninstallApp(const std::string& app_id) {
    auto* proxy = apps::AppServiceProxyFactory::GetForProfile(profile());
    proxy->UninstallSilently(app_id, apps::UninstallSource::kAppList);
  }

  GURL GetUrl1() {
    return embedded_test_server()->GetURL("app.com", "/ssl/google.html");
  }

  GURL GetUrl2() {
    return embedded_test_server()->GetURL("app.com", "/google/google.html");
  }

  content::WebContents* GetWebContents() {
    return browser()->tab_strip_model()->GetActiveWebContents();
  }
};

IN_PROC_BROWSER_TEST_F(MediaAccessExtensionAppsTest,
                       RequestAccessingForChromeInTabs) {
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetUrl1()));

  content::WebContents* web_content1 = GetWebContents();
  // Request accessing the camera for |web_content1|.
  base::OnceClosure video_closure1 = StartMediaCapture(
      web_content1, blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE);
  EXPECT_TRUE(
      AccessingCamera(browser()->profile(), app_constants::kChromeAppId));
  EXPECT_FALSE(
      AccessingMicrophone(browser()->profile(), app_constants::kChromeAppId));

  AddBlankTabAndShow(browser());
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetUrl2()));
  content::WebContents* web_content2 = GetWebContents();
  // Request accessing the microphone for |web_content2|.
  base::OnceClosure audio_closure1 = StartMediaCapture(
      web_content2, blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE);

  EXPECT_TRUE(
      AccessingCamera(browser()->profile(), app_constants::kChromeAppId));
  EXPECT_TRUE(
      AccessingMicrophone(browser()->profile(), app_constants::kChromeAppId));

  // Stop accessing the camera for |web_content1|.
  std::move(video_closure1).Run();
  EXPECT_FALSE(
      AccessingCamera(browser()->profile(), app_constants::kChromeAppId));
  EXPECT_TRUE(
      AccessingMicrophone(browser()->profile(), app_constants::kChromeAppId));

  // Stop accessing the microphone for |web_content2|.
  std::move(audio_closure1).Run();
  EXPECT_FALSE(
      AccessingCamera(browser()->profile(), app_constants::kChromeAppId));
  EXPECT_FALSE(
      AccessingMicrophone(browser()->profile(), app_constants::kChromeAppId));
}

IN_PROC_BROWSER_TEST_F(MediaAccessExtensionAppsTest,
                       RequestAccessingForChromeInNewBrowsers) {
  Browser* browser1 =
      Browser::Create(Browser::CreateParams(browser()->profile(), true));
  ASSERT_TRUE(browser1);
  ASSERT_NE(browser(), browser1);

  AddBlankTabAndShow(browser1);
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser1, GetUrl1()));
  content::WebContents* web_content1 =
      browser1->tab_strip_model()->GetActiveWebContents();
  // Request accessing the camera and the microphone for |web_content1|.
  base::OnceClosure video_closure1 = StartMediaCapture(
      web_content1, blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE);
  base::OnceClosure audio_closure1 = StartMediaCapture(
      web_content1, blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE);
  EXPECT_TRUE(
      AccessingCamera(browser()->profile(), app_constants::kChromeAppId));
  EXPECT_TRUE(
      AccessingMicrophone(browser()->profile(), app_constants::kChromeAppId));

  AddBlankTabAndShow(browser1);
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser1, GetUrl2()));
  content::WebContents* web_content2 =
      browser1->tab_strip_model()->GetActiveWebContents();
  // Request accessing the camera and the microphone for |web_content2|.
  base::OnceClosure video_closure2 = StartMediaCapture(
      web_content2, blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE);
  base::OnceClosure audio_closure2 = StartMediaCapture(
      web_content2, blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE);
  EXPECT_TRUE(
      AccessingCamera(browser()->profile(), app_constants::kChromeAppId));
  EXPECT_TRUE(
      AccessingMicrophone(browser()->profile(), app_constants::kChromeAppId));

  // Close the tab for |web_content2|.
  browser1->tab_strip_model()->CloseSelectedTabs();
  EXPECT_TRUE(
      AccessingCamera(browser()->profile(), app_constants::kChromeAppId));
  EXPECT_TRUE(
      AccessingMicrophone(browser()->profile(), app_constants::kChromeAppId));
  std::move(video_closure2).Run();
  std::move(audio_closure2).Run();

  EXPECT_TRUE(
      AccessingCamera(browser()->profile(), app_constants::kChromeAppId));
  EXPECT_TRUE(
      AccessingMicrophone(browser()->profile(), app_constants::kChromeAppId));

  // Close the tab for |web_content1|.
  browser1->tab_strip_model()->CloseSelectedTabs();
  EXPECT_FALSE(
      AccessingCamera(browser()->profile(), app_constants::kChromeAppId));
  EXPECT_FALSE(
      AccessingMicrophone(browser()->profile(), app_constants::kChromeAppId));

  std::move(video_closure1).Run();
  std::move(audio_closure1).Run();

  EXPECT_FALSE(
      AccessingCamera(browser()->profile(), app_constants::kChromeAppId));
  EXPECT_FALSE(
      AccessingMicrophone(browser()->profile(), app_constants::kChromeAppId));
}

IN_PROC_BROWSER_TEST_F(MediaAccessExtensionAppsTest,
                       RequestAccessingForPlatformApp) {
  const Extension* extension =
      LoadAndLaunchPlatformApp("context_menu", "Launched");
  ASSERT_TRUE(extension);

  content::WebContents* web_contents = GetFirstAppWindowWebContents();
  ASSERT_TRUE(web_contents);

  // Request accessing the camera for |web_contents|.
  base::OnceClosure video_closure = StartMediaCapture(
      web_contents, blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE);

  EXPECT_TRUE(AccessingCamera(browser()->profile(), extension->id()));
  EXPECT_FALSE(AccessingMicrophone(browser()->profile(), extension->id()));

  // Request accessing the microphone for |web_contents|.
  base::OnceClosure audio_closure = StartMediaCapture(
      web_contents, blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE);

  EXPECT_TRUE(AccessingCamera(browser()->profile(), extension->id()));
  EXPECT_TRUE(AccessingMicrophone(browser()->profile(), extension->id()));

  // Stop accessing the microphone for |web_contents|.
  std::move(audio_closure).Run();

  EXPECT_TRUE(AccessingCamera(browser()->profile(), extension->id()));
  EXPECT_FALSE(AccessingMicrophone(browser()->profile(), extension->id()));

  // Stop accessing the camera for |web_contents|.
  std::move(video_closure).Run();

  EXPECT_FALSE(AccessingCamera(browser()->profile(), extension->id()));
  EXPECT_FALSE(AccessingMicrophone(browser()->profile(), extension->id()));
}

IN_PROC_BROWSER_TEST_F(MediaAccessExtensionAppsTest,
                       RequestAccessingForHostApp) {
  const Extension* extension =
      LoadExtension(test_data_dir_.AppendASCII("app1"));
  ASSERT_TRUE(extension);

  // Navigate to the app's launch URL.
  auto url = extensions::AppLaunchInfo::GetLaunchWebURL(extension);
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));

  content::WebContents* web_content1 =
      browser()->tab_strip_model()->GetActiveWebContents();
  ASSERT_TRUE(web_content1);

  // Request accessing the camera and microphone for |web_contents|.
  base::OnceClosure video_closure1 = StartMediaCapture(
      web_content1, blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE);
  base::OnceClosure audio_closure1 = StartMediaCapture(
      web_content1, blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE);

  EXPECT_TRUE(AccessingCamera(browser()->profile(), extension->id()));
  EXPECT_TRUE(AccessingMicrophone(browser()->profile(), extension->id()));

  AddBlankTabAndShow(browser());
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetUrl1()));
  content::WebContents* web_content2 = GetWebContents();

  // Request accessing the camera for |web_content2|.
  base::OnceClosure video_closure2 = StartMediaCapture(
      web_content2, blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE);

  EXPECT_TRUE(AccessingCamera(browser()->profile(), extension->id()));
  EXPECT_TRUE(AccessingMicrophone(browser()->profile(), extension->id()));
  EXPECT_TRUE(
      AccessingCamera(browser()->profile(), app_constants::kChromeAppId));
  EXPECT_FALSE(
      AccessingMicrophone(browser()->profile(), app_constants::kChromeAppId));

  // Uninstall the app.
  std::string app_id = extension->id();
  UninstallApp(app_id);

  EXPECT_FALSE(AccessingCamera(browser()->profile(), app_id));
  EXPECT_FALSE(AccessingMicrophone(browser()->profile(), app_id));
  EXPECT_TRUE(
      AccessingCamera(browser()->profile(), app_constants::kChromeAppId));
  EXPECT_FALSE(
      AccessingMicrophone(browser()->profile(), app_constants::kChromeAppId));

  // Stop accessing the camera for |web_content2|.
  std::move(video_closure2).Run();

  EXPECT_FALSE(AccessingCamera(browser()->profile(), app_id));
  EXPECT_FALSE(AccessingMicrophone(browser()->profile(), app_id));
  EXPECT_FALSE(
      AccessingCamera(browser()->profile(), app_constants::kChromeAppId));
  EXPECT_FALSE(
      AccessingMicrophone(browser()->profile(), app_constants::kChromeAppId));
}

IN_PROC_BROWSER_TEST_F(MediaAccessExtensionAppsTest,
                       RequestAccessingStreamTypesForChromeInTabs) {
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetUrl1()));

  content::WebContents* web_contents = GetWebContents();
  // Request DEVICE_VIDEO_CAPTURE accessing the camera for |web_contents|.
  base::OnceClosure video_closure = StartMediaCapture(
      web_contents, blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE);
  EXPECT_TRUE(
      AccessingCamera(browser()->profile(), app_constants::kChromeAppId));
  EXPECT_FALSE(
      AccessingMicrophone(browser()->profile(), app_constants::kChromeAppId));

  // Request GUM_DESKTOP_VIDEO_CAPTURE accessing the camera for |web_contents|.
  base::OnceClosure desktop_closure = StartMediaCapture(
      web_contents, blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE);
  EXPECT_TRUE(
      AccessingCamera(browser()->profile(), app_constants::kChromeAppId));
  EXPECT_FALSE(
      AccessingMicrophone(browser()->profile(), app_constants::kChromeAppId));

  // Stop GUM_DESKTOP_VIDEO_CAPTURE accessing the camera for |web_contents|.
  std::move(desktop_closure).Run();
  EXPECT_TRUE(
      AccessingCamera(browser()->profile(), app_constants::kChromeAppId));
  EXPECT_FALSE(
      AccessingMicrophone(browser()->profile(), app_constants::kChromeAppId));

  // Stop DEVICE_VIDEO_CAPTURE accessing the camera for |web_contents|.
  std::move(video_closure).Run();
  EXPECT_FALSE(
      AccessingCamera(browser()->profile(), app_constants::kChromeAppId));
  EXPECT_FALSE(
      AccessingMicrophone(browser()->profile(), app_constants::kChromeAppId));
}

class MediaAccessWebAppsTest : public web_app::WebAppBrowserTestBase {
 public:
  MediaAccessWebAppsTest() = default;
  ~MediaAccessWebAppsTest() override = default;

  std::string CreateWebApp(const GURL& url) const {
    auto web_app_info =
        web_app::WebAppInstallInfo::CreateWithStartUrlForTesting(url);
    web_app_info->scope = url;
    return web_app::test::InstallWebApp(browser()->profile(),
                                        std::move(web_app_info));
  }

  void UninstallWebApp(const std::string& app_id) const {
    web_app::WebAppTestUninstallObserver app_listener(browser()->profile());
    app_listener.BeginListening();
    web_app::test::UninstallWebApp(browser()->profile(), app_id);
    app_listener.Wait();
  }

  GURL GetUrl1() {
    return https_server()->GetURL("app.com", "/ssl/google.html");
  }

  GURL GetUrl2() {
    return https_server()->GetURL("app.com", "/google/google.html");
  }

  content::WebContents* GetWebContents() {
    return browser()->tab_strip_model()->GetActiveWebContents();
  }
};

IN_PROC_BROWSER_TEST_F(MediaAccessWebAppsTest, RequestAccessingCamera) {
  std::string app_id = CreateWebApp(GetUrl1());

  // Launch |app_id| in a new tab.
  web_app::LaunchWebAppBrowser(browser()->profile(), app_id);
  web_app::NavigateViaLinkClickToURLAndWait(browser(), GetUrl1());

  // Request accessing the camera for |app_id| in the new tab.
  content::WebContents* web_content1 = GetWebContents();
  base::OnceClosure video_closure1 = StartMediaCapture(
      web_content1, blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE);
  EXPECT_TRUE(AccessingCamera(browser()->profile(), app_id));

  // Launch |app_id| in a new window.
  content::WebContents* web_content2 = OpenApplication(app_id);
  Browser* app_browser = BrowserList::GetInstance()->GetLastActive();
  ASSERT_TRUE(app_browser);
  ASSERT_NE(browser(), app_browser);

  // Request accessing the camera for |app_id| in the new window.
  base::OnceClosure video_closure2 = StartMediaCapture(
      web_content2, blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE);
  EXPECT_TRUE(AccessingCamera(browser()->profile(), app_id));

  // Stop accessing the camera for |app_id| in the tab.
  std::move(video_closure1).Run();
  EXPECT_TRUE(AccessingCamera(browser()->profile(), app_id));

  // Stop accessing the camera for |app_id| in the window.
  std::move(video_closure2).Run();
  EXPECT_FALSE(AccessingCamera(browser()->profile(), app_id));

  web_app::CloseAndWait(app_browser);
  EXPECT_FALSE(AccessingCamera(browser()->profile(), app_id));
  web_app::CloseAndWait(browser());
}

IN_PROC_BROWSER_TEST_F(MediaAccessWebAppsTest, RequestAccessingMicrophone) {
  std::string app_id = CreateWebApp(GetUrl1());

  // Launch |app_id| in a new tab.
  web_app::LaunchWebAppBrowser(browser()->profile(), app_id);
  web_app::NavigateViaLinkClickToURLAndWait(browser(), GetUrl1());

  // Request accessing the microphone for |app_id| in the new tab.
  content::WebContents* web_content1 = GetWebContents();
  base::OnceClosure audio_closure1 = StartMediaCapture(
      web_content1, blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE);
  EXPECT_TRUE(AccessingMicrophone(browser()->profile(), app_id));

  // Launch |app_id| in a new window.
  content::WebContents* web_content2 = OpenApplication(app_id);
  Browser* app_browser = BrowserList::GetInstance()->GetLastActive();
  ASSERT_TRUE(app_browser);
  ASSERT_NE(browser(), app_browser);

  // Request accessing the microphone for |app_id| in the new window.
  base::OnceClosure audio_closure2 = StartMediaCapture(
      web_content2, blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE);
  EXPECT_TRUE(AccessingMicrophone(browser()->profile(), app_id));

  // Close browsers.
  web_app::CloseAndWait(app_browser);
  EXPECT_TRUE(AccessingMicrophone(browser()->profile(), app_id));

  // Stop accessing the microphone for |app_id| in the tab.
  std::move(audio_closure1).Run();
  EXPECT_FALSE(AccessingMicrophone(browser()->profile(), app_id));

  // Stop accessing the microphone for |app_id| in the window.
  std::move(audio_closure2).Run();
  EXPECT_FALSE(AccessingMicrophone(browser()->profile(), app_id));
}

IN_PROC_BROWSER_TEST_F(MediaAccessWebAppsTest, RemoveApp) {
  std::string app_id = CreateWebApp(GetUrl1());

  // Launch |app_id| in a new tab.
  web_app::LaunchWebAppBrowser(browser()->profile(), app_id);
  web_app::NavigateViaLinkClickToURLAndWait(browser(), GetUrl1());

  // Request accessing the camera and the microphone for |app_id| in the new
  // tab.
  content::WebContents* web_content1 = GetWebContents();
  base::OnceClosure audio_closure1 = StartMediaCapture(
      web_content1, blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE);
  base::OnceClosure video_closure1 = StartMediaCapture(
      web_content1, blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE);
  EXPECT_TRUE(AccessingMicrophone(browser()->profile(), app_id));
  EXPECT_TRUE(AccessingCamera(browser()->profile(), app_id));

  // Launch |app_id| in a new window.
  content::WebContents* web_content2 = OpenApplication(app_id);
  Browser* app_browser = BrowserList::GetInstance()->GetLastActive();
  ASSERT_TRUE(app_browser);
  ASSERT_NE(browser(), app_browser);

  // Request accessing the camera and the microphone for |app_id| in the new
  // window.
  base::OnceClosure audio_closure2 = StartMediaCapture(
      web_content2, blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE);
  base::OnceClosure video_closure2 = StartMediaCapture(
      web_content2, blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE);
  EXPECT_TRUE(AccessingMicrophone(browser()->profile(), app_id));
  EXPECT_TRUE(AccessingCamera(browser()->profile(), app_id));

  UninstallWebApp(app_id);
  EXPECT_FALSE(AccessingCamera(browser()->profile(), app_id));
  EXPECT_FALSE(AccessingMicrophone(browser()->profile(), app_id));

  CreateWebApp(GetUrl1());

  EXPECT_FALSE(AccessingCamera(browser()->profile(), app_id));
  EXPECT_FALSE(AccessingMicrophone(browser()->profile(), app_id));
}

IN_PROC_BROWSER_TEST_F(MediaAccessWebAppsTest, TwoApps) {
  std::string app_id1 = CreateWebApp(GetUrl1());

  // Launch |app_id1| in a new tab.
  web_app::LaunchWebAppBrowser(browser()->profile(), app_id1);
  web_app::NavigateViaLinkClickToURLAndWait(browser(), GetUrl1());

  // Request accessing the camera and the microphone for |app_id1| in the new
  // tab.
  content::WebContents* web_content1 = GetWebContents();
  base::OnceClosure audio_closure1 = StartMediaCapture(
      web_content1, blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE);
  base::OnceClosure video_closure1 = StartMediaCapture(
      web_content1, blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE);
  EXPECT_TRUE(AccessingMicrophone(browser()->profile(), app_id1));
  EXPECT_TRUE(AccessingCamera(browser()->profile(), app_id1));

  std::string app_id2 = CreateWebApp(GetUrl2());

  // Launch |app_id2| in a new window.
  content::WebContents* web_content2 = OpenApplication(app_id2);
  Browser* app_browser = BrowserList::GetInstance()->GetLastActive();
  ASSERT_TRUE(app_browser);
  ASSERT_NE(browser(), app_browser);

  // Request accessing the camera and the microphone for |app_id2| in the new
  // window.
  base::OnceClosure audio_closure2 = StartMediaCapture(
      web_content2, blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE);
  base::OnceClosure video_closure2 = StartMediaCapture(
      web_content2, blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE);
  EXPECT_TRUE(AccessingMicrophone(browser()->profile(), app_id2));
  EXPECT_TRUE(AccessingCamera(browser()->profile(), app_id2));

  UninstallWebApp(app_id1);
  EXPECT_FALSE(AccessingCamera(browser()->profile(), app_id1));
  EXPECT_FALSE(AccessingMicrophone(browser()->profile(), app_id1));
  EXPECT_TRUE(AccessingMicrophone(browser()->profile(), app_id2));
  EXPECT_TRUE(AccessingCamera(browser()->profile(), app_id2));
  std::move(audio_closure1).Run();
  std::move(video_closure1).Run();

  // Stop accessing the camera and the microphone for |app_id2|.
  std::move(audio_closure2).Run();
  std::move(video_closure2).Run();
  EXPECT_FALSE(AccessingCamera(browser()->profile(), app_id2));
  EXPECT_FALSE(AccessingCamera(browser()->profile(), app_id2));

  // Navigate to Url1, and check |app_id1| is not accessing the camera or the
  // microphone, because it has been removed.
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetUrl1()));
  auto* web_content3 = GetWebContents();
  base::OnceClosure audio_closure3 = StartMediaCapture(
      web_content3, blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE);
  base::OnceClosure video_closure3 = StartMediaCapture(
      web_content3, blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE);
  EXPECT_FALSE(AccessingCamera(browser()->profile(), app_id1));
  EXPECT_FALSE(AccessingMicrophone(browser()->profile(), app_id1));
  std::move(audio_closure3).Run();
  std::move(video_closure3).Run();

  // Navigate to Url2, and check |app_id2| is accessing the camera and the
  // microphone.
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetUrl2()));
  auto* web_content4 = GetWebContents();
  base::OnceClosure audio_closure4 = StartMediaCapture(
      web_content4, blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE);
  base::OnceClosure video_closure4 = StartMediaCapture(
      web_content4, blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE);
  EXPECT_TRUE(AccessingMicrophone(browser()->profile(), app_id2));
  EXPECT_TRUE(AccessingCamera(browser()->profile(), app_id2));
  EXPECT_FALSE(AccessingCamera(browser()->profile(), app_id1));
  EXPECT_FALSE(AccessingMicrophone(browser()->profile(), app_id1));
}

IN_PROC_BROWSER_TEST_F(MediaAccessWebAppsTest,
                       RequestAccessingCamera_DesktopCaptureAfter) {
  std::string app_id = CreateWebApp(GetUrl1());

  // Launch |app_id| in a new tab.
  web_app::LaunchWebAppBrowser(browser()->profile(), app_id);
  web_app::NavigateViaLinkClickToURLAndWait(browser(), GetUrl1());

  // Request DEVICE_VIDEO_CAPTURE accessing the camera for |app_id| in the new
  // tab.
  content::WebContents* web_contents = GetWebContents();
  base::OnceClosure video_closure = StartMediaCapture(
      web_contents, blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE);
  EXPECT_TRUE(AccessingCamera(browser()->profile(), app_id));

  // Request GUM_DESKTOP_VIDEO_CAPTURE accessing the camera for |app_id| in the
  // new tab.
  base::OnceClosure desktop_closure = StartMediaCapture(
      web_contents, blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE);
  EXPECT_TRUE(AccessingCamera(browser()->profile(), app_id));

  // Stop DEVICE_VIDEO_CAPTURE accessing the camera for |app_id| in the tab.
  std::move(video_closure).Run();
  EXPECT_FALSE(AccessingCamera(browser()->profile(), app_id));

  // Stop GUM_DESKTOP_VIDEO_CAPTURE accessing the camera for |app_id| in the
  // tab.
  std::move(desktop_closure).Run();
  EXPECT_FALSE(AccessingCamera(browser()->profile(), app_id));

  web_app::CloseAndWait(browser());
}

IN_PROC_BROWSER_TEST_F(MediaAccessWebAppsTest,
                       RequestAccessingCamera_DesktopCaptureBefore) {
  std::string app_id = CreateWebApp(GetUrl1());

  // Launch |app_id| in a new tab.
  web_app::LaunchWebAppBrowser(browser()->profile(), app_id);
  web_app::NavigateViaLinkClickToURLAndWait(browser(), GetUrl1());

  // Request GUM_DESKTOP_VIDEO_CAPTURE accessing the camera for |app_id| in the
  // new tab.
  content::WebContents* web_contents = GetWebContents();
  base::OnceClosure desktop_closure = StartMediaCapture(
      web_contents, blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE);
  EXPECT_FALSE(AccessingCamera(browser()->profile(), app_id));

  // Request DEVICE_VIDEO_CAPTURE accessing the camera for |app_id| in the new
  // tab.
  base::OnceClosure video_closure = StartMediaCapture(
      web_contents, blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE);
  EXPECT_TRUE(AccessingCamera(browser()->profile(), app_id));

  // Stop GUM_DESKTOP_VIDEO_CAPTURE accessing the camera for |app_id| in the
  // tab.
  std::move(desktop_closure).Run();
  EXPECT_TRUE(AccessingCamera(browser()->profile(), app_id));

  // Stop DEVICE_VIDEO_CAPTURE accessing the camera for |app_id| in the tab.
  std::move(video_closure).Run();
  EXPECT_FALSE(AccessingCamera(browser()->profile(), app_id));

  web_app::CloseAndWait(browser());
}