2 * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc. All rights
5 * Portions are Copyright (C) 1998 Netscape Communications Corporation.
8 * Robert O'Callahan <roc+@cs.cmu.edu>
9 * David Baron <dbaron@fas.harvard.edu>
10 * Christian Biesinger <cbiesinger@gmail.com>
11 * Randall Jesup <rjesup@wgate.com>
12 * Roland Mainz <roland.mainz@informatik.med.uni-giessen.de>
13 * Josh Soref <timeless@mac.com>
14 * Boris Zbarsky <bzbarsky@mit.edu>
16 * This library is free software; you can redistribute it and/or
17 * modify it under the terms of the GNU Lesser General Public
18 * License as published by the Free Software Foundation; either
19 * version 2.1 of the License, or (at your option) any later version.
21 * This library is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 * Lesser General Public License for more details.
26 * You should have received a copy of the GNU Lesser General Public
27 * License along with this library; if not, write to the Free Software
28 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
30 * Alternatively, the contents of this file may be used under the terms
31 * of either the Mozilla Public License Version 1.1, found at
32 * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public
33 * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html
34 * (the "GPL"), in which case the provisions of the MPL or the GPL are
35 * applicable instead of those above. If you wish to allow use of your
36 * version of this file only under the terms of one of those two
37 * licenses (the MPL or the GPL) and not to allow others to use your
38 * version of this file under the LGPL, indicate your decision by
39 * deletingthe provisions above and replace them with the notice and
40 * other provisions required by the MPL or the GPL, as the case may be.
41 * If you do not delete the provisions above, a recipient may use your
42 * version of this file under any of the LGPL, the MPL or the GPL.
45 #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
47 #include "base/numerics/checked_math.h"
48 #include "base/single_thread_task_runner.h"
49 #include "cc/input/main_thread_scrolling_reason.h"
50 #include "cc/input/snap_selection_strategy.h"
51 #include "cc/layers/picture_layer.h"
52 #include "third_party/blink/public/common/features.h"
53 #include "third_party/blink/public/mojom/scroll/scroll_into_view_params.mojom-blink.h"
54 #include "third_party/blink/public/mojom/scroll/scrollbar_mode.mojom-blink.h"
55 #include "third_party/blink/public/platform/platform.h"
56 #include "third_party/blink/public/platform/task_type.h"
57 #include "third_party/blink/renderer/core/accessibility/ax_object_cache.h"
58 #include "third_party/blink/renderer/core/animation/scroll_timeline.h"
59 #include "third_party/blink/renderer/core/content_capture/content_capture_manager.h"
60 #include "third_party/blink/renderer/core/css/pseudo_style_request.h"
61 #include "third_party/blink/renderer/core/dom/dom_node_ids.h"
62 #include "third_party/blink/renderer/core/dom/node.h"
63 #include "third_party/blink/renderer/core/dom/shadow_root.h"
64 #include "third_party/blink/renderer/core/editing/frame_selection.h"
65 #include "third_party/blink/renderer/core/editing/markers/document_marker_controller.h"
66 #include "third_party/blink/renderer/core/frame/local_frame.h"
67 #include "third_party/blink/renderer/core/frame/local_frame_client.h"
68 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
69 #include "third_party/blink/renderer/core/frame/page_scale_constraints_set.h"
70 #include "third_party/blink/renderer/core/frame/root_frame_viewport.h"
71 #include "third_party/blink/renderer/core/frame/settings.h"
72 #include "third_party/blink/renderer/core/frame/visual_viewport.h"
73 #include "third_party/blink/renderer/core/fullscreen/fullscreen.h"
74 #include "third_party/blink/renderer/core/html/forms/text_control_element.h"
75 #include "third_party/blink/renderer/core/html/html_frame_owner_element.h"
76 #include "third_party/blink/renderer/core/input/event_handler.h"
77 #include "third_party/blink/renderer/core/layout/custom_scrollbar.h"
78 #include "third_party/blink/renderer/core/layout/layout_custom_scrollbar_part.h"
79 #include "third_party/blink/renderer/core/layout/layout_embedded_content.h"
80 #include "third_party/blink/renderer/core/layout/layout_flexible_box.h"
81 #include "third_party/blink/renderer/core/layout/layout_theme.h"
82 #include "third_party/blink/renderer/core/layout/layout_view.h"
83 #include "third_party/blink/renderer/core/layout/ng/legacy_layout_tree_walking.h"
84 #include "third_party/blink/renderer/core/loader/document_loader.h"
85 #include "third_party/blink/renderer/core/page/chrome_client.h"
86 #include "third_party/blink/renderer/core/page/focus_controller.h"
87 #include "third_party/blink/renderer/core/page/page.h"
88 #include "third_party/blink/renderer/core/page/scrolling/fragment_anchor.h"
89 #include "third_party/blink/renderer/core/page/scrolling/root_scroller_controller.h"
90 #include "third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.h"
91 #include "third_party/blink/renderer/core/page/scrolling/snap_coordinator.h"
92 #include "third_party/blink/renderer/core/page/scrolling/top_document_root_scroller_controller.h"
93 #include "third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h"
94 #include "third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.h"
95 #include "third_party/blink/renderer/core/paint/paint_invalidator.h"
96 #include "third_party/blink/renderer/core/paint/paint_layer_fragment.h"
97 #include "third_party/blink/renderer/core/scroll/scroll_alignment.h"
98 #include "third_party/blink/renderer/core/scroll/scroll_animator_base.h"
99 #include "third_party/blink/renderer/core/scroll/scrollbar_theme.h"
100 #include "third_party/blink/renderer/core/scroll/smooth_scroll_sequencer.h"
101 #include "third_party/blink/renderer/platform/graphics/graphics_layer.h"
102 #include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h"
103 #include "third_party/blink/renderer/platform/heap/heap.h"
104 #include "ui/base/ui_base_features.h"
110 // Default value is set to 15 as the default
111 // minimum size used by firefox is 15x15.
112 static const int kDefaultMinimumWidthForResizing = 15;
113 static const int kDefaultMinimumHeightForResizing = 15;
117 PaintLayerScrollableAreaRareData::PaintLayerScrollableAreaRareData() = default;
119 const int kResizerControlExpandRatioForTouch = 2;
121 PaintLayerScrollableArea::PaintLayerScrollableArea(PaintLayer& layer)
123 in_resize_mode_(false),
124 scrolls_overflow_(false),
125 in_overflow_relayout_(false),
126 allow_second_overflow_relayout_(false),
127 needs_composited_scrolling_(false),
128 rebuild_horizontal_scrollbar_layer_(false),
129 rebuild_vertical_scrollbar_layer_(false),
130 previous_vertical_scrollbar_on_left_(false),
131 needs_scroll_offset_clamp_(false),
132 needs_relayout_(false),
133 had_horizontal_scrollbar_before_relayout_(false),
134 had_vertical_scrollbar_before_relayout_(false),
135 had_resizer_before_relayout_(false),
136 scroll_origin_changed_(false),
137 scrollbar_manager_(*this),
138 has_last_committed_scroll_offset_(false),
139 scroll_corner_(nullptr),
141 scroll_anchor_(this),
142 non_composited_main_thread_scrolling_reasons_(0),
143 horizontal_scrollbar_previously_was_overlay_(false),
144 vertical_scrollbar_previously_was_overlay_(false) {
145 if (auto* element = DynamicTo<Element>(GetLayoutBox()->GetNode())) {
146 // We save and restore only the scrollOffset as the other scroll values are
148 scroll_offset_ = element->SavedLayerScrollOffset();
149 if (!scroll_offset_.IsZero())
150 GetScrollAnimator().SetCurrentOffset(scroll_offset_);
151 element->SetSavedLayerScrollOffset(ScrollOffset());
154 GetLayoutBox()->GetDocument().GetSnapCoordinator().AddSnapContainer(
157 LocalFrame* frame = GetLayoutBox()->GetFrame();
161 LocalFrameView* frame_view = frame->View();
165 frame_view->AddScrollableArea(this);
168 PaintLayerScrollableArea::~PaintLayerScrollableArea() {
169 CHECK(HasBeenDisposed());
172 void PaintLayerScrollableArea::DidScroll(const FloatPoint& position) {
173 ScrollableArea::DidScroll(position);
174 // This should be alive if it receives composited scroll callbacks.
175 CHECK(!HasBeenDisposed());
178 void PaintLayerScrollableArea::DisposeImpl() {
181 GetLayoutBox()->GetDocument().GetSnapCoordinator().RemoveSnapContainer(
184 if (InResizeMode() && !GetLayoutBox()->DocumentBeingDestroyed()) {
185 if (LocalFrame* frame = GetLayoutBox()->GetFrame())
186 frame->GetEventHandler().ResizeScrollableAreaDestroyed();
189 if (LocalFrame* frame = GetLayoutBox()->GetFrame()) {
190 if (LocalFrameView* frame_view = frame->View()) {
191 frame_view->RemoveScrollableArea(this);
192 frame_view->RemoveAnimatingScrollableArea(this);
196 non_composited_main_thread_scrolling_reasons_ = 0;
198 if (ScrollingCoordinator* scrolling_coordinator = GetScrollingCoordinator())
199 scrolling_coordinator->WillDestroyScrollableArea(this);
201 if (!GetLayoutBox()->DocumentBeingDestroyed()) {
202 // FIXME: Make setSavedLayerScrollOffset take DoubleSize. crbug.com/414283.
203 if (auto* element = DynamicTo<Element>(GetLayoutBox()->GetNode()))
204 element->SetSavedLayerScrollOffset(scroll_offset_);
207 // Note: it is not safe to call ScrollAnchor::clear if the document is being
208 // destroyed, because LayoutObjectChildList::removeChildNode skips the call to
209 // willBeRemovedFromTree,
210 // leaving the ScrollAnchor with a stale LayoutObject pointer.
211 scroll_anchor_.Dispose();
216 ->GlobalRootScrollerController()
217 .DidDisposeScrollableArea(*this);
219 scrollbar_manager_.Dispose();
222 scroll_corner_->Destroy();
226 ClearScrollableArea();
228 if (SmoothScrollSequencer* sequencer = GetSmoothScrollSequencer())
229 sequencer->DidDisposeScrollableArea(*this);
231 RunScrollCompleteCallbacks();
232 InvalidateScrollTimeline();
237 void PaintLayerScrollableArea::ApplyPendingHistoryRestoreScrollOffset() {
238 if (!pending_view_state_)
241 // TODO(pnoland): attempt to restore the anchor in more places than this.
242 // Anchor-based restore should allow for earlier restoration.
243 bool did_restore = RestoreScrollAnchor(
244 {pending_view_state_->scroll_anchor_data_.selector_,
245 LayoutPoint(pending_view_state_->scroll_anchor_data_.offset_.x(),
246 pending_view_state_->scroll_anchor_data_.offset_.y()),
247 pending_view_state_->scroll_anchor_data_.simhash_});
249 SetScrollOffset(pending_view_state_->scroll_offset_,
250 mojom::blink::ScrollType::kProgrammatic,
251 mojom::blink::ScrollBehavior::kAuto);
254 pending_view_state_.reset();
257 void PaintLayerScrollableArea::Trace(Visitor* visitor) const {
258 visitor->Trace(scrollbar_manager_);
259 visitor->Trace(scroll_anchor_);
260 visitor->Trace(scrolling_background_display_item_client_);
261 visitor->Trace(scroll_corner_display_item_client_);
262 ScrollableArea::Trace(visitor);
265 bool PaintLayerScrollableArea::IsThrottled() const {
266 return GetLayoutBox()->GetFrame()->ShouldThrottleRendering();
269 ChromeClient* PaintLayerScrollableArea::GetChromeClient() const {
270 if (HasBeenDisposed())
272 if (Page* page = GetLayoutBox()->GetFrame()->GetPage())
273 return &page->GetChromeClient();
277 SmoothScrollSequencer* PaintLayerScrollableArea::GetSmoothScrollSequencer()
279 if (HasBeenDisposed())
282 return &GetLayoutBox()->GetFrame()->GetSmoothScrollSequencer();
285 cc::Layer* PaintLayerScrollableArea::LayerForScrolling() const {
286 if (auto* graphics_layer = GraphicsLayerForScrolling())
287 return graphics_layer->CcLayer();
291 cc::Layer* PaintLayerScrollableArea::LayerForHorizontalScrollbar() const {
292 if (auto* graphics_layer = GraphicsLayerForHorizontalScrollbar())
293 return graphics_layer->ContentsLayer();
297 cc::Layer* PaintLayerScrollableArea::LayerForVerticalScrollbar() const {
298 if (auto* graphics_layer = GraphicsLayerForVerticalScrollbar())
299 return graphics_layer->ContentsLayer();
303 cc::Layer* PaintLayerScrollableArea::LayerForScrollCorner() const {
304 if (auto* graphics_layer = GraphicsLayerForScrollCorner())
305 return graphics_layer->CcLayer();
309 GraphicsLayer* PaintLayerScrollableArea::GraphicsLayerForScrolling() const {
310 return Layer()->HasCompositedLayerMapping()
311 ? Layer()->GetCompositedLayerMapping()->ScrollingContentsLayer()
315 GraphicsLayer* PaintLayerScrollableArea::GraphicsLayerForHorizontalScrollbar()
317 if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
320 // See crbug.com/343132.
321 DisableCompositingQueryAsserts disabler;
323 return Layer()->HasCompositedLayerMapping()
325 ->GetCompositedLayerMapping()
326 ->LayerForHorizontalScrollbar()
330 GraphicsLayer* PaintLayerScrollableArea::GraphicsLayerForVerticalScrollbar()
332 if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
335 // See crbug.com/343132.
336 DisableCompositingQueryAsserts disabler;
338 return Layer()->HasCompositedLayerMapping()
339 ? Layer()->GetCompositedLayerMapping()->LayerForVerticalScrollbar()
343 GraphicsLayer* PaintLayerScrollableArea::GraphicsLayerForScrollCorner() const {
344 if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
347 // See crbug.com/343132.
348 DisableCompositingQueryAsserts disabler;
350 return Layer()->HasCompositedLayerMapping()
351 ? Layer()->GetCompositedLayerMapping()->LayerForScrollCorner()
355 bool PaintLayerScrollableArea::IsActive() const {
356 Page* page = GetLayoutBox()->GetFrame()->GetPage();
357 return page && page->GetFocusController().IsActive();
360 bool PaintLayerScrollableArea::IsScrollCornerVisible() const {
361 return !ScrollCornerRect().IsEmpty();
364 static int CornerStart(const LayoutBox& box,
368 if (box.ShouldPlaceBlockDirectionScrollbarOnLogicalLeft())
369 return min_x + box.StyleRef().BorderLeftWidth();
370 return max_x - thickness - box.StyleRef().BorderRightWidth();
373 IntRect PaintLayerScrollableArea::CornerRect() const {
374 int horizontal_thickness;
375 int vertical_thickness;
376 if (!VerticalScrollbar() && !HorizontalScrollbar()) {
377 // FIXME: This isn't right. We need to know the thickness of custom
378 // scrollbars even when they don't exist in order to set the resizer square
380 horizontal_thickness =
385 .WindowToViewportScalar(
386 GetLayoutBox()->GetFrame(),
387 GetPageScrollbarTheme().ScrollbarThickness());
388 vertical_thickness = horizontal_thickness;
389 } else if (VerticalScrollbar() && !HorizontalScrollbar()) {
390 horizontal_thickness = VerticalScrollbar()->ScrollbarThickness();
391 vertical_thickness = horizontal_thickness;
392 } else if (HorizontalScrollbar() && !VerticalScrollbar()) {
393 vertical_thickness = HorizontalScrollbar()->ScrollbarThickness();
394 horizontal_thickness = vertical_thickness;
396 horizontal_thickness = VerticalScrollbar()->ScrollbarThickness();
397 vertical_thickness = HorizontalScrollbar()->ScrollbarThickness();
399 IntSize border_box_size = PixelSnappedBorderBoxSize();
400 return IntRect(CornerStart(*GetLayoutBox(), 0, border_box_size.Width(),
401 horizontal_thickness),
402 border_box_size.Height() - vertical_thickness -
403 GetLayoutBox()->StyleRef().BorderBottomWidth(),
404 horizontal_thickness, vertical_thickness);
407 IntRect PaintLayerScrollableArea::ScrollCornerRect() const {
408 // We have a scrollbar corner when a scrollbar is visible and not filling the
409 // entire length of the box.
410 // This happens when:
411 // (a) A resizer is present and at least one scrollbar is present
412 // (b) Both scrollbars are present.
413 bool has_horizontal_bar = HorizontalScrollbar();
414 bool has_vertical_bar = VerticalScrollbar();
415 bool has_resizer = GetLayoutBox()->StyleRef().HasResize();
416 if ((has_horizontal_bar && has_vertical_bar) ||
417 (has_resizer && (has_horizontal_bar || has_vertical_bar))) {
423 void PaintLayerScrollableArea::SetScrollbarNeedsPaintInvalidation(
424 ScrollbarOrientation orientation) {
425 if (auto* graphics_layer = orientation == kHorizontalScrollbar
426 ? GraphicsLayerForHorizontalScrollbar()
427 : GraphicsLayerForVerticalScrollbar()) {
428 graphics_layer->SetNeedsDisplay();
429 graphics_layer->SetContentsNeedsDisplay();
431 ScrollableArea::SetScrollbarNeedsPaintInvalidation(orientation);
434 void PaintLayerScrollableArea::SetScrollCornerNeedsPaintInvalidation() {
435 if (GraphicsLayer* graphics_layer = GraphicsLayerForScrollCorner()) {
436 graphics_layer->SetNeedsDisplay();
439 ScrollableArea::SetScrollCornerNeedsPaintInvalidation();
443 PaintLayerScrollableArea::ConvertFromScrollbarToContainingEmbeddedContentView(
444 const Scrollbar& scrollbar,
445 const IntRect& scrollbar_rect) const {
446 LayoutView* view = GetLayoutBox()->View();
448 return scrollbar_rect;
450 IntRect rect = scrollbar_rect;
451 rect.Move(ScrollbarOffset(scrollbar));
452 return PixelSnappedIntRect(
453 GetLayoutBox()->LocalToAbsoluteRect(PhysicalRect(rect)));
457 PaintLayerScrollableArea::ConvertFromScrollbarToContainingEmbeddedContentView(
458 const Scrollbar& scrollbar,
459 const IntPoint& scrollbar_point) const {
460 LayoutView* view = GetLayoutBox()->View();
462 return scrollbar_point;
464 IntPoint point = scrollbar_point;
465 point.Move(ScrollbarOffset(scrollbar));
466 return RoundedIntPoint(
467 GetLayoutBox()->LocalToAbsolutePoint(PhysicalOffset(point)));
471 PaintLayerScrollableArea::ConvertFromContainingEmbeddedContentViewToScrollbar(
472 const Scrollbar& scrollbar,
473 const IntPoint& parent_point) const {
474 LayoutView* view = GetLayoutBox()->View();
478 IntPoint point(RoundedIntPoint(
479 GetLayoutBox()->AbsoluteToLocalPoint(PhysicalOffset(parent_point))));
480 point.Move(-ScrollbarOffset(scrollbar));
484 IntPoint PaintLayerScrollableArea::ConvertFromRootFrame(
485 const IntPoint& point_in_root_frame) const {
486 LayoutView* view = GetLayoutBox()->View();
488 return point_in_root_frame;
490 return view->GetFrameView()->ConvertFromRootFrame(point_in_root_frame);
493 int PaintLayerScrollableArea::ScrollSize(
494 ScrollbarOrientation orientation) const {
495 IntSize scroll_dimensions =
496 MaximumScrollOffsetInt() - MinimumScrollOffsetInt();
497 return (orientation == kHorizontalScrollbar) ? scroll_dimensions.Width()
498 : scroll_dimensions.Height();
501 void PaintLayerScrollableArea::UpdateScrollOffset(
502 const ScrollOffset& new_offset,
503 mojom::blink::ScrollType scroll_type) {
504 if (HasBeenDisposed() || GetScrollOffset() == new_offset)
507 TRACE_EVENT2("blink", "PaintLayerScrollableArea::UpdateScrollOffset", "x",
508 new_offset.Width(), "y", new_offset.Height());
509 TRACE_EVENT_INSTANT1("blink", "Type", TRACE_EVENT_SCOPE_THREAD, "type",
512 scroll_offset_ = new_offset;
514 LocalFrame* frame = GetLayoutBox()->GetFrame();
517 LocalFrameView* frame_view = GetLayoutBox()->GetFrameView();
518 bool is_root_layer = Layer()->IsRootLayer();
520 TRACE_EVENT1("devtools.timeline", "ScrollLayer", "data",
521 inspector_scroll_layer_event::Data(GetLayoutBox()));
523 // Update the positions of our child layers (if needed as only fixed layers
524 // should be impacted by a scroll).
525 if (!frame_view->IsInPerformLayout()) {
526 if (!Layer()->IsRootLayer()) {
527 Layer()->SetNeedsCompositingInputsUpdate(false);
528 Layer()->ClearClipRects();
531 // Update regions, scrolling may change the clip of a particular region.
532 frame_view->UpdateDocumentAnnotatedRegions();
534 // As a performance optimization, the scroll offset of the root layer is
535 // not included in EmbeddedContentView's stored frame rect, so there is no
536 // reason to mark the FrameView as needing a geometry update here.
538 frame_view->SetRootLayerDidScroll();
540 frame_view->SetNeedsUpdateGeometries();
543 if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
544 if (auto* scrolling_coordinator = GetScrollingCoordinator())
545 scrolling_coordinator->UpdateCompositorScrollOffset(*frame, *this);
547 UpdateCompositingLayersAfterScroll();
550 // The ScrollOffsetTranslation paint property depends on the scroll offset.
551 // (see: PaintPropertyTreeBuilder::UpdateScrollAndScrollTranslation).
552 GetLayoutBox()->SetNeedsPaintPropertyUpdatePreservingCachedRects();
553 InvalidateScrollTimeline();
555 if (scroll_type == mojom::blink::ScrollType::kUser ||
556 scroll_type == mojom::blink::ScrollType::kCompositor) {
557 Page* page = frame->GetPage();
559 page->GetChromeClient().ClearToolTip(*frame);
562 InvalidatePaintForScrollOffsetChange();
564 // Don't enqueue a scroll event yet for scroll reasons that are not about
565 // explicit changes to scroll. Instead, only do so at the time of the next
566 // lifecycle update, to avoid scroll events that are out of date or don't
567 // result in an actual scroll that is visible to the user. These scroll events
568 // will then be dispatched at the *subsequent* animation frame, because
569 // they happen after layout and therefore the next opportunity to fire the
570 // events is at the next lifecycle update (*).
572 // (*) https://html.spec.whatwg.org/#update-the-rendering steps
573 if (scroll_type == mojom::blink::ScrollType::kClamping ||
574 scroll_type == mojom::blink::ScrollType::kAnchoring) {
575 if (GetLayoutBox()->GetNode())
576 frame_view->SetNeedsEnqueueScrollEvent(this);
578 EnqueueScrollEventIfNeeded();
581 GetLayoutBox()->View()->ClearHitTestCache();
583 // Inform the FrameLoader of the new scroll position, so it can be restored
584 // when navigating back.
586 frame_view->GetFrame().Loader().SaveScrollState();
587 frame_view->DidChangeScrollOffset();
588 if (scroll_type == mojom::blink::ScrollType::kCompositor ||
589 scroll_type == mojom::blink::ScrollType::kUser) {
590 if (DocumentLoader* document_loader = frame->Loader().GetDocumentLoader())
591 document_loader->GetInitialScrollState().was_scrolled_by_user = true;
595 if (FragmentAnchor* anchor = frame_view->GetFragmentAnchor())
596 anchor->DidScroll(scroll_type);
598 if (IsExplicitScrollType(scroll_type)) {
599 if (scroll_type != mojom::blink::ScrollType::kCompositor)
600 ShowNonMacOverlayScrollbars();
601 GetScrollAnchor()->Clear();
603 if (ContentCaptureManager* manager =
604 frame_view->GetFrame().LocalFrameRoot().GetContentCaptureManager()) {
605 manager->OnScrollPositionChanged();
607 if (AXObjectCache* cache =
608 GetLayoutBox()->GetDocument().ExistingAXObjectCache())
609 cache->HandleScrollPositionChanged(GetLayoutBox());
612 void PaintLayerScrollableArea::InvalidatePaintForScrollOffsetChange() {
613 InvalidatePaintForStickyDescendants();
615 auto* box = GetLayoutBox();
616 auto* frame_view = box->GetFrameView();
617 frame_view->InvalidateBackgroundAttachmentFixedDescendantsOnScroll(*box);
619 if (IsA<LayoutView>(box) && frame_view->HasViewportConstrainedObjects() &&
620 !frame_view->InvalidateViewportConstrainedObjects()) {
621 box->SetShouldDoFullPaintInvalidation();
622 box->SetSubtreeShouldCheckForPaintInvalidation();
625 // TODO(chrishtr): remove this slow path once crbug.com/906885 is fixed.
626 // See also https://bugs.chromium.org/p/chromium/issues/detail?id=903287#c10.
627 if (Layer()->EnclosingPaginationLayer())
628 box->SetSubtreeShouldCheckForPaintInvalidation();
630 if (!box->BackgroundNeedsFullPaintInvalidation()) {
631 auto background_paint_location = box->GetBackgroundPaintLocation();
632 bool background_paint_in_graphics_layer =
633 background_paint_location & kBackgroundPaintInGraphicsLayer;
634 bool background_paint_in_scrolling_contents =
635 background_paint_location & kBackgroundPaintInScrollingContents;
637 // Both local attachment background painted in graphics layer and normal
638 // attachment background painted in scrolling contents require paint
639 // invalidation. Fixed attachment background has been dealt with in
640 // frame_view->InvalidateBackgroundAttachmentFixedDescendantsOnScroll().
641 auto background_layers = box->StyleRef().BackgroundLayers();
642 if ((background_layers.AnyLayerHasLocalAttachmentImage() &&
643 background_paint_in_graphics_layer) ||
644 (background_layers.AnyLayerHasDefaultAttachmentImage() &&
645 background_paint_in_scrolling_contents))
646 box->SetBackgroundNeedsFullPaintInvalidation();
649 // If any scrolling content might have been clipped by a cull rect, then
650 // that cull rect could be affected by scroll offset. For composited
651 // scrollers, this will be taken care of by the interest rect computation
652 // in CompositedLayerMapping.
653 // TODO(wangxianzhu): replace this shortcut with interest rects.
654 if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled() ||
655 !UsesCompositedScrolling())
656 Layer()->SetNeedsRepaint();
659 IntSize PaintLayerScrollableArea::ScrollOffsetInt() const {
660 return FlooredIntSize(scroll_offset_);
663 ScrollOffset PaintLayerScrollableArea::GetScrollOffset() const {
664 return scroll_offset_;
667 void PaintLayerScrollableArea::EnqueueScrollEventIfNeeded() {
668 if (scroll_offset_ == last_committed_scroll_offset_ &&
669 has_last_committed_scroll_offset_)
671 last_committed_scroll_offset_ = scroll_offset_;
672 has_last_committed_scroll_offset_ = true;
673 if (HasBeenDisposed())
675 // Schedule the scroll DOM event.
676 if (GetLayoutBox()->GetNode()) {
677 GetLayoutBox()->GetNode()->GetDocument().EnqueueScrollEventForNode(
678 GetLayoutBox()->GetNode());
682 IntSize PaintLayerScrollableArea::MinimumScrollOffsetInt() const {
683 return ToIntSize(-ScrollOrigin());
686 IntSize PaintLayerScrollableArea::MaximumScrollOffsetInt() const {
687 if (!GetLayoutBox() || !GetLayoutBox()->HasOverflowClip())
688 return ToIntSize(-ScrollOrigin());
690 IntSize content_size = ContentsSize();
692 Page* page = GetLayoutBox()->GetDocument().GetPage();
694 TopDocumentRootScrollerController& controller =
695 page->GlobalRootScrollerController();
697 // The global root scroller should be clipped by the top LocalFrameView rather
698 // than it's overflow clipping box. This is to ensure that content exposed by
699 // hiding the URL bar at the bottom of the screen is visible.
700 IntSize visible_size;
701 if (this == controller.RootScrollerArea()) {
702 visible_size = controller.RootScrollerVisibleArea();
705 PixelSnappedIntRect(GetLayoutBox()->OverflowClipRect(
706 GetLayoutBox()->Location(),
707 kIgnorePlatformAndCSSOverlayScrollbarSize))
711 // TODO(skobes): We should really ASSERT that contentSize >= visibleSize
712 // when we are not the root layer, but we can't because contentSize is
713 // based on stale layout overflow data (http://crbug.com/576933).
714 content_size = content_size.ExpandedTo(visible_size);
716 return ToIntSize(-ScrollOrigin() + (content_size - visible_size));
719 void PaintLayerScrollableArea::VisibleSizeChanged() {
720 InvalidateScrollTimeline();
721 ShowNonMacOverlayScrollbars();
724 PhysicalRect PaintLayerScrollableArea::LayoutContentRect(
725 IncludeScrollbarsInRect scrollbar_inclusion) const {
726 // LayoutContentRect is conceptually the same as the box's client rect.
727 LayoutSize layer_size(Layer()->Size());
728 LayoutUnit border_width = GetLayoutBox()->BorderWidth();
729 LayoutUnit border_height = GetLayoutBox()->BorderHeight();
730 LayoutUnit horizontal_scrollbar_height, vertical_scrollbar_width;
731 if (scrollbar_inclusion == kExcludeScrollbars) {
732 horizontal_scrollbar_height = LayoutUnit(
733 HorizontalScrollbar() && !HorizontalScrollbar()->IsOverlayScrollbar()
734 ? HorizontalScrollbar()->ScrollbarThickness()
736 vertical_scrollbar_width = LayoutUnit(
737 VerticalScrollbar() && !VerticalScrollbar()->IsOverlayScrollbar()
738 ? VerticalScrollbar()->ScrollbarThickness()
743 layer_size.Width() - border_width - vertical_scrollbar_width,
744 layer_size.Height() - border_height - horizontal_scrollbar_height);
745 size.ClampNegativeToZero();
746 return PhysicalRect(PhysicalOffset::FromFloatPointRound(ScrollPosition()),
750 IntRect PaintLayerScrollableArea::VisibleContentRect(
751 IncludeScrollbarsInRect scrollbar_inclusion) const {
752 PhysicalRect layout_content_rect(LayoutContentRect(scrollbar_inclusion));
753 // TODO(szager): It's not clear that Floor() is the right thing to do here;
754 // what is the correct behavior for fractional scroll offsets?
755 return IntRect(FlooredIntPoint(layout_content_rect.offset),
756 PixelSnappedIntSize(layout_content_rect.size.ToLayoutSize(),
757 GetLayoutBox()->Location()));
760 PhysicalRect PaintLayerScrollableArea::VisibleScrollSnapportRect(
761 IncludeScrollbarsInRect scrollbar_inclusion) const {
762 const ComputedStyle* style = GetLayoutBox()->Style();
763 PhysicalRect layout_content_rect(LayoutContentRect(scrollbar_inclusion));
764 layout_content_rect.Move(PhysicalOffset(-ScrollOrigin()));
765 LayoutRectOutsets padding(MinimumValueForLength(style->ScrollPaddingTop(),
766 layout_content_rect.Height()),
767 MinimumValueForLength(style->ScrollPaddingRight(),
768 layout_content_rect.Width()),
769 MinimumValueForLength(style->ScrollPaddingBottom(),
770 layout_content_rect.Height()),
771 MinimumValueForLength(style->ScrollPaddingLeft(),
772 layout_content_rect.Width()));
773 layout_content_rect.Contract(padding);
774 return layout_content_rect;
777 IntSize PaintLayerScrollableArea::ContentsSize() const {
778 PhysicalOffset offset(
779 GetLayoutBox()->ClientLeft() + GetLayoutBox()->Location().X(),
780 GetLayoutBox()->ClientTop() + GetLayoutBox()->Location().Y());
781 // TODO(crbug.com/962299): The pixel snapping is incorrect in some cases.
782 return PixelSnappedContentsSize(offset);
785 IntSize PaintLayerScrollableArea::PixelSnappedContentsSize(
786 const PhysicalOffset& paint_offset) const {
787 return PixelSnappedIntRect(PhysicalRect(paint_offset, overflow_rect_.size))
791 void PaintLayerScrollableArea::ContentsResized() {
792 ScrollableArea::ContentsResized();
793 // Need to update the bounds of the scroll property.
794 GetLayoutBox()->SetNeedsPaintPropertyUpdate();
795 Layer()->SetNeedsCompositingInputsUpdate();
796 InvalidateScrollTimeline();
799 IntPoint PaintLayerScrollableArea::LastKnownMousePosition() const {
800 return GetLayoutBox()->GetFrame()
801 ? FlooredIntPoint(GetLayoutBox()
804 .LastKnownMousePositionInRootFrame())
808 bool PaintLayerScrollableArea::ScrollAnimatorEnabled() const {
809 if (HasBeenDisposed())
811 if (Settings* settings = GetLayoutBox()->GetFrame()->GetSettings())
812 return settings->GetScrollAnimatorEnabled();
816 bool PaintLayerScrollableArea::ShouldSuspendScrollAnimations() const {
817 if (HasBeenDisposed())
819 LayoutView* view = GetLayoutBox()->View();
822 return !GetLayoutBox()->GetDocument().LoadEventFinished();
825 void PaintLayerScrollableArea::ScrollbarVisibilityChanged() {
826 UpdateScrollbarEnabledState();
828 // Paint properties need to be updated, because clip rects
829 // are affected by overlay scrollbars.
830 layer_->GetLayoutObject().SetNeedsPaintPropertyUpdate();
832 // TODO(chrishr): this should be able to be removed.
833 layer_->ClearClipRects();
835 if (LayoutView* view = GetLayoutBox()->View())
836 view->ClearHitTestCache();
839 void PaintLayerScrollableArea::ScrollbarFrameRectChanged() {
840 // Size of non-overlay scrollbar affects overflow clip rect.
841 if (!HasOverlayScrollbars())
842 GetLayoutBox()->SetNeedsPaintPropertyUpdate();
845 bool PaintLayerScrollableArea::ScrollbarsCanBeActive() const {
846 LayoutView* view = GetLayoutBox()->View();
850 // TODO(szager): This conditional is weird and likely obsolete. Originally
851 // added in commit eb0d49caaee2b275ff524d3945a74e8d9180eb7d.
852 LocalFrameView* frame_view = view->GetFrameView();
853 if (frame_view != frame_view->GetFrame().View())
856 return !!frame_view->GetFrame().GetDocument();
859 void PaintLayerScrollableArea::RegisterForAnimation() {
860 if (HasBeenDisposed())
862 if (LocalFrame* frame = GetLayoutBox()->GetFrame()) {
863 if (LocalFrameView* frame_view = frame->View())
864 frame_view->AddAnimatingScrollableArea(this);
868 void PaintLayerScrollableArea::DeregisterForAnimation() {
869 if (HasBeenDisposed())
871 if (LocalFrame* frame = GetLayoutBox()->GetFrame()) {
872 if (LocalFrameView* frame_view = frame->View())
873 frame_view->RemoveAnimatingScrollableArea(this);
877 bool PaintLayerScrollableArea::UserInputScrollable(
878 ScrollbarOrientation orientation) const {
879 if (orientation == kVerticalScrollbar &&
880 GetLayoutBox()->GetDocument().IsVerticalScrollEnforced()) {
884 if (GetLayoutBox()->IsIntrinsicallyScrollable(orientation))
887 if (IsA<LayoutView>(GetLayoutBox())) {
888 Document& document = GetLayoutBox()->GetDocument();
889 Element* fullscreen_element = Fullscreen::FullscreenElementFrom(document);
890 if (fullscreen_element && fullscreen_element != document.documentElement())
893 mojom::blink::ScrollbarMode h_mode;
894 mojom::blink::ScrollbarMode v_mode;
895 To<LayoutView>(GetLayoutBox())->CalculateScrollbarModes(h_mode, v_mode);
896 mojom::blink::ScrollbarMode mode =
897 (orientation == kHorizontalScrollbar) ? h_mode : v_mode;
898 return mode == mojom::blink::ScrollbarMode::kAuto ||
899 mode == mojom::blink::ScrollbarMode::kAlwaysOn;
902 EOverflow overflow_style = (orientation == kHorizontalScrollbar)
903 ? GetLayoutBox()->StyleRef().OverflowX()
904 : GetLayoutBox()->StyleRef().OverflowY();
905 return (overflow_style == EOverflow::kScroll ||
906 overflow_style == EOverflow::kAuto ||
907 overflow_style == EOverflow::kOverlay);
910 bool PaintLayerScrollableArea::ShouldPlaceVerticalScrollbarOnLeft() const {
911 return GetLayoutBox()->ShouldPlaceBlockDirectionScrollbarOnLogicalLeft();
914 int PaintLayerScrollableArea::PageStep(ScrollbarOrientation orientation) const {
915 // Paging scroll operations should take scroll-padding into account [1]. So we
916 // use the snapport rect to calculate the page step instead of the visible
918 // [1] https://drafts.csswg.org/css-scroll-snap/#scroll-padding
919 IntSize snapport_size = VisibleScrollSnapportRect().PixelSnappedSize();
920 int length = (orientation == kHorizontalScrollbar) ? snapport_size.Width()
921 : snapport_size.Height();
922 int min_page_step = static_cast<float>(length) *
923 ScrollableArea::MinFractionToStepWhenPaging();
924 int page_step = max(min_page_step, length - MaxOverlapBetweenPages());
925 return max(page_step, 1);
928 LayoutBox* PaintLayerScrollableArea::GetLayoutBox() const {
929 return layer_ ? layer_->GetLayoutBox() : nullptr;
932 PaintLayer* PaintLayerScrollableArea::Layer() const {
936 LayoutUnit PaintLayerScrollableArea::ScrollWidth() const {
937 return overflow_rect_.Width();
940 LayoutUnit PaintLayerScrollableArea::ScrollHeight() const {
941 return overflow_rect_.Height();
944 void PaintLayerScrollableArea::UpdateScrollOrigin() {
945 // This should do nothing prior to first layout; the if-clause will catch
947 if (overflow_rect_.IsEmpty())
949 PhysicalRect scrollable_overflow = overflow_rect_;
950 scrollable_overflow.Move(-PhysicalOffset(GetLayoutBox()->BorderLeft(),
951 GetLayoutBox()->BorderTop()));
952 IntPoint new_origin(FlooredIntPoint(-scrollable_overflow.offset) +
953 GetLayoutBox()->OriginAdjustmentForScrollbars());
954 if (new_origin != scroll_origin_)
955 scroll_origin_changed_ = true;
956 scroll_origin_ = new_origin;
959 void PaintLayerScrollableArea::UpdateScrollDimensions() {
960 PhysicalRect new_overflow_rect = GetLayoutBox()->PhysicalLayoutOverflowRect();
962 // The layout viewport can be larger than the document's layout overflow when
963 // top controls are hidden. Expand the overflow here to ensure that our
964 // contents size >= visible size.
965 new_overflow_rect.Unite(PhysicalRect(
966 new_overflow_rect.offset, LayoutContentRect(kExcludeScrollbars).size));
968 bool resized = overflow_rect_.size != new_overflow_rect.size;
969 overflow_rect_ = new_overflow_rect;
972 UpdateScrollOrigin();
975 void PaintLayerScrollableArea::UpdateScrollbarEnabledState() {
977 GetPageScrollbarTheme().ShouldDisableInvisibleScrollbars() &&
978 ScrollbarsHiddenIfOverlay();
980 if (HorizontalScrollbar())
981 HorizontalScrollbar()->SetEnabled(HasHorizontalOverflow() &&
983 if (VerticalScrollbar())
984 VerticalScrollbar()->SetEnabled(HasVerticalOverflow() && !force_disable);
987 void PaintLayerScrollableArea::UpdateScrollbarProportions() {
988 if (Scrollbar* horizontal_scrollbar = HorizontalScrollbar())
989 horizontal_scrollbar->SetProportion(VisibleWidth(), ContentsSize().Width());
990 if (Scrollbar* vertical_scrollbar = VerticalScrollbar())
991 vertical_scrollbar->SetProportion(VisibleHeight(), ContentsSize().Height());
994 void PaintLayerScrollableArea::SetScrollOffsetUnconditionally(
995 const ScrollOffset& offset,
996 mojom::blink::ScrollType scroll_type) {
997 CancelScrollAnimation();
998 ScrollOffsetChanged(offset, scroll_type);
1001 void PaintLayerScrollableArea::UpdateAfterLayout() {
1002 bool scrollbars_are_frozen =
1003 (in_overflow_relayout_ && !allow_second_overflow_relayout_) ||
1004 FreezeScrollbarsScope::ScrollbarsAreFrozen();
1005 allow_second_overflow_relayout_ = false;
1007 if (NeedsScrollbarReconstruction()) {
1008 SetHasHorizontalScrollbar(false);
1009 SetHasVerticalScrollbar(false);
1010 // In case that DelayScrollOffsetClampScope prevented destruction of the
1012 scrollbar_manager_.DestroyDetachedScrollbars();
1015 UpdateScrollDimensions();
1017 bool has_resizer = GetLayoutBox()->CanResize();
1018 bool resizer_will_change = had_resizer_before_relayout_ != has_resizer;
1019 had_resizer_before_relayout_ = has_resizer;
1021 bool had_horizontal_scrollbar = HasHorizontalScrollbar();
1022 bool had_vertical_scrollbar = HasVerticalScrollbar();
1024 bool needs_horizontal_scrollbar;
1025 bool needs_vertical_scrollbar;
1026 ComputeScrollbarExistence(needs_horizontal_scrollbar,
1027 needs_vertical_scrollbar);
1029 // Removing auto scrollbars is a heuristic and can be incorrect if the content
1030 // size depends on the scrollbar size (e.g., sized with percentages). Removing
1031 // scrollbars can require two additional layout passes so this is only done on
1032 // the first layout (!in_overflow_layout).
1033 if (!in_overflow_relayout_ && !scrollbars_are_frozen &&
1034 TryRemovingAutoScrollbars(needs_horizontal_scrollbar,
1035 needs_vertical_scrollbar)) {
1036 needs_horizontal_scrollbar = needs_vertical_scrollbar = false;
1037 allow_second_overflow_relayout_ = true;
1040 bool horizontal_scrollbar_should_change =
1041 needs_horizontal_scrollbar != had_horizontal_scrollbar;
1042 bool vertical_scrollbar_should_change =
1043 needs_vertical_scrollbar != had_vertical_scrollbar;
1045 bool scrollbars_will_change =
1046 !scrollbars_are_frozen &&
1047 (horizontal_scrollbar_should_change || vertical_scrollbar_should_change);
1048 if (scrollbars_will_change) {
1049 SetHasHorizontalScrollbar(needs_horizontal_scrollbar);
1050 SetHasVerticalScrollbar(needs_vertical_scrollbar);
1052 // If we change scrollbars on the layout viewport, the visual viewport
1053 // needs to update paint properties to account for the correct
1055 if (LocalFrameView* frame_view = GetLayoutBox()->GetFrameView()) {
1056 if (this == frame_view->LayoutViewport()) {
1060 ->GetVisualViewport()
1061 .SetNeedsPaintPropertyUpdate();
1065 UpdateScrollCornerStyle();
1067 Layer()->UpdateSelfPaintingLayer();
1069 // Force an update since we know the scrollbars have changed things.
1070 if (GetLayoutBox()->GetDocument().HasAnnotatedRegions())
1071 GetLayoutBox()->GetDocument().SetAnnotatedRegionsDirty(true);
1073 // Our proprietary overflow: overlay value doesn't trigger a layout.
1074 if (((horizontal_scrollbar_should_change &&
1075 GetLayoutBox()->StyleRef().OverflowX() != EOverflow::kOverlay) ||
1076 (vertical_scrollbar_should_change &&
1077 GetLayoutBox()->StyleRef().OverflowY() != EOverflow::kOverlay))) {
1078 if ((vertical_scrollbar_should_change &&
1079 GetLayoutBox()->IsHorizontalWritingMode()) ||
1080 (horizontal_scrollbar_should_change &&
1081 !GetLayoutBox()->IsHorizontalWritingMode())) {
1082 GetLayoutBox()->SetIntrinsicLogicalWidthsDirty();
1084 if (IsManagedByLayoutNG(*GetLayoutBox())) {
1085 // If the box is managed by LayoutNG, don't go here. We don't want to
1086 // re-enter the NG layout algorithm for this box from here. Just update
1087 // the rectangles, in case scrollbars were added or removed. LayoutNG
1088 // has its own scrollbar change detection mechanism.
1089 UpdateScrollDimensions();
1091 if (PreventRelayoutScope::RelayoutIsPrevented()) {
1092 // We're not doing re-layout right now, but we still want to
1093 // add the scrollbar to the logical width now, to facilitate parent
1095 GetLayoutBox()->UpdateLogicalWidth();
1096 PreventRelayoutScope::SetBoxNeedsLayout(
1097 *this, had_horizontal_scrollbar, had_vertical_scrollbar);
1099 in_overflow_relayout_ = true;
1100 SubtreeLayoutScope layout_scope(*GetLayoutBox());
1101 layout_scope.SetNeedsLayout(
1102 GetLayoutBox(), layout_invalidation_reason::kScrollbarChanged);
1103 if (auto* block = DynamicTo<LayoutBlock>(GetLayoutBox())) {
1104 block->ScrollbarsChanged(horizontal_scrollbar_should_change,
1105 vertical_scrollbar_should_change);
1106 block->UpdateBlockLayout(true);
1108 GetLayoutBox()->UpdateLayout();
1110 in_overflow_relayout_ = false;
1111 scrollbar_manager_.DestroyDetachedScrollbars();
1113 LayoutObject* parent = GetLayoutBox()->Parent();
1114 if (parent && parent->IsFlexibleBox()) {
1115 ToLayoutFlexibleBox(parent)->ClearCachedMainSizeForChild(
1120 } else if (!HasScrollbar() && resizer_will_change) {
1121 Layer()->DirtyStackingContextZOrderLists();
1123 // The snap container data will be updated at the end of the layout update. If
1124 // the data changes, then this will try to re-snap.
1125 SetSnapContainerDataNeedsUpdate(true);
1127 UpdateScrollbarEnabledState();
1129 UpdateScrollbarProportions();
1132 ClampScrollOffsetAfterOverflowChange();
1134 if (!scrollbars_are_frozen) {
1135 UpdateScrollableAreaSet();
1138 PositionOverflowControls();
1141 void PaintLayerScrollableArea::ClampScrollOffsetAfterOverflowChange() {
1142 if (HasBeenDisposed())
1145 // If a vertical scrollbar was removed, the min/max scroll offsets may have
1146 // changed, so the scroll offsets needs to be clamped. If the scroll offset
1147 // did not change, but the scroll origin *did* change, we still need to notify
1148 // the scrollbars to update their dimensions.
1150 if (DelayScrollOffsetClampScope::ClampingIsDelayed()) {
1151 DelayScrollOffsetClampScope::SetNeedsClamp(this);
1155 UpdateScrollDimensions();
1156 if (ScrollOriginChanged()) {
1157 SetScrollOffsetUnconditionally(ClampScrollOffset(GetScrollOffset()));
1159 ScrollableArea::SetScrollOffset(GetScrollOffset(),
1160 mojom::blink::ScrollType::kClamping);
1163 SetNeedsScrollOffsetClamp(false);
1164 ResetScrollOriginChanged();
1165 scrollbar_manager_.DestroyDetachedScrollbars();
1168 void PaintLayerScrollableArea::DidChangeGlobalRootScroller() {
1169 // Being the global root scroller will affect clipping size due to browser
1170 // controls behavior so we need to update compositing based on updated clip
1172 if (auto* element = DynamicTo<Element>(GetLayoutBox()->GetNode()))
1173 element->SetNeedsCompositingUpdate();
1174 GetLayoutBox()->SetNeedsPaintPropertyUpdate();
1176 // On Android, where the VisualViewport supplies scrollbars, we need to
1177 // remove the PLSA's scrollbars if we become the global root scroller.
1178 // In general, this would be problematic as that can cause layout but this
1179 // should only ever apply with overlay scrollbars.
1180 if (GetLayoutBox()->GetFrame()->GetSettings() &&
1181 GetLayoutBox()->GetFrame()->GetSettings()->GetViewportEnabled()) {
1182 bool needs_horizontal_scrollbar;
1183 bool needs_vertical_scrollbar;
1184 ComputeScrollbarExistence(needs_horizontal_scrollbar,
1185 needs_vertical_scrollbar);
1186 SetHasHorizontalScrollbar(needs_horizontal_scrollbar);
1187 SetHasVerticalScrollbar(needs_vertical_scrollbar);
1190 // Recalculate the snap container data since the scrolling behaviour for this
1191 // layout box changed (i.e. it either became the layout viewport or it
1192 // is no longer the layout viewport).
1193 SetSnapContainerDataNeedsUpdate(true);
1196 bool PaintLayerScrollableArea::ShouldPerformScrollAnchoring() const {
1197 return scroll_anchor_.HasScroller() && GetLayoutBox() &&
1198 GetLayoutBox()->StyleRef().OverflowAnchor() !=
1199 EOverflowAnchor::kNone &&
1200 !GetLayoutBox()->GetDocument().FinishingOrIsPrinting();
1203 bool PaintLayerScrollableArea::RestoreScrollAnchor(
1204 const SerializedAnchor& serialized_anchor) {
1205 return ShouldPerformScrollAnchoring() &&
1206 scroll_anchor_.RestoreAnchor(serialized_anchor);
1209 FloatQuad PaintLayerScrollableArea::LocalToVisibleContentQuad(
1210 const FloatQuad& quad,
1211 const LayoutObject* local_object,
1212 MapCoordinatesFlags flags) const {
1213 LayoutBox* box = GetLayoutBox();
1216 DCHECK(local_object);
1217 return local_object->LocalToAncestorQuad(quad, box, flags);
1220 scoped_refptr<base::SingleThreadTaskRunner>
1221 PaintLayerScrollableArea::GetTimerTaskRunner() const {
1222 return GetLayoutBox()->GetFrame()->GetTaskRunner(TaskType::kInternalDefault);
1225 mojom::blink::ScrollBehavior PaintLayerScrollableArea::ScrollBehaviorStyle()
1227 return GetLayoutBox()->StyleRef().GetScrollBehavior();
1230 WebColorScheme PaintLayerScrollableArea::UsedColorScheme() const {
1231 return GetLayoutBox()->StyleRef().UsedColorScheme();
1234 bool PaintLayerScrollableArea::HasHorizontalOverflow() const {
1235 // TODO(szager): Make the algorithm for adding/subtracting overflow:auto
1236 // scrollbars memoryless (crbug.com/625300). This client_width hack will
1237 // prevent the spurious horizontal scrollbar, but it can cause a converse
1238 // problem: it can leave a sliver of horizontal overflow hidden behind the
1239 // vertical scrollbar without creating a horizontal scrollbar. This
1240 // converse problem seems to happen much less frequently in practice, so we
1241 // bias the logic towards preventing unwanted horizontal scrollbars, which
1242 // are more common and annoying.
1243 LayoutUnit client_width =
1244 LayoutContentRect(kIncludeScrollbars).Width() -
1245 VerticalScrollbarWidth(kIgnorePlatformAndCSSOverlayScrollbarSize);
1246 if (NeedsRelayout() && !HadVerticalScrollbarBeforeRelayout())
1247 client_width += VerticalScrollbarWidth();
1248 LayoutUnit scroll_width(ScrollWidth());
1249 LayoutUnit box_x = GetLayoutBox()->Location().X();
1250 return SnapSizeToPixel(scroll_width, box_x) >
1251 SnapSizeToPixel(client_width, box_x);
1254 bool PaintLayerScrollableArea::HasVerticalOverflow() const {
1255 LayoutUnit client_height =
1256 LayoutContentRect(kIncludeScrollbars).Height() -
1257 HorizontalScrollbarHeight(kIgnorePlatformAndCSSOverlayScrollbarSize);
1258 LayoutUnit scroll_height(ScrollHeight());
1259 LayoutUnit box_y = GetLayoutBox()->Location().Y();
1260 return SnapSizeToPixel(scroll_height, box_y) >
1261 SnapSizeToPixel(client_height, box_y);
1264 // This function returns true if the given box requires overflow scrollbars (as
1265 // opposed to the 'viewport' scrollbars managed by the PaintLayerCompositor).
1266 // FIXME: we should use the same scrolling machinery for both the viewport and
1267 // overflow. Currently, we need to avoid producing scrollbars here if they'll be
1268 // handled externally in the RLC.
1269 static bool CanHaveOverflowScrollbars(const LayoutBox& box) {
1270 return box.GetDocument().ViewportDefiningElement() != box.GetNode();
1273 void PaintLayerScrollableArea::UpdateAfterStyleChange(
1274 const ComputedStyle* old_style) {
1275 // Don't do this on first style recalc, before layout has ever happened.
1276 if (!overflow_rect_.size.IsZero()) {
1277 UpdateScrollableAreaSet();
1280 // Whenever background changes on the scrollable element, the scroll bar
1281 // overlay style might need to be changed to have contrast against the
1283 // Skip the need scrollbar check, because we dont know do we need a scrollbar
1284 // when this method get called.
1285 Color old_background;
1288 old_style->VisitedDependentColor(GetCSSPropertyBackgroundColor());
1290 Color new_background = GetLayoutBox()->StyleRef().VisitedDependentColor(
1291 GetCSSPropertyBackgroundColor());
1293 if (new_background != old_background) {
1294 RecalculateScrollbarOverlayColorTheme(new_background);
1297 bool needs_horizontal_scrollbar;
1298 bool needs_vertical_scrollbar;
1299 ComputeScrollbarExistence(needs_horizontal_scrollbar,
1300 needs_vertical_scrollbar, kOverflowIndependent);
1302 UpdateResizerStyle(old_style);
1304 // Avoid some unnecessary computation if there were and will be no scrollbars.
1305 if (!HasScrollbar() && !needs_horizontal_scrollbar &&
1306 !needs_vertical_scrollbar)
1309 bool horizontal_scrollbar_changed =
1310 SetHasHorizontalScrollbar(needs_horizontal_scrollbar);
1311 bool vertical_scrollbar_changed =
1312 SetHasVerticalScrollbar(needs_vertical_scrollbar);
1314 auto* layout_block = DynamicTo<LayoutBlock>(GetLayoutBox());
1316 (horizontal_scrollbar_changed || vertical_scrollbar_changed)) {
1317 layout_block->ScrollbarsChanged(
1318 horizontal_scrollbar_changed, vertical_scrollbar_changed,
1319 LayoutBlock::ScrollbarChangeContext::kStyleChange);
1322 // FIXME: Need to detect a swap from custom to native scrollbars (and vice
1324 if (HorizontalScrollbar())
1325 HorizontalScrollbar()->StyleChanged();
1326 if (VerticalScrollbar())
1327 VerticalScrollbar()->StyleChanged();
1329 UpdateScrollCornerStyle();
1331 if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
1332 bool vertical_scrollbar_on_left = ShouldPlaceVerticalScrollbarOnLeft();
1333 if (vertical_scrollbar_on_left != previous_vertical_scrollbar_on_left_) {
1334 rebuild_vertical_scrollbar_layer_ = true;
1335 previous_vertical_scrollbar_on_left_ = vertical_scrollbar_on_left;
1340 void PaintLayerScrollableArea::UpdateAfterOverflowRecalc() {
1341 UpdateScrollDimensions();
1342 UpdateScrollbarProportions();
1343 UpdateScrollbarEnabledState();
1345 bool needs_horizontal_scrollbar;
1346 bool needs_vertical_scrollbar;
1347 ComputeScrollbarExistence(needs_horizontal_scrollbar,
1348 needs_vertical_scrollbar);
1350 bool horizontal_scrollbar_should_change =
1351 needs_horizontal_scrollbar != HasHorizontalScrollbar();
1352 bool vertical_scrollbar_should_change =
1353 needs_vertical_scrollbar != HasVerticalScrollbar();
1355 if ((GetLayoutBox()->HasAutoHorizontalScrollbar() &&
1356 horizontal_scrollbar_should_change) ||
1357 (GetLayoutBox()->HasAutoVerticalScrollbar() &&
1358 vertical_scrollbar_should_change)) {
1359 GetLayoutBox()->SetNeedsLayoutAndFullPaintInvalidation(
1360 layout_invalidation_reason::kUnknown);
1363 ClampScrollOffsetAfterOverflowChange();
1364 UpdateScrollableAreaSet();
1367 IntRect PaintLayerScrollableArea::RectForHorizontalScrollbar() const {
1368 if (!HasHorizontalScrollbar())
1371 const IntRect& scroll_corner = ScrollCornerRect();
1372 IntSize border_box_size = PixelSnappedBorderBoxSize();
1374 HorizontalScrollbarStart(),
1375 border_box_size.Height() - GetLayoutBox()->BorderBottom().ToInt() -
1376 HorizontalScrollbar()->ScrollbarThickness(),
1377 border_box_size.Width() -
1378 (GetLayoutBox()->BorderLeft() + GetLayoutBox()->BorderRight())
1380 scroll_corner.Width(),
1381 HorizontalScrollbar()->ScrollbarThickness());
1384 IntRect PaintLayerScrollableArea::RectForVerticalScrollbar() const {
1385 if (!HasVerticalScrollbar())
1388 const IntRect& scroll_corner = ScrollCornerRect();
1390 VerticalScrollbarStart(), GetLayoutBox()->BorderTop().ToInt(),
1391 VerticalScrollbar()->ScrollbarThickness(),
1392 PixelSnappedBorderBoxSize().Height() -
1393 (GetLayoutBox()->BorderTop() + GetLayoutBox()->BorderBottom())
1395 scroll_corner.Height());
1398 int PaintLayerScrollableArea::VerticalScrollbarStart() const {
1399 if (GetLayoutBox()->ShouldPlaceBlockDirectionScrollbarOnLogicalLeft())
1400 return GetLayoutBox()->BorderLeft().ToInt();
1401 return PixelSnappedBorderBoxSize().Width() -
1402 GetLayoutBox()->BorderRight().ToInt() -
1403 VerticalScrollbar()->ScrollbarThickness();
1406 int PaintLayerScrollableArea::HorizontalScrollbarStart() const {
1407 int x = GetLayoutBox()->BorderLeft().ToInt();
1408 if (GetLayoutBox()->ShouldPlaceBlockDirectionScrollbarOnLogicalLeft()) {
1409 x += HasVerticalScrollbar() ? VerticalScrollbar()->ScrollbarThickness()
1410 : ResizerCornerRect(kResizerForPointer).Width();
1415 IntSize PaintLayerScrollableArea::ScrollbarOffset(
1416 const Scrollbar& scrollbar) const {
1417 // TODO(szager): Factor out vertical offset calculation into other methods,
1418 // for symmetry with *ScrollbarStart methods for horizontal offset.
1419 if (&scrollbar == VerticalScrollbar()) {
1420 return IntSize(VerticalScrollbarStart(),
1421 GetLayoutBox()->BorderTop().ToInt());
1424 if (&scrollbar == HorizontalScrollbar()) {
1425 return IntSize(HorizontalScrollbarStart(),
1426 GetLayoutBox()->BorderTop().ToInt() +
1427 VisibleContentRect(kIncludeScrollbars).Height() -
1428 HorizontalScrollbar()->ScrollbarThickness());
1435 static inline const LayoutObject& ScrollbarStyleSource(
1436 const LayoutBox& layout_box) {
1437 if (IsA<LayoutView>(layout_box)) {
1438 Document& doc = layout_box.GetDocument();
1439 if (Settings* settings = doc.GetSettings()) {
1440 if (!settings->GetAllowCustomScrollbarInMainFrame() &&
1441 layout_box.GetFrame() && layout_box.GetFrame()->IsMainFrame())
1445 // Try the <body> element first as a scrollbar source, but only if the body
1447 Element* body = doc.body();
1448 if (body && body->GetLayoutObject() && body->GetLayoutObject()->IsBox() &&
1449 body->GetLayoutObject()->StyleRef().HasPseudoElementStyle(
1450 kPseudoIdScrollbar))
1451 return *body->GetLayoutObject();
1453 // If the <body> didn't have a custom style, then the root element might.
1454 Element* doc_element = doc.documentElement();
1455 if (doc_element && doc_element->GetLayoutObject() &&
1456 doc_element->GetLayoutObject()->StyleRef().HasPseudoElementStyle(
1457 kPseudoIdScrollbar))
1458 return *doc_element->GetLayoutObject();
1464 int PaintLayerScrollableArea::HypotheticalScrollbarThickness(
1465 ScrollbarOrientation orientation) const {
1466 Scrollbar* scrollbar = orientation == kHorizontalScrollbar
1467 ? HorizontalScrollbar()
1468 : VerticalScrollbar();
1470 return scrollbar->ScrollbarThickness();
1472 const LayoutObject& style_source = ScrollbarStyleSource(*GetLayoutBox());
1473 bool has_custom_scrollbar_style =
1474 style_source.StyleRef().HasPseudoElementStyle(kPseudoIdScrollbar);
1475 if (has_custom_scrollbar_style) {
1476 return CustomScrollbar::HypotheticalScrollbarThickness(
1477 this, orientation, To<Element>(style_source.GetNode()));
1480 ScrollbarControlSize scrollbar_size = kRegularScrollbar;
1481 if (style_source.StyleRef().HasEffectiveAppearance()) {
1482 scrollbar_size = LayoutTheme::GetTheme().ScrollbarControlSizeForPart(
1483 style_source.StyleRef().EffectiveAppearance());
1485 ScrollbarTheme& theme = GetPageScrollbarTheme();
1486 if (theme.UsesOverlayScrollbars())
1488 int thickness = theme.ScrollbarThickness(scrollbar_size);
1489 return GetLayoutBox()
1493 .WindowToViewportScalar(GetLayoutBox()->GetFrame(), thickness);
1496 bool PaintLayerScrollableArea::NeedsScrollbarReconstruction() const {
1497 if (!HasScrollbar())
1500 const LayoutObject& style_source = ScrollbarStyleSource(*GetLayoutBox());
1502 style_source.IsBox() &&
1503 style_source.StyleRef().HasPseudoElementStyle(kPseudoIdScrollbar);
1505 Scrollbar* scrollbars[] = {HorizontalScrollbar(), VerticalScrollbar()};
1507 for (Scrollbar* scrollbar : scrollbars) {
1511 // We have a native scrollbar that should be custom, or vice versa.
1512 if (scrollbar->IsCustomScrollbar() != needs_custom)
1516 DCHECK(scrollbar->IsCustomScrollbar());
1517 // We have a custom scrollbar with a stale m_owner.
1518 if (To<CustomScrollbar>(scrollbar)->StyleSource()->GetLayoutObject() !=
1523 // Should use custom scrollbar and nothing should change.
1527 // Check if native scrollbar should change.
1528 Page* page = GetLayoutBox()->GetFrame()->LocalFrameRoot().GetPage();
1530 ScrollbarTheme* current_theme = &page->GetScrollbarTheme();
1532 if (current_theme != &scrollbar->GetTheme())
1538 void PaintLayerScrollableArea::ComputeScrollbarExistence(
1539 bool& needs_horizontal_scrollbar,
1540 bool& needs_vertical_scrollbar,
1541 ComputeScrollbarExistenceOption option) const {
1542 // Scrollbars may be hidden or provided by visual viewport or frame instead.
1543 DCHECK(GetLayoutBox()->GetFrame()->GetSettings());
1544 if (VisualViewportSuppliesScrollbars() ||
1545 !CanHaveOverflowScrollbars(*GetLayoutBox()) ||
1546 GetLayoutBox()->GetFrame()->GetSettings()->GetHideScrollbars()) {
1547 needs_horizontal_scrollbar = false;
1548 needs_vertical_scrollbar = false;
1552 mojom::blink::ScrollbarMode h_mode = mojom::blink::ScrollbarMode::kAuto;
1553 mojom::blink::ScrollbarMode v_mode = mojom::blink::ScrollbarMode::kAuto;
1555 // First, determine what behavior the scrollbars say they should have.
1557 if (auto* layout_view = DynamicTo<LayoutView>(GetLayoutBox())) {
1558 // LayoutView is special as there's various quirks and settings that
1559 // style doesn't account for.
1560 layout_view->CalculateScrollbarModes(h_mode, v_mode);
1562 auto overflow_x = GetLayoutBox()->StyleRef().OverflowX();
1563 if (overflow_x == EOverflow::kScroll) {
1564 h_mode = mojom::blink::ScrollbarMode::kAlwaysOn;
1565 } else if (overflow_x == EOverflow::kHidden ||
1566 overflow_x == EOverflow::kVisible) {
1567 h_mode = mojom::blink::ScrollbarMode::kAlwaysOff;
1570 auto overflow_y = GetLayoutBox()->StyleRef().OverflowY();
1571 if (overflow_y == EOverflow::kScroll) {
1572 v_mode = mojom::blink::ScrollbarMode::kAlwaysOn;
1573 } else if (overflow_y == EOverflow::kHidden ||
1574 overflow_y == EOverflow::kVisible) {
1575 v_mode = mojom::blink::ScrollbarMode::kAlwaysOff;
1579 // Since overlay scrollbars (the fade-in/out kind, not overflow: overlay)
1580 // only appear when scrolling, we don't create them if there isn't overflow
1581 // to scroll. Thus, overlay scrollbars can't be "always on". i.e.
1582 // |overlay:scroll| behaves like |overlay:auto|.
1583 bool has_custom_scrollbar_style =
1584 ScrollbarStyleSource(*GetLayoutBox())
1586 .HasPseudoElementStyle(kPseudoIdScrollbar);
1587 bool will_be_overlay = GetPageScrollbarTheme().UsesOverlayScrollbars() &&
1588 !has_custom_scrollbar_style;
1589 if (will_be_overlay) {
1590 if (h_mode == mojom::blink::ScrollbarMode::kAlwaysOn)
1591 h_mode = mojom::blink::ScrollbarMode::kAuto;
1592 if (v_mode == mojom::blink::ScrollbarMode::kAlwaysOn)
1593 v_mode = mojom::blink::ScrollbarMode::kAuto;
1597 // By default, don't make any changes.
1598 needs_horizontal_scrollbar = HasHorizontalScrollbar();
1599 needs_vertical_scrollbar = HasVerticalScrollbar();
1601 // If the behavior doesn't depend on overflow or any other information, we
1604 if (h_mode == mojom::blink::ScrollbarMode::kAlwaysOn)
1605 needs_horizontal_scrollbar = true;
1606 else if (h_mode == mojom::blink::ScrollbarMode::kAlwaysOff)
1607 needs_horizontal_scrollbar = false;
1609 if (v_mode == mojom::blink::ScrollbarMode::kAlwaysOn)
1610 needs_vertical_scrollbar = true;
1611 else if (v_mode == mojom::blink::ScrollbarMode::kAlwaysOff)
1612 needs_vertical_scrollbar = false;
1615 // If this is being performed before layout, we want to only update scrollbar
1616 // existence if its based on purely style based reasons.
1617 if (option == kOverflowIndependent)
1620 // If we have clean layout, we can make a decision on any scrollbars that
1621 // depend on overflow.
1623 if (h_mode == mojom::blink::ScrollbarMode::kAuto) {
1624 // Don't add auto scrollbars if the box contents aren't visible.
1625 needs_horizontal_scrollbar =
1626 GetLayoutBox()->IsRooted() && HasHorizontalOverflow() &&
1627 VisibleContentRect(kIncludeScrollbars).Height();
1629 if (v_mode == mojom::blink::ScrollbarMode::kAuto) {
1630 needs_vertical_scrollbar = GetLayoutBox()->IsRooted() &&
1631 HasVerticalOverflow() &&
1632 VisibleContentRect(kIncludeScrollbars).Width();
1637 bool PaintLayerScrollableArea::TryRemovingAutoScrollbars(
1638 const bool& needs_horizontal_scrollbar,
1639 const bool& needs_vertical_scrollbar) {
1640 // If scrollbars are removed but the content size depends on the scrollbars,
1641 // additional layouts will be required to size the content. Therefore, only
1642 // remove auto scrollbars for the initial layout pass.
1643 DCHECK(!in_overflow_relayout_);
1645 if (!needs_horizontal_scrollbar && !needs_vertical_scrollbar)
1648 if (auto* layout_view = DynamicTo<LayoutView>(GetLayoutBox())) {
1649 mojom::blink::ScrollbarMode h_mode;
1650 mojom::blink::ScrollbarMode v_mode;
1651 layout_view->CalculateScrollbarModes(h_mode, v_mode);
1652 if (h_mode != mojom::blink::ScrollbarMode::kAuto ||
1653 v_mode != mojom::blink::ScrollbarMode::kAuto)
1656 IntSize visible_size_with_scrollbars =
1657 VisibleContentRect(kIncludeScrollbars).Size();
1658 if (ScrollWidth() <= visible_size_with_scrollbars.Width() &&
1659 ScrollHeight() <= visible_size_with_scrollbars.Height()) {
1663 if (!GetLayoutBox()->HasAutoVerticalScrollbar() ||
1664 !GetLayoutBox()->HasAutoHorizontalScrollbar())
1667 PhysicalSize client_size_with_scrollbars =
1668 LayoutContentRect(kIncludeScrollbars).size;
1669 if (ScrollWidth() <= client_size_with_scrollbars.width &&
1670 ScrollHeight() <= client_size_with_scrollbars.height) {
1678 bool PaintLayerScrollableArea::SetHasHorizontalScrollbar(bool has_scrollbar) {
1679 if (FreezeScrollbarsScope::ScrollbarsAreFrozen())
1682 if (has_scrollbar == HasHorizontalScrollbar())
1685 SetScrollbarNeedsPaintInvalidation(kHorizontalScrollbar);
1687 scrollbar_manager_.SetHasHorizontalScrollbar(has_scrollbar);
1689 UpdateScrollOrigin();
1691 // Destroying or creating one bar can cause our scrollbar corner to come and
1692 // go. We need to update the opposite scrollbar's style.
1693 if (HasHorizontalScrollbar())
1694 HorizontalScrollbar()->StyleChanged();
1695 if (HasVerticalScrollbar())
1696 VerticalScrollbar()->StyleChanged();
1698 SetScrollCornerNeedsPaintInvalidation();
1700 // Force an update since we know the scrollbars have changed things.
1701 if (GetLayoutBox()->GetDocument().HasAnnotatedRegions())
1702 GetLayoutBox()->GetDocument().SetAnnotatedRegionsDirty(true);
1706 bool PaintLayerScrollableArea::SetHasVerticalScrollbar(bool has_scrollbar) {
1707 if (FreezeScrollbarsScope::ScrollbarsAreFrozen())
1710 if (GetLayoutBox()->GetDocument().IsVerticalScrollEnforced()) {
1711 // When the policy is enforced the contents of document cannot be scrolled.
1712 // This would make rendering a scrollbar look strange
1713 // (https://crbug.com/898151).
1717 if (has_scrollbar == HasVerticalScrollbar())
1720 SetScrollbarNeedsPaintInvalidation(kVerticalScrollbar);
1722 scrollbar_manager_.SetHasVerticalScrollbar(has_scrollbar);
1724 UpdateScrollOrigin();
1726 // Destroying or creating one bar can cause our scrollbar corner to come and
1727 // go. We need to update the opposite scrollbar's style.
1728 if (HasHorizontalScrollbar())
1729 HorizontalScrollbar()->StyleChanged();
1730 if (HasVerticalScrollbar())
1731 VerticalScrollbar()->StyleChanged();
1733 SetScrollCornerNeedsPaintInvalidation();
1735 // Force an update since we know the scrollbars have changed things.
1736 if (GetLayoutBox()->GetDocument().HasAnnotatedRegions())
1737 GetLayoutBox()->GetDocument().SetAnnotatedRegionsDirty(true);
1741 int PaintLayerScrollableArea::VerticalScrollbarWidth(
1742 OverlayScrollbarClipBehavior overlay_scrollbar_clip_behavior) const {
1743 if (!HasVerticalScrollbar())
1745 if (overlay_scrollbar_clip_behavior ==
1746 kIgnorePlatformAndCSSOverlayScrollbarSize &&
1747 GetLayoutBox()->StyleRef().OverflowY() == EOverflow::kOverlay) {
1750 if ((overlay_scrollbar_clip_behavior == kIgnorePlatformOverlayScrollbarSize ||
1751 overlay_scrollbar_clip_behavior ==
1752 kIgnorePlatformAndCSSOverlayScrollbarSize ||
1753 !VerticalScrollbar()->ShouldParticipateInHitTesting()) &&
1754 VerticalScrollbar()->IsOverlayScrollbar()) {
1757 return VerticalScrollbar()->ScrollbarThickness();
1760 int PaintLayerScrollableArea::HorizontalScrollbarHeight(
1761 OverlayScrollbarClipBehavior overlay_scrollbar_clip_behavior) const {
1762 if (!HasHorizontalScrollbar())
1764 if (overlay_scrollbar_clip_behavior ==
1765 kIgnorePlatformAndCSSOverlayScrollbarSize &&
1766 GetLayoutBox()->StyleRef().OverflowX() == EOverflow::kOverlay) {
1769 if ((overlay_scrollbar_clip_behavior == kIgnorePlatformOverlayScrollbarSize ||
1770 overlay_scrollbar_clip_behavior ==
1771 kIgnorePlatformAndCSSOverlayScrollbarSize ||
1772 !HorizontalScrollbar()->ShouldParticipateInHitTesting()) &&
1773 HorizontalScrollbar()->IsOverlayScrollbar()) {
1776 return HorizontalScrollbar()->ScrollbarThickness();
1779 const cc::SnapContainerData* PaintLayerScrollableArea::GetSnapContainerData()
1781 return RareData() && RareData()->snap_container_data_
1782 ? &RareData()->snap_container_data_.value()
1786 void PaintLayerScrollableArea::SetSnapContainerData(
1787 base::Optional<cc::SnapContainerData> data) {
1788 EnsureRareData().snap_container_data_ = data;
1791 bool PaintLayerScrollableArea::SetTargetSnapAreaElementIds(
1792 cc::TargetSnapAreaElementIds snap_target_ids) {
1793 if (!RareData() || !RareData()->snap_container_data_)
1795 if (RareData()->snap_container_data_.value().SetTargetSnapAreaElementIds(
1797 GetLayoutBox()->SetNeedsPaintPropertyUpdate();
1803 bool PaintLayerScrollableArea::SnapContainerDataNeedsUpdate() const {
1804 return RareData() ? RareData()->snap_container_data_needs_update_ : false;
1807 void PaintLayerScrollableArea::SetSnapContainerDataNeedsUpdate(
1808 bool needs_update) {
1809 EnsureRareData().snap_container_data_needs_update_ = needs_update;
1814 .GetSnapCoordinator()
1815 .SetAnySnapContainerDataNeedsUpdate(true);
1818 bool PaintLayerScrollableArea::NeedsResnap() const {
1819 return RareData() ? RareData()->needs_resnap_ : false;
1822 void PaintLayerScrollableArea::SetNeedsResnap(bool needs_resnap) {
1823 EnsureRareData().needs_resnap_ = needs_resnap;
1826 base::Optional<FloatPoint>
1827 PaintLayerScrollableArea::GetSnapPositionAndSetTarget(
1828 const cc::SnapSelectionStrategy& strategy) {
1829 if (!RareData() || !RareData()->snap_container_data_)
1830 return base::nullopt;
1832 cc::SnapContainerData& data = RareData()->snap_container_data_.value();
1834 return base::nullopt;
1836 cc::TargetSnapAreaElementIds snap_targets;
1837 gfx::ScrollOffset snap_position;
1838 base::Optional<FloatPoint> snap_point;
1839 if (data.FindSnapPosition(strategy, &snap_position, &snap_targets))
1840 snap_point = FloatPoint(snap_position.x(), snap_position.y());
1841 if (data.SetTargetSnapAreaElementIds(snap_targets))
1842 GetLayoutBox()->SetNeedsPaintPropertyUpdate();
1847 bool PaintLayerScrollableArea::HasOverflowControls() const {
1848 // We do not need to check for ScrollCorner because it only exists iff there
1849 // are scrollbars, see: |ScrollCornerRect| and |UpdateScrollCornerStyle|.
1850 DCHECK(!ScrollCorner() || HasScrollbar());
1851 return HasScrollbar() || GetLayoutBox()->CanResize();
1854 bool PaintLayerScrollableArea::HasOverlayOverflowControls() const {
1855 return HasOverlayScrollbars() ||
1856 (!HasScrollbar() && GetLayoutBox()->CanResize());
1859 bool PaintLayerScrollableArea::HasNonOverlayOverflowControls() const {
1860 return HasScrollbar() && !HasOverlayScrollbars();
1863 void PaintLayerScrollableArea::PositionOverflowControls() {
1864 if (!HasOverflowControls())
1867 if (Scrollbar* vertical_scrollbar = VerticalScrollbar()) {
1868 vertical_scrollbar->SetFrameRect(RectForVerticalScrollbar());
1869 if (auto* custom_scrollbar = DynamicTo<CustomScrollbar>(vertical_scrollbar))
1870 custom_scrollbar->PositionScrollbarParts();
1873 if (Scrollbar* horizontal_scrollbar = HorizontalScrollbar()) {
1874 horizontal_scrollbar->SetFrameRect(RectForHorizontalScrollbar());
1875 if (auto* custom_scrollbar =
1876 DynamicTo<CustomScrollbar>(horizontal_scrollbar))
1877 custom_scrollbar->PositionScrollbarParts();
1880 if (scroll_corner_) {
1881 LayoutRect rect(ScrollCornerRect());
1882 scroll_corner_->SetFrameRect(rect);
1883 // TODO(crbug.com/1020913): This should be part of PaintPropertyTreeBuilder
1884 // when we support subpixel layout of overflow controls.
1885 scroll_corner_->GetMutableForPainting().FirstFragment().SetPaintOffset(
1886 PhysicalOffset(rect.Location()));
1890 LayoutRect rect(ResizerCornerRect(kResizerForPointer));
1891 resizer_->SetFrameRect(rect);
1892 // TODO(crbug.com/1020913): This should be part of PaintPropertyTreeBuilder
1893 // when we support subpixel layout of overflow controls.
1894 resizer_->GetMutableForPainting().FirstFragment().SetPaintOffset(
1895 PhysicalOffset(rect.Location()));
1898 // FIXME, this should eventually be removed, once we are certain that
1899 // composited controls get correctly positioned on a compositor update. For
1900 // now, conservatively leaving this unchanged.
1901 if (Layer()->HasCompositedLayerMapping()) {
1902 DisableCompositingQueryAsserts disabler;
1903 Layer()->GetCompositedLayerMapping()->PositionOverflowControlsLayers();
1907 void PaintLayerScrollableArea::UpdateScrollCornerStyle() {
1908 if (!HasNonOverlayOverflowControls()) {
1909 if (scroll_corner_) {
1910 scroll_corner_->Destroy();
1911 scroll_corner_ = nullptr;
1915 const LayoutObject& style_source = ScrollbarStyleSource(*GetLayoutBox());
1916 scoped_refptr<ComputedStyle> corner =
1917 GetLayoutBox()->HasOverflowClip()
1918 ? style_source.GetUncachedPseudoElementStyle(
1919 PseudoElementStyleRequest(kPseudoIdScrollbarCorner),
1920 style_source.Style())
1921 : scoped_refptr<ComputedStyle>(nullptr);
1923 if (!scroll_corner_) {
1924 scroll_corner_ = LayoutCustomScrollbarPart::CreateAnonymous(
1925 &GetLayoutBox()->GetDocument(), this);
1927 scroll_corner_->SetStyle(std::move(corner));
1928 } else if (scroll_corner_) {
1929 scroll_corner_->Destroy();
1930 scroll_corner_ = nullptr;
1934 bool PaintLayerScrollableArea::HitTestOverflowControls(
1935 HitTestResult& result,
1936 const IntPoint& local_point) {
1937 if (!HasOverflowControls())
1940 IntRect resize_control_rect;
1941 if (GetLayoutBox()->StyleRef().HasResize()) {
1942 resize_control_rect = ResizerCornerRect(kResizerForPointer);
1943 if (resize_control_rect.Contains(local_point))
1946 int resize_control_size = max(resize_control_rect.Height(), 0);
1948 IntRect visible_rect = VisibleContentRect(kIncludeScrollbars);
1950 if (HasVerticalScrollbar() &&
1951 VerticalScrollbar()->ShouldParticipateInHitTesting()) {
1952 LayoutRect v_bar_rect(VerticalScrollbarStart(),
1953 GetLayoutBox()->BorderTop().ToInt(),
1954 VerticalScrollbar()->ScrollbarThickness(),
1955 visible_rect.Height() -
1956 (HasHorizontalScrollbar()
1957 ? HorizontalScrollbar()->ScrollbarThickness()
1958 : resize_control_size));
1959 if (v_bar_rect.Contains(local_point)) {
1960 result.SetScrollbar(VerticalScrollbar());
1965 resize_control_size = max(resize_control_rect.Width(), 0);
1966 if (HasHorizontalScrollbar() &&
1967 HorizontalScrollbar()->ShouldParticipateInHitTesting()) {
1968 // TODO(crbug.com/638981): Are the conversions to int intentional?
1969 int h_scrollbar_thickness = HorizontalScrollbar()->ScrollbarThickness();
1970 LayoutRect h_bar_rect(
1971 HorizontalScrollbarStart(),
1972 GetLayoutBox()->BorderTop().ToInt() + visible_rect.Height() -
1973 h_scrollbar_thickness,
1974 visible_rect.Width() - (HasVerticalScrollbar()
1975 ? VerticalScrollbar()->ScrollbarThickness()
1976 : resize_control_size),
1977 h_scrollbar_thickness);
1978 if (h_bar_rect.Contains(local_point)) {
1979 result.SetScrollbar(HorizontalScrollbar());
1984 // FIXME: We should hit test the m_scrollCorner and pass it back through the
1990 IntRect PaintLayerScrollableArea::ResizerCornerRect(
1991 ResizerHitTestType resizer_hit_test_type) const {
1992 if (!GetLayoutBox()->StyleRef().HasResize())
1994 IntRect corner = CornerRect();
1996 if (resizer_hit_test_type == kResizerForTouch) {
1997 // We make the resizer virtually larger for touch hit testing. With the
1998 // expanding ratio k = ResizerControlExpandRatioForTouch, we first move
1999 // the resizer rect (of width w & height h), by (-w * (k-1), -h * (k-1)),
2000 // then expand the rect by new_w/h = w/h * k.
2001 int expand_ratio = kResizerControlExpandRatioForTouch - 1;
2002 corner.Move(-corner.Width() * expand_ratio,
2003 -corner.Height() * expand_ratio);
2004 corner.Expand(corner.Width() * expand_ratio,
2005 corner.Height() * expand_ratio);
2011 IntRect PaintLayerScrollableArea::ScrollCornerAndResizerRect() const {
2012 IntRect scroll_corner_and_resizer = ScrollCornerRect();
2013 if (scroll_corner_and_resizer.IsEmpty())
2014 return ResizerCornerRect(kResizerForPointer);
2015 return scroll_corner_and_resizer;
2018 bool PaintLayerScrollableArea::IsPointInResizeControl(
2019 const IntPoint& absolute_point,
2020 ResizerHitTestType resizer_hit_test_type) const {
2021 if (!GetLayoutBox()->CanResize())
2024 IntPoint local_point = RoundedIntPoint(
2025 GetLayoutBox()->AbsoluteToLocalPoint(PhysicalOffset(absolute_point)));
2026 return ResizerCornerRect(resizer_hit_test_type).Contains(local_point);
2029 bool PaintLayerScrollableArea::HitTestResizerInFragments(
2030 const PaintLayerFragments& layer_fragments,
2031 const HitTestLocation& hit_test_location) const {
2032 if (!GetLayoutBox()->CanResize())
2035 if (layer_fragments.IsEmpty())
2038 for (int i = layer_fragments.size() - 1; i >= 0; --i) {
2039 const PaintLayerFragment& fragment = layer_fragments.at(i);
2040 if (fragment.background_rect.Intersects(hit_test_location)) {
2041 IntRect resizer_corner_rect = ResizerCornerRect(kResizerForPointer);
2042 resizer_corner_rect.MoveBy(RoundedIntPoint(fragment.layer_bounds.offset));
2043 if (resizer_corner_rect.Contains(hit_test_location.RoundedPoint()))
2051 void PaintLayerScrollableArea::UpdateResizerStyle(
2052 const ComputedStyle* old_style) {
2053 if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled() && old_style &&
2054 old_style->UnresolvedResize() !=
2055 GetLayoutBox()->StyleRef().UnresolvedResize()) {
2056 // Invalidate the composited scroll corner layer on resize style change.
2057 if (auto* graphics_layer = GraphicsLayerForScrollCorner())
2058 graphics_layer->SetNeedsDisplay();
2061 if (!resizer_ && !GetLayoutBox()->CanResize())
2064 const LayoutObject& style_source = ScrollbarStyleSource(*GetLayoutBox());
2065 scoped_refptr<ComputedStyle> resizer =
2066 GetLayoutBox()->HasOverflowClip()
2067 ? style_source.GetUncachedPseudoElementStyle(
2068 PseudoElementStyleRequest(kPseudoIdResizer),
2069 style_source.Style())
2070 : scoped_refptr<ComputedStyle>(nullptr);
2073 resizer_ = LayoutCustomScrollbarPart::CreateAnonymous(
2074 &GetLayoutBox()->GetDocument(), this);
2076 resizer_->SetStyle(std::move(resizer));
2077 } else if (resizer_) {
2078 resizer_->Destroy();
2083 void PaintLayerScrollableArea::InvalidateAllStickyConstraints() {
2084 if (PaintLayerScrollableAreaRareData* d = RareData()) {
2085 for (PaintLayer* sticky_layer : d->sticky_constraints_map_.Keys()) {
2086 if (sticky_layer->GetLayoutObject().StyleRef().GetPosition() ==
2087 EPosition::kSticky) {
2088 sticky_layer->SetNeedsCompositingInputsUpdate();
2089 sticky_layer->GetLayoutObject().SetNeedsPaintPropertyUpdate();
2092 d->sticky_constraints_map_.clear();
2096 void PaintLayerScrollableArea::InvalidateStickyConstraintsFor(
2097 PaintLayer* layer) {
2098 if (PaintLayerScrollableAreaRareData* d = RareData()) {
2099 d->sticky_constraints_map_.erase(layer);
2100 if (layer->GetLayoutObject().StyleRef().HasStickyConstrainedPosition()) {
2101 layer->SetNeedsCompositingInputsUpdate();
2102 layer->GetLayoutObject().SetNeedsPaintPropertyUpdate();
2107 bool PaintLayerScrollableArea::HasNonCompositedStickyDescendants() const {
2108 if (const PaintLayerScrollableAreaRareData* d = RareData()) {
2109 for (const PaintLayer* sticky_layer : d->sticky_constraints_map_.Keys()) {
2110 if (sticky_layer->GetLayoutObject().IsSlowRepaintConstrainedObject())
2117 void PaintLayerScrollableArea::InvalidatePaintForStickyDescendants() {
2118 if (PaintLayerScrollableAreaRareData* d = RareData()) {
2119 for (PaintLayer* sticky_layer : d->sticky_constraints_map_.Keys())
2120 sticky_layer->GetLayoutObject().SetNeedsPaintPropertyUpdate();
2124 IntSize PaintLayerScrollableArea::OffsetFromResizeCorner(
2125 const IntPoint& absolute_point) const {
2126 // Currently the resize corner is either the bottom right corner or the bottom
2128 // FIXME: This assumes the location is 0, 0. Is this guaranteed to always be
2130 IntSize element_size = PixelSnappedBorderBoxSize();
2131 if (GetLayoutBox()->ShouldPlaceBlockDirectionScrollbarOnLogicalLeft())
2132 element_size.SetWidth(0);
2133 IntPoint resizer_point = IntPoint(element_size);
2134 IntPoint local_point = RoundedIntPoint(
2135 GetLayoutBox()->AbsoluteToLocalPoint(PhysicalOffset(absolute_point)));
2136 return local_point - resizer_point;
2139 LayoutSize PaintLayerScrollableArea::MinimumSizeForResizing(float zoom_factor) {
2140 LayoutUnit min_width =
2141 MinimumValueForLength(GetLayoutBox()->StyleRef().MinWidth(),
2142 GetLayoutBox()->ContainingBlock()->Size().Width());
2143 LayoutUnit min_height =
2144 MinimumValueForLength(GetLayoutBox()->StyleRef().MinHeight(),
2145 GetLayoutBox()->ContainingBlock()->Size().Height());
2146 min_width = std::max(LayoutUnit(min_width / zoom_factor),
2147 LayoutUnit(kDefaultMinimumWidthForResizing));
2148 min_height = std::max(LayoutUnit(min_height / zoom_factor),
2149 LayoutUnit(kDefaultMinimumHeightForResizing));
2150 return LayoutSize(min_width, min_height);
2153 void PaintLayerScrollableArea::Resize(const IntPoint& pos,
2154 const LayoutSize& old_offset) {
2155 // FIXME: This should be possible on generated content but is not right now.
2156 if (!InResizeMode() || !GetLayoutBox()->CanResize() ||
2157 !GetLayoutBox()->GetNode())
2160 DCHECK(GetLayoutBox()->GetNode()->IsElementNode());
2161 auto* element = To<Element>(GetLayoutBox()->GetNode());
2163 Document& document = element->GetDocument();
2165 float zoom_factor = GetLayoutBox()->StyleRef().EffectiveZoom();
2167 IntSize new_offset =
2168 OffsetFromResizeCorner(document.View()->ConvertFromRootFrame(pos));
2169 new_offset.SetWidth(new_offset.Width() / zoom_factor);
2170 new_offset.SetHeight(new_offset.Height() / zoom_factor);
2172 LayoutSize current_size = GetLayoutBox()->Size();
2173 current_size.Scale(1 / zoom_factor);
2175 LayoutSize adjusted_old_offset = LayoutSize(
2176 old_offset.Width() / zoom_factor, old_offset.Height() / zoom_factor);
2177 if (GetLayoutBox()->ShouldPlaceBlockDirectionScrollbarOnLogicalLeft()) {
2178 new_offset.SetWidth(-new_offset.Width());
2179 adjusted_old_offset.SetWidth(-adjusted_old_offset.Width());
2182 LayoutSize difference((current_size + new_offset - adjusted_old_offset)
2183 .ExpandedTo(MinimumSizeForResizing(zoom_factor)) -
2186 bool is_box_sizing_border =
2187 GetLayoutBox()->StyleRef().BoxSizing() == EBoxSizing::kBorderBox;
2189 EResize resize = GetLayoutBox()->StyleRef().Resize(
2190 GetLayoutBox()->ContainingBlock()->StyleRef());
2191 if (resize != EResize::kVertical && difference.Width()) {
2192 if (element->IsFormControlElement()) {
2193 // Make implicit margins from the theme explicit (see
2194 // <http://bugs.webkit.org/show_bug.cgi?id=9547>).
2195 element->SetInlineStyleProperty(
2196 CSSPropertyID::kMarginLeft,
2197 GetLayoutBox()->MarginLeft() / zoom_factor,
2198 CSSPrimitiveValue::UnitType::kPixels);
2199 element->SetInlineStyleProperty(
2200 CSSPropertyID::kMarginRight,
2201 GetLayoutBox()->MarginRight() / zoom_factor,
2202 CSSPrimitiveValue::UnitType::kPixels);
2204 LayoutUnit base_width =
2205 GetLayoutBox()->Size().Width() -
2206 (is_box_sizing_border ? LayoutUnit()
2207 : GetLayoutBox()->BorderAndPaddingWidth());
2208 base_width = LayoutUnit(base_width / zoom_factor);
2209 element->SetInlineStyleProperty(CSSPropertyID::kWidth,
2210 RoundToInt(base_width + difference.Width()),
2211 CSSPrimitiveValue::UnitType::kPixels);
2214 if (resize != EResize::kHorizontal && difference.Height()) {
2215 if (element->IsFormControlElement()) {
2216 // Make implicit margins from the theme explicit (see
2217 // <http://bugs.webkit.org/show_bug.cgi?id=9547>).
2218 element->SetInlineStyleProperty(CSSPropertyID::kMarginTop,
2219 GetLayoutBox()->MarginTop() / zoom_factor,
2220 CSSPrimitiveValue::UnitType::kPixels);
2221 element->SetInlineStyleProperty(
2222 CSSPropertyID::kMarginBottom,
2223 GetLayoutBox()->MarginBottom() / zoom_factor,
2224 CSSPrimitiveValue::UnitType::kPixels);
2226 LayoutUnit base_height =
2227 GetLayoutBox()->Size().Height() -
2228 (is_box_sizing_border ? LayoutUnit()
2229 : GetLayoutBox()->BorderAndPaddingHeight());
2230 base_height = LayoutUnit(base_height / zoom_factor);
2231 element->SetInlineStyleProperty(
2232 CSSPropertyID::kHeight, RoundToInt(base_height + difference.Height()),
2233 CSSPrimitiveValue::UnitType::kPixels);
2236 document.UpdateStyleAndLayout(DocumentUpdateReason::kSizeChange);
2238 // FIXME: We should also autoscroll the window as necessary to
2239 // keep the point under the cursor in view.
2242 PhysicalRect PaintLayerScrollableArea::ScrollIntoView(
2243 const PhysicalRect& absolute_rect,
2244 const mojom::blink::ScrollIntoViewParamsPtr& params) {
2245 PhysicalRect local_expose_rect =
2246 GetLayoutBox()->AbsoluteToLocalRect(absolute_rect);
2247 PhysicalOffset border_origin_to_scroll_origin(-GetLayoutBox()->BorderLeft(),
2248 -GetLayoutBox()->BorderTop());
2249 // There might be scroll bar between border_origin and scroll_origin.
2250 IntSize scroll_bar_adjustment =
2251 GetLayoutBox()->OriginAdjustmentForScrollbars();
2252 border_origin_to_scroll_origin.left -= scroll_bar_adjustment.Width();
2253 border_origin_to_scroll_origin.top -= scroll_bar_adjustment.Height();
2254 border_origin_to_scroll_origin +=
2255 PhysicalOffset::FromFloatSizeFloor(GetScrollOffset());
2256 // Represent the rect in the container's scroll-origin coordinate.
2257 local_expose_rect.Move(border_origin_to_scroll_origin);
2258 PhysicalRect scroll_snapport_rect = VisibleScrollSnapportRect();
2260 ScrollOffset target_offset = ScrollAlignment::GetScrollOffsetToExpose(
2261 scroll_snapport_rect, local_expose_rect, *params->align_x.get(),
2262 *params->align_y.get(), GetScrollOffset());
2263 ScrollOffset new_scroll_offset(
2264 ClampScrollOffset(RoundedIntSize(target_offset)));
2266 ScrollOffset old_scroll_offset = GetScrollOffset();
2267 if (params->type == mojom::blink::ScrollType::kUser) {
2268 if (!UserInputScrollable(kHorizontalScrollbar))
2269 new_scroll_offset.SetWidth(old_scroll_offset.Width());
2270 if (!UserInputScrollable(kVerticalScrollbar))
2271 new_scroll_offset.SetHeight(old_scroll_offset.Height());
2274 FloatPoint end_point = ScrollOffsetToPosition(new_scroll_offset);
2275 std::unique_ptr<cc::SnapSelectionStrategy> strategy =
2276 cc::SnapSelectionStrategy::CreateForEndPosition(
2277 gfx::ScrollOffset(end_point), true, true);
2278 end_point = GetSnapPositionAndSetTarget(*strategy).value_or(end_point);
2279 new_scroll_offset = ScrollPositionToOffset(end_point);
2281 if (params->is_for_scroll_sequence) {
2282 DCHECK(params->type == mojom::blink::ScrollType::kProgrammatic ||
2283 params->type == mojom::blink::ScrollType::kUser);
2284 mojom::blink::ScrollBehavior behavior = DetermineScrollBehavior(
2285 params->behavior, GetLayoutBox()->StyleRef().GetScrollBehavior());
2286 GetSmoothScrollSequencer()->QueueAnimation(this, new_scroll_offset,
2289 SetScrollOffset(new_scroll_offset, params->type,
2290 mojom::blink::ScrollBehavior::kInstant);
2293 ScrollOffset scroll_offset_difference = new_scroll_offset - old_scroll_offset;
2294 // The container hasn't performed the scroll yet if it's for scroll sequence.
2295 // To calculate the result from the scroll, we move the |local_expose_rect| to
2296 // the will-be-scrolled location.
2297 local_expose_rect.Move(
2298 -PhysicalOffset::FromFloatSizeRound(scroll_offset_difference));
2300 // Represent the rects in the container's border-box coordinate.
2301 local_expose_rect.Move(-border_origin_to_scroll_origin);
2302 scroll_snapport_rect.Move(-border_origin_to_scroll_origin);
2303 PhysicalRect intersect =
2304 Intersection(scroll_snapport_rect, local_expose_rect);
2306 if (intersect.IsEmpty() && !scroll_snapport_rect.IsEmpty() &&
2307 !local_expose_rect.IsEmpty()) {
2308 return GetLayoutBox()->LocalToAbsoluteRect(local_expose_rect);
2310 intersect = GetLayoutBox()->LocalToAbsoluteRect(intersect);
2314 void PaintLayerScrollableArea::UpdateScrollableAreaSet() {
2315 LocalFrame* frame = GetLayoutBox()->GetFrame();
2319 LocalFrameView* frame_view = frame->View();
2324 !GetLayoutBox()->Size().IsZero() &&
2325 ((HasHorizontalOverflow() && GetLayoutBox()->ScrollsOverflowX()) ||
2326 (HasVerticalOverflow() && GetLayoutBox()->ScrollsOverflowY()));
2328 bool is_visible_to_hit_test =
2329 GetLayoutBox()->StyleRef().VisibleToHitTesting();
2330 bool did_scroll_overflow = scrolls_overflow_;
2331 if (auto* layout_view = DynamicTo<LayoutView>(GetLayoutBox())) {
2332 mojom::blink::ScrollbarMode h_mode;
2333 mojom::blink::ScrollbarMode v_mode;
2334 layout_view->CalculateScrollbarModes(h_mode, v_mode);
2335 if (h_mode == mojom::blink::ScrollbarMode::kAlwaysOff &&
2336 v_mode == mojom::blink::ScrollbarMode::kAlwaysOff)
2337 has_overflow = false;
2340 scrolls_overflow_ = has_overflow && is_visible_to_hit_test;
2341 if (did_scroll_overflow == ScrollsOverflow())
2344 if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
2345 // Change of scrolls_overflow may affect whether we create ScrollTranslation
2346 // which is referenced from ScrollDisplayItem. Invalidate scrollbars (but
2347 // not their parts) to repaint the display item.
2348 if (auto* scrollbar = HorizontalScrollbar())
2349 scrollbar->SetNeedsPaintInvalidation(kNoPart);
2350 if (auto* scrollbar = VerticalScrollbar())
2351 scrollbar->SetNeedsPaintInvalidation(kNoPart);
2354 if (RuntimeEnabledFeatures::ImplicitRootScrollerEnabled() &&
2355 scrolls_overflow_) {
2356 if (IsA<LayoutView>(GetLayoutBox())) {
2357 if (Element* owner = GetLayoutBox()->GetDocument().LocalOwner()) {
2358 owner->GetDocument().GetRootScrollerController().ConsiderForImplicit(
2364 .GetRootScrollerController()
2365 .ConsiderForImplicit(*GetLayoutBox()->GetNode());
2369 // The scroll and scroll offset properties depend on |scrollsOverflow| (see:
2370 // PaintPropertyTreeBuilder::updateScrollAndScrollTranslation).
2371 GetLayoutBox()->SetNeedsPaintPropertyUpdate();
2373 // Scroll hit test data depend on whether the box scrolls overflow.
2374 // They are painted in the background phase
2375 // (see: BoxPainter::PaintBoxDecorationBackground).
2376 GetLayoutBox()->SetBackgroundNeedsFullPaintInvalidation();
2378 layer_->DidUpdateScrollsOverflow();
2381 void PaintLayerScrollableArea::UpdateCompositingLayersAfterScroll() {
2382 DCHECK(!RuntimeEnabledFeatures::CompositeAfterPaintEnabled());
2384 DisableCompositingQueryAsserts disabler;
2385 PaintLayerCompositor* compositor = GetLayoutBox()->View()->Compositor();
2386 if (!compositor || !compositor->InCompositingMode())
2389 if (UsesCompositedScrolling()) {
2390 DCHECK(Layer()->HasCompositedLayerMapping());
2391 ScrollingCoordinator* scrolling_coordinator = GetScrollingCoordinator();
2392 bool handled_scroll = scrolling_coordinator &&
2393 scrolling_coordinator->UpdateCompositorScrollOffset(
2394 *GetLayoutBox()->GetFrame(), *this);
2396 if (!handled_scroll) {
2397 compositor->SetNeedsCompositingUpdate(
2398 kCompositingUpdateAfterGeometryChange);
2401 // If we have fixed elements and we scroll the root layer we might
2402 // change compositing since the fixed elements might now overlap a
2403 // composited layer.
2404 if (Layer()->IsRootLayer()) {
2405 LocalFrame* frame = GetLayoutBox()->GetFrame();
2406 if (frame && frame->View()) {
2407 LocalFrameView* view = frame->View();
2408 // When kMaxOverlapBoundsForFixed is enabled, the maximum possible
2409 // overlap (for all possible scroll offsets) of the fixed content has
2410 // been included in the overlap test, so we can skip the compositing
2411 // update on scroll changes for fixed content.
2412 bool requires_compositing_inputs_update =
2413 !base::FeatureList::IsEnabled(features::kMaxOverlapBoundsForFixed)
2414 ? view->HasViewportConstrainedObjects()
2415 : view->HasStickyViewportConstrainedObject();
2416 if (requires_compositing_inputs_update)
2417 Layer()->SetNeedsCompositingInputsUpdate();
2421 Layer()->SetNeedsCompositingInputsUpdate(false);
2425 ScrollingCoordinator* PaintLayerScrollableArea::GetScrollingCoordinator()
2427 LocalFrame* frame = GetLayoutBox()->GetFrame();
2431 Page* page = frame->GetPage();
2435 return page->GetScrollingCoordinator();
2438 bool PaintLayerScrollableArea::ShouldScrollOnMainThread() const {
2439 if (HasBeenDisposed())
2442 // TODO(crbug.com/985127, crbug.com/1015833): We should just use the main
2443 // thread scrolling reasons on the scroll node which should have all required
2444 // reasons. If it was not, we would have inconsistent results here and
2445 // ScrollNode::GetMainThreadScrollingReasons().
2446 if (LocalFrame* frame = GetLayoutBox()->GetFrame()) {
2447 if (frame->View()->GetMainThreadScrollingReasons())
2450 if (HasNonCompositedStickyDescendants())
2453 // Property tree state is not available until the PrePaint lifecycle stage.
2454 DCHECK_GE(GetDocument()->Lifecycle().GetState(),
2455 DocumentLifecycle::kPrePaintClean);
2456 const auto* properties = GetLayoutBox()->FirstFragment().PaintProperties();
2457 if (!properties || !properties->Scroll() ||
2458 properties->Scroll()->GetMainThreadScrollingReasons())
2461 DCHECK(properties->ScrollTranslation());
2462 if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
2463 return !properties->ScrollTranslation()->HasDirectCompositingReasons();
2465 return !GraphicsLayerForScrolling();
2468 static bool LayerNodeMayNeedCompositedScrolling(const PaintLayer* layer) {
2469 // Don't force composite scroll for select or text input elements.
2470 if (Node* node = layer->GetLayoutObject().GetNode()) {
2471 if (IsA<HTMLSelectElement>(node))
2473 if (TextControlElement* text_control = EnclosingTextControl(node)) {
2474 if (IsA<HTMLInputElement>(text_control)) {
2482 bool PaintLayerScrollableArea::ComputeNeedsCompositedScrolling(
2483 bool force_prefer_compositing_to_lcd_text) {
2484 DCHECK_EQ(RuntimeEnabledFeatures::CompositeAfterPaintEnabled()
2485 ? DocumentLifecycle::kInPrePaint
2486 : DocumentLifecycle::kInCompositingUpdate,
2487 GetDocument()->Lifecycle().GetState());
2489 const auto* box = GetLayoutBox();
2490 auto old_background_paint_location = box->GetBackgroundPaintLocation();
2491 non_composited_main_thread_scrolling_reasons_ = 0;
2492 auto new_background_paint_location =
2493 box->ComputeBackgroundPaintLocationIfComposited();
2494 bool needs_composited_scrolling = ComputeNeedsCompositedScrollingInternal(
2495 new_background_paint_location, force_prefer_compositing_to_lcd_text);
2496 if (!needs_composited_scrolling)
2497 new_background_paint_location = kBackgroundPaintInGraphicsLayer;
2498 if (new_background_paint_location != old_background_paint_location) {
2499 box->GetMutableForPainting().SetBackgroundPaintLocation(
2500 new_background_paint_location);
2503 return needs_composited_scrolling;
2506 bool PaintLayerScrollableArea::ComputeNeedsCompositedScrollingInternal(
2507 BackgroundPaintLocation background_paint_location_if_composited,
2508 bool force_prefer_compositing_to_lcd_text) {
2509 DCHECK_EQ(background_paint_location_if_composited,
2510 GetLayoutBox()->ComputeBackgroundPaintLocationIfComposited());
2512 if (CompositingReasonFinder::RequiresCompositingForRootScroller(*layer_))
2515 if (!layer_->ScrollsOverflow())
2518 if (layer_->Size().IsEmpty())
2521 const auto* box = GetLayoutBox();
2523 // Although trivial 3D transforms are not always a direct compositing reason
2524 // (see CompositingReasonFinder::RequiresCompositingFor3DTransform), we treat
2525 // them as one for composited scrolling. This is because of the amount of
2526 // content that depends on this optimization, and because of the long-term
2527 // desire to use composited scrolling whenever possible.
2528 if (box->HasTransformRelatedProperty() &&
2529 box->StyleRef().Has3DTransformOperation()) {
2533 if (!force_prefer_compositing_to_lcd_text &&
2534 (RuntimeEnabledFeatures::PreferNonCompositedScrollingEnabled() ||
2535 !LayerNodeMayNeedCompositedScrolling(layer_))) {
2539 bool needs_composited_scrolling = true;
2541 if (!force_prefer_compositing_to_lcd_text &&
2544 ->GetPreferCompositingToLCDTextEnabled()) {
2545 // TODO(crbug.com/1025927): We may remove this condition.
2546 if (layer_->CompositesWithTransform()) {
2547 non_composited_main_thread_scrolling_reasons_ |=
2548 cc::MainThreadScrollingReason::kHasTransformAndLCDText;
2549 needs_composited_scrolling = false;
2551 if (!box->TextIsKnownToBeOnOpaqueBackground()) {
2552 non_composited_main_thread_scrolling_reasons_ |=
2553 cc::MainThreadScrollingReason::kNotOpaqueForTextAndLCDText;
2554 needs_composited_scrolling = false;
2556 if (!(background_paint_location_if_composited &
2557 kBackgroundPaintInScrollingContents) &&
2558 box->StyleRef().HasBackground()) {
2559 non_composited_main_thread_scrolling_reasons_ |= cc::
2560 MainThreadScrollingReason::kCantPaintScrollingBackgroundAndLCDText;
2561 needs_composited_scrolling = false;
2565 DCHECK(!(non_composited_main_thread_scrolling_reasons_ &
2566 ~cc::MainThreadScrollingReason::kNonCompositedReasons));
2567 return needs_composited_scrolling;
2570 void PaintLayerScrollableArea::UpdateNeedsCompositedScrolling(
2571 bool force_prefer_compositing_to_lcd_text) {
2572 DCHECK(!RuntimeEnabledFeatures::CompositeAfterPaintEnabled());
2574 needs_composited_scrolling_ =
2575 ComputeNeedsCompositedScrolling(force_prefer_compositing_to_lcd_text);
2578 bool PaintLayerScrollableArea::VisualViewportSuppliesScrollbars() const {
2579 LocalFrame* frame = GetLayoutBox()->GetFrame();
2580 if (!frame || !frame->GetSettings())
2583 // On desktop, we always use the layout viewport's scrollbars.
2584 if (!frame->GetSettings()->GetViewportEnabled())
2587 const TopDocumentRootScrollerController& controller =
2588 GetLayoutBox()->GetDocument().GetPage()->GlobalRootScrollerController();
2589 return controller.RootScrollerArea() == this;
2592 bool PaintLayerScrollableArea::ScheduleAnimation() {
2593 if (ChromeClient* client =
2594 GetLayoutBox()->GetFrameView()->GetChromeClient()) {
2595 client->ScheduleAnimation(GetLayoutBox()->GetFrameView());
2601 void PaintLayerScrollableArea::ResetRebuildScrollbarLayerFlags() {
2602 rebuild_horizontal_scrollbar_layer_ = false;
2603 rebuild_vertical_scrollbar_layer_ = false;
2606 cc::AnimationHost* PaintLayerScrollableArea::GetCompositorAnimationHost()
2608 return layer_->GetLayoutObject().GetFrameView()->GetCompositorAnimationHost();
2611 CompositorAnimationTimeline*
2612 PaintLayerScrollableArea::GetCompositorAnimationTimeline() const {
2613 return layer_->GetLayoutObject()
2615 ->GetCompositorAnimationTimeline();
2618 bool PaintLayerScrollableArea::HasTickmarks() const {
2619 return layer_->IsRootLayer() &&
2620 To<LayoutView>(GetLayoutBox())->HasTickmarks();
2623 Vector<IntRect> PaintLayerScrollableArea::GetTickmarks() const {
2624 if (layer_->IsRootLayer())
2625 return To<LayoutView>(GetLayoutBox())->GetTickmarks();
2626 return Vector<IntRect>();
2629 void PaintLayerScrollableArea::ScrollbarManager::SetHasHorizontalScrollbar(
2630 bool has_scrollbar) {
2631 if (has_scrollbar) {
2633 h_bar_ = CreateScrollbar(kHorizontalScrollbar);
2634 h_bar_is_attached_ = 1;
2635 if (!h_bar_->IsCustomScrollbar())
2636 ScrollableArea()->DidAddScrollbar(*h_bar_, kHorizontalScrollbar);
2638 h_bar_is_attached_ = 1;
2641 h_bar_is_attached_ = 0;
2642 if (!DelayScrollOffsetClampScope::ClampingIsDelayed())
2643 DestroyScrollbar(kHorizontalScrollbar);
2647 void PaintLayerScrollableArea::ScrollbarManager::SetHasVerticalScrollbar(
2648 bool has_scrollbar) {
2649 if (has_scrollbar) {
2651 v_bar_ = CreateScrollbar(kVerticalScrollbar);
2652 v_bar_is_attached_ = 1;
2653 if (!v_bar_->IsCustomScrollbar())
2654 ScrollableArea()->DidAddScrollbar(*v_bar_, kVerticalScrollbar);
2656 v_bar_is_attached_ = 1;
2659 v_bar_is_attached_ = 0;
2660 if (!DelayScrollOffsetClampScope::ClampingIsDelayed())
2661 DestroyScrollbar(kVerticalScrollbar);
2665 Scrollbar* PaintLayerScrollableArea::ScrollbarManager::CreateScrollbar(
2666 ScrollbarOrientation orientation) {
2667 DCHECK(orientation == kHorizontalScrollbar ? !h_bar_is_attached_
2668 : !v_bar_is_attached_);
2669 Scrollbar* scrollbar = nullptr;
2670 const LayoutObject& style_source =
2671 ScrollbarStyleSource(*ScrollableArea()->GetLayoutBox());
2672 bool has_custom_scrollbar_style =
2673 style_source.StyleRef().HasPseudoElementStyle(kPseudoIdScrollbar);
2674 if (has_custom_scrollbar_style) {
2675 DCHECK(style_source.GetNode() && style_source.GetNode()->IsElementNode());
2676 scrollbar = MakeGarbageCollected<CustomScrollbar>(
2677 ScrollableArea(), orientation, To<Element>(style_source.GetNode()));
2679 ScrollbarControlSize scrollbar_size = kRegularScrollbar;
2680 if (style_source.StyleRef().HasEffectiveAppearance()) {
2681 scrollbar_size = LayoutTheme::GetTheme().ScrollbarControlSizeForPart(
2682 style_source.StyleRef().EffectiveAppearance());
2684 Element* style_source_element = nullptr;
2685 if (::features::IsFormControlsRefreshEnabled()) {
2686 style_source_element = DynamicTo<Element>(style_source.GetNode());
2688 scrollbar = MakeGarbageCollected<Scrollbar>(
2689 ScrollableArea(), orientation, scrollbar_size, style_source_element,
2694 ->GetChromeClient());
2696 ScrollableArea()->GetLayoutBox()->GetDocument().View()->AddScrollbar(
2701 void PaintLayerScrollableArea::ScrollbarManager::DestroyScrollbar(
2702 ScrollbarOrientation orientation) {
2703 Member<Scrollbar>& scrollbar =
2704 orientation == kHorizontalScrollbar ? h_bar_ : v_bar_;
2705 DCHECK(orientation == kHorizontalScrollbar ? !h_bar_is_attached_
2706 : !v_bar_is_attached_);
2710 ScrollableArea()->SetScrollbarNeedsPaintInvalidation(orientation);
2711 if (orientation == kHorizontalScrollbar)
2712 ScrollableArea()->rebuild_horizontal_scrollbar_layer_ = true;
2714 ScrollableArea()->rebuild_vertical_scrollbar_layer_ = true;
2716 if (!scrollbar->IsCustomScrollbar())
2717 ScrollableArea()->WillRemoveScrollbar(*scrollbar, orientation);
2719 ScrollableArea()->GetLayoutBox()->GetDocument().View()->RemoveScrollbar(
2721 scrollbar->DisconnectFromScrollableArea();
2722 scrollbar = nullptr;
2725 void PaintLayerScrollableArea::ScrollbarManager::DestroyDetachedScrollbars() {
2726 DCHECK(!h_bar_is_attached_ || h_bar_);
2727 DCHECK(!v_bar_is_attached_ || v_bar_);
2728 if (h_bar_ && !h_bar_is_attached_)
2729 DestroyScrollbar(kHorizontalScrollbar);
2730 if (v_bar_ && !v_bar_is_attached_)
2731 DestroyScrollbar(kVerticalScrollbar);
2734 void PaintLayerScrollableArea::ScrollbarManager::Dispose() {
2735 h_bar_is_attached_ = v_bar_is_attached_ = 0;
2736 DestroyScrollbar(kHorizontalScrollbar);
2737 DestroyScrollbar(kVerticalScrollbar);
2740 void PaintLayerScrollableArea::ScrollbarManager::Trace(
2741 blink::Visitor* visitor) const {
2742 visitor->Trace(scrollable_area_);
2743 visitor->Trace(h_bar_);
2744 visitor->Trace(v_bar_);
2747 uint64_t PaintLayerScrollableArea::Id() const {
2748 return DOMNodeIds::IdForNode(GetLayoutBox()->GetNode());
2751 int PaintLayerScrollableArea::PreventRelayoutScope::count_ = 0;
2753 PaintLayerScrollableArea::PreventRelayoutScope::layout_scope_ = nullptr;
2754 bool PaintLayerScrollableArea::PreventRelayoutScope::relayout_needed_ = false;
2756 PaintLayerScrollableArea::PreventRelayoutScope::PreventRelayoutScope(
2757 SubtreeLayoutScope& layout_scope) {
2759 DCHECK(!layout_scope_);
2760 DCHECK(NeedsRelayoutList().IsEmpty());
2761 layout_scope_ = &layout_scope;
2766 PaintLayerScrollableArea::PreventRelayoutScope::~PreventRelayoutScope() {
2767 if (--count_ == 0) {
2768 if (relayout_needed_) {
2769 for (auto scrollable_area : NeedsRelayoutList()) {
2770 DCHECK(scrollable_area->NeedsRelayout());
2771 LayoutBox* box = scrollable_area->GetLayoutBox();
2772 layout_scope_->SetNeedsLayout(
2773 box, layout_invalidation_reason::kScrollbarChanged);
2774 if (auto* layout_block = DynamicTo<LayoutBlock>(box)) {
2775 bool horizontal_scrollbar_changed =
2776 scrollable_area->HasHorizontalScrollbar() !=
2777 scrollable_area->HadHorizontalScrollbarBeforeRelayout();
2778 bool vertical_scrollbar_changed =
2779 scrollable_area->HasVerticalScrollbar() !=
2780 scrollable_area->HadVerticalScrollbarBeforeRelayout();
2781 if (horizontal_scrollbar_changed || vertical_scrollbar_changed) {
2782 layout_block->ScrollbarsChanged(horizontal_scrollbar_changed,
2783 vertical_scrollbar_changed);
2786 scrollable_area->SetNeedsRelayout(false);
2789 NeedsRelayoutList().clear();
2791 layout_scope_ = nullptr;
2795 void PaintLayerScrollableArea::PreventRelayoutScope::SetBoxNeedsLayout(
2796 PaintLayerScrollableArea& scrollable_area,
2797 bool had_horizontal_scrollbar,
2798 bool had_vertical_scrollbar) {
2800 DCHECK(layout_scope_);
2801 if (scrollable_area.NeedsRelayout())
2803 scrollable_area.SetNeedsRelayout(true);
2804 scrollable_area.SetHadHorizontalScrollbarBeforeRelayout(
2805 had_horizontal_scrollbar);
2806 scrollable_area.SetHadVerticalScrollbarBeforeRelayout(had_vertical_scrollbar);
2808 relayout_needed_ = true;
2809 NeedsRelayoutList().push_back(&scrollable_area);
2812 void PaintLayerScrollableArea::PreventRelayoutScope::ResetRelayoutNeeded() {
2813 DCHECK_EQ(count_, 0);
2814 DCHECK(NeedsRelayoutList().IsEmpty());
2815 relayout_needed_ = false;
2818 HeapVector<Member<PaintLayerScrollableArea>>&
2819 PaintLayerScrollableArea::PreventRelayoutScope::NeedsRelayoutList() {
2820 DEFINE_STATIC_LOCAL(
2821 Persistent<HeapVector<Member<PaintLayerScrollableArea>>>,
2822 needs_relayout_list,
2823 (MakeGarbageCollected<HeapVector<Member<PaintLayerScrollableArea>>>()));
2824 return *needs_relayout_list;
2827 int PaintLayerScrollableArea::FreezeScrollbarsScope::count_ = 0;
2829 int PaintLayerScrollableArea::DelayScrollOffsetClampScope::count_ = 0;
2831 PaintLayerScrollableArea::DelayScrollOffsetClampScope::
2832 DelayScrollOffsetClampScope() {
2833 DCHECK(count_ > 0 || NeedsClampList().IsEmpty());
2837 PaintLayerScrollableArea::DelayScrollOffsetClampScope::
2838 ~DelayScrollOffsetClampScope() {
2840 DelayScrollOffsetClampScope::ClampScrollableAreas();
2843 void PaintLayerScrollableArea::DelayScrollOffsetClampScope::SetNeedsClamp(
2844 PaintLayerScrollableArea* scrollable_area) {
2845 if (!scrollable_area->NeedsScrollOffsetClamp()) {
2846 scrollable_area->SetNeedsScrollOffsetClamp(true);
2847 NeedsClampList().push_back(scrollable_area);
2851 void PaintLayerScrollableArea::DelayScrollOffsetClampScope::
2852 ClampScrollableAreas() {
2853 for (auto& scrollable_area : NeedsClampList())
2854 scrollable_area->ClampScrollOffsetAfterOverflowChange();
2855 NeedsClampList().clear();
2858 HeapVector<Member<PaintLayerScrollableArea>>&
2859 PaintLayerScrollableArea::DelayScrollOffsetClampScope::NeedsClampList() {
2860 DEFINE_STATIC_LOCAL(
2861 Persistent<HeapVector<Member<PaintLayerScrollableArea>>>,
2863 (MakeGarbageCollected<HeapVector<Member<PaintLayerScrollableArea>>>()));
2864 return *needs_clamp_list;
2867 ScrollbarTheme& PaintLayerScrollableArea::GetPageScrollbarTheme() const {
2868 // If PaintLayer is destructed before PaintLayerScrollable area, we can not
2869 // get the page scrollbar theme setting.
2870 DCHECK(!HasBeenDisposed());
2872 Page* page = GetLayoutBox()->GetFrame()->GetPage();
2875 return page->GetScrollbarTheme();
2878 void PaintLayerScrollableArea::DidAddScrollbar(
2879 Scrollbar& scrollbar,
2880 ScrollbarOrientation orientation) {
2881 // Z-order of reparented scrollbar is updated along with the z-order lists.
2882 if (scrollbar.IsOverlayScrollbar())
2883 layer_->DirtyStackingContextZOrderLists();
2885 ScrollableArea::DidAddScrollbar(scrollbar, orientation);
2888 void PaintLayerScrollableArea::WillRemoveScrollbar(
2889 Scrollbar& scrollbar,
2890 ScrollbarOrientation orientation) {
2891 if (layer_->NeedsReorderOverlayOverflowControls()) {
2892 // Z-order of reparented scrollbar is updated along with the z-order lists.
2893 DCHECK(scrollbar.IsOverlayScrollbar());
2894 layer_->DirtyStackingContextZOrderLists();
2897 if (!scrollbar.IsCustomScrollbar() &&
2898 !(orientation == kHorizontalScrollbar
2899 ? GraphicsLayerForHorizontalScrollbar()
2900 : GraphicsLayerForVerticalScrollbar())) {
2901 ObjectPaintInvalidator(*GetLayoutBox())
2902 .SlowSetPaintingLayerNeedsRepaintAndInvalidateDisplayItemClient(
2903 scrollbar, PaintInvalidationReason::kScrollControl);
2906 ScrollableArea::WillRemoveScrollbar(scrollbar, orientation);
2909 // Returns true if the scroll control is invalidated.
2910 static bool ScrollControlNeedsPaintInvalidation(
2911 const IntRect& new_visual_rect,
2912 const IntRect& previous_visual_rect,
2913 bool needs_paint_invalidation) {
2914 if (new_visual_rect != previous_visual_rect)
2916 if (previous_visual_rect.IsEmpty()) {
2917 DCHECK(new_visual_rect.IsEmpty());
2918 // Do not issue an empty invalidation.
2922 return needs_paint_invalidation;
2925 static IntRect InvalidatePaintOfScrollbarIfNeeded(
2926 Scrollbar* scrollbar,
2927 GraphicsLayer* graphics_layer,
2928 bool& previously_was_overlay,
2929 const IntRect& previous_visual_rect,
2930 bool needs_paint_invalidation,
2932 bool& box_geometry_has_been_invalidated,
2933 const PaintInvalidatorContext& context) {
2934 bool is_overlay = scrollbar && scrollbar->IsOverlayScrollbar();
2936 IntRect new_visual_rect;
2938 new_visual_rect = scrollbar->FrameRect();
2939 // TODO(crbug.com/1020913): We should not round paint_offset but should
2940 // consider subpixel accumulation when painting scrollbars.
2941 new_visual_rect.MoveBy(
2942 RoundedIntPoint(context.fragment_data->PaintOffset()));
2945 if (needs_paint_invalidation && graphics_layer) {
2946 // If the scrollbar needs paint invalidation but didn't change location/size
2947 // or the scrollbar is an overlay scrollbar (visual rect is empty),
2948 // invalidating the graphics layer is enough (which has been done in
2949 // ScrollableArea::setScrollbarNeedsPaintInvalidation()).
2950 needs_paint_invalidation = false;
2951 DCHECK(!graphics_layer->PaintsContentOrHitTest() ||
2952 graphics_layer->GetPaintController().GetPaintArtifact().IsEmpty());
2955 // Invalidate the box's display item client if the box's padding box size is
2956 // affected by change of the non-overlay scrollbar width. We detect change of
2957 // visual rect size instead of change of scrollbar width, which may have some
2958 // false-positives (e.g. the scrollbar changed length but not width) but won't
2959 // invalidate more than expected because in the false-positive case the box
2960 // must have changed size and have been invalidated.
2961 IntSize new_scrollbar_used_space_in_box;
2963 new_scrollbar_used_space_in_box = new_visual_rect.Size();
2964 IntSize previous_scrollbar_used_space_in_box;
2965 if (!previously_was_overlay)
2966 previous_scrollbar_used_space_in_box = previous_visual_rect.Size();
2968 // The IsEmpty() check avoids invalidaiton in cases when the visual rect
2969 // changes from (0,0 0x0) to (0,0 0x100).
2970 if (!box_geometry_has_been_invalidated &&
2971 !(new_scrollbar_used_space_in_box.IsEmpty() &&
2972 previous_scrollbar_used_space_in_box.IsEmpty()) &&
2973 new_scrollbar_used_space_in_box != previous_scrollbar_used_space_in_box) {
2974 context.painting_layer->SetNeedsRepaint();
2975 ObjectPaintInvalidator(box).InvalidateDisplayItemClient(
2976 box, PaintInvalidationReason::kGeometry);
2977 box_geometry_has_been_invalidated = true;
2980 previously_was_overlay = is_overlay;
2982 if (!scrollbar || graphics_layer ||
2983 !ScrollControlNeedsPaintInvalidation(
2984 new_visual_rect, previous_visual_rect, needs_paint_invalidation))
2985 return new_visual_rect;
2987 context.painting_layer->SetNeedsRepaint();
2988 ObjectPaintInvalidator(box).InvalidateDisplayItemClient(
2989 *scrollbar, PaintInvalidationReason::kScrollControl);
2990 if (scrollbar->IsCustomScrollbar()) {
2991 To<CustomScrollbar>(scrollbar)
2992 ->InvalidateDisplayItemClientsOfScrollbarParts();
2995 return new_visual_rect;
2998 void PaintLayerScrollableArea::InvalidatePaintOfScrollControlsIfNeeded(
2999 const PaintInvalidatorContext& context) {
3000 LayoutBox& box = *GetLayoutBox();
3001 bool box_geometry_has_been_invalidated = false;
3002 SetHorizontalScrollbarVisualRect(InvalidatePaintOfScrollbarIfNeeded(
3003 HorizontalScrollbar(), GraphicsLayerForHorizontalScrollbar(),
3004 horizontal_scrollbar_previously_was_overlay_,
3005 horizontal_scrollbar_visual_rect_,
3006 HorizontalScrollbarNeedsPaintInvalidation(), box,
3007 box_geometry_has_been_invalidated, context));
3008 SetVerticalScrollbarVisualRect(InvalidatePaintOfScrollbarIfNeeded(
3009 VerticalScrollbar(), GraphicsLayerForVerticalScrollbar(),
3010 vertical_scrollbar_previously_was_overlay_,
3011 vertical_scrollbar_visual_rect_,
3012 VerticalScrollbarNeedsPaintInvalidation(), box,
3013 box_geometry_has_been_invalidated, context));
3015 IntRect scroll_corner_and_resizer_visual_rect = ScrollCornerAndResizerRect();
3016 // TODO(crbug.com/1020913): We should not round paint_offset but should
3017 // consider subpixel accumulation when painting scrollbars.
3018 scroll_corner_and_resizer_visual_rect.MoveBy(
3019 RoundedIntPoint(context.fragment_data->PaintOffset()));
3020 if (ScrollControlNeedsPaintInvalidation(
3021 scroll_corner_and_resizer_visual_rect,
3022 scroll_corner_and_resizer_visual_rect_,
3023 ScrollCornerNeedsPaintInvalidation())) {
3024 SetScrollCornerAndResizerVisualRect(scroll_corner_and_resizer_visual_rect);
3025 if (LayoutCustomScrollbarPart* scroll_corner = ScrollCorner()) {
3026 ObjectPaintInvalidator(*scroll_corner)
3027 .SlowSetPaintingLayerNeedsRepaintAndInvalidateDisplayItemClient(
3028 *scroll_corner, PaintInvalidationReason::kScrollControl);
3030 if (LayoutCustomScrollbarPart* resizer = Resizer()) {
3031 ObjectPaintInvalidator(*resizer)
3032 .SlowSetPaintingLayerNeedsRepaintAndInvalidateDisplayItemClient(
3033 *resizer, PaintInvalidationReason::kScrollControl);
3035 if (!GraphicsLayerForScrollCorner()) {
3036 context.painting_layer->SetNeedsRepaint();
3037 ObjectPaintInvalidator(box).InvalidateDisplayItemClient(
3038 GetScrollCornerDisplayItemClient(),
3039 PaintInvalidationReason::kGeometry);
3043 ClearNeedsPaintInvalidationForScrollControls();
3046 void PaintLayerScrollableArea::ClearPreviousVisualRects() {
3047 SetHorizontalScrollbarVisualRect(IntRect());
3048 SetVerticalScrollbarVisualRect(IntRect());
3049 SetScrollCornerAndResizerVisualRect(IntRect());
3052 void PaintLayerScrollableArea::SetHorizontalScrollbarVisualRect(
3053 const IntRect& rect) {
3054 horizontal_scrollbar_visual_rect_ = rect;
3055 if (Scrollbar* scrollbar = HorizontalScrollbar())
3056 scrollbar->SetVisualRect(rect);
3059 void PaintLayerScrollableArea::SetVerticalScrollbarVisualRect(
3060 const IntRect& rect) {
3061 vertical_scrollbar_visual_rect_ = rect;
3062 if (Scrollbar* scrollbar = VerticalScrollbar())
3063 scrollbar->SetVisualRect(rect);
3066 void PaintLayerScrollableArea::SetScrollCornerAndResizerVisualRect(
3067 const IntRect& rect) {
3068 scroll_corner_and_resizer_visual_rect_ = rect;
3069 if (LayoutCustomScrollbarPart* scroll_corner = ScrollCorner())
3070 scroll_corner->GetMutableForPainting().FirstFragment().SetVisualRect(rect);
3071 if (LayoutCustomScrollbarPart* resizer = Resizer())
3072 resizer->GetMutableForPainting().FirstFragment().SetVisualRect(rect);
3075 void PaintLayerScrollableArea::ScrollControlWasSetNeedsPaintInvalidation() {
3076 GetLayoutBox()->SetShouldCheckForPaintInvalidation();
3079 void PaintLayerScrollableArea::DidScrollWithScrollbar(
3081 ScrollbarOrientation orientation,
3082 WebInputEvent::Type type) {
3083 WebFeature scrollbar_use_uma;
3085 case kBackButtonEndPart:
3086 case kForwardButtonStartPart:
3088 GetLayoutBox()->GetDocument(),
3089 WebFeature::kScrollbarUseScrollbarButtonReversedDirection);
3091 case kBackButtonStartPart:
3092 case kForwardButtonEndPart:
3094 (orientation == kVerticalScrollbar
3095 ? WebFeature::kScrollbarUseVerticalScrollbarButton
3096 : WebFeature::kScrollbarUseHorizontalScrollbarButton);
3099 if (orientation == kVerticalScrollbar) {
3101 (WebInputEvent::IsMouseEventType(type)
3102 ? WebFeature::kVerticalScrollbarThumbScrollingWithMouse
3103 : WebFeature::kVerticalScrollbarThumbScrollingWithTouch);
3106 (WebInputEvent::IsMouseEventType(type)
3107 ? WebFeature::kHorizontalScrollbarThumbScrollingWithMouse
3108 : WebFeature::kHorizontalScrollbarThumbScrollingWithTouch);
3111 case kBackTrackPart:
3112 case kForwardTrackPart:
3114 (orientation == kVerticalScrollbar
3115 ? WebFeature::kScrollbarUseVerticalScrollbarTrack
3116 : WebFeature::kScrollbarUseHorizontalScrollbarTrack);
3122 Document& document = GetLayoutBox()->GetDocument();
3124 UseCounter::Count(document, scrollbar_use_uma);
3127 CompositorElementId PaintLayerScrollableArea::GetScrollElementId() const {
3128 return CompositorElementIdFromUniqueObjectId(
3129 GetLayoutBox()->UniqueId(), CompositorElementIdNamespace::kScroll);
3132 IntSize PaintLayerScrollableArea::PixelSnappedBorderBoxSize() const {
3133 // TODO(crbug.com/1020913): We use this method during
3134 // PositionOverflowControls() even before the paint offset is updated.
3135 // This can be fixed only after we support subpixels in overflow control
3136 // geometry. For now we ensure correct pixel snapping of overflow controls by
3137 // calling PositionOverflowControls() again when paint offset is updated.
3138 return GetLayoutBox()->PixelSnappedBorderBoxSize(
3139 GetLayoutBox()->FirstFragment().PaintOffset());
3143 PaintLayerScrollableArea::ScrollingBackgroundDisplayItemClient::VisualRect()
3145 const auto* box = scrollable_area_->GetLayoutBox();
3147 const auto& paint_offset = box->FirstFragment().PaintOffset();
3148 auto overflow_clip_rect =
3149 PixelSnappedIntRect(box->OverflowClipRect(paint_offset));
3150 auto scroll_size = scrollable_area_->PixelSnappedContentsSize(paint_offset);
3151 // Ensure scrolling contents are at least as large as the scroll clip
3152 scroll_size = scroll_size.ExpandedTo(overflow_clip_rect.Size());
3153 IntRect result(overflow_clip_rect.Location(), scroll_size);
3155 if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
3157 scrollable_area_->layer_->GraphicsLayerBacking()->VisualRect());
3161 // The HTML element of a document is special, in that it can have a transform,
3162 // but the bounds of the painted area of the element still extends beyond
3163 // its actual size to encompass the entire viewport canvas. This is
3164 // accomplished in ViewPainter by starting with a rect in viewport canvas
3165 // space that is equal to the size of the viewport canvas, then mapping it
3166 // into the local border box space of the HTML element, and painting a rect
3167 // equal to the bounding box of the result. We need to add in that mapped rect
3169 const Document& document = box->GetDocument();
3170 if (IsA<LayoutView>(box) &&
3171 (document.IsXMLDocument() || document.IsHTMLDocument())) {
3172 if (const auto* document_element = document.documentElement()) {
3173 if (const auto* document_element_object =
3174 document_element->GetLayoutObject()) {
3175 const PropertyTreeState& document_element_state =
3176 document_element_object->FirstFragment().LocalBorderBoxProperties();
3177 const PropertyTreeState& view_contents_state =
3178 box->FirstFragment().ContentsProperties();
3179 IntRect result_in_view = result;
3180 GeometryMapper::SourceToDestinationRect(
3181 view_contents_state.Transform(), document_element_state.Transform(),
3183 result.Unite(result_in_view);
3192 PaintLayerScrollableArea::ScrollingBackgroundDisplayItemClient::DebugName()
3194 return "Scrolling background of " +
3195 scrollable_area_->GetLayoutBox()->DebugName();
3199 PaintLayerScrollableArea::ScrollingBackgroundDisplayItemClient::OwnerNodeId()
3201 return static_cast<const DisplayItemClient*>(scrollable_area_->GetLayoutBox())
3205 IntRect PaintLayerScrollableArea::ScrollCornerDisplayItemClient::VisualRect()
3207 return scrollable_area_->scroll_corner_and_resizer_visual_rect_;
3210 String PaintLayerScrollableArea::ScrollCornerDisplayItemClient::DebugName()
3212 return "Scroll corner of " + scrollable_area_->GetLayoutBox()->DebugName();
3215 DOMNodeId PaintLayerScrollableArea::ScrollCornerDisplayItemClient::OwnerNodeId()
3217 return static_cast<const DisplayItemClient*>(scrollable_area_->GetLayoutBox())
3221 } // namespace blink