blob: ffd167bf2e23c3cf33f7f8e52c8f1aff678af512 [file] [log] [blame]
// Copyright (c) 2012 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 "base/command_line.h"
#include "base/location.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/stringprintf.h"
#include "base/test/test_timeouts.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "chrome/browser/extensions/api/tab_capture/tab_capture_api.h"
#include "chrome/browser/extensions/extension_apitest.h"
#include "chrome/browser/extensions/tab_helper.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
#include "chrome/browser/ui/tabs/tab_utils.h"
#include "chrome/common/chrome_switches.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/test_utils.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/common/switches.h"
#include "extensions/test/extension_test_message_listener.h"
#include "extensions/test/result_catcher.h"
#if defined(OS_WIN)
#include "base/win/windows_version.h"
#endif
namespace extensions {
namespace {
const char kExtensionId[] = "ddchlicdkolnonkihahngkmmmjnjlkkf";
class TabCaptureApiTest : public ExtensionApiTest {
public:
void SetUpCommandLine(base::CommandLine* command_line) override {
ExtensionApiTest::SetUpCommandLine(command_line);
// Specify smallish window size to make testing of tab capture less CPU
// intensive.
command_line->AppendSwitchASCII(::switches::kWindowSize, "300,300");
}
void AddExtensionToCommandLineWhitelist() {
base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
switches::kWhitelistedExtensionID, kExtensionId);
}
protected:
void SimulateMouseClickInCurrentTab() {
content::SimulateMouseClick(
browser()->tab_strip_model()->GetActiveWebContents(),
0,
blink::WebMouseEvent::Button::Left);
}
};
class TabCaptureApiPixelTest : public TabCaptureApiTest {
public:
void SetUp() override {
if (!IsTooIntensiveForThisPlatform())
EnablePixelOutput();
TabCaptureApiTest::SetUp();
}
protected:
bool IsTooIntensiveForThisPlatform() const {
#if defined(OS_WIN)
if (base::win::GetVersion() < base::win::VERSION_VISTA)
return true;
#endif
// The tests are too slow to succeed with OSMesa on the bots.
if (UsingOSMesa())
return true;
#if defined(NDEBUG)
return false;
#else
// TODO(miu): Look into enabling these tests for the Debug build bots once
// they prove to be stable again on the Release bots.
// http://crbug.com/396413
return !base::CommandLine::ForCurrentProcess()->HasSwitch(
"run-tab-capture-api-pixel-tests");
#endif
}
};
// Tests the logic that examines the constraints to determine the starting
// off-screen tab size.
TEST(TabCaptureCaptureOffscreenTabTest, DetermineInitialSize) {
using extensions::api::tab_capture::CaptureOptions;
using extensions::api::tab_capture::MediaStreamConstraint;
// Empty options --> 1280x720
CaptureOptions options;
EXPECT_EQ(gfx::Size(1280, 720),
TabCaptureCaptureOffscreenTabFunction::DetermineInitialSize(
options));
// Use specified mandatory maximum size.
options.video_constraints.reset(new MediaStreamConstraint());
base::DictionaryValue* properties =
&options.video_constraints->mandatory.additional_properties;
properties->SetInteger("maxWidth", 123);
properties->SetInteger("maxHeight", 456);
EXPECT_EQ(gfx::Size(123, 456),
TabCaptureCaptureOffscreenTabFunction::DetermineInitialSize(
options));
// Use default size if larger than mandatory minimum size. Else, use
// mandatory minimum size.
options.video_constraints.reset(new MediaStreamConstraint());
properties = &options.video_constraints->mandatory.additional_properties;
properties->SetInteger("minWidth", 123);
properties->SetInteger("minHeight", 456);
EXPECT_EQ(gfx::Size(1280, 720),
TabCaptureCaptureOffscreenTabFunction::DetermineInitialSize(
options));
properties->SetInteger("minWidth", 2560);
properties->SetInteger("minHeight", 1440);
EXPECT_EQ(gfx::Size(2560, 1440),
TabCaptureCaptureOffscreenTabFunction::DetermineInitialSize(
options));
// Use specified optional maximum size, if no mandatory size was specified.
options.video_constraints.reset(new MediaStreamConstraint());
options.video_constraints->optional.reset(
new MediaStreamConstraint::Optional());
properties = &options.video_constraints->optional->additional_properties;
properties->SetInteger("maxWidth", 456);
properties->SetInteger("maxHeight", 123);
EXPECT_EQ(gfx::Size(456, 123),
TabCaptureCaptureOffscreenTabFunction::DetermineInitialSize(
options));
// ...unless a mandatory minimum size was specified:
options.video_constraints->mandatory.additional_properties.SetInteger(
"minWidth", 500);
options.video_constraints->mandatory.additional_properties.SetInteger(
"minHeight", 600);
EXPECT_EQ(gfx::Size(500, 600),
TabCaptureCaptureOffscreenTabFunction::DetermineInitialSize(
options));
// Use default size if larger than optional minimum size. Else, use optional
// minimum size.
options.video_constraints.reset(new MediaStreamConstraint());
options.video_constraints->optional.reset(
new MediaStreamConstraint::Optional());
properties = &options.video_constraints->optional->additional_properties;
properties->SetInteger("minWidth", 9999);
properties->SetInteger("minHeight", 8888);
EXPECT_EQ(gfx::Size(9999, 8888),
TabCaptureCaptureOffscreenTabFunction::DetermineInitialSize(
options));
}
// Tests API behaviors, including info queries, and constraints violations.
IN_PROC_BROWSER_TEST_F(TabCaptureApiTest, ApiTests) {
AddExtensionToCommandLineWhitelist();
ASSERT_TRUE(RunExtensionSubtest("tab_capture", "api_tests.html")) << message_;
}
// Tests that there is a maximum limitation to the number of simultaneous
// off-screen tabs.
IN_PROC_BROWSER_TEST_F(TabCaptureApiTest, MaxOffscreenTabs) {
AddExtensionToCommandLineWhitelist();
ASSERT_TRUE(RunExtensionSubtest("tab_capture", "max_offscreen_tabs.html"))
<< message_;
}
// Tests that tab capture video frames can be received in a VIDEO element.
IN_PROC_BROWSER_TEST_F(TabCaptureApiPixelTest, EndToEndWithoutRemoting) {
if (IsTooIntensiveForThisPlatform()) {
LOG(WARNING) << "Skipping this CPU-intensive test on this platform/build.";
return;
}
AddExtensionToCommandLineWhitelist();
ASSERT_TRUE(RunExtensionSubtest(
"tab_capture", "end_to_end.html?method=local&colorDeviation=10"))
<< message_;
}
// Tests that video frames are captured, transported via WebRTC, and finally
// received in a VIDEO element. More allowance is provided for color deviation
// because of the additional layers of video processing performed within
// WebRTC.
IN_PROC_BROWSER_TEST_F(TabCaptureApiPixelTest, EndToEndThroughWebRTC) {
if (IsTooIntensiveForThisPlatform()) {
LOG(WARNING) << "Skipping this CPU-intensive test on this platform/build.";
return;
}
AddExtensionToCommandLineWhitelist();
ASSERT_TRUE(RunExtensionSubtest(
"tab_capture", "end_to_end.html?method=webrtc&colorDeviation=50"))
<< message_;
}
// Tests that tab capture video frames can be received in a VIDEO element from
// an off-screen tab.
IN_PROC_BROWSER_TEST_F(TabCaptureApiPixelTest, OffscreenTabEndToEnd) {
if (IsTooIntensiveForThisPlatform()) {
LOG(WARNING) << "Skipping this CPU-intensive test on this platform/build.";
return;
}
AddExtensionToCommandLineWhitelist();
ASSERT_TRUE(RunExtensionSubtest("tab_capture", "offscreen_end_to_end.html"))
<< message_;
}
// Tests that off-screen tabs can't do evil things (e.g., access local files).
IN_PROC_BROWSER_TEST_F(TabCaptureApiPixelTest, OffscreenTabEvilTests) {
if (IsTooIntensiveForThisPlatform()) {
LOG(WARNING) << "Skipping this CPU-intensive test on this platform/build.";
return;
}
AddExtensionToCommandLineWhitelist();
ASSERT_TRUE(RunExtensionSubtest("tab_capture", "offscreen_evil_tests.html"))
<< message_;
}
// http://crbug.com/177163
#if defined(OS_WIN) && !defined(NDEBUG)
#define MAYBE_GetUserMediaTest DISABLED_GetUserMediaTest
#else
#define MAYBE_GetUserMediaTest GetUserMediaTest
#endif
// Tests that getUserMedia() is NOT a way to start tab capture.
IN_PROC_BROWSER_TEST_F(TabCaptureApiTest, MAYBE_GetUserMediaTest) {
ExtensionTestMessageListener listener("ready", true);
ASSERT_TRUE(RunExtensionSubtest("tab_capture", "get_user_media_test.html"))
<< message_;
EXPECT_TRUE(listener.WaitUntilSatisfied());
content::OpenURLParams params(GURL("about:blank"), content::Referrer(),
WindowOpenDisposition::NEW_FOREGROUND_TAB,
ui::PAGE_TRANSITION_LINK, false);
content::WebContents* web_contents = browser()->OpenURL(params);
content::RenderFrameHost* const main_frame = web_contents->GetMainFrame();
ASSERT_TRUE(main_frame);
listener.Reply(base::StringPrintf("web-contents-media-stream://%i:%i",
main_frame->GetProcess()->GetID(),
main_frame->GetRoutingID()));
ResultCatcher catcher;
catcher.RestrictToBrowserContext(browser()->profile());
EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
}
// http://crbug.com/177163
#if defined(OS_WIN) && !defined(NDEBUG)
#define MAYBE_ActiveTabPermission DISABLED_ActiveTabPermission
#else
#define MAYBE_ActiveTabPermission ActiveTabPermission
#endif
// Make sure tabCapture.capture only works if the tab has been granted
// permission via an extension icon click or the extension is whitelisted.
IN_PROC_BROWSER_TEST_F(TabCaptureApiTest, MAYBE_ActiveTabPermission) {
ExtensionTestMessageListener before_open_tab("ready1", true);
ExtensionTestMessageListener before_grant_permission("ready2", true);
ExtensionTestMessageListener before_open_new_tab("ready3", true);
ExtensionTestMessageListener before_whitelist_extension("ready4", true);
ASSERT_TRUE(RunExtensionSubtest("tab_capture",
"active_tab_permission_test.html"))
<< message_;
// Open a new tab and make sure capture is denied.
EXPECT_TRUE(before_open_tab.WaitUntilSatisfied());
content::OpenURLParams params(GURL("http://google.com"), content::Referrer(),
WindowOpenDisposition::NEW_FOREGROUND_TAB,
ui::PAGE_TRANSITION_LINK, false);
content::WebContents* web_contents = browser()->OpenURL(params);
before_open_tab.Reply("");
// Grant permission and make sure capture succeeds.
EXPECT_TRUE(before_grant_permission.WaitUntilSatisfied());
const Extension* extension = ExtensionRegistry::Get(
web_contents->GetBrowserContext())->enabled_extensions().GetByID(
kExtensionId);
TabHelper::FromWebContents(web_contents)
->active_tab_permission_granter()->GrantIfRequested(extension);
before_grant_permission.Reply("");
// Open a new tab and make sure capture is denied.
EXPECT_TRUE(before_open_new_tab.WaitUntilSatisfied());
browser()->OpenURL(params);
before_open_new_tab.Reply("");
// Add extension to whitelist and make sure capture succeeds.
EXPECT_TRUE(before_whitelist_extension.WaitUntilSatisfied());
AddExtensionToCommandLineWhitelist();
before_whitelist_extension.Reply("");
ResultCatcher catcher;
catcher.RestrictToBrowserContext(browser()->profile());
EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
}
// http://crbug.com/177163
#if defined(OS_WIN) && !defined(NDEBUG)
#define MAYBE_FullscreenEvents DISABLED_FullscreenEvents
#else
#define MAYBE_FullscreenEvents FullscreenEvents
#endif
// Tests that fullscreen transitions during a tab capture session dispatch
// events to the onStatusChange listener. The test loads a page that toggles
// fullscreen mode, using the Fullscreen Javascript API, in response to mouse
// clicks.
IN_PROC_BROWSER_TEST_F(TabCaptureApiTest, MAYBE_FullscreenEvents) {
AddExtensionToCommandLineWhitelist();
ExtensionTestMessageListener capture_started("tab_capture_started", false);
ExtensionTestMessageListener entered_fullscreen("entered_fullscreen", false);
ASSERT_TRUE(RunExtensionSubtest("tab_capture", "fullscreen_test.html"))
<< message_;
EXPECT_TRUE(capture_started.WaitUntilSatisfied());
// Click on the page to trigger the Javascript that will toggle the tab into
// fullscreen mode.
SimulateMouseClickInCurrentTab();
EXPECT_TRUE(entered_fullscreen.WaitUntilSatisfied());
// Click again to exit fullscreen mode.
SimulateMouseClickInCurrentTab();
// Wait until the page examines its results and calls chrome.test.succeed().
ResultCatcher catcher;
catcher.RestrictToBrowserContext(browser()->profile());
EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
}
// Times out on Win dbg bots: http://crbug.com/177163
// Flaky on MSan bots: http://crbug.com/294431
#if (defined(OS_WIN) && !defined(NDEBUG)) || defined(MEMORY_SANITIZER)
#define MAYBE_GrantForChromePages DISABLED_GrantForChromePages
#else
#define MAYBE_GrantForChromePages GrantForChromePages
#endif
// Make sure tabCapture API can be granted for Chrome:// pages.
IN_PROC_BROWSER_TEST_F(TabCaptureApiTest, MAYBE_GrantForChromePages) {
ExtensionTestMessageListener before_open_tab("ready1", true);
ASSERT_TRUE(RunExtensionSubtest("tab_capture",
"active_tab_chrome_pages.html"))
<< message_;
EXPECT_TRUE(before_open_tab.WaitUntilSatisfied());
// Open a tab on a chrome:// page and make sure we can capture.
content::OpenURLParams params(GURL("chrome://version"), content::Referrer(),
WindowOpenDisposition::NEW_FOREGROUND_TAB,
ui::PAGE_TRANSITION_LINK, false);
content::WebContents* web_contents = browser()->OpenURL(params);
const Extension* extension = ExtensionRegistry::Get(
web_contents->GetBrowserContext())->enabled_extensions().GetByID(
kExtensionId);
TabHelper::FromWebContents(web_contents)
->active_tab_permission_granter()->GrantIfRequested(extension);
before_open_tab.Reply("");
ResultCatcher catcher;
catcher.RestrictToBrowserContext(browser()->profile());
EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
}
// http://crbug.com/177163
#if defined(OS_WIN) && !defined(NDEBUG)
#define MAYBE_CaptureInSplitIncognitoMode DISABLED_CaptureInSplitIncognitoMode
#else
#define MAYBE_CaptureInSplitIncognitoMode CaptureInSplitIncognitoMode
#endif
// Tests that a tab in incognito mode can be captured.
IN_PROC_BROWSER_TEST_F(TabCaptureApiTest, MAYBE_CaptureInSplitIncognitoMode) {
AddExtensionToCommandLineWhitelist();
ASSERT_TRUE(RunExtensionSubtest("tab_capture",
"start_tab_capture.html",
kFlagEnableIncognito | kFlagUseIncognito))
<< message_;
}
// http://crbug.com/177163
#if defined(OS_WIN) && !defined(NDEBUG)
#define MAYBE_Constraints DISABLED_Constraints
#else
#define MAYBE_Constraints Constraints
#endif
// Tests that valid constraints allow tab capture to start, while invalid ones
// do not.
IN_PROC_BROWSER_TEST_F(TabCaptureApiTest, MAYBE_Constraints) {
AddExtensionToCommandLineWhitelist();
ASSERT_TRUE(RunExtensionSubtest("tab_capture", "constraints.html"))
<< message_;
}
// http://crbug.com/177163
#if defined(OS_WIN) && !defined(NDEBUG)
#define MAYBE_TabIndicator DISABLED_TabIndicator
#else
#define MAYBE_TabIndicator TabIndicator
#endif
// Tests that the tab indicator (in the tab strip) is shown during tab capture.
IN_PROC_BROWSER_TEST_F(TabCaptureApiTest, MAYBE_TabIndicator) {
ASSERT_EQ(TabAlertState::NONE,
chrome::GetTabAlertStateForContents(
browser()->tab_strip_model()->GetActiveWebContents()));
// A TabStripModelObserver that quits the MessageLoop whenever the UI's model
// is sent an event that changes the indicator status.
class IndicatorChangeObserver : public TabStripModelObserver {
public:
explicit IndicatorChangeObserver(Browser* browser)
: browser_(browser),
last_alert_state_(chrome::GetTabAlertStateForContents(
browser->tab_strip_model()->GetActiveWebContents())) {
browser_->tab_strip_model()->AddObserver(this);
}
~IndicatorChangeObserver() override {
browser_->tab_strip_model()->RemoveObserver(this);
}
TabAlertState last_alert_state() const { return last_alert_state_; }
void TabChangedAt(content::WebContents* contents,
int index,
TabChangeType change_type) override {
const TabAlertState alert_state =
chrome::GetTabAlertStateForContents(contents);
if (alert_state != last_alert_state_) {
last_alert_state_ = alert_state;
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::MessageLoop::QuitWhenIdleClosure());
}
}
private:
Browser* const browser_;
TabAlertState last_alert_state_;
};
IndicatorChangeObserver observer(browser());
ASSERT_EQ(TabAlertState::NONE, observer.last_alert_state());
// Run an extension test that just turns on tab capture, which should cause
// the indicator to turn on.
AddExtensionToCommandLineWhitelist();
ASSERT_TRUE(RunExtensionSubtest("tab_capture", "start_tab_capture.html"))
<< message_;
// Run the browser until the indicator turns on.
const base::TimeTicks start_time = base::TimeTicks::Now();
while (observer.last_alert_state() != TabAlertState::TAB_CAPTURING) {
if (base::TimeTicks::Now() - start_time >
TestTimeouts::action_max_timeout()) {
EXPECT_EQ(TabAlertState::TAB_CAPTURING, observer.last_alert_state());
return;
}
content::RunMessageLoop();
}
}
} // namespace
} // namespace extensions