diff options
Diffstat (limited to 'chromium/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc')
-rw-r--r-- | chromium/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc | 514 |
1 files changed, 386 insertions, 128 deletions
diff --git a/chromium/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc b/chromium/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc index ec6d6ca5d58..ee72a82c200 100644 --- a/chromium/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc +++ b/chromium/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc @@ -44,12 +44,14 @@ #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h" +#include "base/numerics/checked_math.h" #include "base/single_thread_task_runner.h" #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/public/platform/task_type.h" #include "third_party/blink/public/platform/web_scroll_into_view_params.h" +#include "third_party/blink/renderer/core/accessibility/ax_object_cache.h" +#include "third_party/blink/renderer/core/animation/scroll_timeline.h" #include "third_party/blink/renderer/core/css/pseudo_style_request.h" -#include "third_party/blink/renderer/core/dom/ax_object_cache.h" #include "third_party/blink/renderer/core/dom/dom_node_ids.h" #include "third_party/blink/renderer/core/dom/node.h" #include "third_party/blink/renderer/core/dom/shadow_root.h" @@ -84,13 +86,15 @@ #include "third_party/blink/renderer/core/page/scrolling/top_document_root_scroller_controller.h" #include "third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h" #include "third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.h" +#include "third_party/blink/renderer/core/paint/find_paint_offset_and_visual_rect_needing_update.h" +#include "third_party/blink/renderer/core/paint/paint_invalidator.h" #include "third_party/blink/renderer/core/paint/paint_layer_fragment.h" #include "third_party/blink/renderer/platform/graphics/graphics_layer.h" #include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h" #include "third_party/blink/renderer/platform/scroll/scroll_alignment.h" #include "third_party/blink/renderer/platform/scroll/scroll_animator_base.h" #include "third_party/blink/renderer/platform/scroll/scrollbar_theme.h" -#include "third_party/blink/renderer/platform/wtf/checked_numeric.h" +#include "third_party/blink/renderer/platform/scroll/smooth_scroll_sequencer.h" namespace blink { @@ -121,8 +125,6 @@ const int kResizerControlExpandRatioForTouch = 2; PaintLayerScrollableArea::PaintLayerScrollableArea(PaintLayer& layer) : layer_(&layer), - next_topmost_scroll_child_(nullptr), - topmost_scroll_child_(nullptr), in_resize_mode_(false), scrolls_overflow_(false), in_overflow_relayout_(false), @@ -134,11 +136,14 @@ PaintLayerScrollableArea::PaintLayerScrollableArea(PaintLayer& layer) needs_relayout_(false), had_horizontal_scrollbar_before_relayout_(false), had_vertical_scrollbar_before_relayout_(false), + scroll_origin_changed_(false), scrollbar_manager_(*this), scroll_corner_(nullptr), resizer_(nullptr), scroll_anchor_(this), - non_composited_main_thread_scrolling_reasons_(0) { + non_composited_main_thread_scrolling_reasons_(0), + horizontal_scrollbar_previously_was_overlay_(false), + vertical_scrollbar_previously_was_overlay_(false) { Node* node = GetLayoutBox()->GetNode(); if (node && node->IsElementNode()) { // We save and restore only the scrollOffset as the other scroll values are @@ -156,8 +161,8 @@ PaintLayerScrollableArea::~PaintLayerScrollableArea() { DCHECK(HasBeenDisposed()); } -void PaintLayerScrollableArea::DidScroll(const gfx::ScrollOffset& offset) { - ScrollableArea::DidScroll(offset); +void PaintLayerScrollableArea::DidScroll(const FloatPoint& position) { + ScrollableArea::DidScroll(position); // This should be alive if it receives composited scroll callbacks. CHECK(!HasBeenDisposed()); } @@ -216,16 +221,6 @@ void PaintLayerScrollableArea::Dispose() { if (SmoothScrollSequencer* sequencer = GetSmoothScrollSequencer()) sequencer->DidDisposeScrollableArea(*this); - { - // Here using the stale compositing data is in fact what we want to do - // because the graphics layer which hasn't been removed yet may be used in - // the meantime to try to deliver scroll related updates. - DisableCompositingQueryAsserts disabler; - GraphicsLayer* graphics_layer = LayerForScrolling(); - if (graphics_layer) - graphics_layer->ScrollableAreaDisposed(); - } - layer_ = nullptr; } @@ -239,24 +234,11 @@ void PaintLayerScrollableArea::Trace(blink::Visitor* visitor) { ScrollableArea::Trace(visitor); } -void PaintLayerScrollableArea::CalculateScrollbarModes( - ScrollbarMode& h_mode, - ScrollbarMode& v_mode) const { - DCHECK(GetLayoutBox()->IsLayoutView()); - - // FrameViewAutoSizeInfo manually controls the appearance of the main frame's - // scrollbars so defer to those if we're in AutoSize mode. - if (AutosizeVerticalScrollbarMode() != kScrollbarAuto || - AutosizeHorizontalScrollbarMode() != kScrollbarAuto) { - h_mode = AutosizeHorizontalScrollbarMode(); - v_mode = AutosizeVerticalScrollbarMode(); - return; - } - - ToLayoutView(GetLayoutBox())->CalculateScrollbarModes(h_mode, v_mode); +bool PaintLayerScrollableArea::IsThrottled() const { + return GetLayoutBox()->GetFrame()->ShouldThrottleRendering(); } -ChromeClient* PaintLayerScrollableArea::GetChromeClient() const { +PlatformChromeClient* PaintLayerScrollableArea::GetChromeClient() const { if (HasBeenDisposed()) return nullptr; if (Page* page = GetLayoutBox()->GetFrame()->GetPage()) @@ -681,9 +663,10 @@ IntRect PaintLayerScrollableArea::VisibleContentRect( GetLayoutBox()->Location())); } -LayoutRect PaintLayerScrollableArea::VisibleScrollSnapportRect() const { +LayoutRect PaintLayerScrollableArea::VisibleScrollSnapportRect( + IncludeScrollbarsInRect scrollbar_inclusion) const { const ComputedStyle* style = GetLayoutBox()->Style(); - LayoutRect layout_content_rect(LayoutContentRect(kExcludeScrollbars)); + LayoutRect layout_content_rect(LayoutContentRect(scrollbar_inclusion)); layout_content_rect.MoveBy(LayoutPoint(-ScrollOrigin())); LayoutRectOutsets padding(MinimumValueForLength(style->ScrollPaddingTop(), layout_content_rect.Height()), @@ -707,10 +690,6 @@ void PaintLayerScrollableArea::ContentsResized() { GetLayoutBox()->SetNeedsPaintPropertyUpdate(); } -bool PaintLayerScrollableArea::IsScrollable() const { - return ScrollsOverflow(); -} - IntPoint PaintLayerScrollableArea::LastKnownMousePosition() const { return GetLayoutBox()->GetFrame() ? GetLayoutBox() ->GetFrame() @@ -733,7 +712,7 @@ bool PaintLayerScrollableArea::ShouldSuspendScrollAnimations() const { LayoutView* view = GetLayoutBox()->View(); if (!view) return true; - return view->GetFrameView()->ShouldSuspendScrollAnimations(); + return !GetLayoutBox()->GetDocument().LoadEventFinished(); } void PaintLayerScrollableArea::ScrollbarVisibilityChanged() { @@ -760,13 +739,20 @@ bool PaintLayerScrollableArea::ScrollbarsCanBeActive() const { LayoutView* view = GetLayoutBox()->View(); if (!view) return false; - return view->GetFrameView()->ScrollbarsCanBeActive(); + + // TODO(szager): This conditional is weird and likely obsolete. Originally + // added in commit eb0d49caaee2b275ff524d3945a74e8d9180eb7d. + LocalFrameView* frame_view = view->GetFrameView(); + if (frame_view != frame_view->GetFrame().View()) + return false; + + return !!frame_view->GetFrame().GetDocument(); } IntRect PaintLayerScrollableArea::ScrollableAreaBoundingBox() const { if (LocalFrame* frame = GetLayoutBox()->GetFrame()) { if (LocalFrameView* local_root = frame->LocalFrameRoot().View()) { - return local_root->RootFrameToDocument(frame->View()->AbsoluteToRootFrame( + return local_root->RootFrameToDocument(frame->View()->ConvertToRootFrame( GetLayoutBox()->AbsoluteBoundingBoxRect(0))); } } @@ -804,7 +790,7 @@ bool PaintLayerScrollableArea::UserInputScrollable( ScrollbarMode h_mode; ScrollbarMode v_mode; - CalculateScrollbarModes(h_mode, v_mode); + ToLayoutView(GetLayoutBox())->CalculateScrollbarModes(h_mode, v_mode); ScrollbarMode mode = (orientation == kHorizontalScrollbar) ? h_mode : v_mode; return mode == kScrollbarAuto || mode == kScrollbarAlwaysOn; @@ -823,12 +809,16 @@ bool PaintLayerScrollableArea::ShouldPlaceVerticalScrollbarOnLeft() const { } int PaintLayerScrollableArea::PageStep(ScrollbarOrientation orientation) const { - int length = - (orientation == kHorizontalScrollbar) ? VisibleWidth() : VisibleHeight(); + // 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().PixelSnappedSize(); + int length = (orientation == kHorizontalScrollbar) ? snapport_size.Width() + : snapport_size.Height(); int min_page_step = static_cast<float>(length) * ScrollableArea::MinFractionToStepWhenPaging(); int page_step = max(min_page_step, length - MaxOverlapBetweenPages()); - return max(page_step, 1); } @@ -866,8 +856,11 @@ void PaintLayerScrollableArea::UpdateScrollOrigin() { LayoutRect scrollable_overflow(overflow_rect_); scrollable_overflow.Move(-GetLayoutBox()->BorderLeft(), -GetLayoutBox()->BorderTop()); - SetScrollOrigin(-scrollable_overflow.PixelSnappedLocation() + - GetLayoutBox()->OriginAdjustmentForScrollbars()); + IntPoint new_origin(-scrollable_overflow.PixelSnappedLocation() + + GetLayoutBox()->OriginAdjustmentForScrollbars()); + if (new_origin != scroll_origin_) + scroll_origin_changed_ = true; + scroll_origin_ = new_origin; } void PaintLayerScrollableArea::UpdateScrollDimensions() { @@ -1039,6 +1032,9 @@ void PaintLayerScrollableArea::UpdateAfterLayout() { } void PaintLayerScrollableArea::ClampScrollOffsetAfterOverflowChange() { + if (HasBeenDisposed()) + return; + // If a vertical scrollbar was removed, the min/max scroll offsets may have // changed, so the scroll offsets needs to be clamped. If the scroll offset // did not change, but the scroll origin *did* change, we still need to notify @@ -1229,13 +1225,8 @@ void PaintLayerScrollableArea::UpdateAfterStyleChange( UpdateResizerStyle(old_style); } -bool PaintLayerScrollableArea::UpdateAfterCompositingChange() { +void PaintLayerScrollableArea::UpdateAfterCompositingChange() { Layer()->UpdateScrollingStateAfterCompositingChange(); - const bool layers_changed = - topmost_scroll_child_ != next_topmost_scroll_child_; - topmost_scroll_child_ = next_topmost_scroll_child_; - next_topmost_scroll_child_ = nullptr; - return layers_changed; } void PaintLayerScrollableArea::UpdateAfterOverflowRecalc() { @@ -1340,43 +1331,63 @@ IntSize PaintLayerScrollableArea::ScrollbarOffset( } static inline const LayoutObject& ScrollbarStyleSource( - const LayoutObject& layout_object) { - if (Node* node = layout_object.GetNode()) { - if (layout_object.IsLayoutView()) { - Document& doc = node->GetDocument(); - if (Settings* settings = doc.GetSettings()) { - if (!settings->GetAllowCustomScrollbarInMainFrame() && - layout_object.GetFrame() && layout_object.GetFrame()->IsMainFrame()) - return layout_object; - } - - // Try the <body> element first as a scrollbar source. - Element* body = doc.body(); - if (body && body->GetLayoutObject() && - body->GetLayoutObject()->Style()->HasPseudoStyle(kPseudoIdScrollbar)) - return *body->GetLayoutObject(); - - // If the <body> didn't have a custom style, then the root element might. - Element* doc_element = doc.documentElement(); - if (doc_element && doc_element->GetLayoutObject() && - doc_element->GetLayoutObject()->Style()->HasPseudoStyle( - kPseudoIdScrollbar)) - return *doc_element->GetLayoutObject(); + const LayoutBox& layout_box) { + if (layout_box.IsLayoutView()) { + Document& doc = layout_box.GetDocument(); + if (Settings* settings = doc.GetSettings()) { + if (!settings->GetAllowCustomScrollbarInMainFrame() && + layout_box.GetFrame() && layout_box.GetFrame()->IsMainFrame()) + return layout_box; } - if (layout_object.StyleRef().HasPseudoStyle(kPseudoIdScrollbar)) - return layout_object; + // Try the <body> element first as a scrollbar source, but only if the body + // can scroll. + Element* body = doc.body(); + if (body && body->GetLayoutObject() && body->GetLayoutObject()->IsBox() && + body->GetLayoutObject()->Style()->HasPseudoStyle(kPseudoIdScrollbar)) + return *body->GetLayoutObject(); - if (ShadowRoot* shadow_root = node->ContainingShadowRoot()) { - if (shadow_root->IsUserAgent()) { - if (LayoutObject* host_layout_object = - shadow_root->host().GetLayoutObject()) - return *host_layout_object; - } - } + // If the <body> didn't have a custom style, then the root element might. + Element* doc_element = doc.documentElement(); + if (doc_element && doc_element->GetLayoutObject() && + doc_element->GetLayoutObject()->Style()->HasPseudoStyle( + kPseudoIdScrollbar)) + return *doc_element->GetLayoutObject(); + } + + return layout_box; +} + +int PaintLayerScrollableArea::HypotheticalScrollbarThickness( + ScrollbarOrientation orientation) const { + Scrollbar* scrollbar = orientation == kHorizontalScrollbar + ? HorizontalScrollbar() + : VerticalScrollbar(); + if (scrollbar) + return scrollbar->ScrollbarThickness(); + + const LayoutObject& style_source = ScrollbarStyleSource(*GetLayoutBox()); + bool has_custom_scrollbar_style = + style_source.StyleRef().HasPseudoStyle(kPseudoIdScrollbar); + if (has_custom_scrollbar_style) { + return LayoutScrollbar::HypotheticalScrollbarThickness( + orientation, *GetLayoutBox(), style_source); } - return layout_object; + ScrollbarControlSize scrollbar_size = kRegularScrollbar; + if (style_source.StyleRef().HasAppearance()) { + scrollbar_size = LayoutTheme::GetTheme().ScrollbarControlSizeForPart( + style_source.StyleRef().Appearance()); + } + ScrollbarTheme& theme = GetPageScrollbarTheme(); + if (theme.UsesOverlayScrollbars()) + return 0; + int thickness = theme.ScrollbarThickness(scrollbar_size); + return GetLayoutBox() + ->GetDocument() + .GetPage() + ->GetChromeClient() + .WindowToViewportScalar(thickness); } bool PaintLayerScrollableArea::NeedsScrollbarReconstruction() const { @@ -1401,8 +1412,10 @@ bool PaintLayerScrollableArea::NeedsScrollbarReconstruction() const { if (needs_custom) { DCHECK(scrollbar->IsCustomScrollbar()); // We have a custom scrollbar with a stale m_owner. - if (ToLayoutScrollbar(scrollbar)->StyleSource() != style_source) + if (ToLayoutScrollbar(scrollbar)->StyleSource()->GetLayoutObject() != + style_source) { return true; + } // Should use custom scrollbar and nothing should change. continue; @@ -1456,7 +1469,7 @@ void PaintLayerScrollableArea::ComputeScrollbarExistence( if (GetLayoutBox()->IsLayoutView()) { ScrollbarMode h_mode; ScrollbarMode v_mode; - CalculateScrollbarModes(h_mode, v_mode); + ToLayoutView(GetLayoutBox())->CalculateScrollbarModes(h_mode, v_mode); // Look for the scrollbarModes and reset the needs Horizontal & vertical // Scrollbar values based on scrollbarModes, as during force style change @@ -1488,7 +1501,7 @@ bool PaintLayerScrollableArea::TryRemovingAutoScrollbars( if (GetLayoutBox()->IsLayoutView()) { ScrollbarMode h_mode; ScrollbarMode v_mode; - CalculateScrollbarModes(h_mode, v_mode); + ToLayoutView(GetLayoutBox())->CalculateScrollbarModes(h_mode, v_mode); if (h_mode != kScrollbarAuto || v_mode != kScrollbarAuto) return false; @@ -1608,7 +1621,7 @@ int PaintLayerScrollableArea::HorizontalScrollbarHeight( return HorizontalScrollbar()->ScrollbarThickness(); } -void PaintLayerScrollableArea::SnapAfterScrollbarDragging( +void PaintLayerScrollableArea::SnapAfterScrollbarScrolling( ScrollbarOrientation orientation) { SnapCoordinator* snap_coordinator = GetLayoutBox()->GetDocument().GetSnapCoordinator(); @@ -1772,8 +1785,8 @@ bool PaintLayerScrollableArea::IsPointInResizeControl( if (!GetLayoutBox()->CanResize()) return false; - IntPoint local_point = RoundedIntPoint( - GetLayoutBox()->AbsoluteToLocal(absolute_point, kUseTransforms)); + IntPoint local_point = RoundedIntPoint(GetLayoutBox()->AbsoluteToLocal( + FloatPoint(absolute_point), kUseTransforms)); IntRect local_bounds(IntPoint(), Layer()->PixelSnappedSize()); return ResizerCornerRect(local_bounds, resizer_hit_test_type) .Contains(local_point); @@ -1901,8 +1914,8 @@ IntSize PaintLayerScrollableArea::OffsetFromResizeCorner( if (GetLayoutBox()->ShouldPlaceBlockDirectionScrollbarOnLogicalLeft()) element_size.SetWidth(0); IntPoint resizer_point = IntPoint(element_size); - IntPoint local_point = RoundedIntPoint( - GetLayoutBox()->AbsoluteToLocal(absolute_point, kUseTransforms)); + IntPoint local_point = RoundedIntPoint(GetLayoutBox()->AbsoluteToLocal( + FloatPoint(absolute_point), kUseTransforms)); return local_point - resizer_point; } @@ -1935,7 +1948,7 @@ void PaintLayerScrollableArea::Resize(const IntPoint& pos, float zoom_factor = GetLayoutBox()->Style()->EffectiveZoom(); IntSize new_offset = - OffsetFromResizeCorner(document.View()->RootFrameToContents(pos)); + OffsetFromResizeCorner(document.View()->ConvertFromRootFrame(pos)); new_offset.SetWidth(new_offset.Width() / zoom_factor); new_offset.SetHeight(new_offset.Height() / zoom_factor); @@ -2081,7 +2094,7 @@ void PaintLayerScrollableArea::UpdateScrollableAreaSet() { if (GetLayoutBox()->IsLayoutView()) { ScrollbarMode h_mode; ScrollbarMode v_mode; - CalculateScrollbarModes(h_mode, v_mode); + ToLayoutView(GetLayoutBox())->CalculateScrollbarModes(h_mode, v_mode); if (h_mode == kScrollbarAlwaysOff && v_mode == kScrollbarAlwaysOff) has_overflow = false; } @@ -2204,8 +2217,13 @@ bool PaintLayerScrollableArea::ComputeNeedsCompositedScrolling( if (CompositingReasonFinder::RequiresCompositingForRootScroller(*layer)) return true; - if (!layer->ScrollsOverflow()) + // TODO(crbug.com/839341): Remove ScrollTimeline check once we support + // main-thread AnimationWorklet and don't need to promote the scroll-source. + Node* node = layer->GetLayoutObject().GetNode(); + if (!layer->ScrollsOverflow() && + !ScrollTimeline::HasActiveScrollTimeline(node)) { return false; + } if (layer->Size().IsEmpty()) return false; @@ -2229,7 +2247,10 @@ bool PaintLayerScrollableArea::ComputeNeedsCompositedScrolling( ToLayoutBox(layer->GetLayoutObject()).PaddingBoxRect()) && !layer->CompositesWithTransform() && !layer->CompositesWithOpacity(); - if (!layer_has_been_composited && + // TODO(crbug.com/839341): Remove ScrollTimeline check once we support + // main-thread AnimationWorklet and don't need to promote the scroll-source. + if (!ScrollTimeline::HasActiveScrollTimeline(node) && + !layer_has_been_composited && !layer->Compositor()->PreferCompositingToLCDTextEnabled() && !background_supports_lcd_text) { if (layer->CompositesWithOpacity()) { @@ -2276,14 +2297,6 @@ void PaintLayerScrollableArea::UpdateNeedsCompositedScrolling( } } -void PaintLayerScrollableArea::SetTopmostScrollChild(PaintLayer* scroll_child) { - // We only want to track the topmost scroll child for scrollable areas with - // overlay scrollbars. - if (!HasOverlayScrollbars()) - return; - next_topmost_scroll_child_ = scroll_child; -} - bool PaintLayerScrollableArea::VisualViewportSuppliesScrollbars() const { LocalFrame* frame = GetLayoutBox()->GetFrame(); if (!frame || !frame->GetSettings()) @@ -2301,7 +2314,7 @@ bool PaintLayerScrollableArea::VisualViewportSuppliesScrollbars() const { } bool PaintLayerScrollableArea::ScheduleAnimation() { - if (ChromeClient* client = GetChromeClient()) { + if (ChromeClient* client = ToChromeClient(GetChromeClient())) { client->ScheduleAnimation(GetLayoutBox()->GetFrame()->View()); return true; } @@ -2326,26 +2339,8 @@ PaintLayerScrollableArea::GetCompositorAnimationTimeline() const { } void PaintLayerScrollableArea::GetTickmarks(Vector<IntRect>& tickmarks) const { - if (layer_->IsRootLayer()) { - tickmarks = GetLayoutBox() - ->GetDocument() - .Markers() - .LayoutRectsForTextMatchMarkers(); - } -} - -PaintLayerScrollableArea* -PaintLayerScrollableArea::ScrollbarManager::ScrollableArea() { - return ToPaintLayerScrollableArea(scrollable_area_.Get()); -} - -void PaintLayerScrollableArea::ScrollbarManager::DestroyDetachedScrollbars() { - DCHECK(!h_bar_is_attached_ || h_bar_); - DCHECK(!v_bar_is_attached_ || v_bar_); - if (h_bar_ && !h_bar_is_attached_) - DestroyScrollbar(kHorizontalScrollbar); - if (v_bar_ && !v_bar_is_attached_) - DestroyScrollbar(kVerticalScrollbar); + if (layer_->IsRootLayer()) + tickmarks = ToLayoutView(GetLayoutBox())->GetTickmarks(); } void PaintLayerScrollableArea::ScrollbarManager::SetHasHorizontalScrollbar( @@ -2396,7 +2391,6 @@ Scrollbar* PaintLayerScrollableArea::ScrollbarManager::CreateScrollbar( const LayoutObject& style_source = ScrollbarStyleSource(*ScrollableArea()->GetLayoutBox()); bool has_custom_scrollbar_style = - style_source.IsBox() && style_source.StyleRef().HasPseudoStyle(kPseudoIdScrollbar); if (has_custom_scrollbar_style) { DCHECK(style_source.GetNode() && style_source.GetNode()->IsElementNode()); @@ -2444,6 +2438,28 @@ void PaintLayerScrollableArea::ScrollbarManager::DestroyScrollbar( scrollbar = nullptr; } +void PaintLayerScrollableArea::ScrollbarManager::DestroyDetachedScrollbars() { + DCHECK(!h_bar_is_attached_ || h_bar_); + DCHECK(!v_bar_is_attached_ || v_bar_); + if (h_bar_ && !h_bar_is_attached_) + DestroyScrollbar(kHorizontalScrollbar); + if (v_bar_ && !v_bar_is_attached_) + DestroyScrollbar(kVerticalScrollbar); +} + +void PaintLayerScrollableArea::ScrollbarManager::Dispose() { + h_bar_is_attached_ = v_bar_is_attached_ = 0; + DestroyScrollbar(kHorizontalScrollbar); + DestroyScrollbar(kVerticalScrollbar); +} + +void PaintLayerScrollableArea::ScrollbarManager::Trace( + blink::Visitor* visitor) { + visitor->Trace(scrollable_area_); + visitor->Trace(h_bar_); + visitor->Trace(v_bar_); +} + uint64_t PaintLayerScrollableArea::Id() const { return DOMNodeIds::IdForNode(GetLayoutBox()->GetNode()); } @@ -2568,4 +2584,246 @@ ScrollbarTheme& PaintLayerScrollableArea::GetPageScrollbarTheme() const { return page->GetScrollbarTheme(); } +void PaintLayerScrollableArea::WillRemoveScrollbar( + Scrollbar& scrollbar, + ScrollbarOrientation orientation) { + if (!scrollbar.IsCustomScrollbar() && + !(orientation == kHorizontalScrollbar ? LayerForHorizontalScrollbar() + : LayerForVerticalScrollbar())) { + ObjectPaintInvalidator(*GetLayoutBox()) + .SlowSetPaintingLayerNeedsRepaintAndInvalidateDisplayItemClient( + scrollbar, PaintInvalidationReason::kScrollControl); + } + + ScrollableArea::WillRemoveScrollbar(scrollbar, orientation); +} + +static LayoutRect ScrollControlVisualRect( + const IntRect& scroll_control_rect, + const LayoutBox& box, + const PaintInvalidatorContext& context, + const LayoutRect& previous_visual_rect) { + LayoutRect visual_rect(scroll_control_rect); +#if DCHECK_IS_ON() + FindVisualRectNeedingUpdateScope finder(box, context, previous_visual_rect, + visual_rect); +#endif + if (!context.NeedsVisualRectUpdate(box)) + return previous_visual_rect; + + // No need to apply any paint offset. Scroll controls paint in a different + // transform space than their contained box (the scrollbarPaintOffset + // transform node). + return visual_rect; +} + +// Returns true if the scroll control is invalidated. +static bool InvalidatePaintOfScrollControlIfNeeded( + const LayoutRect& new_visual_rect, + const LayoutRect& previous_visual_rect, + bool needs_paint_invalidation, + LayoutBox& box, + const LayoutBoxModelObject& paint_invalidation_container) { + bool should_invalidate_new_rect = needs_paint_invalidation; + if (new_visual_rect != previous_visual_rect) { + should_invalidate_new_rect = true; + } else if (previous_visual_rect.IsEmpty()) { + DCHECK(new_visual_rect.IsEmpty()); + // Do not issue an empty invalidation. + should_invalidate_new_rect = false; + } + + return should_invalidate_new_rect; +} + +static LayoutRect InvalidatePaintOfScrollbarIfNeeded( + Scrollbar* scrollbar, + GraphicsLayer* graphics_layer, + bool& previously_was_overlay, + const LayoutRect& previous_visual_rect, + bool needs_paint_invalidation_arg, + LayoutBox& box, + const PaintInvalidatorContext& context) { + bool is_overlay = scrollbar && scrollbar->IsOverlayScrollbar(); + + LayoutRect new_visual_rect; + // Calculate visual rect of the scrollbar, except overlay composited + // scrollbars because we invalidate the graphics layer only. + if (scrollbar && !(graphics_layer && is_overlay)) { + new_visual_rect = ScrollControlVisualRect(scrollbar->FrameRect(), box, + context, previous_visual_rect); + } + + bool needs_paint_invalidation = needs_paint_invalidation_arg; + if (needs_paint_invalidation && graphics_layer) { + // If the scrollbar needs paint invalidation but didn't change location/size + // or the scrollbar is an overlay scrollbar (visual rect is empty), + // invalidating the graphics layer is enough (which has been done in + // ScrollableArea::setScrollbarNeedsPaintInvalidation()). + // Otherwise invalidatePaintOfScrollControlIfNeeded() below will invalidate + // the old and new location of the scrollbar on the box's paint invalidation + // container to ensure newly expanded/shrunk areas of the box to be + // invalidated. + needs_paint_invalidation = false; + DCHECK(!graphics_layer->DrawsContent() || + graphics_layer->GetPaintController().GetPaintArtifact().IsEmpty()); + } + + // Invalidate the box's display item client if the box's padding box size is + // affected by change of the non-overlay scrollbar width. We detect change of + // visual rect size instead of change of scrollbar width change, which may + // have some false-positives (e.g. the scrollbar changed length but not width) + // but won't invalidate more than expected because in the false-positive case + // the box must have changed size and have been invalidated. + const LayoutBoxModelObject& paint_invalidation_container = + *context.paint_invalidation_container; + LayoutSize new_scrollbar_used_space_in_box; + if (!is_overlay) + new_scrollbar_used_space_in_box = new_visual_rect.Size(); + LayoutSize previous_scrollbar_used_space_in_box; + if (!previously_was_overlay) + previous_scrollbar_used_space_in_box = previous_visual_rect.Size(); + + // The IsEmpty() check avoids invalidaiton in cases when the visual rect + // changes from (0,0 0x0) to (0,0 0x100). + if (!(new_scrollbar_used_space_in_box.IsEmpty() && + previous_scrollbar_used_space_in_box.IsEmpty()) && + new_scrollbar_used_space_in_box != previous_scrollbar_used_space_in_box) { + context.painting_layer->SetNeedsRepaint(); + ObjectPaintInvalidator(box).InvalidateDisplayItemClient( + box, PaintInvalidationReason::kGeometry); + } + + bool invalidated = InvalidatePaintOfScrollControlIfNeeded( + new_visual_rect, previous_visual_rect, needs_paint_invalidation, box, + paint_invalidation_container); + + previously_was_overlay = is_overlay; + + if (!invalidated || !scrollbar || graphics_layer) + return new_visual_rect; + + context.painting_layer->SetNeedsRepaint(); + ObjectPaintInvalidator(box).InvalidateDisplayItemClient( + *scrollbar, PaintInvalidationReason::kScrollControl); + if (scrollbar->IsCustomScrollbar()) { + ToLayoutScrollbar(scrollbar) + ->InvalidateDisplayItemClientsOfScrollbarParts(); + } + + return new_visual_rect; +} + +void PaintLayerScrollableArea::InvalidatePaintOfScrollControlsIfNeeded( + const PaintInvalidatorContext& context) { + LayoutBox& box = *GetLayoutBox(); + SetHorizontalScrollbarVisualRect(InvalidatePaintOfScrollbarIfNeeded( + HorizontalScrollbar(), LayerForHorizontalScrollbar(), + horizontal_scrollbar_previously_was_overlay_, + horizontal_scrollbar_visual_rect_, + HorizontalScrollbarNeedsPaintInvalidation(), box, context)); + SetVerticalScrollbarVisualRect(InvalidatePaintOfScrollbarIfNeeded( + VerticalScrollbar(), LayerForVerticalScrollbar(), + vertical_scrollbar_previously_was_overlay_, + vertical_scrollbar_visual_rect_, + VerticalScrollbarNeedsPaintInvalidation(), box, context)); + + LayoutRect scroll_corner_and_resizer_visual_rect = + ScrollControlVisualRect(ScrollCornerAndResizerRect(), box, context, + scroll_corner_and_resizer_visual_rect_); + const LayoutBoxModelObject& paint_invalidation_container = + *context.paint_invalidation_container; + if (InvalidatePaintOfScrollControlIfNeeded( + scroll_corner_and_resizer_visual_rect, + scroll_corner_and_resizer_visual_rect_, + ScrollCornerNeedsPaintInvalidation(), box, + paint_invalidation_container)) { + SetScrollCornerAndResizerVisualRect(scroll_corner_and_resizer_visual_rect); + if (LayoutScrollbarPart* scroll_corner = ScrollCorner()) { + ObjectPaintInvalidator(*scroll_corner) + .InvalidateDisplayItemClientsIncludingNonCompositingDescendants( + PaintInvalidationReason::kScrollControl); + } + if (LayoutScrollbarPart* resizer = Resizer()) { + ObjectPaintInvalidator(*resizer) + .InvalidateDisplayItemClientsIncludingNonCompositingDescendants( + PaintInvalidationReason::kScrollControl); + } + } + + ClearNeedsPaintInvalidationForScrollControls(); +} + +void PaintLayerScrollableArea::ClearPreviousVisualRects() { + SetHorizontalScrollbarVisualRect(LayoutRect()); + SetVerticalScrollbarVisualRect(LayoutRect()); + SetScrollCornerAndResizerVisualRect(LayoutRect()); +} + +void PaintLayerScrollableArea::SetHorizontalScrollbarVisualRect( + const LayoutRect& rect) { + horizontal_scrollbar_visual_rect_ = rect; + if (Scrollbar* scrollbar = HorizontalScrollbar()) + scrollbar->SetVisualRect(rect); +} + +void PaintLayerScrollableArea::SetVerticalScrollbarVisualRect( + const LayoutRect& rect) { + vertical_scrollbar_visual_rect_ = rect; + if (Scrollbar* scrollbar = VerticalScrollbar()) + scrollbar->SetVisualRect(rect); +} + +void PaintLayerScrollableArea::SetScrollCornerAndResizerVisualRect( + const LayoutRect& rect) { + scroll_corner_and_resizer_visual_rect_ = rect; + if (LayoutScrollbarPart* scroll_corner = ScrollCorner()) + scroll_corner->GetMutableForPainting().FirstFragment().SetVisualRect(rect); + if (LayoutScrollbarPart* resizer = Resizer()) + resizer->GetMutableForPainting().FirstFragment().SetVisualRect(rect); +} + +void PaintLayerScrollableArea::ScrollControlWasSetNeedsPaintInvalidation() { + GetLayoutBox()->SetMayNeedPaintInvalidation(); +} + +void PaintLayerScrollableArea::DidScrollWithScrollbar( + ScrollbarPart part, + ScrollbarOrientation orientation) { + WebFeature scrollbar_use_uma; + switch (part) { + case kBackButtonStartPart: + case kForwardButtonStartPart: + case kBackButtonEndPart: + case kForwardButtonEndPart: + scrollbar_use_uma = + (orientation == kVerticalScrollbar + ? WebFeature::kScrollbarUseVerticalScrollbarButton + : WebFeature::kScrollbarUseHorizontalScrollbarButton); + break; + case kThumbPart: + scrollbar_use_uma = + (orientation == kVerticalScrollbar + ? WebFeature::kScrollbarUseVerticalScrollbarThumb + : WebFeature::kScrollbarUseHorizontalScrollbarThumb); + break; + case kBackTrackPart: + case kForwardTrackPart: + scrollbar_use_uma = + (orientation == kVerticalScrollbar + ? WebFeature::kScrollbarUseVerticalScrollbarTrack + : WebFeature::kScrollbarUseHorizontalScrollbarTrack); + break; + default: + return; + } + + UseCounter::Count(GetLayoutBox()->GetDocument(), scrollbar_use_uma); +} + +CompositorElementId PaintLayerScrollableArea::GetCompositorElementId() const { + return CompositorElementIdFromUniqueObjectId( + GetLayoutBox()->UniqueId(), CompositorElementIdNamespace::kScroll); +} + } // namespace blink |