blob: da2d1feb143cf5160704eda579afebc5a23e4056 [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/strings/grit/blink_strings.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/layout/layout_object.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/heap/heap.h"
#include "third_party/blink/renderer/platform/text/platform_locale.h"
namespace {
constexpr base::TimeDelta kPictureInPictureStyleChangeTransitionDuration =
base::TimeDelta::FromMilliseconds(200);
constexpr base::TimeDelta kPictureInPictureHiddenAnimationSeconds =
base::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) const 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().domWindow(),
MakeGarbageCollected<VideoElementResizeObserverDelegate>(this))),
interstitial_timer_(
videoElement.GetDocument().GetTaskRunner(TaskType::kInternalMedia),
this,
&PictureInPictureInterstitial::ToggleInterstitialTimerFired),
video_element_(&videoElement) {
SetShadowPseudoId(AtomicString("-internal-media-interstitial"));
background_image_ = MakeGarbageCollected<HTMLImageElement>(GetDocument());
background_image_->SetShadowPseudoId(
AtomicString("-internal-media-interstitial-background-image"));
background_image_->setAttribute(
html_names::kSrcAttr,
videoElement.FastGetAttribute(html_names::kPosterAttr));
ParserAppendChild(background_image_);
message_element_ = MakeGarbageCollected<HTMLDivElement>(GetDocument());
message_element_->SetShadowPseudoId(
AtomicString("-internal-picture-in-picture-interstitial-message"));
message_element_->setInnerText(
GetVideoElement().GetLocale().QueryString(
IDS_MEDIA_PICTURE_IN_PICTURE_INTERSTITIAL_TEXT),
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(CSSPropertyID::kDisplay);
interstitial_timer_.StartOneShot(
kPictureInPictureStyleChangeTransitionDuration, FROM_HERE);
DCHECK(GetVideoElement().CcLayer());
GetVideoElement().CcLayer()->SetIsDrawable(false);
GetVideoElement().CcLayer()->SetHitTestable(false);
}
void PictureInPictureInterstitial::Hide() {
if (!should_be_visible_)
return;
if (interstitial_timer_.IsActive())
interstitial_timer_.Stop();
should_be_visible_ = false;
SetInlineStyleProperty(CSSPropertyID::kOpacity, 0,
CSSPrimitiveValue::UnitType::kNumber);
interstitial_timer_.StartOneShot(kPictureInPictureHiddenAnimationSeconds,
FROM_HERE);
if (GetVideoElement().CcLayer()) {
GetVideoElement().CcLayer()->SetIsDrawable(true);
GetVideoElement().CcLayer()->SetHitTestable(true);
}
}
Node::InsertionNotificationRequest PictureInPictureInterstitial::InsertedInto(
ContainerNode& root) {
if (GetVideoElement().isConnected() && !resize_observer_) {
resize_observer_ = ResizeObserver::Create(
GetVideoElement().GetDocument().domWindow(),
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())));
// Force a layout since |LayoutMedia::UpdateLayout()| will sometimes miss a
// layout otherwise.
if (GetLayoutObject())
GetLayoutObject()->SetNeedsLayout(layout_invalidation_reason::kSizeChanged);
}
void PictureInPictureInterstitial::ToggleInterstitialTimerFired(TimerBase*) {
interstitial_timer_.Stop();
if (should_be_visible_) {
SetInlineStyleProperty(CSSPropertyID::kBackgroundColor, CSSValueID::kBlack);
SetInlineStyleProperty(CSSPropertyID::kOpacity, 1,
CSSPrimitiveValue::UnitType::kNumber);
} else {
SetInlineStyleProperty(CSSPropertyID::kDisplay, CSSValueID::kNone);
}
}
void PictureInPictureInterstitial::OnPosterImageChanged() {
background_image_->setAttribute(
html_names::kSrcAttr,
GetVideoElement().FastGetAttribute(html_names::kPosterAttr));
}
void PictureInPictureInterstitial::Trace(Visitor* visitor) const {
visitor->Trace(resize_observer_);
visitor->Trace(interstitial_timer_);
visitor->Trace(video_element_);
visitor->Trace(background_image_);
visitor->Trace(message_element_);
HTMLDivElement::Trace(visitor);
}
} // namespace blink