blob: 64c220322625151a6f66856c7525bca9eb294d57 [file] [log] [blame]
/*
* Copyright (c) 2010, Google Inc. All rights reserved.
* Copyright (C) 2008, 2011 Apple Inc. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "third_party/blink/renderer/core/scroll/scrollable_area.h"
#include "build/build_config.h"
#include "cc/layers/picture_layer.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/renderer/core/page/chrome_client.h"
#include "third_party/blink/renderer/core/scroll/programmatic_scroll_animator.h"
#include "third_party/blink/renderer/core/scroll/scroll_animator_base.h"
#include "third_party/blink/renderer/core/scroll/scrollbar_theme.h"
#include "third_party/blink/renderer/core/scroll/smooth_scroll_sequencer.h"
#include "third_party/blink/renderer/platform/graphics/graphics_layer.h"
#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
#include "third_party/blink/renderer/platform/scroll/main_thread_scrolling_reason.h"
static const int kPixelsPerLineStep = 40;
static const float kMinFractionToStepWhenPaging = 0.875f;
namespace blink {
int ScrollableArea::PixelsPerLineStep(ChromeClient* host) {
if (!host)
return kPixelsPerLineStep;
return host->WindowToViewportScalar(kPixelsPerLineStep);
}
float ScrollableArea::MinFractionToStepWhenPaging() {
return kMinFractionToStepWhenPaging;
}
int ScrollableArea::MaxOverlapBetweenPages() const {
return GetPageScrollbarTheme().MaxOverlapBetweenPages();
}
// static
ScrollBehavior ScrollableArea::DetermineScrollBehavior(
ScrollBehavior behavior_from_param,
ScrollBehavior behavior_from_style) {
if (behavior_from_param == kScrollBehaviorSmooth)
return kScrollBehaviorSmooth;
if (behavior_from_param == kScrollBehaviorAuto &&
behavior_from_style == kScrollBehaviorSmooth) {
return kScrollBehaviorSmooth;
}
return kScrollBehaviorInstant;
}
ScrollableArea::ScrollableArea()
: scrollbar_overlay_color_theme_(kScrollbarOverlayColorThemeDark),
horizontal_scrollbar_needs_paint_invalidation_(false),
vertical_scrollbar_needs_paint_invalidation_(false),
scroll_corner_needs_paint_invalidation_(false),
scrollbars_hidden_if_overlay_(true),
scrollbar_captured_(false),
mouse_over_scrollbar_(false),
needs_show_scrollbar_layers_(false),
uses_composited_scrolling_(false) {}
ScrollableArea::~ScrollableArea() = default;
void ScrollableArea::ClearScrollableArea() {
#if defined(OS_MACOSX)
if (scroll_animator_)
scroll_animator_->Dispose();
#endif
scroll_animator_.Clear();
programmatic_scroll_animator_.Clear();
if (fade_overlay_scrollbars_timer_)
fade_overlay_scrollbars_timer_->Stop();
}
ScrollAnimatorBase& ScrollableArea::GetScrollAnimator() const {
if (!scroll_animator_)
scroll_animator_ =
ScrollAnimatorBase::Create(const_cast<ScrollableArea*>(this));
return *scroll_animator_;
}
ProgrammaticScrollAnimator& ScrollableArea::GetProgrammaticScrollAnimator()
const {
if (!programmatic_scroll_animator_)
programmatic_scroll_animator_ =
ProgrammaticScrollAnimator::Create(const_cast<ScrollableArea*>(this));
return *programmatic_scroll_animator_;
}
GraphicsLayer* ScrollableArea::LayerForContainer() const {
return LayerForScrolling() ? LayerForScrolling()->Parent() : nullptr;
}
ScrollbarOrientation ScrollableArea::ScrollbarOrientationFromDirection(
ScrollDirectionPhysical direction) const {
return (direction == kScrollUp || direction == kScrollDown)
? kVerticalScrollbar
: kHorizontalScrollbar;
}
float ScrollableArea::ScrollStep(ScrollGranularity granularity,
ScrollbarOrientation orientation) const {
switch (granularity) {
case kScrollByLine:
return LineStep(orientation);
case kScrollByPage:
return PageStep(orientation);
case kScrollByDocument:
return DocumentStep(orientation);
case kScrollByPixel:
case kScrollByPrecisePixel:
return PixelStep(orientation);
default:
NOTREACHED();
return 0.0f;
}
}
ScrollResult ScrollableArea::UserScroll(ScrollGranularity granularity,
const ScrollOffset& delta) {
TRACE_EVENT2("input", "ScrollableArea::UserScroll", "x", delta.Width(), "y",
delta.Height());
float step_x = ScrollStep(granularity, kHorizontalScrollbar);
float step_y = ScrollStep(granularity, kVerticalScrollbar);
ScrollOffset pixel_delta(delta);
pixel_delta.Scale(step_x, step_y);
ScrollOffset scrollable_axis_delta(
UserInputScrollable(kHorizontalScrollbar) ? pixel_delta.Width() : 0,
UserInputScrollable(kVerticalScrollbar) ? pixel_delta.Height() : 0);
if (scrollable_axis_delta.IsZero()) {
return ScrollResult(false, false, pixel_delta.Width(),
pixel_delta.Height());
}
CancelProgrammaticScrollAnimation();
if (SmoothScrollSequencer* sequencer = GetSmoothScrollSequencer())
sequencer->AbortAnimations();
ScrollResult result =
GetScrollAnimator().UserScroll(granularity, pixel_delta);
// Delta that wasn't scrolled because the axis is !userInputScrollable
// should count as unusedScrollDelta.
ScrollOffset unscrollable_axis_delta = pixel_delta - scrollable_axis_delta;
result.unused_scroll_delta_x += unscrollable_axis_delta.Width();
result.unused_scroll_delta_y += unscrollable_axis_delta.Height();
return result;
}
void ScrollableArea::SetScrollOffset(const ScrollOffset& offset,
ScrollType scroll_type,
ScrollBehavior behavior) {
if (scroll_type != kSequencedScroll && scroll_type != kClampingScroll &&
scroll_type != kAnchoringScroll) {
if (SmoothScrollSequencer* sequencer = GetSmoothScrollSequencer())
sequencer->AbortAnimations();
}
ScrollOffset clamped_offset = ClampScrollOffset(offset);
if (clamped_offset == GetScrollOffset())
return;
if (behavior == kScrollBehaviorAuto)
behavior = ScrollBehaviorStyle();
switch (scroll_type) {
case kCompositorScroll:
case kClampingScroll:
ScrollOffsetChanged(clamped_offset, scroll_type);
break;
case kAnchoringScroll:
GetScrollAnimator().AdjustAnimationAndSetScrollOffset(clamped_offset,
scroll_type);
break;
case kProgrammaticScroll:
ProgrammaticScrollHelper(clamped_offset, behavior, false);
break;
case kSequencedScroll:
ProgrammaticScrollHelper(clamped_offset, behavior, true);
break;
case kUserScroll:
UserScrollHelper(clamped_offset, behavior);
break;
default:
NOTREACHED();
}
}
void ScrollableArea::ScrollBy(const ScrollOffset& delta,
ScrollType type,
ScrollBehavior behavior) {
SetScrollOffset(GetScrollOffset() + delta, type, behavior);
}
void ScrollableArea::SetScrollOffsetSingleAxis(ScrollbarOrientation orientation,
float offset,
ScrollType scroll_type,
ScrollBehavior behavior) {
ScrollOffset new_offset;
if (orientation == kHorizontalScrollbar)
new_offset =
ScrollOffset(offset, GetScrollAnimator().CurrentOffset().Height());
else
new_offset =
ScrollOffset(GetScrollAnimator().CurrentOffset().Width(), offset);
// TODO(bokan): Note, this doesn't use the derived class versions since this
// method is currently used exclusively by code that adjusts the position by
// the scroll origin and the derived class versions differ on whether they
// take that into account or not.
ScrollableArea::SetScrollOffset(new_offset, scroll_type, behavior);
}
void ScrollableArea::ProgrammaticScrollHelper(const ScrollOffset& offset,
ScrollBehavior scroll_behavior,
bool is_sequenced_scroll) {
CancelScrollAnimation();
if (scroll_behavior == kScrollBehaviorSmooth) {
GetProgrammaticScrollAnimator().AnimateToOffset(offset,
is_sequenced_scroll);
} else {
GetProgrammaticScrollAnimator().ScrollToOffsetWithoutAnimation(
offset, is_sequenced_scroll);
}
}
void ScrollableArea::UserScrollHelper(const ScrollOffset& offset,
ScrollBehavior scroll_behavior) {
CancelProgrammaticScrollAnimation();
if (SmoothScrollSequencer* sequencer = GetSmoothScrollSequencer())
sequencer->AbortAnimations();
float x = UserInputScrollable(kHorizontalScrollbar)
? offset.Width()
: GetScrollAnimator().CurrentOffset().Width();
float y = UserInputScrollable(kVerticalScrollbar)
? offset.Height()
: GetScrollAnimator().CurrentOffset().Height();
// Smooth user scrolls (keyboard, wheel clicks) are handled via the userScroll
// method.
// TODO(bokan): The userScroll method should probably be modified to call this
// method and ScrollAnimatorBase to have a simpler
// animateToOffset method like the ProgrammaticScrollAnimator.
DCHECK_EQ(scroll_behavior, kScrollBehaviorInstant);
GetScrollAnimator().ScrollToOffsetWithoutAnimation(ScrollOffset(x, y));
}
LayoutRect ScrollableArea::ScrollIntoView(
const LayoutRect& rect_in_absolute,
const WebScrollIntoViewParams& params) {
// TODO(bokan): This should really be implemented here but ScrollAlignment is
// in Core which is a dependency violation.
NOTREACHED();
return LayoutRect();
}
void ScrollableArea::ScrollOffsetChanged(const ScrollOffset& offset,
ScrollType scroll_type) {
TRACE_EVENT0("blink", "ScrollableArea::scrollOffsetChanged");
ScrollOffset old_offset = GetScrollOffset();
ScrollOffset truncated_offset = ShouldUseIntegerScrollOffset()
? ScrollOffset(FlooredIntSize(offset))
: offset;
// Tell the derived class to scroll its contents.
UpdateScrollOffset(truncated_offset, scroll_type);
// If the layout object has been detached as a result of updating the scroll
// this object will be cleaned up shortly.
if (HasBeenDisposed())
return;
// Tell the scrollbars to update their thumb postions.
// If the scrollbar does not have its own layer, it must always be
// invalidated to reflect the new thumb offset, even if the theme did not
// invalidate any individual part.
if (Scrollbar* horizontal_scrollbar = this->HorizontalScrollbar())
horizontal_scrollbar->OffsetDidChange();
if (Scrollbar* vertical_scrollbar = this->VerticalScrollbar())
vertical_scrollbar->OffsetDidChange();
if (GetScrollOffset() != old_offset) {
GetScrollAnimator().NotifyContentAreaScrolled(
GetScrollOffset() - old_offset, scroll_type);
}
GetScrollAnimator().SetCurrentOffset(offset);
}
bool ScrollableArea::ScrollBehaviorFromString(const String& behavior_string,
ScrollBehavior& behavior) {
if (behavior_string == "auto")
behavior = kScrollBehaviorAuto;
else if (behavior_string == "instant")
behavior = kScrollBehaviorInstant;
else if (behavior_string == "smooth")
behavior = kScrollBehaviorSmooth;
else
return false;
return true;
}
// NOTE: Only called from Internals for testing.
void ScrollableArea::UpdateScrollOffsetFromInternals(const IntSize& offset) {
ScrollOffsetChanged(ScrollOffset(offset), kProgrammaticScroll);
}
void ScrollableArea::ContentAreaWillPaint() const {
if (ScrollAnimatorBase* scroll_animator = ExistingScrollAnimator())
scroll_animator->ContentAreaWillPaint();
}
void ScrollableArea::MouseEnteredContentArea() const {
if (ScrollAnimatorBase* scroll_animator = ExistingScrollAnimator())
scroll_animator->MouseEnteredContentArea();
}
void ScrollableArea::MouseExitedContentArea() const {
if (ScrollAnimatorBase* scroll_animator = ExistingScrollAnimator())
scroll_animator->MouseEnteredContentArea();
}
void ScrollableArea::MouseMovedInContentArea() const {
if (ScrollAnimatorBase* scroll_animator = ExistingScrollAnimator())
scroll_animator->MouseMovedInContentArea();
}
void ScrollableArea::MouseEnteredScrollbar(Scrollbar& scrollbar) {
mouse_over_scrollbar_ = true;
GetScrollAnimator().MouseEnteredScrollbar(scrollbar);
ShowOverlayScrollbars();
if (fade_overlay_scrollbars_timer_)
fade_overlay_scrollbars_timer_->Stop();
}
void ScrollableArea::MouseExitedScrollbar(Scrollbar& scrollbar) {
mouse_over_scrollbar_ = false;
GetScrollAnimator().MouseExitedScrollbar(scrollbar);
if (HasOverlayScrollbars() && !scrollbars_hidden_if_overlay_) {
// This will kick off the fade out timer.
ShowOverlayScrollbars();
}
}
void ScrollableArea::MouseCapturedScrollbar() {
scrollbar_captured_ = true;
ShowOverlayScrollbars();
if (fade_overlay_scrollbars_timer_)
fade_overlay_scrollbars_timer_->Stop();
}
void ScrollableArea::MouseReleasedScrollbar() {
scrollbar_captured_ = false;
// This will kick off the fade out timer.
ShowOverlayScrollbars();
}
void ScrollableArea::ContentAreaDidShow() const {
if (ScrollAnimatorBase* scroll_animator = ExistingScrollAnimator())
scroll_animator->ContentAreaDidShow();
}
void ScrollableArea::ContentAreaDidHide() const {
if (ScrollAnimatorBase* scroll_animator = ExistingScrollAnimator())
scroll_animator->ContentAreaDidHide();
}
void ScrollableArea::FinishCurrentScrollAnimations() const {
if (ScrollAnimatorBase* scroll_animator = ExistingScrollAnimator())
scroll_animator->FinishCurrentScrollAnimations();
}
void ScrollableArea::DidAddScrollbar(Scrollbar& scrollbar,
ScrollbarOrientation orientation) {
if (orientation == kVerticalScrollbar)
GetScrollAnimator().DidAddVerticalScrollbar(scrollbar);
else
GetScrollAnimator().DidAddHorizontalScrollbar(scrollbar);
// <rdar://problem/9797253> AppKit resets the scrollbar's style when you
// attach a scrollbar
SetScrollbarOverlayColorTheme(GetScrollbarOverlayColorTheme());
}
void ScrollableArea::WillRemoveScrollbar(Scrollbar& scrollbar,
ScrollbarOrientation orientation) {
if (ScrollAnimatorBase* scroll_animator = ExistingScrollAnimator()) {
if (orientation == kVerticalScrollbar)
scroll_animator->WillRemoveVerticalScrollbar(scrollbar);
else
scroll_animator->WillRemoveHorizontalScrollbar(scrollbar);
}
}
void ScrollableArea::ContentsResized() {
if (ScrollAnimatorBase* scroll_animator = ExistingScrollAnimator())
scroll_animator->ContentsResized();
}
bool ScrollableArea::HasOverlayScrollbars() const {
Scrollbar* v_scrollbar = VerticalScrollbar();
if (v_scrollbar && v_scrollbar->IsOverlayScrollbar())
return true;
Scrollbar* h_scrollbar = HorizontalScrollbar();
return h_scrollbar && h_scrollbar->IsOverlayScrollbar();
}
void ScrollableArea::SetScrollbarOverlayColorTheme(
ScrollbarOverlayColorTheme overlay_theme) {
scrollbar_overlay_color_theme_ = overlay_theme;
if (Scrollbar* scrollbar = HorizontalScrollbar()) {
GetPageScrollbarTheme().UpdateScrollbarOverlayColorTheme(*scrollbar);
scrollbar->SetNeedsPaintInvalidation(kAllParts);
}
if (Scrollbar* scrollbar = VerticalScrollbar()) {
GetPageScrollbarTheme().UpdateScrollbarOverlayColorTheme(*scrollbar);
scrollbar->SetNeedsPaintInvalidation(kAllParts);
}
}
void ScrollableArea::RecalculateScrollbarOverlayColorTheme(
Color background_color) {
ScrollbarOverlayColorTheme old_overlay_theme =
GetScrollbarOverlayColorTheme();
ScrollbarOverlayColorTheme overlay_theme = kScrollbarOverlayColorThemeDark;
// Reduce the background color from RGB to a lightness value
// and determine which scrollbar style to use based on a lightness
// heuristic.
double hue, saturation, lightness;
background_color.GetHSL(hue, saturation, lightness);
if (lightness <= .5 && background_color.Alpha())
overlay_theme = kScrollbarOverlayColorThemeLight;
if (old_overlay_theme != overlay_theme)
SetScrollbarOverlayColorTheme(overlay_theme);
}
void ScrollableArea::SetScrollbarNeedsPaintInvalidation(
ScrollbarOrientation orientation) {
if (orientation == kHorizontalScrollbar) {
if (GraphicsLayer* graphics_layer = LayerForHorizontalScrollbar()) {
graphics_layer->SetNeedsDisplay();
graphics_layer->SetContentsNeedsDisplay();
}
horizontal_scrollbar_needs_paint_invalidation_ = true;
} else {
if (GraphicsLayer* graphics_layer = LayerForVerticalScrollbar()) {
graphics_layer->SetNeedsDisplay();
graphics_layer->SetContentsNeedsDisplay();
}
vertical_scrollbar_needs_paint_invalidation_ = true;
}
ScrollControlWasSetNeedsPaintInvalidation();
}
void ScrollableArea::SetScrollCornerNeedsPaintInvalidation() {
if (GraphicsLayer* graphics_layer = LayerForScrollCorner()) {
graphics_layer->SetNeedsDisplay();
return;
}
scroll_corner_needs_paint_invalidation_ = true;
ScrollControlWasSetNeedsPaintInvalidation();
}
bool ScrollableArea::HasLayerForHorizontalScrollbar() const {
return LayerForHorizontalScrollbar();
}
bool ScrollableArea::HasLayerForVerticalScrollbar() const {
return LayerForVerticalScrollbar();
}
bool ScrollableArea::HasLayerForScrollCorner() const {
return LayerForScrollCorner();
}
void ScrollableArea::LayerForScrollingDidChange(
CompositorAnimationTimeline* timeline) {
if (ProgrammaticScrollAnimator* programmatic_scroll_animator =
ExistingProgrammaticScrollAnimator())
programmatic_scroll_animator->LayerForCompositedScrollingDidChange(
timeline);
if (ScrollAnimatorBase* scroll_animator = ExistingScrollAnimator())
scroll_animator->LayerForCompositedScrollingDidChange(timeline);
}
void ScrollableArea::ServiceScrollAnimations(double monotonic_time) {
bool requires_animation_service = false;
if (ScrollAnimatorBase* scroll_animator = ExistingScrollAnimator()) {
scroll_animator->TickAnimation(monotonic_time);
if (scroll_animator->HasAnimationThatRequiresService())
requires_animation_service = true;
}
if (ProgrammaticScrollAnimator* programmatic_scroll_animator =
ExistingProgrammaticScrollAnimator()) {
programmatic_scroll_animator->TickAnimation(monotonic_time);
if (programmatic_scroll_animator->HasAnimationThatRequiresService())
requires_animation_service = true;
}
if (!requires_animation_service)
DeregisterForAnimation();
}
void ScrollableArea::UpdateCompositorScrollAnimations() {
if (ProgrammaticScrollAnimator* programmatic_scroll_animator =
ExistingProgrammaticScrollAnimator())
programmatic_scroll_animator->UpdateCompositorAnimations();
if (ScrollAnimatorBase* scroll_animator = ExistingScrollAnimator())
scroll_animator->UpdateCompositorAnimations();
}
void ScrollableArea::CancelScrollAnimation() {
if (ScrollAnimatorBase* scroll_animator = ExistingScrollAnimator())
scroll_animator->CancelAnimation();
}
void ScrollableArea::CancelProgrammaticScrollAnimation() {
if (ProgrammaticScrollAnimator* programmatic_scroll_animator =
ExistingProgrammaticScrollAnimator())
programmatic_scroll_animator->CancelAnimation();
}
bool ScrollableArea::ShouldScrollOnMainThread() const {
if (GraphicsLayer* layer = LayerForScrolling()) {
uint32_t reasons = layer->CcLayer()->main_thread_scrolling_reasons();
// Should scroll on main thread unless the reason is the one that is set
// by the ScrollAnimator, in which case, the animation can still be
// scheduled on the compositor.
// TODO(ymalik): We have a non-transient "main thread scrolling reason"
// that doesn't actually cause shouldScrollOnMainThread() to be true.
// This is confusing and should be cleaned up.
return !!(reasons &
~MainThreadScrollingReason::kHandlingScrollFromMainThread);
}
return true;
}
bool ScrollableArea::ScrollbarsHiddenIfOverlay() const {
return HasOverlayScrollbars() && scrollbars_hidden_if_overlay_;
}
void ScrollableArea::SetScrollbarsHiddenIfOverlay(bool hidden) {
// If scrollable area has been disposed, we can not get the page scrollbar
// theme setting. Should early return here.
if (HasBeenDisposed())
return;
if (!GetPageScrollbarTheme().UsesOverlayScrollbars())
return;
if (scrollbars_hidden_if_overlay_ == static_cast<unsigned>(hidden))
return;
scrollbars_hidden_if_overlay_ = hidden;
ScrollbarVisibilityChanged();
}
void ScrollableArea::FadeOverlayScrollbarsTimerFired(TimerBase*) {
SetScrollbarsHiddenIfOverlay(true);
}
void ScrollableArea::ShowOverlayScrollbars() {
if (!GetPageScrollbarTheme().UsesOverlayScrollbars())
return;
SetScrollbarsHiddenIfOverlay(false);
needs_show_scrollbar_layers_ = true;
const TimeDelta time_until_disable =
GetPageScrollbarTheme().OverlayScrollbarFadeOutDelay() +
GetPageScrollbarTheme().OverlayScrollbarFadeOutDuration();
// If the overlay scrollbars don't fade out, don't do anything. This is the
// case for the mock overlays used in tests and on Mac, where the fade-out is
// animated in ScrollAnimatorMac.
// We also don't fade out overlay scrollbar for popup since we don't create
// compositor for popup and thus they don't appear on hover so users without
// a wheel can't scroll if they fade out.
if (time_until_disable.is_zero() || GetChromeClient()->IsPopup())
return;
if (!fade_overlay_scrollbars_timer_) {
fade_overlay_scrollbars_timer_.reset(new TaskRunnerTimer<ScrollableArea>(
Platform::Current()->MainThread()->Scheduler()->CompositorTaskRunner(),
this, &ScrollableArea::FadeOverlayScrollbarsTimerFired));
}
if (!scrollbar_captured_ && !mouse_over_scrollbar_) {
fade_overlay_scrollbars_timer_->StartOneShot(time_until_disable, FROM_HERE);
}
}
IntSize ScrollableArea::ClampScrollOffset(const IntSize& scroll_offset) const {
return scroll_offset.ShrunkTo(MaximumScrollOffsetInt())
.ExpandedTo(MinimumScrollOffsetInt());
}
ScrollOffset ScrollableArea::ClampScrollOffset(
const ScrollOffset& scroll_offset) const {
return scroll_offset.ShrunkTo(MaximumScrollOffset())
.ExpandedTo(MinimumScrollOffset());
}
int ScrollableArea::LineStep(ScrollbarOrientation) const {
return PixelsPerLineStep(GetChromeClient());
}
int ScrollableArea::PageStep(ScrollbarOrientation orientation) const {
// Paging scroll operations should take scroll-padding into account [1]. So we
// use the snapport rect to calculate the page step instead of the visible
// rect.
// [1] https://drafts.csswg.org/css-scroll-snap/#scroll-padding
IntSize snapport_size =
VisibleScrollSnapportRect(kIncludeScrollbars).PixelSnappedSize();
int length = (orientation == kHorizontalScrollbar) ? snapport_size.Width()
: snapport_size.Height();
int min_page_step =
static_cast<float>(length) * MinFractionToStepWhenPaging();
int page_step = std::max(min_page_step, length - MaxOverlapBetweenPages());
return std::max(page_step, 1);
}
int ScrollableArea::DocumentStep(ScrollbarOrientation orientation) const {
return ScrollSize(orientation);
}
float ScrollableArea::PixelStep(ScrollbarOrientation) const {
return 1;
}
int ScrollableArea::VerticalScrollbarWidth(
OverlayScrollbarClipBehavior behavior) const {
DCHECK_EQ(behavior, kIgnorePlatformOverlayScrollbarSize);
if (Scrollbar* vertical_bar = VerticalScrollbar())
return !vertical_bar->IsOverlayScrollbar() ? vertical_bar->Width() : 0;
return 0;
}
int ScrollableArea::HorizontalScrollbarHeight(
OverlayScrollbarClipBehavior behavior) const {
DCHECK_EQ(behavior, kIgnorePlatformOverlayScrollbarSize);
if (Scrollbar* horizontal_bar = HorizontalScrollbar())
return !horizontal_bar->IsOverlayScrollbar() ? horizontal_bar->Height() : 0;
return 0;
}
FloatQuad ScrollableArea::LocalToVisibleContentQuad(const FloatQuad& quad,
const LayoutObject*,
unsigned) const {
FloatQuad result(quad);
result.Move(-GetScrollOffset());
return result;
}
IntSize ScrollableArea::ExcludeScrollbars(const IntSize& size) const {
return IntSize(std::max(0, size.Width() - VerticalScrollbarWidth()),
std::max(0, size.Height() - HorizontalScrollbarHeight()));
}
void ScrollableArea::DidScroll(const FloatPoint& position) {
ScrollOffset new_offset(ScrollPositionToOffset(position));
SetScrollOffset(new_offset, kCompositorScroll);
}
CompositorElementId ScrollableArea::GetScrollbarElementId(
ScrollbarOrientation orientation) {
CompositorElementId scrollable_element_id = GetCompositorElementId();
DCHECK(scrollable_element_id);
CompositorElementIdNamespace element_id_namespace =
orientation == kHorizontalScrollbar
? CompositorElementIdNamespace::kHorizontalScrollbar
: CompositorElementIdNamespace::kVerticalScrollbar;
return CompositorElementIdFromUniqueObjectId(
scrollable_element_id.GetInternalValue(), element_id_namespace);
}
void ScrollableArea::Trace(blink::Visitor* visitor) {
visitor->Trace(scroll_animator_);
visitor->Trace(programmatic_scroll_animator_);
}
} // namespace blink