blob: 81f00a36fed66920656c8f95dfdb68864fc29e7a [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/strings/utf_string_conversions.h"
#include "base/test/scoped_feature_list.h"
#include "build/build_config.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/viz/common/surfaces/surface_id.h"
#include "content/public/browser/overlay_window.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/browser_test_utils.h"
#include "media/base/media_switches.h"
#include "net/dns/mock_host_resolver.h"
#if !defined(OS_ANDROID)
#include "chrome/browser/ui/views/overlay/overlay_window_views.h"
#include "ui/views/widget/widget_observer.h"
#endif
class PictureInPictureWindowControllerBrowserTest
: public InProcessBrowserTest {
public:
PictureInPictureWindowControllerBrowserTest() {
feature_list_.InitWithFeatures(
{media::kPictureInPicture, media::kUseSurfaceLayerForVideo}, {});
}
void SetUpCommandLine(base::CommandLine* command_line) override {
InProcessBrowserTest::SetUpCommandLine(command_line);
command_line->AppendSwitch(
switches::kEnableExperimentalWebPlatformFeatures);
}
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_;
}
void LoadTabAndEnterPictureInPicture(Browser* browser) {
GURL test_page_url = ui_test_utils::GetTestUrl(
base::FilePath(base::FilePath::kCurrentDirectory),
base::FilePath(
FILE_PATH_LITERAL("media/picture-in-picture/window-size.html")));
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);
}
#if !defined(OS_ANDROID)
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_;
};
#endif // !defined(OS_ANDROID)
private:
content::PictureInPictureWindowController* pip_window_controller_ = nullptr;
base::test::ScopedFeatureList feature_list_;
DISALLOW_COPY_AND_ASSIGN(PictureInPictureWindowControllerBrowserTest);
};
// Checks the creation of the window controller, as well as basic window
// creation and visibility.
IN_PROC_BROWSER_TEST_F(PictureInPictureWindowControllerBrowserTest,
CreationAndVisibility) {
GURL test_page_url = ui_test_utils::GetTestUrl(
base::FilePath(base::FilePath::kCurrentDirectory),
base::FilePath(
FILE_PATH_LITERAL("media/picture-in-picture/window-size.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);
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());
}
// 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(
FILE_PATH_LITERAL("media/picture-in-picture/window-size.html")));
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());
}
#if !defined(OS_ANDROID)
// 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(
FILE_PATH_LITERAL("media/picture-in-picture/window-size.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);
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());
}
#endif // !defined(OS_ANDROID)
// 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(
FILE_PATH_LITERAL("media/picture-in-picture/window-size.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);
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("left");
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(
FILE_PATH_LITERAL("media/picture-in-picture/window-size.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);
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("left");
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.
IN_PROC_BROWSER_TEST_F(PictureInPictureWindowControllerBrowserTest,
CloseWindowFromWebAPIWhilePlaying) {
GURL test_page_url = ui_test_utils::GetTestUrl(
base::FilePath(base::FilePath::kCurrentDirectory),
base::FilePath(
FILE_PATH_LITERAL("media/picture-in-picture/window-size.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);
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("left");
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(
FILE_PATH_LITERAL("media/picture-in-picture/window-size.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);
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("left");
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(
FILE_PATH_LITERAL("media/picture-in-picture/window-size.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);
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();"));
// 'left' is sent when the first video leaves Picture-in-Picture.
base::string16 expected_title = base::ASCIIToUTF16("left");
EXPECT_EQ(expected_title,
content::TitleWatcher(active_web_contents, expected_title)
.WaitAndGetTitle());
in_picture_in_picture = false;
EXPECT_TRUE(ExecuteScriptAndExtractBool(
active_web_contents, "isInPictureInPicture();", &in_picture_in_picture));
EXPECT_FALSE(in_picture_in_picture);
bool is_paused = false;
EXPECT_TRUE(ExecuteScriptAndExtractBool(active_web_contents, "isPaused();",
&is_paused));
EXPECT_FALSE(is_paused);
}
// 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) {
GURL test_page_url = ui_test_utils::GetTestUrl(
base::FilePath(base::FilePath::kCurrentDirectory),
base::FilePath(
FILE_PATH_LITERAL("media/picture-in-picture/window-size.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);
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(window_controller()->GetWindowForTesting()->IsVisible());
EXPECT_TRUE(content::ExecuteScript(active_web_contents, "video.src = null;"));
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());
}
// 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(
FILE_PATH_LITERAL("media/picture-in-picture/window-size.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);
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("left");
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(
FILE_PATH_LITERAL("media/picture-in-picture/window-size.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);
{
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());
}
// 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(
FILE_PATH_LITERAL("media/picture-in-picture/window-size.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);
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());
content::PictureInPictureWindowController* first_controller =
window_controller();
EXPECT_TRUE(first_controller->GetWindowForTesting()->IsVisible());
Browser* second_browser = CreateBrowser(browser()->profile());
LoadTabAndEnterPictureInPicture(second_browser);
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());
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->IsFullscreenForCurrentTab());
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(
FILE_PATH_LITERAL("media/picture-in-picture/window-size.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);
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->IsFullscreenForCurrentTab());
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(
FILE_PATH_LITERAL("media/picture-in-picture/window-size.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);
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());
}
#if !defined(OS_ANDROID)
// TODO(mlamouri): enable this tests on other platforms when aspect ratio is
// implemented.
#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
// Tests that when a new surface id is sent to the Picture-in-Picture window, it
// doesn't move back to its default position.
// TODO(https://crbug.com/862505): test is currently flaky.
IN_PROC_BROWSER_TEST_F(PictureInPictureWindowControllerBrowserTest,
DISABLED_SurfaceIdChangeDoesNotMoveWindow) {
LoadTabAndEnterPictureInPicture(browser());
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 and wait for ack.
{
WidgetBoundsChangeWaiter waiter(overlay_window);
window_controller()->EmbedSurface(
viz::SurfaceId(
viz::FrameSinkId(1, 1),
viz::LocalSurfaceId(9, base::UnguessableToken::Create())),
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);
}
#endif // defined(OS_LINUX) && !defined(OS_CHROMEOS)
#endif // !defined(OS_ANDROID)