blob: adc321639f66beb08c50c04a37f490899dc1d099 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <string>
#include "base/memory/raw_ptr.h"
#include "base/path_service.h"
#include "base/strings/strcat.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 defined(OS_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/ash/policy/dlp/dlp_content_manager_ash_test_helper.h"
#include "chrome/browser/chromeos/policy/dlp/dlp_content_restriction_set.h"
#endif
namespace {
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";
// TODO(https://crbug.com/1215089): Enable on Lacros.
#if !BUILDFLAG(IS_CHROMEOS_LACROS)
// 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";
static const std::u16string kViewTabMessagePrefix = u"View tab:";
#endif
enum class DisplaySurfaceType { kTab, kWindow, kScreen };
enum class GetDisplayMediaVariant : int {
kStandard = 0,
kPreferCurrentTab = 1
};
struct TestConfigForPicker {
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;
};
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) {
std::string result;
EXPECT_TRUE(content::ExecuteScriptAndExtractString(
tab->GetMainFrame(),
base::StringPrintf("runGetDisplayMedia(%s, \"top-level-document\");",
constraints.c_str()),
&result));
#if defined(OS_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" : "capture-failure");
}
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);
}
// TODO(https://crbug.com/1215089): Enable on Lacros.
#if !BUILDFLAG(IS_CHROMEOS_LACROS)
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);
}
#endif
} // namespace
// Base class for top level tests for getDisplayMedia().
class WebRtcScreenCaptureBrowserTest : public WebRtcTestBase {
public:
~WebRtcScreenCaptureBrowserTest() override = default;
void SetUpInProcessBrowserTestFixture() override {
DetectErrorsInJavaScript();
}
virtual bool PreferCurrentTab() const = 0;
std::string GetConstraints(bool video, bool audio) const {
return base::StringPrintf(
"{video:%s, audio: %s, preferCurrentTab: %s}", video ? "true" : "false",
audio ? "true" : "false", PreferCurrentTab() ? "true" : "false");
}
};
// Top level test for getDisplayMedia().
// Pops picker UI and shares by default.
class WebRtcScreenCaptureBrowserTestWithPicker
: public WebRtcScreenCaptureBrowserTest,
public testing::WithParamInterface<TestConfigForPicker> {
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_;
};
// TODO(1170479): Real desktop capture is flaky on below platforms.
#if defined(OS_WIN)
#define MAYBE_ScreenCaptureVideo DISABLED_ScreenCaptureVideo
#else
#define MAYBE_ScreenCaptureVideo ScreenCaptureVideo
#endif // defined(OS_WIN)
IN_PROC_BROWSER_TEST_P(WebRtcScreenCaptureBrowserTestWithPicker,
MAYBE_ScreenCaptureVideo) {
ASSERT_TRUE(embedded_test_server()->Start());
content::WebContents* tab = OpenTestPageInNewTab(kMainHtmlPage);
RunGetDisplayMedia(tab, GetConstraints(/*video=*/true, /*audio=*/false),
/*is_fake_ui=*/false, test_config_.accept_this_tab_capture,
/*is_tab_capture=*/PreferCurrentTab());
}
#if BUILDFLAG(IS_CHROMEOS_ASH)
IN_PROC_BROWSER_TEST_P(WebRtcScreenCaptureBrowserTestWithPicker,
ScreenCaptureVideoWithDlp) {
ASSERT_TRUE(embedded_test_server()->Start());
policy::DlpContentManagerAshTestHelper helper;
content::WebContents* tab = OpenTestPageInNewTab(kMainHtmlPage);
RunGetDisplayMedia(tab, GetConstraints(/*video=*/true, /*audio=*/false),
/*is_fake_ui=*/false, 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->GetMainFrame(), "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->GetMainFrame(), "waitVideoMuted();", &result));
EXPECT_EQ(result, "muted");
const policy::DlpContentRestrictionSet kEmptyRestrictionSet;
helper.ChangeConfidentiality(tab, kEmptyRestrictionSet);
EXPECT_TRUE(content::ExecuteScriptAndExtractString(
tab->GetMainFrame(), "waitVideoUnmuted();", &result));
EXPECT_EQ(result, "unmuted");
}
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
// TODO(1170479): Real desktop capture is flaky on below platforms.
#if defined(OS_WIN)
#define MAYBE_ScreenCaptureVideoAndAudio DISABLED_ScreenCaptureVideoAndAudio
// On linux debug bots, it's flaky as well.
#elif ((defined(OS_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 ((defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)) && \
defined(ADDRESS_SANITIZER))
#define MAYBE_ScreenCaptureVideoAndAudio DISABLED_ScreenCaptureVideoAndAudio
#else
#define MAYBE_ScreenCaptureVideoAndAudio ScreenCaptureVideoAndAudio
#endif // defined(OS_WIN)
IN_PROC_BROWSER_TEST_P(WebRtcScreenCaptureBrowserTestWithPicker,
MAYBE_ScreenCaptureVideoAndAudio) {
ASSERT_TRUE(embedded_test_server()->Start());
content::WebContents* tab = OpenTestPageInNewTab(kMainHtmlPage);
RunGetDisplayMedia(tab, GetConstraints(/*video=*/true, /*audio=*/true),
/*is_fake_ui=*/false, test_config_.accept_this_tab_capture,
/*is_tab_capture=*/PreferCurrentTab());
}
INSTANTIATE_TEST_SUITE_P(
All,
WebRtcScreenCaptureBrowserTestWithPicker,
testing::Values(TestConfigForPicker{/*should_prefer_current_tab_=*/false,
/*accept_this_tab_capture=*/true},
TestConfigForPicker{/*should_prefer_current_tab_=*/true,
/*accept_this_tab_capture=*/true},
TestConfigForPicker{/*should_prefer_current_tab_=*/true,
/*accept_this_tab_capture=*/false}));
// 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),
/*is_fake_ui=*/true, /*expect_success=*/true,
/*is_tab_capture=*/PreferCurrentTab());
std::string result;
EXPECT_TRUE(content::ExecuteScriptAndExtractString(
tab->GetMainFrame(), "getDisplaySurfaceSetting();", &result));
EXPECT_EQ(result, test_config_.display_surface);
EXPECT_TRUE(content::ExecuteScriptAndExtractString(
tab->GetMainFrame(), "getLogicalSurfaceSetting();", &result));
EXPECT_EQ(result, "true");
EXPECT_TRUE(content::ExecuteScriptAndExtractString(
tab->GetMainFrame(), "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),
/*is_fake_ui=*/true, /*expect_success=*/true,
/*is_tab_capture=*/PreferCurrentTab());
std::string result;
EXPECT_TRUE(content::ExecuteScriptAndExtractString(
tab->GetMainFrame(), "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->GetMainFrame(), "getWidthSetting();", &result));
EXPECT_EQ(result, base::StringPrintf("%d", kMaxWidth));
EXPECT_TRUE(content::ExecuteScriptAndExtractString(
tab->GetMainFrame(), "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"}));
// TODO(https://crbug.com/1215089): Enable this test suite on Lacros.
#if !BUILDFLAG(IS_CHROMEOS_LACROS)
class WebRtcScreenCapturePermissionPolicyBrowserTest
: public WebRtcScreenCaptureBrowserTest,
public testing::WithParamInterface<
std::pair<GetDisplayMediaVariant, bool>> {
public:
WebRtcScreenCapturePermissionPolicyBrowserTest()
: tested_variant_(GetParam().first),
allowlisted_by_policy_(GetParam().second) {}
~WebRtcScreenCapturePermissionPolicyBrowserTest() override = default;
void SetUpCommandLine(base::CommandLine* command_line) override {
command_line->AppendSwitch(
switches::kEnableExperimentalWebPlatformFeatures);
command_line->AppendSwitchASCII(
switches::kAutoSelectTabCaptureSourceByTitle, "WebRTC Automated Test");
}
bool PreferCurrentTab() const override {
return tested_variant_ == GetDisplayMediaVariant::kPreferCurrentTab;
}
protected:
const GetDisplayMediaVariant tested_variant_;
const bool allowlisted_by_policy_;
private:
};
INSTANTIATE_TEST_SUITE_P(
All,
WebRtcScreenCapturePermissionPolicyBrowserTest,
testing::Values(std::make_pair(GetDisplayMediaVariant::kStandard,
/*allowlisted_by_policy=*/false),
std::make_pair(GetDisplayMediaVariant::kStandard,
/*allowlisted_by_policy=*/true),
std::make_pair(GetDisplayMediaVariant::kPreferCurrentTab,
/*allowlisted_by_policy=*/false),
std::make_pair(GetDisplayMediaVariant::kPreferCurrentTab,
/*allowlisted_by_policy=*/true)));
// Flaky on Win bots http://crbug.com/1264805
#if defined(OS_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());
const std::string constraints =
base::StringPrintf("{video: true, preferCurrentTab: %s}",
PreferCurrentTab() ? "true" : "false");
std::string result;
EXPECT_TRUE(content::ExecuteScriptAndExtractString(
OpenTestPageInNewTab(kMainHtmlPage)->GetMainFrame(),
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");
}
#endif
// 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 matchlist(base::Value::Type::LIST);
matchlist.Append("*");
browser()->profile()->GetPrefs()->Set(prefs::kTabCaptureAllowedByOrigins,
matchlist);
}
void TearDownOnMainThread() override {
extensions::PlatformAppBrowserTest::TearDownOnMainThread();
browser()->profile()->GetPrefs()->Set(prefs::kTabCaptureAllowedByOrigins,
base::Value(base::Value::Type::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 matchlist(base::Value::Type::LIST);
matchlist.Append("*");
browser()->profile()->GetPrefs()->Set(
prefs::kSameOriginTabCaptureAllowedByOrigins, matchlist);
}
void TearDownOnMainThread() override {
WebRtcScreenCaptureBrowserTest::TearDownOnMainThread();
browser()->profile()->GetPrefs()->Set(
prefs::kSameOriginTabCaptureAllowedByOrigins,
base::Value(base::Value::Type::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),
/*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, {TabStripModel::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->GetMainFrame(), "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),
/*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, {TabStripModel::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->GetMainFrame(), "returnToTest(video_track.readyState);",
&result));
EXPECT_EQ(result, "live");
}
class GetDisplayMediaVideoTrackBrowserTest
: public WebRtcTestBase,
public testing::WithParamInterface<
std::tuple<bool, bool, DisplaySurfaceType>> {
public:
GetDisplayMediaVideoTrackBrowserTest()
: conditional_focus_enabled_(std::get<0>(GetParam())),
region_capture_enabled_(std::get<1>(GetParam())),
display_surface_type_(std::get<2>(GetParam())) {}
~GetDisplayMediaVideoTrackBrowserTest() override = default;
void SetUpInProcessBrowserTestFixture() override {
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 matchlist(base::Value::Type::LIST);
matchlist.Append("*");
browser()->profile()->GetPrefs()->Set(prefs::kTabCaptureAllowedByOrigins,
matchlist);
#endif
}
// 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_->GetMainFrame(),
"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;
if (conditional_focus_enabled_) {
enabled_blink_features.push_back("ConditionalFocus");
}
if (region_capture_enabled_) {
enabled_blink_features.push_back("RegionCapture");
}
if (!enabled_blink_features.empty()) {
command_line->AppendSwitchASCII(
switches::kEnableBlinkFeatures,
base::JoinString(enabled_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_->GetMainFrame(), "getVideoTrackType();", &result));
return result;
}
std::string GetVideoCloneTrackType() {
std::string result;
EXPECT_TRUE(content::ExecuteScriptAndExtractString(
tab_->GetMainFrame(), "getVideoCloneTrackType();", &result));
return result;
}
bool HasAudioTrack() {
std::string result;
EXPECT_TRUE(content::ExecuteScriptAndExtractString(
tab_->GetMainFrame(), "hasAudioTrack();", &result));
EXPECT_TRUE(result == "true" || result == "false");
return result == "true";
}
std::string GetAudioTrackType() {
std::string result;
EXPECT_TRUE(content::ExecuteScriptAndExtractString(
tab_->GetMainFrame(), "getAudioTrackType();", &result));
return result;
}
std::string ExpectedVideoTrackType() const {
switch (display_surface_type_) {
case DisplaySurfaceType::kTab:
return region_capture_enabled_
? "BrowserCaptureMediaStreamTrack"
: conditional_focus_enabled_ ? "FocusableMediaStreamTrack"
: "MediaStreamTrack";
case DisplaySurfaceType::kWindow:
return conditional_focus_enabled_ || region_capture_enabled_
? "FocusableMediaStreamTrack"
: "MediaStreamTrack";
case DisplaySurfaceType::kScreen:
return "MediaStreamTrack";
}
NOTREACHED();
return "Error";
}
protected:
const bool conditional_focus_enabled_;
const bool region_capture_enabled_;
const DisplaySurfaceType display_surface_type_;
private:
raw_ptr<content::WebContents> tab_ = nullptr;
};
INSTANTIATE_TEST_SUITE_P(
_,
GetDisplayMediaVideoTrackBrowserTest,
testing::Combine(/*conditional_focus_enabled=*/testing::Bool(),
/*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) ? "ConditionalFocus" : "",
std::get<1>(info.param) ? "RegionCapture" : "",
std::get<2>(info.param) == DisplaySurfaceType::kTab
? "Tab"
: std::get<2>(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");
}
}
// TODO(https://crbug.com/1215089): Enable this test suite on Lacros.
#if !BUILDFLAG(IS_CHROMEOS_LACROS)
class GetDisplayMediaChangeSourceBrowserTest : public WebRtcTestBase {
public:
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, kCapturedTabTitle);
}
};
IN_PROC_BROWSER_TEST_F(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, "{video: true}", /*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->GetMainFrame()->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->GetMainFrame()->GetLastCommittedOrigin(),
url_formatter::SchemeDisplay::OMIT_HTTP_AND_HTTPS)));
// 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->GetMainFrame()->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->GetMainFrame()->GetLastCommittedOrigin(),
url_formatter::SchemeDisplay::OMIT_HTTP_AND_HTTPS)));
}
IN_PROC_BROWSER_TEST_F(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, "{video: true}", /*is_fake_ui=*/false,
/*expect_success=*/true,
/*is_tab_capture=*/true);
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->GetMainFrame()->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->GetMainFrame()->GetLastCommittedOrigin(),
url_formatter::SchemeDisplay::OMIT_HTTP_AND_HTTPS)));
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->GetMainFrame()->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->GetMainFrame()->GetLastCommittedOrigin(),
url_formatter::SchemeDisplay::OMIT_HTTP_AND_HTTPS)));
}
#endif