blob: 1440cd737ad63525750eda44dd512ef767c93b54 [file] [log] [blame]
// Copyright 2017 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/picture_in_picture_interstitial.h"
#include "cc/layers/layer.h"
#include "third_party/blink/public/platform/web_localized_string.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/geometry/dom_rect_read_only.h"
#include "third_party/blink/renderer/core/html/html_image_element.h"
#include "third_party/blink/renderer/core/html/media/html_video_element.h"
#include "third_party/blink/renderer/core/html/media/media_controls.h"
#include "third_party/blink/renderer/core/resize_observer/resize_observer.h"
#include "third_party/blink/renderer/core/resize_observer/resize_observer_entry.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/text/platform_locale.h"
namespace {
constexpr TimeDelta kPictureInPictureStyleChangeTransitionDuration =
TimeDelta::FromMilliseconds(200);
constexpr TimeDelta kPictureInPictureHiddenAnimationSeconds =
TimeDelta::FromMilliseconds(300);
} // namespace
namespace blink {
class PictureInPictureInterstitial::VideoElementResizeObserverDelegate final
: public ResizeObserver::Delegate {
public:
explicit VideoElementResizeObserverDelegate(
PictureInPictureInterstitial* interstitial)
: interstitial_(interstitial) {
DCHECK(interstitial);
}
~VideoElementResizeObserverDelegate() override = default;
void OnResize(
const HeapVector<Member<ResizeObserverEntry>>& entries) override {
DCHECK_EQ(1u, entries.size());
DCHECK_EQ(entries[0]->target(), interstitial_->GetVideoElement());
DCHECK(entries[0]->contentRect());
interstitial_->NotifyElementSizeChanged(*entries[0]->contentRect());
}
void Trace(Visitor* visitor) override {
visitor->Trace(interstitial_);
ResizeObserver::Delegate::Trace(visitor);
}
private:
Member<PictureInPictureInterstitial> interstitial_;
};
PictureInPictureInterstitial::PictureInPictureInterstitial(
HTMLVideoElement& videoElement)
: HTMLDivElement(videoElement.GetDocument()),
resize_observer_(ResizeObserver::Create(
videoElement.GetDocument(),
MakeGarbageCollected<VideoElementResizeObserverDelegate>(this))),
interstitial_timer_(
videoElement.GetDocument().GetTaskRunner(TaskType::kInternalMedia),
this,
&PictureInPictureInterstitial::ToggleInterstitialTimerFired),
video_element_(&videoElement) {
SetShadowPseudoId(AtomicString("-internal-media-interstitial"));
background_image_ = HTMLImageElement::Create(GetDocument());
background_image_->SetShadowPseudoId(
AtomicString("-internal-media-interstitial-background-image"));
background_image_->SetSrc(videoElement.getAttribute(html_names::kPosterAttr));
ParserAppendChild(background_image_);
message_element_ = HTMLDivElement::Create(GetDocument());
message_element_->SetShadowPseudoId(
AtomicString("-internal-picture-in-picture-interstitial-message"));
message_element_->setInnerText(
GetVideoElement().GetLocale().QueryString(
WebLocalizedString::kPictureInPictureInterstitialText),
ASSERT_NO_EXCEPTION);
ParserAppendChild(message_element_);
resize_observer_->observe(video_element_);
}
void PictureInPictureInterstitial::Show() {
if (should_be_visible_)
return;
if (interstitial_timer_.IsActive())
interstitial_timer_.Stop();
should_be_visible_ = true;
RemoveInlineStyleProperty(CSSPropertyDisplay);
interstitial_timer_.StartOneShot(
kPictureInPictureStyleChangeTransitionDuration, FROM_HERE);
DCHECK(GetVideoElement().CcLayer());
GetVideoElement().CcLayer()->SetIsDrawable(false);
}
void PictureInPictureInterstitial::Hide() {
if (!should_be_visible_)
return;
if (interstitial_timer_.IsActive())
interstitial_timer_.Stop();
should_be_visible_ = false;
SetInlineStyleProperty(CSSPropertyOpacity, 0,
CSSPrimitiveValue::UnitType::kNumber);
interstitial_timer_.StartOneShot(kPictureInPictureHiddenAnimationSeconds,
FROM_HERE);
if (GetVideoElement().CcLayer())
GetVideoElement().CcLayer()->SetIsDrawable(true);
}
Node::InsertionNotificationRequest PictureInPictureInterstitial::InsertedInto(
ContainerNode& root) {
if (GetVideoElement().isConnected() && !resize_observer_) {
resize_observer_ = ResizeObserver::Create(
GetVideoElement().GetDocument(),
MakeGarbageCollected<VideoElementResizeObserverDelegate>(this));
resize_observer_->observe(&GetVideoElement());
}
return HTMLDivElement::InsertedInto(root);
}
void PictureInPictureInterstitial::RemovedFrom(ContainerNode&) {
DCHECK(!GetVideoElement().isConnected());
if (resize_observer_) {
resize_observer_->disconnect();
resize_observer_.Clear();
}
}
void PictureInPictureInterstitial::NotifyElementSizeChanged(
const DOMRectReadOnly& new_size) {
message_element_->setAttribute(
"class", MediaControls::GetSizingCSSClass(
MediaControls::GetSizingClass(new_size.width())));
}
void PictureInPictureInterstitial::ToggleInterstitialTimerFired(TimerBase*) {
interstitial_timer_.Stop();
if (should_be_visible_) {
SetInlineStyleProperty(CSSPropertyBackgroundColor, CSSValueBlack);
SetInlineStyleProperty(CSSPropertyOpacity, 1,
CSSPrimitiveValue::UnitType::kNumber);
} else {
SetInlineStyleProperty(CSSPropertyDisplay, CSSValueNone);
}
}
void PictureInPictureInterstitial::OnPosterImageChanged() {
background_image_->SetSrc(
GetVideoElement().getAttribute(html_names::kPosterAttr));
}
void PictureInPictureInterstitial::Trace(Visitor* visitor) {
visitor->Trace(resize_observer_);
visitor->Trace(video_element_);
visitor->Trace(background_image_);
visitor->Trace(message_element_);
HTMLDivElement::Trace(visitor);
}
} // namespace blink