blob: a97d22882367777f6b8c4e31f1c3f67479d3fd8e [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 "content/public/browser/picture_in_picture_window_controller.h"
#include "base/bind.h"
#include "base/path_service.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_feature_list.h"
#include "build/build_config.h"
#include "chrome/browser/chrome_content_browser_client.h"
#include "chrome/browser/devtools/devtools_window_testing.h"
#include "chrome/browser/picture_in_picture/picture_in_picture_window_manager.h"
#include "chrome/browser/platform_util.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/views/overlay/overlay_window_views.h"
#include "chrome/browser/ui/views/overlay/playback_image_button.h"
#include "chrome/browser/ui/views/overlay/skip_ad_label_button.h"
#include "chrome/browser/ui/views/overlay/track_image_button.h"
#include "chrome/browser/ui/web_applications/app_browser_controller.h"
#include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
#include "chrome/browser/ui/web_applications/web_app_controller_browsertest.h"
#include "chrome/browser/web_applications/test/web_app_test.h"
#include "chrome/common/web_application_info.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/viz/common/frame_sinks/copy_output_request.h"
#include "components/viz/common/frame_sinks/copy_output_result.h"
#include "components/viz/common/surfaces/surface_id.h"
#include "content/public/browser/media_session.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/overlay_window.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/test_navigation_observer.h"
#include "media/base/media_switches.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "services/media_session/public/cpp/features.h"
#include "skia/ext/image_operations.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "third_party/blink/public/common/web_preferences/web_preferences.h"
#include "ui/events/base_event_utils.h"
#include "ui/gfx/codec/png_codec.h"
#include "ui/views/controls/button/image_button.h"
#include "ui/views/widget/widget_observer.h"
#if defined(OS_CHROMEOS)
#include "ash/public/cpp/accelerators.h"
#include "ui/base/accelerators/accelerator.h"
#include "ui/base/hit_test.h"
#endif
#if defined(TOOLKIT_VIEWS)
#include "chrome/browser/ui/views/overlay/overlay_window_views.h"
#endif
using ::testing::_;
using web_app::ProviderType;
namespace {
class MockPictureInPictureWindowController
: public content::PictureInPictureWindowController {
public:
MockPictureInPictureWindowController() = default;
// PictureInPictureWindowController:
MOCK_METHOD0(Show, void());
MOCK_METHOD1(Close, void(bool));
MOCK_METHOD0(CloseAndFocusInitiator, void());
MOCK_METHOD0(OnWindowDestroyed, void());
MOCK_METHOD0(GetWindowForTesting, content::OverlayWindow*());
MOCK_METHOD0(UpdateLayerBounds, void());
MOCK_METHOD0(IsPlayerActive, bool());
MOCK_METHOD0(GetWebContents, content::WebContents*());
MOCK_METHOD2(UpdatePlaybackState, void(bool, bool));
MOCK_METHOD0(TogglePlayPause, bool());
MOCK_METHOD0(SkipAd, void());
MOCK_METHOD0(NextTrack, void());
MOCK_METHOD0(PreviousTrack, void());
private:
DISALLOW_COPY_AND_ASSIGN(MockPictureInPictureWindowController);
};
const base::FilePath::CharType kPictureInPictureWindowSizePage[] =
FILE_PATH_LITERAL("media/picture-in-picture/window-size.html");
} // namespace
class PictureInPictureWindowControllerBrowserTest
: public InProcessBrowserTest {
public:
PictureInPictureWindowControllerBrowserTest() = default;
void SetUpOnMainThread() override {
host_resolver()->AddRule("*", "127.0.0.1");
embedded_test_server()->ServeFilesFromSourceDirectory("content/test/data");
ASSERT_TRUE(embedded_test_server()->Start());
}
void SetUpWindowController(content::WebContents* web_contents) {
pip_window_controller_ =
content::PictureInPictureWindowController::GetOrCreateForWebContents(
web_contents);
}
content::PictureInPictureWindowController* window_controller() {
return pip_window_controller_;
}
MockPictureInPictureWindowController& mock_controller() {
return mock_controller_;
}
void LoadTabAndEnterPictureInPicture(Browser* browser,
const base::FilePath& file_path) {
GURL test_page_url = ui_test_utils::GetTestUrl(
base::FilePath(base::FilePath::kCurrentDirectory), file_path);
ui_test_utils::NavigateToURL(browser, test_page_url);
content::WebContents* active_web_contents =
browser->tab_strip_model()->GetActiveWebContents();
ASSERT_NE(nullptr, active_web_contents);
SetUpWindowController(active_web_contents);
bool result = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
active_web_contents, "enterPictureInPicture();", &result));
EXPECT_TRUE(result);
}
// The WebContents that is passed to this method must have a
// "isInPictureInPicture()" method returning a boolean and when the video
// leaves Picture-in-Picture, it must change the WebContents title to
// "leavepictureinpicture".
void ExpectLeavePictureInPicture(content::WebContents* web_contents) {
// 'leavepictureinpicture' is the title of the tab when the event is
// received.
const base::string16 expected_title =
base::ASCIIToUTF16("leavepictureinpicture");
EXPECT_EQ(
expected_title,
content::TitleWatcher(web_contents, expected_title).WaitAndGetTitle());
bool in_picture_in_picture = true;
EXPECT_TRUE(ExecuteScriptAndExtractBool(
web_contents, "isInPictureInPicture();", &in_picture_in_picture));
EXPECT_FALSE(in_picture_in_picture);
}
class WidgetBoundsChangeWaiter : public views::WidgetObserver {
public:
explicit WidgetBoundsChangeWaiter(views::Widget* widget)
: widget_(widget), initial_bounds_(widget->GetWindowBoundsInScreen()) {
widget_->AddObserver(this);
}
~WidgetBoundsChangeWaiter() final { widget_->RemoveObserver(this); }
void OnWidgetBoundsChanged(views::Widget* widget, const gfx::Rect&) final {
run_loop_.Quit();
}
void Wait() {
if (widget_->GetWindowBoundsInScreen() != initial_bounds_)
return;
run_loop_.Run();
}
private:
views::Widget* widget_;
gfx::Rect initial_bounds_;
base::RunLoop run_loop_;
};
void MoveMouseOver(OverlayWindowViews* window) {
gfx::Point p(window->GetBounds().x(), window->GetBounds().y());
ui::MouseEvent moved_over(ui::ET_MOUSE_MOVED, p, p, ui::EventTimeForNow(),
ui::EF_NONE, ui::EF_NONE);
window->OnMouseEvent(&moved_over);
}
private:
content::PictureInPictureWindowController* pip_window_controller_ = nullptr;
MockPictureInPictureWindowController mock_controller_;
DISALLOW_COPY_AND_ASSIGN(PictureInPictureWindowControllerBrowserTest);
};
// Checks the creation of the window controller, as well as basic window
// creation, visibility and activation.
IN_PROC_BROWSER_TEST_F(PictureInPictureWindowControllerBrowserTest,
CreationAndVisibilityAndActivation) {
GURL test_page_url = ui_test_utils::GetTestUrl(
base::FilePath(base::FilePath::kCurrentDirectory),
base::FilePath(kPictureInPictureWindowSizePage));
ui_test_utils::NavigateToURL(browser(), test_page_url);
content::WebContents* active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(active_web_contents != nullptr);
SetUpWindowController(active_web_contents);
ASSERT_TRUE(window_controller() != nullptr);
ASSERT_TRUE(window_controller()->GetWindowForTesting() != nullptr);
EXPECT_FALSE(window_controller()->GetWindowForTesting()->IsVisible());
bool result = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
active_web_contents, "enterPictureInPicture();", &result));
EXPECT_TRUE(result);
EXPECT_TRUE(window_controller()->GetWindowForTesting()->IsVisible());
#if defined(TOOLKIT_VIEWS)
auto* overlay_window = window_controller()->GetWindowForTesting();
gfx::NativeWindow native_window =
static_cast<OverlayWindowViews*>(overlay_window)->GetNativeWindow();
#if defined(OS_CHROMEOS) || defined(OS_MAC)
EXPECT_FALSE(platform_util::IsWindowActive(native_window));
#else
EXPECT_TRUE(platform_util::IsWindowActive(native_window));
#endif
#endif
}
#if !defined(OS_CHROMEOS)
class PictureInPicturePixelComparisonBrowserTest
: public PictureInPictureWindowControllerBrowserTest {
public:
void SetUpCommandLine(base::CommandLine* command_line) override {
PictureInPictureWindowControllerBrowserTest::SetUpCommandLine(command_line);
command_line->AppendSwitch(
switches::kEnableExperimentalWebPlatformFeatures);
command_line->AppendSwitch(switches::kDisableGpu);
}
base::FilePath GetFilePath(base::FilePath::StringPieceType relative_path) {
base::FilePath base_dir;
CHECK(base::PathService::Get(base::DIR_SOURCE_ROOT, &base_dir));
// The path relative to <chromium src> for pixel test data.
const base::FilePath::StringPieceType kTestDataPath =
FILE_PATH_LITERAL("chrome/test/data/media/picture-in-picture/");
base::FilePath full_path =
base_dir.Append(kTestDataPath).Append(relative_path);
return full_path;
}
void ReadbackResult(base::RepeatingClosure quit_run_loop,
std::unique_ptr<viz::CopyOutputResult> result) {
ASSERT_FALSE(result->IsEmpty());
EXPECT_EQ(viz::CopyOutputResult::Format::RGBA_BITMAP, result->format());
result_bitmap_ = std::make_unique<SkBitmap>(result->AsSkBitmap());
EXPECT_TRUE(result_bitmap_->readyToDraw());
quit_run_loop.Run();
}
bool ReadImageFile(const base::FilePath& file_path, SkBitmap* read_image) {
std::string png_string;
base::ReadFileToString(file_path, &png_string);
return gfx::PNGCodec::Decode(
reinterpret_cast<const unsigned char*>(png_string.data()),
png_string.length(), read_image);
}
void TakeOverlayWindowScreenshot(OverlayWindowViews* overlay_window_views) {
base::RunLoop run_loop;
std::unique_ptr<viz::CopyOutputRequest> request =
std::make_unique<viz::CopyOutputRequest>(
viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP,
base::BindOnce(
&PictureInPicturePixelComparisonBrowserTest::ReadbackResult,
base::Unretained(this), run_loop.QuitClosure()));
overlay_window_views->GetLayerForTesting()->RequestCopyOfOutput(
std::move(request));
run_loop.Run();
}
bool CompareImages(const SkBitmap& actual_bmp, const SkBitmap& expected_bmp) {
// Allowable error and thresholds because of small color shift by
// video to image conversion and GPU issues.
const int kAllowableError = 3;
// Number of pixels with an error
int error_pixels_count = 0;
gfx::Rect error_bounding_rect;
for (int x = 0; x < actual_bmp.width(); ++x) {
for (int y = 0; y < actual_bmp.height(); ++y) {
SkColor actual_color = actual_bmp.getColor(x, y);
SkColor expected_color = expected_bmp.getColor(x, y);
if ((fabs(SkColorGetR(actual_color) - SkColorGetR(expected_color)) >
kAllowableError) ||
(fabs(SkColorGetG(actual_color) - SkColorGetG(expected_color)) >
kAllowableError) ||
(fabs(SkColorGetB(actual_color) - SkColorGetB(expected_color))) >
kAllowableError) {
++error_pixels_count;
error_bounding_rect.Union(gfx::Rect(x, y, 1, 1));
}
}
}
if (error_pixels_count != 0) {
LOG(ERROR) << "Number of pixel with an error: " << error_pixels_count;
LOG(ERROR) << "Error Bounding Box : " << error_bounding_rect.ToString();
return false;
}
return true;
}
void Wait(base::TimeDelta timeout) {
base::RunLoop run_loop;
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE, run_loop.QuitClosure(), timeout);
run_loop.Run();
}
SkBitmap& GetResultBitmap() { return *result_bitmap_; }
private:
std::unique_ptr<SkBitmap> result_bitmap_;
};
// TODO(cliffordcheng): enable on Windows when compile errors are resolved.
// Plays a video and then trigger Picture-in-Picture. Grabs a screenshot of
// Picture-in-Picture window and verifies it's as expected.
IN_PROC_BROWSER_TEST_F(PictureInPicturePixelComparisonBrowserTest, VideoPlay) {
base::ScopedAllowBlockingForTesting allow_blocking;
LoadTabAndEnterPictureInPicture(
browser(), base::FilePath(FILE_PATH_LITERAL(
"media/picture-in-picture/pixel_test.html")));
content::WebContents* active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
bool in_picture_in_picture = false;
ASSERT_TRUE(ExecuteScriptAndExtractBool(
active_web_contents, "isInPictureInPicture();", &in_picture_in_picture));
EXPECT_TRUE(in_picture_in_picture);
EXPECT_TRUE(content::ExecuteScript(active_web_contents, "video.play();"));
SetUpWindowController(active_web_contents);
ASSERT_NE(nullptr, window_controller());
EXPECT_TRUE(window_controller()->GetWindowForTesting()->IsVisible());
OverlayWindowViews* overlay_window_views = static_cast<OverlayWindowViews*>(
window_controller()->GetWindowForTesting());
overlay_window_views->SetSize(gfx::Size(402, 268));
base::string16 expected_title = base::ASCIIToUTF16("resized");
EXPECT_EQ(expected_title,
content::TitleWatcher(active_web_contents, expected_title)
.WaitAndGetTitle());
Wait(base::TimeDelta::FromSeconds(3));
TakeOverlayWindowScreenshot(overlay_window_views);
SkBitmap expected_image;
base::FilePath expected_image_path =
GetFilePath(FILE_PATH_LITERAL("pixel_expected_video_play.png"));
ASSERT_TRUE(ReadImageFile(expected_image_path, &expected_image));
EXPECT_TRUE(CompareImages(GetResultBitmap(), expected_image));
}
// Plays a video in PiP. Trigger the play and pause control in PiP by using a
// mouse move. Capture the images and verift they are expected.
IN_PROC_BROWSER_TEST_F(PictureInPicturePixelComparisonBrowserTest,
PlayAndPauseControls) {
base::ScopedAllowBlockingForTesting allow_blocking;
LoadTabAndEnterPictureInPicture(
browser(), base::FilePath(FILE_PATH_LITERAL(
"media/picture-in-picture/pixel_test.html")));
content::WebContents* active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
bool in_picture_in_picture = false;
ASSERT_TRUE(ExecuteScriptAndExtractBool(
active_web_contents, "isInPictureInPicture();", &in_picture_in_picture));
EXPECT_TRUE(in_picture_in_picture);
EXPECT_TRUE(window_controller()->GetWindowForTesting()->IsVisible());
OverlayWindowViews* overlay_window_views = static_cast<OverlayWindowViews*>(
window_controller()->GetWindowForTesting());
bool result = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
active_web_contents, "changeVideoSrc();", &result));
const int resize_width = 402, resize_height = 268;
overlay_window_views->SetSize(gfx::Size(resize_width, resize_height));
base::string16 expected_title = base::ASCIIToUTF16("resized");
EXPECT_EQ(expected_title,
content::TitleWatcher(active_web_contents, expected_title)
.WaitAndGetTitle());
EXPECT_TRUE(content::ExecuteScript(active_web_contents, "video.play();"));
Wait(base::TimeDelta::FromSeconds(3));
MoveMouseOver(overlay_window_views);
TakeOverlayWindowScreenshot(overlay_window_views);
base::FilePath expected_pause_image_path =
GetFilePath(FILE_PATH_LITERAL("pixel_expected_pause_control.png"));
base::FilePath expected_play_image_path =
GetFilePath(FILE_PATH_LITERAL("pixel_expected_play_control.png"));
// If the test image is cropped, usually off by 1 pixel, use another image.
if (GetResultBitmap().width() < resize_width ||
GetResultBitmap().height() < resize_height) {
LOG(INFO) << "Actual image is cropped and backup images are used. "
<< "Test image dimension: "
<< "(" << GetResultBitmap().width() << "x"
<< GetResultBitmap().height() << "). "
<< "Expected image dimension: "
<< "(" << resize_width << "x" << resize_height << ")";
expected_pause_image_path =
GetFilePath(FILE_PATH_LITERAL("pixel_expected_pause_control_crop.png"));
expected_play_image_path =
GetFilePath(FILE_PATH_LITERAL("pixel_expected_play_control_crop.png"));
}
SkBitmap expected_image;
ASSERT_TRUE(ReadImageFile(expected_pause_image_path, &expected_image));
EXPECT_TRUE(CompareImages(GetResultBitmap(), expected_image));
EXPECT_TRUE(content::ExecuteScript(active_web_contents, "video.pause();"));
Wait(base::TimeDelta::FromSeconds(3));
MoveMouseOver(overlay_window_views);
TakeOverlayWindowScreenshot(overlay_window_views);
ASSERT_TRUE(ReadImageFile(expected_play_image_path, &expected_image));
EXPECT_TRUE(CompareImages(GetResultBitmap(), expected_image));
}
#endif // !defined(OS_CHROMEOS)
// Tests that when an active WebContents accurately tracks whether a video
// is in Picture-in-Picture.
IN_PROC_BROWSER_TEST_F(PictureInPictureWindowControllerBrowserTest,
TabIconUpdated) {
GURL test_page_url = ui_test_utils::GetTestUrl(
base::FilePath(base::FilePath::kCurrentDirectory),
base::FilePath(kPictureInPictureWindowSizePage));
ui_test_utils::NavigateToURL(browser(), test_page_url);
content::WebContents* active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(active_web_contents);
// First test there is no video playing in Picture-in-Picture.
EXPECT_FALSE(active_web_contents->HasPictureInPictureVideo());
// Start playing video in Picture-in-Picture and retest with the
// opposite assertion.
SetUpWindowController(active_web_contents);
ASSERT_TRUE(window_controller());
bool result = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
active_web_contents, "enterPictureInPicture();", &result));
EXPECT_TRUE(result);
EXPECT_TRUE(active_web_contents->HasPictureInPictureVideo());
// Stop video being played Picture-in-Picture and check if that's tracked.
window_controller()->Close(true /* should_pause_video */);
EXPECT_FALSE(active_web_contents->HasPictureInPictureVideo());
// Reload page should not crash.
ui_test_utils::NavigateToURL(browser(), test_page_url);
}
// Tests that when creating a Picture-in-Picture window a size is sent to the
// caller and if the window is resized, the caller is also notified.
IN_PROC_BROWSER_TEST_F(PictureInPictureWindowControllerBrowserTest,
ResizeEventFired) {
GURL test_page_url = ui_test_utils::GetTestUrl(
base::FilePath(base::FilePath::kCurrentDirectory),
base::FilePath(kPictureInPictureWindowSizePage));
ui_test_utils::NavigateToURL(browser(), test_page_url);
content::WebContents* active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(active_web_contents);
SetUpWindowController(active_web_contents);
ASSERT_TRUE(window_controller());
content::OverlayWindow* overlay_window =
window_controller()->GetWindowForTesting();
ASSERT_TRUE(overlay_window);
ASSERT_FALSE(overlay_window->IsVisible());
bool result = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
active_web_contents, "enterPictureInPicture();", &result));
EXPECT_TRUE(result);
static_cast<OverlayWindowViews*>(overlay_window)
->SetSize(gfx::Size(400, 400));
base::string16 expected_title = base::ASCIIToUTF16("resized");
EXPECT_EQ(expected_title,
content::TitleWatcher(active_web_contents, expected_title)
.WaitAndGetTitle());
}
// Tests that when closing a Picture-in-Picture window, the video element is
// reflected as no longer in Picture-in-Picture.
IN_PROC_BROWSER_TEST_F(PictureInPictureWindowControllerBrowserTest,
CloseWindowWhilePlaying) {
GURL test_page_url = ui_test_utils::GetTestUrl(
base::FilePath(base::FilePath::kCurrentDirectory),
base::FilePath(kPictureInPictureWindowSizePage));
ui_test_utils::NavigateToURL(browser(), test_page_url);
content::WebContents* active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(active_web_contents);
SetUpWindowController(active_web_contents);
ASSERT_TRUE(window_controller());
EXPECT_TRUE(content::ExecuteScript(active_web_contents, "video.play();"));
bool result = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
active_web_contents, "enterPictureInPicture();", &result));
EXPECT_TRUE(result);
bool in_picture_in_picture = false;
EXPECT_TRUE(ExecuteScriptAndExtractBool(
active_web_contents, "isInPictureInPicture();", &in_picture_in_picture));
EXPECT_TRUE(in_picture_in_picture);
window_controller()->Close(true /* should_pause_video */);
base::string16 expected_title = base::ASCIIToUTF16("leavepictureinpicture");
EXPECT_EQ(expected_title,
content::TitleWatcher(active_web_contents, expected_title)
.WaitAndGetTitle());
bool is_paused = false;
EXPECT_TRUE(ExecuteScriptAndExtractBool(active_web_contents, "isPaused();",
&is_paused));
EXPECT_TRUE(is_paused);
}
// Ditto, when the video isn't playing.
IN_PROC_BROWSER_TEST_F(PictureInPictureWindowControllerBrowserTest,
CloseWindowWithoutPlaying) {
GURL test_page_url = ui_test_utils::GetTestUrl(
base::FilePath(base::FilePath::kCurrentDirectory),
base::FilePath(kPictureInPictureWindowSizePage));
ui_test_utils::NavigateToURL(browser(), test_page_url);
content::WebContents* active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(active_web_contents);
SetUpWindowController(active_web_contents);
bool result = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
active_web_contents, "enterPictureInPicture();", &result));
EXPECT_TRUE(result);
bool in_picture_in_picture = false;
EXPECT_TRUE(ExecuteScriptAndExtractBool(
active_web_contents, "isInPictureInPicture();", &in_picture_in_picture));
EXPECT_TRUE(in_picture_in_picture);
ASSERT_TRUE(window_controller());
window_controller()->Close(true /* should_pause_video */);
base::string16 expected_title = base::ASCIIToUTF16("leavepictureinpicture");
EXPECT_EQ(expected_title,
content::TitleWatcher(active_web_contents, expected_title)
.WaitAndGetTitle());
}
// Tests that when closing a Picture-in-Picture window, the video element
// no longer in Picture-in-Picture can't enter Picture-in-Picture right away.
IN_PROC_BROWSER_TEST_F(PictureInPictureWindowControllerBrowserTest,
CloseWindowCantEnterPictureInPictureAgain) {
GURL test_page_url = ui_test_utils::GetTestUrl(
base::FilePath(base::FilePath::kCurrentDirectory),
base::FilePath(kPictureInPictureWindowSizePage));
ui_test_utils::NavigateToURL(browser(), test_page_url);
content::WebContents* active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(active_web_contents);
SetUpWindowController(active_web_contents);
bool result = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
active_web_contents, "enterPictureInPicture();", &result));
EXPECT_TRUE(result);
EXPECT_TRUE(content::ExecuteScriptWithoutUserGesture(
active_web_contents, "tryToEnterPictureInPictureAfterLeaving();"));
bool in_picture_in_picture = false;
EXPECT_TRUE(content::ExecuteScriptWithoutUserGestureAndExtractBool(
active_web_contents, "isInPictureInPicture();", &in_picture_in_picture));
EXPECT_TRUE(in_picture_in_picture);
ASSERT_TRUE(window_controller());
window_controller()->Close(true /* should_pause_video */);
base::string16 expected_title =
base::ASCIIToUTF16("failed to enter Picture-in-Picture after leaving");
EXPECT_EQ(expected_title,
content::TitleWatcher(active_web_contents, expected_title)
.WaitAndGetTitle());
}
// Tests that when closing a Picture-in-Picture window from the Web API, the
// video element is not paused.
// Flaky on Linux and Windows: http://crbug.com/1001538
IN_PROC_BROWSER_TEST_F(PictureInPictureWindowControllerBrowserTest,
DISABLED_CloseWindowFromWebAPIWhilePlaying) {
GURL test_page_url = ui_test_utils::GetTestUrl(
base::FilePath(base::FilePath::kCurrentDirectory),
base::FilePath(kPictureInPictureWindowSizePage));
ui_test_utils::NavigateToURL(browser(), test_page_url);
content::WebContents* active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(active_web_contents);
SetUpWindowController(active_web_contents);
ASSERT_TRUE(window_controller());
EXPECT_TRUE(content::ExecuteScript(active_web_contents, "video.play();"));
bool result = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
active_web_contents, "enterPictureInPicture();", &result));
EXPECT_TRUE(result);
EXPECT_TRUE(
content::ExecuteScript(active_web_contents, "exitPictureInPicture();"));
// 'left' is sent when the first video leaves Picture-in-Picture.
base::string16 expected_title = base::ASCIIToUTF16("leavepictureinpicture");
EXPECT_EQ(expected_title,
content::TitleWatcher(active_web_contents, expected_title)
.WaitAndGetTitle());
bool is_paused = false;
EXPECT_TRUE(ExecuteScriptAndExtractBool(active_web_contents, "isPaused();",
&is_paused));
EXPECT_FALSE(is_paused);
}
// Tests that when starting a new Picture-in-Picture session from the same
// video, the video stays in Picture-in-Picture mode.
IN_PROC_BROWSER_TEST_F(PictureInPictureWindowControllerBrowserTest,
RequestPictureInPictureTwiceFromSameVideo) {
GURL test_page_url = ui_test_utils::GetTestUrl(
base::FilePath(base::FilePath::kCurrentDirectory),
base::FilePath(kPictureInPictureWindowSizePage));
ui_test_utils::NavigateToURL(browser(), test_page_url);
content::WebContents* active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(active_web_contents);
SetUpWindowController(active_web_contents);
ASSERT_TRUE(window_controller());
EXPECT_TRUE(content::ExecuteScript(active_web_contents, "video.play();"));
{
bool result = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
active_web_contents, "enterPictureInPicture();", &result));
EXPECT_TRUE(result);
}
bool in_picture_in_picture = false;
EXPECT_TRUE(ExecuteScriptAndExtractBool(
active_web_contents, "isInPictureInPicture();", &in_picture_in_picture));
EXPECT_TRUE(in_picture_in_picture);
EXPECT_TRUE(
content::ExecuteScript(active_web_contents, "exitPictureInPicture();"));
// 'left' is sent when the video leaves Picture-in-Picture.
base::string16 expected_title = base::ASCIIToUTF16("leavepictureinpicture");
EXPECT_EQ(expected_title,
content::TitleWatcher(active_web_contents, expected_title)
.WaitAndGetTitle());
{
bool result = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
active_web_contents, "enterPictureInPicture();", &result));
EXPECT_TRUE(result);
}
in_picture_in_picture = false;
EXPECT_TRUE(ExecuteScriptAndExtractBool(
active_web_contents, "isInPictureInPicture();", &in_picture_in_picture));
EXPECT_TRUE(in_picture_in_picture);
bool is_paused = false;
EXPECT_TRUE(ExecuteScriptAndExtractBool(active_web_contents, "isPaused();",
&is_paused));
EXPECT_FALSE(is_paused);
}
// Tests that when starting a new Picture-in-Picture session from the same tab,
// the previous video is no longer in Picture-in-Picture mode.
IN_PROC_BROWSER_TEST_F(PictureInPictureWindowControllerBrowserTest,
OpenSecondPictureInPictureStopsFirst) {
GURL test_page_url = ui_test_utils::GetTestUrl(
base::FilePath(base::FilePath::kCurrentDirectory),
base::FilePath(kPictureInPictureWindowSizePage));
ui_test_utils::NavigateToURL(browser(), test_page_url);
content::WebContents* active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(active_web_contents);
SetUpWindowController(active_web_contents);
ASSERT_TRUE(window_controller());
EXPECT_TRUE(content::ExecuteScript(active_web_contents, "video.play();"));
bool result = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
active_web_contents, "enterPictureInPicture();", &result));
EXPECT_TRUE(result);
bool in_picture_in_picture = false;
EXPECT_TRUE(ExecuteScriptAndExtractBool(
active_web_contents, "isInPictureInPicture();", &in_picture_in_picture));
EXPECT_TRUE(in_picture_in_picture);
EXPECT_TRUE(
content::ExecuteScript(active_web_contents, "secondPictureInPicture();"));
ExpectLeavePictureInPicture(active_web_contents);
bool is_paused = false;
EXPECT_TRUE(ExecuteScriptAndExtractBool(active_web_contents, "isPaused();",
&is_paused));
EXPECT_FALSE(is_paused);
OverlayWindowViews* overlay_window = static_cast<OverlayWindowViews*>(
window_controller()->GetWindowForTesting());
EXPECT_TRUE(overlay_window->IsVisible());
EXPECT_EQ(overlay_window->playback_state_for_testing(),
OverlayWindowViews::PlaybackState::kPaused);
}
// Tests that resetting video src when video is in Picture-in-Picture session
// keep Picture-in-Picture window opened.
IN_PROC_BROWSER_TEST_F(PictureInPictureWindowControllerBrowserTest,
ResetVideoSrcKeepsPictureInPictureWindowOpened) {
LoadTabAndEnterPictureInPicture(
browser(), base::FilePath(kPictureInPictureWindowSizePage));
EXPECT_TRUE(window_controller()->GetWindowForTesting()->IsVisible());
OverlayWindowViews* overlay_window = static_cast<OverlayWindowViews*>(
window_controller()->GetWindowForTesting());
EXPECT_TRUE(overlay_window->video_layer_for_testing()->visible());
content::WebContents* active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
EXPECT_TRUE(content::ExecuteScript(active_web_contents, "video.src = null;"));
bool in_picture_in_picture = false;
EXPECT_TRUE(ExecuteScriptAndExtractBool(
active_web_contents, "isInPictureInPicture();", &in_picture_in_picture));
EXPECT_TRUE(in_picture_in_picture);
EXPECT_TRUE(window_controller()->GetWindowForTesting()->IsVisible());
EXPECT_TRUE(overlay_window->video_layer_for_testing()->visible());
}
// Tests that updating video src when video is in Picture-in-Picture session
// keep Picture-in-Picture window opened.
IN_PROC_BROWSER_TEST_F(PictureInPictureWindowControllerBrowserTest,
UpdateVideoSrcKeepsPictureInPictureWindowOpened) {
LoadTabAndEnterPictureInPicture(
browser(), base::FilePath(kPictureInPictureWindowSizePage));
EXPECT_TRUE(window_controller()->GetWindowForTesting()->IsVisible());
OverlayWindowViews* overlay_window = static_cast<OverlayWindowViews*>(
window_controller()->GetWindowForTesting());
EXPECT_TRUE(overlay_window->video_layer_for_testing()->visible());
content::WebContents* active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
bool result = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
active_web_contents, "changeVideoSrc();", &result));
EXPECT_TRUE(result);
bool in_picture_in_picture = false;
EXPECT_TRUE(ExecuteScriptAndExtractBool(
active_web_contents, "isInPictureInPicture();", &in_picture_in_picture));
EXPECT_TRUE(in_picture_in_picture);
EXPECT_TRUE(window_controller()->GetWindowForTesting()->IsVisible());
EXPECT_TRUE(overlay_window->video_layer_for_testing()->visible());
}
// Tests that changing video src to media stream when video is in
// Picture-in-Picture session keep Picture-in-Picture window opened.
IN_PROC_BROWSER_TEST_F(
PictureInPictureWindowControllerBrowserTest,
ChangeVideoSrcToMediaStreamKeepsPictureInPictureWindowOpened) {
LoadTabAndEnterPictureInPicture(
browser(), base::FilePath(kPictureInPictureWindowSizePage));
EXPECT_TRUE(window_controller()->GetWindowForTesting()->IsVisible());
OverlayWindowViews* overlay_window = static_cast<OverlayWindowViews*>(
window_controller()->GetWindowForTesting());
EXPECT_TRUE(overlay_window->video_layer_for_testing()->visible());
content::WebContents* active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
bool result = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
active_web_contents, "changeVideoSrcToMediaStream();", &result));
EXPECT_TRUE(result);
bool in_picture_in_picture = false;
EXPECT_TRUE(ExecuteScriptAndExtractBool(
active_web_contents, "isInPictureInPicture();", &in_picture_in_picture));
EXPECT_TRUE(in_picture_in_picture);
EXPECT_TRUE(window_controller()->GetWindowForTesting()->IsVisible());
EXPECT_TRUE(overlay_window->video_layer_for_testing()->visible());
EXPECT_FALSE(overlay_window->previous_track_controls_view_for_testing()
->layer()
->visible());
EXPECT_FALSE(overlay_window->play_pause_controls_view_for_testing()
->layer()
->visible());
EXPECT_FALSE(overlay_window->next_track_controls_view_for_testing()
->layer()
->visible());
}
// Tests that we can enter Picture-in-Picture when a video is not preloaded,
// using the metadata optimizations. This test is checking that there are no
// crashes.
IN_PROC_BROWSER_TEST_F(PictureInPictureWindowControllerBrowserTest,
EnterMetadataPosterOptimisation) {
GURL test_page_url = ui_test_utils::GetTestUrl(
base::FilePath(base::FilePath::kCurrentDirectory),
base::FilePath(FILE_PATH_LITERAL(
"media/picture-in-picture/player_metadata_poster.html")));
ui_test_utils::NavigateToURL(browser(), test_page_url);
content::WebContents* active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(active_web_contents);
SetUpWindowController(active_web_contents);
bool result = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
active_web_contents, "enterPictureInPicture();", &result));
EXPECT_TRUE(result);
}
// Tests that calling PictureInPictureWindowController::Close() twice has no
// side effect.
IN_PROC_BROWSER_TEST_F(PictureInPictureWindowControllerBrowserTest,
CloseTwiceSideEffects) {
GURL test_page_url = ui_test_utils::GetTestUrl(
base::FilePath(base::FilePath::kCurrentDirectory),
base::FilePath(kPictureInPictureWindowSizePage));
ui_test_utils::NavigateToURL(browser(), test_page_url);
content::WebContents* active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(active_web_contents);
SetUpWindowController(active_web_contents);
ASSERT_TRUE(window_controller());
bool result = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
active_web_contents, "enterPictureInPicture();", &result));
EXPECT_TRUE(result);
window_controller()->Close(true /* should_pause_video */);
// Wait for the window to close.
base::string16 expected_title = base::ASCIIToUTF16("leavepictureinpicture");
EXPECT_EQ(expected_title,
content::TitleWatcher(active_web_contents, expected_title)
.WaitAndGetTitle());
bool video_paused = false;
// Video is paused after Picture-in-Picture window was closed.
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
active_web_contents, "isPaused();", &video_paused));
EXPECT_TRUE(video_paused);
// Resume playback.
ASSERT_TRUE(content::ExecuteScript(active_web_contents, "video.play();"));
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
active_web_contents, "isPaused();", &video_paused));
EXPECT_FALSE(video_paused);
// This should be a no-op because the window is not visible.
window_controller()->Close(true /* should_pause_video */);
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
active_web_contents, "isPaused();", &video_paused));
EXPECT_FALSE(video_paused);
}
// Checks entering Picture-in-Picture on multiple tabs, where the initial tab
// has been closed.
IN_PROC_BROWSER_TEST_F(PictureInPictureWindowControllerBrowserTest,
PictureInPictureAfterClosingTab) {
GURL test_page_url = ui_test_utils::GetTestUrl(
base::FilePath(base::FilePath::kCurrentDirectory),
base::FilePath(kPictureInPictureWindowSizePage));
ui_test_utils::NavigateToURL(browser(), test_page_url);
content::WebContents* active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(active_web_contents != nullptr);
SetUpWindowController(active_web_contents);
{
bool result = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
active_web_contents, "enterPictureInPicture();", &result));
EXPECT_TRUE(result);
}
ASSERT_TRUE(window_controller()->GetWindowForTesting()->IsVisible());
// Open a new tab in the browser.
AddTabAtIndex(1, test_page_url, ui::PAGE_TRANSITION_TYPED);
ASSERT_TRUE(window_controller()->GetWindowForTesting()->IsVisible());
EXPECT_EQ(2, browser()->tab_strip_model()->count());
EXPECT_EQ(1, browser()->tab_strip_model()->active_index());
// Once the initiator tab is closed, the controller should also be torn down.
browser()->tab_strip_model()->CloseWebContentsAt(0, 0);
EXPECT_EQ(1, browser()->tab_strip_model()->count());
EXPECT_EQ(0, browser()->tab_strip_model()->active_index());
// Open video in Picture-in-Picture mode again, on the new tab.
active_web_contents = browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(active_web_contents != nullptr);
SetUpWindowController(active_web_contents);
{
bool result = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
active_web_contents, "enterPictureInPicture();", &result));
EXPECT_TRUE(result);
}
ASSERT_TRUE(window_controller()->GetWindowForTesting()->IsVisible());
}
// Closing a tab that lost Picture-in-Picture because a new tab entered it
// should not close the current Picture-in-Picture window.
IN_PROC_BROWSER_TEST_F(PictureInPictureWindowControllerBrowserTest,
PictureInPictureDoNotCloseAfterClosingTab) {
GURL test_page_url = ui_test_utils::GetTestUrl(
base::FilePath(base::FilePath::kCurrentDirectory),
base::FilePath(kPictureInPictureWindowSizePage));
ui_test_utils::NavigateToURL(browser(), test_page_url);
content::WebContents* initial_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(initial_web_contents != nullptr);
SetUpWindowController(initial_web_contents);
{
bool result = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
initial_web_contents, "enterPictureInPicture();", &result));
EXPECT_TRUE(result);
}
EXPECT_TRUE(window_controller()->GetWindowForTesting()->IsVisible());
// Open a new tab in the browser and starts Picture-in-Picture.
AddTabAtIndex(1, test_page_url, ui::PAGE_TRANSITION_TYPED);
EXPECT_EQ(2, browser()->tab_strip_model()->count());
EXPECT_EQ(1, browser()->tab_strip_model()->active_index());
content::WebContents* new_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(new_web_contents != nullptr);
{
content::PictureInPictureWindowController* pip_window_controller =
content::PictureInPictureWindowController::GetOrCreateForWebContents(
new_web_contents);
bool result = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
new_web_contents, "enterPictureInPicture();", &result));
EXPECT_TRUE(result);
EXPECT_TRUE(pip_window_controller->GetWindowForTesting()->IsVisible());
// 'left' is sent when the first tab leaves Picture-in-Picture.
base::string16 expected_title = base::ASCIIToUTF16("leavepictureinpicture");
EXPECT_EQ(expected_title,
content::TitleWatcher(initial_web_contents, expected_title)
.WaitAndGetTitle());
}
// Closing the initial tab should not get the new tab to leave
// Picture-in-Picture.
browser()->tab_strip_model()->CloseWebContentsAt(0, 0);
EXPECT_EQ(1, browser()->tab_strip_model()->count());
EXPECT_EQ(0, browser()->tab_strip_model()->active_index());
base::RunLoop().RunUntilIdle();
bool in_picture_in_picture = false;
ASSERT_TRUE(ExecuteScriptAndExtractBool(
new_web_contents, "isInPictureInPicture();", &in_picture_in_picture));
EXPECT_TRUE(in_picture_in_picture);
}
// Killing an iframe that lost Picture-in-Picture because the main frame entered
// it should not close the current Picture-in-Picture window.
IN_PROC_BROWSER_TEST_F(PictureInPictureWindowControllerBrowserTest,
PictureInPictureDoNotCloseAfterKillingFrame) {
GURL test_page_url = ui_test_utils::GetTestUrl(
base::FilePath(base::FilePath::kCurrentDirectory),
base::FilePath(
FILE_PATH_LITERAL("media/picture-in-picture/iframe-test.html")));
ui_test_utils::NavigateToURL(browser(), test_page_url);
content::WebContents* active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(active_web_contents != nullptr);
SetUpWindowController(active_web_contents);
std::vector<content::RenderFrameHost*> render_frame_hosts =
active_web_contents->GetAllFrames();
ASSERT_EQ(2u, render_frame_hosts.size());
content::RenderFrameHost* iframe =
render_frame_hosts[0] == active_web_contents->GetMainFrame()
? render_frame_hosts[1]
: render_frame_hosts[0];
{
bool result = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
iframe, "enterPictureInPicture();", &result));
EXPECT_TRUE(result);
}
EXPECT_TRUE(window_controller()->GetWindowForTesting()->IsVisible());
bool result = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
active_web_contents, "enterPictureInPicture();", &result));
EXPECT_TRUE(result);
EXPECT_TRUE(window_controller()->GetWindowForTesting()->IsVisible());
base::RunLoop().RunUntilIdle();
{
bool in_picture_in_picture = false;
ASSERT_TRUE(
ExecuteScriptAndExtractBool(iframe,
"window.domAutomationController.send(!!"
"document.pictureInPictureElement);",
&in_picture_in_picture));
EXPECT_FALSE(in_picture_in_picture);
}
// Removing the iframe should not lead to the main frame leaving
// Picture-in-Picture.
ASSERT_TRUE(content::ExecuteScript(active_web_contents, "removeFrame();"));
EXPECT_EQ(1u, active_web_contents->GetAllFrames().size());
EXPECT_TRUE(window_controller()->GetWindowForTesting()->IsVisible());
base::RunLoop().RunUntilIdle();
{
bool in_picture_in_picture = false;
ASSERT_TRUE(
ExecuteScriptAndExtractBool(active_web_contents,
"window.domAutomationController.send(!!"
"document.pictureInPictureElement);",
&in_picture_in_picture));
EXPECT_TRUE(in_picture_in_picture);
}
}
// Checks setting disablePictureInPicture on video just after requesting
// Picture-in-Picture doesn't result in a window opened.
IN_PROC_BROWSER_TEST_F(PictureInPictureWindowControllerBrowserTest,
RequestPictureInPictureAfterDisablePictureInPicture) {
GURL test_page_url = ui_test_utils::GetTestUrl(
base::FilePath(base::FilePath::kCurrentDirectory),
base::FilePath(kPictureInPictureWindowSizePage));
ui_test_utils::NavigateToURL(browser(), test_page_url);
content::WebContents* active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(active_web_contents != nullptr);
SetUpWindowController(active_web_contents);
bool result = true;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
active_web_contents, "requestPictureInPictureAndDisable();", &result));
EXPECT_FALSE(result);
ASSERT_FALSE(window_controller()->GetWindowForTesting()->IsVisible());
}
// Checks that a video in Picture-in-Picture stops if its iframe is removed.
IN_PROC_BROWSER_TEST_F(PictureInPictureWindowControllerBrowserTest,
FrameEnterLeaveClosesWindow) {
GURL test_page_url = ui_test_utils::GetTestUrl(
base::FilePath(base::FilePath::kCurrentDirectory),
base::FilePath(
FILE_PATH_LITERAL("media/picture-in-picture/iframe-test.html")));
ui_test_utils::NavigateToURL(browser(), test_page_url);
content::WebContents* active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(active_web_contents != nullptr);
SetUpWindowController(active_web_contents);
std::vector<content::RenderFrameHost*> render_frame_hosts =
active_web_contents->GetAllFrames();
ASSERT_EQ(2u, render_frame_hosts.size());
content::RenderFrameHost* iframe =
render_frame_hosts[0] == active_web_contents->GetMainFrame()
? render_frame_hosts[1]
: render_frame_hosts[0];
// Wait for video metadata to load.
base::string16 expected_title = base::ASCIIToUTF16("loadedmetadata");
EXPECT_EQ(expected_title,
content::TitleWatcher(active_web_contents, expected_title)
.WaitAndGetTitle());
bool result = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
iframe, "enterPictureInPicture();", &result));
EXPECT_TRUE(result);
EXPECT_TRUE(window_controller()->GetWindowForTesting()->IsVisible());
ASSERT_TRUE(content::ExecuteScript(active_web_contents, "removeFrame();"));
EXPECT_EQ(1u, active_web_contents->GetAllFrames().size());
EXPECT_FALSE(window_controller()->GetWindowForTesting()->IsVisible());
}
IN_PROC_BROWSER_TEST_F(PictureInPictureWindowControllerBrowserTest,
CrossOriginFrameEnterLeaveCloseWindow) {
GURL embed_url = embedded_test_server()->GetURL(
"a.com", "/media/picture-in-picture/iframe-content.html");
GURL main_url = embedded_test_server()->GetURL(
"example.com", "/media/picture-in-picture/iframe-test.html?embed_url=" +
embed_url.spec());
ui_test_utils::NavigateToURL(browser(), main_url);
content::WebContents* active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(active_web_contents != nullptr);
SetUpWindowController(active_web_contents);
std::vector<content::RenderFrameHost*> render_frame_hosts =
active_web_contents->GetAllFrames();
ASSERT_EQ(2u, render_frame_hosts.size());
content::RenderFrameHost* iframe =
render_frame_hosts[0] == active_web_contents->GetMainFrame()
? render_frame_hosts[1]
: render_frame_hosts[0];
// Wait for video metadata to load.
base::string16 expected_title = base::ASCIIToUTF16("loadedmetadata");
EXPECT_EQ(expected_title,
content::TitleWatcher(active_web_contents, expected_title)
.WaitAndGetTitle());
bool result = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
iframe, "enterPictureInPicture();", &result));
EXPECT_TRUE(result);
EXPECT_TRUE(window_controller()->GetWindowForTesting()->IsVisible());
ASSERT_TRUE(content::ExecuteScript(active_web_contents, "removeFrame();"));
EXPECT_EQ(1u, active_web_contents->GetAllFrames().size());
EXPECT_FALSE(window_controller()->GetWindowForTesting()->IsVisible());
}
IN_PROC_BROWSER_TEST_F(PictureInPictureWindowControllerBrowserTest,
MultipleBrowserWindowOnePIPWindow) {
LoadTabAndEnterPictureInPicture(
browser(), base::FilePath(kPictureInPictureWindowSizePage));
content::PictureInPictureWindowController* first_controller =
window_controller();
EXPECT_TRUE(first_controller->GetWindowForTesting()->IsVisible());
Browser* second_browser = CreateBrowser(browser()->profile());
LoadTabAndEnterPictureInPicture(
second_browser, base::FilePath(kPictureInPictureWindowSizePage));
content::PictureInPictureWindowController* second_controller =
window_controller();
EXPECT_FALSE(first_controller->GetWindowForTesting()->IsVisible());
EXPECT_TRUE(second_controller->GetWindowForTesting()->IsVisible());
}
IN_PROC_BROWSER_TEST_F(PictureInPictureWindowControllerBrowserTest,
EnterPictureInPictureThenFullscreen) {
LoadTabAndEnterPictureInPicture(
browser(), base::FilePath(kPictureInPictureWindowSizePage));
content::WebContents* active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(content::ExecuteScript(active_web_contents, "enterFullscreen()"));
base::string16 expected_title = base::ASCIIToUTF16("fullscreen");
EXPECT_EQ(expected_title,
content::TitleWatcher(active_web_contents, expected_title)
.WaitAndGetTitle());
EXPECT_TRUE(active_web_contents->IsFullscreen());
EXPECT_FALSE(window_controller()->GetWindowForTesting()->IsVisible());
}
IN_PROC_BROWSER_TEST_F(PictureInPictureWindowControllerBrowserTest,
EnterFullscreenThenPictureInPicture) {
GURL test_page_url = ui_test_utils::GetTestUrl(
base::FilePath(base::FilePath::kCurrentDirectory),
base::FilePath(kPictureInPictureWindowSizePage));
ui_test_utils::NavigateToURL(browser(), test_page_url);
content::WebContents* active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(active_web_contents != nullptr);
SetUpWindowController(active_web_contents);
ASSERT_TRUE(content::ExecuteScript(active_web_contents, "enterFullscreen()"));
base::string16 expected_title = base::ASCIIToUTF16("fullscreen");
EXPECT_EQ(expected_title,
content::TitleWatcher(active_web_contents, expected_title)
.WaitAndGetTitle());
bool result = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
active_web_contents, "enterPictureInPicture();", &result));
EXPECT_TRUE(result);
EXPECT_FALSE(active_web_contents->IsFullscreen());
EXPECT_TRUE(window_controller()->GetWindowForTesting()->IsVisible());
}
IN_PROC_BROWSER_TEST_F(PictureInPictureWindowControllerBrowserTest,
EnterPictureInPictureThenNavigateAwayCloseWindow) {
GURL test_page_url = ui_test_utils::GetTestUrl(
base::FilePath(base::FilePath::kCurrentDirectory),
base::FilePath(kPictureInPictureWindowSizePage));
ui_test_utils::NavigateToURL(browser(), test_page_url);
content::WebContents* active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(active_web_contents);
SetUpWindowController(active_web_contents);
ASSERT_TRUE(window_controller());
bool result = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
active_web_contents, "enterPictureInPicture();", &result));
EXPECT_TRUE(result);
EXPECT_TRUE(window_controller()->GetWindowForTesting()->IsVisible());
// Same document navigations should not close Picture-in-Picture window.
EXPECT_TRUE(content::ExecuteScript(
active_web_contents, "window.location = '#foo'; window.history.back();"));
EXPECT_TRUE(window_controller()->GetWindowForTesting()->IsVisible());
// Picture-in-Picture window should be closed after navigating away.
GURL another_page_url = ui_test_utils::GetTestUrl(
base::FilePath(base::FilePath::kCurrentDirectory),
base::FilePath(
FILE_PATH_LITERAL("media/picture-in-picture/iframe-size.html")));
ui_test_utils::NavigateToURL(browser(), another_page_url);
EXPECT_FALSE(window_controller()->GetWindowForTesting()->IsVisible());
}
// Tests that when a new surface id is sent to the Picture-in-Picture window, it
// doesn't move back to its default position.
// crbug.com/1002489: disabled due to flakiness.
IN_PROC_BROWSER_TEST_F(PictureInPictureWindowControllerBrowserTest,
DISABLED_SurfaceIdChangeDoesNotMoveWindow) {
LoadTabAndEnterPictureInPicture(
browser(), base::FilePath(kPictureInPictureWindowSizePage));
content::WebContents* active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
OverlayWindowViews* overlay_window = static_cast<OverlayWindowViews*>(
window_controller()->GetWindowForTesting());
ASSERT_TRUE(overlay_window);
ASSERT_TRUE(overlay_window->IsVisible());
// Move and resize the window to the top left corner and wait for ack.
{
WidgetBoundsChangeWaiter waiter(overlay_window);
overlay_window->SetBounds(gfx::Rect(0, 0, 400, 400));
waiter.Wait();
}
// Wait for signal that the window was resized.
base::string16 expected_title = base::ASCIIToUTF16("resized");
EXPECT_EQ(expected_title,
content::TitleWatcher(active_web_contents, expected_title)
.WaitAndGetTitle());
// Simulate a new surface layer and a change in aspect ratio then wait for
// ack.
{
WidgetBoundsChangeWaiter waiter(overlay_window);
overlay_window->SetSurfaceId(viz::SurfaceId(
viz::FrameSinkId(1, 1),
viz::LocalSurfaceId(9, base::UnguessableToken::Create())));
overlay_window->UpdateVideoSize(gfx::Size(200, 100));
waiter.Wait();
}
// The position should be closer to the (0, 0) than the default one (bottom
// right corner). This will be reflected by checking that the position is
// below (100, 100).
EXPECT_LT(overlay_window->GetBounds().x(), 100);
EXPECT_LT(overlay_window->GetBounds().y(), 100);
}
// Tests that the Picture-in-Picture state is properly updated when the window
// is closed at a system level.
IN_PROC_BROWSER_TEST_F(PictureInPictureWindowControllerBrowserTest,
CloseWindowNotifiesController) {
LoadTabAndEnterPictureInPicture(
browser(), base::FilePath(kPictureInPictureWindowSizePage));
content::WebContents* active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
OverlayWindowViews* overlay_window = static_cast<OverlayWindowViews*>(
window_controller()->GetWindowForTesting());
ASSERT_TRUE(overlay_window);
ASSERT_TRUE(overlay_window->IsVisible());
// Simulate closing from the system.
overlay_window->OnNativeWidgetDestroyed();
ExpectLeavePictureInPicture(active_web_contents);
}
// Tests that the play/pause icon state is properly updated when a
// Picture-in-Picture is created after a reload.
IN_PROC_BROWSER_TEST_F(PictureInPictureWindowControllerBrowserTest,
PlayPauseStateAtCreation) {
LoadTabAndEnterPictureInPicture(
browser(), base::FilePath(kPictureInPictureWindowSizePage));
content::WebContents* active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(content::ExecuteScript(active_web_contents, "video.play();"));
bool is_paused = true;
EXPECT_TRUE(ExecuteScriptAndExtractBool(active_web_contents, "isPaused();",
&is_paused));
EXPECT_FALSE(is_paused);
EXPECT_TRUE(window_controller()->GetWindowForTesting()->IsVisible());
OverlayWindowViews* overlay_window = static_cast<OverlayWindowViews*>(
window_controller()->GetWindowForTesting());
EXPECT_EQ(overlay_window->playback_state_for_testing(),
OverlayWindowViews::PlaybackState::kPlaying);
EXPECT_TRUE(overlay_window->video_layer_for_testing()->visible());
ASSERT_TRUE(
content::ExecuteScript(active_web_contents, "exitPictureInPicture();"));
content::TestNavigationObserver observer(active_web_contents, 1);
chrome::Reload(browser(), WindowOpenDisposition::CURRENT_TAB);
observer.Wait();
{
content::WebContents* active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
bool result = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
active_web_contents, "enterPictureInPicture();", &result));
EXPECT_TRUE(result);
bool is_paused = false;
EXPECT_TRUE(ExecuteScriptAndExtractBool(active_web_contents, "isPaused();",
&is_paused));
EXPECT_TRUE(is_paused);
OverlayWindowViews* overlay_window = static_cast<OverlayWindowViews*>(
window_controller()->GetWindowForTesting());
EXPECT_EQ(overlay_window->playback_state_for_testing(),
OverlayWindowViews::PlaybackState::kPaused);
}
}
IN_PROC_BROWSER_TEST_F(PictureInPictureWindowControllerBrowserTest,
EnterUsingControllerShowsWindow) {
auto* pip_window_manager = PictureInPictureWindowManager::GetInstance();
ASSERT_NE(nullptr, pip_window_manager);
// Show the non-WebContents based Picture-in-Picture window controller.
EXPECT_CALL(mock_controller(), Show());
pip_window_manager->EnterPictureInPictureWithController(&mock_controller());
}
IN_PROC_BROWSER_TEST_F(PictureInPictureWindowControllerBrowserTest,
EnterUsingWebContentsThenUsingController) {
// Enter using WebContents.
LoadTabAndEnterPictureInPicture(
browser(), base::FilePath(kPictureInPictureWindowSizePage));
OverlayWindowViews* overlay_window = static_cast<OverlayWindowViews*>(
window_controller()->GetWindowForTesting());
ASSERT_NE(nullptr, overlay_window);
EXPECT_TRUE(overlay_window->IsVisible());
auto* pip_window_manager = PictureInPictureWindowManager::GetInstance();
ASSERT_NE(nullptr, pip_window_manager);
// The new Picture-in-Picture window should be shown.
EXPECT_CALL(mock_controller(), Show());
pip_window_manager->EnterPictureInPictureWithController(&mock_controller());
// WebContents sourced Picture-in-Picture should stop.
ExpectLeavePictureInPicture(
browser()->tab_strip_model()->GetActiveWebContents());
}
IN_PROC_BROWSER_TEST_F(PictureInPictureWindowControllerBrowserTest,
EnterUsingControllerThenEnterUsingWebContents) {
auto* pip_window_manager = PictureInPictureWindowManager::GetInstance();
ASSERT_NE(nullptr, pip_window_manager);
// Show the non-WebContents based Picture-in-Picture window controller.
EXPECT_CALL(mock_controller(), GetWebContents())
.WillOnce(testing::Return(nullptr));
EXPECT_CALL(mock_controller(), Show());
pip_window_manager->EnterPictureInPictureWithController(&mock_controller());
// Now show the WebContents based Picture-in-Picture window controller.
// This should close the existing window and show the new one.
EXPECT_CALL(mock_controller(), Close(_));
LoadTabAndEnterPictureInPicture(
browser(), base::FilePath(kPictureInPictureWindowSizePage));
OverlayWindowViews* overlay_window = static_cast<OverlayWindowViews*>(
window_controller()->GetWindowForTesting());
ASSERT_TRUE(overlay_window);
EXPECT_TRUE(overlay_window->IsVisible());
}
// This checks that a video in Picture-in-Picture with preload none, when
// changing source willproperly update the associated media player id. This is
// checked by closing the window because the test it at a too high level to be
// able to check the actual media player id being used.
IN_PROC_BROWSER_TEST_F(PictureInPictureWindowControllerBrowserTest,
PreloadNoneSrcChangeThenLoad) {
GURL test_page_url = ui_test_utils::GetTestUrl(
base::FilePath(base::FilePath::kCurrentDirectory),
base::FilePath(FILE_PATH_LITERAL(
"media/picture-in-picture/player_preload_none.html")));
ui_test_utils::NavigateToURL(browser(), test_page_url);
content::WebContents* active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(active_web_contents);
SetUpWindowController(active_web_contents);
bool result = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(active_web_contents,
"play();", &result));
ASSERT_TRUE(result);
result = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
active_web_contents, "enterPictureInPicture();", &result));
ASSERT_TRUE(result);
result = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
active_web_contents, "changeSrcAndLoad();", &result));
ASSERT_TRUE(result);
window_controller()->Close(true /* should_pause_video */);
// The video should leave Picture-in-Picture mode.
ExpectLeavePictureInPicture(active_web_contents);
}
// Tests that opening a Picture-in-Picture window from a video in an iframe
// will not lead to a crash when the tab is closed while devtools is open.
IN_PROC_BROWSER_TEST_F(PictureInPictureWindowControllerBrowserTest,
OpenInFrameWithDevToolsDoesNotCrash) {
GURL test_page_url = ui_test_utils::GetTestUrl(
base::FilePath(base::FilePath::kCurrentDirectory),
base::FilePath(
FILE_PATH_LITERAL("media/picture-in-picture/iframe-test.html")));
ui_test_utils::NavigateToURL(browser(), test_page_url);
content::WebContents* active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(active_web_contents != nullptr);
SetUpWindowController(active_web_contents);
std::vector<content::RenderFrameHost*> render_frame_hosts =
active_web_contents->GetAllFrames();
ASSERT_EQ(2u, render_frame_hosts.size());
content::RenderFrameHost* iframe =
render_frame_hosts[0] == active_web_contents->GetMainFrame()
? render_frame_hosts[1]
: render_frame_hosts[0];
// Wait for video metadata to load.
base::string16 expected_title = base::ASCIIToUTF16("loadedmetadata");
EXPECT_EQ(expected_title,
content::TitleWatcher(active_web_contents, expected_title)
.WaitAndGetTitle());
// Attaching devtools triggers the change in timing that leads to the crash.
DevToolsWindow* window = DevToolsWindowTesting::OpenDevToolsWindowSync(
browser(), true /*is_docked=*/);
{
bool result = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
iframe, "enterPictureInPicture();", &result));
EXPECT_TRUE(result);
}
EXPECT_TRUE(window_controller()->GetWindowForTesting()->IsVisible());
EXPECT_EQ(2u, active_web_contents->GetAllFrames().size());
// Open a new tab in the browser.
AddTabAtIndex(1, GURL("about:blank"), ui::PAGE_TRANSITION_TYPED);
ASSERT_TRUE(window_controller()->GetWindowForTesting()->IsVisible());
EXPECT_EQ(2, browser()->tab_strip_model()->count());
EXPECT_EQ(1, browser()->tab_strip_model()->active_index());
// Closing the initiator should not crash Chrome.
content::WebContentsDestroyedWatcher destroyed_watcher(active_web_contents);
browser()->tab_strip_model()->CloseWebContentsAt(0, 0);
destroyed_watcher.Wait();
// Make sure the window and therefore Chrome_DevToolsADBThread shutdown
// gracefully.
DevToolsWindowTesting::CloseDevToolsWindowSync(window);
}
#if defined(OS_CHROMEOS)
// Tests that video in Picture-in-Picture is paused when user presses
// VKEY_MEDIA_PLAY_PAUSE key even if there's another media playing in a
// foreground tab.
// TODO(https://crbug.com/1070810) flaky test
IN_PROC_BROWSER_TEST_F(PictureInPictureWindowControllerBrowserTest,
DISABLED_HandleMediaKeyPlayPause) {
GURL test_page_url = ui_test_utils::GetTestUrl(
base::FilePath(base::FilePath::kCurrentDirectory),
base::FilePath(kPictureInPictureWindowSizePage));
ui_test_utils::NavigateToURL(browser(), test_page_url);
content::WebContents* first_active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(first_active_web_contents);
EXPECT_TRUE(
content::ExecuteScript(first_active_web_contents, "video.play();"));
bool result = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
first_active_web_contents, "enterPictureInPicture();", &result));
EXPECT_TRUE(result);
Browser* second_browser = CreateBrowser(browser()->profile());
ui_test_utils::NavigateToURL(second_browser, test_page_url);
content::WebContents* second_active_web_contents =
second_browser->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(second_active_web_contents);
EXPECT_TRUE(
content::ExecuteScript(second_active_web_contents, "video.play();"));
ash::AcceleratorController::Get()->Process(
ui::Accelerator(ui::VKEY_MEDIA_PLAY_PAUSE, ui::EF_NONE));
base::RunLoop().RunUntilIdle();
bool is_paused = false;
// Picture-in-Picture video in first browser window is paused.
EXPECT_TRUE(ExecuteScriptAndExtractBool(first_active_web_contents,
"isPaused();", &is_paused));
EXPECT_TRUE(is_paused);
// Video in second browser window is not paused.
EXPECT_TRUE(ExecuteScriptAndExtractBool(second_active_web_contents,
"isPaused();", &is_paused));
EXPECT_FALSE(is_paused);
}
// Tests that the back-to-tab, close, and resize controls move properly as
// the window changes quadrants.
IN_PROC_BROWSER_TEST_F(PictureInPictureWindowControllerBrowserTest,
MovingQuadrantsMovesBackToTabAndResizeControls) {
GURL test_page_url = ui_test_utils::GetTestUrl(
base::FilePath(base::FilePath::kCurrentDirectory),
base::FilePath(kPictureInPictureWindowSizePage));
ui_test_utils::NavigateToURL(browser(), test_page_url);
content::WebContents* active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(active_web_contents);
SetUpWindowController(active_web_contents);
ASSERT_TRUE(window_controller());
content::OverlayWindow* overlay_window =
window_controller()->GetWindowForTesting();
ASSERT_TRUE(overlay_window);
ASSERT_FALSE(overlay_window->IsVisible());
bool result = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
active_web_contents, "enterPictureInPicture();", &result));
ASSERT_TRUE(result);
OverlayWindowViews* overlay_window_views =
static_cast<OverlayWindowViews*>(overlay_window);
// The PiP window starts in the bottom-right quadrant of the screen.
gfx::Rect bottom_right_bounds = overlay_window_views->GetBounds();
// The relative center point of the window.
gfx::Point center(bottom_right_bounds.width() / 2,
bottom_right_bounds.height() / 2);
gfx::Point close_button_position =
overlay_window_views->close_image_position_for_testing();
gfx::Point resize_button_position =
overlay_window_views->resize_handle_position_for_testing();
// The close button should be in the top right corner.
EXPECT_LT(center.x(), close_button_position.x());
EXPECT_GT(center.y(), close_button_position.y());
// The resize button should be in the top left corner.
EXPECT_GT(center.x(), resize_button_position.x());
EXPECT_GT(center.y(), resize_button_position.y());
// The resize button hit test should start a top left resizing drag.
EXPECT_EQ(HTTOPLEFT, overlay_window_views->GetResizeHTComponent());
// Move the window to the bottom left corner.
gfx::Rect bottom_left_bounds(0, bottom_right_bounds.y(),
bottom_right_bounds.width(),
bottom_right_bounds.height());
overlay_window_views->SetBounds(bottom_left_bounds);
close_button_position =
overlay_window_views->close_image_position_for_testing();
resize_button_position =
overlay_window_views->resize_handle_position_for_testing();
// The close button should be in the top left corner.
EXPECT_GT(center.x(), close_button_position.x());
EXPECT_GT(center.y(), close_button_position.y());
// The resize button should be in the top right corner.
EXPECT_LT(center.x(), resize_button_position.x());
EXPECT_GT(center.y(), resize_button_position.y());
// The resize button hit test should start a top right resizing drag.
EXPECT_EQ(HTTOPRIGHT, overlay_window_views->GetResizeHTComponent());
// Move the window to the top right corner.
gfx::Rect top_right_bounds(bottom_right_bounds.x(), 0,
bottom_right_bounds.width(),
bottom_right_bounds.height());
overlay_window_views->SetBounds(top_right_bounds);
close_button_position =
overlay_window_views->close_image_position_for_testing();
resize_button_position =
overlay_window_views->resize_handle_position_for_testing();
// The close button should be in the top right corner.
EXPECT_LT(center.x(), close_button_position.x());
EXPECT_GT(center.y(), close_button_position.y());
// The resize button should be in the bottom left corner.
EXPECT_GT(center.x(), resize_button_position.x());
EXPECT_LT(center.y(), resize_button_position.y());
// The resize button hit test should start a bottom left resizing drag.
EXPECT_EQ(HTBOTTOMLEFT, overlay_window_views->GetResizeHTComponent());
// Move the window to the top left corner.
gfx::Rect top_left_bounds(0, 0, bottom_right_bounds.width(),
bottom_right_bounds.height());
overlay_window_views->SetBounds(top_left_bounds);
close_button_position =
overlay_window_views->close_image_position_for_testing();
resize_button_position =
overlay_window_views->resize_handle_position_for_testing();
// The close button should be in the top right corner.
EXPECT_LT(center.x(), close_button_position.x());
EXPECT_GT(center.y(), close_button_position.y());
// The resize button should be in the bottom right corner.
EXPECT_LT(center.x(), resize_button_position.x());
EXPECT_LT(center.y(), resize_button_position.y());
// The resize button hit test should start a bottom right resizing drag.
EXPECT_EQ(HTBOTTOMRIGHT, overlay_window_views->GetResizeHTComponent());
}
#endif // defined(OS_CHROMEOS)
// Tests that the Play/Pause button is displayed appropriately in the
// Picture-in-Picture window.
IN_PROC_BROWSER_TEST_F(PictureInPictureWindowControllerBrowserTest,
PlayPauseButtonVisibility) {
LoadTabAndEnterPictureInPicture(
browser(), base::FilePath(kPictureInPictureWindowSizePage));
content::WebContents* active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_NE(nullptr, active_web_contents);
OverlayWindowViews* overlay_window = static_cast<OverlayWindowViews*>(
window_controller()->GetWindowForTesting());
ASSERT_TRUE(overlay_window);
// Play/Pause button is displayed if video is not a mediastream.
MoveMouseOver(overlay_window);
EXPECT_TRUE(
overlay_window->play_pause_controls_view_for_testing()->IsDrawn());
// Play/Pause button is hidden if video is a mediastream.
bool result = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
active_web_contents, "changeVideoSrcToMediaStream();", &result));
EXPECT_TRUE(result);
MoveMouseOver(overlay_window);
EXPECT_FALSE(
overlay_window->play_pause_controls_view_for_testing()->IsDrawn());
// Play/Pause button is not hidden anymore when video is not a mediastream.
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
active_web_contents, "changeVideoSrc();", &result));
EXPECT_TRUE(result);
MoveMouseOver(overlay_window);
EXPECT_TRUE(
overlay_window->play_pause_controls_view_for_testing()->IsDrawn());
}
// Check that page visibility API events are fired when tab is hidden, shown,
// and even occluded.
IN_PROC_BROWSER_TEST_F(PictureInPictureWindowControllerBrowserTest,
PageVisibilityEventsFired) {
GURL test_page_url = ui_test_utils::GetTestUrl(
base::FilePath(base::FilePath::kCurrentDirectory),
base::FilePath(kPictureInPictureWindowSizePage));
ui_test_utils::NavigateToURL(browser(), test_page_url);
content::WebContents* active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_NE(nullptr, active_web_contents);
ASSERT_TRUE(content::ExecuteScript(active_web_contents,
"addVisibilityChangeEventListener();"));
// Hide page and check that the document visibility is hidden.
active_web_contents->WasHidden();
base::string16 expected_title = base::ASCIIToUTF16("hidden");
EXPECT_EQ(expected_title,
content::TitleWatcher(active_web_contents, expected_title)
.WaitAndGetTitle());
// Show page and check that the document visibility is visible.
active_web_contents->WasShown();
expected_title = base::ASCIIToUTF16("visible");
EXPECT_EQ(expected_title,
content::TitleWatcher(active_web_contents, expected_title)
.WaitAndGetTitle());
// Occlude page and check that the document visibility is hidden.
active_web_contents->WasOccluded();
expected_title = base::ASCIIToUTF16("hidden");
EXPECT_EQ(expected_title,
content::TitleWatcher(active_web_contents, expected_title)
.WaitAndGetTitle());
}
// Check that page visibility API events are fired even when video is in
// Picture-in-Picture and video playback is not disrupted.
IN_PROC_BROWSER_TEST_F(PictureInPictureWindowControllerBrowserTest,
PageVisibilityEventsFiredWhenPictureInPicture) {
LoadTabAndEnterPictureInPicture(
browser(), base::FilePath(kPictureInPictureWindowSizePage));
content::WebContents* active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_NE(nullptr, active_web_contents);
ASSERT_TRUE(content::ExecuteScript(active_web_contents, "video.play();"));
bool is_paused = true;
ASSERT_TRUE(ExecuteScriptAndExtractBool(active_web_contents, "isPaused();",
&is_paused));
EXPECT_FALSE(is_paused);
ASSERT_TRUE(content::ExecuteScript(active_web_contents,
"addVisibilityChangeEventListener();"));
// Hide page and check that the document visibility is hidden.
active_web_contents->WasHidden();
base::string16 expected_title = base::ASCIIToUTF16("hidden");
EXPECT_EQ(expected_title,
content::TitleWatcher(active_web_contents, expected_title)
.WaitAndGetTitle());
// Check that the video is still in Picture-in-Picture and playing.
bool in_picture_in_picture = false;
ASSERT_TRUE(ExecuteScriptAndExtractBool(
active_web_contents, "isInPictureInPicture();", &in_picture_in_picture));
EXPECT_TRUE(in_picture_in_picture);
ASSERT_TRUE(ExecuteScriptAndExtractBool(active_web_contents, "isPaused();",
&is_paused));
EXPECT_FALSE(is_paused);
// Show page and check that the document visibility is visible.
active_web_contents->WasShown();
expected_title = base::ASCIIToUTF16("visible");
EXPECT_EQ(expected_title,
content::TitleWatcher(active_web_contents, expected_title)
.WaitAndGetTitle());
// Check that the video is still in Picture-in-Picture and playing.
ASSERT_TRUE(ExecuteScriptAndExtractBool(
active_web_contents, "isInPictureInPicture();", &in_picture_in_picture));
EXPECT_TRUE(in_picture_in_picture);
ASSERT_TRUE(ExecuteScriptAndExtractBool(active_web_contents, "isPaused();",
&is_paused));
EXPECT_FALSE(is_paused);
// Occlude page and check that the document visibility is hidden.
active_web_contents->WasOccluded();
expected_title = base::ASCIIToUTF16("hidden");
EXPECT_EQ(expected_title,
content::TitleWatcher(active_web_contents, expected_title)
.WaitAndGetTitle());
// Check that the video is still in Picture-in-Picture and playing.
ASSERT_TRUE(ExecuteScriptAndExtractBool(
active_web_contents, "isInPictureInPicture();", &in_picture_in_picture));
EXPECT_TRUE(in_picture_in_picture);
ASSERT_TRUE(ExecuteScriptAndExtractBool(active_web_contents, "isPaused();",
&is_paused));
EXPECT_FALSE(is_paused);
}
class MediaSessionPictureInPictureWindowControllerBrowserTest
: public PictureInPictureWindowControllerBrowserTest {
public:
void SetUpCommandLine(base::CommandLine* command_line) override {
PictureInPictureWindowControllerBrowserTest::SetUpCommandLine(command_line);
command_line->AppendSwitchASCII(switches::kEnableBlinkFeatures,
"MediaSession,SkipAd");
scoped_feature_list_.InitWithFeatures(
{media_session::features::kMediaSessionService}, {});
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
// Tests that a Skip Ad button is displayed in the Picture-in-Picture window
// when Media Session Action "skipad" is handled by the website.
IN_PROC_BROWSER_TEST_F(MediaSessionPictureInPictureWindowControllerBrowserTest,
SkipAdButtonVisibility) {
LoadTabAndEnterPictureInPicture(
browser(), base::FilePath(kPictureInPictureWindowSizePage));
OverlayWindowViews* overlay_window = static_cast<OverlayWindowViews*>(
window_controller()->GetWindowForTesting());
ASSERT_TRUE(overlay_window);
// Skip Ad button is not displayed initially when mouse is hovering over the
// window.
MoveMouseOver(overlay_window);
EXPECT_FALSE(
overlay_window->skip_ad_controls_view_for_testing()->layer()->visible());
content::WebContents* active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
// Skip Ad button is not displayed if video is not playing even if mouse is
// hovering over the window and media session action handler has been set.
ASSERT_TRUE(content::ExecuteScript(
active_web_contents, "setMediaSessionActionHandler('skipad');"));
base::RunLoop().RunUntilIdle();
MoveMouseOver(overlay_window);
EXPECT_FALSE(
overlay_window->skip_ad_controls_view_for_testing()->layer()->visible());
// Play video and check that Skip Ad button is now displayed when
// video plays and mouse is hovering over the window.
ASSERT_TRUE(content::ExecuteScript(active_web_contents, "video.play();"));
base::RunLoop().RunUntilIdle();
MoveMouseOver(overlay_window);
EXPECT_TRUE(
overlay_window->skip_ad_controls_view_for_testing()->layer()->visible());
// Unset action handler and check that Skip Ad button is not displayed when
// video plays and mouse is hovering over the window.
ASSERT_TRUE(content::ExecuteScript(
active_web_contents, "unsetMediaSessionActionHandler('skipad');"));
base::RunLoop().RunUntilIdle();
MoveMouseOver(overlay_window);
EXPECT_FALSE(
overlay_window->skip_ad_controls_view_for_testing()->layer()->visible());
}
// Tests that the Play/Plause button is displayed in the Picture-in-Picture
// window when Media Session actions "play" and "pause" are handled by the
// website even if video is a media stream.
IN_PROC_BROWSER_TEST_F(MediaSessionPictureInPictureWindowControllerBrowserTest,
PlayPauseButtonVisibility) {
LoadTabAndEnterPictureInPicture(
browser(), base::FilePath(kPictureInPictureWindowSizePage));
OverlayWindowViews* overlay_window = static_cast<OverlayWindowViews*>(
window_controller()->GetWindowForTesting());
ASSERT_TRUE(overlay_window);
content::WebContents* active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
// Play/Pause button is hidden if playing video is a mediastream and mouse is
// hovering over the window.
bool result = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
active_web_contents, "changeVideoSrcToMediaStream();", &result));
EXPECT_TRUE(result);
base::RunLoop().RunUntilIdle();
MoveMouseOver(overlay_window);
EXPECT_FALSE(
overlay_window->play_pause_controls_view_for_testing()->IsDrawn());
// Play second video (non-muted) so that Media Session becomes active.
ASSERT_TRUE(
content::ExecuteScript(active_web_contents, "secondVideo.play();"));
// Set Media Session action "play" handler and check that Play/Pause button
// is still hidden when mouse is hovering over the window.
ASSERT_TRUE(content::ExecuteScript(active_web_contents,
"setMediaSessionActionHandler('play');"));
base::RunLoop().RunUntilIdle();
MoveMouseOver(overlay_window);
EXPECT_FALSE(
overlay_window->play_pause_controls_view_for_testing()->IsDrawn());
// Set Media Session action "pause" handler and check that Play/Pause button
// is now displayed when mouse is hovering over the window.
ASSERT_TRUE(content::ExecuteScript(active_web_contents,
"setMediaSessionActionHandler('pause');"));
base::RunLoop().RunUntilIdle();
MoveMouseOver(overlay_window);
EXPECT_TRUE(
overlay_window->play_pause_controls_view_for_testing()->IsDrawn());
// Unset Media Session action "pause" handler and check that Play/Pause button
// is hidden when mouse is hovering over the window.
ASSERT_TRUE(content::ExecuteScript(
active_web_contents, "unsetMediaSessionActionHandler('pause');"));
base::RunLoop().RunUntilIdle();
MoveMouseOver(overlay_window);
EXPECT_FALSE(
overlay_window->play_pause_controls_view_for_testing()->IsDrawn());
ASSERT_TRUE(
content::ExecuteScript(active_web_contents, "exitPictureInPicture();"));
// Reset Media Session action "pause" handler and check that Play/Pause button
// is now displayed when mouse is hovering over the window when it enters
// Picture-in-Picture again.
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(content::ExecuteScript(active_web_contents,
"setMediaSessionActionHandler('pause');"));
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
active_web_contents, "enterPictureInPicture();", &result));
EXPECT_TRUE(result);
base::RunLoop().RunUntilIdle();
MoveMouseOver(overlay_window);
EXPECT_TRUE(
overlay_window->play_pause_controls_view_for_testing()->IsDrawn());
}
// Tests that a Next Track button is displayed in the Picture-in-Picture window
// when Media Session Action "nexttrack" is handled by the website.
IN_PROC_BROWSER_TEST_F(MediaSessionPictureInPictureWindowControllerBrowserTest,
NextTrackButtonVisibility) {
LoadTabAndEnterPictureInPicture(
browser(), base::FilePath(kPictureInPictureWindowSizePage));
OverlayWindowViews* overlay_window = static_cast<OverlayWindowViews*>(
window_controller()->GetWindowForTesting());
ASSERT_TRUE(overlay_window);
// Next Track button is not displayed initially when mouse is hovering over
// the window.
MoveMouseOver(overlay_window);
EXPECT_FALSE(
overlay_window->next_track_controls_view_for_testing()->IsDrawn());
content::WebContents* active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
// Next Track button is not displayed if video is not playing even if mouse is
// hovering over the window and media session action handler has been set.
ASSERT_TRUE(content::ExecuteScript(
active_web_contents, "setMediaSessionActionHandler('nexttrack');"));
base::RunLoop().RunUntilIdle();
MoveMouseOver(overlay_window);
EXPECT_FALSE(
overlay_window->next_track_controls_view_for_testing()->IsDrawn());
// Play video and check that Next Track button is now displayed when
// video plays and mouse is hovering over the window.
ASSERT_TRUE(content::ExecuteScript(active_web_contents, "video.play();"));
base::RunLoop().RunUntilIdle();
MoveMouseOver(overlay_window);
EXPECT_TRUE(
overlay_window->next_track_controls_view_for_testing()->IsDrawn());
gfx::Rect next_track_bounds =
overlay_window->next_track_controls_view_for_testing()
->GetBoundsInScreen();
// Unset action handler and check that Next Track button is not displayed when
// video plays and mouse is hovering over the window.
ASSERT_TRUE(content::ExecuteScript(
active_web_contents, "unsetMediaSessionActionHandler('nexttrack');"));
base::RunLoop().RunUntilIdle();
MoveMouseOver(overlay_window);
EXPECT_FALSE(
overlay_window->next_track_controls_view_for_testing()->IsDrawn());
// Next Track button is still at the same previous location.
EXPECT_EQ(next_track_bounds,
overlay_window->next_track_controls_view_for_testing()
->GetBoundsInScreen());
}
// Tests that Next Track button bounds are updated right away when
// Picture-in-Picture window controls are hidden.
IN_PROC_BROWSER_TEST_F(MediaSessionPictureInPictureWindowControllerBrowserTest,
NextTrackButtonBounds) {
LoadTabAndEnterPictureInPicture(
browser(), base::FilePath(kPictureInPictureWindowSizePage));
OverlayWindowViews* overlay_window = static_cast<OverlayWindowViews*>(
window_controller()->GetWindowForTesting());
ASSERT_TRUE(overlay_window);
content::WebContents* active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
gfx::Rect next_track_bounds =
overlay_window->next_track_controls_view_for_testing()
->GetBoundsInScreen();
ASSERT_TRUE(content::ExecuteScript(
active_web_contents, "setMediaSessionActionHandler('nexttrack');"));
ASSERT_TRUE(content::ExecuteScript(active_web_contents, "video.play();"));
base::RunLoop().RunUntilIdle();
EXPECT_NE(next_track_bounds,
overlay_window->next_track_controls_view_for_testing()
->GetBoundsInScreen());
}
// Tests that a Previous Track button is displayed in the Picture-in-Picture
// window when Media Session Action "previoustrack" is handled by the website.
IN_PROC_BROWSER_TEST_F(MediaSessionPictureInPictureWindowControllerBrowserTest,
PreviousTrackButtonVisibility) {
LoadTabAndEnterPictureInPicture(
browser(), base::FilePath(kPictureInPictureWindowSizePage));
OverlayWindowViews* overlay_window = static_cast<OverlayWindowViews*>(
window_controller()->GetWindowForTesting());
ASSERT_TRUE(overlay_window);
// Previous Track button is not displayed initially when mouse is hovering
// over the window.
MoveMouseOver(overlay_window);
EXPECT_FALSE(
overlay_window->previous_track_controls_view_for_testing()->IsDrawn());
content::WebContents* active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
// Previous Track button is not displayed if video is not playing even if
// mouse is hovering over the window and media session action handler has been
// set.
ASSERT_TRUE(content::ExecuteScript(
active_web_contents, "setMediaSessionActionHandler('previoustrack');"));
base::RunLoop().RunUntilIdle();
MoveMouseOver(overlay_window);
EXPECT_FALSE(
overlay_window->previous_track_controls_view_for_testing()->IsDrawn());
// Play video and check that Previous Track button is now displayed when
// video plays and mouse is hovering over the window.
ASSERT_TRUE(content::ExecuteScript(active_web_contents, "video.play();"));
base::RunLoop().RunUntilIdle();
MoveMouseOver(overlay_window);
EXPECT_TRUE(
overlay_window->previous_track_controls_view_for_testing()->IsDrawn());
gfx::Rect previous_track_bounds =
overlay_window->previous_track_controls_view_for_testing()
->GetBoundsInScreen();
// Unset action handler and check that Previous Track button is not displayed
// when video plays and mouse is hovering over the window.
ASSERT_TRUE(content::ExecuteScript(
active_web_contents, "unsetMediaSessionActionHandler('previoustrack');"));
base::RunLoop().RunUntilIdle();
MoveMouseOver(overlay_window);
EXPECT_FALSE(
overlay_window->previous_track_controls_view_for_testing()->IsDrawn());
// Previous Track button is still at the same previous location.
EXPECT_EQ(previous_track_bounds,
overlay_window->previous_track_controls_view_for_testing()
->GetBoundsInScreen());
}
// Tests that Previous Track button bounds are updated right away when
// Picture-in-Picture window controls are hidden.
IN_PROC_BROWSER_TEST_F(MediaSessionPictureInPictureWindowControllerBrowserTest,
PreviousTrackButtonBounds) {
LoadTabAndEnterPictureInPicture(
browser(), base::FilePath(kPictureInPictureWindowSizePage));
OverlayWindowViews* overlay_window = static_cast<OverlayWindowViews*>(
window_controller()->GetWindowForTesting());
ASSERT_TRUE(overlay_window);
content::WebContents* active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
gfx::Rect previous_track_bounds =
overlay_window->previous_track_controls_view_for_testing()
->GetBoundsInScreen();
ASSERT_TRUE(content::ExecuteScript(
active_web_contents, "setMediaSessionActionHandler('previoustrack');"));
ASSERT_TRUE(content::ExecuteScript(active_web_contents, "video.play();"));
base::RunLoop().RunUntilIdle();
EXPECT_NE(previous_track_bounds,
overlay_window->previous_track_controls_view_for_testing()
->GetBoundsInScreen());
}
// Tests that clicking the Skip Ad button in the Picture-in-Picture window
// calls the Media Session Action "skipad" handler function.
IN_PROC_BROWSER_TEST_F(MediaSessionPictureInPictureWindowControllerBrowserTest,
SkipAdHandlerCalled) {
LoadTabAndEnterPictureInPicture(
browser(), base::FilePath(kPictureInPictureWindowSizePage));
content::WebContents* active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(content::ExecuteScript(active_web_contents, "video.play();"));
ASSERT_TRUE(content::ExecuteScript(
active_web_contents, "setMediaSessionActionHandler('skipad');"));
base::RunLoop().RunUntilIdle();
// Simulates user clicking "Skip Ad" and check the handler function is called.
window_controller()->SkipAd();
base::string16 expected_title = base::ASCIIToUTF16("skipad");
EXPECT_EQ(expected_title,
content::TitleWatcher(active_web_contents, expected_title)
.WaitAndGetTitle());
}
// Tests that clicking the Play/Pause button in the Picture-in-Picture window
// calls the Media Session actions "play" and "pause" handler functions.
IN_PROC_BROWSER_TEST_F(MediaSessionPictureInPictureWindowControllerBrowserTest,
PlayPauseHandlersCalled) {
LoadTabAndEnterPictureInPicture(
browser(), base::FilePath(kPictureInPictureWindowSizePage));
content::WebContents* active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(content::ExecuteScript(active_web_contents, "video.play();"));
ASSERT_TRUE(content::ExecuteScript(active_web_contents,
"setMediaSessionActionHandler('play');"));
ASSERT_TRUE(content::ExecuteScript(active_web_contents,
"setMediaSessionActionHandler('pause');"));
base::RunLoop().RunUntilIdle();
// Simulates user clicking "Play/Pause" and check that the "pause" handler
// function is called.
window_controller()->TogglePlayPause();
base::string16 expected_title = base::ASCIIToUTF16("pause");
EXPECT_EQ(expected_title,
content::TitleWatcher(active_web_contents, expected_title)
.WaitAndGetTitle());
EXPECT_TRUE(content::ExecuteScript(active_web_contents, "video.pause();"));
// Simulates user clicking "Play/Pause" and check that the "play" handler
// function is called.
window_controller()->TogglePlayPause();
expected_title = base::ASCIIToUTF16("play");
EXPECT_EQ(expected_title,
content::TitleWatcher(active_web_contents, expected_title)
.WaitAndGetTitle());
}
// Tests that clicking the Next Track button in the Picture-in-Picture window
// calls the Media Session Action "nexttrack" handler function.
IN_PROC_BROWSER_TEST_F(MediaSessionPictureInPictureWindowControllerBrowserTest,
NextTrackHandlerCalled) {
LoadTabAndEnterPictureInPicture(
browser(), base::FilePath(kPictureInPictureWindowSizePage));
content::WebContents* active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(content::ExecuteScript(active_web_contents, "video.play();"));
ASSERT_TRUE(content::ExecuteScript(
active_web_contents, "setMediaSessionActionHandler('nexttrack');"));
base::RunLoop().RunUntilIdle();
// Simulates user clicking "Next Track" and check the handler function is
// called.
window_controller()->NextTrack();
base::string16 expected_title = base::ASCIIToUTF16("nexttrack");
EXPECT_EQ(expected_title,
content::TitleWatcher(active_web_contents, expected_title)
.WaitAndGetTitle());
}
// Tests that clicking the Previous Track button in the Picture-in-Picture
// window calls the Media Session Action "previoustrack" handler function.
IN_PROC_BROWSER_TEST_F(MediaSessionPictureInPictureWindowControllerBrowserTest,
PreviousTrackHandlerCalled) {
LoadTabAndEnterPictureInPicture(
browser(), base::FilePath(kPictureInPictureWindowSizePage));
content::WebContents* active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(content::ExecuteScript(active_web_contents, "video.play();"));
ASSERT_TRUE(content::ExecuteScript(
active_web_contents, "setMediaSessionActionHandler('previoustrack');"));
base::RunLoop().RunUntilIdle();
// Simulates user clicking "Previous Track" and check the handler function is
// called.
window_controller()->PreviousTrack();
base::string16 expected_title = base::ASCIIToUTF16("previoustrack");
EXPECT_EQ(expected_title,
content::TitleWatcher(active_web_contents, expected_title)
.WaitAndGetTitle());
}
// Tests that stopping Media Sessions closes the Picture-in-Picture window.
IN_PROC_BROWSER_TEST_F(MediaSessionPictureInPictureWindowControllerBrowserTest,
StopMediaSessionClosesPictureInPictureWindow) {
LoadTabAndEnterPictureInPicture(
browser(), base::FilePath(kPictureInPictureWindowSizePage));
content::WebContents* active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(content::ExecuteScript(active_web_contents, "video.play();"));
base::RunLoop().RunUntilIdle();
content::MediaSession::Get(active_web_contents)
->Stop(content::MediaSession::SuspendType::kUI);
base::string16 expected_title = base::ASCIIToUTF16("leavepictureinpicture");
EXPECT_EQ(expected_title,
content::TitleWatcher(active_web_contents, expected_title)
.WaitAndGetTitle());
}
class AutoPictureInPictureWindowControllerBrowserTest
: public PictureInPictureWindowControllerBrowserTest {
public:
void SetUpCommandLine(base::CommandLine* command_line) override {
PictureInPictureWindowControllerBrowserTest::SetUpCommandLine(command_line);
command_line->AppendSwitchASCII(switches::kEnableBlinkFeatures,
"AutoPictureInPicture");
}
};
// Hide page and check that entering Auto Picture-in-Picture is not triggered.
// This test is most likely going to be flaky the day the tested thing fails.
// Do NOT disable test. Ping /chrome/browser/picture_in_picture/OWNERS instead.
IN_PROC_BROWSER_TEST_F(AutoPictureInPictureWindowControllerBrowserTest,
AutoEnterPictureInPictureIsNotTriggeredInRegularWebApp) {
GURL test_page_url = ui_test_utils::GetTestUrl(
base::FilePath(base::FilePath::kCurrentDirectory),
base::FilePath(kPictureInPictureWindowSizePage));
ui_test_utils::NavigateToURL(browser(), test_page_url);
content::WebContents* active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_NE(nullptr, active_web_contents);
ASSERT_TRUE(content::ExecuteScript(active_web_contents, "video.play();"));
ASSERT_TRUE(content::ExecuteScript(active_web_contents,
"video.autoPictureInPicture = true;"));
ASSERT_TRUE(content::ExecuteScript(active_web_contents,
"addVisibilityChangeEventListener();"));
// Hide page and check that there is no video that enters Picture-in-Picture
// automatically.
active_web_contents->WasHidden();
base::string16 expected_title = base::ASCIIToUTF16("hidden");
EXPECT_EQ(expected_title,
content::TitleWatcher(active_web_contents, expected_title)
.WaitAndGetTitle());
bool in_picture_in_picture = false;
ASSERT_TRUE(ExecuteScriptAndExtractBool(
active_web_contents, "isInPictureInPicture();", &in_picture_in_picture));
EXPECT_FALSE(in_picture_in_picture);
}
// Show page and check that exiting Auto Picture-in-Picture is triggered.
// This test is most likely going to be flaky the day the tested thing fails.
// Do NOT disable test. Ping /chrome/browser/picture_in_picture/OWNERS instead.
IN_PROC_BROWSER_TEST_F(AutoPictureInPictureWindowControllerBrowserTest,
AutoExitPictureInPictureIsTriggeredInRegularWebApp) {
GURL test_page_url = ui_test_utils::GetTestUrl(
base::FilePath(base::FilePath::kCurrentDirectory),
base::FilePath(kPictureInPictureWindowSizePage));
ui_test_utils::NavigateToURL(browser(), test_page_url);
content::WebContents* active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_NE(nullptr, active_web_contents);
ASSERT_TRUE(content::ExecuteScript(active_web_contents, "video.play();"));
ASSERT_TRUE(content::ExecuteScript(active_web_contents,
"video.autoPictureInPicture = true;"));
ASSERT_TRUE(content::ExecuteScript(active_web_contents,
"addVisibilityChangeEventListener();"));
// Hide page.
active_web_contents->WasHidden();
base::string16 expected_title = base::ASCIIToUTF16("hidden");
EXPECT_EQ(expected_title,
content::TitleWatcher(active_web_contents, expected_title)
.WaitAndGetTitle());
// Enter Picture-in-Picture manually.
bool result = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
active_web_contents, "enterPictureInPicture();", &result));
EXPECT_TRUE(result);
// Show page and check that video left Picture-in-Picture automatically.
active_web_contents->WasShown();
expected_title = base::ASCIIToUTF16("visible");
EXPECT_EQ(expected_title,
content::TitleWatcher(active_web_contents, expected_title)
.WaitAndGetTitle());
bool in_picture_in_picture = false;
ASSERT_TRUE(ExecuteScriptAndExtractBool(
active_web_contents, "isInPictureInPicture();", &in_picture_in_picture));
EXPECT_FALSE(in_picture_in_picture);
}
// Show/hide fullscreen page and check that Auto Picture-in-Picture is
// triggered.
// Crashes on Mac only. http://crbug.com/1058087
#if defined(OS_MAC)
#define MAYBE_AutoPictureInPictureTriggeredWhenFullscreen \
DISABLED_AutoPictureInPictureTriggeredWhenFullscreen
#else
#define MAYBE_AutoPictureInPictureTriggeredWhenFullscreen \
AutoPictureInPictureTriggeredWhenFullscreen
#endif
IN_PROC_BROWSER_TEST_F(AutoPictureInPictureWindowControllerBrowserTest,
MAYBE_AutoPictureInPictureTriggeredWhenFullscreen) {
GURL test_page_url = ui_test_utils::GetTestUrl(
base::FilePath(base::FilePath::kCurrentDirectory),
base::FilePath(kPictureInPictureWindowSizePage));
ui_test_utils::NavigateToURL(browser(), test_page_url);
content::WebContents* active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_NE(nullptr, active_web_contents);
ASSERT_TRUE(content::ExecuteScript(active_web_contents, "enterFullscreen()"));
base::string16 expected_title = base::ASCIIToUTF16("fullscreen");
EXPECT_EQ(expected_title,
content::TitleWatcher(active_web_contents, expected_title)
.WaitAndGetTitle());
EXPECT_TRUE(content::ExecuteScript(active_web_contents,
"addPictureInPictureEventListeners();"));
EXPECT_TRUE(content::ExecuteScript(active_web_contents, "video.play();"));
ASSERT_TRUE(content::ExecuteScript(active_web_contents,
"video.autoPictureInPicture = true;"));
SetUpWindowController(active_web_contents);
EXPECT_FALSE(window_controller()->GetWindowForTesting()->IsVisible());
// Hide page and check that video entered Picture-in-Picture automatically.
active_web_contents->WasHidden();
expected_title = base::ASCIIToUTF16("enterpictureinpicture");
EXPECT_EQ(expected_title,
content::TitleWatcher(active_web_contents, expected_title)
.WaitAndGetTitle());
EXPECT_TRUE(window_controller()->GetWindowForTesting()->IsVisible());
// Show page and check that video left Picture-in-Picture automatically.
active_web_contents->WasShown();
expected_title = base::ASCIIToUTF16("leavepictureinpicture");
EXPECT_EQ(expected_title,
content::TitleWatcher(active_web_contents, expected_title)
.WaitAndGetTitle());
EXPECT_FALSE(window_controller()->GetWindowForTesting()->IsVisible());
}
namespace {
class ChromeContentBrowserClientOverrideWebAppScope
: public ChromeContentBrowserClient {
public:
ChromeContentBrowserClientOverrideWebAppScope() = default;
~ChromeContentBrowserClientOverrideWebAppScope() override = default;
void OverrideWebkitPrefs(
content::RenderViewHost* rvh,
blink::web_pref::WebPreferences* web_prefs) override {
ChromeContentBrowserClient::OverrideWebkitPrefs(rvh, web_prefs);
web_prefs->web_app_scope = web_app_scope_;
}
void set_web_app_scope(const GURL& web_app_scope) {
web_app_scope_ = web_app_scope;
}
private:
GURL web_app_scope_;
};
} // namespace
class WebAppPictureInPictureWindowControllerBrowserTest
: public web_app::WebAppControllerBrowserTest {
public:
WebAppPictureInPictureWindowControllerBrowserTest() = default;
~WebAppPictureInPictureWindowControllerBrowserTest() override = default;
void SetUpCommandLine(base::CommandLine* command_line) override {
command_line->AppendSwitch(
switches::kEnableExperimentalWebPlatformFeatures);
web_app::WebAppControllerBrowserTest::SetUpCommandLine(command_line);
}
GURL main_url() {
return https_server()->GetURL(
"/extensions/auto_picture_in_picture/main.html");
}
Browser* InstallAndLaunchPWA(const GURL& start_url) {
auto web_app_info = std::make_unique<WebApplicationInfo>();
web_app_info->start_url = start_url;
web_app_info->scope = start_url.GetOrigin();
web_app_info->open_as_window = true;
const web_app::AppId app_id = InstallWebApp(std::move(web_app_info));
Browser* app_browser = LaunchWebAppBrowserAndWait(app_id);
web_contents_ = app_browser->tab_strip_model()->GetActiveWebContents();
EXPECT_TRUE(content::WaitForLoadStop(web_contents_));
return app_browser;
}
content::WebContents* web_contents() { return web_contents_; }
private:
content::WebContents* web_contents_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(WebAppPictureInPictureWindowControllerBrowserTest);
};
// Show/hide pwa page and check that Auto Picture-in-Picture is triggered.
IN_PROC_BROWSER_TEST_P(WebAppPictureInPictureWindowControllerBrowserTest,
AutoPictureInPicture) {
InstallAndLaunchPWA(main_url());
bool result = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(web_contents(),
"playVideo();", &result));
ASSERT_TRUE(result);
ASSERT_TRUE(content::ExecuteScript(web_contents(),
"video.autoPictureInPicture = true;"));
// Hide page and check that video entered Picture-in-Picture automatically.
web_contents()->WasHidden();
base::string16 expected_title =
base::ASCIIToUTF16("video.enterpictureinpicture");
EXPECT_EQ(
expected_title,
content::TitleWatcher(web_contents(), expected_title).WaitAndGetTitle());
// Show page and check that video left Picture-in-Picture automatically.
web_contents()->WasShown();
expected_title = base::ASCIIToUTF16("video.leavepictureinpicture");
EXPECT_EQ(
expected_title,
content::TitleWatcher(web_contents(), expected_title).WaitAndGetTitle());
}
// Show pwa page and check that Auto Picture-in-Picture is not triggered if
// document is not inside the scope specified in the Web App Manifest.
IN_PROC_BROWSER_TEST_P(
WebAppPictureInPictureWindowControllerBrowserTest,
AutoPictureInPictureNotTriggeredIfDocumentNotInWebAppScope) {
// We open a web app with a different scope
// Then go to our usual test page.
Browser* app_browser = InstallAndLaunchPWA(
https_server()->GetURL("www.foobar.com", "/web_apps/basic.html"));
web_app::NavigateToURLAndWait(app_browser, main_url());
EXPECT_TRUE(app_browser->app_controller()->ShouldShowCustomTabBar());
bool result = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(web_contents(),
"playVideo();", &result));
ASSERT_TRUE(result);
ASSERT_TRUE(content::ExecuteScript(web_contents(),
"video.autoPictureInPicture = true;"));
// Hide page and check that the video did not entered
// Picture-in-Picture automatically.
web_contents()->WasHidden();
base::string16 expected_title = base::ASCIIToUTF16("hidden");
EXPECT_EQ(
expected_title,
content::TitleWatcher(web_contents(), expected_title).WaitAndGetTitle());
bool in_picture_in_picture = false;
ASSERT_TRUE(ExecuteScriptAndExtractBool(
web_contents(), "isInPictureInPicture();", &in_picture_in_picture));
EXPECT_FALSE(in_picture_in_picture);
}
// Show pwa page and check that Auto Picture-in-Picture is not triggered if
// video is not playing.
IN_PROC_BROWSER_TEST_P(WebAppPictureInPictureWindowControllerBrowserTest,
AutoPictureInPictureNotTriggeredIfVideoNotPlaying) {
InstallAndLaunchPWA(main_url());
ASSERT_TRUE(content::ExecuteScript(web_contents(),
"video.autoPictureInPicture = true;"));
bool is_paused = false;
EXPECT_TRUE(
ExecuteScriptAndExtractBool(web_contents(), "isPaused();", &is_paused));
EXPECT_TRUE(is_paused);
// Hide page and check that the video did not entered
// Picture-in-Picture automatically.
web_contents()->WasHidden();
base::string16 expected_title = base::ASCIIToUTF16("hidden");
EXPECT_EQ(
expected_title,
content::TitleWatcher(web_contents(), expected_title).WaitAndGetTitle());
bool in_picture_in_picture = false;
ASSERT_TRUE(ExecuteScriptAndExtractBool(
web_contents(), "isInPictureInPicture();", &in_picture_in_picture));
EXPECT_FALSE(in_picture_in_picture);
}
// Check that Auto Picture-in-Picture is not triggered if there's already a
// video in Picture-in-Picture.
IN_PROC_BROWSER_TEST_P(
WebAppPictureInPictureWindowControllerBrowserTest,
AutoPictureInPictureWhenPictureInPictureWindowAlreadyVisible) {
InstallAndLaunchPWA(main_url());
// Enter Picture-in-Picture for the first video and set Auto
// Picture-in-Picture for the second video.
bool result = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(web_contents(),
"playVideo();", &result));
ASSERT_TRUE(result);
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
web_contents(), "enterPictureInPicture();", &result));
EXPECT_TRUE(result);
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
web_contents(), "playSecondVideo();", &result));
ASSERT_TRUE(result);
ASSERT_TRUE(content::ExecuteScript(
web_contents(), "secondVideo.autoPictureInPicture = true;"));
// Hide page and check that the second video did not entered
// Picture-in-Picture automatically.
web_contents()->WasHidden();
base::string16 expected_title = base::ASCIIToUTF16("hidden");
EXPECT_EQ(
expected_title,
content::TitleWatcher(web_contents(), expected_title).WaitAndGetTitle());
// Check that the first video is still in Picture-in-Picture.
bool in_picture_in_picture = false;
ASSERT_TRUE(ExecuteScriptAndExtractBool(
web_contents(), "isInPictureInPicture();", &in_picture_in_picture));
EXPECT_TRUE(in_picture_in_picture);
}
// Check that video does not leave Picture-in-Picture automatically when it
// doesn't have the Auto Picture-in-Picture attribute set.
IN_PROC_BROWSER_TEST_P(
WebAppPictureInPictureWindowControllerBrowserTest,
AutoPictureInPictureNotTriggeredOnPageShownIfNoAttribute) {
InstallAndLaunchPWA(main_url());
bool result = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(web_contents(),
"playVideo();", &result));
ASSERT_TRUE(result);
ASSERT_TRUE(content::ExecuteScript(web_contents(),
"video.autoPictureInPicture = true;"));
// Hide page and check that video entered Picture-in-Picture automatically.
web_contents()->WasHidden();
base::string16 expected_title =
base::ASCIIToUTF16("video.enterpictureinpicture");
EXPECT_EQ(
expected_title,
content::TitleWatcher(web_contents(), expected_title).WaitAndGetTitle());
ASSERT_TRUE(content::ExecuteScript(web_contents(),
"video.autoPictureInPicture = false;"));
// Show page and check that video did not leave Picture-in-Picture
// automatically as it doesn't have the Auto Picture-in-Picture attribute set
// anymore.
web_contents()->WasShown();
expected_title = base::ASCIIToUTF16("visible");
EXPECT_EQ(
expected_title,
content::TitleWatcher(web_contents(), expected_title).WaitAndGetTitle());
// Check that the video is still in Picture-in-Picture.
bool in_picture_in_picture = false;
ASSERT_TRUE(ExecuteScriptAndExtractBool(
web_contents(), "isInPictureInPicture();", &in_picture_in_picture));
EXPECT_TRUE(in_picture_in_picture);
}
// TODO(http://crbug/1001249): flaky.
// Check that Auto Picture-in-Picture applies only to the video element whose
// autoPictureInPicture attribute was set most recently
IN_PROC_BROWSER_TEST_P(WebAppPictureInPictureWindowControllerBrowserTest,
DISABLED_AutoPictureInPictureAttributeApplies) {
InstallAndLaunchPWA(main_url());
bool result = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(web_contents(),
"playVideo();", &result));
ASSERT_TRUE(result);
ASSERT_TRUE(content::ExecuteScript(web_contents(),
"video.autoPictureInPicture = true;"));
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
web_contents(), "playSecondVideo();", &result));
ASSERT_TRUE(result);
ASSERT_TRUE(content::ExecuteScript(
web_contents(), "secondVideo.autoPictureInPicture = true;"));
// Hide page and check that second video is the video that enters
// Picture-in-Picture automatically.
web_contents()->WasHidden();
base::string16 expected_title =
base::ASCIIToUTF16("secondVideo.enterpictureinpicture");
EXPECT_EQ(
expected_title,
content::TitleWatcher(web_contents(), expected_title).WaitAndGetTitle());
// Show page and unset Auto Picture-in-Picture attribute on second video.
web_contents()->WasShown();
expected_title = base::ASCIIToUTF16("visible");
EXPECT_EQ(
expected_title,
content::TitleWatcher(web_contents(), expected_title).WaitAndGetTitle());
ASSERT_TRUE(content::ExecuteScript(
web_contents(), "secondVideo.autoPictureInPicture = false;"));
// Hide page and check that first video is the video that enters
// Picture-in-Picture automatically.
web_contents()->WasHidden();
expected_title = base::ASCIIToUTF16("video.enterpictureinpicture");
EXPECT_EQ(
expected_title,
content::TitleWatcher(web_contents(), expected_title).WaitAndGetTitle());
// Show page and unset Auto Picture-in-Picture attribute on first video.
web_contents()->WasShown();
expected_title = base::ASCIIToUTF16("visible");
EXPECT_EQ(
expected_title,
content::TitleWatcher(web_contents(), expected_title).WaitAndGetTitle());
ASSERT_TRUE(content::ExecuteScript(web_contents(),
"video.autoPictureInPicture = false;"));
// Hide page and check that there is no video that enters Picture-in-Picture
// automatically.
web_contents()->WasHidden();
expected_title = base::ASCIIToUTF16("hidden");
EXPECT_EQ(
expected_title,
content::TitleWatcher(web_contents(), expected_title).WaitAndGetTitle());
// Show page and append a video with Auto Picture-in-Picture attribute.
web_contents()->WasShown();
expected_title = base::ASCIIToUTF16("visible");
EXPECT_EQ(
expected_title,
content::TitleWatcher(web_contents(), expected_title).WaitAndGetTitle());
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
web_contents(), "addHtmlVideoWithAutoPictureInPicture();", &result));
ASSERT_TRUE(result);
// Hide page and check that the html video is the video that enters
// Picture-in-Picture automatically.
web_contents()->WasHidden();
expected_title = base::ASCIIToUTF16("htmlVideo.enterpictureinpicture");
EXPECT_EQ(
expected_title,
content::TitleWatcher(web_contents(), expected_title).WaitAndGetTitle());
}
// Check that video does not leave Picture-in-Picture automatically when it
// not the most recent element with the Auto Picture-in-Picture attribute set.
IN_PROC_BROWSER_TEST_P(
WebAppPictureInPictureWindowControllerBrowserTest,
AutoPictureInPictureNotTriggeredOnPageShownIfNotEnteredAutoPictureInPicture) {
InstallAndLaunchPWA(main_url());
bool result = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(web_contents(),
"playVideo();", &result));
ASSERT_TRUE(result);
ASSERT_TRUE(content::ExecuteScript(web_contents(),
"video.autoPictureInPicture = true;"));
// Hide page and check that video entered Picture-in-Picture automatically.
web_contents()->WasHidden();
base::string16 expected_title =
base::ASCIIToUTF16("video.enterpictureinpicture");
EXPECT_EQ(
expected_title,
content::TitleWatcher(web_contents(), expected_title).WaitAndGetTitle());
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
web_contents(), "playSecondVideo();", &result));
ASSERT_TRUE(result);
ASSERT_TRUE(content::ExecuteScript(
web_contents(), "secondVideo.autoPictureInPicture = true;"));
// Show page and check that video did not leave Picture-in-Picture
// automatically as it's not the most recent element with the Auto
// Picture-in-Picture attribute set anymore.
web_contents()->WasShown();
expected_title = base::ASCIIToUTF16("visible");
EXPECT_EQ(
expected_title,
content::TitleWatcher(web_contents(), expected_title).WaitAndGetTitle());
// Check that the video is still in Picture-in-Picture.
bool in_picture_in_picture = false;
ASSERT_TRUE(ExecuteScriptAndExtractBool(
web_contents(), "isInPictureInPicture();", &in_picture_in_picture));
EXPECT_TRUE(in_picture_in_picture);
}
// Check that video with no audio that is paused when hidden is still eligible
// to enter Auto Picture-in-Picture and resumes playback.
IN_PROC_BROWSER_TEST_P(
WebAppPictureInPictureWindowControllerBrowserTest,
AutoPictureInPictureTriggeredOnPageHiddenIfVideoPausedWhenHidden) {
InstallAndLaunchPWA(main_url());
bool result = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
web_contents(), "changeVideoSrcToNoAudioTrackVideo();", &result));
EXPECT_TRUE(result);
ASSERT_TRUE(content::ExecuteScript(web_contents(),
"video.autoPictureInPicture = true;"));
// Hide page and check that video entered Picture-in-Picture automatically
// and is playing.
web_contents()->WasHidden();
base::string16 expected_title =
base::ASCIIToUTF16("video.enterpictureinpicture");
EXPECT_EQ(
expected_title,
content::TitleWatcher(web_contents(), expected_title).WaitAndGetTitle());
// Check that video playback is still playing.
bool is_paused = false;
EXPECT_TRUE(
ExecuteScriptAndExtractBool(web_contents(), "isPaused();", &is_paused));
EXPECT_FALSE(is_paused);
}
// Check that video with no audio that is paused when hidden resumes playback
// when it enters Picture-in-Picture.
IN_PROC_BROWSER_TEST_F(PictureInPictureWindowControllerBrowserTest,
VideoWithNoAudioPausedWhenHiddenResumesPlayback) {
GURL test_page_url = ui_test_utils::GetTestUrl(
base::FilePath(base::FilePath::kCurrentDirectory),
base::FilePath(kPictureInPictureWindowSizePage));
ui_test_utils::NavigateToURL(browser(), test_page_url);
content::WebContents* active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_NE(nullptr, active_web_contents);
bool result = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
active_web_contents, "changeVideoSrcToNoAudioTrackVideo();", &result));
EXPECT_TRUE(result);
ASSERT_TRUE(
content::ExecuteScript(active_web_contents, "addPauseEventListener();"));
// Hide page and check that the video is paused first.
active_web_contents->WasHidden();
base::string16 expected_title = base::ASCIIToUTF16("pause");
EXPECT_EQ(expected_title,
content::TitleWatcher(active_web_contents, expected_title)
.WaitAndGetTitle());
ASSERT_TRUE(
content::ExecuteScript(active_web_contents, "addPlayEventListener();"));
// Enter Picture-in-Picture.
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
active_web_contents, "enterPictureInPicture();", &result));
EXPECT_TRUE(result);
// Check that video playback has resumed.
expected_title = base::ASCIIToUTF16("play");
EXPECT_EQ(expected_title,
content::TitleWatcher(active_web_contents, expected_title)
.WaitAndGetTitle());
}
// Tests that exiting Picture-in-Picture when the video has no source fires the
// event and resolves the callback.
IN_PROC_BROWSER_TEST_F(PictureInPictureWindowControllerBrowserTest,
ExitFireEventAndCallbackWhenNoSource) {
LoadTabAndEnterPictureInPicture(
browser(), base::FilePath(kPictureInPictureWindowSizePage));
content::WebContents* active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(content::ExecuteScript(active_web_contents,
"video.src=''; exitPictureInPicture();"));
// 'left' is sent when the first video leaves Picture-in-Picture.
base::string16 expected_title = base::ASCIIToUTF16("leavepictureinpicture");
EXPECT_EQ(expected_title,
content::TitleWatcher(active_web_contents, expected_title)
.WaitAndGetTitle());
}
// Tests that when closing the window after the player was reset, the <video>
// element is still notified.
IN_PROC_BROWSER_TEST_F(PictureInPictureWindowControllerBrowserTest,
ResetPlayerCloseWindowNotifiesElement) {
LoadTabAndEnterPictureInPicture(
browser(), base::FilePath(kPictureInPictureWindowSizePage));
content::WebContents* active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
// Video should be in Picture-in-Picture.
{
bool in_picture_in_picture = false;
ASSERT_TRUE(ExecuteScriptAndExtractBool(active_web_contents,
"isInPictureInPicture();",
&in_picture_in_picture));
EXPECT_TRUE(in_picture_in_picture);
}
// Reset video source and wait for the notification.
ASSERT_TRUE(content::ExecuteScript(active_web_contents, "resetVideo();"));
base::string16 expected_title = base::ASCIIToUTF16("emptied");
EXPECT_EQ(expected_title,
content::TitleWatcher(active_web_contents, expected_title)
.WaitAndGetTitle());
window_controller()->Close(true /* should_pause_video */);
// Video should no longer be in Picture-in-Picture.
ExpectLeavePictureInPicture(active_web_contents);
}
// TODO(crbug.com/1002489): Test is flaky on Linux.
#if defined(OS_LINUX) || defined(OS_CHROMEOS)
#define MAYBE_UpdateMaxSize DISABLED_UpdateMaxSize
#else
#define MAYBE_UpdateMaxSize UpdateMaxSize
#endif
IN_PROC_BROWSER_TEST_F(PictureInPictureWindowControllerBrowserTest,
MAYBE_UpdateMaxSize) {
LoadTabAndEnterPictureInPicture(
browser(), base::FilePath(kPictureInPictureWindowSizePage));
content::WebContents* active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_NE(nullptr, active_web_contents);
OverlayWindowViews* overlay_window = static_cast<OverlayWindowViews*>(
window_controller()->GetWindowForTesting());
ASSERT_TRUE(overlay_window);
// Size should be half the work area.
gfx::Size window_size(100, 100);
window_size = overlay_window->UpdateMaxSize(gfx::Rect(100, 100), window_size);
EXPECT_EQ(gfx::Size(50, 50), window_size);
EXPECT_EQ(gfx::Size(50, 50), overlay_window->GetMaximumSize());
// If the max size increases then we should keep the existing window size.
window_size = overlay_window->UpdateMaxSize(gfx::Rect(200, 200), window_size);
EXPECT_EQ(gfx::Size(50, 50), window_size);
EXPECT_EQ(gfx::Size(100, 100), overlay_window->GetMaximumSize());
// If the max size decreases then we should shrink to fit.
window_size = overlay_window->UpdateMaxSize(gfx::Rect(50, 50), window_size);
EXPECT_EQ(gfx::Size(25, 25), window_size);
EXPECT_EQ(gfx::Size(25, 25), overlay_window->GetMaximumSize());
}
// Tests that play/pause video playback is toggled if there are no focus
// afforfances on the Picture-in-Picture window buttons when user hits space
// keyboard key.
IN_PROC_BROWSER_TEST_F(PictureInPictureWindowControllerBrowserTest,
SpaceKeyTogglePlayPause) {
LoadTabAndEnterPictureInPicture(
browser(), base::FilePath(kPictureInPictureWindowSizePage));
content::WebContents* active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(content::ExecuteScript(active_web_contents, "video.play();"));
ASSERT_TRUE(
content::ExecuteScript(active_web_contents, "addPauseEventListener();"));
OverlayWindowViews* overlay_window = static_cast<OverlayWindowViews*>(
window_controller()->GetWindowForTesting());
ASSERT_TRUE(overlay_window);
ASSERT_FALSE(overlay_window->GetFocusManager()->GetFocusedView());
ui::KeyEvent space_key_pressed(ui::ET_KEY_PRESSED, ui::VKEY_SPACE,
ui::DomCode::SPACE, ui::EF_NONE);
overlay_window->OnKeyEvent(&space_key_pressed);
base::string16 expected_title = base::ASCIIToUTF16("pause");
EXPECT_EQ(expected_title,
content::TitleWatcher(active_web_contents, expected_title)
.WaitAndGetTitle());
}
INSTANTIATE_TEST_SUITE_P(All,
WebAppPictureInPictureWindowControllerBrowserTest,
::testing::Values(ProviderType::kBookmarkApps,
ProviderType::kWebApps),
web_app::ProviderTypeParamToString);