| // Copyright 2020 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <memory> |
| #include <vector> |
| #include "build/build_config.h" |
| |
| #include "base/path_service.h" |
| #include "content/browser/media/media_browsertest.h" |
| #include "content/browser/media/media_web_contents_observer.h" |
| #include "content/browser/renderer_host/render_view_host_impl.h" |
| #include "content/public/browser/web_contents_observer.h" |
| #include "content/public/common/content_paths.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/content_browser_test.h" |
| #include "content/public/test/content_browser_test_utils.h" |
| #include "content/shell/browser/shell.h" |
| |
| namespace content { |
| |
| namespace { |
| |
| enum class FullscreenTestEvent { |
| kEnterFullscreen, // Some element in the tab goes fullscreen |
| kLeaveFullscreen, // Tab's element stopped being fullscreen |
| kEffectivelyFullscreen, // <video> gets effective fullscreen flag set. |
| kNotEffectivelyFullscreen, // <video> gets effective fullscreen flag unset. |
| kInvalidEvent = 0xBADF00D |
| }; |
| |
| class FullscreenEventsRecorder : public WebContentsObserver { |
| public: |
| explicit FullscreenEventsRecorder(WebContents* web_contents) |
| : WebContentsObserver(web_contents) {} |
| |
| FullscreenEventsRecorder(const FullscreenEventsRecorder&) = delete; |
| FullscreenEventsRecorder& operator=(const FullscreenEventsRecorder&) = delete; |
| |
| void MediaEffectivelyFullscreenChanged(bool value) override { |
| AddEvent(value ? FullscreenTestEvent::kEffectivelyFullscreen |
| : FullscreenTestEvent::kNotEffectivelyFullscreen); |
| } |
| |
| void DidToggleFullscreenModeForTab(bool entered_fullscreen, |
| bool will_cause_resize) override { |
| AddEvent(entered_fullscreen ? FullscreenTestEvent::kEnterFullscreen |
| : FullscreenTestEvent::kLeaveFullscreen); |
| } |
| |
| void Wait(size_t events_count) { |
| if (events_.size() >= events_count) |
| return; |
| expected_event_count_ = events_count; |
| run_loop_ = std::make_unique<base::RunLoop>(); |
| run_loop_->Run(); |
| } |
| |
| FullscreenTestEvent event(size_t index) { |
| if (index >= events_.size()) |
| return FullscreenTestEvent::kInvalidEvent; |
| return events_[index]; |
| } |
| |
| private: |
| std::unique_ptr<base::RunLoop> run_loop_; |
| size_t expected_event_count_ = 0; |
| FullscreenTestEvent last_event_ = FullscreenTestEvent::kInvalidEvent; |
| std::vector<FullscreenTestEvent> events_; |
| |
| void AddEvent(FullscreenTestEvent e) { |
| if (last_event_ == e) |
| return; |
| events_.push_back(e); |
| last_event_ = e; |
| if (events_.size() == expected_event_count_ && run_loop_) |
| run_loop_->Quit(); |
| } |
| }; |
| |
| } // namespace |
| |
| class FullscreenDetectionTest : public ContentBrowserTest { |
| public: |
| void SetUpOnMainThread() override { |
| ContentBrowserTest::SetUpOnMainThread(); |
| |
| base::FilePath test_data_dir; |
| ASSERT_TRUE(base::PathService::Get(content::DIR_TEST_DATA, &test_data_dir)); |
| embedded_test_server()->ServeFilesFromDirectory(test_data_dir); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| } |
| }; |
| |
| // The most basic test possible and most obvious case of fullscreeness. |
| // A <video> itself goes fullscreen, and it's marked as effectively-fullscreen. |
| // Once the <video> exits fullscreen mode, it's not effectively-fullscreen |
| // any more. |
| IN_PROC_BROWSER_TEST_F(FullscreenDetectionTest, RegularVideoTagFullscreen) { |
| auto* web_contents = shell()->web_contents(); |
| EXPECT_TRUE(NavigateToURL( |
| shell(), embedded_test_server()->GetURL("/media/fullscreen.html"))); |
| |
| FullscreenEventsRecorder recorder(web_contents); |
| ASSERT_TRUE(content::ExecJs(web_contents, "makeFullscreen('small_video')")); |
| recorder.Wait(2); |
| EXPECT_EQ(recorder.event(0), FullscreenTestEvent::kEnterFullscreen); |
| EXPECT_EQ(recorder.event(1), FullscreenTestEvent::kEffectivelyFullscreen); |
| |
| EXPECT_TRUE(content::ExecJs(web_contents, "exitFullscreen()")); |
| recorder.Wait(4); |
| EXPECT_EQ(recorder.event(2), FullscreenTestEvent::kLeaveFullscreen); |
| EXPECT_EQ(recorder.event(3), FullscreenTestEvent::kNotEffectivelyFullscreen); |
| } |
| |
| // A <div>, containing a big <video>, goes fullscreen. |
| // This div effectively become the viewport. |
| // Since the <video> occupies most of the <div>, and thus most of the screen, |
| // it's marked as effectively-fullscreen. |
| // Once the <div> exits fullscreen mode, <video> is not effectively-fullscreen |
| // any more. |
| IN_PROC_BROWSER_TEST_F(FullscreenDetectionTest, EncompassingDivFullscreen) { |
| auto* web_contents = shell()->web_contents(); |
| EXPECT_TRUE(NavigateToURL( |
| shell(), embedded_test_server()->GetURL("/media/fullscreen.html"))); |
| |
| FullscreenEventsRecorder recorder(web_contents); |
| |
| ASSERT_TRUE(content::ExecJs(web_contents, "makeFullscreen('big_div')")); |
| |
| recorder.Wait(2); |
| EXPECT_EQ(recorder.event(0), FullscreenTestEvent::kEnterFullscreen); |
| EXPECT_EQ(recorder.event(1), FullscreenTestEvent::kEffectivelyFullscreen); |
| |
| ASSERT_TRUE(content::ExecJs(web_contents, "exitFullscreen()")); |
| recorder.Wait(4); |
| EXPECT_EQ(recorder.event(2), FullscreenTestEvent::kLeaveFullscreen); |
| EXPECT_EQ(recorder.event(3), FullscreenTestEvent::kNotEffectivelyFullscreen); |
| } |
| |
| // A <div>, containing a <video>, goes fullscreen. |
| // But the <video> occupies a small part of <div> (and viewport), and |
| // it is not enough to be counted as effectively-fullscreen. |
| IN_PROC_BROWSER_TEST_F(FullscreenDetectionTest, EncompassingDivNotFullscreen) { |
| auto* web_contents = shell()->web_contents(); |
| EXPECT_TRUE(NavigateToURL( |
| shell(), embedded_test_server()->GetURL("/media/fullscreen.html"))); |
| |
| FullscreenEventsRecorder recorder(web_contents); |
| |
| ASSERT_TRUE(content::ExecJs(web_contents, "makeFullscreen('small_div')")); |
| |
| recorder.Wait(1); |
| EXPECT_EQ(recorder.event(0), FullscreenTestEvent::kEnterFullscreen); |
| |
| ASSERT_TRUE(content::ExecJs(web_contents, "exitFullscreen()")); |
| recorder.Wait(2); |
| EXPECT_EQ(recorder.event(1), FullscreenTestEvent::kLeaveFullscreen); |
| } |
| |
| // A <div>, containing a <video>, goes fullscreen. |
| // The test changes size of the <video> relative to the viewport and observes |
| // how it gets and looses effectively-fullscreen status. |
| // This somewhat emulates YT scrolling in the fullscreen mode. |
| IN_PROC_BROWSER_TEST_F(FullscreenDetectionTest, VideoTagSizeChange) { |
| auto* web_contents = shell()->web_contents(); |
| EXPECT_TRUE(NavigateToURL( |
| shell(), embedded_test_server()->GetURL("/media/fullscreen.html"))); |
| |
| FullscreenEventsRecorder recorder(web_contents); |
| |
| ASSERT_TRUE(content::ExecJs(web_contents, "makeFullscreen('small_div')")); |
| |
| recorder.Wait(1); |
| EXPECT_EQ(recorder.event(0), FullscreenTestEvent::kEnterFullscreen); |
| |
| ASSERT_TRUE(content::ExecJs(web_contents, "makeBig('small_video')")); |
| recorder.Wait(2); |
| EXPECT_EQ(recorder.event(1), FullscreenTestEvent::kEffectivelyFullscreen); |
| |
| ASSERT_TRUE(content::ExecJs(web_contents, "makeSmall('small_video')")); |
| recorder.Wait(3); |
| EXPECT_EQ(recorder.event(2), FullscreenTestEvent::kNotEffectivelyFullscreen); |
| |
| ASSERT_TRUE(content::ExecJs(web_contents, "makePortrait('small_video')")); |
| recorder.Wait(4); |
| EXPECT_EQ(recorder.event(3), FullscreenTestEvent::kEffectivelyFullscreen); |
| |
| ASSERT_TRUE(content::ExecJs(web_contents, "exitFullscreen()")); |
| recorder.Wait(5); |
| EXPECT_EQ(recorder.event(4), FullscreenTestEvent::kLeaveFullscreen); |
| } |
| |
| // Test how attaching/detaching the <video> affects its fullscreen status. |
| IN_PROC_BROWSER_TEST_F(FullscreenDetectionTest, DetachAttachDuringFullscreen) { |
| auto* web_contents = shell()->web_contents(); |
| EXPECT_TRUE(NavigateToURL( |
| shell(), embedded_test_server()->GetURL("/media/fullscreen.html"))); |
| |
| FullscreenEventsRecorder recorder(web_contents); |
| |
| ASSERT_TRUE(content::ExecJs(web_contents, "makeFullscreen('big_div')")); |
| |
| recorder.Wait(2); |
| EXPECT_EQ(recorder.event(0), FullscreenTestEvent::kEnterFullscreen); |
| EXPECT_EQ(recorder.event(1), FullscreenTestEvent::kEffectivelyFullscreen); |
| |
| ASSERT_TRUE(content::ExecJs(web_contents, "detach('big_video')")); |
| recorder.Wait(3); |
| EXPECT_EQ(recorder.event(2), FullscreenTestEvent::kNotEffectivelyFullscreen); |
| |
| ASSERT_TRUE(content::ExecJs(web_contents, "attach_to('big_div')")); |
| recorder.Wait(4); |
| EXPECT_EQ(recorder.event(3), FullscreenTestEvent::kEffectivelyFullscreen); |
| |
| ASSERT_TRUE(content::ExecJs(web_contents, "exitFullscreen()")); |
| recorder.Wait(6); |
| EXPECT_EQ(recorder.event(4), FullscreenTestEvent::kLeaveFullscreen); |
| EXPECT_EQ(recorder.event(5), FullscreenTestEvent::kNotEffectivelyFullscreen); |
| } |
| |
| // The test changes visibility of the <video> and observes |
| // how it gets and loses effectively-fullscreen status. |
| // TODO(crbug.com/1352246): Re-enable this test |
| #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_ANDROID) |
| #define MAYBE_HideVideoTag DISABLED_HideVideoTag |
| #else |
| #define MAYBE_HideVideoTag HideVideoTag |
| #endif |
| IN_PROC_BROWSER_TEST_F(FullscreenDetectionTest, MAYBE_HideVideoTag) { |
| auto* web_contents = shell()->web_contents(); |
| EXPECT_TRUE(NavigateToURL( |
| shell(), embedded_test_server()->GetURL("/media/fullscreen.html"))); |
| |
| FullscreenEventsRecorder recorder(web_contents); |
| |
| ASSERT_TRUE(content::ExecJs(web_contents, "makeFullscreen('big_div')")); |
| |
| recorder.Wait(2); |
| EXPECT_EQ(recorder.event(0), FullscreenTestEvent::kEnterFullscreen); |
| EXPECT_EQ(recorder.event(1), FullscreenTestEvent::kEffectivelyFullscreen); |
| |
| ASSERT_TRUE(content::ExecJs(web_contents, "hide('big_video')")); |
| recorder.Wait(3); |
| EXPECT_EQ(recorder.event(2), FullscreenTestEvent::kNotEffectivelyFullscreen); |
| |
| ASSERT_TRUE(content::ExecJs(web_contents, "makeBig('big_video')")); |
| recorder.Wait(4); |
| EXPECT_EQ(recorder.event(3), FullscreenTestEvent::kEffectivelyFullscreen); |
| |
| ASSERT_TRUE(content::ExecJs(web_contents, "exitFullscreen()")); |
| recorder.Wait(6); |
| EXPECT_EQ(recorder.event(4), FullscreenTestEvent::kLeaveFullscreen); |
| EXPECT_EQ(recorder.event(5), FullscreenTestEvent::kNotEffectivelyFullscreen); |
| } |
| |
| } // namespace content |