blob: 8b7f0b48d655601a14fbf08bfa9cc1c1c0d7d4ca [file] [log] [blame]
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <array>
#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/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "build/build_config.h"
#include "build/buildflag.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"
#include "media/capture/mojom/video_capture_types.mojom.h"
#include "testing/gmock/include/gmock/gmock-matchers.h"
#include "third_party/blink/public/common/features.h"
#include "ui/gl/gl_switches.h"
namespace {
using ::content::WebContents;
using ::media::mojom::SubCaptureTargetType;
using ::testing::Bool;
using ::testing::Combine;
using ::testing::TestParamInfo;
using ::testing::Values;
using ::testing::WithParamInterface;
// Comparisons with sub-capture targets (CropTargets/RestrictionTargets) are
// limited. We can only know that a one was returned by the test. However, we
// keep this matcher around in anticipation of targets being made either
// comparable or stringifiable. Then we can do more interesting comparisons,
// like ensuring uniqueness or checking that repeatedly calling
// CropTarget.fromElement() or RestrictionTarget.fromElement() on the same
// Element yields either the same target, or an equivalent one.
MATCHER_P(IsExpectedTarget, expected_target_index, "") {
static_assert(std::is_same<decltype(arg), const std::string&>::value, "");
return arg == expected_target_index;
}
const char kMainPageTitle[] = "Region/Element Capture Test - Page 1 (Main)";
const char kOtherPageTitle[] = "Region/Element Capture Test - Page 2 (Main)";
// Tracks SubCaptureTargetIdWebContentsHelper::kMaxIdsPerWebContents.
constexpr size_t kMaxIdsPerWebContents = 100;
enum {
kMainPageTopLevelDocument,
kMainPageEmbeddedDocument,
kOtherPageTopLevelDocument,
kOtherPageEmbeddedDocument,
kMailmanServer,
kServerCount // Must be last.
};
enum Tab {
kMainTab,
kOtherTab,
kTabCount // Must be last.
};
enum class Frame {
kNone,
kTopLevelDocument,
kEmbeddedFrame,
};
enum class Track { kOriginal, kClone, kSecond };
const char* ToString(Frame frame) {
switch (frame) {
case Frame::kNone:
return "none";
case Frame::kTopLevelDocument:
return "top-level";
case Frame::kEmbeddedFrame:
return "embedded";
}
NOTREACHED();
}
const char* ToString(Track track) {
switch (track) {
case Track::kOriginal:
return "original";
case Track::kClone:
return "clone";
case Track::kSecond:
return "second";
}
NOTREACHED();
}
const char* ToString(SubCaptureTargetType type) {
switch (type) {
case SubCaptureTargetType::kCropTarget:
return "crop-target";
case SubCaptureTargetType::kRestrictionTarget:
return "restriction-target";
}
NOTREACHED();
}
// Conveniently pack together all relevant information about a tab and
// conveniently expose test controls on it.
struct TabInfo {
void StartEmbeddingFrame(const GURL& url) {
EXPECT_EQ(content::EvalJs(web_contents->GetPrimaryMainFrame(),
base::StringPrintf("startEmbeddingFrame('%s');",
url.spec().c_str())),
"embedding-done");
}
void SetUpMailman(const GURL& url) {
EXPECT_EQ(content::EvalJs(web_contents->GetPrimaryMainFrame(),
base::StringPrintf("setUpMailman('%s');",
url.spec().c_str())),
"mailman-ready");
}
void StartCapture() {
// Bring the tab into focus. This avoids getDisplayMedia rejection.
browser->tab_strip_model()->ActivateTabAt(tab_strip_index);
EXPECT_EQ(
content::EvalJs(web_contents->GetPrimaryMainFrame(), "startCapture();"),
"top-level-capture-success");
}
void StartCaptureFromEmbeddedFrame() {
// Bring the tab into focus. This avoids getDisplayMedia rejection.
browser->tab_strip_model()->ActivateTabAt(tab_strip_index);
EXPECT_EQ(content::EvalJs(web_contents->GetPrimaryMainFrame(),
"startCaptureFromEmbeddedFrame();"),
"embedded-capture-success");
}
bool StartSecondCapture(Frame frame) {
// Bring the tab into focus. This avoids getDisplayMedia rejection.
browser->tab_strip_model()->ActivateTabAt(tab_strip_index);
return content::EvalJs(
web_contents->GetPrimaryMainFrame(),
base::StringPrintf("startSecondCapture('%s');", ToString(frame)))
.ExtractString() ==
base::StrCat({ToString(frame), "-second-capture-success"});
}
bool StopCapture(Frame frame, Track track) {
return content::EvalJs(web_contents->GetPrimaryMainFrame(),
base::StringPrintf("stopCapture('%s', '%s');",
ToString(frame), ToString(track)))
.ExtractString() ==
base::StrCat({ToString(frame), "-stop-success"});
}
std::string SubCaptureTargetFromElement(
SubCaptureTargetType type,
Frame frame,
const std::string& element_id = "div") {
CHECK_NE(frame, Frame::kNone);
const std::string js_instruction =
base::StringPrintf("subCaptureTargetFromElement('%s', '%s', '%s');",
ToString(type), ToString(frame), element_id.c_str());
return content::EvalJs(web_contents->GetPrimaryMainFrame(), js_instruction)
.ExtractString();
}
// Takes as input either the sub-capture target[*], or "undefined" if the test
// wants track.cropTo(undefined) or track.restrictTo(undefined) to be invoked.
//
// [*] Actually, because CropTargets/RestrictionTargets are not stringifiable,
// an index of the CropTarget/RestrictionTarget is used, and translated by JS
// code back into the token it had previously stored.
bool ApplySubCaptureTarget(const std::string& target,
SubCaptureTargetType sub_capture_type,
Frame frame,
Track track = Track::kOriginal) {
CHECK(frame == Frame::kTopLevelDocument || frame == Frame::kEmbeddedFrame);
std::string script_result =
content::EvalJs(web_contents->GetPrimaryMainFrame(),
base::StringPrintf(
"applySubCaptureByIndex('%s', '%s', '%s', '%s');",
target.c_str(), ToString(sub_capture_type),
ToString(frame), ToString(track)))
.ExtractString();
EXPECT_EQ(0u, script_result.rfind(ToString(frame), 0)) << script_result;
return script_result == base::StringPrintf("%s-%s-success", ToString(frame),
ToString(sub_capture_type));
}
bool CloneTrack() {
std::string script_result =
content::EvalJs(web_contents->GetPrimaryMainFrame(), "clone();")
.ExtractString();
DCHECK(script_result == "clone-track-success" ||
script_result == "clone-track-failure");
return script_result == "clone-track-success";
}
bool Deallocate(Track track) {
std::string script_result =
content::EvalJs(
web_contents->GetPrimaryMainFrame(),
base::StringPrintf("deallocate('%s');", ToString(track)))
.ExtractString();
DCHECK(script_result == "deallocate-failure" ||
script_result == "deallocate-success");
return script_result == "deallocate-success";
}
bool HideElement(const char* element_id) {
std::string script_result =
content::EvalJs(web_contents->GetPrimaryMainFrame(),
base::StringPrintf("hideElement('%s');", element_id))
.ExtractString();
DCHECK(script_result == "hide-element-failure" ||
script_result == "hide-element-success");
return script_result == "hide-element-success";
}
bool CreateNewElement(Frame frame, const char* tag, const std::string& id) {
DCHECK_NE(frame, Frame::kNone);
std::string script_result =
content::EvalJs(web_contents->GetPrimaryMainFrame(),
base::StrCat({"createNewElement(\"", ToString(frame),
"\", \"", tag, "\", \"", id, "\");"}))
.ExtractString();
if (frame == Frame::kEmbeddedFrame) {
EXPECT_EQ(0u, script_result.rfind("embedded-", 0)) << script_result;
return script_result == "embedded-new-element-success";
}
DCHECK(frame == Frame::kTopLevelDocument);
EXPECT_EQ(0u, script_result.rfind("top-level-", 0)) << script_result;
return script_result == "top-level-new-element-success";
}
bool CreateNewDivElement(Frame frame, const std::string& id) {
return CreateNewElement(frame, "div", id);
}
raw_ptr<Browser, AcrossTasksDanglingUntriaged> browser;
raw_ptr<WebContents, AcrossTasksDanglingUntriaged> web_contents;
int tab_strip_index;
};
} // namespace
// Essentially depends on InProcessBrowserTest, but WebRtcTestBase provides
// detection of JS errors.
class SubCaptureBrowserTestBase : public WebRtcTestBase {
public:
SubCaptureBrowserTestBase() = default;
~SubCaptureBrowserTestBase() override = default;
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);
// MSan and GL do not get along so avoid using the GPU with MSan.
// TODO(crbug.com/40260482): Remove this after fixing feature
// detection in 0c tab capture path as it'll no longer be needed.
#if !BUILDFLAG(IS_CHROMEOS) && !defined(MEMORY_SANITIZER)
command_line->AppendSwitch(switches::kUseGpuInTests);
#endif
command_line_ = command_line;
}
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, and also embeds
// a cross-origin iframe.
void SetUpPage(const std::string& top_level_document_document,
net::EmbeddedTestServer* top_level_document_server,
const std::string& embedded_iframe_document,
net::EmbeddedTestServer* embedded_iframe_server,
TabInfo* tab_info) const {
chrome::AddTabAt(browser(), GURL(url::kAboutBlankURL), -1, true);
EXPECT_TRUE(ui_test_utils::NavigateToURL(
browser(),
top_level_document_server->GetURL(top_level_document_document)));
WebContents* const web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
permissions::PermissionRequestManager::FromWebContents(web_contents)
->set_auto_response_for_test(
permissions::PermissionRequestManager::ACCEPT_ALL);
*tab_info = {browser(), web_contents,
browser()->tab_strip_model()->active_index()};
tab_info->StartEmbeddingFrame(
embedded_iframe_server->GetURL(embedded_iframe_document));
// Start embedding a frame whose sole purpose is to be same-origin
// across all other documents and therefore allow communication
// between them all over a shared BroadcastChannel.
tab_info->SetUpMailman(
servers_[kMailmanServer]->GetURL("/webrtc/sub_capture_mailman.html"));
}
// Set up all (necessary) tabs, loads iframes, and start capturing the
// relevant tab.
void SetUpTest(Frame capturing_entity, bool self_capture) {
// Other page (for other-tab-capture).
SetUpPage("/webrtc/sub_capture_other_main.html",
servers_[kOtherPageTopLevelDocument].get(),
"/webrtc/sub_capture_other_embedded.html",
servers_[kOtherPageEmbeddedDocument].get(), &tabs_[kOtherTab]);
// Main page (for self-capture). Instantiate it second to help it get focus.
SetUpPage("/webrtc/sub_capture_main.html",
servers_[kMainPageTopLevelDocument].get(),
"/webrtc/sub_capture_embedded.html",
servers_[kMainPageEmbeddedDocument].get(), &tabs_[kMainTab]);
DCHECK(command_line_);
command_line_->AppendSwitchASCII(
switches::kAutoSelectTabCaptureSourceByTitle,
self_capture ? kMainPageTitle : kOtherPageTitle);
if (capturing_entity == Frame::kTopLevelDocument) {
tabs_[kMainTab].StartCapture();
} else if (capturing_entity == Frame::kEmbeddedFrame) {
tabs_[kMainTab].StartCaptureFromEmbeddedFrame();
}
}
// Manipulation after SetUpCommandLine, but before capture starts,
// allows tests to set which tab to capture.
raw_ptr<base::CommandLine> command_line_ = nullptr;
// Holds the tabs manipulated by this test.
std::array<TabInfo, kTabCount> tabs_;
// Each page is served from a distinct origin, thereby proving that
// cropping/restricting works irrespective of whether iframes are
// in/out-of-process.
std::vector<std::unique_ptr<net::EmbeddedTestServer>> servers_;
base::test::ScopedFeatureList scoped_feature_list_;
};
// Test that cropTo() and restrictTo() work on some expected element types.
class SubCaptureBrowserTargetElementTest
: public SubCaptureBrowserTestBase,
public WithParamInterface<std::tuple<SubCaptureTargetType, const char*>> {
public:
SubCaptureBrowserTargetElementTest()
: sub_capture_type_(std::get<0>(GetParam())),
target_element_(std::get<1>(GetParam())) {}
~SubCaptureBrowserTargetElementTest() override = default;
void TestCanApplyToElementTag(const char* tag) {
TabInfo& tab = tabs_[kMainTab];
const std::string element_id = base::StrCat({"new_id_", tag});
ASSERT_TRUE(
tab.CreateNewElement(Frame::kTopLevelDocument, tag, element_id));
const std::string target = tab.SubCaptureTargetFromElement(
sub_capture_type_, Frame::kTopLevelDocument, element_id);
ASSERT_THAT(target, IsExpectedTarget("0"));
EXPECT_TRUE(tab.ApplySubCaptureTarget(target, sub_capture_type_,
Frame::kTopLevelDocument));
}
protected:
const SubCaptureTargetType sub_capture_type_;
const char* const target_element_;
};
std::string SubCaptureBrowserTargetElementTestParamsToString(
const TestParamInfo<SubCaptureBrowserTargetElementTest::ParamType>& info) {
const std::string api =
std::get<0>(info.param) == SubCaptureTargetType::kCropTarget
? "RegionCapture"
: "ElementCapture";
std::string element = std::get<1>(info.param);
element[0] = std::toupper(element[0]);
return api + element;
}
INSTANTIATE_TEST_SUITE_P(
,
SubCaptureBrowserTargetElementTest,
Combine(Values(SubCaptureTargetType::kCropTarget,
SubCaptureTargetType::kRestrictionTarget),
Values("a",
"blockquote",
"body",
"button",
"canvas",
"col",
"div",
"fieldset",
"form",
"h1",
"header",
"hr"
"iframe",
"img",
"input",
"output",
"span",
"svg",
"video")),
SubCaptureBrowserTargetElementTestParamsToString);
IN_PROC_BROWSER_TEST_P(SubCaptureBrowserTargetElementTest, ApplyToElement) {
SetUpTest(Frame::kTopLevelDocument, /*self_capture=*/true);
TestCanApplyToElementTag(target_element_);
}
class SubCaptureBrowserTest : public SubCaptureBrowserTestBase,
public WithParamInterface<SubCaptureTargetType> {
public:
SubCaptureBrowserTest() : type_(GetParam()) {}
~SubCaptureBrowserTest() override = default;
protected:
const SubCaptureTargetType type_;
};
std::string SubCaptureBrowserTestParamsToString(
const TestParamInfo<SubCaptureBrowserTest::ParamType>& info) {
return info.param == SubCaptureTargetType::kCropTarget ? "RegionCapture"
: "ElementCapture";
}
INSTANTIATE_TEST_SUITE_P(,
SubCaptureBrowserTest,
Values(SubCaptureTargetType::kCropTarget,
SubCaptureTargetType::kRestrictionTarget),
SubCaptureBrowserTestParamsToString);
IN_PROC_BROWSER_TEST_P(SubCaptureBrowserTest,
FromElementReturnsValidIdInMainPage) {
SetUpTest(Frame::kTopLevelDocument, /*self_capture=*/true);
EXPECT_THAT(tabs_[kMainTab].SubCaptureTargetFromElement(
type_, Frame::kTopLevelDocument),
IsExpectedTarget("0"));
}
IN_PROC_BROWSER_TEST_P(SubCaptureBrowserTest,
FromElementReturnsValidIdInCrossOriginIframe) {
SetUpTest(Frame::kTopLevelDocument, /*self_capture=*/true);
EXPECT_THAT(
tabs_[kMainTab].SubCaptureTargetFromElement(type_, Frame::kEmbeddedFrame),
IsExpectedTarget("0"));
}
IN_PROC_BROWSER_TEST_P(SubCaptureBrowserTest,
SubCaptureAllowedIfTopLevelAppliesToElementInTopLevel) {
SetUpTest(Frame::kTopLevelDocument, /*self_capture=*/true);
TabInfo& tab = tabs_[kMainTab];
const std::string target =
tab.SubCaptureTargetFromElement(type_, Frame::kTopLevelDocument);
ASSERT_THAT(target, IsExpectedTarget("0"));
EXPECT_TRUE(
tab.ApplySubCaptureTarget(target, type_, Frame::kTopLevelDocument));
}
IN_PROC_BROWSER_TEST_P(SubCaptureBrowserTest,
SubCaptureAllowedIfTopLevelAppliesToElementInEmbedded) {
SetUpTest(Frame::kTopLevelDocument, /*self_capture=*/true);
TabInfo& tab = tabs_[kMainTab];
const std::string target =
tab.SubCaptureTargetFromElement(type_, Frame::kEmbeddedFrame);
ASSERT_THAT(target, IsExpectedTarget("0"));
EXPECT_TRUE(
tab.ApplySubCaptureTarget(target, type_, Frame::kTopLevelDocument));
}
IN_PROC_BROWSER_TEST_P(
SubCaptureBrowserTest,
SubCaptureAllowedIfEmbeddedFrameAppliesToElementInTopLevel) {
SetUpTest(Frame::kEmbeddedFrame, /*self_capture=*/true);
TabInfo& tab = tabs_[kMainTab];
const std::string target =
tab.SubCaptureTargetFromElement(type_, Frame::kTopLevelDocument);
ASSERT_THAT(target, IsExpectedTarget("0"));
EXPECT_TRUE(tab.ApplySubCaptureTarget(target, type_, Frame::kEmbeddedFrame));
}
IN_PROC_BROWSER_TEST_P(
SubCaptureBrowserTest,
SubCaptureAllowedIfEmbeddedFrameAppliesToElementInEmbedded) {
SetUpTest(Frame::kEmbeddedFrame, /*self_capture=*/true);
TabInfo& tab = tabs_[kMainTab];
const std::string target =
tab.SubCaptureTargetFromElement(type_, Frame::kEmbeddedFrame);
ASSERT_THAT(target, IsExpectedTarget("0"));
EXPECT_TRUE(tab.ApplySubCaptureTarget(target, type_, Frame::kEmbeddedFrame));
}
IN_PROC_BROWSER_TEST_P(SubCaptureBrowserTest, SubCaptureAllowedToUndo) {
SetUpTest(Frame::kTopLevelDocument, /*self_capture=*/true);
TabInfo& tab = tabs_[kMainTab];
const std::string target =
tab.SubCaptureTargetFromElement(type_, Frame::kTopLevelDocument);
ASSERT_THAT(target, IsExpectedTarget("0"));
ASSERT_TRUE(
tab.ApplySubCaptureTarget(target, type_, Frame::kTopLevelDocument));
EXPECT_TRUE(
tab.ApplySubCaptureTarget("undefined", type_, Frame::kTopLevelDocument));
}
IN_PROC_BROWSER_TEST_P(
SubCaptureBrowserTest,
MayAttemptToUndoSubCaptureOnTracksWhereSubCaptureHasNotBeenApplied) {
SetUpTest(Frame::kTopLevelDocument, /*self_capture=*/true);
TabInfo& tab = tabs_[kMainTab];
const std::string target =
tab.SubCaptureTargetFromElement(type_, Frame::kTopLevelDocument);
ASSERT_THAT(target, IsExpectedTarget("0"));
// ApplySubCaptureTarget(target) is intentionally not called.
// Instead, the test immediately calls ApplySubCaptureTarget(undefined) on a
// still-uncropped-and-unrestricted track, attempting to undo sub-capture
// although it's not been applied.
EXPECT_TRUE(
tab.ApplySubCaptureTarget("undefined", type_, Frame::kTopLevelDocument));
}
// The Promise resolves when it's guaranteed that no additional frames will be
// issued with an earlier sub-capture-target version. That an actual frame be
// issued at all, let alone with the new sub-capture-target version, is not
// actually required, or else these promises could languish unfulfilled
// indefinitely.
IN_PROC_BROWSER_TEST_P(SubCaptureBrowserTest,
SubCaptureOfInvisibleElementResolvesInTimelyFashion) {
SetUpTest(Frame::kTopLevelDocument, /*self_capture=*/true);
TabInfo& tab = tabs_[kMainTab];
ASSERT_TRUE(tab.HideElement("div"));
const std::string target =
tab.SubCaptureTargetFromElement(type_, Frame::kTopLevelDocument, "div");
ASSERT_THAT(target, IsExpectedTarget("0"));
EXPECT_TRUE(
tab.ApplySubCaptureTarget(target, type_, Frame::kTopLevelDocument));
}
// TODO(crbug.com/431852186): Re-enable after flakes are resolved.
#if BUILDFLAG(IS_LINUX)
#define MAYBE_MaxIdsInTopLevelDocument DISABLED_MaxIdsInTopLevelDocument
#else
#define MAYBE_MaxIdsInTopLevelDocument MaxIdsInTopLevelDocument
#endif
IN_PROC_BROWSER_TEST_P(SubCaptureBrowserTest, MAYBE_MaxIdsInTopLevelDocument) {
SetUpTest(Frame::kNone, /*self_capture=*/false);
TabInfo& tab = tabs_[kMainTab];
for (size_t i = 0; i < kMaxIdsPerWebContents; ++i) {
const std::string element_id = ("new_id_" + base::NumberToString(i));
ASSERT_TRUE(tab.CreateNewDivElement(Frame::kTopLevelDocument, element_id));
const std::string target = tab.SubCaptureTargetFromElement(
type_, Frame::kTopLevelDocument, element_id);
ASSERT_THAT(target, IsExpectedTarget(base::NumberToString(i)));
}
// Create one more element - this one won't get a sub-capture-target.
const std::string element_id =
("new_id_" + base::NumberToString(kMaxIdsPerWebContents));
ASSERT_TRUE(tab.CreateNewDivElement(Frame::kTopLevelDocument, element_id));
EXPECT_EQ(tab.SubCaptureTargetFromElement(type_, Frame::kTopLevelDocument,
element_id),
base::StringPrintf("top-level-produce-%s-error", ToString(type_)));
}
// TODO(crbug.com/431852186): Re-enable after flakes are resolved.
#if BUILDFLAG(IS_LINUX)
#define MAYBE_MaxIdsInEmbeddedFrame DISABLED_MaxIdsInEmbeddedFrame
#else
#define MAYBE_MaxIdsInEmbeddedFrame MaxIdsInEmbeddedFrame
#endif
IN_PROC_BROWSER_TEST_P(SubCaptureBrowserTest, MAYBE_MaxIdsInEmbeddedFrame) {
SetUpTest(Frame::kNone, /*self_capture=*/false);
TabInfo& tab = tabs_[kMainTab];
for (size_t i = 0; i < kMaxIdsPerWebContents; ++i) {
const std::string element_id = ("new_id_" + base::NumberToString(i));
ASSERT_TRUE(tab.CreateNewDivElement(Frame::kEmbeddedFrame, element_id));
const std::string target = tab.SubCaptureTargetFromElement(
type_, Frame::kEmbeddedFrame, element_id);
ASSERT_THAT(target, IsExpectedTarget(base::NumberToString(i)));
}
// Create one more element - this one won't get a sub-capture-target.
const std::string element_id =
("new_id_" + base::NumberToString(kMaxIdsPerWebContents));
ASSERT_TRUE(tab.CreateNewDivElement(Frame::kEmbeddedFrame, element_id));
EXPECT_EQ(
tab.SubCaptureTargetFromElement(type_, Frame::kEmbeddedFrame, element_id),
base::StringPrintf("embedded-produce-%s-error", ToString(type_)));
}
// TODO(crbug.com/431852186): Re-enable after flakes are resolved.
#if BUILDFLAG(IS_LINUX)
#define MAYBE_MaxIdsSharedBetweenFramesInTab \
DISABLED_MaxIdsSharedBetweenFramesInTab
#else
#define MAYBE_MaxIdsSharedBetweenFramesInTab MaxIdsSharedBetweenFramesInTab
#endif
IN_PROC_BROWSER_TEST_P(SubCaptureBrowserTest,
MAYBE_MaxIdsSharedBetweenFramesInTab) {
SetUpTest(Frame::kNone, /*self_capture=*/false);
TabInfo& tab = tabs_[kMainTab];
static_assert(kMaxIdsPerWebContents > 1, "");
// Create (kMaxIdsPerWebContents - 1) new elements and assign each a
// sub-capture-target.
for (size_t i = 0; i < kMaxIdsPerWebContents - 1; ++i) {
const std::string element_id = ("new_id_" + base::NumberToString(i));
ASSERT_TRUE(tab.CreateNewDivElement(Frame::kTopLevelDocument, element_id));
const std::string target = tab.SubCaptureTargetFromElement(
type_, Frame::kTopLevelDocument, element_id);
ASSERT_THAT(target, IsExpectedTarget(base::NumberToString(i)));
}
// One more in the embedded frame is possible.
std::string element_id =
("new_id_" + base::NumberToString(kMaxIdsPerWebContents - 1));
ASSERT_TRUE(tab.CreateNewDivElement(Frame::kEmbeddedFrame, element_id));
const std::string target =
tab.SubCaptureTargetFromElement(type_, Frame::kEmbeddedFrame, element_id);
EXPECT_THAT(target, IsExpectedTarget(
base::NumberToString(kMaxIdsPerWebContents - 1)));
// Create one more element - this one won't get a sub-capture-target.
element_id = ("new_id_" + base::NumberToString(kMaxIdsPerWebContents));
ASSERT_TRUE(tab.CreateNewDivElement(Frame::kTopLevelDocument, element_id));
EXPECT_EQ(tab.SubCaptureTargetFromElement(type_, Frame::kTopLevelDocument,
element_id),
base::StringPrintf("top-level-produce-%s-error", ToString(type_)));
// Neither in the top-level nor in the embedded frame.
element_id = ("new_id_" + base::NumberToString(kMaxIdsPerWebContents + 1));
ASSERT_TRUE(tab.CreateNewDivElement(Frame::kEmbeddedFrame, element_id));
EXPECT_EQ(
tab.SubCaptureTargetFromElement(type_, Frame::kEmbeddedFrame, element_id),
base::StringPrintf("embedded-produce-%s-error", ToString(type_)));
}
// Tests related to behavior when cloning.
class SubCaptureClonesBrowserTestBase : public SubCaptureBrowserTestBase {
public:
static constexpr char kTarget0[] = "0";
static constexpr char kTarget1[] = "1";
~SubCaptureClonesBrowserTestBase() override = default;
void SetUpCommandLine(base::CommandLine* command_line) override {
command_line->AppendSwitchASCII("js-flags", "--expose-gc");
SubCaptureBrowserTestBase::SetUpCommandLine(command_line);
}
// Has to be called from within the test's body.
void ManualSetUp(SubCaptureTargetType type) {
SetUpTest(Frame::kTopLevelDocument, /*self_capture=*/true);
EXPECT_THAT(tabs_[kMainTab].SubCaptureTargetFromElement(
type, Frame::kTopLevelDocument, "div"),
IsExpectedTarget(kTarget0));
EXPECT_THAT(tabs_[kMainTab].SubCaptureTargetFromElement(
type, Frame::kTopLevelDocument, "embedded_frame"),
IsExpectedTarget(kTarget1));
}
bool ApplySubCaptureTarget(SubCaptureTargetType type,
const std::string& target,
Frame frame,
Track track) {
return tabs_[kMainTab].ApplySubCaptureTarget(target, type, frame, track);
}
bool CloneTrack() { return tabs_[kMainTab].CloneTrack(); }
bool Deallocate(Track track) { return tabs_[kMainTab].Deallocate(track); }
};
class SubCaptureClonesBrowserTest
: public SubCaptureClonesBrowserTestBase,
public WithParamInterface<SubCaptureTargetType> {
public:
SubCaptureClonesBrowserTest() : type_(GetParam()) {}
~SubCaptureClonesBrowserTest() override = default;
protected:
const SubCaptureTargetType type_;
};
std::string SubCaptureClonesBrowserTestParamsToString(
const TestParamInfo<SubCaptureClonesBrowserTest::ParamType>& info) {
return info.param == SubCaptureTargetType::kCropTarget ? "RegionCapture"
: "ElementCapture";
}
INSTANTIATE_TEST_SUITE_P(,
SubCaptureClonesBrowserTest,
Values(SubCaptureTargetType::kCropTarget,
SubCaptureTargetType::kRestrictionTarget),
SubCaptureClonesBrowserTestParamsToString);
// Sanity cloning 1/2.
IN_PROC_BROWSER_TEST_P(SubCaptureClonesBrowserTest,
CanCloneTracksWhichHadNoSubCaptureApplied) {
ManualSetUp(type_);
EXPECT_TRUE(CloneTrack());
}
// Sanity cloning 2/2.
IN_PROC_BROWSER_TEST_P(SubCaptureClonesBrowserTest,
CanCloneTrackWhichHaveSubCaptureApplied) {
ManualSetUp(type_);
ASSERT_TRUE(ApplySubCaptureTarget(type_, kTarget0, Frame::kTopLevelDocument,
Track::kOriginal));
EXPECT_TRUE(CloneTrack());
}
// Restrictions on cloned tracks 1/3.
IN_PROC_BROWSER_TEST_P(SubCaptureClonesBrowserTest,
CannotApplySubCaptureOnClones) {
ManualSetUp(type_);
ASSERT_TRUE(CloneTrack());
EXPECT_FALSE(ApplySubCaptureTarget(type_, kTarget0, Frame::kTopLevelDocument,
Track::kClone));
}
// Restrictions on cloned tracks 2/3.
IN_PROC_BROWSER_TEST_P(SubCaptureClonesBrowserTest,
CannotReapplySubCaptureOnClones) {
ManualSetUp(type_);
ASSERT_TRUE(ApplySubCaptureTarget(type_, kTarget0, Frame::kTopLevelDocument,
Track::kOriginal));
ASSERT_TRUE(CloneTrack());
EXPECT_FALSE(ApplySubCaptureTarget(type_, kTarget1, Frame::kTopLevelDocument,
Track::kClone));
}
// Restrictions on cloned tracks 3/3.
IN_PROC_BROWSER_TEST_P(SubCaptureClonesBrowserTest,
CannotUndoSubCaptureOnClones) {
ManualSetUp(type_);
ASSERT_TRUE(ApplySubCaptureTarget(type_, kTarget0, Frame::kTopLevelDocument,
Track::kOriginal));
ASSERT_TRUE(CloneTrack());
EXPECT_FALSE(ApplySubCaptureTarget(type_, "undefined",
Frame::kTopLevelDocument, Track::kClone));
}
// Restrictions on original track that has a clone 1/3.
IN_PROC_BROWSER_TEST_P(SubCaptureClonesBrowserTest,
CannotApplySubCaptureOnTracksThatHaveClones) {
ManualSetUp(type_);
ASSERT_TRUE(CloneTrack());
EXPECT_FALSE(ApplySubCaptureTarget(type_, kTarget0, Frame::kTopLevelDocument,
Track::kOriginal));
}
// Restrictions on original track that has a clone 2/3.
IN_PROC_BROWSER_TEST_P(SubCaptureClonesBrowserTest,
CannotReapplySubCaptureOnTracksThatHaveClones) {
ManualSetUp(type_);
ASSERT_TRUE(ApplySubCaptureTarget(type_, kTarget0, Frame::kTopLevelDocument,
Track::kOriginal));
ASSERT_TRUE(CloneTrack());
EXPECT_FALSE(ApplySubCaptureTarget(type_, kTarget1, Frame::kTopLevelDocument,
Track::kOriginal));
}
// Restrictions on original track that has a clone 3/3.
IN_PROC_BROWSER_TEST_P(SubCaptureClonesBrowserTest,
CannotUndoSubCaptureOnTracksThatHaveClones) {
ManualSetUp(type_);
ASSERT_TRUE(ApplySubCaptureTarget(type_, kTarget0, Frame::kTopLevelDocument,
Track::kOriginal));
ASSERT_TRUE(CloneTrack());
EXPECT_FALSE(ApplySubCaptureTarget(
type_, "undefined", Frame::kTopLevelDocument, Track::kOriginal));
}
// Original track becomes unblocked for sub-capture after clone is GCed 1/3.
//
// TODO(crbug.com/40858400) Re-enable for macOS and ChromeOS after flakes are
// resolved.
// TODO(crbug.com/40919381): Also flakes on linux-bfcache-rel, so turning the
// test off entirely.
IN_PROC_BROWSER_TEST_P(
SubCaptureClonesBrowserTest,
DISABLED_CanApplySubCaptureOnOriginalTrackAfterCloneIsGarbageCollected) {
ManualSetUp(type_);
ASSERT_TRUE(CloneTrack());
ASSERT_FALSE(ApplySubCaptureTarget(type_, kTarget0, Frame::kTopLevelDocument,
Track::kOriginal));
ASSERT_TRUE(Deallocate(Track::kClone));
EXPECT_TRUE(ApplySubCaptureTarget(type_, kTarget0, Frame::kTopLevelDocument,
Track::kOriginal));
}
// Original track becomes unblocked for sub-capture after clone is GCed 2/3.
//
// TODO(crbug.com/40858400) Re-enable after flakes are resolved.
IN_PROC_BROWSER_TEST_P(
SubCaptureClonesBrowserTest,
DISABLED_CanReapplySubCaptureOnOriginalTrackAfterCloneIsGarbageCollected) {
ManualSetUp(type_);
ASSERT_TRUE(ApplySubCaptureTarget(type_, kTarget0, Frame::kTopLevelDocument,
Track::kOriginal));
ASSERT_TRUE(CloneTrack());
ASSERT_FALSE(ApplySubCaptureTarget(type_, kTarget1, Frame::kTopLevelDocument,
Track::kOriginal));
ASSERT_TRUE(Deallocate(Track::kClone));
EXPECT_TRUE(ApplySubCaptureTarget(type_, kTarget1, Frame::kTopLevelDocument,
Track::kOriginal));
}
// Original track becomes unblocked for sub-capture clone is GCed 3/3.
//
// TODO(crbug.com/40860614): Re-enable this test.
IN_PROC_BROWSER_TEST_P(
SubCaptureClonesBrowserTest,
DISABLED_CanUndoSubCaptureOnOriginalTrackAfterCloneIsGarbageCollected) {
ManualSetUp(type_);
ASSERT_TRUE(ApplySubCaptureTarget(type_, kTarget0, Frame::kTopLevelDocument,
Track::kOriginal));
ASSERT_TRUE(CloneTrack());
ASSERT_FALSE(ApplySubCaptureTarget(
type_, "undefined", Frame::kTopLevelDocument, Track::kOriginal));
ASSERT_TRUE(Deallocate(Track::kClone));
EXPECT_TRUE(ApplySubCaptureTarget(
type_, "undefined", Frame::kTopLevelDocument, Track::kOriginal));
}
// Cloned track becomes unblocked for sub-capture after original is GCed 1/3.
//
// The following test is disabled because of a loosely-related issue,
// where an original track is kept alive if a clone exists, but not vice versa.
// TODO(crbug.com/40845775): Uncomment after fixing the aforementioned issue.
IN_PROC_BROWSER_TEST_P(
SubCaptureClonesBrowserTest,
DISABLED_CanApplySubCaptureOnCloneAfterOriginalTrackIsGarbageCollected) {
ManualSetUp(type_);
ASSERT_TRUE(CloneTrack());
ASSERT_FALSE(ApplySubCaptureTarget(type_, kTarget0, Frame::kTopLevelDocument,
Track::kClone));
ASSERT_TRUE(Deallocate(Track::kOriginal));
EXPECT_TRUE(ApplySubCaptureTarget(type_, kTarget0, Frame::kTopLevelDocument,
Track::kClone));
}
// Cloned track becomes unblocked for sub-capture after original is GCed 2/3.
//
// The following test is disabled because of a loosely-related issue,
// where an original track is kept alive if a clone exists, but not vice versa.
// TODO(crbug.com/40845775): Uncomment after fixing the aforementioned issue.
IN_PROC_BROWSER_TEST_P(
SubCaptureClonesBrowserTest,
DISABLED_CanReapplySubCaptureOnCloneAfterOriginalTrackIsGarbageCollected) {
ManualSetUp(type_);
ASSERT_TRUE(ApplySubCaptureTarget(type_, kTarget0, Frame::kTopLevelDocument,
Track::kOriginal));
ASSERT_TRUE(CloneTrack());
ASSERT_FALSE(ApplySubCaptureTarget(type_, kTarget1, Frame::kTopLevelDocument,
Track::kClone));
ASSERT_TRUE(Deallocate(Track::kOriginal));
EXPECT_TRUE(ApplySubCaptureTarget(type_, kTarget1, Frame::kTopLevelDocument,
Track::kClone));
}
// Cloned track becomes unblocked for sub-capture after original is GCed 3/3.
//
// The following test is disabled because of a loosely-related issue,
// where an original track is kept alive if a clone exists, but not vice versa.
// TODO(crbug.com/40845775): Uncomment after fixing the aforementioned issue.
IN_PROC_BROWSER_TEST_P(
SubCaptureClonesBrowserTest,
DISABLED_CanUndoSubCaptureOnCloneAfterOriginalTrackIsGarbageCollected) {
ManualSetUp(type_);
ASSERT_TRUE(ApplySubCaptureTarget(type_, kTarget0, Frame::kTopLevelDocument,
Track::kOriginal));
ASSERT_TRUE(CloneTrack());
ASSERT_FALSE(ApplySubCaptureTarget(type_, "undefined",
Frame::kTopLevelDocument, Track::kClone));
ASSERT_TRUE(Deallocate(Track::kOriginal));
EXPECT_TRUE(ApplySubCaptureTarget(type_, "undefined",
Frame::kTopLevelDocument, Track::kClone));
}
// Tests similar issues to cloning, but with multiple captures triggering
// these issues instead.
class SubCaptureMultiCaptureBrowserTest
: public SubCaptureClonesBrowserTestBase,
public WithParamInterface<std::tuple<SubCaptureTargetType, Frame>> {
public:
SubCaptureMultiCaptureBrowserTest()
: type_(std::get<0>(GetParam())),
second_capture_frame_(std::get<1>(GetParam())) {}
~SubCaptureMultiCaptureBrowserTest() override = default;
// Has to be called from within the test's body.
void ManualSetUp() {
SetUpTest(Frame::kTopLevelDocument, /*self_capture=*/true);
EXPECT_THAT(tabs_[kMainTab].SubCaptureTargetFromElement(
type_, Frame::kTopLevelDocument, "div"),
IsExpectedTarget(kTarget0));
EXPECT_THAT(tabs_[kMainTab].SubCaptureTargetFromElement(
type_, Frame::kTopLevelDocument, "embedded_frame"),
IsExpectedTarget(kTarget1));
}
bool StartSecondCapture() {
return tabs_[kMainTab].StartSecondCapture(second_capture_frame_);
}
bool StopCapture(Track track) {
return tabs_[kMainTab].StopCapture(Frame::kTopLevelDocument, track);
}
protected:
const SubCaptureTargetType type_;
const Frame second_capture_frame_;
};
std::string SubCaptureMultiCaptureBrowserTestParamsToString(
const TestParamInfo<SubCaptureMultiCaptureBrowserTest::ParamType>& info) {
const std::string api =
std::get<0>(info.param) == SubCaptureTargetType::kCropTarget
? "RegionCapture"
: "ElementCapture";
const std::string frame = std::get<1>(info.param) == Frame::kTopLevelDocument
? "TopLevelFrame"
: "EmbeddedFrame";
return base::StrCat({api, "AndSecondCaptureFrameIs", frame});
}
INSTANTIATE_TEST_SUITE_P(
,
SubCaptureMultiCaptureBrowserTest,
Combine(Values(SubCaptureTargetType::kCropTarget,
SubCaptureTargetType::kRestrictionTarget),
Values(Frame::kTopLevelDocument, Frame::kEmbeddedFrame)),
SubCaptureMultiCaptureBrowserTestParamsToString);
IN_PROC_BROWSER_TEST_P(SubCaptureMultiCaptureBrowserTest,
CanSelfCaptureAgainIfNeverAppliedSubCapture) {
ManualSetUp();
EXPECT_TRUE(StartSecondCapture());
}
IN_PROC_BROWSER_TEST_P(SubCaptureMultiCaptureBrowserTest,
CannotSelfCaptureAgainIfAppliedSubCapture) {
ManualSetUp();
ASSERT_TRUE(ApplySubCaptureTarget(type_, kTarget1, Frame::kTopLevelDocument,
Track::kOriginal));
EXPECT_FALSE(StartSecondCapture());
}
IN_PROC_BROWSER_TEST_P(SubCaptureMultiCaptureBrowserTest,
CannotSelfCaptureAgainIfSubCapturedAppliedAndUnapplied) {
ManualSetUp();
ASSERT_TRUE(ApplySubCaptureTarget(type_, kTarget1, Frame::kTopLevelDocument,
Track::kOriginal));
ASSERT_TRUE(ApplySubCaptureTarget(
type_, "undefined", Frame::kTopLevelDocument, Track::kOriginal));
EXPECT_FALSE(StartSecondCapture());
}
IN_PROC_BROWSER_TEST_P(SubCaptureMultiCaptureBrowserTest,
CanSelfCaptureAgainIfSubCaptureSessionStopped) {
ManualSetUp();
ASSERT_TRUE(ApplySubCaptureTarget(type_, kTarget1, Frame::kTopLevelDocument,
Track::kOriginal));
ASSERT_TRUE(StopCapture(Track::kOriginal));
EXPECT_TRUE(StartSecondCapture());
}
IN_PROC_BROWSER_TEST_P(
SubCaptureMultiCaptureBrowserTest,
CannotApplySubCaptureIfMultipleSelfCaptureSessionsExist) {
ManualSetUp();
ASSERT_TRUE(StartSecondCapture());
EXPECT_FALSE(ApplySubCaptureTarget(type_, kTarget1, Frame::kTopLevelDocument,
Track::kOriginal));
EXPECT_FALSE(ApplySubCaptureTarget(type_, kTarget1, second_capture_frame_,
Track::kSecond));
}
// Tests that sub-capture is:
// - Always allowed for self-capture (given evergreen conditions still apply,
// such as that the target belongs to the captured tab).
// - Allowed for Region Capture if the relevant feature flag is enabled.
class SubCaptureSelfAndCrossCaptureBrowserTest
: public SubCaptureBrowserTestBase,
public WithParamInterface<
std::tuple<SubCaptureTargetType, Frame, bool, bool, Tab, Frame>> {
public:
SubCaptureSelfAndCrossCaptureBrowserTest()
: type_(std::get<0>(GetParam())),
capturing_entity_(std::get<1>(GetParam())),
self_capture_(std::get<2>(GetParam())),
cross_tab_region_capture_allowed_(std::get<3>(GetParam())),
target_element_tab_(std::get<4>(GetParam())),
target_frame_(std::get<5>(GetParam())) {
scoped_feature_list_.InitWithFeatureStates(
{{features::kRegionCaptureOfOtherTabs,
cross_tab_region_capture_allowed_}});
}
~SubCaptureSelfAndCrossCaptureBrowserTest() override = default;
bool CaptureTypeAllowed() const {
const base::Feature* feature = nullptr;
switch (type_) {
case SubCaptureTargetType::kCropTarget:
feature = &features::kRegionCaptureOfOtherTabs;
break;
case SubCaptureTargetType::kRestrictionTarget:
feature = &features::kElementCaptureOfOtherTabs;
break;
}
CHECK(feature);
return self_capture_ || base::FeatureList::IsEnabled(*feature);
}
bool TargetBelongsToCapturedTab() const {
return target_element_tab_ ==
(self_capture_ ? Tab::kMainTab : Tab::kOtherTab);
}
protected:
// Whether Region Capture or Element Capture is tested.
const SubCaptureTargetType type_;
// The capture is done from kMainTab in all instances of this parameterized
// test. |capturing_entity_| controls whether the capture is initiated
// from the top-level document of said tab, or an embedded frame.
const Frame capturing_entity_;
// Whether capturing self, or capturing the other tab.
const bool self_capture_;
// Associated with `kRegionCaptureOfOtherTabs`, which controls whether
// sub-capture is only permitted to self-capture, or generally.
const bool cross_tab_region_capture_allowed_;
// Determines the the element on whose sub-capture-target we'll call cropTo()
// or restrictTo():
// * |target_element_tab_| - whether it's in kMainTab or in kOtherTab.
// * |target_frame_| - whether it's in the top-level or an embedded frame.
const Tab target_element_tab_;
const Frame target_frame_; // Top-level or embedded frame.
base::test::ScopedFeatureList scoped_feature_list_;
};
std::string SubCaptureSelfAndCrossCaptureBrowserTestParamsToString(
const TestParamInfo<SubCaptureSelfAndCrossCaptureBrowserTest::ParamType>&
info) {
return base::StrCat(
{std::get<0>(info.param) == SubCaptureTargetType::kCropTarget
? "RegionCapture"
: "ElementCapture",
std::get<1>(info.param) == Frame::kTopLevelDocument ? "TopLevel"
: "EmbeddedFrame",
std::get<2>(info.param) ? "SelfCapturing" : "CapturingOtherTab",
std::get<3>(info.param) ? "CrossTabRegionCaptureAllowed"
: "CrossTabRegionCaptureDisallowed"
"AndApplyingToElementIn",
std::get<4>(info.param) == kMainTab ? "OwnTabs" : "OtherTabs",
std::get<5>(info.param) == Frame::kTopLevelDocument ? "TopLevel"
: "EmbeddedFrame"});
}
INSTANTIATE_TEST_SUITE_P(
,
SubCaptureSelfAndCrossCaptureBrowserTest,
Combine(Values(SubCaptureTargetType::kCropTarget,
SubCaptureTargetType::kRestrictionTarget),
Values(Frame::kTopLevelDocument, Frame::kEmbeddedFrame),
Bool(),
Bool(),
Values(kMainTab, kOtherTab),
Values(Frame::kTopLevelDocument, Frame::kEmbeddedFrame)),
SubCaptureSelfAndCrossCaptureBrowserTestParamsToString);
IN_PROC_BROWSER_TEST_P(SubCaptureSelfAndCrossCaptureBrowserTest,
ApplySubCapture) {
SetUpTest(capturing_entity_, self_capture_);
// Prevent test false-positive - ensure that both tabs participating in the
// test have at least one associated sub-capture-target, or otherwise they
// would not have a SubCaptureTargetIdWebContentsHelper.
// To make things even clearer, ensure both the top-level and the embedded
// frame have produced sub-capture-targets. (This should not be necessary,
// but is done as an extra buffer against false-positives.)
ASSERT_THAT(tabs_[kMainTab].SubCaptureTargetFromElement(
type_, Frame::kTopLevelDocument),
IsExpectedTarget("0"));
ASSERT_THAT(
tabs_[kMainTab].SubCaptureTargetFromElement(type_, Frame::kEmbeddedFrame),
IsExpectedTarget("1"));
ASSERT_THAT(tabs_[kOtherTab].SubCaptureTargetFromElement(
type_, Frame::kTopLevelDocument),
IsExpectedTarget("2"));
ASSERT_THAT(tabs_[kOtherTab].SubCaptureTargetFromElement(
type_, Frame::kEmbeddedFrame),
IsExpectedTarget("3"));
const std::string target =
tabs_[target_element_tab_].SubCaptureTargetFromElement(type_,
target_frame_);
ASSERT_THAT(target, IsExpectedTarget("4"));
const bool expect_permitted =
CaptureTypeAllowed() && TargetBelongsToCapturedTab();
EXPECT_EQ(expect_permitted, tabs_[kMainTab].ApplySubCaptureTarget(
target, type_, capturing_entity_));
}
class SubCaptureUnapplyBrowserTest
: public SubCaptureBrowserTestBase,
public WithParamInterface<std::tuple<bool, bool, SubCaptureTargetType>> {
public:
SubCaptureUnapplyBrowserTest()
: self_capture_(std::get<0>(GetParam())),
cross_tab_sub_capture_allowed_(std::get<1>(GetParam())),
type_(std::get<2>(GetParam())) {
scoped_feature_list_.InitWithFeatureStates(
{{features::kRegionCaptureOfOtherTabs, cross_tab_sub_capture_allowed_},
{features::kElementCaptureOfOtherTabs,
cross_tab_sub_capture_allowed_}});
}
~SubCaptureUnapplyBrowserTest() override = default;
protected:
const bool self_capture_;
const bool cross_tab_sub_capture_allowed_;
const SubCaptureTargetType type_;
};
std::string SubCaptureUnapplyBrowserTestParamsToString(
const TestParamInfo<SubCaptureUnapplyBrowserTest::ParamType>& info) {
const bool self_capture = std::get<0>(info.param);
const bool cross_tab_sub_capture_allowed = std::get<1>(info.param);
const SubCaptureTargetType type = std::get<2>(info.param);
return base::StrCat(
{"Capturing", self_capture ? "Self" : "Other", "CrossTabSubCapture",
cross_tab_sub_capture_allowed ? "Allowed" : "Disallowed",
type == SubCaptureTargetType::kCropTarget ? "Region" : "Element",
"Capture"});
}
INSTANTIATE_TEST_SUITE_P(
,
SubCaptureUnapplyBrowserTest,
Combine(Bool(),
Bool(),
Values(SubCaptureTargetType::kCropTarget,
SubCaptureTargetType::kRestrictionTarget)),
SubCaptureUnapplyBrowserTestParamsToString);
IN_PROC_BROWSER_TEST_P(SubCaptureUnapplyBrowserTest, ApplyToElement) {
const Frame relevant_frame = Frame::kTopLevelDocument;
SetUpTest(relevant_frame, self_capture_);
TabInfo& tab = tabs_[kMainTab];
EXPECT_TRUE(tab.ApplySubCaptureTarget("undefined", type_, relevant_frame));
}