| // 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 <string> |
| #include <vector> |
| |
| #include "base/base_switches.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_number_conversions.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 "chrome/browser/apps/platform_apps/app_browsertest_util.h" |
| #include "chrome/browser/media/webrtc/webrtc_browsertest_base.h" |
| #include "chrome/browser/ui/browser_tabstrip.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/confirm_infobar_delegate.h" |
| #include "components/infobars/core/infobar.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 "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" |
| |
| #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_manager_test_helper.h" |
| #include "chrome/browser/chromeos/policy/dlp/dlp_content_restriction_set.h" |
| #endif |
| |
| namespace { |
| |
| using base::test::FeatureRef; |
| |
| static const char kMainHtmlPage[] = "/webrtc/webrtc_getdisplaymedia_test.html"; |
| static const char kMainHtmlFileName[] = "webrtc_getdisplaymedia_test.html"; |
| static const char kSameOriginRenamedTitle[] = "Renamed Same Origin Tab"; |
| static const char kMainHtmlTitle[] = "WebRTC Automated Test"; |
| // The captured tab is identified by its title. |
| static const char kCapturedTabTitle[] = "totally-unique-captured-page-title"; |
| static const char kCapturedPageMain[] = "/webrtc/captured_page_main.html"; |
| static const std::u16string kShareThisTabInsteadMessage = |
| u"Share this tab instead"; |
| |
| enum class DisplaySurfaceType { kTab, kWindow, kScreen }; |
| |
| enum class GetDisplayMediaVariant : int { |
| kStandard = 0, |
| kPreferCurrentTab = 1 |
| }; |
| |
| struct TestConfigForPicker { |
| TestConfigForPicker(bool should_prefer_current_tab, |
| bool accept_this_tab_capture) |
| : should_prefer_current_tab(should_prefer_current_tab), |
| accept_this_tab_capture(accept_this_tab_capture) {} |
| |
| explicit TestConfigForPicker(std::tuple<bool, bool> input_tuple) |
| : TestConfigForPicker(std::get<0>(input_tuple), |
| std::get<1>(input_tuple)) {} |
| |
| // If true, specify {preferCurrentTab: true}. |
| // Otherwise, either don't specify it, or set it to false. |
| bool should_prefer_current_tab; |
| |
| // |accept_this_tab_capture| is only applicable if |
| // |should_prefer_current_tab| is set to true. Then, setting |
| // |accept_this_tab_capture| to true accepts the current tab, and |
| // |accept_this_tab_capture| set to false implies dismissing the media picker. |
| bool accept_this_tab_capture; |
| }; |
| |
| struct TestConfigForFakeUI { |
| bool should_prefer_current_tab; |
| const char* display_surface; |
| }; |
| |
| struct TestConfigForSelectAllScreens { |
| const char* display_surface; |
| bool enable_select_all_screens; |
| }; |
| |
| struct TestConfigForHiDpi { |
| bool enable_hidpi; |
| int constraint_width; |
| int constraint_height; |
| }; |
| |
| constexpr char kAppWindowTitle[] = "AppWindow Display Capture Test"; |
| |
| std::string DisplaySurfaceTypeAsString( |
| DisplaySurfaceType display_surface_type) { |
| switch (display_surface_type) { |
| case DisplaySurfaceType::kTab: |
| return "browser"; |
| case DisplaySurfaceType::kWindow: |
| return "window"; |
| case DisplaySurfaceType::kScreen: |
| return "screen"; |
| } |
| NOTREACHED(); |
| return "error"; |
| } |
| |
| 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 = "") { |
| DCHECK(!expect_success || expected_error.empty()); |
| |
| std::string result; |
| EXPECT_TRUE(content::ExecuteScriptAndExtractString( |
| tab->GetPrimaryMainFrame(), |
| base::StringPrintf( |
| "runGetDisplayMedia(%s, \"top-level-document\", \"%s\");", |
| constraints.c_str(), expected_error.c_str()), |
| &result)); |
| |
| #if BUILDFLAG(IS_MAC) |
| if (!is_fake_ui && !is_tab_capture && |
| system_media_permissions::CheckSystemScreenCapturePermission() != |
| system_media_permissions::SystemPermission::kAllowed) { |
| expect_success = false; |
| } |
| #endif |
| |
| EXPECT_EQ(result, expect_success ? "capture-success" |
| : expected_error.empty() ? "capture-failure" |
| : "expected-error"); |
| } |
| |
| void StopAllTracks(content::WebContents* tab) { |
| std::string result; |
| EXPECT_TRUE(content::ExecuteScriptAndExtractString( |
| tab->GetPrimaryMainFrame(), "stopAllTracks();", &result)); |
| EXPECT_EQ(result, "stopped"); |
| } |
| |
| void UpdateWebContentsTitle(content::WebContents* contents, |
| const std::u16string& title) { |
| content::NavigationEntry* entry = |
| contents->GetController().GetLastCommittedEntry(); |
| ASSERT_TRUE(entry); |
| contents->UpdateTitleForEntry(entry, title); |
| } |
| |
| GURL GetFileURL(const char* filename) { |
| base::ScopedAllowBlockingForTesting allow_blocking; |
| base::FilePath path; |
| base::PathService::Get(chrome::DIR_TEST_DATA, &path); |
| path = path.AppendASCII("webrtc").AppendASCII(filename); |
| CHECK(base::PathExists(path)); |
| return net::FilePathToFileURL(path); |
| } |
| |
| infobars::ContentInfoBarManager* GetInfoBarManager( |
| content::WebContents* web_contents) { |
| return infobars::ContentInfoBarManager::FromWebContents(web_contents); |
| } |
| |
| ConfirmInfoBarDelegate* GetDelegate(content::WebContents* web_contents) { |
| return static_cast<ConfirmInfoBarDelegate*>( |
| GetInfoBarManager(web_contents)->infobar_at(0)->delegate()); |
| } |
| |
| bool HasSecondaryButton(content::WebContents* web_contents) { |
| return GetDelegate(web_contents)->GetButtons() & |
| ConfirmInfoBarDelegate::InfoBarButton::BUTTON_CANCEL; |
| } |
| |
| std::u16string GetSecondaryButtonLabel(content::WebContents* web_contents) { |
| DCHECK(HasSecondaryButton(web_contents)); // Test error otherwise. |
| return GetDelegate(web_contents) |
| ->GetButtonLabel(ConfirmInfoBarDelegate::InfoBarButton::BUTTON_CANCEL); |
| } |
| |
| } // namespace |
| |
| // Base class for top level tests for getDisplayMedia(). |
| class WebRtcScreenCaptureBrowserTest : public WebRtcTestBase { |
| public: |
| ~WebRtcScreenCaptureBrowserTest() override = default; |
| |
| enum class SelectAllScreens { kUndefined = 0, kTrue = 1, kFalse = 2 }; |
| |
| void SetUpInProcessBrowserTestFixture() override { |
| DetectErrorsInJavaScript(); |
| } |
| |
| virtual bool PreferCurrentTab() const = 0; |
| |
| std::string GetConstraints(bool video, |
| bool audio, |
| SelectAllScreens select_all_screens) const { |
| std::string select_all_screens_property = |
| (select_all_screens == SelectAllScreens::kUndefined) |
| ? "" |
| : base::StringPrintf( |
| "autoSelectAllScreens: %s", |
| (select_all_screens == SelectAllScreens::kFalse) ? "false" |
| : "true"); |
| return base::StringPrintf( |
| "{video: %s, audio: %s, preferCurrentTab: %s, %s}", |
| video ? "true" : "false", audio ? "true" : "false", |
| PreferCurrentTab() ? "true" : "false", |
| select_all_screens_property.c_str()); |
| } |
| }; |
| |
| // Top level test for getDisplayMedia(). |
| // Pops picker UI and shares by default. |
| class WebRtcScreenCaptureBrowserTestWithPicker |
| : public WebRtcScreenCaptureBrowserTest, |
| public testing::WithParamInterface<std::tuple<bool, bool>> { |
| public: |
| WebRtcScreenCaptureBrowserTestWithPicker() : test_config_(GetParam()) {} |
| |
| void SetUpCommandLine(base::CommandLine* command_line) override { |
| command_line->AppendSwitch( |
| switches::kEnableExperimentalWebPlatformFeatures); |
| if (test_config_.should_prefer_current_tab) { |
| command_line->AppendSwitch(test_config_.accept_this_tab_capture |
| ? switches::kThisTabCaptureAutoAccept |
| : switches::kThisTabCaptureAutoReject); |
| } else { |
| #if BUILDFLAG(IS_CHROMEOS_ASH) |
| command_line->AppendSwitchASCII(switches::kAutoSelectDesktopCaptureSource, |
| "Display"); |
| #else |
| command_line->AppendSwitchASCII(switches::kAutoSelectDesktopCaptureSource, |
| "Entire screen"); |
| #endif // BUILDFLAG(IS_CHROMEOS_ASH) |
| } |
| } |
| |
| bool PreferCurrentTab() const override { |
| return test_config_.should_prefer_current_tab; |
| } |
| |
| const TestConfigForPicker test_config_; |
| }; |
| |
| INSTANTIATE_TEST_SUITE_P(All, |
| WebRtcScreenCaptureBrowserTestWithPicker, |
| testing::Combine( |
| /*should_prefer_current_tab=*/testing::Bool(), |
| /*accept_this_tab_capture=*/testing::Bool())); |
| |
| // TODO(1170479): Real desktop capture is flaky on below platforms. |
| #if BUILDFLAG(IS_WIN) |
| #define MAYBE_ScreenCaptureVideo DISABLED_ScreenCaptureVideo |
| #else |
| #define MAYBE_ScreenCaptureVideo ScreenCaptureVideo |
| #endif // BUILDFLAG(IS_WIN) |
| IN_PROC_BROWSER_TEST_P(WebRtcScreenCaptureBrowserTestWithPicker, |
| MAYBE_ScreenCaptureVideo) { |
| if (!test_config_.should_prefer_current_tab && |
| !test_config_.accept_this_tab_capture) { |
| GTEST_SKIP(); |
| } |
| |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| 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 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; |
| } |
| |
| std::string result; |
| EXPECT_TRUE(content::ExecuteScriptAndExtractString( |
| tab->GetPrimaryMainFrame(), "waitVideoUnmuted();", &result)); |
| EXPECT_EQ(result, "unmuted"); |
| |
| const policy::DlpContentRestrictionSet kScreenShareRestricted( |
| policy::DlpContentRestriction::kScreenShare, |
| policy::DlpRulesManager::Level::kBlock); |
| |
| helper.ChangeConfidentiality(tab, kScreenShareRestricted); |
| content::WaitForLoadStop(tab); |
| |
| EXPECT_TRUE(content::ExecuteScriptAndExtractString( |
| tab->GetPrimaryMainFrame(), "waitVideoMuted();", &result)); |
| EXPECT_EQ(result, "muted"); |
| |
| const policy::DlpContentRestrictionSet kEmptyRestrictionSet; |
| helper.ChangeConfidentiality(tab, kEmptyRestrictionSet); |
| |
| EXPECT_TRUE(content::ExecuteScriptAndExtractString( |
| tab->GetPrimaryMainFrame(), "waitVideoUnmuted();", &result)); |
| EXPECT_EQ(result, "unmuted"); |
| } |
| #endif // BUILDFLAG(IS_CHROMEOS_ASH) |
| |
| // TODO(1170479): Real desktop capture is flaky on below platforms. |
| #if BUILDFLAG(IS_WIN) |
| #define MAYBE_ScreenCaptureVideoAndAudio DISABLED_ScreenCaptureVideoAndAudio |
| // On linux debug bots, it's flaky as well. |
| #elif ((BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)) && \ |
| !defined(NDEBUG)) |
| #define MAYBE_ScreenCaptureVideoAndAudio DISABLED_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 DISABLED_ScreenCaptureVideoAndAudio |
| #else |
| #define MAYBE_ScreenCaptureVideoAndAudio ScreenCaptureVideoAndAudio |
| #endif // BUILDFLAG(IS_WIN) |
| IN_PROC_BROWSER_TEST_P(WebRtcScreenCaptureBrowserTestWithPicker, |
| MAYBE_ScreenCaptureVideoAndAudio) { |
| if (!test_config_.should_prefer_current_tab && |
| !test_config_.accept_this_tab_capture) { |
| GTEST_SKIP(); |
| } |
| |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| content::WebContents* tab = OpenTestPageInNewTab(kMainHtmlPage); |
| RunGetDisplayMedia(tab, |
| GetConstraints( |
| /*video=*/true, /*audio=*/true, |
| /*select_all_screens=*/ |
| SelectAllScreens::kUndefined), |
| /*is_fake_ui=*/false, |
| /*expect_success=*/test_config_.accept_this_tab_capture, |
| /*is_tab_capture=*/PreferCurrentTab()); |
| } |
| |
| // Top level test for getDisplayMedia(). |
| // Skips picker UI and uses fake device with specified type. |
| class WebRtcScreenCaptureBrowserTestWithFakeUI |
| : public WebRtcScreenCaptureBrowserTest, |
| public testing::WithParamInterface<TestConfigForFakeUI> { |
| public: |
| WebRtcScreenCaptureBrowserTestWithFakeUI() : test_config_(GetParam()) {} |
| |
| void SetUpCommandLine(base::CommandLine* command_line) override { |
| 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 test_config_.should_prefer_current_tab; |
| } |
| |
| protected: |
| const TestConfigForFakeUI test_config_; |
| }; |
| |
| IN_PROC_BROWSER_TEST_P(WebRtcScreenCaptureBrowserTestWithFakeUI, |
| ScreenCaptureVideo) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| content::WebContents* tab = OpenTestPageInNewTab(kMainHtmlPage); |
| RunGetDisplayMedia(tab, |
| GetConstraints( |
| /*video=*/true, /*audio=*/false, |
| /*select_all_screens=*/ |
| SelectAllScreens::kUndefined), |
| /*is_fake_ui=*/true, /*expect_success=*/true, |
| /*is_tab_capture=*/PreferCurrentTab()); |
| |
| std::string result; |
| EXPECT_TRUE(content::ExecuteScriptAndExtractString( |
| tab->GetPrimaryMainFrame(), "getDisplaySurfaceSetting();", &result)); |
| EXPECT_EQ(result, test_config_.display_surface); |
| |
| EXPECT_TRUE(content::ExecuteScriptAndExtractString( |
| tab->GetPrimaryMainFrame(), "getLogicalSurfaceSetting();", &result)); |
| EXPECT_EQ(result, "true"); |
| |
| EXPECT_TRUE(content::ExecuteScriptAndExtractString( |
| tab->GetPrimaryMainFrame(), "getCursorSetting();", &result)); |
| EXPECT_EQ(result, "never"); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(WebRtcScreenCaptureBrowserTestWithFakeUI, |
| ScreenCaptureVideoAndAudio) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| content::WebContents* tab = OpenTestPageInNewTab(kMainHtmlPage); |
| RunGetDisplayMedia(tab, |
| GetConstraints( |
| /*video=*/true, /*audio=*/true, |
| /*select_all_screens=*/ |
| SelectAllScreens::kUndefined), |
| /*is_fake_ui=*/true, /*expect_success=*/true, |
| /*is_tab_capture=*/PreferCurrentTab()); |
| |
| std::string result; |
| EXPECT_TRUE(content::ExecuteScriptAndExtractString( |
| tab->GetPrimaryMainFrame(), "hasAudioTrack();", &result)); |
| EXPECT_EQ(result, "true"); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(WebRtcScreenCaptureBrowserTestWithFakeUI, |
| ScreenCaptureWithConstraints) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| content::WebContents* tab = OpenTestPageInNewTab(kMainHtmlPage); |
| const int kMaxWidth = 200; |
| const int kMaxFrameRate = 6; |
| const std::string constraints = base::StringPrintf( |
| "{video: {width: {max: %d}, frameRate: {max: %d}}, " |
| "should_prefer_current_tab: " |
| "%s}", |
| kMaxWidth, kMaxFrameRate, |
| test_config_.should_prefer_current_tab ? "true" : "false"); |
| RunGetDisplayMedia(tab, constraints, |
| /*is_fake_ui=*/true, /*expect_success=*/true, |
| /*is_tab_capture=*/PreferCurrentTab()); |
| |
| std::string result; |
| EXPECT_TRUE(content::ExecuteScriptAndExtractString( |
| tab->GetPrimaryMainFrame(), "getWidthSetting();", &result)); |
| EXPECT_EQ(result, base::StringPrintf("%d", kMaxWidth)); |
| |
| EXPECT_TRUE(content::ExecuteScriptAndExtractString( |
| tab->GetPrimaryMainFrame(), "getFrameRateSetting();", &result)); |
| EXPECT_EQ(result, base::StringPrintf("%d", kMaxFrameRate)); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| All, |
| WebRtcScreenCaptureBrowserTestWithFakeUI, |
| testing::Values(TestConfigForFakeUI{/*should_prefer_current_tab=*/false, |
| /*display_surface=*/"monitor"}, |
| TestConfigForFakeUI{/*should_prefer_current_tab=*/false, |
| /*display_surface=*/"window"}, |
| TestConfigForFakeUI{/*should_prefer_current_tab=*/false, |
| /*display_surface=*/"browser"}, |
| TestConfigForFakeUI{/*should_prefer_current_tab=*/true, |
| /*display_surface=*/"browser"})); |
| |
| class WebRtcScreenCapturePermissionPolicyBrowserTest |
| : public WebRtcScreenCaptureBrowserTest, |
| public testing::WithParamInterface< |
| std::tuple<GetDisplayMediaVariant, bool>> { |
| public: |
| WebRtcScreenCapturePermissionPolicyBrowserTest() |
| : tested_variant_(std::get<0>(GetParam())), |
| allowlisted_by_policy_(std::get<1>(GetParam())) {} |
| |
| ~WebRtcScreenCapturePermissionPolicyBrowserTest() override = default; |
| |
| void SetUpCommandLine(base::CommandLine* command_line) override { |
| command_line->AppendSwitch( |
| switches::kEnableExperimentalWebPlatformFeatures); |
| command_line->AppendSwitchASCII( |
| switches::kAutoSelectTabCaptureSourceByTitle, kMainHtmlTitle); |
| } |
| |
| bool PreferCurrentTab() const override { |
| return tested_variant_ == GetDisplayMediaVariant::kPreferCurrentTab; |
| } |
| |
| protected: |
| const GetDisplayMediaVariant tested_variant_; |
| const bool allowlisted_by_policy_; |
| }; |
| |
| INSTANTIATE_TEST_SUITE_P( |
| All, |
| WebRtcScreenCapturePermissionPolicyBrowserTest, |
| testing::Combine(testing::Values(GetDisplayMediaVariant::kStandard, |
| GetDisplayMediaVariant::kPreferCurrentTab), |
| /*allowlisted_by_policy=*/testing::Bool())); |
| |
| // Flaky on Win bots http://crbug.com/1264805 |
| #if BUILDFLAG(IS_WIN) |
| #define MAYBE_ScreenShareFromEmbedded DISABLED_ScreenShareFromEmbedded |
| #else |
| #define MAYBE_ScreenShareFromEmbedded ScreenShareFromEmbedded |
| #endif |
| IN_PROC_BROWSER_TEST_P(WebRtcScreenCapturePermissionPolicyBrowserTest, |
| MAYBE_ScreenShareFromEmbedded) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| // The use of selfBrowserSurface is in order to simplify the test by |
| // using just one tab. It is orthogonal to the test's purpose. |
| const std::string constraints = base::StringPrintf( |
| "{video: true, selfBrowserSurface: 'include', preferCurrentTab: %s}", |
| PreferCurrentTab() ? "true" : "false"); |
| |
| std::string result; |
| EXPECT_TRUE(content::ExecuteScriptAndExtractString( |
| OpenTestPageInNewTab(kMainHtmlPage)->GetPrimaryMainFrame(), |
| base::StringPrintf( |
| "runGetDisplayMedia(%s, \"%s\");", constraints.c_str(), |
| allowlisted_by_policy_ ? "allowedFrame" : "disallowedFrame"), |
| &result)); |
| EXPECT_EQ(result, allowlisted_by_policy_ ? "embedded-capture-success" |
| : "embedded-capture-failure"); |
| } |
| |
| // 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 { |
| public: |
| WebRtcAppWindowCaptureBrowserTestWithPicker() = default; |
| |
| void SetUpCommandLine(base::CommandLine* command_line) override { |
| PlatformAppBrowserTest::SetUpCommandLine(command_line); |
| command_line->AppendSwitch( |
| switches::kEnableExperimentalWebPlatformFeatures); |
| command_line->AppendSwitchASCII( |
| switches::kAutoSelectTabCaptureSourceByTitle, kAppWindowTitle); |
| } |
| |
| void SetUpOnMainThread() override { |
| extensions::PlatformAppBrowserTest::SetUpOnMainThread(); |
| ASSERT_TRUE(StartEmbeddedTestServer()); |
| |
| // We will restrict all pages to "Tab Capture" only. This should force App |
| // Windows to show up in the tabs list, and thus make it selectable. |
| base::Value::List matchlist; |
| matchlist.Append("*"); |
| browser()->profile()->GetPrefs()->SetList( |
| prefs::kTabCaptureAllowedByOrigins, std::move(matchlist)); |
| } |
| |
| void TearDownOnMainThread() override { |
| extensions::PlatformAppBrowserTest::TearDownOnMainThread(); |
| browser()->profile()->GetPrefs()->SetList( |
| prefs::kTabCaptureAllowedByOrigins, base::Value::List()); |
| } |
| |
| extensions::AppWindow* CreateAppWindowWithTitle(const std::u16string& title) { |
| extensions::AppWindow* app_window = CreateTestAppWindow("{}"); |
| EXPECT_TRUE(app_window); |
| UpdateWebContentsTitle(app_window->web_contents(), title); |
| |
| return app_window; |
| } |
| |
| // This is mostly lifted from WebRtcBrowserTestBase, with the exception that |
| // because we know we're setting the auto-accept switches, we don't need to |
| // set the PermissionsManager auto accept. |
| content::WebContents* OpenTestPageInNewTab(const std::string& test_url) { |
| chrome::AddTabAt(browser(), GURL(url::kAboutBlankURL), -1, true); |
| GURL url = embedded_test_server()->GetURL(test_url); |
| EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| return browser()->tab_strip_model()->GetActiveWebContents(); |
| } |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(WebRtcAppWindowCaptureBrowserTestWithPicker, |
| CaptureAppWindow) { |
| extensions::AppWindow* app_window = |
| CreateAppWindowWithTitle(base::UTF8ToUTF16(std::string(kAppWindowTitle))); |
| content::WebContents* capturing_tab = OpenTestPageInNewTab(kMainHtmlPage); |
| |
| RunGetDisplayMedia(capturing_tab, "{video: true}", /*is_fake_ui=*/false, |
| /*expect_success=*/true, |
| /*is_tab_capture=*/true); |
| CloseAppWindow(app_window); |
| } |
| |
| // Base class for running tests with a SameOrigin policy applied. |
| class WebRtcSameOriginPolicyBrowserTest |
| : public WebRtcScreenCaptureBrowserTest { |
| public: |
| ~WebRtcSameOriginPolicyBrowserTest() override = default; |
| |
| bool PreferCurrentTab() const override { return false; } |
| |
| void SetUpCommandLine(base::CommandLine* command_line) override { |
| WebRtcScreenCaptureBrowserTest::SetUpCommandLine(command_line); |
| command_line->AppendSwitch( |
| switches::kEnableExperimentalWebPlatformFeatures); |
| command_line->AppendSwitchASCII( |
| switches::kAutoSelectTabCaptureSourceByTitle, kSameOriginRenamedTitle); |
| } |
| |
| void SetUpOnMainThread() override { |
| WebRtcScreenCaptureBrowserTest::SetUpOnMainThread(); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| // Restrict all origins to SameOrigin tab capture only. |
| base::Value::List matchlist; |
| matchlist.Append("*"); |
| browser()->profile()->GetPrefs()->SetList( |
| prefs::kSameOriginTabCaptureAllowedByOrigins, std::move(matchlist)); |
| } |
| |
| void TearDownOnMainThread() override { |
| WebRtcScreenCaptureBrowserTest::TearDownOnMainThread(); |
| browser()->profile()->GetPrefs()->SetList( |
| prefs::kSameOriginTabCaptureAllowedByOrigins, base::Value::List()); |
| } |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(WebRtcSameOriginPolicyBrowserTest, |
| TerminateOnNavigationAwayFromSameOrigin) { |
| // Open two pages, one to be captured, and one to do the capturing. Note that |
| // we open the capturing page second so that is focused to allow the |
| // getDisplayMedia request to succeed. |
| content::WebContents* target_tab = OpenTestPageInNewTab(kMainHtmlPage); |
| content::WebContents* capturing_tab = OpenTestPageInNewTab(kMainHtmlPage); |
| |
| // Update the target tab to a unique title, so that we can ensure that it is |
| // the one that gets captured via the autoselection. |
| UpdateWebContentsTitle( |
| target_tab, base::UTF8ToUTF16(std::string(kSameOriginRenamedTitle))); |
| RunGetDisplayMedia(capturing_tab, |
| GetConstraints( |
| /*video=*/true, /*audio=*/true, |
| /*select_all_screens=*/ |
| SelectAllScreens::kUndefined), |
| /*is_fake_ui=*/false, /*expect_success=*/true, |
| /*is_tab_capture=*/true); |
| |
| // Though the target tab should've been focused as a result of starting the |
| // capture, we don't want to take a dependency on that behavior. Ensure that |
| // the target tab is focused, so that we can navigate it easily. If it is |
| // already focused, this will just no-op. |
| int target_index = |
| browser()->tab_strip_model()->GetIndexOfWebContents(target_tab); |
| browser()->tab_strip_model()->ActivateTabAt( |
| target_index, TabStripUserGestureDetails( |
| TabStripUserGestureDetails::GestureType::kOther)); |
| ASSERT_EQ(target_tab, browser()->tab_strip_model()->GetActiveWebContents()); |
| |
| // We navigate to a FileURL so that the origin will change, which should |
| // trigger the capture to end. |
| ASSERT_TRUE( |
| ui_test_utils::NavigateToURL(browser(), GetFileURL(kMainHtmlFileName))); |
| |
| // Verify that the video stream has ended. |
| std::string result; |
| EXPECT_TRUE(content::ExecuteScriptAndExtractString( |
| capturing_tab->GetPrimaryMainFrame(), "waitVideoEnded();", &result)); |
| EXPECT_EQ(result, "ended"); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(WebRtcSameOriginPolicyBrowserTest, |
| ContinueCapturingForSameOriginNavigation) { |
| // Open two pages, one to be captured, and one to do the capturing. Note that |
| // we open the capturing page second so that is focused to allow the |
| // getDisplayMedia request to succeed. |
| content::WebContents* target_tab = OpenTestPageInNewTab(kMainHtmlPage); |
| content::WebContents* capturing_tab = OpenTestPageInNewTab(kMainHtmlPage); |
| |
| // Update the target tab to a unique title, so that we can ensure that it is |
| // the one that gets captured via the autoselection. |
| UpdateWebContentsTitle( |
| target_tab, base::UTF8ToUTF16(std::string(kSameOriginRenamedTitle))); |
| RunGetDisplayMedia(capturing_tab, |
| GetConstraints( |
| /*video=*/true, /*audio=*/true, |
| /*select_all_screens=*/ |
| SelectAllScreens::kUndefined), |
| /*is_fake_ui=*/false, /*expect_success=*/true, |
| /*is_tab_capture=*/true); |
| |
| // Though the target tab should've been focused as a result of starting the |
| // capture, we don't want to take a dependency on that behavior. Ensure that |
| // the target tab is focused, so that we can navigate it easily. If it is |
| // already focused, this will just no-op. |
| int target_index = |
| browser()->tab_strip_model()->GetIndexOfWebContents(target_tab); |
| browser()->tab_strip_model()->ActivateTabAt( |
| target_index, TabStripUserGestureDetails( |
| TabStripUserGestureDetails::GestureType::kOther)); |
| ASSERT_EQ(target_tab, browser()->tab_strip_model()->GetActiveWebContents()); |
| |
| // We navigate using the test server so that the origin doesn't change. |
| ASSERT_TRUE(ui_test_utils::NavigateToURL( |
| browser(), |
| embedded_test_server()->GetURL("/webrtc/captured_page_main.html"))); |
| |
| // Verify that the video hasn't been ended. |
| std::string result; |
| EXPECT_TRUE(content::ExecuteScriptAndExtractString( |
| capturing_tab->GetPrimaryMainFrame(), |
| "returnToTest(video_track.readyState);", &result)); |
| EXPECT_EQ(result, "live"); |
| } |
| |
| class GetDisplayMediaVideoTrackBrowserTest |
| : public WebRtcTestBase, |
| public testing::WithParamInterface<std::tuple<bool, DisplaySurfaceType>> { |
| public: |
| GetDisplayMediaVideoTrackBrowserTest() |
| : region_capture_enabled_(std::get<0>(GetParam())), |
| display_surface_type_(std::get<1>(GetParam())) {} |
| |
| ~GetDisplayMediaVideoTrackBrowserTest() override = default; |
| |
| void SetUpInProcessBrowserTestFixture() override { |
| DetectErrorsInJavaScript(); |
| } |
| |
| void SetUpOnMainThread() override { |
| WebRtcTestBase::SetUpOnMainThread(); |
| |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| } |
| |
| // Unlike SetUp(), this is called from the test body. This allows skipping |
| // this test for (platform, test-case) combinations which are not supported. |
| void SetupTest() { |
| // Fire up the page. |
| tab_ = OpenTestPageInNewTab(kMainHtmlPage); |
| |
| // Initiate the capture. |
| std::string result; |
| ASSERT_TRUE(content::ExecuteScriptAndExtractString( |
| tab_->GetPrimaryMainFrame(), |
| "runGetDisplayMedia({video: true, audio: true}, " |
| "\"top-level-document\");", |
| &result)); |
| ASSERT_EQ(result, "capture-success"); |
| } |
| |
| void SetUpCommandLine(base::CommandLine* command_line) override { |
| WebRtcTestBase::SetUpCommandLine(command_line); |
| |
| std::vector<std::string> enabled_blink_features; |
| std::vector<std::string> disabled_blink_features; |
| |
| if (region_capture_enabled_) { |
| enabled_blink_features.push_back("RegionCapture"); |
| } else { |
| disabled_blink_features.push_back("RegionCapture"); |
| } |
| |
| if (!enabled_blink_features.empty()) { |
| command_line->AppendSwitchASCII( |
| switches::kEnableBlinkFeatures, |
| base::JoinString(enabled_blink_features, ",")); |
| } |
| |
| if (!disabled_blink_features.empty()) { |
| command_line->AppendSwitchASCII( |
| switches::kDisableBlinkFeatures, |
| base::JoinString(disabled_blink_features, ",")); |
| } |
| |
| command_line->AppendSwitch(switches::kUseFakeUIForMediaStream); |
| command_line->AppendSwitchASCII( |
| switches::kUseFakeDeviceForMediaStream, |
| base::StrCat({"display-media-type=", |
| DisplaySurfaceTypeAsString(display_surface_type_)})); |
| } |
| |
| std::string GetVideoTrackType() { |
| std::string result; |
| EXPECT_TRUE(content::ExecuteScriptAndExtractString( |
| tab_->GetPrimaryMainFrame(), "getVideoTrackType();", &result)); |
| return result; |
| } |
| |
| std::string GetVideoCloneTrackType() { |
| std::string result; |
| EXPECT_TRUE(content::ExecuteScriptAndExtractString( |
| tab_->GetPrimaryMainFrame(), "getVideoCloneTrackType();", &result)); |
| return result; |
| } |
| |
| bool HasAudioTrack() { |
| std::string result; |
| EXPECT_TRUE(content::ExecuteScriptAndExtractString( |
| tab_->GetPrimaryMainFrame(), "hasAudioTrack();", &result)); |
| EXPECT_TRUE(result == "true" || result == "false"); |
| return result == "true"; |
| } |
| |
| std::string GetAudioTrackType() { |
| std::string result; |
| EXPECT_TRUE(content::ExecuteScriptAndExtractString( |
| tab_->GetPrimaryMainFrame(), "getAudioTrackType();", &result)); |
| return result; |
| } |
| |
| std::string ExpectedVideoTrackType() const { |
| switch (display_surface_type_) { |
| case DisplaySurfaceType::kTab: |
| return region_capture_enabled_ ? "BrowserCaptureMediaStreamTrack" |
| : "MediaStreamTrack"; |
| case DisplaySurfaceType::kWindow: |
| case DisplaySurfaceType::kScreen: |
| return "MediaStreamTrack"; |
| } |
| NOTREACHED(); |
| return "Error"; |
| } |
| |
| protected: |
| const bool region_capture_enabled_; |
| const DisplaySurfaceType display_surface_type_; |
| |
| private: |
| raw_ptr<content::WebContents, DanglingUntriaged> tab_ = nullptr; |
| }; |
| |
| INSTANTIATE_TEST_SUITE_P( |
| _, |
| GetDisplayMediaVideoTrackBrowserTest, |
| testing::Combine(/*region_capture_enabled=*/testing::Bool(), |
| /*display_surface_type=*/ |
| testing::Values(DisplaySurfaceType::kTab, |
| DisplaySurfaceType::kWindow, |
| DisplaySurfaceType::kScreen)), |
| [](const testing::TestParamInfo< |
| GetDisplayMediaVideoTrackBrowserTest::ParamType>& info) { |
| return base::StrCat( |
| {std::get<0>(info.param) ? "RegionCapture" : "", |
| std::get<1>(info.param) == DisplaySurfaceType::kTab ? "Tab" |
| : std::get<1>(info.param) == DisplaySurfaceType::kWindow |
| ? "Window" |
| : "Screen"}); |
| }); |
| |
| // 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) { |
| SetupTest(); |
| |
| // Test #1: The video track is of the expected type. |
| EXPECT_EQ(GetVideoTrackType(), ExpectedVideoTrackType()); |
| |
| // Test #2: Video clones are of the same type as the original. |
| EXPECT_EQ(GetVideoTrackType(), GetVideoCloneTrackType()); |
| |
| // Test #3: Audio tracks are all simply MediaStreamTrack. |
| if (HasAudioTrack()) { |
| EXPECT_EQ(GetAudioTrackType(), "MediaStreamTrack"); |
| } |
| } |
| |
| // 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> { |
| public: |
| GetDisplayMediaHiDpiBrowserTest() : test_config_(GetParam()) {} |
| |
| // The browser window size must be consistent with the |
| // INSTANTIATE_TEST_SUITE_P TestConfigForHiDpi configurations below. See the |
| // comments there for more details. |
| static constexpr int kBrowserWindowWidth = 800; |
| static constexpr int kBrowserWindowHeight = 600; |
| |
| bool enable_hidpi() const { return test_config_.enable_hidpi; } |
| int constraint_width() const { return test_config_.constraint_width; } |
| int constraint_height() const { return test_config_.constraint_height; } |
| |
| void SetUpInProcessBrowserTestFixture() override { |
| if (enable_hidpi()) { |
| feature_list_.InitAndEnableFeature(media::kWebContentsCaptureHiDpi); |
| } else { |
| feature_list_.InitAndDisableFeature(media::kWebContentsCaptureHiDpi); |
| } |
| |
| WebRtcTestBase::SetUpInProcessBrowserTestFixture(); |
| |
| DetectErrorsInJavaScript(); |
| } |
| |
| void SetUpOnMainThread() override { |
| WebRtcTestBase::SetUpOnMainThread(); |
| |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| #if BUILDFLAG(IS_CHROMEOS_LACROS) |
| // The picker itself shows previews which are unsupported in Lacros tests. |
| base::Value::List matchlist; |
| matchlist.Append("*"); |
| browser()->profile()->GetPrefs()->SetList( |
| prefs::kTabCaptureAllowedByOrigins, std::move(matchlist)); |
| #endif |
| |
| // Fire up the page. |
| tab_ = OpenTestPageInNewTab(kMainHtmlPage); |
| } |
| |
| void SetUpCommandLine(base::CommandLine* command_line) override { |
| WebRtcTestBase::SetUpCommandLine(command_line); |
| |
| command_line->AppendSwitch( |
| switches::kEnableExperimentalWebPlatformFeatures); |
| |
| command_line->AppendSwitch(switches::kThisTabCaptureAutoAccept); |
| |
| command_line->AppendSwitchASCII( |
| switches::kWindowSize, |
| base::StringPrintf("%d,%d", kBrowserWindowWidth, kBrowserWindowHeight)); |
| |
| // Optionally, in case the test isn't working correctly, you can turn on |
| // debug logging for the feature to help track down problems. For example: |
| // command_line->AppendSwitchASCII(switches::kVModule, |
| // "*host_view*=1,*frame_tracker*=3"); |
| } |
| |
| std::string ResizeVideoForHiDpiCapture(int width, int height) { |
| return RunJs(base::StringPrintf("resizeVideoForHiDpiCapture(%d, %d);", |
| width, height)); |
| } |
| |
| double GetDevicePixelRatio() { |
| std::string result = RunJs("getDevicePixelRatio();"); |
| double device_pixel_ratio; |
| EXPECT_TRUE(base::StringToDouble(result, &device_pixel_ratio)); |
| return device_pixel_ratio; |
| } |
| |
| std::string GetDisplaySurfaceSetting() { |
| return RunJs("getDisplaySurfaceSetting();"); |
| } |
| |
| std::string GetLogicalSurfaceSetting() { |
| return RunJs("getLogicalSurfaceSetting();"); |
| } |
| |
| content::WebContents* Tab() const { return tab_; } |
| |
| private: |
| std::string RunJs(const std::string& command) { |
| std::string result; |
| EXPECT_TRUE(content::ExecuteScriptAndExtractString( |
| tab_->GetPrimaryMainFrame(), command, &result)); |
| return result; |
| } |
| |
| base::test::ScopedFeatureList feature_list_; |
| const TestConfigForHiDpi test_config_; |
| raw_ptr<content::WebContents, DanglingUntriaged> tab_ = nullptr; |
| }; |
| |
| IN_PROC_BROWSER_TEST_P(GetDisplayMediaHiDpiBrowserTest, Capture) { |
| ASSERT_EQ(GetDevicePixelRatio(), 1.0); |
| |
| // Initiate the capture. |
| RunGetDisplayMedia( |
| Tab(), |
| base::StringPrintf("{video: {width: {max: %d}, height: {max: %d}}, " |
| "preferCurrentTab: true}", |
| constraint_width(), constraint_height()), |
| /*is_fake_ui=*/false, /*expect_success=*/true, |
| /*is_tab_capture=*/true); |
| |
| // Ensure that the video is larger than the source tab to encourage use of a |
| // higher-resolution video stream. The size is arbitrary, but it should be |
| // significantly bigger than the switches::kWindowSize configured in this |
| // test's setup. |
| EXPECT_EQ(ResizeVideoForHiDpiCapture(kBrowserWindowWidth * 2, |
| kBrowserWindowHeight * 2), |
| "success"); |
| |
| EXPECT_EQ(GetDisplaySurfaceSetting(), "browser"); |
| |
| EXPECT_EQ(GetLogicalSurfaceSetting(), "true"); |
| |
| // The HiDPI scale change only occurs once the capture has actually started |
| // and the size information was propagated back to the browser process. |
| // Waiting for the video to start playing helps ensure that this is the case. |
| StartDetectingVideo(Tab(), "local-view"); |
| WaitForVideoToPlay(Tab()); |
| |
| // If the video size is higher resolution than the browser window |
| // size, expect that HiDPI mode should be active. This requires |
| // the feature to be enabled. |
| bool expect_hidpi = enable_hidpi() && |
| constraint_width() > kBrowserWindowWidth && |
| constraint_height() > kBrowserWindowHeight; |
| |
| double device_pixel_ratio = GetDevicePixelRatio(); |
| if (expect_hidpi) { |
| EXPECT_GT(device_pixel_ratio, 1.0); |
| EXPECT_LE(device_pixel_ratio, 2.0); |
| } else { |
| EXPECT_EQ(device_pixel_ratio, 1.0); |
| } |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| All, |
| GetDisplayMediaHiDpiBrowserTest, |
| // The test configurations use both large and small constraint sizes. The |
| // small constraint sizes must be smaller than the configured window size |
| // (cf. kBrowserWindowWidth and kBrowserWindowHeight in |
| // GetDisplayMediaHiDpiBrowserTest above), and the large sizes must be |
| // significantly larger than the browser window size. |
| testing::Values(TestConfigForHiDpi{/*enable_hidpi=*/false, |
| /*constraint_width=*/3840, |
| /*constraint_height=*/2160}, |
| TestConfigForHiDpi{/*enable_hidpi=*/true, |
| /*constraint_width=*/640, |
| /*constraint_height=*/480}, |
| TestConfigForHiDpi{/*enable_hidpi=*/true, |
| /*constraint_width=*/3840, |
| /*constraint_height=*/2160})); |
| #endif |
| |
| class GetDisplayMediaChangeSourceBrowserTest |
| : public WebRtcTestBase, |
| public testing::WithParamInterface<std::tuple<bool, bool, bool>> { |
| public: |
| GetDisplayMediaChangeSourceBrowserTest() |
| : dynamic_surface_switching_requested_(std::get<0>(GetParam())), |
| feature_enabled_(std::get<1>(GetParam())), |
| user_shared_audio_(std::get<2>(GetParam())) {} |
| ~GetDisplayMediaChangeSourceBrowserTest() override = default; |
| |
| void SetUp() override { |
| // TODO(crbug.com/1381951): Fix GetDisplayMediaChangeSourceBrowserTest with |
| // audio requested on ChromeOS |
| #if (BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)) |
| if (dynamic_surface_switching_requested_ && feature_enabled_ && |
| user_shared_audio_) { |
| GTEST_SKIP(); |
| } |
| #endif |
| WebRtcTestBase::SetUp(); |
| } |
| |
| void SetUpInProcessBrowserTestFixture() override { |
| feature_list_.InitWithFeatureState( |
| media::kShareThisTabInsteadButtonGetDisplayMedia, feature_enabled_); |
| |
| WebRtcTestBase::SetUpInProcessBrowserTestFixture(); |
| |
| DetectErrorsInJavaScript(); |
| |
| base::FilePath test_dir; |
| ASSERT_TRUE(base::PathService::Get(chrome::DIR_TEST_DATA, &test_dir)); |
| } |
| |
| void SetUpCommandLine(base::CommandLine* command_line) override { |
| command_line->AppendSwitch( |
| switches::kEnableExperimentalWebPlatformFeatures); |
| command_line->AppendSwitchASCII( |
| switches::kAutoSelectTabCaptureSourceByTitle, kCapturedTabTitle); |
| if (!user_shared_audio_) { |
| command_line->AppendSwitch(switches::kScreenCaptureAudioDefaultUnchecked); |
| } |
| } |
| |
| std::string GetConstraints() const { |
| return base::StringPrintf( |
| "{video: true, audio: true, surfaceSwitching: \"%s\"}", |
| dynamic_surface_switching_requested_ ? "include" : "exclude"); |
| } |
| |
| bool ShouldShowShareThisTabInsteadButton() const { |
| return dynamic_surface_switching_requested_ && feature_enabled_; |
| } |
| |
| private: |
| base::test::ScopedFeatureList feature_list_; |
| const bool dynamic_surface_switching_requested_; |
| const bool feature_enabled_; |
| const bool user_shared_audio_; |
| }; |
| |
| INSTANTIATE_TEST_SUITE_P(All, |
| GetDisplayMediaChangeSourceBrowserTest, |
| testing::Combine(testing::Bool(), |
| testing::Bool(), |
| testing::Bool())); |
| |
| IN_PROC_BROWSER_TEST_P(GetDisplayMediaChangeSourceBrowserTest, ChangeSource) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| content::WebContents* captured_tab = OpenTestPageInNewTab(kCapturedPageMain); |
| content::WebContents* other_tab = OpenTestPageInNewTab(kMainHtmlPage); |
| content::WebContents* capturing_tab = OpenTestPageInNewTab(kMainHtmlPage); |
| |
| RunGetDisplayMedia(capturing_tab, GetConstraints(), /*is_fake_ui=*/false, |
| /*expect_success=*/true, |
| /*is_tab_capture=*/true); |
| |
| EXPECT_TRUE(captured_tab->IsBeingCaptured()); |
| EXPECT_FALSE(other_tab->IsBeingCaptured()); |
| EXPECT_FALSE(capturing_tab->IsBeingCaptured()); |
| EXPECT_EQ( |
| GetSecondaryButtonLabel(captured_tab), |
| l10n_util::GetStringFUTF16( |
| IDS_TAB_SHARING_INFOBAR_SWITCH_TO_BUTTON, |
| url_formatter::FormatOriginForSecurityDisplay( |
| captured_tab->GetPrimaryMainFrame()->GetLastCommittedOrigin(), |
| url_formatter::SchemeDisplay::OMIT_HTTP_AND_HTTPS))); |
| EXPECT_EQ( |
| GetSecondaryButtonLabel(capturing_tab), |
| l10n_util::GetStringFUTF16( |
| IDS_TAB_SHARING_INFOBAR_SWITCH_TO_BUTTON, |
| url_formatter::FormatOriginForSecurityDisplay( |
| capturing_tab->GetPrimaryMainFrame()->GetLastCommittedOrigin(), |
| url_formatter::SchemeDisplay::OMIT_HTTP_AND_HTTPS))); |
| if (!ShouldShowShareThisTabInsteadButton()) { |
| EXPECT_FALSE(HasSecondaryButton(other_tab)); |
| return; |
| } |
| EXPECT_EQ(GetSecondaryButtonLabel(other_tab), kShareThisTabInsteadMessage); |
| |
| // Click the secondary button, i.e., the "Share this tab instead" button |
| GetDelegate(other_tab)->Cancel(); |
| |
| // Wait until the capture of the other tab has started. |
| while (!other_tab->IsBeingCaptured()) { |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| EXPECT_FALSE(captured_tab->IsBeingCaptured()); |
| EXPECT_TRUE(other_tab->IsBeingCaptured()); |
| EXPECT_FALSE(capturing_tab->IsBeingCaptured()); |
| EXPECT_EQ(GetSecondaryButtonLabel(captured_tab), kShareThisTabInsteadMessage); |
| EXPECT_EQ(GetSecondaryButtonLabel(other_tab), |
| l10n_util::GetStringFUTF16( |
| IDS_TAB_SHARING_INFOBAR_SWITCH_TO_BUTTON, |
| url_formatter::FormatOriginForSecurityDisplay( |
| other_tab->GetPrimaryMainFrame()->GetLastCommittedOrigin(), |
| url_formatter::SchemeDisplay::OMIT_HTTP_AND_HTTPS))); |
| EXPECT_EQ( |
| GetSecondaryButtonLabel(capturing_tab), |
| l10n_util::GetStringFUTF16( |
| IDS_TAB_SHARING_INFOBAR_SWITCH_TO_BUTTON, |
| url_formatter::FormatOriginForSecurityDisplay( |
| capturing_tab->GetPrimaryMainFrame()->GetLastCommittedOrigin(), |
| url_formatter::SchemeDisplay::OMIT_HTTP_AND_HTTPS))); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(GetDisplayMediaChangeSourceBrowserTest, |
| ChangeSourceThenStopTracksRemovesIndicators) { |
| if (!ShouldShowShareThisTabInsteadButton()) { |
| GTEST_SKIP(); |
| } |
| |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| OpenTestPageInNewTab(kCapturedPageMain); |
| content::WebContents* other_tab = OpenTestPageInNewTab(kMainHtmlPage); |
| content::WebContents* capturing_tab = OpenTestPageInNewTab(kMainHtmlPage); |
| |
| RunGetDisplayMedia(capturing_tab, GetConstraints(), /*is_fake_ui=*/false, |
| /*expect_success=*/true, |
| /*is_tab_capture=*/true); |
| |
| // Click the secondary button, i.e., the "Share this tab instead" button |
| GetDelegate(other_tab)->Cancel(); |
| |
| // Wait until the capture of the other tab has started. |
| while (!other_tab->IsBeingCaptured()) { |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| ASSERT_EQ(GetInfoBarManager(capturing_tab)->infobar_count(), 1u); |
| StopAllTracks(capturing_tab); |
| do { |
| base::RunLoop().RunUntilIdle(); |
| } while (GetInfoBarManager(capturing_tab)->infobar_count() > 0u); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(GetDisplayMediaChangeSourceBrowserTest, |
| ChangeSourceReject) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| content::WebContents* captured_tab = OpenTestPageInNewTab(kCapturedPageMain); |
| content::WebContents* other_tab = OpenTestPageInNewTab(kMainHtmlPage); |
| content::WebContents* capturing_tab = OpenTestPageInNewTab(kMainHtmlPage); |
| |
| RunGetDisplayMedia(capturing_tab, GetConstraints(), /*is_fake_ui=*/false, |
| /*expect_success=*/true, |
| /*is_tab_capture=*/true); |
| |
| EXPECT_TRUE(captured_tab->IsBeingCaptured()); |
| EXPECT_FALSE(other_tab->IsBeingCaptured()); |
| EXPECT_FALSE(capturing_tab->IsBeingCaptured()); |
| EXPECT_EQ( |
| GetSecondaryButtonLabel(captured_tab), |
| l10n_util::GetStringFUTF16( |
| IDS_TAB_SHARING_INFOBAR_SWITCH_TO_BUTTON, |
| url_formatter::FormatOriginForSecurityDisplay( |
| captured_tab->GetPrimaryMainFrame()->GetLastCommittedOrigin(), |
| url_formatter::SchemeDisplay::OMIT_HTTP_AND_HTTPS))); |
| EXPECT_EQ( |
| GetSecondaryButtonLabel(capturing_tab), |
| l10n_util::GetStringFUTF16( |
| IDS_TAB_SHARING_INFOBAR_SWITCH_TO_BUTTON, |
| url_formatter::FormatOriginForSecurityDisplay( |
| capturing_tab->GetPrimaryMainFrame()->GetLastCommittedOrigin(), |
| url_formatter::SchemeDisplay::OMIT_HTTP_AND_HTTPS))); |
| if (!ShouldShowShareThisTabInsteadButton()) { |
| EXPECT_FALSE(HasSecondaryButton(other_tab)); |
| return; |
| } |
| EXPECT_EQ(GetSecondaryButtonLabel(other_tab), kShareThisTabInsteadMessage); |
| |
| browser()->tab_strip_model()->ActivateTabAt( |
| browser()->tab_strip_model()->GetIndexOfWebContents(other_tab)); |
| while (browser()->tab_strip_model()->GetActiveWebContents() != other_tab) { |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| browser()->profile()->GetPrefs()->SetBoolean(prefs::kScreenCaptureAllowed, |
| false); |
| |
| // Click the secondary button, i.e., the "Share this tab instead" button. This |
| // is rejected since screen capture is not allowed by the above policy. |
| GetDelegate(other_tab)->Cancel(); |
| |
| // When "Share this tab instead" fails for other_tab, the focus goes back to |
| // the captured tab. Wait until that happens: |
| while (browser()->tab_strip_model()->GetActiveWebContents() != captured_tab) { |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| EXPECT_TRUE(captured_tab->IsBeingCaptured()); |
| EXPECT_FALSE(other_tab->IsBeingCaptured()); |
| EXPECT_FALSE(capturing_tab->IsBeingCaptured()); |
| EXPECT_EQ( |
| GetSecondaryButtonLabel(captured_tab), |
| l10n_util::GetStringFUTF16( |
| IDS_TAB_SHARING_INFOBAR_SWITCH_TO_BUTTON, |
| url_formatter::FormatOriginForSecurityDisplay( |
| captured_tab->GetPrimaryMainFrame()->GetLastCommittedOrigin(), |
| url_formatter::SchemeDisplay::OMIT_HTTP_AND_HTTPS))); |
| EXPECT_EQ(GetSecondaryButtonLabel(other_tab), kShareThisTabInsteadMessage); |
| EXPECT_EQ( |
| GetSecondaryButtonLabel(capturing_tab), |
| l10n_util::GetStringFUTF16( |
| IDS_TAB_SHARING_INFOBAR_SWITCH_TO_BUTTON, |
| url_formatter::FormatOriginForSecurityDisplay( |
| capturing_tab->GetPrimaryMainFrame()->GetLastCommittedOrigin(), |
| url_formatter::SchemeDisplay::OMIT_HTTP_AND_HTTPS))); |
| } |
| |
| class GetDisplayMediaSelfBrowserSurfaceBrowserTest |
| : public WebRtcTestBase, |
| public testing::WithParamInterface<std::string> { |
| public: |
| GetDisplayMediaSelfBrowserSurfaceBrowserTest() |
| : self_browser_surface_(GetParam()) {} |
| |
| void SetUpInProcessBrowserTestFixture() override { |
| WebRtcTestBase::SetUpInProcessBrowserTestFixture(); |
| |
| DetectErrorsInJavaScript(); |
| |
| base::FilePath test_dir; |
| ASSERT_TRUE(base::PathService::Get(chrome::DIR_TEST_DATA, &test_dir)); |
| } |
| |
| void SetUpCommandLine(base::CommandLine* command_line) override { |
| command_line->AppendSwitch( |
| switches::kEnableExperimentalWebPlatformFeatures); |
| |
| command_line->AppendSwitchASCII( |
| switches::kAutoSelectTabCaptureSourceByTitle, kMainHtmlTitle); |
| } |
| |
| std::string GetConstraints(bool prefer_current_tab = false) { |
| std::vector<std::string> constraints = {"video: true"}; |
| if (!self_browser_surface_.empty()) { |
| constraints.push_back(base::StringPrintf("selfBrowserSurface: \"%s\"", |
| self_browser_surface_.c_str())); |
| } |
| if (prefer_current_tab) { |
| constraints.push_back("preferCurrentTab: true"); |
| } |
| prefer_current_tab_ = prefer_current_tab; |
| return "{" + base::JoinString(constraints, ",") + "}"; |
| } |
| |
| bool IsSelfBrowserSurfaceExclude() const { |
| if (self_browser_surface_ == "" && !prefer_current_tab_) { |
| // Special case - when using the new order, selfBrowserSurface |
| // defaults to "exclude", unless {preferCurrentTab: true} is specified. |
| return true; |
| } |
| return self_browser_surface_ == "exclude"; |
| } |
| |
| protected: |
| // If empty, the constraint is unused. Otherwise, the value is either |
| // "include" or "exclude" |
| const std::string self_browser_surface_; |
| |
| // Whether {preferCurrentTab: true} will be specified by the test. |
| bool prefer_current_tab_ = false; |
| }; |
| |
| INSTANTIATE_TEST_SUITE_P(All, |
| GetDisplayMediaSelfBrowserSurfaceBrowserTest, |
| testing::Values("", "include", "exclude")); |
| |
| IN_PROC_BROWSER_TEST_P(GetDisplayMediaSelfBrowserSurfaceBrowserTest, |
| SelfBrowserSurfaceChangesCapturedTab) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| // This test relies on |capturing_tab| appearing earlier in the media picker, |
| // and being auto-selected earlier if it is offered. |
| content::WebContents* other_tab = OpenTestPageInNewTab(kMainHtmlPage); |
| content::WebContents* capturing_tab = OpenTestPageInNewTab(kMainHtmlPage); |
| |
| // Success expected either way, with the *other* tab being captured |
| // when selfBrowserCapture is set to "exclude". |
| RunGetDisplayMedia(capturing_tab, GetConstraints(), /*is_fake_ui=*/false, |
| /*expect_success=*/true, /*is_tab_capture=*/true); |
| |
| EXPECT_EQ(!IsSelfBrowserSurfaceExclude(), capturing_tab->IsBeingCaptured()); |
| EXPECT_EQ(IsSelfBrowserSurfaceExclude(), other_tab->IsBeingCaptured()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(GetDisplayMediaSelfBrowserSurfaceBrowserTest, |
| SelfBrowserSurfaceInteractionWithPreferCurrentTab) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| // This test relies on |capturing_tab| appearing earlier in the media picker, |
| // and being auto-selected earlier if it is offered. |
| content::WebContents* other_tab = OpenTestPageInNewTab(kMainHtmlPage); |
| content::WebContents* capturing_tab = OpenTestPageInNewTab(kMainHtmlPage); |
| |
| // Test focal point - getDisplayMedia() rejects if preferCurrentTab |
| // and exclude-current-tab are simultaneously specified. |
| // Note that preferCurrentTab is hard-coded in this test while |
| // exclude-current-tab is parameterized. |
| const bool expect_success = (self_browser_surface_ != "exclude"); |
| const std::string expected_error = |
| expect_success ? "" |
| : "TypeError: Failed to execute 'getDisplayMedia' on " |
| "'MediaDevices': Self-contradictory configuration " |
| "(preferCurrentTab and selfBrowserSurface=exclude)."; |
| RunGetDisplayMedia(capturing_tab, GetConstraints(/*prefer_current_tab=*/true), |
| /*is_fake_ui=*/false, expect_success, |
| /*is_tab_capture=*/true, expected_error); |
| |
| EXPECT_EQ(!IsSelfBrowserSurfaceExclude(), capturing_tab->IsBeingCaptured()); |
| EXPECT_FALSE(other_tab->IsBeingCaptured()); |
| } |
| |
| #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 GetDisplayMedia and GetDisplayMediaSetAutoSelectAllScreens |
| // 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, |
| testing::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 |