chromium/chrome/browser/media/webrtc/webrtc_getdisplaymedia_browsertest.cc

// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <optional>
#include <string>
#include <tuple>
#include <vector>

#include "base/base_switches.h"
#include "base/command_line.h"
#include "base/files/file_util.h"
#include "base/memory/raw_ptr.h"
#include "base/path_service.h"
#include "base/strings/strcat.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "build/config/chromebox_for_meetings/buildflags.h"  // PLATFORM_CFM
#include "chrome/browser/apps/platform_apps/app_browsertest_util.h"
#include "chrome/browser/media/webrtc/webrtc_browsertest_base.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_tabstrip.h"
#include "chrome/browser/ui/tab_sharing/tab_sharing_infobar_delegate.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "chrome/grit/generated_resources.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/infobars/content/content_infobar_manager.h"
#include "components/infobars/core/infobar.h"
#include "components/policy/core/browser/browser_policy_connector.h"
#include "components/policy/core/common/mock_configuration_policy_provider.h"
#include "components/policy/core/common/policy_map.h"
#include "components/policy/core/common/policy_types.h"
#include "components/policy/policy_constants.h"
#include "components/prefs/pref_service.h"
#include "components/url_formatter/elide_url.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/mock_captured_surface_controller.h"
#include "media/base/media_switches.h"
#include "net/base/filename_util.h"
#include "third_party/blink/public/common/features.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gl/gl_switches.h"

#if BUILDFLAG(IS_MAC)
#include "base/mac/mac_util.h"
#include "chrome/browser/media/webrtc/system_media_capture_permissions_mac.h"
#endif

#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "chrome/browser/chromeos/policy/dlp/dlp_content_restriction_set.h"
#include "chrome/browser/chromeos/policy/dlp/test/dlp_content_manager_test_helper.h"
#endif

namespace {

FeatureRef;
BrowserThread;
CapturedSurfaceController;
GlobalRenderFrameHostId;
MockCapturedSurfaceController;
WebContents;
WebContentsMediaCaptureId;
_;
Bool;
Combine;
Mock;
Values;
TabSharingInfoBarButton;

CapturedSurfaceControllerFactoryCallback;

static const char kMainHtmlPage[] =;
static const char kMainHtmlFileName[] =;
static const char kSameOriginRenamedTitle[] =;
static const char kMainHtmlTitle[] =;
// The captured tab is identified by its title.
static const char kCapturedTabTitle[] =;
static const char kCapturedPageMain[] =;
static const std::u16string kShareThisTabInsteadMessage =;

constexpr TabSharingInfoBarButton kCscIndicator =;

enum class DisplaySurfaceType {};

enum class GetDisplayMediaVariant : int {};

struct TestConfigForPicker {};

struct TestConfigForFakeUI {};

struct TestConfigForSelectAllScreens {};

struct TestConfigForHiDpi {};

constexpr char kAppWindowTitle[] =;

constexpr char kEmbeddedTestServerOrigin[] =;
constexpr char kOtherOrigin[] =;

std::string DisplaySurfaceTypeAsString(
    DisplaySurfaceType display_surface_type) {}

void RunGetDisplayMedia(content::WebContents* tab,
                        const std::string& constraints,
                        bool is_fake_ui,
                        bool expect_success,
                        bool is_tab_capture,
                        const std::string& expected_error = "",
                        bool with_user_gesture = true) {}

void StopAllTracks(content::WebContents* tab) {}

void UpdateWebContentsTitle(content::WebContents* contents,
                            const std::u16string& title) {}

GURL GetFileURL(const char* filename) {}

infobars::ContentInfoBarManager* GetInfoBarManager(
    content::WebContents* web_contents) {}

TabSharingInfoBarDelegate* GetDelegate(content::WebContents* web_contents,
                                       size_t infobar_index = 0) {}

bool HasCscIndicator(content::WebContents* web_contents) {}

bool HasShareThisTabInsteadButton(content::WebContents* web_contents) {}

std::u16string GetShareThisTabInsteadButtonLabel(
    content::WebContents* web_contents) {}

void AdjustCommandLineForZeroCopyCapture(base::CommandLine* command_line) {}

}  // namespace

// Base class for top level tests for getDisplayMedia().
class WebRtcScreenCaptureBrowserTest : public WebRtcTestBase {};

// Top level test for getDisplayMedia().
// Pops picker UI and shares by default.
class WebRtcScreenCaptureBrowserTestWithPicker
    : public WebRtcScreenCaptureBrowserTest,
      public testing::WithParamInterface<std::tuple<bool, bool>> {};

INSTANTIATE_TEST_SUITE_P();

// TODO(crbug.com/40744542): Real desktop capture is flaky on below platforms.
// TODO(crbug.com/41493366): enable this flaky test.
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
#define MAYBE_ScreenCaptureVideo
#else
#define MAYBE_ScreenCaptureVideo
#endif  // BUILDFLAG(IS_WIN)
IN_PROC_BROWSER_TEST_P(WebRtcScreenCaptureBrowserTestWithPicker,
                       MAYBE_ScreenCaptureVideo) {}

#if BUILDFLAG(IS_CHROMEOS_ASH)
IN_PROC_BROWSER_TEST_P(WebRtcScreenCaptureBrowserTestWithPicker,
                       ScreenCaptureVideoWithDlp) {
  if (!test_config_.should_prefer_current_tab &&
      !test_config_.accept_this_tab_capture) {
    GTEST_SKIP();
  }

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

  policy::DlpContentManagerTestHelper helper;
  content::WebContents* tab = OpenTestPageInNewTab(kMainHtmlPage);
  RunGetDisplayMedia(tab,
                     GetConstraints(
                         /*video=*/true, /*audio=*/false,
                         /*select_all_screens=*/
                         SelectAllScreens::kUndefined),
                     /*is_fake_ui=*/false,
                     /*expect_success=*/test_config_.accept_this_tab_capture,
                     /*is_tab_capture=*/PreferCurrentTab());

  if (!test_config_.accept_this_tab_capture) {
    // This test is not relevant for this parameterized test case because it
    // does not capture the tab/display surface.
    return;
  }

  EXPECT_EQ(content::EvalJs(tab->GetPrimaryMainFrame(), "waitVideoUnmuted();"),
            "unmuted");

  const policy::DlpContentRestrictionSet kScreenShareRestricted(
      policy::DlpContentRestriction::kScreenShare,
      policy::DlpRulesManager::Level::kBlock);

  helper.ChangeConfidentiality(tab, kScreenShareRestricted);
  content::WaitForLoadStop(tab);

  EXPECT_EQ(content::EvalJs(tab->GetPrimaryMainFrame(), "waitVideoMuted();"),
            "muted");

  const policy::DlpContentRestrictionSet kEmptyRestrictionSet;
  helper.ChangeConfidentiality(tab, kEmptyRestrictionSet);

  EXPECT_EQ(content::EvalJs(tab->GetPrimaryMainFrame(), "waitVideoUnmuted();"),
            "unmuted");
}
#endif  // BUILDFLAG(IS_CHROMEOS_ASH)

// TODO(crbug.com/40744542): Real desktop capture is flaky on below platforms.
// TODO(crbug.com/41493366): enable this flaky test.
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
#define MAYBE_ScreenCaptureVideoAndAudio
// On linux debug bots, it's flaky as well.
#elif ((BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)) && \
       !defined(NDEBUG))
#define MAYBE_ScreenCaptureVideoAndAudio
// On linux asan bots, it's flaky as well - msan and other rel bot are fine.
#elif ((BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)) && \
       defined(ADDRESS_SANITIZER))
#define MAYBE_ScreenCaptureVideoAndAudio
#else
#define MAYBE_ScreenCaptureVideoAndAudio
#endif  // BUILDFLAG(IS_WIN)
IN_PROC_BROWSER_TEST_P(WebRtcScreenCaptureBrowserTestWithPicker,
                       MAYBE_ScreenCaptureVideoAndAudio) {}

// Top level test for getDisplayMedia().
// Skips picker UI and uses fake device with specified type.
class WebRtcScreenCaptureBrowserTestWithFakeUI
    : public WebRtcScreenCaptureBrowserTest,
      public testing::WithParamInterface<TestConfigForFakeUI> {};

IN_PROC_BROWSER_TEST_P(WebRtcScreenCaptureBrowserTestWithFakeUI,
                       ScreenCaptureVideo) {}

IN_PROC_BROWSER_TEST_P(WebRtcScreenCaptureBrowserTestWithFakeUI,
                       ScreenCaptureVideoAndAudio) {}

IN_PROC_BROWSER_TEST_P(WebRtcScreenCaptureBrowserTestWithFakeUI,
                       ScreenCaptureWithConstraints) {}

INSTANTIATE_TEST_SUITE_P();

class WebRtcScreenCapturePermissionPolicyBrowserTest
    : public WebRtcScreenCaptureBrowserTest,
      public testing::WithParamInterface<
          std::tuple<GetDisplayMediaVariant, bool>> {};

INSTANTIATE_TEST_SUITE_P();

// Flaky on Win bots http://crbug.com/1264805
#if BUILDFLAG(IS_WIN)
#define MAYBE_ScreenShareFromEmbedded
#else
#define MAYBE_ScreenShareFromEmbedded
#endif
IN_PROC_BROWSER_TEST_P(WebRtcScreenCapturePermissionPolicyBrowserTest,
                       MAYBE_ScreenShareFromEmbedded) {}

// Test class used to test WebRTC with App Windows. Unfortunately, due to
// creating a diamond pattern of inheritance, we can only inherit from one of
// the PlatformAppBrowserTest and WebRtcBrowserTestBase (or it's children).
// We need a lot more heavy lifting on creating the AppWindow than we would get
// from WebRtcBrowserTestBase; so we inherit from PlatformAppBrowserTest to
// minimize the code duplication.
class WebRtcAppWindowCaptureBrowserTestWithPicker
    : public extensions::PlatformAppBrowserTest {};

IN_PROC_BROWSER_TEST_F(WebRtcAppWindowCaptureBrowserTestWithPicker,
                       CaptureAppWindow) {}

// Base class for running tests with a SameOrigin policy applied.
class WebRtcSameOriginPolicyBrowserTest
    : public WebRtcScreenCaptureBrowserTest {};

IN_PROC_BROWSER_TEST_F(WebRtcSameOriginPolicyBrowserTest,
                       TerminateOnNavigationAwayFromSameOrigin) {}

IN_PROC_BROWSER_TEST_F(WebRtcSameOriginPolicyBrowserTest,
                       ContinueCapturingForSameOriginNavigation) {}

class GetDisplayMediaVideoTrackBrowserTest
    : public WebRtcTestBase,
      public testing::WithParamInterface<std::tuple<bool, DisplaySurfaceType>> {};

INSTANTIATE_TEST_SUITE_P();

// Normally, each of these these would have its own test, but the number of
// combinations and the setup time for browser-tests make this undesirable,
// especially given the simplicity of each of these tests.
// After both (a) Conditional Focus and (b) Region Capture ship, this can
// simpplified to three non-parameterized tests (tab/window/screen).
IN_PROC_BROWSER_TEST_P(GetDisplayMediaVideoTrackBrowserTest, RunCombinedTest) {}

// Flaky on Mac, Windows, and ChromeOS bots, https://crbug.com/1371309
// Also some flakes on Linux ASAN/MSAN builds.
#if BUILDFLAG(IS_LINUX) && \
    !(defined(MEMORY_SANITIZER) || defined(ADDRESS_SANITIZER))
class GetDisplayMediaHiDpiBrowserTest
    : public WebRtcTestBase,
      public testing::WithParamInterface<TestConfigForHiDpi> {};

IN_PROC_BROWSER_TEST_P(GetDisplayMediaHiDpiBrowserTest, Capture) {}

INSTANTIATE_TEST_SUITE_P();
#endif

class GetDisplayMediaChangeSourceBrowserTest
    : public WebRtcTestBase,
      public testing::WithParamInterface<std::tuple<bool, bool, bool>> {};

INSTANTIATE_TEST_SUITE_P();

// TODO(crbug.com/40900706) Re-enable flaky test.
IN_PROC_BROWSER_TEST_P(GetDisplayMediaChangeSourceBrowserTest,
                       DISABLED_ChangeSource) {}

// TODO(crbug.com/40900706) Re-enable flaky test.
IN_PROC_BROWSER_TEST_P(GetDisplayMediaChangeSourceBrowserTest,
                       DISABLED_ChangeSourceThenStopTracksRemovesIndicators) {}

// TODO(crbug.com/40900706) Re-enable flaky test.
IN_PROC_BROWSER_TEST_P(GetDisplayMediaChangeSourceBrowserTest,
                       DISABLED_ChangeSourceReject) {}

class GetDisplayMediaSelfBrowserSurfaceBrowserTest
    : public WebRtcTestBase,
      public testing::WithParamInterface<std::string> {};

INSTANTIATE_TEST_SUITE_P();

IN_PROC_BROWSER_TEST_P(GetDisplayMediaSelfBrowserSurfaceBrowserTest,
                       SelfBrowserSurfaceChangesCapturedTab) {}

IN_PROC_BROWSER_TEST_P(GetDisplayMediaSelfBrowserSurfaceBrowserTest,
                       SelfBrowserSurfaceInteractionWithPreferCurrentTab) {}

#if BUILDFLAG(IS_CHROMEOS_LACROS) || BUILDFLAG(IS_CHROMEOS_ASH)

class WebRtcScreenCaptureSelectAllScreensTest
    : public WebRtcScreenCaptureBrowserTest,
      public testing::WithParamInterface<TestConfigForSelectAllScreens> {
 public:
  WebRtcScreenCaptureSelectAllScreensTest() : test_config_(GetParam()) {}
  ~WebRtcScreenCaptureSelectAllScreensTest() override = default;

  void SetUpCommandLine(base::CommandLine* command_line) override {
    // Enables GetAllScreensMedia features for multi surface capture.
    // TODO(simonha): remove when feature becomes stable.
    if (test_config_.enable_select_all_screens) {
      command_line->AppendSwitch(switches::kEnableBlinkTestFeatures);
    }
    command_line->AppendSwitch(
        switches::kEnableExperimentalWebPlatformFeatures);
    command_line->AppendSwitch(switches::kUseFakeUIForMediaStream);
    command_line->RemoveSwitch(switches::kUseFakeDeviceForMediaStream);
    command_line->AppendSwitchASCII(
        switches::kUseFakeDeviceForMediaStream,
        base::StringPrintf("display-media-type=%s",
                           test_config_.display_surface));
  }

  bool PreferCurrentTab() const override { return false; }

 protected:
  TestConfigForSelectAllScreens test_config_;
};

IN_PROC_BROWSER_TEST_P(WebRtcScreenCaptureSelectAllScreensTest,
                       GetDisplayMediaAutoSelectAllScreensTrueDisallowed) {
  ASSERT_TRUE(embedded_test_server()->Start());

  content::WebContents* tab = OpenTestPageInNewTab(kMainHtmlPage);
  RunGetDisplayMedia(tab,
                     GetConstraints(/*video=*/true, /*audio=*/false,
                                    /*select_all_screens=*/
                                    SelectAllScreens::kTrue),
                     /*is_fake_ui=*/true,
                     /*expect_success=*/!test_config_.enable_select_all_screens,
                     /*is_tab_capture=*/false);
}

IN_PROC_BROWSER_TEST_P(WebRtcScreenCaptureSelectAllScreensTest,
                       GetDisplayMediaAutoSelectAllScreensFalseAlwaysAllowed) {
  ASSERT_TRUE(embedded_test_server()->Start());

  content::WebContents* tab = OpenTestPageInNewTab(kMainHtmlPage);
  RunGetDisplayMedia(tab,
                     GetConstraints(/*video=*/true, /*audio=*/false,
                                    /*select_all_screens=*/
                                    SelectAllScreens::kFalse),
                     /*is_fake_ui=*/true, /*expect_success=*/true,
                     /*is_tab_capture=*/false);
}

INSTANTIATE_TEST_SUITE_P(
    All,
    WebRtcScreenCaptureSelectAllScreensTest,
    Values(TestConfigForSelectAllScreens{/*display_surface=*/"browser",
                                         /*enable_select_all_screens=*/true},
           TestConfigForSelectAllScreens{/*display_surface=*/"browser",
                                         /*enable_select_all_screens=*/false},
           TestConfigForSelectAllScreens{/*display_surface=*/"window",
                                         /*enable_select_all_screens=*/true},
           TestConfigForSelectAllScreens{/*display_surface=*/"window",
                                         /*enable_select_all_screens=*/false},
           TestConfigForSelectAllScreens{/*display_surface=*/"monitor",
                                         /*enable_select_all_screens=*/true},
           TestConfigForSelectAllScreens{/*display_surface=*/"monitor",
                                         /*enable_select_all_screens=*/false}));

#endif  // BUILDFLAG(IS_CHROMEOS_LACROS) || BUILDFLAG(IS_CHROMEOS_ASH)

class GetDisplayMediaTransientActivationRequiredTest
    : public WebRtcScreenCaptureBrowserTest,
      public testing::WithParamInterface<
          std::tuple<bool, bool, bool, std::optional<std::string>>> {};

IN_PROC_BROWSER_TEST_P(GetDisplayMediaTransientActivationRequiredTest, Check) {}

INSTANTIATE_TEST_SUITE_P();

// Encapsulates information about a capture-session in which one tab starts
// out capturing a specific other tab, and later possibly moves to capturing
// another tab. The encapsulation of this state allows for more succinct tests,
// especially when testing multiple concurrent capture-sessions.
class CaptureSessionDetails {};

class GetDisplayMediaCapturedSurfaceControlTest : public WebRtcTestBase {};

CscAction;

IN_PROC_BROWSER_TEST_F(GetDisplayMediaCapturedSurfaceControlTest,
                       ChangeSourceTriggersUpdateCaptureTarget) {}

void GetDisplayMediaCapturedSurfaceControlTest::
    RunChangeSourceWorksOnCorrectCaptureSession(
        size_t session_experiencing_change) {}

// Test when the first of two capture sessions experiences the source-change.
IN_PROC_BROWSER_TEST_F(GetDisplayMediaCapturedSurfaceControlTest,
                       ChangeSourceWorksOnCorrectCaptureSession0) {}

// Test when the second of two capture sessions experiences the source-change.
IN_PROC_BROWSER_TEST_F(GetDisplayMediaCapturedSurfaceControlTest,
                       ChangeSourceWorksOnCorrectCaptureSession1) {}

class GetDisplayMediaCapturedSurfaceControlIndicatorTest
    : public GetDisplayMediaCapturedSurfaceControlTest,
      public testing::WithParamInterface<CscAction> {};

INSTANTIATE_TEST_SUITE_P();

IN_PROC_BROWSER_TEST_P(GetDisplayMediaCapturedSurfaceControlIndicatorTest,
                       IndicatorNotShownBeforeApiInvocation) {}

IN_PROC_BROWSER_TEST_P(GetDisplayMediaCapturedSurfaceControlIndicatorTest,
                       IndicatorShownAfterWriteAccessApiInvocation) {}

IN_PROC_BROWSER_TEST_P(
    GetDisplayMediaCapturedSurfaceControlIndicatorTest,
    IndicatorStateRetainedAfterShareThisTabInsteadNoCscBefore) {}

IN_PROC_BROWSER_TEST_P(
    GetDisplayMediaCapturedSurfaceControlIndicatorTest,
    IndicatorStateRetainedAfterShareThisTabInsteadAfterCscAction) {}