blob: cff5de5efc1b0c31f31e0542649dd391a195a95e [file] [log] [blame]
// Copyright 2021 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 <memory>
#include <string>
#include <vector>
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/memory/raw_ptr.h"
#include "base/path_service.h"
#include "base/strings/strcat.h"
#include "base/strings/stringprintf.h"
#include "build/build_config.h"
#include "build/buildflag.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/media/webrtc/webrtc_browsertest_base.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_tabstrip.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/permissions/permission_request_manager.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_features.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_base.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/prerender_test_util.h"
// TODO(crbug.com/1215089): Enable this test suite on Lacros.
#if !BUILDFLAG(IS_CHROMEOS_LACROS)
using content::WebContents;
namespace {
// The captured tab is identified by its title.
const char kCapturedTabTitle[] = "totally-unique-captured-page-title";
// Capturing page (top-level document).
const char kCapturingPageMain[] = "/webrtc/capturing_page_main.html";
// Capturing page (embedded document).
const char kCapturingPageEmbedded[] = "/webrtc/capturing_page_embedded.html";
// Captured page.
const char kCapturedPageMain[] = "/webrtc/captured_page_main.html";
// Similar contents to kCapturedPageMain, but on a different page, which can
// be served same-origin or cross-origin.
const char kCapturedPageOther[] = "/webrtc/captured_page_other.html";
const char* kArbitraryOrigin = "https://arbitrary-origin.com";
const char* kNoCaptureHandle = "no-capture-handle";
const char* kNoEmbeddedCaptureHandle = "no-embedded-capture-handle";
enum class BrowserType { kRegular, kIncognito };
std::string StringifyPermittedOrigins(
const std::vector<std::string>& permitted_origins) {
if (permitted_origins.empty()) {
return "[]";
}
return base::StrCat(
{"[\"", base::JoinString(permitted_origins, "\", \""), "\"]"});
}
std::string StringifyCaptureHandle(WebContents* web_contents,
bool expose_origin,
const std::string& handle) {
if (!expose_origin && handle.empty()) {
return "";
}
std::string origin_str;
if (expose_origin) {
const auto origin = web_contents->GetMainFrame()->GetLastCommittedOrigin();
origin_str =
base::StringPrintf(",\"origin\":\"%s\"", origin.Serialize().c_str());
}
return base::StringPrintf("{\"handle\":\"%s\"%s}", handle.c_str(),
origin_str.c_str());
}
// Conveniently pack together a captured tab and the capture-handle that
// is expected to be observed by capturers from a permitted origin.
struct TabInfo {
void StartCapturing() {
// Bring the tab into focus. This avoids getDisplayMedia rejection.
browser->tab_strip_model()->ActivateTabAt(tab_strip_index);
std::string script_result;
EXPECT_TRUE(content::ExecuteScriptAndExtractString(
web_contents->GetMainFrame(), "captureOtherTab();", &script_result));
EXPECT_EQ(script_result, "capture-success");
}
void StartCapturingFromEmbeddedFrame() {
std::string script_result;
EXPECT_TRUE(content::ExecuteScriptAndExtractString(
web_contents->GetMainFrame(), "captureOtherTabFromEmbeddedFrame();",
&script_result));
EXPECT_EQ(script_result, "embedded-capture-success");
}
url::Origin GetOrigin() const {
return web_contents->GetMainFrame()->GetLastCommittedOrigin();
}
std::string GetOriginAsString() const { return GetOrigin().Serialize(); }
void SetCaptureHandleConfig(
bool expose_origin,
const std::string& handle,
const std::vector<std::string>& permitted_origins) {
std::string script_result;
EXPECT_TRUE(content::ExecuteScriptAndExtractString(
web_contents->GetMainFrame(),
base::StringPrintf(
"callSetCaptureHandleConfig(%s, \"%s\", %s);",
expose_origin ? "true" : "false", handle.c_str(),
StringifyPermittedOrigins(permitted_origins).c_str()),
&script_result));
EXPECT_EQ(script_result, "capture-handle-set");
capture_handle =
StringifyCaptureHandle(web_contents, expose_origin, handle);
}
std::string ReadCaptureHandle() {
std::string script_result;
EXPECT_TRUE(content::ExecuteScriptAndExtractString(
web_contents->GetMainFrame(), "readCaptureHandle();", &script_result));
return script_result;
}
std::string ReadCaptureHandleInEmbeddedFrame() {
std::string script_result;
EXPECT_TRUE(content::ExecuteScriptAndExtractString(
web_contents->GetMainFrame(), "readCaptureHandleInEmbeddedFrame();",
&script_result));
return script_result;
}
void Navigate(GURL url, bool expect_handle_reset = false) {
std::string script_result;
ASSERT_TRUE(content::ExecuteScriptAndExtractString(
web_contents->GetMainFrame(),
base::StringPrintf("clickLinkToUrl(\"%s\");", url.spec().c_str()),
&script_result));
ASSERT_EQ(script_result, "link-success");
if (expect_handle_reset) {
capture_handle = "";
}
}
std::string LastEvent() {
std::string script_result = "error-not-modified";
EXPECT_TRUE(content::ExecuteScriptAndExtractString(
web_contents->GetMainFrame(), "readLastEvent();", &script_result));
return script_result;
}
std::string LastEmbeddedEvent() {
std::string script_result = "error-not-modified";
EXPECT_TRUE(content::ExecuteScriptAndExtractString(
web_contents->GetMainFrame(), "readLastEmbeddedEvent();",
&script_result));
return script_result;
}
void StartEmbeddingFrame(const GURL& url) {
std::string script_result;
EXPECT_TRUE(content::ExecuteScriptAndExtractString(
web_contents->GetMainFrame(),
base::StringPrintf("startEmbeddingFrame('%s');", url.spec().c_str()),
&script_result));
EXPECT_EQ(script_result, "embedding-done");
}
raw_ptr<Browser> browser;
raw_ptr<WebContents> web_contents;
int tab_strip_index;
std::string capture_handle; // Expected value for those who may observe.
};
TabInfo MakeTabInfoFromActiveTab(Browser* browser) {
WebContents* const web_contents =
browser->tab_strip_model()->GetActiveWebContents();
// "POISON_VALUE" intentionally fails comparisons if unset when read.
return TabInfo{browser, web_contents,
browser->tab_strip_model()->active_index(), "POISON_VALUE"};
}
} // namespace
// Essentially depends on InProcessBrowserTest, but WebRtcTestBase provides
// detection of JS errors.
class CaptureHandleBrowserTest : public WebRtcTestBase {
public:
void SetUpInProcessBrowserTestFixture() override {
WebRtcTestBase::SetUpInProcessBrowserTestFixture();
DetectErrorsInJavaScript();
base::FilePath test_dir;
ASSERT_TRUE(base::PathService::Get(chrome::DIR_TEST_DATA, &test_dir));
for (int i = 0; i < kServerCount; ++i) {
servers_.emplace_back(std::make_unique<net::EmbeddedTestServer>());
servers_[i]->ServeFilesFromDirectory(test_dir);
ASSERT_TRUE(servers_[i]->Start());
}
}
void SetUpCommandLine(base::CommandLine* command_line) override {
command_line->AppendSwitch(
switches::kEnableExperimentalWebPlatformFeatures);
command_line->AppendSwitchASCII(
switches::kAutoSelectTabCaptureSourceByTitle, kCapturedTabTitle);
}
void TearDownOnMainThread() override {
for (auto& server : servers_) {
if (server) {
ASSERT_TRUE(server->ShutdownAndWaitUntilComplete());
}
}
WebRtcTestBase::TearDownOnMainThread();
}
// Same as WebRtcTestBase::OpenTestPageInNewTab, but does not assume
// a single embedded server is used for all pages.
WebContents* OpenTestPageInNewTab(Browser* browser,
const std::string& test_page,
net::EmbeddedTestServer* server) const {
chrome::AddTabAt(browser, GURL(url::kAboutBlankURL), -1, true);
GURL url = server->GetURL(test_page);
EXPECT_TRUE(ui_test_utils::NavigateToURL(browser, url));
WebContents* new_tab = browser->tab_strip_model()->GetActiveWebContents();
permissions::PermissionRequestManager::FromWebContents(new_tab)
->set_auto_response_for_test(
permissions::PermissionRequestManager::ACCEPT_ALL);
return new_tab;
}
Browser* GetBrowser(BrowserType browser_type) {
DCHECK(browser_type == BrowserType::kRegular ||
browser_type == BrowserType::kIncognito);
if (browser_type == BrowserType::kRegular) {
return browser();
}
if (!incognito_browser_) {
incognito_browser_ = CreateIncognitoBrowser();
}
return incognito_browser_;
}
TabInfo SetUpCapturingPage(bool start_capturing,
BrowserType browser_type = BrowserType::kRegular) {
Browser* const browser = GetBrowser(browser_type);
OpenTestPageInNewTab(browser, kCapturingPageMain,
servers_[kCapturingServer].get());
auto result = MakeTabInfoFromActiveTab(browser);
if (start_capturing) {
result.StartCapturing();
}
event_sinks_.push_back(result.web_contents);
return result;
}
TabInfo SetUpCapturedPage(bool expose_origin,
const std::string& handle,
const std::vector<std::string>& permitted_origins,
bool self_capture = false,
BrowserType browser_type = BrowserType::kRegular) {
// Normally, the captured page has its own server (=origin) and own file.
// But if self-capture is tested, use the origin and page of the capturer.
const char* page = self_capture ? kCapturingPageMain : kCapturedPageMain;
const int server_index = self_capture ? kCapturingServer : kCapturedServer;
Browser* const browser = GetBrowser(browser_type);
auto* const web_contents =
OpenTestPageInNewTab(browser, page, servers_[server_index].get());
// The target for getDisplayMedia is determined via the title. If we want
// the capturing page to capture itself, then it has to change its title.
if (self_capture) {
std::string script_result;
EXPECT_TRUE(content::ExecuteScriptAndExtractString(
web_contents->GetMainFrame(),
base::StringPrintf("setTitle(\"%s\");", kCapturedTabTitle),
&script_result));
EXPECT_EQ(script_result, "title-changed");
}
auto tab_info = MakeTabInfoFromActiveTab(browser);
tab_info.SetCaptureHandleConfig(expose_origin, handle, permitted_origins);
return tab_info;
}
enum {
kCapturedServer,
kOtherCapturedServer,
kCapturingServer, // Top-level document.
kEmbeddedCapturingServer, // Embedded iframe.
// Must be last.
kServerCount
};
// Checked for no unconsumed events.
std::vector<WebContents*> event_sinks_;
// Three servers to create three origins (different ports). One server for the
// captured page, one for the top-level capturer and one for the embedded
// capturer. Some tests will use one server for multiple pages so as to
// make them same-origin.
std::vector<std::unique_ptr<net::EmbeddedTestServer>> servers_;
// Incognito browser.
// Note: The regular one is accessible via browser().
raw_ptr<Browser> incognito_browser_ = nullptr;
};
IN_PROC_BROWSER_TEST_F(CaptureHandleBrowserTest,
HandleAndOriginExposedIfAllPermitted) {
TabInfo captured_tab =
SetUpCapturedPage(/*expose_origin=*/true, "handle", {"*"});
TabInfo capturing_tab = SetUpCapturingPage(/*start_capturing=*/true);
// The capture handle set by the captured tab is observable by the capturer.
EXPECT_EQ(capturing_tab.ReadCaptureHandle(), captured_tab.capture_handle);
}
IN_PROC_BROWSER_TEST_F(CaptureHandleBrowserTest,
HandleAndOriginExposedIfCapturerOriginPermitted) {
TabInfo capturing_tab = SetUpCapturingPage(/*start_capturing=*/false);
TabInfo captured_tab = SetUpCapturedPage(/*expose_origin=*/true, "handle",
{capturing_tab.GetOriginAsString()});
capturing_tab.StartCapturing();
// The capture handle set by the captured tab is observable by the capturer.
EXPECT_EQ(capturing_tab.ReadCaptureHandle(), captured_tab.capture_handle);
}
IN_PROC_BROWSER_TEST_F(CaptureHandleBrowserTest,
HandleAndOriginNotExposedIfCapturerOriginNotPermitted) {
TabInfo capturing_tab = SetUpCapturingPage(/*start_capturing=*/false);
TabInfo captured_tab =
SetUpCapturedPage(/*expose_origin=*/true, "handle", {kArbitraryOrigin});
capturing_tab.StartCapturing();
// The capture handle isn't observable by the capturer.
EXPECT_EQ(capturing_tab.ReadCaptureHandle(), kNoCaptureHandle);
}
// TODO(crbug.com/1217873): Test disabled on Mac due to multiple failing bots.
#if BUILDFLAG(IS_MAC)
#define MAYBE_HandleNotExposedIfTopLevelAllowlistedButCallingFrameNotAllowlisted \
DISABLED_HandleNotExposedIfTopLevelAllowlistedButCallingFrameNotAllowlisted
#else
#define MAYBE_HandleNotExposedIfTopLevelAllowlistedButCallingFrameNotAllowlisted \
HandleNotExposedIfTopLevelAllowlistedButCallingFrameNotAllowlisted
#endif
IN_PROC_BROWSER_TEST_F(
CaptureHandleBrowserTest,
MAYBE_HandleNotExposedIfTopLevelAllowlistedButCallingFrameNotAllowlisted) {
TabInfo capturing_tab = SetUpCapturingPage(/*start_capturing=*/false);
const url::Origin& top_level_capturer_origin =
url::Origin::Create(servers_[kCapturingServer]->base_url());
const url::Origin& embedded_capturer_origin =
url::Origin::Create(servers_[kEmbeddedCapturingServer]->base_url());
ASSERT_FALSE(
top_level_capturer_origin.IsSameOriginWith(embedded_capturer_origin));
TabInfo captured_tab =
SetUpCapturedPage(/*expose_origin=*/true, "handle",
{top_level_capturer_origin.Serialize()});
capturing_tab.StartEmbeddingFrame(
servers_[kEmbeddedCapturingServer]->GetURL(kCapturingPageEmbedded));
capturing_tab.StartCapturingFromEmbeddedFrame();
// The capture handle isn't observable by the capturer.
EXPECT_EQ(capturing_tab.ReadCaptureHandleInEmbeddedFrame(),
kNoEmbeddedCaptureHandle);
// Even when the capture handle changes - no events are fired and the
// capture handle remains unobservable via getCaptureHandle.
captured_tab.SetCaptureHandleConfig(/*expose_origin=*/true, "new_handle",
{top_level_capturer_origin.Serialize()});
}
// TODO(crbug.com/1217873): Test disabled on Mac due to multiple failing bots.
#if BUILDFLAG(IS_MAC)
#define MAYBE_HandleExposedIfCallingFrameAllowlistedEvenIfTopLevelNotAllowlisted \
DISABLED_HandleExposedIfCallingFrameAllowlistedEvenIfTopLevelNotAllowlisted
#else
#define MAYBE_HandleExposedIfCallingFrameAllowlistedEvenIfTopLevelNotAllowlisted \
HandleExposedIfCallingFrameAllowlistedEvenIfTopLevelNotAllowlisted
#endif
IN_PROC_BROWSER_TEST_F(
CaptureHandleBrowserTest,
MAYBE_HandleExposedIfCallingFrameAllowlistedEvenIfTopLevelNotAllowlisted) {
TabInfo capturing_tab = SetUpCapturingPage(/*start_capturing=*/false);
const url::Origin& top_level_capturer_origin =
url::Origin::Create(servers_[kCapturingServer]->base_url());
const url::Origin& embedded_capturer_origin =
url::Origin::Create(servers_[kEmbeddedCapturingServer]->base_url());
ASSERT_FALSE(
top_level_capturer_origin.IsSameOriginWith(embedded_capturer_origin));
TabInfo captured_tab = SetUpCapturedPage(
/*expose_origin=*/true, "handle", {embedded_capturer_origin.Serialize()});
capturing_tab.StartEmbeddingFrame(
servers_[kEmbeddedCapturingServer]->GetURL(kCapturingPageEmbedded));
capturing_tab.StartCapturingFromEmbeddedFrame();
// The capture handle is observable by the capturer.
EXPECT_EQ(capturing_tab.ReadCaptureHandleInEmbeddedFrame(),
captured_tab.capture_handle);
// When the capture handle changes, events are fired and the
// capture handle remains observable via getCaptureHandle.
captured_tab.SetCaptureHandleConfig(/*expose_origin=*/true, "new_handle",
{embedded_capturer_origin.Serialize()});
EXPECT_EQ(capturing_tab.LastEmbeddedEvent(), captured_tab.capture_handle);
EXPECT_EQ(capturing_tab.ReadCaptureHandleInEmbeddedFrame(),
captured_tab.capture_handle);
}
IN_PROC_BROWSER_TEST_F(CaptureHandleBrowserTest, CanExposeOnlyHandle) {
TabInfo captured_tab =
SetUpCapturedPage(/*expose_origin=*/false, "handle", {"*"});
ASSERT_EQ(captured_tab.capture_handle.find("origin"), std::string::npos);
TabInfo capturing_tab = SetUpCapturingPage(/*start_capturing=*/true);
// The capture handle set by the captured tab is observable by the capturer.
EXPECT_EQ(capturing_tab.ReadCaptureHandle(), captured_tab.capture_handle);
}
IN_PROC_BROWSER_TEST_F(CaptureHandleBrowserTest,
CanExposeEmptyHandleIfExposingOrigin) {
TabInfo captured_tab =
SetUpCapturedPage(/*expose_origin=*/true, /*handle=*/"", {"*"});
// Still expecting "handle: \"\"" in there.
ASSERT_NE(captured_tab.capture_handle.find("handle"), std::string::npos);
TabInfo capturing_tab = SetUpCapturingPage(/*start_capturing=*/true);
// The capture handle set by the captured tab is observable by the capturer.
EXPECT_EQ(capturing_tab.ReadCaptureHandle(), captured_tab.capture_handle);
}
IN_PROC_BROWSER_TEST_F(CaptureHandleBrowserTest,
EmptyCaptureHandleConfigMeansCaptureHandleNotExposed) {
// Note - even if we set permitted origins, the empty config is empty.
TabInfo captured_tab =
SetUpCapturedPage(/*expose_origin=*/false, /*handle=*/"", {"*"});
// Not expecting "handle: \"\"" in there, nor "origin:..."
ASSERT_EQ(captured_tab.capture_handle, "");
TabInfo capturing_tab = SetUpCapturingPage(/*start_capturing=*/true);
// The capture handle isn't observable by the capturer.
EXPECT_EQ(capturing_tab.ReadCaptureHandle(), kNoCaptureHandle);
}
IN_PROC_BROWSER_TEST_F(
CaptureHandleBrowserTest,
CallingSetCaptureHandleConfigWithEmptyConfigFiresEventAndClearsValue) {
TabInfo captured_tab =
SetUpCapturedPage(/*expose_origin=*/true, "handle", {"*"});
TabInfo capturing_tab = SetUpCapturingPage(/*start_capturing=*/true);
// The capture handle set by the captured tab is observable by the capturer.
EXPECT_EQ(capturing_tab.ReadCaptureHandle(), captured_tab.capture_handle);
// New CaptureHandleConfig set by captured tab triggers an event, and all
// subsequent calls to getCaptureHandle produce the new values.
captured_tab.SetCaptureHandleConfig(/*expose_origin=*/false, "", {});
EXPECT_EQ(capturing_tab.LastEvent(), "{}");
EXPECT_EQ(capturing_tab.ReadCaptureHandle(), kNoCaptureHandle);
}
IN_PROC_BROWSER_TEST_F(
CaptureHandleBrowserTest,
CallingSetCaptureHandleConfigWithNewHandleChangesConfigAndFiresEvent) {
TabInfo captured_tab =
SetUpCapturedPage(/*expose_origin=*/true, "handle", {"*"});
TabInfo capturing_tab = SetUpCapturingPage(/*start_capturing=*/true);
// The capture handle set by the captured tab is observable by the capturer.
EXPECT_EQ(capturing_tab.ReadCaptureHandle(), captured_tab.capture_handle);
// New CaptureHandleConfig set by captured tab triggers an event, and all
// subsequent calls to getCaptureHandle produce the new values.
captured_tab.SetCaptureHandleConfig(/*expose_origin=*/true, "new_handle",
{"*"});
EXPECT_EQ(capturing_tab.LastEvent(), captured_tab.capture_handle);
EXPECT_EQ(capturing_tab.ReadCaptureHandle(), captured_tab.capture_handle);
}
IN_PROC_BROWSER_TEST_F(
CaptureHandleBrowserTest,
CallingSetCaptureHandleConfigWithNewOriginValueChangesConfigAndFiresEvent) {
TabInfo captured_tab =
SetUpCapturedPage(/*expose_origin=*/true, "handle", {"*"});
TabInfo capturing_tab = SetUpCapturingPage(/*start_capturing=*/true);
// The capture handle set by the captured tab is observable by the capturer.
EXPECT_EQ(capturing_tab.ReadCaptureHandle(), captured_tab.capture_handle);
// New CaptureHandleConfig set by captured tab triggers an event, and all
// subsequent calls to getCaptureHandle produce the new values.
captured_tab.SetCaptureHandleConfig(/*expose_origin=*/false, "handle", {"*"});
EXPECT_EQ(capturing_tab.LastEvent(), captured_tab.capture_handle);
EXPECT_EQ(capturing_tab.ReadCaptureHandle(), captured_tab.capture_handle);
}
IN_PROC_BROWSER_TEST_F(
CaptureHandleBrowserTest,
PermittedOriginsChangeThatRemovesCapturerCausesEventAndEmptyConfig) {
TabInfo captured_tab =
SetUpCapturedPage(/*expose_origin=*/true, "handle", {"*"});
TabInfo capturing_tab = SetUpCapturingPage(/*start_capturing=*/true);
// The capture handle set by the captured tab is observable by the capturer.
EXPECT_EQ(capturing_tab.ReadCaptureHandle(), captured_tab.capture_handle);
// New CaptureHandleConfig set by captured tab triggers an event, and all
// subsequent calls to getCaptureHandle produce the new values.
captured_tab.SetCaptureHandleConfig(/*expose_origin=*/true, "handle",
{kArbitraryOrigin});
EXPECT_EQ(capturing_tab.LastEvent(), "{}");
EXPECT_EQ(capturing_tab.ReadCaptureHandle(), kNoCaptureHandle);
}
IN_PROC_BROWSER_TEST_F(
CaptureHandleBrowserTest,
PermittedOriginsChangeThatAddsCapturerCausesEventAndConfigExposure) {
TabInfo captured_tab =
SetUpCapturedPage(/*expose_origin=*/true, "handle", {kArbitraryOrigin});
TabInfo capturing_tab = SetUpCapturingPage(/*start_capturing=*/true);
// The capture handle set by the captured tab is observable by the capturer.
EXPECT_EQ(capturing_tab.ReadCaptureHandle(), kNoCaptureHandle);
// New CaptureHandleConfig set by captured tab triggers an event, and all
// subsequent calls to getCaptureHandle produce the new values.
captured_tab.SetCaptureHandleConfig(/*expose_origin=*/true, "handle", {"*"});
EXPECT_EQ(capturing_tab.LastEvent(), captured_tab.capture_handle);
EXPECT_EQ(capturing_tab.ReadCaptureHandle(), captured_tab.capture_handle);
}
IN_PROC_BROWSER_TEST_F(
CaptureHandleBrowserTest,
PermittedOriginsChangeThatDoesNotAffectCapturerDoesNotCauseEventOrChange) {
TabInfo captured_tab =
SetUpCapturedPage(/*expose_origin=*/true, "handle", {"*"});
TabInfo capturing_tab = SetUpCapturingPage(/*start_capturing=*/true);
// The capture handle set by the captured tab is observable by the capturer.
EXPECT_EQ(capturing_tab.ReadCaptureHandle(), captured_tab.capture_handle);
// New CaptureHandleConfig set by captured tab triggers an event, and all
// subsequent calls to getCaptureHandle produce the new values.
captured_tab.SetCaptureHandleConfig(/*expose_origin=*/true, "handle",
{capturing_tab.GetOriginAsString()});
EXPECT_EQ(capturing_tab.ReadCaptureHandle(), captured_tab.capture_handle);
}
IN_PROC_BROWSER_TEST_F(CaptureHandleBrowserTest,
SameDocumentNavigationDoesNotClearTheCaptureHandle) {
TabInfo captured_tab =
SetUpCapturedPage(/*expose_origin=*/true, "handle", {"*"});
TabInfo capturing_tab = SetUpCapturingPage(/*start_capturing=*/true);
// Sanity test - there was an initial handle.
EXPECT_EQ(capturing_tab.ReadCaptureHandle(), captured_tab.capture_handle);
// In-document navigation does not change the capture handle (config).
std::string navigation_result;
EXPECT_TRUE(content::ExecuteScriptAndExtractString(
captured_tab.web_contents->GetMainFrame(), "clickLinkToPageBottom();",
&navigation_result));
ASSERT_EQ(navigation_result, "navigated");
// No event was fired (verified in teardown) and getCaptureHandle returns the
// same configuration as previously.
EXPECT_EQ(capturing_tab.ReadCaptureHandle(), captured_tab.capture_handle);
}
IN_PROC_BROWSER_TEST_F(CaptureHandleBrowserTest,
CrossDocumentNavigationClearsTheCaptureHandle) {
TabInfo captured_tab =
SetUpCapturedPage(/*expose_origin=*/true, "handle", {"*"});
TabInfo capturing_tab = SetUpCapturingPage(/*start_capturing=*/true);
// Sanity test - there was an initial handle.
EXPECT_EQ(capturing_tab.ReadCaptureHandle(), captured_tab.capture_handle);
// Cross-document navigation clears the capture handle (config).
captured_tab.Navigate(servers_[kCapturedServer]->GetURL(kCapturedPageOther),
/*expect_handle_reset=*/true);
// Navigation cleared the the capture handle, and that fired an event
// with the empty CaptureHandle.
EXPECT_EQ(capturing_tab.LastEvent(), "{}");
EXPECT_EQ(capturing_tab.ReadCaptureHandle(), kNoCaptureHandle);
}
IN_PROC_BROWSER_TEST_F(CaptureHandleBrowserTest,
CrossOriginNavigationClearsTheCaptureHandle) {
TabInfo captured_tab =
SetUpCapturedPage(/*expose_origin=*/true, "handle", {"*"});
TabInfo capturing_tab = SetUpCapturingPage(/*start_capturing=*/true);
// Sanity test - there was an initial handle.
EXPECT_EQ(capturing_tab.ReadCaptureHandle(), captured_tab.capture_handle);
// Sanity over the test itself - the new server has a different origin.
ASSERT_FALSE(url::Origin::Create(servers_[kOtherCapturedServer]->base_url())
.IsSameOriginWith(captured_tab.GetOrigin()));
// Cross-origin navigation clears the capture handle (config) and fires
// an event with the empty CaptureHandle.
captured_tab.Navigate(
servers_[kOtherCapturedServer]->GetURL(kCapturedPageOther),
/*expect_handle_reset=*/true);
EXPECT_EQ(capturing_tab.LastEvent(), "{}");
EXPECT_EQ(capturing_tab.ReadCaptureHandle(), kNoCaptureHandle);
}
IN_PROC_BROWSER_TEST_F(CaptureHandleBrowserTest,
SelfCaptureSanityWhenPermitted) {
TabInfo tab = SetUpCapturedPage(/*expose_origin=*/true, "handle", {"*"},
/*self_capture=*/true);
tab.StartCapturing();
// Correct initial value read.
EXPECT_EQ(tab.ReadCaptureHandle(), tab.capture_handle);
// Events correctly fired when self-capturing.
tab.SetCaptureHandleConfig(/*expose_origin=*/true, "new_handle", {"*"});
EXPECT_EQ(tab.LastEvent(), tab.capture_handle);
EXPECT_EQ(tab.ReadCaptureHandle(), tab.capture_handle);
}
IN_PROC_BROWSER_TEST_F(CaptureHandleBrowserTest,
SelfCaptureSanityWhenNotPermitted) {
TabInfo tab =
SetUpCapturedPage(/*expose_origin=*/true, "handle", {kArbitraryOrigin},
/*self_capture=*/true);
ASSERT_TRUE(tab.GetOrigin().IsSameOriginWith(tab.GetOrigin()));
tab.StartCapturing();
// Correct initial value read.
EXPECT_EQ(tab.ReadCaptureHandle(), kNoCaptureHandle);
// No events fired when self-capturing but not allowed to observe.
tab.SetCaptureHandleConfig(/*expose_origin=*/true, "new_handle",
{kArbitraryOrigin});
EXPECT_EQ(tab.ReadCaptureHandle(), kNoCaptureHandle);
}
// TODO(crbug/1219998): Disabled because of flakiness.
#if BUILDFLAG(IS_WIN)
#define MAYBE_RegularTabCannotReadIncognitoTabCaptureHandle \
DISABLED_RegularTabCannotReadIncognitoTabCaptureHandle
#else
#define MAYBE_RegularTabCannotReadIncognitoTabCaptureHandle \
RegularTabCannotReadIncognitoTabCaptureHandle
#endif
IN_PROC_BROWSER_TEST_F(CaptureHandleBrowserTest,
MAYBE_RegularTabCannotReadIncognitoTabCaptureHandle) {
TabInfo captured_tab =
SetUpCapturedPage(/*expose_origin=*/true, "handle", {"*"},
/*self_capture=*/false, BrowserType::kIncognito);
TabInfo capturing_tab =
SetUpCapturingPage(/*start_capturing=*/true, BrowserType::kRegular);
// Can neither observe the value when capture starts, nor receive events when
// the capture handle changes.
EXPECT_EQ(capturing_tab.ReadCaptureHandle(), kNoCaptureHandle);
captured_tab.SetCaptureHandleConfig(/*expose_origin=*/true, "new_handle",
{"*"});
EXPECT_EQ(capturing_tab.ReadCaptureHandle(), kNoCaptureHandle);
}
// TODO(crbug/1248619): Disabled because of flakiness.
IN_PROC_BROWSER_TEST_F(CaptureHandleBrowserTest,
DISABLED_IncognitoTabCannotReadRegularTabCaptureHandle) {
TabInfo captured_tab =
SetUpCapturedPage(/*expose_origin=*/true, "handle", {"*"},
/*self_capture=*/false, BrowserType::kRegular);
TabInfo capturing_tab =
SetUpCapturingPage(/*start_capturing=*/true, BrowserType::kIncognito);
// Can neither observe the value when capture starts, nor receive events when
// the capture handle changes.
EXPECT_EQ(capturing_tab.ReadCaptureHandle(), kNoCaptureHandle);
captured_tab.SetCaptureHandleConfig(/*expose_origin=*/true, "new_handle",
{"*"});
EXPECT_EQ(capturing_tab.ReadCaptureHandle(), kNoCaptureHandle);
}
IN_PROC_BROWSER_TEST_F(CaptureHandleBrowserTest,
IncognitoTabCannotReadIncognitoTabCaptureHandle) {
TabInfo captured_tab =
SetUpCapturedPage(/*expose_origin=*/true, "handle", {"*"},
/*self_capture=*/false, BrowserType::kIncognito);
TabInfo capturing_tab =
SetUpCapturingPage(/*start_capturing=*/true, BrowserType::kIncognito);
// Can neither observe the value when capture starts, nor receive events when
// the capture handle changes.
EXPECT_EQ(capturing_tab.ReadCaptureHandle(), kNoCaptureHandle);
captured_tab.SetCaptureHandleConfig(/*expose_origin=*/true, "new_handle",
{"*"});
EXPECT_EQ(capturing_tab.ReadCaptureHandle(), kNoCaptureHandle);
}
// TODO(crbug/1219998): Disabled because of flakiness.
#if BUILDFLAG(IS_WIN)
#define MAYBE_IncognitoTabCanReadIncognitoTabCaptureHandleIfSelfCapture \
DISABLED_IncognitoTabCanReadIncognitoTabCaptureHandleIfSelfCapture
#else
#define MAYBE_IncognitoTabCanReadIncognitoTabCaptureHandleIfSelfCapture \
IncognitoTabCanReadIncognitoTabCaptureHandleIfSelfCapture
#endif
IN_PROC_BROWSER_TEST_F(
CaptureHandleBrowserTest,
MAYBE_IncognitoTabCanReadIncognitoTabCaptureHandleIfSelfCapture) {
TabInfo tab =
SetUpCapturedPage(/*expose_origin=*/true, "handle", {"*"},
/*self_capture=*/true, BrowserType::kIncognito);
tab.StartCapturing();
// Can observe the value when capture starts.
EXPECT_EQ(tab.ReadCaptureHandle(), tab.capture_handle);
// Receives event of changes to the capture handle.
tab.SetCaptureHandleConfig(/*expose_origin=*/true, "new_handle", {"*"});
EXPECT_EQ(tab.LastEvent(), tab.capture_handle);
EXPECT_EQ(tab.ReadCaptureHandle(), tab.capture_handle);
}
class CaptureHandleBrowserTestPrerender : public CaptureHandleBrowserTest {
public:
CaptureHandleBrowserTestPrerender() {
prerender_helper_ = std::make_unique<content::test::PrerenderTestHelper>(
base::BindRepeating(
&CaptureHandleBrowserTestPrerender::GetCapturedWebContents,
base::Unretained(this)));
}
content::WebContents* GetCapturedWebContents() {
return captured_web_contents_;
}
protected:
std::unique_ptr<content::test::PrerenderTestHelper> prerender_helper_;
raw_ptr<WebContents> captured_web_contents_ = nullptr;
};
// Verifies that pre-rendered pages don't change the capture handle config.
IN_PROC_BROWSER_TEST_F(CaptureHandleBrowserTestPrerender,
EmptyConfigForCrossDocumentNavigations) {
TabInfo captured_tab =
SetUpCapturedPage(/*expose_origin=*/true, "handle", {"*"});
captured_web_contents_ = captured_tab.web_contents;
TabInfo capturing_tab = SetUpCapturingPage(/*start_capturing=*/true);
// The capture handle set by the captured tab is observable by the capturer.
EXPECT_EQ(capturing_tab.ReadCaptureHandle(), captured_tab.capture_handle);
// Pre-render a document in the captured tab.
const auto host_id = prerender_helper_->AddPrerender(
servers_[kCapturedServer]->GetURL(kCapturedPageOther));
content::RenderFrameHost* prerender_rfh =
prerender_helper_->GetPrerenderedMainFrameHost(host_id);
std::string script_result;
EXPECT_TRUE(content::ExecuteScriptAndExtractString(
prerender_rfh,
base::StringPrintf("callSetCaptureHandleConfig(%s, \"%s\", %s);", "true",
"prerender_handle",
StringifyPermittedOrigins({"*"}).c_str()),
&script_result));
EXPECT_EQ(script_result, "capture-handle-set");
EXPECT_EQ(capturing_tab.ReadCaptureHandle(), captured_tab.capture_handle);
}
#endif // !BUILDFLAG(IS_CHROMEOS_LACROS)