blob: eba5c8f347e9dbd6fded4ef53771cbe06c891643 [file] [log] [blame]
// Copyright 2015 The Chromium Authors
// 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/frame/root_frame_viewport.h"
#include "base/barrier_callback.h"
#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
#include "base/task/single_thread_task_runner.h"
#include "cc/input/snap_selection_strategy.h"
#include "third_party/blink/public/mojom/scroll/scroll_into_view_params.mojom-blink.h"
#include "third_party/blink/renderer/core/frame/local_frame_view.h"
#include "third_party/blink/renderer/core/frame/visual_viewport.h"
#include "third_party/blink/renderer/core/layout/layout_box.h"
#include "third_party/blink/renderer/core/layout/scroll_anchor.h"
#include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
#include "third_party/blink/renderer/core/scroll/scroll_alignment.h"
#include "third_party/blink/renderer/core/scroll/scroll_animator.h"
#include "third_party/blink/renderer/core/scroll/scroll_animator_base.h"
#include "third_party/blink/renderer/core/scroll/scrollable_area.h"
#include "third_party/blink/renderer/core/scroll/smooth_scroll_sequencer.h"
#include "third_party/blink/renderer/platform/geometry/layout_rect.h"
#include "ui/gfx/geometry/rect_f.h"
namespace blink {
namespace {
// Computes the rect of valid scroll offsets reachable by user scrolls for the
// scrollable area.
gfx::RectF GetUserScrollableRect(const ScrollableArea& area) {
gfx::RectF user_scrollable_rect;
ScrollOffset scrollable_size =
area.MaximumScrollOffset() - area.MinimumScrollOffset();
if (area.UserInputScrollable(kHorizontalScrollbar)) {
user_scrollable_rect.set_x(area.MinimumScrollOffset().x());
user_scrollable_rect.set_width(scrollable_size.x());
} else {
user_scrollable_rect.set_x(area.GetScrollOffset().x());
user_scrollable_rect.set_width(0);
}
if (area.UserInputScrollable(kVerticalScrollbar)) {
user_scrollable_rect.set_y(area.MinimumScrollOffset().y());
user_scrollable_rect.set_height(scrollable_size.y());
} else {
user_scrollable_rect.set_y(area.GetScrollOffset().y());
user_scrollable_rect.set_height(0);
}
return user_scrollable_rect;
}
static base::RepeatingCallback<void(ScrollableArea::ScrollCompletionMode)>
MakeViewportScrollCompletion(ScrollableArea::ScrollCallback callback) {
return callback
? base::BarrierCallback<ScrollableArea::ScrollCompletionMode>(
2, WTF::BindOnce(
[](ScrollableArea::ScrollCallback on_finish,
const std::vector<
ScrollableArea::ScrollCompletionMode>
completion_modes) {
auto completion_mode =
ScrollableArea::ScrollCompletionMode::kFinished;
for (auto mode : completion_modes) {
if (mode == ScrollableArea::ScrollCompletionMode::
kInterruptedByScroll) {
completion_mode = ScrollableArea::
ScrollCompletionMode::kInterruptedByScroll;
}
}
std::move(on_finish).Run(completion_mode);
},
std::move(callback)))
: base::RepeatingCallback<void(
ScrollableArea::ScrollCompletionMode)>();
}
} // namespace
RootFrameViewport::RootFrameViewport(ScrollableArea& visual_viewport,
ScrollableArea& layout_viewport)
: ScrollableArea(visual_viewport.GetCompositorTaskRunner()),
visual_viewport_(visual_viewport),
should_restore_scroll_(false) {
SetLayoutViewport(layout_viewport);
}
void RootFrameViewport::SetLayoutViewport(ScrollableArea& new_layout_viewport) {
if (layout_viewport_.Get() == &new_layout_viewport)
return;
if (layout_viewport_ && layout_viewport_->GetScrollAnchor())
layout_viewport_->GetScrollAnchor()->SetScroller(layout_viewport_.Get());
layout_viewport_ = &new_layout_viewport;
if (layout_viewport_->GetScrollAnchor())
layout_viewport_->GetScrollAnchor()->SetScroller(this);
}
ScrollableArea& RootFrameViewport::LayoutViewport() const {
DCHECK(layout_viewport_);
return *layout_viewport_;
}
PhysicalRect RootFrameViewport::RootContentsToLayoutViewportContents(
LocalFrameView& root_frame_view,
const PhysicalRect& rect) const {
PhysicalRect ret = rect;
// If the root LocalFrameView is the layout viewport then coordinates in the
// root LocalFrameView's content space are already in the layout viewport's
// content space.
if (root_frame_view.LayoutViewport() == &LayoutViewport())
return ret;
// Make the given rect relative to the top of the layout viewport's content
// by adding the scroll position.
// TODO(bokan): This will have to be revisited if we ever remove the
// restriction that a root scroller must be exactly screen filling.
ret.Move(
PhysicalOffset::FromVector2dFRound(LayoutViewport().GetScrollOffset()));
return ret;
}
void RootFrameViewport::RestoreToAnchor(const ScrollOffset& target_offset) {
// Clamp the scroll offset of each viewport now so that we force any invalid
// offsets to become valid so we can compute the correct deltas.
GetVisualViewport().SetScrollOffset(GetVisualViewport().GetScrollOffset(),
mojom::blink::ScrollType::kProgrammatic);
LayoutViewport().SetScrollOffset(LayoutViewport().GetScrollOffset(),
mojom::blink::ScrollType::kProgrammatic);
ScrollOffset delta = target_offset - GetScrollOffset();
GetVisualViewport().SetScrollOffset(
GetVisualViewport().GetScrollOffset() + delta,
mojom::blink::ScrollType::kProgrammatic);
delta = target_offset - GetScrollOffset();
if (RuntimeEnabledFeatures::FractionalScrollOffsetsEnabled()) {
LayoutViewport().SetScrollOffset(LayoutViewport().GetScrollOffset() + delta,
mojom::blink::ScrollType::kProgrammatic);
} else {
gfx::Vector2d layout_delta(
delta.x() < 0 ? floor(delta.x()) : ceil(delta.x()),
delta.y() < 0 ? floor(delta.y()) : ceil(delta.y()));
LayoutViewport().SetScrollOffset(
ScrollOffset(LayoutViewport().ScrollOffsetInt() + layout_delta),
mojom::blink::ScrollType::kProgrammatic);
}
delta = target_offset - GetScrollOffset();
GetVisualViewport().SetScrollOffset(
GetVisualViewport().GetScrollOffset() + delta,
mojom::blink::ScrollType::kProgrammatic);
}
void RootFrameViewport::DidUpdateVisualViewport() {
if (ScrollAnchor* anchor = LayoutViewport().GetScrollAnchor())
anchor->Clear();
}
LayoutBox* RootFrameViewport::GetLayoutBox() const {
return LayoutViewport().GetLayoutBox();
}
gfx::QuadF RootFrameViewport::LocalToVisibleContentQuad(
const gfx::QuadF& quad,
const LayoutObject* local_object,
unsigned flags) const {
if (!layout_viewport_)
return quad;
gfx::QuadF viewport_quad =
layout_viewport_->LocalToVisibleContentQuad(quad, local_object, flags);
if (visual_viewport_) {
viewport_quad = visual_viewport_->LocalToVisibleContentQuad(
viewport_quad, local_object, flags);
}
return viewport_quad;
}
scoped_refptr<base::SingleThreadTaskRunner>
RootFrameViewport::GetTimerTaskRunner() const {
return LayoutViewport().GetTimerTaskRunner();
}
int RootFrameViewport::HorizontalScrollbarHeight(
OverlayScrollbarClipBehavior behavior) const {
return LayoutViewport().HorizontalScrollbarHeight(behavior);
}
int RootFrameViewport::VerticalScrollbarWidth(
OverlayScrollbarClipBehavior behavior) const {
return LayoutViewport().VerticalScrollbarWidth(behavior);
}
void RootFrameViewport::UpdateScrollAnimator() {
GetScrollAnimator().SetCurrentOffset(ScrollOffsetFromScrollAnimators());
}
ScrollOffset RootFrameViewport::ScrollOffsetFromScrollAnimators() const {
return GetVisualViewport().GetScrollAnimator().CurrentOffset() +
LayoutViewport().GetScrollAnimator().CurrentOffset();
}
gfx::Rect RootFrameViewport::VisibleContentRect(
IncludeScrollbarsInRect scrollbar_inclusion) const {
return gfx::Rect(
gfx::PointAtOffsetFromOrigin(ScrollOffsetInt()),
GetVisualViewport().VisibleContentRect(scrollbar_inclusion).size());
}
PhysicalRect RootFrameViewport::VisibleScrollSnapportRect(
IncludeScrollbarsInRect scrollbar_inclusion) const {
// The effective viewport is the intersection of the visual viewport with the
// layout viewport.
PhysicalRect frame_rect_in_content(
PhysicalOffset::FromVector2dFRound(LayoutViewport().GetScrollOffset()),
PhysicalSize(
LayoutViewport().VisibleContentRect(scrollbar_inclusion).size()));
PhysicalRect visual_rect_in_content(
PhysicalOffset::FromVector2dFRound(
LayoutViewport().GetScrollOffset() +
GetVisualViewport().GetScrollAnimator().CurrentOffset()),
PhysicalSize(
GetVisualViewport().VisibleContentRect(scrollbar_inclusion).size()));
PhysicalRect visible_scroll_snapport =
Intersection(visual_rect_in_content, frame_rect_in_content);
if (!LayoutViewport().GetLayoutBox())
return visible_scroll_snapport;
const ComputedStyle* style = LayoutViewport().GetLayoutBox()->Style();
visible_scroll_snapport.ContractEdges(
MinimumValueForLength(style->ScrollPaddingTop(),
visible_scroll_snapport.Height()),
MinimumValueForLength(style->ScrollPaddingRight(),
visible_scroll_snapport.Width()),
MinimumValueForLength(style->ScrollPaddingBottom(),
visible_scroll_snapport.Height()),
MinimumValueForLength(style->ScrollPaddingLeft(),
visible_scroll_snapport.Width()));
return visible_scroll_snapport;
}
bool RootFrameViewport::ShouldUseIntegerScrollOffset() const {
// Fractionals are floored in the ScrollAnimatorBase but it's important that
// the ScrollAnimators of the visual and layout viewports get the precise
// fractional number so never use integer scrolling for RootFrameViewport,
// we'll let the truncation happen in the subviewports.
return false;
}
bool RootFrameViewport::IsActive() const {
return LayoutViewport().IsActive();
}
int RootFrameViewport::ScrollSize(ScrollbarOrientation orientation) const {
gfx::Vector2d scroll_dimensions =
MaximumScrollOffsetInt() - MinimumScrollOffsetInt();
return (orientation == kHorizontalScrollbar) ? scroll_dimensions.x()
: scroll_dimensions.y();
}
bool RootFrameViewport::IsScrollCornerVisible() const {
return LayoutViewport().IsScrollCornerVisible();
}
gfx::Rect RootFrameViewport::ScrollCornerRect() const {
return LayoutViewport().ScrollCornerRect();
}
void RootFrameViewport::ApplyPendingHistoryRestoreScrollOffset() {
if (!pending_view_state_)
return;
bool should_restore_scale = pending_view_state_->page_scale_factor_;
// For main frame restore scale and visual viewport position
ScrollOffset visual_viewport_offset(
pending_view_state_->visual_viewport_scroll_offset_);
// If the visual viewport's offset is (-1, -1) it means the history item
// is an old version of HistoryItem so distribute the scroll between
// the main frame and the visual viewport as best as we can.
if (visual_viewport_offset.x() == -1 && visual_viewport_offset.y() == -1) {
visual_viewport_offset = pending_view_state_->scroll_offset_ -
LayoutViewport().GetScrollOffset();
}
auto* visual_viewport = static_cast<VisualViewport*>(&GetVisualViewport());
if (should_restore_scale && should_restore_scroll_) {
visual_viewport->SetScaleAndLocation(
pending_view_state_->page_scale_factor_,
visual_viewport->IsPinchGestureActive(),
gfx::PointAtOffsetFromOrigin(visual_viewport_offset));
} else if (should_restore_scale) {
visual_viewport->SetScale(pending_view_state_->page_scale_factor_);
} else if (should_restore_scroll_) {
visual_viewport->SetLocation(
gfx::PointAtOffsetFromOrigin(visual_viewport_offset));
}
should_restore_scroll_ = false;
pending_view_state_.reset();
}
bool RootFrameViewport::SetScrollOffset(
const ScrollOffset& offset,
mojom::blink::ScrollType scroll_type,
mojom::blink::ScrollBehavior scroll_behavior,
ScrollCallback on_finish) {
UpdateScrollAnimator();
if (scroll_behavior == mojom::blink::ScrollBehavior::kAuto)
scroll_behavior = ScrollBehaviorStyle();
if (scroll_type == mojom::blink::ScrollType::kAnchoring) {
return DistributeScrollBetweenViewports(offset, scroll_type,
scroll_behavior, kLayoutViewport,
std::move(on_finish));
}
if (scroll_behavior == mojom::blink::ScrollBehavior::kSmooth) {
return DistributeScrollBetweenViewports(offset, scroll_type,
scroll_behavior, kVisualViewport,
std::move(on_finish));
}
ScrollOffset clamped_offset = ClampScrollOffset(offset);
return ScrollableArea::SetScrollOffset(clamped_offset, scroll_type,
scroll_behavior, std::move(on_finish));
}
mojom::blink::ScrollBehavior RootFrameViewport::ScrollBehaviorStyle() const {
return LayoutViewport().ScrollBehaviorStyle();
}
mojom::blink::ColorScheme RootFrameViewport::UsedColorSchemeScrollbars() const {
return LayoutViewport().UsedColorSchemeScrollbars();
}
ScrollOffset RootFrameViewport::ClampToUserScrollableOffset(
const ScrollOffset& offset) const {
ScrollOffset scroll_offset = offset;
gfx::RectF layout_scrollable = GetUserScrollableRect(LayoutViewport());
gfx::RectF visual_scrollable = GetUserScrollableRect(GetVisualViewport());
gfx::RectF user_scrollable(
layout_scrollable.origin() + visual_scrollable.OffsetFromOrigin(),
layout_scrollable.size() + visual_scrollable.size());
scroll_offset.set_x(
ClampTo(scroll_offset.x(), user_scrollable.x(), user_scrollable.right()));
scroll_offset.set_y(ClampTo(scroll_offset.y(), user_scrollable.y(),
user_scrollable.bottom()));
return scroll_offset;
}
PhysicalRect RootFrameViewport::ScrollIntoView(
const PhysicalRect& rect_in_absolute,
const PhysicalBoxStrut& scroll_margin,
const mojom::blink::ScrollIntoViewParamsPtr& params) {
PhysicalRect scroll_snapport_rect = VisibleScrollSnapportRect();
PhysicalRect rect_in_document = rect_in_absolute;
rect_in_document.Move(
PhysicalOffset::FromVector2dFFloor(LayoutViewport().GetScrollOffset()));
ScrollOffset new_scroll_offset =
ClampScrollOffset(ScrollAlignment::GetScrollOffsetToExpose(
scroll_snapport_rect, rect_in_document, scroll_margin,
*params->align_x.get(), *params->align_y.get(), GetScrollOffset()));
if (params->type == mojom::blink::ScrollType::kUser)
new_scroll_offset = ClampToUserScrollableOffset(new_scroll_offset);
gfx::PointF end_point = ScrollOffsetToPosition(new_scroll_offset);
std::unique_ptr<cc::SnapSelectionStrategy> strategy =
cc::SnapSelectionStrategy::CreateForEndPosition(end_point, true, true);
if (GetLayoutBox()) {
end_point = GetSnapPositionAndSetTarget(*strategy).value_or(end_point);
new_scroll_offset = ScrollPositionToOffset(end_point);
}
if (new_scroll_offset != GetScrollOffset()) {
if (params->is_for_scroll_sequence) {
CHECK(GetSmoothScrollSequencer());
DCHECK(params->type == mojom::blink::ScrollType::kProgrammatic ||
params->type == mojom::blink::ScrollType::kUser);
mojom::blink::ScrollBehavior behavior = DetermineScrollBehavior(
params->behavior, GetLayoutBox()->StyleRef().GetScrollBehavior());
GetSmoothScrollSequencer()->QueueAnimation(this, new_scroll_offset,
behavior);
} else {
ScrollableArea::SetScrollOffset(new_scroll_offset, params->type);
}
}
// Return the newly moved rect to absolute coordinates.
// TODO(szager): PaintLayerScrollableArea::ScrollIntoView clips the return
// value to the visible content rect, but this does not.
// TODO(bokan): This returns an unchanged rect for scroll sequences (the PLSA
// version correctly computes what the rect will be when the sequence is
// executed) and we can't just adjust by `new_scroll_offset` since, to get to
// absolute coordinates, we must offset by only the layout viewport's scroll.
rect_in_document.Move(
-PhysicalOffset::FromVector2dFRound(LayoutViewport().GetScrollOffset()));
return rect_in_document;
}
void RootFrameViewport::UpdateScrollOffset(
const ScrollOffset& offset,
mojom::blink::ScrollType scroll_type) {
DistributeScrollBetweenViewports(offset, scroll_type,
mojom::blink::ScrollBehavior::kInstant,
kVisualViewport);
}
bool RootFrameViewport::DistributeScrollBetweenViewports(
const ScrollOffset& offset,
mojom::blink::ScrollType scroll_type,
mojom::blink::ScrollBehavior behavior,
ViewportToScrollFirst scroll_first,
ScrollCallback on_finish) {
// Make sure we use the scroll offsets as reported by each viewport's
// ScrollAnimatorBase, since its ScrollableArea's offset may have the
// fractional part truncated off.
// TODO(szager): Now that scroll offsets are stored as floats, can we take the
// scroll offset directly from the ScrollableArea's rather than the animators?
ScrollOffset old_offset = ScrollOffsetFromScrollAnimators();
ScrollOffset delta = offset - old_offset;
if (delta.IsZero()) {
if (on_finish) {
std::move(on_finish).Run(
ScrollableArea::ScrollCompletionMode::kZeroDelta);
}
return false;
}
ScrollableArea& primary =
scroll_first == kVisualViewport ? GetVisualViewport() : LayoutViewport();
ScrollableArea& secondary =
scroll_first == kVisualViewport ? LayoutViewport() : GetVisualViewport();
// Compute the clamped offsets for both viewports before performing any
// scrolling since the order of distribution can vary (and is typically
// visualViewport-first) but, per-spec, if we scroll both viewports the
// scroll event must be sent to the DOMWindow first, then to the
// VisualViewport. Thus, we'll always perform the scrolls in that order,
// regardless of the order of distribution.
ScrollOffset primary_offset = primary.ClampScrollOffset(
primary.GetScrollAnimator().CurrentOffset() + delta);
ScrollOffset unconsumed_by_primary =
(primary.GetScrollAnimator().CurrentOffset() + delta) - primary_offset;
ScrollOffset secondary_offset = secondary.ClampScrollOffset(
secondary.GetScrollAnimator().CurrentOffset() + unconsumed_by_primary);
auto all_done = MakeViewportScrollCompletion(std::move(on_finish));
// DistributeScrollBetweenViewports can be called from SetScrollOffset,
// so we assume that aborting sequenced smooth scrolls has been handled.
// It can also be called from inside an animation to set the offset in
// each frame. In that case, we shouldn't abort sequenced smooth scrolls.
// Actually apply the scroll the layout viewport first so that the DOM event
// is dispatched to the DOMWindow before the VisualViewport.
bool did_scroll = LayoutViewport().SetScrollOffset(
scroll_first == kLayoutViewport ? primary_offset : secondary_offset,
scroll_type, behavior, all_done);
did_scroll |= GetVisualViewport().SetScrollOffset(
scroll_first == kVisualViewport ? primary_offset : secondary_offset,
scroll_type, behavior, all_done);
return did_scroll;
}
gfx::Vector2d RootFrameViewport::ScrollOffsetInt() const {
return gfx::ToFlooredVector2d(GetScrollOffset());
}
ScrollOffset RootFrameViewport::GetScrollOffset() const {
return LayoutViewport().GetScrollOffset() +
GetVisualViewport().GetScrollOffset();
}
gfx::Vector2d RootFrameViewport::MinimumScrollOffsetInt() const {
return LayoutViewport().MinimumScrollOffsetInt() +
GetVisualViewport().MinimumScrollOffsetInt();
}
gfx::Vector2d RootFrameViewport::MaximumScrollOffsetInt() const {
return LayoutViewport().MaximumScrollOffsetInt() +
GetVisualViewport().MaximumScrollOffsetInt();
}
ScrollOffset RootFrameViewport::MaximumScrollOffset() const {
return LayoutViewport().MaximumScrollOffset() +
GetVisualViewport().MaximumScrollOffset();
}
gfx::Size RootFrameViewport::ContentsSize() const {
return LayoutViewport().ContentsSize();
}
bool RootFrameViewport::UsesCompositedScrolling() const {
return LayoutViewport().UsesCompositedScrolling();
}
bool RootFrameViewport::ShouldScrollOnMainThread() const {
return LayoutViewport().ShouldScrollOnMainThread();
}
bool RootFrameViewport::ScrollbarsCanBeActive() const {
return LayoutViewport().ScrollbarsCanBeActive();
}
bool RootFrameViewport::UserInputScrollable(
ScrollbarOrientation orientation) const {
return GetVisualViewport().UserInputScrollable(orientation) ||
LayoutViewport().UserInputScrollable(orientation);
}
bool RootFrameViewport::ShouldPlaceVerticalScrollbarOnLeft() const {
return LayoutViewport().ShouldPlaceVerticalScrollbarOnLeft();
}
void RootFrameViewport::ScrollControlWasSetNeedsPaintInvalidation() {
LayoutViewport().ScrollControlWasSetNeedsPaintInvalidation();
}
cc::Layer* RootFrameViewport::LayerForHorizontalScrollbar() const {
return LayoutViewport().LayerForHorizontalScrollbar();
}
cc::Layer* RootFrameViewport::LayerForVerticalScrollbar() const {
return LayoutViewport().LayerForVerticalScrollbar();
}
cc::Layer* RootFrameViewport::LayerForScrollCorner() const {
return LayoutViewport().LayerForScrollCorner();
}
// This method distributes the scroll between the visual and layout viewport.
ScrollResult RootFrameViewport::UserScroll(
ui::ScrollGranularity granularity,
const ScrollOffset& delta,
ScrollableArea::ScrollCallback on_finish) {
// TODO(bokan/ymalik): Once smooth scrolling is permanently enabled we
// should be able to remove this method override and use the base class
// version: ScrollableArea::userScroll.
UpdateScrollAnimator();
ScrollOffset pixel_delta = ResolveScrollDelta(granularity, delta);
// Precompute the amount of possible scrolling since, when animated,
// ScrollAnimator::userScroll will report having consumed the total given
// scroll delta, regardless of how much will actually scroll, but we need to
// know how much to leave for the layout viewport.
ScrollOffset visual_consumed_delta =
GetVisualViewport().GetScrollAnimator().ComputeDeltaToConsume(
pixel_delta);
// Split the remaining delta between scrollable and unscrollable axes of the
// layout viewport. We only pass a delta to the scrollable axes and remember
// how much was held back so we can add it to the unused delta in the
// result.
ScrollOffset layout_delta = pixel_delta - visual_consumed_delta;
ScrollOffset scrollable_axis_delta(
LayoutViewport().UserInputScrollable(kHorizontalScrollbar)
? layout_delta.x()
: 0,
LayoutViewport().UserInputScrollable(kVerticalScrollbar)
? layout_delta.y()
: 0);
ScrollOffset layout_consumed_delta =
LayoutViewport().GetScrollAnimator().ComputeDeltaToConsume(
scrollable_axis_delta);
if (ScrollAnimatorEnabled()) {
bool visual_viewport_has_running_animation =
GetVisualViewport().GetScrollAnimator().HasRunningAnimation();
bool layout_viewport_has_running_animation =
LayoutViewport().GetScrollAnimator().HasRunningAnimation();
// We reset |user_scroll_sequence_affects_layout_viewport_| only if this
// UserScroll is not a continuation of a longer sequence because an earlier
// UserScroll in the sequence may have already affected the layout
// viewport.
if (!visual_viewport_has_running_animation &&
!layout_viewport_has_running_animation) {
user_scroll_sequence_affects_layout_viewport_ = false;
}
}
// If there won't be any scrolling, bail early so we don't produce any side
// effects like cancelling existing animations.
if (visual_consumed_delta.IsZero() && layout_consumed_delta.IsZero()) {
if (on_finish) {
std::move(on_finish).Run(
ScrollableArea::ScrollCompletionMode::kZeroDelta);
}
return ScrollResult(false, false, pixel_delta.x(), pixel_delta.y());
}
CancelProgrammaticScrollAnimation();
if (SmoothScrollSequencer* sequencer = GetSmoothScrollSequencer())
sequencer->AbortAnimations();
// TODO(bokan): Why do we call userScroll on the animators directly and
// not through the ScrollableAreas?
if (visual_consumed_delta == pixel_delta) {
ScrollResult visual_result =
GetVisualViewport().GetScrollAnimator().UserScroll(
granularity, visual_consumed_delta, std::move(on_finish));
return visual_result;
}
if (!layout_consumed_delta.IsZero()) {
user_scroll_sequence_affects_layout_viewport_ = true;
}
if (layout_consumed_delta == pixel_delta) {
ScrollResult layout_result =
LayoutViewport().GetScrollAnimator().UserScroll(
granularity, scrollable_axis_delta, std::move(on_finish));
return layout_result;
}
auto all_done = MakeViewportScrollCompletion(std::move(on_finish));
ScrollResult visual_result =
GetVisualViewport().GetScrollAnimator().UserScroll(
granularity, visual_consumed_delta, all_done);
ScrollResult layout_result = LayoutViewport().GetScrollAnimator().UserScroll(
granularity, scrollable_axis_delta, all_done);
// Remember to add any delta not used because of !userInputScrollable to the
// unusedScrollDelta in the result.
ScrollOffset unscrollable_axis_delta = layout_delta - scrollable_axis_delta;
return ScrollResult(
visual_result.did_scroll_x || layout_result.did_scroll_x,
visual_result.did_scroll_y || layout_result.did_scroll_y,
layout_result.unused_scroll_delta_x + unscrollable_axis_delta.x(),
layout_result.unused_scroll_delta_y + unscrollable_axis_delta.y());
}
bool RootFrameViewport::ScrollAnimatorEnabled() const {
return LayoutViewport().ScrollAnimatorEnabled();
}
CompositorElementId RootFrameViewport::GetScrollElementId() const {
return LayoutViewport().GetScrollElementId();
}
CompositorElementId RootFrameViewport::GetScrollbarElementId(
ScrollbarOrientation orientation) {
return GetVisualViewport().VisualViewportSuppliesScrollbars()
? GetVisualViewport().GetScrollbarElementId(orientation)
: LayoutViewport().GetScrollbarElementId(orientation);
}
ChromeClient* RootFrameViewport::GetChromeClient() const {
return LayoutViewport().GetChromeClient();
}
SmoothScrollSequencer* RootFrameViewport::GetSmoothScrollSequencer() const {
return LayoutViewport().GetSmoothScrollSequencer();
}
void RootFrameViewport::ServiceScrollAnimations(double monotonic_time) {
ScrollableArea::ServiceScrollAnimations(monotonic_time);
LayoutViewport().ServiceScrollAnimations(monotonic_time);
GetVisualViewport().ServiceScrollAnimations(monotonic_time);
}
void RootFrameViewport::UpdateCompositorScrollAnimations() {
ScrollableArea::UpdateCompositorScrollAnimations();
LayoutViewport().UpdateCompositorScrollAnimations();
GetVisualViewport().UpdateCompositorScrollAnimations();
}
void RootFrameViewport::CancelProgrammaticScrollAnimation() {
ScrollableArea::CancelProgrammaticScrollAnimation();
LayoutViewport().CancelProgrammaticScrollAnimation();
GetVisualViewport().CancelProgrammaticScrollAnimation();
}
void RootFrameViewport::ClearScrollableArea() {
ScrollableArea::ClearScrollableArea();
LayoutViewport().ClearScrollableArea();
GetVisualViewport().ClearScrollableArea();
}
ScrollbarTheme& RootFrameViewport::GetPageScrollbarTheme() const {
return LayoutViewport().GetPageScrollbarTheme();
}
const cc::SnapContainerData* RootFrameViewport::GetSnapContainerData() const {
return LayoutViewport().GetSnapContainerData();
}
void RootFrameViewport::SetSnapContainerData(
std::optional<cc::SnapContainerData> data) {
LayoutViewport().SetSnapContainerData(data);
}
bool RootFrameViewport::SetTargetSnapAreaElementIds(
cc::TargetSnapAreaElementIds snap_target_ids) {
return LayoutViewport().SetTargetSnapAreaElementIds(snap_target_ids);
}
void RootFrameViewport::DropCompositorScrollDeltaNextCommit() {
LayoutViewport().DropCompositorScrollDeltaNextCommit();
GetVisualViewport().DropCompositorScrollDeltaNextCommit();
}
bool RootFrameViewport::SnapContainerDataNeedsUpdate() const {
return LayoutViewport().SnapContainerDataNeedsUpdate();
}
void RootFrameViewport::SetSnapContainerDataNeedsUpdate(bool needs_update) {
LayoutViewport().SetSnapContainerDataNeedsUpdate(needs_update);
}
std::optional<gfx::PointF> RootFrameViewport::GetSnapPositionAndSetTarget(
const cc::SnapSelectionStrategy& strategy) {
return LayoutViewport().GetSnapPositionAndSetTarget(strategy);
}
gfx::PointF RootFrameViewport::ScrollOffsetToPosition(
const ScrollOffset& offset) const {
return LayoutViewport().ScrollOffsetToPosition(offset);
}
ScrollOffset RootFrameViewport::ScrollPositionToOffset(
const gfx::PointF& position) const {
return LayoutViewport().ScrollPositionToOffset(position);
}
void RootFrameViewport::Trace(Visitor* visitor) const {
visitor->Trace(visual_viewport_);
visitor->Trace(layout_viewport_);
ScrollableArea::Trace(visitor);
}
void RootFrameViewport::UpdateSnappedTargetsAndEnqueueSnapChanged() {
LayoutViewport().UpdateSnappedTargetsAndEnqueueSnapChanged();
}
std::optional<cc::TargetSnapAreaElementIds>
RootFrameViewport::GetSnapchangingTargetIds() const {
return LayoutViewport().GetSnapchangingTargetIds();
}
void RootFrameViewport::SetSnapchangingTargetIds(
std::optional<cc::TargetSnapAreaElementIds> new_target_ids) {
LayoutViewport().SetSnapchangingTargetIds(new_target_ids);
}
void RootFrameViewport::UpdateSnapChangingTargetsAndEnqueueSnapChanging(
const cc::TargetSnapAreaElementIds& new_target_ids) {
LayoutViewport().UpdateSnapChangingTargetsAndEnqueueSnapChanging(
new_target_ids);
}
const cc::SnapSelectionStrategy* RootFrameViewport::GetImplSnapStrategy()
const {
return LayoutViewport().GetImplSnapStrategy();
}
void RootFrameViewport::SetImplSnapStrategy(
std::unique_ptr<cc::SnapSelectionStrategy> strategy) {
LayoutViewport().SetImplSnapStrategy(std::move(strategy));
}
void RootFrameViewport::EnqueueSnapChangingEventFromImplIfNeeded() {
LayoutViewport().EnqueueSnapChangingEventFromImplIfNeeded();
}
std::optional<cc::ElementId> RootFrameViewport::GetTargetedSnapAreaId() {
return LayoutViewport().GetTargetedSnapAreaId();
}
void RootFrameViewport::SetTargetedSnapAreaId(
const std::optional<cc::ElementId>& id) {
LayoutViewport().SetTargetedSnapAreaId(id);
}
} // namespace blink