blob: 1e345abb5e450a8587956847e897434b4fc93c9f [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 "third_party/blink/renderer/core/html/media/html_video_element.h"
#include "cc/layers/layer.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/media/display_type.h"
#include "third_party/blink/public/platform/web_fullscreen_video_status.h"
#include "third_party/blink/renderer/core/dom/shadow_root.h"
#include "third_party/blink/renderer/core/frame/settings.h"
#include "third_party/blink/renderer/core/html/media/html_media_test_helper.h"
#include "third_party/blink/renderer/core/html_names.h"
#include "third_party/blink/renderer/core/layout/layout_box_model_object.h"
#include "third_party/blink/renderer/core/loader/empty_clients.h"
#include "third_party/blink/renderer/core/paint/paint_layer.h"
#include "third_party/blink/renderer/core/testing/page_test_base.h"
#include "third_party/blink/renderer/platform/heap/heap.h"
#include "third_party/blink/renderer/platform/testing/empty_web_media_player.h"
#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
using testing::_;
namespace blink {
namespace {
class HTMLVideoElementMockMediaPlayer : public EmptyWebMediaPlayer {
public:
MOCK_METHOD1(SetIsEffectivelyFullscreen, void(WebFullscreenVideoStatus));
MOCK_METHOD1(OnDisplayTypeChanged, void(DisplayType));
MOCK_CONST_METHOD0(HasAvailableVideoFrame, bool());
};
} // namespace
class HTMLVideoElementTest : public PageTestBase {
public:
void SetUp() override {
auto mock_media_player =
std::make_unique<HTMLVideoElementMockMediaPlayer>();
media_player_ = mock_media_player.get();
SetupPageWithClients(nullptr,
MakeGarbageCollected<test::MediaStubLocalFrameClient>(
std::move(mock_media_player)),
nullptr);
video_ = MakeGarbageCollected<HTMLVideoElement>(GetDocument());
GetDocument().body()->appendChild(video_);
}
void SetFakeCcLayer(cc::Layer* layer) { video_->SetCcLayer(layer); }
HTMLVideoElement* video() { return video_.Get(); }
HTMLVideoElementMockMediaPlayer* MockWebMediaPlayer() {
return media_player_;
}
private:
Persistent<HTMLVideoElement> video_;
// Owned by HTMLVideoElementFrameClient.
HTMLVideoElementMockMediaPlayer* media_player_;
};
TEST_F(HTMLVideoElementTest, PictureInPictureInterstitialAndTextContainer) {
scoped_refptr<cc::Layer> layer = cc::Layer::Create();
SetFakeCcLayer(layer.get());
video()->SetBooleanAttribute(html_names::kControlsAttr, true);
video()->SetSrc("http://example.com/foo.mp4");
test::RunPendingTasks();
// Simulate the text track being displayed.
video()->UpdateTextTrackDisplay();
video()->UpdateTextTrackDisplay();
// Simulate entering Picture-in-Picture.
EXPECT_CALL(*MockWebMediaPlayer(),
OnDisplayTypeChanged(DisplayType::kInline));
video()->OnEnteredPictureInPicture();
// Simulate that text track are displayed again.
video()->UpdateTextTrackDisplay();
EXPECT_EQ(3u, video()->EnsureUserAgentShadowRoot().CountChildren());
EXPECT_CALL(*MockWebMediaPlayer(),
OnDisplayTypeChanged(DisplayType::kInline));
// Reset cc::layer to avoid crashes depending on timing.
SetFakeCcLayer(nullptr);
}
TEST_F(HTMLVideoElementTest, PictureInPictureInterstitial_Reattach) {
scoped_refptr<cc::Layer> layer = cc::Layer::Create();
SetFakeCcLayer(layer.get());
video()->SetBooleanAttribute(html_names::kControlsAttr, true);
video()->SetSrc("http://example.com/foo.mp4");
test::RunPendingTasks();
EXPECT_CALL(*MockWebMediaPlayer(),
OnDisplayTypeChanged(DisplayType::kInline));
EXPECT_CALL(*MockWebMediaPlayer(), HasAvailableVideoFrame())
.WillRepeatedly(testing::Return(true));
// Simulate entering Picture-in-Picture.
video()->OnEnteredPictureInPicture();
EXPECT_CALL(*MockWebMediaPlayer(), OnDisplayTypeChanged(DisplayType::kInline))
.Times(3);
// Try detaching and reattaching. This should not crash.
GetDocument().body()->removeChild(video());
GetDocument().body()->appendChild(video());
GetDocument().body()->removeChild(video());
}
TEST_F(HTMLVideoElementTest, EffectivelyFullscreen_DisplayType) {
video()->SetSrc("http://example.com/foo.mp4");
test::RunPendingTasks();
UpdateAllLifecyclePhasesForTest();
EXPECT_EQ(DisplayType::kInline, video()->GetDisplayType());
// Vector of data to use for tests. First value is to be set when calling
// SetIsEffectivelyFullscreen(). The second one is the expected DisplayType.
// This is testing all possible values of WebFullscreenVideoStatus and then
// sets the value back to a value that should put the DisplayType back to
// inline.
Vector<std::pair<WebFullscreenVideoStatus, DisplayType>> tests = {
{WebFullscreenVideoStatus::kNotEffectivelyFullscreen,
DisplayType::kInline},
{WebFullscreenVideoStatus::kFullscreenAndPictureInPictureEnabled,
DisplayType::kFullscreen},
{WebFullscreenVideoStatus::kFullscreenAndPictureInPictureDisabled,
DisplayType::kFullscreen},
{WebFullscreenVideoStatus::kNotEffectivelyFullscreen,
DisplayType::kInline},
};
for (const auto& test : tests) {
EXPECT_CALL(*MockWebMediaPlayer(), SetIsEffectivelyFullscreen(test.first));
EXPECT_CALL(*MockWebMediaPlayer(), OnDisplayTypeChanged(test.second));
video()->SetIsEffectivelyFullscreen(test.first);
EXPECT_EQ(test.second, video()->GetDisplayType());
testing::Mock::VerifyAndClearExpectations(MockWebMediaPlayer());
}
}
TEST_F(HTMLVideoElementTest, ChangeLayerNeedsCompositingUpdate) {
video()->SetSrc("http://example.com/foo.mp4");
test::RunPendingTasks();
UpdateAllLifecyclePhasesForTest();
auto layer1 = cc::Layer::Create();
SetFakeCcLayer(layer1.get());
ASSERT_TRUE(video()->GetLayoutObject()->HasLayer());
auto* paint_layer =
To<LayoutBoxModelObject>(video()->GetLayoutObject())->Layer();
EXPECT_TRUE(paint_layer->NeedsCompositingInputsUpdate());
UpdateAllLifecyclePhasesForTest();
EXPECT_FALSE(paint_layer->NeedsCompositingInputsUpdate());
// Change to another cc layer.
auto layer2 = cc::Layer::Create();
SetFakeCcLayer(layer2.get());
EXPECT_TRUE(paint_layer->NeedsCompositingInputsUpdate());
UpdateAllLifecyclePhasesForTest();
EXPECT_FALSE(paint_layer->NeedsCompositingInputsUpdate());
// Remove cc layer.
SetFakeCcLayer(nullptr);
EXPECT_TRUE(paint_layer->NeedsCompositingInputsUpdate());
UpdateAllLifecyclePhasesForTest();
EXPECT_FALSE(paint_layer->NeedsCompositingInputsUpdate());
}
TEST_F(HTMLVideoElementTest, HasAvailableVideoFrameChecksWMP) {
video()->SetSrc("http://example.com/foo.mp4");
test::RunPendingTasks();
UpdateAllLifecyclePhasesForTest();
EXPECT_CALL(*MockWebMediaPlayer(), HasAvailableVideoFrame())
.WillOnce(testing::Return(false))
.WillOnce(testing::Return(true));
EXPECT_FALSE(video()->HasAvailableVideoFrame());
EXPECT_TRUE(video()->HasAvailableVideoFrame());
}
TEST_F(HTMLVideoElementTest, AutoPIPExitPIPTest) {
video()->SetSrc("http://example.com/foo.mp4");
test::RunPendingTasks();
// Set in auto PIP.
video()->OnBecamePersistentVideo(true);
// Shouldn't get to PictureInPictureController::ExitPictureInPicture
// and fail the DCHECK.
EXPECT_NO_FATAL_FAILURE(video()->DidEnterFullscreen());
test::RunPendingTasks();
}
// TODO(1190335): Remove this once we no longer support "default poster image"
// Blink embedders (such as Webview) can set the default poster image for a
// video using `blink::Settings`. In some cases we still need to distinguish
// between a "real" poster image and the default poster image.
TEST_F(HTMLVideoElementTest, DefaultPosterImage) {
String const kDefaultPosterImage = "http://www.example.com/foo.jpg";
// Override the default poster image
GetDocument().GetSettings()->SetDefaultVideoPosterURL(kDefaultPosterImage);
// Need to create a new video element, since
// `HTMLVideoElement::default_poster_url_` is set upon construction.
auto* video = MakeGarbageCollected<HTMLVideoElement>(GetDocument());
GetDocument().body()->appendChild(video);
// Assert that video element (without an explicitly set poster image url) has
// the same poster image URL as what we just set.
EXPECT_TRUE(video->IsDefaultPosterImageURL());
EXPECT_EQ(kDefaultPosterImage, video->PosterImageURL());
// Set the poster image of the video to something
video->setAttribute(html_names::kPosterAttr,
"http://www.example.com/bar.jpg");
EXPECT_FALSE(video->IsDefaultPosterImageURL());
EXPECT_NE(kDefaultPosterImage, video->PosterImageURL());
}
} // namespace blink