Commit 060c450b authored by Eliot Courtney's avatar Eliot Courtney Committed by Commit Bot

[pip] Let PictureInPictureWindowManager enter PIP without a WebContents.

Bug: 841886
Bug: b/69370942
Test: browser_tests
Change-Id: I2e054dfb2e78032cecceb55c8998f0ee64259cc8
Reviewed-on: https://chromium-review.googlesource.com/1156202
Commit-Queue: Eliot Courtney <edcourtney@chromium.org>
Reviewed-by: default avatarMounir Lamouri <mlamouri@chromium.org>
Cr-Commit-Position: refs/heads/master@{#581846}
parent 3bfb6808
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "chrome/browser/picture_in_picture/picture_in_picture_window_manager.h"
#include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_commands.h" #include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/browser/ui/tabs/tab_strip_model.h"
...@@ -19,6 +20,7 @@ ...@@ -19,6 +20,7 @@
#include "content/public/test/browser_test_utils.h" #include "content/public/test/browser_test_utils.h"
#include "content/public/test/test_navigation_observer.h" #include "content/public/test/test_navigation_observer.h"
#include "net/dns/mock_host_resolver.h" #include "net/dns/mock_host_resolver.h"
#include "testing/gmock/include/gmock/gmock.h"
#if !defined(OS_ANDROID) #if !defined(OS_ANDROID)
#include "chrome/browser/ui/views/overlay/overlay_window_views.h" #include "chrome/browser/ui/views/overlay/overlay_window_views.h"
...@@ -26,6 +28,34 @@ ...@@ -26,6 +28,34 @@
#include "ui/views/widget/widget_observer.h" #include "ui/views/widget/widget_observer.h"
#endif #endif
using ::testing::_;
namespace {
class MockPictureInPictureWindowController
: public content::PictureInPictureWindowController {
public:
MockPictureInPictureWindowController() = default;
// PictureInPictureWindowController:
MOCK_METHOD0(Show, gfx::Size());
MOCK_METHOD1(Close, void(bool));
MOCK_METHOD0(OnWindowDestroyed, void());
MOCK_METHOD1(ClickCustomControl, void(const std::string&));
MOCK_METHOD2(EmbedSurface, void(const viz::SurfaceId&, const gfx::Size&));
MOCK_METHOD0(GetWindowForTesting, content::OverlayWindow*());
MOCK_METHOD0(UpdateLayerBounds, void());
MOCK_METHOD0(IsPlayerActive, bool());
MOCK_METHOD0(GetInitiatorWebContents, content::WebContents*());
MOCK_METHOD2(UpdatePlaybackState, void(bool, bool));
MOCK_METHOD0(TogglePlayPause, bool());
private:
DISALLOW_COPY_AND_ASSIGN(MockPictureInPictureWindowController);
};
} // namespace
class PictureInPictureWindowControllerBrowserTest class PictureInPictureWindowControllerBrowserTest
: public InProcessBrowserTest { : public InProcessBrowserTest {
public: public:
...@@ -47,6 +77,10 @@ class PictureInPictureWindowControllerBrowserTest ...@@ -47,6 +77,10 @@ class PictureInPictureWindowControllerBrowserTest
return pip_window_controller_; return pip_window_controller_;
} }
MockPictureInPictureWindowController& mock_controller() {
return mock_controller_;
}
void LoadTabAndEnterPictureInPicture(Browser* browser) { void LoadTabAndEnterPictureInPicture(Browser* browser) {
GURL test_page_url = ui_test_utils::GetTestUrl( GURL test_page_url = ui_test_utils::GetTestUrl(
base::FilePath(base::FilePath::kCurrentDirectory), base::FilePath(base::FilePath::kCurrentDirectory),
...@@ -95,6 +129,7 @@ class PictureInPictureWindowControllerBrowserTest ...@@ -95,6 +129,7 @@ class PictureInPictureWindowControllerBrowserTest
private: private:
content::PictureInPictureWindowController* pip_window_controller_ = nullptr; content::PictureInPictureWindowController* pip_window_controller_ = nullptr;
MockPictureInPictureWindowController mock_controller_;
DISALLOW_COPY_AND_ASSIGN(PictureInPictureWindowControllerBrowserTest); DISALLOW_COPY_AND_ASSIGN(PictureInPictureWindowControllerBrowserTest);
}; };
...@@ -1003,6 +1038,68 @@ IN_PROC_BROWSER_TEST_F(PictureInPictureWindowControllerBrowserTest, ...@@ -1003,6 +1038,68 @@ IN_PROC_BROWSER_TEST_F(PictureInPictureWindowControllerBrowserTest,
} }
} }
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());
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.
bool in_picture_in_picture = false;
content::WebContents* active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
EXPECT_TRUE(ExecuteScriptAndExtractBool(
active_web_contents, "isInPictureInPicture();", &in_picture_in_picture));
EXPECT_FALSE(in_picture_in_picture);
// TODO(edcourtney): When the renderer process is destroyed, it calls into
// MediaWebContentsObserver::ExitPictureInPictureInternal which Closes the
// current PIP. However, this may not be a WebContents sourced PIP, so this
// close can be spurious.
EXPECT_CALL(mock_controller(), Close(_));
}
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(), 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());
OverlayWindowViews* overlay_window = static_cast<OverlayWindowViews*>(
window_controller()->GetWindowForTesting());
ASSERT_TRUE(overlay_window);
EXPECT_TRUE(overlay_window->IsVisible());
}
#endif // !defined(OS_ANDROID) #endif // !defined(OS_ANDROID)
// This checks that a video in Picture-in-Picture with preload none, when // This checks that a video in Picture-in-Picture with preload none, when
......
...@@ -40,6 +40,18 @@ PictureInPictureWindowManager* PictureInPictureWindowManager::GetInstance() { ...@@ -40,6 +40,18 @@ PictureInPictureWindowManager* PictureInPictureWindowManager::GetInstance() {
return base::Singleton<PictureInPictureWindowManager>::get(); return base::Singleton<PictureInPictureWindowManager>::get();
} }
void PictureInPictureWindowManager::EnterPictureInPictureWithController(
content::PictureInPictureWindowController* pip_window_controller) {
// If there was already a controller, close the existing window before
// creating the next one.
if (pip_window_controller_)
CloseWindowInternal();
pip_window_controller_ = pip_window_controller;
pip_window_controller_->Show();
}
gfx::Size PictureInPictureWindowManager::EnterPictureInPicture( gfx::Size PictureInPictureWindowManager::EnterPictureInPicture(
content::WebContents* web_contents, content::WebContents* web_contents,
const viz::SurfaceId& surface_id, const viz::SurfaceId& surface_id,
...@@ -49,9 +61,11 @@ gfx::Size PictureInPictureWindowManager::EnterPictureInPicture( ...@@ -49,9 +61,11 @@ gfx::Size PictureInPictureWindowManager::EnterPictureInPicture(
if (pip_window_controller_) if (pip_window_controller_)
CloseWindowInternal(); CloseWindowInternal();
// Create or update |pip_window_controller_| for the current WebContents. // Create or update |pip_window_controller_| for the current WebContents, if
// it is a WebContents based PIP.
if (!pip_window_controller_ || if (!pip_window_controller_ ||
pip_window_controller_->GetInitiatorWebContents() != web_contents) { (pip_window_controller_->GetInitiatorWebContents() != nullptr &&
pip_window_controller_->GetInitiatorWebContents() != web_contents)) {
CreateWindowInternal(web_contents); CreateWindowInternal(web_contents);
} }
...@@ -73,7 +87,6 @@ void PictureInPictureWindowManager::CreateWindowInternal( ...@@ -73,7 +87,6 @@ void PictureInPictureWindowManager::CreateWindowInternal(
} }
void PictureInPictureWindowManager::CloseWindowInternal() { void PictureInPictureWindowManager::CloseWindowInternal() {
DCHECK(contents_observer_);
DCHECK(pip_window_controller_); DCHECK(pip_window_controller_);
contents_observer_.reset(); contents_observer_.reset();
......
...@@ -29,6 +29,11 @@ class PictureInPictureWindowManager { ...@@ -29,6 +29,11 @@ class PictureInPictureWindowManager {
// Returns the singleton instance. // Returns the singleton instance.
static PictureInPictureWindowManager* GetInstance(); static PictureInPictureWindowManager* GetInstance();
// Some PIP windows (e.g. from ARC) may not have a WebContents as the source
// of the PIP content. This function lets them provide their own window
// controller directly.
void EnterPictureInPictureWithController(
content::PictureInPictureWindowController* pip_window_controller);
gfx::Size EnterPictureInPicture(content::WebContents*, gfx::Size EnterPictureInPicture(content::WebContents*,
const viz::SurfaceId&, const viz::SurfaceId&,
const gfx::Size&); const gfx::Size&);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment