blob: 94e25c143aa49afd700e9a432e5fdf37b56574d1 [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/modules/media_controls/elements/media_control_slider_element.h"
#include "third_party/blink/renderer/core/html/html_div_element.h"
#include "third_party/blink/renderer/core/html/shadow/shadow_element_names.h"
#include "third_party/blink/renderer/core/input_type_names.h"
#include "third_party/blink/renderer/core/layout/layout_box.h"
#include "third_party/blink/renderer/core/layout/layout_box_model_object.h"
#include "third_party/blink/renderer/core/layout/layout_view.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/modules/media_controls/elements/media_control_elements_helper.h"
#include "third_party/blink/renderer/modules/media_controls/media_controls_impl.h"
#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
namespace {
void SetSegmentDivPosition(blink::HTMLDivElement* segment,
blink::MediaControlSliderElement::Position position,
int width,
float zoom_factor) {
int segment_width =
clampTo<int>(floor((position.width * width) / zoom_factor));
int segment_left = clampTo<int>(floor((position.left * width) / zoom_factor));
int current_width = 0;
int current_left = 0;
// Get the current width and left for the segment. If the box is not present
// then it will be a nullptr so we should assume zero.
blink::LayoutBox* box = segment->GetLayoutBox();
if (box) {
current_width = box->PixelSnappedWidth();
current_left = box->LogicalLeft().ToInt();
}
// If the width and left has not changed then do not update the segment.
if (segment_width == current_width && segment_left == current_left)
return;
StringBuilder builder;
builder.Append("width: ");
builder.AppendNumber(segment_width);
builder.Append("px; left: ");
builder.AppendNumber(segment_left);
builder.Append("px;");
segment->setAttribute("style", builder.ToAtomicString());
}
} // namespace.
namespace blink {
class MediaControlSliderElement::MediaControlSliderElementResizeObserverDelegate
final : public ResizeObserver::Delegate {
public:
explicit MediaControlSliderElementResizeObserverDelegate(
MediaControlSliderElement* element)
: element_(element) {
DCHECK(element);
}
~MediaControlSliderElementResizeObserverDelegate() override = default;
void OnResize(
const HeapVector<Member<ResizeObserverEntry>>& entries) override {
DCHECK_EQ(1u, entries.size());
DCHECK_EQ(entries[0]->target(), element_);
element_->NotifyElementSizeChanged();
}
void Trace(Visitor* visitor) const override {
visitor->Trace(element_);
ResizeObserver::Delegate::Trace(visitor);
}
private:
Member<MediaControlSliderElement> element_;
};
MediaControlSliderElement::MediaControlSliderElement(
MediaControlsImpl& media_controls)
: MediaControlInputElement(media_controls),
before_segment_position_(0, 0),
after_segment_position_(0, 0),
segment_highlight_before_(nullptr),
segment_highlight_after_(nullptr),
resize_observer_(ResizeObserver::Create(
GetDocument().domWindow(),
MakeGarbageCollected<MediaControlSliderElementResizeObserverDelegate>(
this))) {
setType(input_type_names::kRange);
setAttribute(html_names::kStepAttr, "any");
OnControlsShown();
}
Element& MediaControlSliderElement::GetTrackElement() {
// The timeline element has a shadow root with the following
// structure:
//
// #shadow-root
// - div
// - div::-webkit-slider-runnable-track#track
Element* track = GetShadowRoot()->getElementById(AtomicString("track"));
DCHECK(track);
return *track;
}
void MediaControlSliderElement::SetupBarSegments() {
DCHECK((segment_highlight_after_ && segment_highlight_before_) ||
(!segment_highlight_after_ && !segment_highlight_before_));
if (segment_highlight_after_ || segment_highlight_before_)
return;
Element& track = GetTrackElement();
track.SetShadowPseudoId(
shadow_element_names::kPseudoMediaControlsSegmentedTrack);
// Add the following structure to #track.
//
// div::internal-track-segment-background (container)
// - div::internal-track-segment-highlight-before (blue highlight)
// - div::internal-track-segment-highlight-after (dark gray highlight)
HTMLDivElement* background = MediaControlElementsHelper::CreateDiv(
"-internal-track-segment-background", &track);
segment_highlight_before_ = MediaControlElementsHelper::CreateDiv(
"-internal-track-segment-highlight-before", background);
segment_highlight_after_ = MediaControlElementsHelper::CreateDiv(
"-internal-track-segment-highlight-after", background);
}
void MediaControlSliderElement::SetBeforeSegmentPosition(
MediaControlSliderElement::Position position) {
DCHECK(segment_highlight_before_);
before_segment_position_ = position;
SetSegmentDivPosition(segment_highlight_before_, before_segment_position_,
TrackWidth(), ZoomFactor());
}
void MediaControlSliderElement::SetAfterSegmentPosition(
MediaControlSliderElement::Position position) {
DCHECK(segment_highlight_after_);
after_segment_position_ = position;
SetSegmentDivPosition(segment_highlight_after_, after_segment_position_,
TrackWidth(), ZoomFactor());
}
int MediaControlSliderElement::TrackWidth() {
LayoutBoxModelObject* box = GetTrackElement().GetLayoutBoxModelObject();
return box ? box->OffsetWidth().Round() : 0;
}
float MediaControlSliderElement::ZoomFactor() const {
if (!GetDocument().GetLayoutView())
return 1;
return GetDocument().GetLayoutView()->ZoomFactor();
}
void MediaControlSliderElement::NotifyElementSizeChanged() {
SetSegmentDivPosition(segment_highlight_before_, before_segment_position_,
TrackWidth(), ZoomFactor());
SetSegmentDivPosition(segment_highlight_after_, after_segment_position_,
TrackWidth(), ZoomFactor());
}
void MediaControlSliderElement::Trace(Visitor* visitor) const {
visitor->Trace(segment_highlight_before_);
visitor->Trace(segment_highlight_after_);
visitor->Trace(resize_observer_);
MediaControlInputElement::Trace(visitor);
}
void MediaControlSliderElement::OnControlsShown() {
resize_observer_->observe(this);
}
void MediaControlSliderElement::OnControlsHidden() {
resize_observer_->disconnect();
}
} // namespace blink