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 "third_party/blink/public/platform/platform.h"
50 #include "third_party/blink/public/platform/task_type.h"
51 #include "third_party/blink/public/platform/web_scroll_into_view_params.h"
52 #include "third_party/blink/renderer/core/accessibility/ax_object_cache.h"
53 #include "third_party/blink/renderer/core/animation/scroll_timeline.h"
54 #include "third_party/blink/renderer/core/css/pseudo_style_request.h"
55 #include "third_party/blink/renderer/core/dom/dom_node_ids.h"
56 #include "third_party/blink/renderer/core/dom/node.h"
57 #include "third_party/blink/renderer/core/dom/shadow_root.h"
58 #include "third_party/blink/renderer/core/editing/frame_selection.h"
59 #include "third_party/blink/renderer/core/editing/markers/document_marker_controller.h"
60 #include "third_party/blink/renderer/core/frame/local_frame.h"
61 #include "third_party/blink/renderer/core/frame/local_frame_client.h"
62 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
63 #include "third_party/blink/renderer/core/frame/page_scale_constraints_set.h"
64 #include "third_party/blink/renderer/core/frame/root_frame_viewport.h"
65 #include "third_party/blink/renderer/core/frame/settings.h"
66 #include "third_party/blink/renderer/core/frame/visual_viewport.h"
67 #include "third_party/blink/renderer/core/fullscreen/fullscreen.h"
68 #include "third_party/blink/renderer/core/html/forms/text_control_element.h"
69 #include "third_party/blink/renderer/core/html/html_frame_owner_element.h"
70 #include "third_party/blink/renderer/core/input/event_handler.h"
71 #include "third_party/blink/renderer/core/layout/layout_embedded_content.h"
72 #include "third_party/blink/renderer/core/layout/layout_flexible_box.h"
73 #include "third_party/blink/renderer/core/layout/layout_scrollbar.h"
74 #include "third_party/blink/renderer/core/layout/layout_scrollbar_part.h"
75 #include "third_party/blink/renderer/core/layout/layout_theme.h"
76 #include "third_party/blink/renderer/core/layout/layout_view.h"
77 #include "third_party/blink/renderer/core/layout/logical_values.h"
78 #include "third_party/blink/renderer/core/layout/ng/legacy_layout_tree_walking.h"
79 #include "third_party/blink/renderer/core/loader/document_loader.h"
80 #include "third_party/blink/renderer/core/page/chrome_client.h"
81 #include "third_party/blink/renderer/core/page/focus_controller.h"
82 #include "third_party/blink/renderer/core/page/page.h"
83 #include "third_party/blink/renderer/core/page/scrolling/fragment_anchor.h"
84 #include "third_party/blink/renderer/core/page/scrolling/root_scroller_controller.h"
85 #include "third_party/blink/renderer/core/page/scrolling/root_scroller_util.h"
86 #include "third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.h"
87 #include "third_party/blink/renderer/core/page/scrolling/snap_coordinator.h"
88 #include "third_party/blink/renderer/core/page/scrolling/top_document_root_scroller_controller.h"
89 #include "third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h"
90 #include "third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.h"
91 #include "third_party/blink/renderer/core/paint/find_paint_offset_and_visual_rect_needing_update.h"
92 #include "third_party/blink/renderer/core/paint/paint_invalidator.h"
93 #include "third_party/blink/renderer/core/paint/paint_layer_fragment.h"
94 #include "third_party/blink/renderer/core/scroll/scroll_animator_base.h"
95 #include "third_party/blink/renderer/core/scroll/scrollbar_theme.h"
96 #include "third_party/blink/renderer/core/scroll/smooth_scroll_sequencer.h"
97 #include "third_party/blink/renderer/platform/graphics/graphics_layer.h"
98 #include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h"
99 #include "third_party/blink/renderer/platform/scroll/scroll_alignment.h"
105 // Default value is set to 15 as the default
106 // minimum size used by firefox is 15x15.
107 static const int kDefaultMinimumWidthForResizing = 15;
108 static const int kDefaultMinimumHeightForResizing = 15;
112 static LayoutRect LocalToAbsolute(const LayoutBox& box, LayoutRect rect) {
114 box.LocalToAbsoluteQuad(FloatQuad(FloatRect(rect)), kUseTransforms)
118 static LayoutRect AbsoluteToLocal(const LayoutBox& box, LayoutRect rect) {
120 box.AbsoluteToLocalQuad(FloatQuad(FloatRect(rect)), kUseTransforms)
124 PaintLayerScrollableAreaRareData::PaintLayerScrollableAreaRareData() = default;
126 const int kResizerControlExpandRatioForTouch = 2;
128 PaintLayerScrollableArea::PaintLayerScrollableArea(PaintLayer& layer)
130 in_resize_mode_(false),
131 scrolls_overflow_(false),
132 in_overflow_relayout_(false),
133 allow_second_overflow_relayout_(false),
134 needs_composited_scrolling_(false),
135 rebuild_horizontal_scrollbar_layer_(false),
136 rebuild_vertical_scrollbar_layer_(false),
137 needs_scroll_offset_clamp_(false),
138 needs_relayout_(false),
139 had_horizontal_scrollbar_before_relayout_(false),
140 had_vertical_scrollbar_before_relayout_(false),
141 scroll_origin_changed_(false),
142 scrollbar_manager_(*this),
143 scroll_corner_(nullptr),
145 scroll_anchor_(this),
146 non_composited_main_thread_scrolling_reasons_(0),
147 horizontal_scrollbar_previously_was_overlay_(false),
148 vertical_scrollbar_previously_was_overlay_(false),
149 scrolling_background_display_item_client_(*this) {
150 Node* node = GetLayoutBox()->GetNode();
151 if (node && node->IsElementNode()) {
152 // We save and restore only the scrollOffset as the other scroll values are
154 Element* element = ToElement(node);
155 scroll_offset_ = element->SavedLayerScrollOffset();
156 if (!scroll_offset_.IsZero())
157 GetScrollAnimator().SetCurrentOffset(scroll_offset_);
158 element->SetSavedLayerScrollOffset(ScrollOffset());
160 UpdateResizerAreaSet();
163 PaintLayerScrollableArea::~PaintLayerScrollableArea() {
164 CHECK(HasBeenDisposed());
167 void PaintLayerScrollableArea::DidScroll(const FloatPoint& position) {
168 ScrollableArea::DidScroll(position);
169 // This should be alive if it receives composited scroll callbacks.
170 CHECK(!HasBeenDisposed());
173 void PaintLayerScrollableArea::Dispose() {
174 if (InResizeMode() && !GetLayoutBox()->DocumentBeingDestroyed()) {
175 if (LocalFrame* frame = GetLayoutBox()->GetFrame())
176 frame->GetEventHandler().ResizeScrollableAreaDestroyed();
179 if (LocalFrame* frame = GetLayoutBox()->GetFrame()) {
180 if (LocalFrameView* frame_view = frame->View()) {
181 frame_view->RemoveScrollableArea(this);
182 frame_view->RemoveAnimatingScrollableArea(this);
186 non_composited_main_thread_scrolling_reasons_ = 0;
188 if (ScrollingCoordinator* scrolling_coordinator = GetScrollingCoordinator())
189 scrolling_coordinator->WillDestroyScrollableArea(this);
191 if (!GetLayoutBox()->DocumentBeingDestroyed()) {
192 Node* node = GetLayoutBox()->GetNode();
193 // FIXME: Make setSavedLayerScrollOffset take DoubleSize. crbug.com/414283.
194 if (node && node->IsElementNode())
195 ToElement(node)->SetSavedLayerScrollOffset(scroll_offset_);
198 if (LocalFrame* frame = GetLayoutBox()->GetFrame()) {
199 if (LocalFrameView* frame_view = frame->View())
200 frame_view->RemoveResizerArea(*GetLayoutBox());
203 // Note: it is not safe to call ScrollAnchor::clear if the document is being
204 // destroyed, because LayoutObjectChildList::removeChildNode skips the call to
205 // willBeRemovedFromTree,
206 // leaving the ScrollAnchor with a stale LayoutObject pointer.
207 scroll_anchor_.Dispose();
212 ->GlobalRootScrollerController()
213 .DidDisposeScrollableArea(*this);
215 scrollbar_manager_.Dispose();
218 scroll_corner_->Destroy();
222 ClearScrollableArea();
224 if (SmoothScrollSequencer* sequencer = GetSmoothScrollSequencer())
225 sequencer->DidDisposeScrollableArea(*this);
230 bool PaintLayerScrollableArea::HasBeenDisposed() const {
234 void PaintLayerScrollableArea::Trace(blink::Visitor* visitor) {
235 visitor->Trace(scrollbar_manager_);
236 visitor->Trace(scroll_anchor_);
237 visitor->Trace(scrolling_background_display_item_client_);
238 ScrollableArea::Trace(visitor);
241 bool PaintLayerScrollableArea::IsThrottled() const {
242 return GetLayoutBox()->GetFrame()->ShouldThrottleRendering();
245 ChromeClient* PaintLayerScrollableArea::GetChromeClient() const {
246 if (HasBeenDisposed())
248 if (Page* page = GetLayoutBox()->GetFrame()->GetPage())
249 return &page->GetChromeClient();
253 SmoothScrollSequencer* PaintLayerScrollableArea::GetSmoothScrollSequencer()
255 if (HasBeenDisposed())
258 return &GetLayoutBox()->GetFrame()->GetSmoothScrollSequencer();
261 GraphicsLayer* PaintLayerScrollableArea::LayerForScrolling() const {
262 return Layer()->HasCompositedLayerMapping()
263 ? Layer()->GetCompositedLayerMapping()->ScrollingContentsLayer()
267 GraphicsLayer* PaintLayerScrollableArea::LayerForHorizontalScrollbar() const {
268 // See crbug.com/343132.
269 DisableCompositingQueryAsserts disabler;
271 return Layer()->HasCompositedLayerMapping()
273 ->GetCompositedLayerMapping()
274 ->LayerForHorizontalScrollbar()
278 GraphicsLayer* PaintLayerScrollableArea::LayerForVerticalScrollbar() const {
279 // See crbug.com/343132.
280 DisableCompositingQueryAsserts disabler;
282 return Layer()->HasCompositedLayerMapping()
283 ? Layer()->GetCompositedLayerMapping()->LayerForVerticalScrollbar()
287 GraphicsLayer* PaintLayerScrollableArea::LayerForScrollCorner() const {
288 // See crbug.com/343132.
289 DisableCompositingQueryAsserts disabler;
291 return Layer()->HasCompositedLayerMapping()
292 ? Layer()->GetCompositedLayerMapping()->LayerForScrollCorner()
296 bool PaintLayerScrollableArea::IsActive() const {
297 Page* page = GetLayoutBox()->GetFrame()->GetPage();
298 return page && page->GetFocusController().IsActive();
301 bool PaintLayerScrollableArea::IsScrollCornerVisible() const {
302 return !ScrollCornerRect().IsEmpty();
305 static int CornerStart(const LayoutBox& box,
309 if (box.ShouldPlaceBlockDirectionScrollbarOnLogicalLeft())
310 return min_x + box.StyleRef().BorderLeftWidth();
311 return max_x - thickness - box.StyleRef().BorderRightWidth();
314 IntRect PaintLayerScrollableArea::PaintLayerScrollableArea::CornerRect(
315 const IntRect& bounds) const {
316 int horizontal_thickness;
317 int vertical_thickness;
318 if (!VerticalScrollbar() && !HorizontalScrollbar()) {
319 // FIXME: This isn't right. We need to know the thickness of custom
320 // scrollbars even when they don't exist in order to set the resizer square
322 horizontal_thickness = GetPageScrollbarTheme().ScrollbarThickness();
323 vertical_thickness = horizontal_thickness;
324 } else if (VerticalScrollbar() && !HorizontalScrollbar()) {
325 horizontal_thickness = VerticalScrollbar()->ScrollbarThickness();
326 vertical_thickness = horizontal_thickness;
327 } else if (HorizontalScrollbar() && !VerticalScrollbar()) {
328 vertical_thickness = HorizontalScrollbar()->ScrollbarThickness();
329 horizontal_thickness = vertical_thickness;
331 horizontal_thickness = VerticalScrollbar()->ScrollbarThickness();
332 vertical_thickness = HorizontalScrollbar()->ScrollbarThickness();
334 return IntRect(CornerStart(*GetLayoutBox(), bounds.X(), bounds.MaxX(),
335 horizontal_thickness),
336 bounds.MaxY() - vertical_thickness -
337 GetLayoutBox()->StyleRef().BorderBottomWidth(),
338 horizontal_thickness, vertical_thickness);
341 IntRect PaintLayerScrollableArea::ScrollCornerRect() const {
342 // We have a scrollbar corner when a scrollbar is visible and not filling the
343 // entire length of the box.
344 // This happens when:
345 // (a) A resizer is present and at least one scrollbar is present
346 // (b) Both scrollbars are present.
347 bool has_horizontal_bar = HorizontalScrollbar();
348 bool has_vertical_bar = VerticalScrollbar();
349 bool has_resizer = GetLayoutBox()->StyleRef().Resize() != EResize::kNone;
350 if ((has_horizontal_bar && has_vertical_bar) ||
351 (has_resizer && (has_horizontal_bar || has_vertical_bar))) {
352 return CornerRect(GetLayoutBox()->PixelSnappedBorderBoxRect(
353 Layer()->SubpixelAccumulation()));
359 PaintLayerScrollableArea::ConvertFromScrollbarToContainingEmbeddedContentView(
360 const Scrollbar& scrollbar,
361 const IntRect& scrollbar_rect) const {
362 LayoutView* view = GetLayoutBox()->View();
364 return scrollbar_rect;
366 IntRect rect = scrollbar_rect;
367 rect.Move(ScrollbarOffset(scrollbar));
369 return view->GetFrameView()->ConvertFromLayoutObject(*GetLayoutBox(), rect);
373 PaintLayerScrollableArea::ConvertFromScrollbarToContainingEmbeddedContentView(
374 const Scrollbar& scrollbar,
375 const IntPoint& scrollbar_point) const {
376 LayoutView* view = GetLayoutBox()->View();
378 return scrollbar_point;
380 IntPoint point = scrollbar_point;
381 point.Move(ScrollbarOffset(scrollbar));
382 return view->GetFrameView()->ConvertFromLayoutObject(*GetLayoutBox(), point);
386 PaintLayerScrollableArea::ConvertFromContainingEmbeddedContentViewToScrollbar(
387 const Scrollbar& scrollbar,
388 const IntPoint& parent_point) const {
389 LayoutView* view = GetLayoutBox()->View();
393 IntPoint point = view->GetFrameView()->ConvertToLayoutObject(*GetLayoutBox(),
396 point.Move(-ScrollbarOffset(scrollbar));
400 IntPoint PaintLayerScrollableArea::ConvertFromRootFrame(
401 const IntPoint& point_in_root_frame) const {
402 LayoutView* view = GetLayoutBox()->View();
404 return point_in_root_frame;
406 return view->GetFrameView()->ConvertFromRootFrame(point_in_root_frame);
409 int PaintLayerScrollableArea::ScrollSize(
410 ScrollbarOrientation orientation) const {
411 IntSize scroll_dimensions =
412 MaximumScrollOffsetInt() - MinimumScrollOffsetInt();
413 return (orientation == kHorizontalScrollbar) ? scroll_dimensions.Width()
414 : scroll_dimensions.Height();
417 void PaintLayerScrollableArea::UpdateScrollOffset(
418 const ScrollOffset& new_offset,
419 ScrollType scroll_type) {
420 if (HasBeenDisposed() || GetScrollOffset() == new_offset)
423 scroll_offset_ = new_offset;
425 LocalFrame* frame = GetLayoutBox()->GetFrame();
428 LocalFrameView* frame_view = GetLayoutBox()->GetFrameView();
429 bool is_root_layer = Layer()->IsRootLayer();
431 TRACE_EVENT1("devtools.timeline", "ScrollLayer", "data",
432 inspector_scroll_layer_event::Data(GetLayoutBox()));
434 // FIXME(420741): Resolve circular dependency between scroll offset and
435 // compositing state, and remove this disabler.
436 DisableCompositingQueryAsserts disabler;
438 // Update the positions of our child layers (if needed as only fixed layers
439 // should be impacted by a scroll).
440 if (!frame_view->IsInPerformLayout()) {
441 // If we're in the middle of layout, we'll just update layers once layout
443 Layer()->UpdateLayerPositionsAfterOverflowScroll();
444 // Update regions, scrolling may change the clip of a particular region.
445 frame_view->UpdateDocumentAnnotatedRegions();
447 // As a performance optimization, the scroll offset of the root layer is
448 // not included in EmbeddedContentView's stored frame rect, so there is no
449 // reason to mark the FrameView as needing a geometry update here.
451 frame_view->SetRootLayerDidScroll();
453 frame_view->SetNeedsUpdateGeometries();
455 UpdateCompositingLayersAfterScroll();
457 GetLayoutBox()->MayUpdateHoverWhenContentUnderMouseChanged(
458 frame->GetEventHandler());
460 if (scroll_type == kUserScroll || scroll_type == kCompositorScroll) {
461 Page* page = frame->GetPage();
463 page->GetChromeClient().ClearToolTip(*frame);
466 InvalidatePaintForScrollOffsetChange();
468 // The scrollOffsetTranslation paint property depends on the scroll offset.
469 // (see: PaintPropertyTreeBuilder::UpdateScrollAndScrollTranslation).
470 GetLayoutBox()->SetNeedsPaintPropertyUpdate();
472 // Schedule the scroll DOM event.
473 if (GetLayoutBox()->GetNode()) {
474 GetLayoutBox()->GetNode()->GetDocument().EnqueueScrollEventForNode(
475 GetLayoutBox()->GetNode());
478 GetLayoutBox()->View()->ClearHitTestCache();
480 // Inform the FrameLoader of the new scroll position, so it can be restored
481 // when navigating back.
483 frame_view->GetFrame().Loader().SaveScrollState();
484 frame_view->DidChangeScrollOffset();
485 if (scroll_type == kCompositorScroll || scroll_type == kUserScroll) {
486 if (DocumentLoader* document_loader = frame->Loader().GetDocumentLoader())
487 document_loader->GetInitialScrollState().was_scrolled_by_user = true;
491 if (FragmentAnchor* anchor = frame_view->GetFragmentAnchor())
492 anchor->DidScroll(scroll_type);
494 if (IsExplicitScrollType(scroll_type)) {
495 if (scroll_type != kCompositorScroll)
496 ShowOverlayScrollbars();
497 GetScrollAnchor()->Clear();
500 if (AXObjectCache* cache =
501 GetLayoutBox()->GetDocument().ExistingAXObjectCache())
502 cache->HandleScrollPositionChanged(GetLayoutBox());
505 void PaintLayerScrollableArea::InvalidatePaintForScrollOffsetChange() {
506 InvalidatePaintForStickyDescendants();
508 auto* box = GetLayoutBox();
509 auto* frame_view = box->GetFrameView();
510 frame_view->InvalidateBackgroundAttachmentFixedDescendantsOnScroll(*box);
512 if (box->IsLayoutView() && frame_view->HasViewportConstrainedObjects() &&
513 !frame_view->InvalidateViewportConstrainedObjects()) {
514 box->SetShouldDoFullPaintInvalidation();
515 box->SetSubtreeShouldCheckForPaintInvalidation();
518 // TODO(chrishtr): remove this slow path once crbug.com/906885 is fixed.
519 // See also https://bugs.chromium.org/p/chromium/issues/detail?id=903287#c10.
520 if (Layer()->EnclosingPaginationLayer())
521 box->SetSubtreeShouldCheckForPaintInvalidation();
523 // If not composited, background always paints into the main graphics layer.
524 bool background_paint_in_graphics_layer = true;
525 bool background_paint_in_scrolling_contents = false;
526 if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled() ||
527 UsesCompositedScrolling()) {
528 auto background_paint_location = box->GetBackgroundPaintLocation();
529 background_paint_in_graphics_layer =
530 background_paint_location & kBackgroundPaintInGraphicsLayer;
531 background_paint_in_scrolling_contents =
532 background_paint_location & kBackgroundPaintInScrollingContents;
535 // Both local attachment background painted in graphics layer and normal
536 // attachment background painted in scrolling contents require paint
537 // invalidation. Fixed attachment background has been dealt with in
538 // frame_view->InvalidateBackgroundAttachmentFixedDescendantsOnScroll().
539 auto background_layers = box->StyleRef().BackgroundLayers();
540 if ((background_layers.AnyLayerHasLocalAttachmentImage() &&
541 background_paint_in_graphics_layer) ||
542 (background_layers.AnyLayerHasDefaultAttachmentImage() &&
543 background_paint_in_scrolling_contents))
544 box->SetBackgroundNeedsFullPaintInvalidation();
546 // If any scrolling content might have been clipped by a cull rect, then
547 // that cull rect could be affected by scroll offset. For composited
548 // scrollers, this will be taken care of by the interest rect computation
549 // in CompositedLayerMapping.
550 // TODO(wangxianzhu): replace this shortcut with interest rects.
551 if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled() ||
552 !UsesCompositedScrolling())
553 Layer()->SetNeedsRepaint();
556 IntSize PaintLayerScrollableArea::ScrollOffsetInt() const {
557 return FlooredIntSize(scroll_offset_);
560 ScrollOffset PaintLayerScrollableArea::GetScrollOffset() const {
561 return scroll_offset_;
564 IntSize PaintLayerScrollableArea::MinimumScrollOffsetInt() const {
565 return ToIntSize(-ScrollOrigin());
568 IntSize PaintLayerScrollableArea::MaximumScrollOffsetInt() const {
569 if (!GetLayoutBox() || !GetLayoutBox()->HasOverflowClip())
570 return ToIntSize(-ScrollOrigin());
572 IntSize content_size = ContentsSize();
574 Page* page = GetLayoutBox()->GetDocument().GetPage();
576 TopDocumentRootScrollerController& controller =
577 page->GlobalRootScrollerController();
579 // The global root scroller should be clipped by the top LocalFrameView rather
580 // than it's overflow clipping box. This is to ensure that content exposed by
581 // hiding the URL bar at the bottom of the screen is visible.
582 IntSize visible_size;
583 if (this == controller.RootScrollerArea()) {
584 visible_size = controller.RootScrollerVisibleArea();
587 PixelSnappedIntRect(GetLayoutBox()->OverflowClipRect(
588 GetLayoutBox()->Location(),
589 kIgnorePlatformAndCSSOverlayScrollbarSize))
593 // TODO(skobes): We should really ASSERT that contentSize >= visibleSize
594 // when we are not the root layer, but we can't because contentSize is
595 // based on stale layout overflow data (http://crbug.com/576933).
596 content_size = content_size.ExpandedTo(visible_size);
598 return ToIntSize(-ScrollOrigin() + (content_size - visible_size));
601 void PaintLayerScrollableArea::VisibleSizeChanged() {
602 ShowOverlayScrollbars();
605 LayoutRect PaintLayerScrollableArea::LayoutContentRect(
606 IncludeScrollbarsInRect scrollbar_inclusion) const {
607 // LayoutContentRect is conceptually the same as the box's client rect.
608 LayoutSize layer_size(Layer()->Size());
609 LayoutUnit border_width = GetLayoutBox()->BorderWidth();
610 LayoutUnit border_height = GetLayoutBox()->BorderHeight();
611 LayoutUnit horizontal_scrollbar_height, vertical_scrollbar_width;
612 if (scrollbar_inclusion == kExcludeScrollbars) {
613 horizontal_scrollbar_height = LayoutUnit(
614 HorizontalScrollbar() && !HorizontalScrollbar()->IsOverlayScrollbar()
615 ? HorizontalScrollbar()->ScrollbarThickness()
617 vertical_scrollbar_width = LayoutUnit(
618 VerticalScrollbar() && !VerticalScrollbar()->IsOverlayScrollbar()
619 ? VerticalScrollbar()->ScrollbarThickness()
624 LayoutPoint(ScrollPosition()),
626 layer_size.Width() - border_width - vertical_scrollbar_width,
627 layer_size.Height() - border_height - horizontal_scrollbar_height)
628 .ExpandedTo(LayoutSize()));
631 IntRect PaintLayerScrollableArea::VisibleContentRect(
632 IncludeScrollbarsInRect scrollbar_inclusion) const {
633 LayoutRect layout_content_rect(LayoutContentRect(scrollbar_inclusion));
634 // TODO(szager): It's not clear that Floor() is the right thing to do here;
635 // what is the correct behavior for fractional scroll offsets?
636 return IntRect(FlooredIntPoint(layout_content_rect.Location()),
637 PixelSnappedIntSize(layout_content_rect.Size(),
638 GetLayoutBox()->Location()));
641 LayoutRect PaintLayerScrollableArea::VisibleScrollSnapportRect(
642 IncludeScrollbarsInRect scrollbar_inclusion) const {
643 const ComputedStyle* style = GetLayoutBox()->Style();
644 LayoutRect layout_content_rect(LayoutContentRect(scrollbar_inclusion));
645 layout_content_rect.MoveBy(LayoutPoint(-ScrollOrigin()));
646 LayoutRectOutsets padding(MinimumValueForLength(style->ScrollPaddingTop(),
647 layout_content_rect.Height()),
648 MinimumValueForLength(style->ScrollPaddingRight(),
649 layout_content_rect.Width()),
650 MinimumValueForLength(style->ScrollPaddingBottom(),
651 layout_content_rect.Height()),
652 MinimumValueForLength(style->ScrollPaddingLeft(),
653 layout_content_rect.Width()));
654 layout_content_rect.Contract(padding);
655 return layout_content_rect;
658 IntSize PaintLayerScrollableArea::ContentsSize() const {
660 GetLayoutBox()->ClientLeft() + GetLayoutBox()->Location().X(),
661 GetLayoutBox()->ClientTop() + GetLayoutBox()->Location().Y());
662 return PixelSnappedContentsSize(offset);
665 IntSize PaintLayerScrollableArea::PixelSnappedContentsSize(
666 const LayoutPoint& paint_offset) const {
667 return PixelSnappedIntSize(overflow_rect_.Size(), paint_offset);
670 void PaintLayerScrollableArea::ContentsResized() {
671 ScrollableArea::ContentsResized();
672 // Need to update the bounds of the scroll property.
673 GetLayoutBox()->SetNeedsPaintPropertyUpdate();
674 Layer()->SetNeedsCompositingInputsUpdate();
677 IntPoint PaintLayerScrollableArea::LastKnownMousePosition() const {
678 return GetLayoutBox()->GetFrame() ? GetLayoutBox()
681 .LastKnownMousePositionInRootFrame()
685 bool PaintLayerScrollableArea::ScrollAnimatorEnabled() const {
686 if (HasBeenDisposed())
688 if (Settings* settings = GetLayoutBox()->GetFrame()->GetSettings())
689 return settings->GetScrollAnimatorEnabled();
693 bool PaintLayerScrollableArea::ShouldSuspendScrollAnimations() const {
694 if (HasBeenDisposed())
696 LayoutView* view = GetLayoutBox()->View();
699 return !GetLayoutBox()->GetDocument().LoadEventFinished();
702 void PaintLayerScrollableArea::ScrollbarVisibilityChanged() {
703 UpdateScrollbarEnabledState();
705 // Paint properties need to be updated, because clip rects
706 // are affected by overlay scrollbars.
707 layer_->GetLayoutObject().SetNeedsPaintPropertyUpdate();
709 // TODO(chrishr): this should be able to be removed.
710 layer_->ClearClipRects();
712 if (LayoutView* view = GetLayoutBox()->View())
713 view->ClearHitTestCache();
716 void PaintLayerScrollableArea::ScrollbarFrameRectChanged() {
717 // Size of non-overlay scrollbar affects overflow clip rect.
718 if (!HasOverlayScrollbars())
719 GetLayoutBox()->SetNeedsPaintPropertyUpdate();
722 bool PaintLayerScrollableArea::ScrollbarsCanBeActive() const {
723 LayoutView* view = GetLayoutBox()->View();
727 // TODO(szager): This conditional is weird and likely obsolete. Originally
728 // added in commit eb0d49caaee2b275ff524d3945a74e8d9180eb7d.
729 LocalFrameView* frame_view = view->GetFrameView();
730 if (frame_view != frame_view->GetFrame().View())
733 return !!frame_view->GetFrame().GetDocument();
736 IntRect PaintLayerScrollableArea::ScrollableAreaBoundingBox() const {
737 if (LocalFrame* frame = GetLayoutBox()->GetFrame()) {
738 if (LocalFrameView* local_root = frame->LocalFrameRoot().View()) {
739 return local_root->RootFrameToDocument(frame->View()->ConvertToRootFrame(
740 GetLayoutBox()->AbsoluteBoundingBoxRect(0)));
746 void PaintLayerScrollableArea::RegisterForAnimation() {
747 if (HasBeenDisposed())
749 if (LocalFrame* frame = GetLayoutBox()->GetFrame()) {
750 if (LocalFrameView* frame_view = frame->View())
751 frame_view->AddAnimatingScrollableArea(this);
755 void PaintLayerScrollableArea::DeregisterForAnimation() {
756 if (HasBeenDisposed())
758 if (LocalFrame* frame = GetLayoutBox()->GetFrame()) {
759 if (LocalFrameView* frame_view = frame->View())
760 frame_view->RemoveAnimatingScrollableArea(this);
764 bool PaintLayerScrollableArea::UserInputScrollable(
765 ScrollbarOrientation orientation) const {
766 if (orientation == kVerticalScrollbar &&
767 GetLayoutBox()->GetDocument().IsVerticalScrollEnforced()) {
771 if (GetLayoutBox()->IsIntrinsicallyScrollable(orientation))
774 if (GetLayoutBox()->IsLayoutView()) {
775 Document& document = GetLayoutBox()->GetDocument();
776 Element* fullscreen_element = Fullscreen::FullscreenElementFrom(document);
777 if (fullscreen_element && fullscreen_element != document.documentElement())
780 ScrollbarMode h_mode;
781 ScrollbarMode v_mode;
782 ToLayoutView(GetLayoutBox())->CalculateScrollbarModes(h_mode, v_mode);
784 (orientation == kHorizontalScrollbar) ? h_mode : v_mode;
785 return mode == kScrollbarAuto || mode == kScrollbarAlwaysOn;
788 EOverflow overflow_style = (orientation == kHorizontalScrollbar)
789 ? GetLayoutBox()->StyleRef().OverflowX()
790 : GetLayoutBox()->StyleRef().OverflowY();
791 return (overflow_style == EOverflow::kScroll ||
792 overflow_style == EOverflow::kAuto ||
793 overflow_style == EOverflow::kOverlay);
796 bool PaintLayerScrollableArea::ShouldPlaceVerticalScrollbarOnLeft() const {
797 return GetLayoutBox()->ShouldPlaceBlockDirectionScrollbarOnLogicalLeft();
800 int PaintLayerScrollableArea::PageStep(ScrollbarOrientation orientation) const {
801 // Paging scroll operations should take scroll-padding into account [1]. So we
802 // use the snapport rect to calculate the page step instead of the visible
804 // [1] https://drafts.csswg.org/css-scroll-snap/#scroll-padding
805 IntSize snapport_size = VisibleScrollSnapportRect().PixelSnappedSize();
806 int length = (orientation == kHorizontalScrollbar) ? snapport_size.Width()
807 : snapport_size.Height();
808 int min_page_step = static_cast<float>(length) *
809 ScrollableArea::MinFractionToStepWhenPaging();
810 int page_step = max(min_page_step, length - MaxOverlapBetweenPages());
811 return max(page_step, 1);
814 LayoutBox* PaintLayerScrollableArea::GetLayoutBox() const {
815 return layer_ ? layer_->GetLayoutBox() : nullptr;
818 PaintLayer* PaintLayerScrollableArea::Layer() const {
822 LayoutUnit PaintLayerScrollableArea::ScrollWidth() const {
823 return overflow_rect_.Width();
826 LayoutUnit PaintLayerScrollableArea::ScrollHeight() const {
827 return overflow_rect_.Height();
830 void PaintLayerScrollableArea::UpdateScrollOrigin() {
831 // This should do nothing prior to first layout; the if-clause will catch
833 if (OverflowRect().IsEmpty())
835 LayoutRect scrollable_overflow(overflow_rect_);
836 scrollable_overflow.Move(-GetLayoutBox()->BorderLeft(),
837 -GetLayoutBox()->BorderTop());
838 IntPoint new_origin(FlooredIntPoint(-scrollable_overflow.Location()) +
839 GetLayoutBox()->OriginAdjustmentForScrollbars());
840 if (new_origin != scroll_origin_)
841 scroll_origin_changed_ = true;
842 scroll_origin_ = new_origin;
845 void PaintLayerScrollableArea::UpdateScrollDimensions() {
846 LayoutRect new_overflow_rect = GetLayoutBox()->LayoutOverflowRect();
847 GetLayoutBox()->FlipForWritingMode(new_overflow_rect);
849 // The layout viewport can be larger than the document's layout overflow when
850 // top controls are hidden. Expand the overflow here to ensure that our
851 // contents size >= visible size.
852 new_overflow_rect.Unite(
853 LayoutRect(new_overflow_rect.Location(),
854 LayoutContentRect(kExcludeScrollbars).Size()));
856 if (overflow_rect_.Size() != new_overflow_rect.Size())
858 overflow_rect_ = new_overflow_rect;
859 UpdateScrollOrigin();
862 void PaintLayerScrollableArea::UpdateScrollbarEnabledState() {
864 GetPageScrollbarTheme().ShouldDisableInvisibleScrollbars() &&
865 ScrollbarsHiddenIfOverlay();
867 if (HorizontalScrollbar())
868 HorizontalScrollbar()->SetEnabled(HasHorizontalOverflow() &&
870 if (VerticalScrollbar())
871 VerticalScrollbar()->SetEnabled(HasVerticalOverflow() && !force_disable);
874 void PaintLayerScrollableArea::UpdateScrollbarProportions() {
875 if (Scrollbar* horizontal_scrollbar = HorizontalScrollbar())
876 horizontal_scrollbar->SetProportion(VisibleWidth(), ContentsSize().Width());
877 if (Scrollbar* vertical_scrollbar = VerticalScrollbar())
878 vertical_scrollbar->SetProportion(VisibleHeight(), ContentsSize().Height());
881 void PaintLayerScrollableArea::SetScrollOffsetUnconditionally(
882 const ScrollOffset& offset,
883 ScrollType scroll_type) {
884 CancelScrollAnimation();
885 ScrollOffsetChanged(offset, scroll_type);
888 void PaintLayerScrollableArea::UpdateAfterLayout() {
889 bool scrollbars_are_frozen =
890 (in_overflow_relayout_ && !allow_second_overflow_relayout_) ||
891 FreezeScrollbarsScope::ScrollbarsAreFrozen();
892 allow_second_overflow_relayout_ = false;
894 if (NeedsScrollbarReconstruction()) {
895 SetHasHorizontalScrollbar(false);
896 SetHasVerticalScrollbar(false);
899 UpdateScrollDimensions();
901 bool had_horizontal_scrollbar = HasHorizontalScrollbar();
902 bool had_vertical_scrollbar = HasVerticalScrollbar();
904 bool needs_horizontal_scrollbar;
905 bool needs_vertical_scrollbar;
906 ComputeScrollbarExistence(needs_horizontal_scrollbar,
907 needs_vertical_scrollbar);
909 // Removing auto scrollbars is a heuristic and can be incorrect if the content
910 // size depends on the scrollbar size (e.g., sized with percentages). Removing
911 // scrollbars can require two additional layout passes so this is only done on
912 // the first layout (!in_overflow_layout).
913 if (!in_overflow_relayout_ && !scrollbars_are_frozen &&
914 TryRemovingAutoScrollbars(needs_horizontal_scrollbar,
915 needs_vertical_scrollbar)) {
916 needs_horizontal_scrollbar = needs_vertical_scrollbar = false;
917 allow_second_overflow_relayout_ = true;
920 bool horizontal_scrollbar_should_change =
921 needs_horizontal_scrollbar != had_horizontal_scrollbar;
922 bool vertical_scrollbar_should_change =
923 needs_vertical_scrollbar != had_vertical_scrollbar;
925 bool scrollbars_will_change =
926 !scrollbars_are_frozen &&
927 (horizontal_scrollbar_should_change || vertical_scrollbar_should_change);
928 if (scrollbars_will_change) {
929 SetHasHorizontalScrollbar(needs_horizontal_scrollbar);
930 SetHasVerticalScrollbar(needs_vertical_scrollbar);
932 // If we change scrollbars on the layout viewport, the visual viewport
933 // needs to update paint properties to account for the correct
935 if (LocalFrameView* frame_view = GetLayoutBox()->GetFrameView()) {
936 if (this == frame_view->LayoutViewport()) {
940 ->GetVisualViewport()
941 .SetNeedsPaintPropertyUpdate();
946 UpdateScrollCornerStyle();
948 Layer()->UpdateSelfPaintingLayer();
950 // Force an update since we know the scrollbars have changed things.
951 if (GetLayoutBox()->GetDocument().HasAnnotatedRegions())
952 GetLayoutBox()->GetDocument().SetAnnotatedRegionsDirty(true);
954 // Our proprietary overflow: overlay value doesn't trigger a layout.
955 if (((horizontal_scrollbar_should_change &&
956 GetLayoutBox()->StyleRef().OverflowX() != EOverflow::kOverlay) ||
957 (vertical_scrollbar_should_change &&
958 GetLayoutBox()->StyleRef().OverflowY() != EOverflow::kOverlay))) {
959 if ((vertical_scrollbar_should_change &&
960 GetLayoutBox()->IsHorizontalWritingMode()) ||
961 (horizontal_scrollbar_should_change &&
962 !GetLayoutBox()->IsHorizontalWritingMode())) {
963 GetLayoutBox()->SetPreferredLogicalWidthsDirty();
965 if (IsManagedByLayoutNG(*GetLayoutBox())) {
966 // If the box is managed by LayoutNG, don't go here. We don't want to
967 // re-enter the NG layout algorithm for this box from here. Just update
968 // the rectangles, in case scrollbars were added or removed. LayoutNG
969 // has its own scrollbar change detection mechanism.
970 UpdateScrollDimensions();
972 if (PreventRelayoutScope::RelayoutIsPrevented()) {
973 // We're not doing re-layout right now, but we still want to
974 // add the scrollbar to the logical width now, to facilitate parent
976 GetLayoutBox()->UpdateLogicalWidth();
977 PreventRelayoutScope::SetBoxNeedsLayout(
978 *this, had_horizontal_scrollbar, had_vertical_scrollbar);
980 in_overflow_relayout_ = true;
981 SubtreeLayoutScope layout_scope(*GetLayoutBox());
982 layout_scope.SetNeedsLayout(
983 GetLayoutBox(), layout_invalidation_reason::kScrollbarChanged);
984 if (GetLayoutBox()->IsLayoutBlock()) {
985 LayoutBlock* block = ToLayoutBlock(GetLayoutBox());
986 block->ScrollbarsChanged(horizontal_scrollbar_should_change,
987 vertical_scrollbar_should_change);
988 block->UpdateBlockLayout(true);
990 GetLayoutBox()->UpdateLayout();
992 in_overflow_relayout_ = false;
993 scrollbar_manager_.DestroyDetachedScrollbars();
995 LayoutObject* parent = GetLayoutBox()->Parent();
996 if (parent && parent->IsFlexibleBox()) {
997 ToLayoutFlexibleBox(parent)->ClearCachedMainSizeForChild(
1006 // compositing/overflow/automatically-opt-into-composited-scrolling-after-style-change.html.
1007 DisableCompositingQueryAsserts disabler;
1009 UpdateScrollbarEnabledState();
1011 UpdateScrollbarProportions();
1014 if (!scrollbars_are_frozen && HasOverlayScrollbars()) {
1015 if (!ScrollSize(kHorizontalScrollbar))
1016 SetHasHorizontalScrollbar(false);
1017 if (!ScrollSize(kVerticalScrollbar))
1018 SetHasVerticalScrollbar(false);
1021 ClampScrollOffsetAfterOverflowChange();
1023 if (!scrollbars_are_frozen) {
1024 UpdateScrollableAreaSet();
1027 DisableCompositingQueryAsserts disabler;
1028 PositionOverflowControls();
1031 void PaintLayerScrollableArea::ClampScrollOffsetAfterOverflowChange() {
1032 if (HasBeenDisposed())
1035 // If a vertical scrollbar was removed, the min/max scroll offsets may have
1036 // changed, so the scroll offsets needs to be clamped. If the scroll offset
1037 // did not change, but the scroll origin *did* change, we still need to notify
1038 // the scrollbars to update their dimensions.
1040 if (DelayScrollOffsetClampScope::ClampingIsDelayed()) {
1041 DelayScrollOffsetClampScope::SetNeedsClamp(this);
1045 UpdateScrollDimensions();
1046 if (ScrollOriginChanged())
1047 SetScrollOffsetUnconditionally(ClampScrollOffset(GetScrollOffset()));
1049 ScrollableArea::SetScrollOffset(GetScrollOffset(), kClampingScroll);
1051 SetNeedsScrollOffsetClamp(false);
1052 ResetScrollOriginChanged();
1053 scrollbar_manager_.DestroyDetachedScrollbars();
1056 void PaintLayerScrollableArea::DidChangeGlobalRootScroller() {
1057 // Being the global root scroller will affect clipping size due to browser
1058 // controls behavior so we need to update compositing based on updated clip
1060 if (GetLayoutBox()->GetNode()->IsElementNode()) {
1061 ToElement(GetLayoutBox()->GetNode())->SetNeedsCompositingUpdate();
1062 GetLayoutBox()->SetNeedsPaintPropertyUpdate();
1065 // On Android, where the VisualViewport supplies scrollbars, we need to
1066 // remove the PLSA's scrollbars if we become the global root scroller.
1067 // In general, this would be problematic as that can cause layout but this
1068 // should only ever apply with overlay scrollbars.
1069 if (GetLayoutBox()->GetFrame()->GetSettings() &&
1070 GetLayoutBox()->GetFrame()->GetSettings()->GetViewportEnabled()) {
1071 bool needs_horizontal_scrollbar;
1072 bool needs_vertical_scrollbar;
1073 ComputeScrollbarExistence(needs_horizontal_scrollbar,
1074 needs_vertical_scrollbar);
1075 SetHasHorizontalScrollbar(needs_horizontal_scrollbar);
1076 SetHasVerticalScrollbar(needs_vertical_scrollbar);
1080 bool PaintLayerScrollableArea::ShouldPerformScrollAnchoring() const {
1081 return scroll_anchor_.HasScroller() && GetLayoutBox() &&
1082 GetLayoutBox()->StyleRef().OverflowAnchor() !=
1083 EOverflowAnchor::kNone &&
1084 !GetLayoutBox()->GetDocument().FinishingOrIsPrinting();
1087 bool PaintLayerScrollableArea::RestoreScrollAnchor(
1088 const SerializedAnchor& serialized_anchor) {
1089 return ShouldPerformScrollAnchoring() &&
1090 scroll_anchor_.RestoreAnchor(serialized_anchor);
1093 FloatQuad PaintLayerScrollableArea::LocalToVisibleContentQuad(
1094 const FloatQuad& quad,
1095 const LayoutObject* local_object,
1096 MapCoordinatesFlags flags) const {
1097 LayoutBox* box = GetLayoutBox();
1100 DCHECK(local_object);
1101 return local_object->LocalToAncestorQuad(quad, box, flags);
1104 scoped_refptr<base::SingleThreadTaskRunner>
1105 PaintLayerScrollableArea::GetTimerTaskRunner() const {
1106 return GetLayoutBox()->GetFrame()->GetTaskRunner(TaskType::kInternalDefault);
1109 ScrollBehavior PaintLayerScrollableArea::ScrollBehaviorStyle() const {
1110 return GetLayoutBox()->StyleRef().GetScrollBehavior();
1113 bool PaintLayerScrollableArea::HasHorizontalOverflow() const {
1114 // TODO(szager): Make the algorithm for adding/subtracting overflow:auto
1115 // scrollbars memoryless (crbug.com/625300). This client_width hack will
1116 // prevent the spurious horizontal scrollbar, but it can cause a converse
1117 // problem: it can leave a sliver of horizontal overflow hidden behind the
1118 // vertical scrollbar without creating a horizontal scrollbar. This
1119 // converse problem seems to happen much less frequently in practice, so we
1120 // bias the logic towards preventing unwanted horizontal scrollbars, which
1121 // are more common and annoying.
1122 LayoutUnit client_width =
1123 LayoutContentRect(kIncludeScrollbars).Width() -
1124 VerticalScrollbarWidth(kIgnorePlatformAndCSSOverlayScrollbarSize);
1125 if (NeedsRelayout() && !HadVerticalScrollbarBeforeRelayout())
1126 client_width += VerticalScrollbarWidth();
1127 LayoutUnit scroll_width(ScrollWidth());
1128 LayoutUnit box_x = GetLayoutBox()->Location().X();
1129 return SnapSizeToPixel(scroll_width, box_x) >
1130 SnapSizeToPixel(client_width, box_x);
1133 bool PaintLayerScrollableArea::HasVerticalOverflow() const {
1134 LayoutUnit client_height =
1135 LayoutContentRect(kIncludeScrollbars).Height() -
1136 HorizontalScrollbarHeight(kIgnorePlatformAndCSSOverlayScrollbarSize);
1137 LayoutUnit scroll_height(ScrollHeight());
1138 LayoutUnit box_y = GetLayoutBox()->Location().Y();
1139 return SnapSizeToPixel(scroll_height, box_y) >
1140 SnapSizeToPixel(client_height, box_y);
1143 // This function returns true if the given box requires overflow scrollbars (as
1144 // opposed to the 'viewport' scrollbars managed by the PaintLayerCompositor).
1145 // FIXME: we should use the same scrolling machinery for both the viewport and
1146 // overflow. Currently, we need to avoid producing scrollbars here if they'll be
1147 // handled externally in the RLC.
1148 static bool CanHaveOverflowScrollbars(const LayoutBox& box) {
1149 return box.GetDocument().ViewportDefiningElement() != box.GetNode();
1152 void PaintLayerScrollableArea::UpdateAfterStyleChange(
1153 const ComputedStyle* old_style) {
1154 // Don't do this on first style recalc, before layout has ever happened.
1155 if (!OverflowRect().Size().IsZero()) {
1156 UpdateScrollableAreaSet();
1159 // Whenever background changes on the scrollable element, the scroll bar
1160 // overlay style might need to be changed to have contrast against the
1162 // Skip the need scrollbar check, because we dont know do we need a scrollbar
1163 // when this method get called.
1164 Color old_background;
1167 old_style->VisitedDependentColor(GetCSSPropertyBackgroundColor());
1169 Color new_background = GetLayoutBox()->StyleRef().VisitedDependentColor(
1170 GetCSSPropertyBackgroundColor());
1172 if (new_background != old_background) {
1173 RecalculateScrollbarOverlayColorTheme(new_background);
1176 bool needs_horizontal_scrollbar;
1177 bool needs_vertical_scrollbar;
1178 // We add auto scrollbars only during layout to prevent spurious activations.
1179 ComputeScrollbarExistence(needs_horizontal_scrollbar,
1180 needs_vertical_scrollbar, kForbidAddingAutoBars);
1182 // Avoid some unnecessary computation if there were and will be no scrollbars.
1183 if (!HasScrollbar() && !needs_horizontal_scrollbar &&
1184 !needs_vertical_scrollbar)
1187 bool horizontal_scrollbar_changed =
1188 SetHasHorizontalScrollbar(needs_horizontal_scrollbar);
1189 bool vertical_scrollbar_changed =
1190 SetHasVerticalScrollbar(needs_vertical_scrollbar);
1192 if (GetLayoutBox()->IsLayoutBlock() &&
1193 (horizontal_scrollbar_changed || vertical_scrollbar_changed)) {
1194 ToLayoutBlock(GetLayoutBox())
1195 ->ScrollbarsChanged(horizontal_scrollbar_changed,
1196 vertical_scrollbar_changed,
1197 LayoutBlock::ScrollbarChangeContext::kStyleChange);
1200 // With overflow: scroll, scrollbars are always visible but may be disabled.
1201 // When switching to another value, we need to re-enable them (see bug 11985).
1202 if (HasHorizontalScrollbar() && old_style &&
1203 old_style->OverflowX() == EOverflow::kScroll &&
1204 GetLayoutBox()->StyleRef().OverflowX() != EOverflow::kScroll) {
1205 HorizontalScrollbar()->SetEnabled(true);
1208 if (HasVerticalScrollbar() && old_style &&
1209 old_style->OverflowY() == EOverflow::kScroll &&
1210 GetLayoutBox()->StyleRef().OverflowY() != EOverflow::kScroll) {
1211 VerticalScrollbar()->SetEnabled(true);
1214 // FIXME: Need to detect a swap from custom to native scrollbars (and vice
1216 if (HorizontalScrollbar())
1217 HorizontalScrollbar()->StyleChanged();
1218 if (VerticalScrollbar())
1219 VerticalScrollbar()->StyleChanged();
1221 UpdateScrollCornerStyle();
1222 UpdateResizerAreaSet();
1223 UpdateResizerStyle(old_style);
1226 void PaintLayerScrollableArea::UpdateAfterOverflowRecalc() {
1227 UpdateScrollDimensions();
1228 UpdateScrollbarProportions();
1230 bool needs_horizontal_scrollbar;
1231 bool needs_vertical_scrollbar;
1232 ComputeScrollbarExistence(needs_horizontal_scrollbar,
1233 needs_vertical_scrollbar);
1235 bool horizontal_scrollbar_should_change =
1236 needs_horizontal_scrollbar != HasHorizontalScrollbar();
1237 bool vertical_scrollbar_should_change =
1238 needs_vertical_scrollbar != HasVerticalScrollbar();
1240 if ((GetLayoutBox()->HasAutoHorizontalScrollbar() &&
1241 horizontal_scrollbar_should_change) ||
1242 (GetLayoutBox()->HasAutoVerticalScrollbar() &&
1243 vertical_scrollbar_should_change)) {
1244 GetLayoutBox()->SetNeedsLayoutAndFullPaintInvalidation(
1245 layout_invalidation_reason::kUnknown);
1248 ClampScrollOffsetAfterOverflowChange();
1251 IntRect PaintLayerScrollableArea::RectForHorizontalScrollbar(
1252 const IntRect& border_box_rect) const {
1253 if (!HasHorizontalScrollbar())
1256 const IntRect& scroll_corner = ScrollCornerRect();
1259 HorizontalScrollbarStart(border_box_rect.X()),
1260 border_box_rect.MaxY() - GetLayoutBox()->BorderBottom().ToInt() -
1261 HorizontalScrollbar()->ScrollbarThickness(),
1262 border_box_rect.Width() -
1263 (GetLayoutBox()->BorderLeft() + GetLayoutBox()->BorderRight())
1265 scroll_corner.Width(),
1266 HorizontalScrollbar()->ScrollbarThickness());
1269 IntRect PaintLayerScrollableArea::RectForVerticalScrollbar(
1270 const IntRect& border_box_rect) const {
1271 if (!HasVerticalScrollbar())
1274 const IntRect& scroll_corner = ScrollCornerRect();
1277 VerticalScrollbarStart(border_box_rect.X(), border_box_rect.MaxX()),
1278 border_box_rect.Y() + GetLayoutBox()->BorderTop().ToInt(),
1279 VerticalScrollbar()->ScrollbarThickness(),
1280 border_box_rect.Height() -
1281 (GetLayoutBox()->BorderTop() + GetLayoutBox()->BorderBottom())
1283 scroll_corner.Height());
1286 int PaintLayerScrollableArea::VerticalScrollbarStart(int min_x,
1288 if (GetLayoutBox()->ShouldPlaceBlockDirectionScrollbarOnLogicalLeft())
1289 return min_x + GetLayoutBox()->BorderLeft().ToInt();
1290 return max_x - GetLayoutBox()->BorderRight().ToInt() -
1291 VerticalScrollbar()->ScrollbarThickness();
1294 int PaintLayerScrollableArea::HorizontalScrollbarStart(int min_x) const {
1295 int x = min_x + GetLayoutBox()->BorderLeft().ToInt();
1296 if (GetLayoutBox()->ShouldPlaceBlockDirectionScrollbarOnLogicalLeft())
1297 x += HasVerticalScrollbar()
1298 ? VerticalScrollbar()->ScrollbarThickness()
1299 : ResizerCornerRect(GetLayoutBox()->PixelSnappedBorderBoxRect(
1300 Layer()->SubpixelAccumulation()),
1306 IntSize PaintLayerScrollableArea::ScrollbarOffset(
1307 const Scrollbar& scrollbar) const {
1308 // TODO(szager): Factor out vertical offset calculation into other methods,
1309 // for symmetry with *ScrollbarStart methods for horizontal offset.
1310 if (&scrollbar == VerticalScrollbar()) {
1312 VerticalScrollbarStart(0, Layer()->PixelSnappedSize().Width()),
1313 GetLayoutBox()->BorderTop().ToInt());
1316 if (&scrollbar == HorizontalScrollbar()) {
1317 return IntSize(HorizontalScrollbarStart(0),
1318 GetLayoutBox()->BorderTop().ToInt() +
1319 VisibleContentRect(kIncludeScrollbars).Height() -
1320 HorizontalScrollbar()->ScrollbarThickness());
1327 static inline const LayoutObject& ScrollbarStyleSource(
1328 const LayoutBox& layout_box) {
1329 if (layout_box.IsLayoutView()) {
1330 Document& doc = layout_box.GetDocument();
1331 if (Settings* settings = doc.GetSettings()) {
1332 if (!settings->GetAllowCustomScrollbarInMainFrame() &&
1333 layout_box.GetFrame() && layout_box.GetFrame()->IsMainFrame())
1337 // Try the <body> element first as a scrollbar source, but only if the body
1339 Element* body = doc.body();
1340 if (body && body->GetLayoutObject() && body->GetLayoutObject()->IsBox() &&
1341 body->GetLayoutObject()->StyleRef().HasPseudoStyle(kPseudoIdScrollbar))
1342 return *body->GetLayoutObject();
1344 // If the <body> didn't have a custom style, then the root element might.
1345 Element* doc_element = doc.documentElement();
1346 if (doc_element && doc_element->GetLayoutObject() &&
1347 doc_element->GetLayoutObject()->StyleRef().HasPseudoStyle(
1348 kPseudoIdScrollbar))
1349 return *doc_element->GetLayoutObject();
1355 int PaintLayerScrollableArea::HypotheticalScrollbarThickness(
1356 ScrollbarOrientation orientation) const {
1357 Scrollbar* scrollbar = orientation == kHorizontalScrollbar
1358 ? HorizontalScrollbar()
1359 : VerticalScrollbar();
1361 return scrollbar->ScrollbarThickness();
1363 const LayoutObject& style_source = ScrollbarStyleSource(*GetLayoutBox());
1364 bool has_custom_scrollbar_style =
1365 style_source.StyleRef().HasPseudoStyle(kPseudoIdScrollbar);
1366 if (has_custom_scrollbar_style) {
1367 return LayoutScrollbar::HypotheticalScrollbarThickness(
1368 orientation, *GetLayoutBox(), style_source);
1371 ScrollbarControlSize scrollbar_size = kRegularScrollbar;
1372 if (style_source.StyleRef().HasAppearance()) {
1373 scrollbar_size = LayoutTheme::GetTheme().ScrollbarControlSizeForPart(
1374 style_source.StyleRef().Appearance());
1376 ScrollbarTheme& theme = GetPageScrollbarTheme();
1377 if (theme.UsesOverlayScrollbars())
1379 int thickness = theme.ScrollbarThickness(scrollbar_size);
1380 return GetLayoutBox()
1384 .WindowToViewportScalar(thickness);
1387 bool PaintLayerScrollableArea::NeedsScrollbarReconstruction() const {
1388 if (!HasScrollbar())
1391 const LayoutObject& style_source = ScrollbarStyleSource(*GetLayoutBox());
1393 style_source.IsBox() &&
1394 style_source.StyleRef().HasPseudoStyle(kPseudoIdScrollbar);
1396 Scrollbar* scrollbars[] = {HorizontalScrollbar(), VerticalScrollbar()};
1398 for (Scrollbar* scrollbar : scrollbars) {
1402 // We have a native scrollbar that should be custom, or vice versa.
1403 if (scrollbar->IsCustomScrollbar() != needs_custom)
1407 DCHECK(scrollbar->IsCustomScrollbar());
1408 // We have a custom scrollbar with a stale m_owner.
1409 if (ToLayoutScrollbar(scrollbar)->StyleSource()->GetLayoutObject() !=
1414 // Should use custom scrollbar and nothing should change.
1418 // Check if native scrollbar should change.
1419 Page* page = GetLayoutBox()->GetFrame()->LocalFrameRoot().GetPage();
1421 ScrollbarTheme* current_theme = &page->GetScrollbarTheme();
1423 if (current_theme != &scrollbar->GetTheme())
1429 void PaintLayerScrollableArea::ComputeScrollbarExistence(
1430 bool& needs_horizontal_scrollbar,
1431 bool& needs_vertical_scrollbar,
1432 ComputeScrollbarExistenceOption option) const {
1433 // Scrollbars may be hidden or provided by visual viewport or frame instead.
1434 DCHECK(GetLayoutBox()->GetFrame()->GetSettings());
1435 if (VisualViewportSuppliesScrollbars() ||
1436 !CanHaveOverflowScrollbars(*GetLayoutBox()) ||
1437 GetLayoutBox()->GetFrame()->GetSettings()->GetHideScrollbars()) {
1438 needs_horizontal_scrollbar = false;
1439 needs_vertical_scrollbar = false;
1443 needs_horizontal_scrollbar = GetLayoutBox()->ScrollsOverflowX();
1444 needs_vertical_scrollbar = GetLayoutBox()->ScrollsOverflowY();
1446 // Don't add auto scrollbars if the box contents aren't visible.
1447 if (GetLayoutBox()->HasAutoHorizontalScrollbar()) {
1448 if (option == kForbidAddingAutoBars)
1449 needs_horizontal_scrollbar &= HasHorizontalScrollbar();
1450 needs_horizontal_scrollbar &=
1451 GetLayoutBox()->IsRooted() && HasHorizontalOverflow() &&
1452 VisibleContentRect(kIncludeScrollbars).Height();
1455 if (GetLayoutBox()->HasAutoVerticalScrollbar()) {
1456 if (option == kForbidAddingAutoBars)
1457 needs_vertical_scrollbar &= HasVerticalScrollbar();
1458 needs_vertical_scrollbar &= GetLayoutBox()->IsRooted() &&
1459 HasVerticalOverflow() &&
1460 VisibleContentRect(kIncludeScrollbars).Width();
1463 if (GetLayoutBox()->IsLayoutView()) {
1464 ScrollbarMode h_mode;
1465 ScrollbarMode v_mode;
1466 ToLayoutView(GetLayoutBox())->CalculateScrollbarModes(h_mode, v_mode);
1468 // Look for the scrollbarModes and reset the needs Horizontal & vertical
1469 // Scrollbar values based on scrollbarModes, as during force style change
1470 // StyleResolver::styleForDocument returns documentStyle with no overflow
1471 // values, due to which we are destroying the scrollbars that were already
1473 if (h_mode == kScrollbarAlwaysOn)
1474 needs_horizontal_scrollbar = true;
1475 else if (h_mode == kScrollbarAlwaysOff)
1476 needs_horizontal_scrollbar = false;
1477 if (v_mode == kScrollbarAlwaysOn)
1478 needs_vertical_scrollbar = true;
1479 else if (v_mode == kScrollbarAlwaysOff)
1480 needs_vertical_scrollbar = false;
1484 bool PaintLayerScrollableArea::TryRemovingAutoScrollbars(
1485 const bool& needs_horizontal_scrollbar,
1486 const bool& needs_vertical_scrollbar) {
1487 // If scrollbars are removed but the content size depends on the scrollbars,
1488 // additional layouts will be required to size the content. Therefore, only
1489 // remove auto scrollbars for the initial layout pass.
1490 DCHECK(!in_overflow_relayout_);
1492 if (!needs_horizontal_scrollbar && !needs_vertical_scrollbar)
1495 if (GetLayoutBox()->IsLayoutView()) {
1496 ScrollbarMode h_mode;
1497 ScrollbarMode v_mode;
1498 ToLayoutView(GetLayoutBox())->CalculateScrollbarModes(h_mode, v_mode);
1499 if (h_mode != kScrollbarAuto || v_mode != kScrollbarAuto)
1502 IntSize visible_size_with_scrollbars =
1503 VisibleContentRect(kIncludeScrollbars).Size();
1504 if (ScrollWidth() <= visible_size_with_scrollbars.Width() &&
1505 ScrollHeight() <= visible_size_with_scrollbars.Height()) {
1509 if (!GetLayoutBox()->HasAutoVerticalScrollbar() ||
1510 !GetLayoutBox()->HasAutoHorizontalScrollbar())
1513 LayoutSize client_size_with_scrollbars =
1514 LayoutContentRect(kIncludeScrollbars).Size();
1515 if (ScrollWidth() <= client_size_with_scrollbars.Width() &&
1516 ScrollHeight() <= client_size_with_scrollbars.Height()) {
1524 bool PaintLayerScrollableArea::SetHasHorizontalScrollbar(bool has_scrollbar) {
1525 if (FreezeScrollbarsScope::ScrollbarsAreFrozen())
1528 if (has_scrollbar == HasHorizontalScrollbar())
1531 SetScrollbarNeedsPaintInvalidation(kHorizontalScrollbar);
1533 scrollbar_manager_.SetHasHorizontalScrollbar(has_scrollbar);
1535 UpdateScrollOrigin();
1537 // Destroying or creating one bar can cause our scrollbar corner to come and
1538 // go. We need to update the opposite scrollbar's style.
1539 if (HasHorizontalScrollbar())
1540 HorizontalScrollbar()->StyleChanged();
1541 if (HasVerticalScrollbar())
1542 VerticalScrollbar()->StyleChanged();
1544 SetScrollCornerNeedsPaintInvalidation();
1546 // Force an update since we know the scrollbars have changed things.
1547 if (GetLayoutBox()->GetDocument().HasAnnotatedRegions())
1548 GetLayoutBox()->GetDocument().SetAnnotatedRegionsDirty(true);
1552 bool PaintLayerScrollableArea::SetHasVerticalScrollbar(bool has_scrollbar) {
1553 if (FreezeScrollbarsScope::ScrollbarsAreFrozen())
1556 if (GetLayoutBox()->GetDocument().IsVerticalScrollEnforced()) {
1557 // When the policy is enforced the contents of document cannot be scrolled.
1558 // This would make rendering a scrollbar look strange
1559 // (https://crbug.com/898151).
1563 if (has_scrollbar == HasVerticalScrollbar())
1566 SetScrollbarNeedsPaintInvalidation(kVerticalScrollbar);
1568 scrollbar_manager_.SetHasVerticalScrollbar(has_scrollbar);
1570 UpdateScrollOrigin();
1572 // Destroying or creating one bar can cause our scrollbar corner to come and
1573 // go. We need to update the opposite scrollbar's style.
1574 if (HasHorizontalScrollbar())
1575 HorizontalScrollbar()->StyleChanged();
1576 if (HasVerticalScrollbar())
1577 VerticalScrollbar()->StyleChanged();
1579 SetScrollCornerNeedsPaintInvalidation();
1581 // Force an update since we know the scrollbars have changed things.
1582 if (GetLayoutBox()->GetDocument().HasAnnotatedRegions())
1583 GetLayoutBox()->GetDocument().SetAnnotatedRegionsDirty(true);
1587 int PaintLayerScrollableArea::VerticalScrollbarWidth(
1588 OverlayScrollbarClipBehavior overlay_scrollbar_clip_behavior) const {
1589 if (!HasVerticalScrollbar())
1591 if (overlay_scrollbar_clip_behavior ==
1592 kIgnorePlatformAndCSSOverlayScrollbarSize &&
1593 GetLayoutBox()->StyleRef().OverflowY() == EOverflow::kOverlay) {
1596 if ((overlay_scrollbar_clip_behavior == kIgnorePlatformOverlayScrollbarSize ||
1597 overlay_scrollbar_clip_behavior ==
1598 kIgnorePlatformAndCSSOverlayScrollbarSize ||
1599 !VerticalScrollbar()->ShouldParticipateInHitTesting()) &&
1600 VerticalScrollbar()->IsOverlayScrollbar()) {
1603 return VerticalScrollbar()->ScrollbarThickness();
1606 int PaintLayerScrollableArea::HorizontalScrollbarHeight(
1607 OverlayScrollbarClipBehavior overlay_scrollbar_clip_behavior) const {
1608 if (!HasHorizontalScrollbar())
1610 if (overlay_scrollbar_clip_behavior ==
1611 kIgnorePlatformAndCSSOverlayScrollbarSize &&
1612 GetLayoutBox()->StyleRef().OverflowX() == EOverflow::kOverlay) {
1615 if ((overlay_scrollbar_clip_behavior == kIgnorePlatformOverlayScrollbarSize ||
1616 overlay_scrollbar_clip_behavior ==
1617 kIgnorePlatformAndCSSOverlayScrollbarSize ||
1618 !HorizontalScrollbar()->ShouldParticipateInHitTesting()) &&
1619 HorizontalScrollbar()->IsOverlayScrollbar()) {
1622 return HorizontalScrollbar()->ScrollbarThickness();
1625 void PaintLayerScrollableArea::SnapAfterScrollbarScrolling(
1626 ScrollbarOrientation orientation) {
1627 SnapCoordinator* snap_coordinator =
1628 GetLayoutBox()->GetDocument().GetSnapCoordinator();
1629 if (!snap_coordinator)
1632 snap_coordinator->SnapAtCurrentPosition(*GetLayoutBox(),
1633 orientation == kHorizontalScrollbar,
1634 orientation == kVerticalScrollbar);
1637 void PaintLayerScrollableArea::PositionOverflowControls() {
1638 if (!HasScrollbar() && !GetLayoutBox()->CanResize())
1641 const IntRect border_box =
1642 GetLayoutBox()->PixelSnappedBorderBoxRect(layer_->SubpixelAccumulation());
1644 if (Scrollbar* vertical_scrollbar = VerticalScrollbar())
1645 vertical_scrollbar->SetFrameRect(RectForVerticalScrollbar(border_box));
1647 if (Scrollbar* horizontal_scrollbar = HorizontalScrollbar())
1648 horizontal_scrollbar->SetFrameRect(RectForHorizontalScrollbar(border_box));
1650 const IntRect& scroll_corner = ScrollCornerRect();
1652 scroll_corner_->SetFrameRect(LayoutRect(scroll_corner));
1655 resizer_->SetFrameRect(
1656 LayoutRect(ResizerCornerRect(border_box, kResizerForPointer)));
1658 // FIXME, this should eventually be removed, once we are certain that
1659 // composited controls get correctly positioned on a compositor update. For
1660 // now, conservatively leaving this unchanged.
1661 if (Layer()->HasCompositedLayerMapping())
1662 Layer()->GetCompositedLayerMapping()->PositionOverflowControlsLayers();
1665 void PaintLayerScrollableArea::UpdateScrollCornerStyle() {
1666 if (!scroll_corner_ && !HasScrollbar())
1668 if (!scroll_corner_ && HasOverlayScrollbars())
1671 const LayoutObject& style_source = ScrollbarStyleSource(*GetLayoutBox());
1672 scoped_refptr<ComputedStyle> corner =
1673 GetLayoutBox()->HasOverflowClip()
1674 ? style_source.GetUncachedPseudoStyle(
1675 PseudoStyleRequest(kPseudoIdScrollbarCorner),
1676 style_source.Style())
1677 : scoped_refptr<ComputedStyle>(nullptr);
1679 if (!scroll_corner_) {
1680 scroll_corner_ = LayoutScrollbarPart::CreateAnonymous(
1681 &GetLayoutBox()->GetDocument(), this);
1682 scroll_corner_->SetDangerousOneWayParent(GetLayoutBox());
1684 scroll_corner_->SetStyleWithWritingModeOfParent(std::move(corner));
1685 } else if (scroll_corner_) {
1686 scroll_corner_->Destroy();
1687 scroll_corner_ = nullptr;
1691 bool PaintLayerScrollableArea::HitTestOverflowControls(
1692 HitTestResult& result,
1693 const IntPoint& local_point) {
1694 if (!HasScrollbar() && !GetLayoutBox()->CanResize())
1697 IntRect resize_control_rect;
1698 if (GetLayoutBox()->StyleRef().Resize() != EResize::kNone) {
1699 resize_control_rect =
1700 ResizerCornerRect(GetLayoutBox()->PixelSnappedBorderBoxRect(
1701 Layer()->SubpixelAccumulation()),
1702 kResizerForPointer);
1703 if (resize_control_rect.Contains(local_point))
1706 int resize_control_size = max(resize_control_rect.Height(), 0);
1708 IntRect visible_rect = VisibleContentRect(kIncludeScrollbars);
1710 if (HasVerticalScrollbar() &&
1711 VerticalScrollbar()->ShouldParticipateInHitTesting()) {
1712 LayoutRect v_bar_rect(
1713 VerticalScrollbarStart(0, Layer()->PixelSnappedSize().Width()),
1714 GetLayoutBox()->BorderTop().ToInt(),
1715 VerticalScrollbar()->ScrollbarThickness(),
1716 visible_rect.Height() -
1717 (HasHorizontalScrollbar()
1718 ? HorizontalScrollbar()->ScrollbarThickness()
1719 : resize_control_size));
1720 if (v_bar_rect.Contains(local_point)) {
1721 result.SetScrollbar(VerticalScrollbar());
1726 resize_control_size = max(resize_control_rect.Width(), 0);
1727 if (HasHorizontalScrollbar() &&
1728 HorizontalScrollbar()->ShouldParticipateInHitTesting()) {
1729 // TODO(crbug.com/638981): Are the conversions to int intentional?
1730 int h_scrollbar_thickness = HorizontalScrollbar()->ScrollbarThickness();
1731 LayoutRect h_bar_rect(
1732 HorizontalScrollbarStart(0),
1733 GetLayoutBox()->BorderTop().ToInt() + visible_rect.Height() -
1734 h_scrollbar_thickness,
1735 visible_rect.Width() - (HasVerticalScrollbar()
1736 ? VerticalScrollbar()->ScrollbarThickness()
1737 : resize_control_size),
1738 h_scrollbar_thickness);
1739 if (h_bar_rect.Contains(local_point)) {
1740 result.SetScrollbar(HorizontalScrollbar());
1745 // FIXME: We should hit test the m_scrollCorner and pass it back through the
1751 IntRect PaintLayerScrollableArea::ResizerCornerRect(
1752 const IntRect& bounds,
1753 ResizerHitTestType resizer_hit_test_type) const {
1754 if (GetLayoutBox()->StyleRef().Resize() == EResize::kNone)
1756 IntRect corner = CornerRect(bounds);
1758 if (resizer_hit_test_type == kResizerForTouch) {
1759 // We make the resizer virtually larger for touch hit testing. With the
1760 // expanding ratio k = ResizerControlExpandRatioForTouch, we first move
1761 // the resizer rect (of width w & height h), by (-w * (k-1), -h * (k-1)),
1762 // then expand the rect by new_w/h = w/h * k.
1763 int expand_ratio = kResizerControlExpandRatioForTouch - 1;
1764 corner.Move(-corner.Width() * expand_ratio,
1765 -corner.Height() * expand_ratio);
1766 corner.Expand(corner.Width() * expand_ratio,
1767 corner.Height() * expand_ratio);
1773 IntRect PaintLayerScrollableArea::ScrollCornerAndResizerRect() const {
1774 IntRect scroll_corner_and_resizer = ScrollCornerRect();
1775 if (scroll_corner_and_resizer.IsEmpty()) {
1776 scroll_corner_and_resizer =
1777 ResizerCornerRect(GetLayoutBox()->PixelSnappedBorderBoxRect(
1778 Layer()->SubpixelAccumulation()),
1779 kResizerForPointer);
1781 return scroll_corner_and_resizer;
1784 bool PaintLayerScrollableArea::IsPointInResizeControl(
1785 const IntPoint& absolute_point,
1786 ResizerHitTestType resizer_hit_test_type) const {
1787 if (!GetLayoutBox()->CanResize())
1790 IntPoint local_point = RoundedIntPoint(GetLayoutBox()->AbsoluteToLocal(
1791 FloatPoint(absolute_point), kUseTransforms));
1792 IntRect local_bounds(IntPoint(), Layer()->PixelSnappedSize());
1793 return ResizerCornerRect(local_bounds, resizer_hit_test_type)
1794 .Contains(local_point);
1797 bool PaintLayerScrollableArea::HitTestResizerInFragments(
1798 const PaintLayerFragments& layer_fragments,
1799 const HitTestLocation& hit_test_location) const {
1800 if (!GetLayoutBox()->CanResize())
1803 if (layer_fragments.IsEmpty())
1806 for (int i = layer_fragments.size() - 1; i >= 0; --i) {
1807 const PaintLayerFragment& fragment = layer_fragments.at(i);
1808 if (fragment.background_rect.Intersects(hit_test_location) &&
1809 ResizerCornerRect(PixelSnappedIntRect(fragment.layer_bounds),
1811 .Contains(hit_test_location.RoundedPoint()))
1818 void PaintLayerScrollableArea::UpdateResizerAreaSet() {
1819 LocalFrame* frame = GetLayoutBox()->GetFrame();
1822 LocalFrameView* frame_view = frame->View();
1825 if (GetLayoutBox()->CanResize())
1826 frame_view->AddResizerArea(*GetLayoutBox());
1828 frame_view->RemoveResizerArea(*GetLayoutBox());
1831 void PaintLayerScrollableArea::UpdateResizerStyle(
1832 const ComputedStyle* old_style) {
1833 if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled() && old_style &&
1834 old_style->Resize() != GetLayoutBox()->StyleRef().Resize()) {
1835 // Invalidate the composited scroll corner layer on resize style change.
1836 if (auto* graphics_layer = LayerForScrollCorner())
1837 graphics_layer->SetNeedsDisplay();
1840 if (!resizer_ && !GetLayoutBox()->CanResize())
1843 const LayoutObject& style_source = ScrollbarStyleSource(*GetLayoutBox());
1844 scoped_refptr<ComputedStyle> resizer =
1845 GetLayoutBox()->HasOverflowClip()
1846 ? style_source.GetUncachedPseudoStyle(
1847 PseudoStyleRequest(kPseudoIdResizer), style_source.Style())
1848 : scoped_refptr<ComputedStyle>(nullptr);
1851 resizer_ = LayoutScrollbarPart::CreateAnonymous(
1852 &GetLayoutBox()->GetDocument(), this);
1853 resizer_->SetDangerousOneWayParent(GetLayoutBox());
1855 resizer_->SetStyleWithWritingModeOfParent(std::move(resizer));
1856 } else if (resizer_) {
1857 resizer_->Destroy();
1862 void PaintLayerScrollableArea::InvalidateAllStickyConstraints() {
1863 if (PaintLayerScrollableAreaRareData* d = RareData()) {
1864 for (PaintLayer* sticky_layer : d->sticky_constraints_map_.Keys()) {
1865 if (sticky_layer->GetLayoutObject().StyleRef().GetPosition() ==
1866 EPosition::kSticky) {
1867 sticky_layer->SetNeedsCompositingInputsUpdate();
1868 sticky_layer->GetLayoutObject().SetNeedsPaintPropertyUpdate();
1871 d->sticky_constraints_map_.clear();
1875 void PaintLayerScrollableArea::InvalidateStickyConstraintsFor(
1877 bool needs_compositing_update) {
1878 if (PaintLayerScrollableAreaRareData* d = RareData()) {
1879 d->sticky_constraints_map_.erase(layer);
1880 if (needs_compositing_update &&
1881 layer->GetLayoutObject().StyleRef().HasStickyConstrainedPosition()) {
1882 layer->SetNeedsCompositingInputsUpdate();
1883 layer->GetLayoutObject().SetNeedsPaintPropertyUpdate();
1888 bool PaintLayerScrollableArea::HasStickyDescendants() const {
1889 if (const PaintLayerScrollableAreaRareData* d = RareData())
1890 return !d->sticky_constraints_map_.IsEmpty();
1894 bool PaintLayerScrollableArea::HasNonCompositedStickyDescendants() const {
1895 if (const PaintLayerScrollableAreaRareData* d = RareData()) {
1896 for (const PaintLayer* sticky_layer : d->sticky_constraints_map_.Keys()) {
1897 if (sticky_layer->GetLayoutObject().IsSlowRepaintConstrainedObject())
1904 void PaintLayerScrollableArea::InvalidatePaintForStickyDescendants() {
1905 if (PaintLayerScrollableAreaRareData* d = RareData()) {
1906 for (PaintLayer* sticky_layer : d->sticky_constraints_map_.Keys())
1907 sticky_layer->GetLayoutObject().SetNeedsPaintPropertyUpdate();
1911 IntSize PaintLayerScrollableArea::OffsetFromResizeCorner(
1912 const IntPoint& absolute_point) const {
1913 // Currently the resize corner is either the bottom right corner or the bottom
1915 // FIXME: This assumes the location is 0, 0. Is this guaranteed to always be
1917 IntSize element_size = Layer()->PixelSnappedSize();
1918 if (GetLayoutBox()->ShouldPlaceBlockDirectionScrollbarOnLogicalLeft())
1919 element_size.SetWidth(0);
1920 IntPoint resizer_point = IntPoint(element_size);
1921 IntPoint local_point = RoundedIntPoint(GetLayoutBox()->AbsoluteToLocal(
1922 FloatPoint(absolute_point), kUseTransforms));
1923 return local_point - resizer_point;
1926 LayoutSize PaintLayerScrollableArea::MinimumSizeForResizing(float zoom_factor) {
1927 LayoutUnit min_width =
1928 MinimumValueForLength(GetLayoutBox()->StyleRef().MinWidth(),
1929 GetLayoutBox()->ContainingBlock()->Size().Width());
1930 LayoutUnit min_height =
1931 MinimumValueForLength(GetLayoutBox()->StyleRef().MinHeight(),
1932 GetLayoutBox()->ContainingBlock()->Size().Height());
1933 min_width = std::max(LayoutUnit(min_width / zoom_factor),
1934 LayoutUnit(kDefaultMinimumWidthForResizing));
1935 min_height = std::max(LayoutUnit(min_height / zoom_factor),
1936 LayoutUnit(kDefaultMinimumHeightForResizing));
1937 return LayoutSize(min_width, min_height);
1940 void PaintLayerScrollableArea::Resize(const IntPoint& pos,
1941 const LayoutSize& old_offset) {
1942 // FIXME: This should be possible on generated content but is not right now.
1943 if (!InResizeMode() || !GetLayoutBox()->CanResize() ||
1944 !GetLayoutBox()->GetNode())
1947 DCHECK(GetLayoutBox()->GetNode()->IsElementNode());
1948 Element* element = ToElement(GetLayoutBox()->GetNode());
1950 Document& document = element->GetDocument();
1952 float zoom_factor = GetLayoutBox()->StyleRef().EffectiveZoom();
1954 IntSize new_offset =
1955 OffsetFromResizeCorner(document.View()->ConvertFromRootFrame(pos));
1956 new_offset.SetWidth(new_offset.Width() / zoom_factor);
1957 new_offset.SetHeight(new_offset.Height() / zoom_factor);
1959 LayoutSize current_size = GetLayoutBox()->Size();
1960 current_size.Scale(1 / zoom_factor);
1962 LayoutSize adjusted_old_offset = LayoutSize(
1963 old_offset.Width() / zoom_factor, old_offset.Height() / zoom_factor);
1964 if (GetLayoutBox()->ShouldPlaceBlockDirectionScrollbarOnLogicalLeft()) {
1965 new_offset.SetWidth(-new_offset.Width());
1966 adjusted_old_offset.SetWidth(-adjusted_old_offset.Width());
1969 LayoutSize difference((current_size + new_offset - adjusted_old_offset)
1970 .ExpandedTo(MinimumSizeForResizing(zoom_factor)) -
1973 bool is_box_sizing_border =
1974 GetLayoutBox()->StyleRef().BoxSizing() == EBoxSizing::kBorderBox;
1977 ResolvedResize(GetLayoutBox()->StyleRef(),
1978 GetLayoutBox()->ContainingBlock()->StyleRef());
1979 if (resize != EResize::kVertical && difference.Width()) {
1980 if (element->IsFormControlElement()) {
1981 // Make implicit margins from the theme explicit (see
1982 // <http://bugs.webkit.org/show_bug.cgi?id=9547>).
1983 element->SetInlineStyleProperty(
1984 CSSPropertyMarginLeft, GetLayoutBox()->MarginLeft() / zoom_factor,
1985 CSSPrimitiveValue::UnitType::kPixels);
1986 element->SetInlineStyleProperty(
1987 CSSPropertyMarginRight, GetLayoutBox()->MarginRight() / zoom_factor,
1988 CSSPrimitiveValue::UnitType::kPixels);
1990 LayoutUnit base_width =
1991 GetLayoutBox()->Size().Width() -
1992 (is_box_sizing_border ? LayoutUnit()
1993 : GetLayoutBox()->BorderAndPaddingWidth());
1994 base_width = LayoutUnit(base_width / zoom_factor);
1995 element->SetInlineStyleProperty(CSSPropertyWidth,
1996 RoundToInt(base_width + difference.Width()),
1997 CSSPrimitiveValue::UnitType::kPixels);
2000 if (resize != EResize::kHorizontal && difference.Height()) {
2001 if (element->IsFormControlElement()) {
2002 // Make implicit margins from the theme explicit (see
2003 // <http://bugs.webkit.org/show_bug.cgi?id=9547>).
2004 element->SetInlineStyleProperty(CSSPropertyMarginTop,
2005 GetLayoutBox()->MarginTop() / zoom_factor,
2006 CSSPrimitiveValue::UnitType::kPixels);
2007 element->SetInlineStyleProperty(
2008 CSSPropertyMarginBottom, GetLayoutBox()->MarginBottom() / zoom_factor,
2009 CSSPrimitiveValue::UnitType::kPixels);
2011 LayoutUnit base_height =
2012 GetLayoutBox()->Size().Height() -
2013 (is_box_sizing_border ? LayoutUnit()
2014 : GetLayoutBox()->BorderAndPaddingHeight());
2015 base_height = LayoutUnit(base_height / zoom_factor);
2016 element->SetInlineStyleProperty(
2017 CSSPropertyHeight, RoundToInt(base_height + difference.Height()),
2018 CSSPrimitiveValue::UnitType::kPixels);
2021 document.UpdateStyleAndLayout();
2023 // FIXME (Radar 4118564): We should also autoscroll the window as necessary to
2024 // keep the point under the cursor in view.
2027 LayoutRect PaintLayerScrollableArea::ScrollIntoView(
2028 const LayoutRect& absolute_rect,
2029 const WebScrollIntoViewParams& params) {
2030 LayoutRect local_expose_rect =
2031 AbsoluteToLocal(*GetLayoutBox(), absolute_rect);
2032 LayoutSize border_origin_to_scroll_origin =
2033 LayoutSize(-GetLayoutBox()->BorderLeft(), -GetLayoutBox()->BorderTop()) +
2034 LayoutSize(GetScrollOffset());
2035 // Represent the rect in the container's scroll-origin coordinate.
2036 local_expose_rect.Move(border_origin_to_scroll_origin);
2037 LayoutRect scroll_snapport_rect = VisibleScrollSnapportRect();
2039 ScrollOffset target_offset = ScrollAlignment::GetScrollOffsetToExpose(
2040 scroll_snapport_rect, local_expose_rect, params.GetScrollAlignmentX(),
2041 params.GetScrollAlignmentY(), GetScrollOffset());
2042 ScrollOffset new_scroll_offset(
2043 ClampScrollOffset(RoundedIntSize(target_offset)));
2045 ScrollOffset old_scroll_offset = GetScrollOffset();
2046 if (params.GetScrollType() == kUserScroll) {
2047 if (!UserInputScrollable(kHorizontalScrollbar))
2048 new_scroll_offset.SetWidth(old_scroll_offset.Width());
2049 if (!UserInputScrollable(kVerticalScrollbar))
2050 new_scroll_offset.SetHeight(old_scroll_offset.Height());
2053 FloatPoint end_point = ScrollOffsetToPosition(new_scroll_offset);
2054 std::unique_ptr<SnapSelectionStrategy> strategy =
2055 SnapSelectionStrategy::CreateForEndPosition(gfx::ScrollOffset(end_point),
2057 end_point = GetLayoutBox()
2059 .GetSnapCoordinator()
2060 ->GetSnapPosition(*GetLayoutBox(), *strategy)
2061 .value_or(end_point);
2062 new_scroll_offset = ScrollPositionToOffset(end_point);
2064 if (params.is_for_scroll_sequence) {
2065 DCHECK(params.GetScrollType() == kProgrammaticScroll ||
2066 params.GetScrollType() == kUserScroll);
2067 ScrollBehavior behavior =
2068 DetermineScrollBehavior(params.GetScrollBehavior(),
2069 GetLayoutBox()->StyleRef().GetScrollBehavior());
2070 GetSmoothScrollSequencer()->QueueAnimation(this, new_scroll_offset,
2073 SetScrollOffset(new_scroll_offset, params.GetScrollType(),
2074 kScrollBehaviorInstant);
2077 ScrollOffset scroll_offset_difference = new_scroll_offset - old_scroll_offset;
2078 // The container hasn't performed the scroll yet if it's for scroll sequence.
2079 // To calculate the result from the scroll, we move the |local_expose_rect| to
2080 // the will-be-scrolled location.
2081 local_expose_rect.Move(-LayoutSize(scroll_offset_difference));
2083 // Represent the rects in the container's border-box coordinate.
2084 local_expose_rect.Move(-border_origin_to_scroll_origin);
2085 scroll_snapport_rect.Move(-border_origin_to_scroll_origin);
2086 LayoutRect intersect = Intersection(scroll_snapport_rect, local_expose_rect);
2088 if (intersect.IsEmpty() && !scroll_snapport_rect.IsEmpty() &&
2089 !local_expose_rect.IsEmpty()) {
2090 return LocalToAbsolute(*GetLayoutBox(), local_expose_rect);
2092 intersect = LocalToAbsolute(*GetLayoutBox(), intersect);
2096 void PaintLayerScrollableArea::UpdateScrollableAreaSet() {
2097 LocalFrame* frame = GetLayoutBox()->GetFrame();
2101 LocalFrameView* frame_view = frame->View();
2106 !GetLayoutBox()->Size().IsZero() &&
2107 ((HasHorizontalOverflow() && GetLayoutBox()->ScrollsOverflowX()) ||
2108 (HasVerticalOverflow() && GetLayoutBox()->ScrollsOverflowY()));
2110 bool is_visible_to_hit_test =
2111 GetLayoutBox()->StyleRef().VisibleToHitTesting();
2112 bool did_scroll_overflow = scrolls_overflow_;
2113 if (GetLayoutBox()->IsLayoutView()) {
2114 ScrollbarMode h_mode;
2115 ScrollbarMode v_mode;
2116 ToLayoutView(GetLayoutBox())->CalculateScrollbarModes(h_mode, v_mode);
2117 if (h_mode == kScrollbarAlwaysOff && v_mode == kScrollbarAlwaysOff)
2118 has_overflow = false;
2120 scrolls_overflow_ = has_overflow && is_visible_to_hit_test;
2121 if (did_scroll_overflow == ScrollsOverflow())
2124 if (RuntimeEnabledFeatures::ImplicitRootScrollerEnabled() &&
2125 scrolls_overflow_) {
2126 if (GetLayoutBox()->IsLayoutView()) {
2127 if (Element* owner = GetLayoutBox()->GetDocument().LocalOwner()) {
2128 owner->GetDocument().GetRootScrollerController().ConsiderForImplicit(
2134 .GetRootScrollerController()
2135 .ConsiderForImplicit(*GetLayoutBox()->GetNode());
2139 // The scroll and scroll offset properties depend on |scrollsOverflow| (see:
2140 // PaintPropertyTreeBuilder::updateScrollAndScrollTranslation).
2141 GetLayoutBox()->SetNeedsPaintPropertyUpdate();
2143 if (scrolls_overflow_) {
2144 DCHECK(CanHaveOverflowScrollbars(*GetLayoutBox()));
2145 frame_view->AddScrollableArea(this);
2147 frame_view->RemoveScrollableArea(this);
2150 layer_->DidUpdateScrollsOverflow();
2153 void PaintLayerScrollableArea::UpdateCompositingLayersAfterScroll() {
2154 PaintLayerCompositor* compositor = GetLayoutBox()->View()->Compositor();
2155 if (!compositor->InCompositingMode())
2158 if (UsesCompositedScrolling()) {
2159 DCHECK(Layer()->HasCompositedLayerMapping());
2160 ScrollingCoordinator* scrolling_coordinator = GetScrollingCoordinator();
2161 bool handled_scroll =
2162 Layer()->IsRootLayer() && scrolling_coordinator &&
2163 scrolling_coordinator->UpdateCompositedScrollOffset(this);
2165 if (!handled_scroll) {
2166 if (!RuntimeEnabledFeatures::BlinkGenPropertyTreesEnabled()) {
2167 // In non-BGPT mode, we need to do a full sub-tree update here, because
2168 // we need to update the position property to compute
2169 // offset_to_transform_parent. For more context, see the comment from
2171 // https://chromium-review.googlesource.com/c/chromium/src/+/1403639/6/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
2172 Layer()->GetCompositedLayerMapping()->SetNeedsGraphicsLayerUpdate(
2173 kGraphicsLayerUpdateSubtree);
2175 compositor->SetNeedsCompositingUpdate(
2176 kCompositingUpdateAfterGeometryChange);
2179 // If we have fixed elements and we scroll the root layer we might
2180 // change compositing since the fixed elements might now overlap a
2181 // composited layer.
2182 if (Layer()->IsRootLayer()) {
2183 LocalFrame* frame = GetLayoutBox()->GetFrame();
2184 if (frame && frame->View() &&
2185 frame->View()->HasViewportConstrainedObjects()) {
2186 Layer()->SetNeedsCompositingInputsUpdate();
2190 Layer()->SetNeedsCompositingInputsUpdate();
2194 ScrollingCoordinator* PaintLayerScrollableArea::GetScrollingCoordinator()
2196 LocalFrame* frame = GetLayoutBox()->GetFrame();
2200 Page* page = frame->GetPage();
2204 return page->GetScrollingCoordinator();
2207 bool PaintLayerScrollableArea::ShouldScrollOnMainThread() const {
2208 if (HasBeenDisposed())
2210 if (LocalFrame* frame = GetLayoutBox()->GetFrame()) {
2211 if (frame->View()->GetMainThreadScrollingReasons())
2214 if (HasNonCompositedStickyDescendants())
2216 return ScrollableArea::ShouldScrollOnMainThread();
2219 static bool LayerNodeMayNeedCompositedScrolling(const PaintLayer* layer) {
2220 // Don't force composite scroll for select or text input elements.
2221 if (Node* node = layer->GetLayoutObject().GetNode()) {
2222 if (IsHTMLSelectElement(node))
2224 if (TextControlElement* text_control = EnclosingTextControl(node)) {
2225 if (IsHTMLInputElement(text_control)) {
2233 bool PaintLayerScrollableArea::ComputeNeedsCompositedScrolling(
2234 const bool layer_has_been_composited,
2235 const PaintLayer* layer) {
2236 non_composited_main_thread_scrolling_reasons_ = 0;
2238 if (CompositingReasonFinder::RequiresCompositingForRootScroller(*layer))
2241 // TODO(crbug.com/839341): Remove ScrollTimeline check once we support
2242 // main-thread AnimationWorklet and don't need to promote the scroll-source.
2243 Node* node = layer->GetLayoutObject().GetNode();
2244 if (!layer->ScrollsOverflow() &&
2245 !ScrollTimeline::HasActiveScrollTimeline(node)) {
2249 if (layer->Size().IsEmpty())
2252 if (!layer_has_been_composited &&
2253 !LayerNodeMayNeedCompositedScrolling(layer)) {
2257 bool needs_composited_scrolling = true;
2259 // TODO(flackr): Allow integer transforms as long as all of the ancestor
2260 // transforms are also integer.
2261 bool background_supports_lcd_text =
2262 GetLayoutBox()->StyleRef().IsStackingContext() &&
2263 GetLayoutBox()->GetBackgroundPaintLocation(
2264 &non_composited_main_thread_scrolling_reasons_) &
2265 kBackgroundPaintInScrollingContents &&
2266 layer->BackgroundIsKnownToBeOpaqueInRect(
2267 ToLayoutBox(layer->GetLayoutObject()).PhysicalPaddingBoxRect(),
2269 !layer->CompositesWithTransform() && !layer->CompositesWithOpacity();
2271 // TODO(crbug.com/839341): Remove ScrollTimeline check once we support
2272 // main-thread AnimationWorklet and don't need to promote the scroll-source.
2273 if (!ScrollTimeline::HasActiveScrollTimeline(node) &&
2274 !layer_has_been_composited &&
2275 !layer->Compositor()->PreferCompositingToLCDTextEnabled() &&
2276 !background_supports_lcd_text) {
2277 if (layer->CompositesWithOpacity()) {
2278 non_composited_main_thread_scrolling_reasons_ |=
2279 MainThreadScrollingReason::kHasOpacityAndLCDText;
2281 if (layer->CompositesWithTransform()) {
2282 non_composited_main_thread_scrolling_reasons_ |=
2283 MainThreadScrollingReason::kHasTransformAndLCDText;
2285 if (!layer->BackgroundIsKnownToBeOpaqueInRect(
2286 ToLayoutBox(layer->GetLayoutObject()).PhysicalPaddingBoxRect(),
2288 non_composited_main_thread_scrolling_reasons_ |=
2289 MainThreadScrollingReason::kBackgroundNotOpaqueInRectAndLCDText;
2291 if (!layer->GetLayoutObject().StyleRef().IsStackingContext()) {
2292 non_composited_main_thread_scrolling_reasons_ |=
2293 MainThreadScrollingReason::kIsNotStackingContextAndLCDText;
2296 needs_composited_scrolling = false;
2299 if (layer->GetLayoutObject().HasClip() ||
2300 layer->HasDescendantWithClipPath() || !!layer->ClipPathAncestor()) {
2301 non_composited_main_thread_scrolling_reasons_ |=
2302 MainThreadScrollingReason::kHasClipRelatedProperty;
2303 needs_composited_scrolling = false;
2306 DCHECK(!(non_composited_main_thread_scrolling_reasons_ &
2307 ~MainThreadScrollingReason::kNonCompositedReasons));
2308 return needs_composited_scrolling;
2311 void PaintLayerScrollableArea::UpdateNeedsCompositedScrolling(
2312 bool layer_has_been_composited) {
2313 const bool needs_composited_scrolling =
2314 ComputeNeedsCompositedScrolling(layer_has_been_composited, Layer());
2316 if (static_cast<bool>(needs_composited_scrolling_) !=
2317 needs_composited_scrolling) {
2318 needs_composited_scrolling_ = needs_composited_scrolling;
2322 bool PaintLayerScrollableArea::VisualViewportSuppliesScrollbars() const {
2323 LocalFrame* frame = GetLayoutBox()->GetFrame();
2324 if (!frame || !frame->GetSettings())
2327 // On desktop, we always use the layout viewport's scrollbars.
2328 if (!frame->GetSettings()->GetViewportEnabled())
2331 const TopDocumentRootScrollerController& controller =
2332 GetLayoutBox()->GetDocument().GetPage()->GlobalRootScrollerController();
2333 return controller.RootScrollerArea() == this;
2336 bool PaintLayerScrollableArea::ScheduleAnimation() {
2337 if (ChromeClient* client = GetChromeClient()) {
2338 client->ScheduleAnimation(GetLayoutBox()->GetFrame()->View());
2344 void PaintLayerScrollableArea::ResetRebuildScrollbarLayerFlags() {
2345 rebuild_horizontal_scrollbar_layer_ = false;
2346 rebuild_vertical_scrollbar_layer_ = false;
2349 CompositorAnimationHost* PaintLayerScrollableArea::GetCompositorAnimationHost()
2351 return layer_->GetLayoutObject().GetFrameView()->GetCompositorAnimationHost();
2354 CompositorAnimationTimeline*
2355 PaintLayerScrollableArea::GetCompositorAnimationTimeline() const {
2356 return layer_->GetLayoutObject()
2358 ->GetCompositorAnimationTimeline();
2361 void PaintLayerScrollableArea::GetTickmarks(Vector<IntRect>& tickmarks) const {
2362 if (layer_->IsRootLayer())
2363 tickmarks = ToLayoutView(GetLayoutBox())->GetTickmarks();
2366 void PaintLayerScrollableArea::ScrollbarManager::SetHasHorizontalScrollbar(
2367 bool has_scrollbar) {
2368 if (has_scrollbar) {
2369 // This doesn't hit in any tests, but since the equivalent code in
2370 // setHasVerticalScrollbar does, presumably this code does as well.
2371 DisableCompositingQueryAsserts disabler;
2373 h_bar_ = CreateScrollbar(kHorizontalScrollbar);
2374 h_bar_is_attached_ = 1;
2375 if (!h_bar_->IsCustomScrollbar())
2376 ScrollableArea()->DidAddScrollbar(*h_bar_, kHorizontalScrollbar);
2378 h_bar_is_attached_ = 1;
2381 h_bar_is_attached_ = 0;
2382 if (!DelayScrollOffsetClampScope::ClampingIsDelayed())
2383 DestroyScrollbar(kHorizontalScrollbar);
2387 void PaintLayerScrollableArea::ScrollbarManager::SetHasVerticalScrollbar(
2388 bool has_scrollbar) {
2389 if (has_scrollbar) {
2390 DisableCompositingQueryAsserts disabler;
2392 v_bar_ = CreateScrollbar(kVerticalScrollbar);
2393 v_bar_is_attached_ = 1;
2394 if (!v_bar_->IsCustomScrollbar())
2395 ScrollableArea()->DidAddScrollbar(*v_bar_, kVerticalScrollbar);
2397 v_bar_is_attached_ = 1;
2400 v_bar_is_attached_ = 0;
2401 if (!DelayScrollOffsetClampScope::ClampingIsDelayed())
2402 DestroyScrollbar(kVerticalScrollbar);
2406 Scrollbar* PaintLayerScrollableArea::ScrollbarManager::CreateScrollbar(
2407 ScrollbarOrientation orientation) {
2408 DCHECK(orientation == kHorizontalScrollbar ? !h_bar_is_attached_
2409 : !v_bar_is_attached_);
2410 Scrollbar* scrollbar = nullptr;
2411 const LayoutObject& style_source =
2412 ScrollbarStyleSource(*ScrollableArea()->GetLayoutBox());
2413 bool has_custom_scrollbar_style =
2414 style_source.StyleRef().HasPseudoStyle(kPseudoIdScrollbar);
2415 if (has_custom_scrollbar_style) {
2416 DCHECK(style_source.GetNode() && style_source.GetNode()->IsElementNode());
2417 scrollbar = LayoutScrollbar::CreateCustomScrollbar(
2418 ScrollableArea(), orientation, ToElement(style_source.GetNode()));
2420 ScrollbarControlSize scrollbar_size = kRegularScrollbar;
2421 if (style_source.StyleRef().HasAppearance()) {
2422 scrollbar_size = LayoutTheme::GetTheme().ScrollbarControlSizeForPart(
2423 style_source.StyleRef().Appearance());
2425 scrollbar = Scrollbar::Create(ScrollableArea(), orientation, scrollbar_size,
2430 ->GetChromeClient());
2432 ScrollableArea()->GetLayoutBox()->GetDocument().View()->AddScrollbar(
2437 void PaintLayerScrollableArea::ScrollbarManager::DestroyScrollbar(
2438 ScrollbarOrientation orientation) {
2439 Member<Scrollbar>& scrollbar =
2440 orientation == kHorizontalScrollbar ? h_bar_ : v_bar_;
2441 DCHECK(orientation == kHorizontalScrollbar ? !h_bar_is_attached_
2442 : !v_bar_is_attached_);
2446 ScrollableArea()->SetScrollbarNeedsPaintInvalidation(orientation);
2447 if (orientation == kHorizontalScrollbar)
2448 ScrollableArea()->rebuild_horizontal_scrollbar_layer_ = true;
2450 ScrollableArea()->rebuild_vertical_scrollbar_layer_ = true;
2452 if (!scrollbar->IsCustomScrollbar())
2453 ScrollableArea()->WillRemoveScrollbar(*scrollbar, orientation);
2455 ScrollableArea()->GetLayoutBox()->GetDocument().View()->RemoveScrollbar(
2457 scrollbar->DisconnectFromScrollableArea();
2458 scrollbar = nullptr;
2461 void PaintLayerScrollableArea::ScrollbarManager::DestroyDetachedScrollbars() {
2462 DCHECK(!h_bar_is_attached_ || h_bar_);
2463 DCHECK(!v_bar_is_attached_ || v_bar_);
2464 if (h_bar_ && !h_bar_is_attached_)
2465 DestroyScrollbar(kHorizontalScrollbar);
2466 if (v_bar_ && !v_bar_is_attached_)
2467 DestroyScrollbar(kVerticalScrollbar);
2470 void PaintLayerScrollableArea::ScrollbarManager::Dispose() {
2471 h_bar_is_attached_ = v_bar_is_attached_ = 0;
2472 DestroyScrollbar(kHorizontalScrollbar);
2473 DestroyScrollbar(kVerticalScrollbar);
2476 void PaintLayerScrollableArea::ScrollbarManager::Trace(
2477 blink::Visitor* visitor) {
2478 visitor->Trace(scrollable_area_);
2479 visitor->Trace(h_bar_);
2480 visitor->Trace(v_bar_);
2483 uint64_t PaintLayerScrollableArea::Id() const {
2484 return DOMNodeIds::IdForNode(GetLayoutBox()->GetNode());
2487 int PaintLayerScrollableArea::PreventRelayoutScope::count_ = 0;
2489 PaintLayerScrollableArea::PreventRelayoutScope::layout_scope_ = nullptr;
2490 bool PaintLayerScrollableArea::PreventRelayoutScope::relayout_needed_ = false;
2492 PaintLayerScrollableArea::PreventRelayoutScope::PreventRelayoutScope(
2493 SubtreeLayoutScope& layout_scope) {
2495 DCHECK(!layout_scope_);
2496 DCHECK(NeedsRelayoutList().IsEmpty());
2497 layout_scope_ = &layout_scope;
2502 PaintLayerScrollableArea::PreventRelayoutScope::~PreventRelayoutScope() {
2503 if (--count_ == 0) {
2504 if (relayout_needed_) {
2505 for (auto scrollable_area : NeedsRelayoutList()) {
2506 DCHECK(scrollable_area->NeedsRelayout());
2507 LayoutBox* box = scrollable_area->GetLayoutBox();
2508 layout_scope_->SetNeedsLayout(
2509 box, layout_invalidation_reason::kScrollbarChanged);
2510 if (box->IsLayoutBlock()) {
2511 bool horizontal_scrollbar_changed =
2512 scrollable_area->HasHorizontalScrollbar() !=
2513 scrollable_area->HadHorizontalScrollbarBeforeRelayout();
2514 bool vertical_scrollbar_changed =
2515 scrollable_area->HasVerticalScrollbar() !=
2516 scrollable_area->HadVerticalScrollbarBeforeRelayout();
2517 if (horizontal_scrollbar_changed || vertical_scrollbar_changed) {
2518 ToLayoutBlock(box)->ScrollbarsChanged(horizontal_scrollbar_changed,
2519 vertical_scrollbar_changed);
2522 scrollable_area->SetNeedsRelayout(false);
2525 NeedsRelayoutList().clear();
2527 layout_scope_ = nullptr;
2531 void PaintLayerScrollableArea::PreventRelayoutScope::SetBoxNeedsLayout(
2532 PaintLayerScrollableArea& scrollable_area,
2533 bool had_horizontal_scrollbar,
2534 bool had_vertical_scrollbar) {
2536 DCHECK(layout_scope_);
2537 if (scrollable_area.NeedsRelayout())
2539 scrollable_area.SetNeedsRelayout(true);
2540 scrollable_area.SetHadHorizontalScrollbarBeforeRelayout(
2541 had_horizontal_scrollbar);
2542 scrollable_area.SetHadVerticalScrollbarBeforeRelayout(had_vertical_scrollbar);
2544 relayout_needed_ = true;
2545 NeedsRelayoutList().push_back(&scrollable_area);
2548 void PaintLayerScrollableArea::PreventRelayoutScope::ResetRelayoutNeeded() {
2549 DCHECK_EQ(count_, 0);
2550 DCHECK(NeedsRelayoutList().IsEmpty());
2551 relayout_needed_ = false;
2554 HeapVector<Member<PaintLayerScrollableArea>>&
2555 PaintLayerScrollableArea::PreventRelayoutScope::NeedsRelayoutList() {
2556 DEFINE_STATIC_LOCAL(
2557 Persistent<HeapVector<Member<PaintLayerScrollableArea>>>,
2558 needs_relayout_list,
2559 (MakeGarbageCollected<HeapVector<Member<PaintLayerScrollableArea>>>()));
2560 return *needs_relayout_list;
2563 int PaintLayerScrollableArea::FreezeScrollbarsScope::count_ = 0;
2565 int PaintLayerScrollableArea::DelayScrollOffsetClampScope::count_ = 0;
2567 PaintLayerScrollableArea::DelayScrollOffsetClampScope::
2568 DelayScrollOffsetClampScope() {
2569 DCHECK(count_ > 0 || NeedsClampList().IsEmpty());
2573 PaintLayerScrollableArea::DelayScrollOffsetClampScope::
2574 ~DelayScrollOffsetClampScope() {
2576 DelayScrollOffsetClampScope::ClampScrollableAreas();
2579 void PaintLayerScrollableArea::DelayScrollOffsetClampScope::SetNeedsClamp(
2580 PaintLayerScrollableArea* scrollable_area) {
2581 if (!scrollable_area->NeedsScrollOffsetClamp()) {
2582 scrollable_area->SetNeedsScrollOffsetClamp(true);
2583 NeedsClampList().push_back(scrollable_area);
2587 void PaintLayerScrollableArea::DelayScrollOffsetClampScope::
2588 ClampScrollableAreas() {
2589 for (auto& scrollable_area : NeedsClampList())
2590 scrollable_area->ClampScrollOffsetAfterOverflowChange();
2591 NeedsClampList().clear();
2594 HeapVector<Member<PaintLayerScrollableArea>>&
2595 PaintLayerScrollableArea::DelayScrollOffsetClampScope::NeedsClampList() {
2596 DEFINE_STATIC_LOCAL(
2597 Persistent<HeapVector<Member<PaintLayerScrollableArea>>>,
2599 (MakeGarbageCollected<HeapVector<Member<PaintLayerScrollableArea>>>()));
2600 return *needs_clamp_list;
2603 ScrollbarTheme& PaintLayerScrollableArea::GetPageScrollbarTheme() const {
2604 // If PaintLayer is destructed before PaintLayerScrollable area, we can not
2605 // get the page scrollbar theme setting.
2606 DCHECK(!HasBeenDisposed());
2608 Page* page = GetLayoutBox()->GetFrame()->GetPage();
2611 return page->GetScrollbarTheme();
2614 void PaintLayerScrollableArea::WillRemoveScrollbar(
2615 Scrollbar& scrollbar,
2616 ScrollbarOrientation orientation) {
2617 if (!scrollbar.IsCustomScrollbar() &&
2618 !(orientation == kHorizontalScrollbar ? LayerForHorizontalScrollbar()
2619 : LayerForVerticalScrollbar())) {
2620 ObjectPaintInvalidator(*GetLayoutBox())
2621 .SlowSetPaintingLayerNeedsRepaintAndInvalidateDisplayItemClient(
2622 scrollbar, PaintInvalidationReason::kScrollControl);
2625 ScrollableArea::WillRemoveScrollbar(scrollbar, orientation);
2628 static LayoutRect ScrollControlVisualRect(
2629 const IntRect& scroll_control_rect,
2630 const LayoutBox& box,
2631 const PaintInvalidatorContext& context,
2632 const LayoutRect& previous_visual_rect) {
2633 LayoutRect visual_rect(scroll_control_rect);
2635 FindVisualRectNeedingUpdateScope finder(box, context, previous_visual_rect,
2638 if (!context.NeedsVisualRectUpdate(box))
2639 return previous_visual_rect;
2641 // No need to apply any paint offset. Scroll controls paint in a different
2642 // transform space than their contained box (the scrollbarPaintOffset
2647 // Returns true if the scroll control is invalidated.
2648 static bool InvalidatePaintOfScrollControlIfNeeded(
2649 const LayoutRect& new_visual_rect,
2650 const LayoutRect& previous_visual_rect,
2651 bool needs_paint_invalidation,
2653 const LayoutBoxModelObject& paint_invalidation_container) {
2654 bool should_invalidate_new_rect = needs_paint_invalidation;
2655 if (new_visual_rect != previous_visual_rect) {
2656 should_invalidate_new_rect = true;
2657 } else if (previous_visual_rect.IsEmpty()) {
2658 DCHECK(new_visual_rect.IsEmpty());
2659 // Do not issue an empty invalidation.
2660 should_invalidate_new_rect = false;
2663 return should_invalidate_new_rect;
2666 static LayoutRect InvalidatePaintOfScrollbarIfNeeded(
2667 Scrollbar* scrollbar,
2668 GraphicsLayer* graphics_layer,
2669 bool& previously_was_overlay,
2670 const LayoutRect& previous_visual_rect,
2671 bool needs_paint_invalidation_arg,
2673 const PaintInvalidatorContext& context) {
2674 bool is_overlay = scrollbar && scrollbar->IsOverlayScrollbar();
2676 LayoutRect new_visual_rect;
2677 // Calculate visual rect of the scrollbar, except overlay composited
2678 // scrollbars because we invalidate the graphics layer only.
2679 if (scrollbar && !(graphics_layer && is_overlay)) {
2680 new_visual_rect = ScrollControlVisualRect(scrollbar->FrameRect(), box,
2681 context, previous_visual_rect);
2684 bool needs_paint_invalidation = needs_paint_invalidation_arg;
2685 if (needs_paint_invalidation && graphics_layer) {
2686 // If the scrollbar needs paint invalidation but didn't change location/size
2687 // or the scrollbar is an overlay scrollbar (visual rect is empty),
2688 // invalidating the graphics layer is enough (which has been done in
2689 // ScrollableArea::setScrollbarNeedsPaintInvalidation()).
2690 // Otherwise invalidatePaintOfScrollControlIfNeeded() below will invalidate
2691 // the old and new location of the scrollbar on the box's paint invalidation
2692 // container to ensure newly expanded/shrunk areas of the box to be
2694 needs_paint_invalidation = false;
2695 DCHECK(!graphics_layer->PaintsContentOrHitTest() ||
2696 graphics_layer->GetPaintController().GetPaintArtifact().IsEmpty());
2699 // Invalidate the box's display item client if the box's padding box size is
2700 // affected by change of the non-overlay scrollbar width. We detect change of
2701 // visual rect size instead of change of scrollbar width change, which may
2702 // have some false-positives (e.g. the scrollbar changed length but not width)
2703 // but won't invalidate more than expected because in the false-positive case
2704 // the box must have changed size and have been invalidated.
2705 const LayoutBoxModelObject& paint_invalidation_container =
2706 *context.paint_invalidation_container;
2707 LayoutSize new_scrollbar_used_space_in_box;
2709 new_scrollbar_used_space_in_box = new_visual_rect.Size();
2710 LayoutSize previous_scrollbar_used_space_in_box;
2711 if (!previously_was_overlay)
2712 previous_scrollbar_used_space_in_box = previous_visual_rect.Size();
2714 // The IsEmpty() check avoids invalidaiton in cases when the visual rect
2715 // changes from (0,0 0x0) to (0,0 0x100).
2716 if (!(new_scrollbar_used_space_in_box.IsEmpty() &&
2717 previous_scrollbar_used_space_in_box.IsEmpty()) &&
2718 new_scrollbar_used_space_in_box != previous_scrollbar_used_space_in_box) {
2719 context.painting_layer->SetNeedsRepaint();
2720 ObjectPaintInvalidator(box).InvalidateDisplayItemClient(
2721 box, PaintInvalidationReason::kGeometry);
2724 bool invalidated = InvalidatePaintOfScrollControlIfNeeded(
2725 new_visual_rect, previous_visual_rect, needs_paint_invalidation, box,
2726 paint_invalidation_container);
2728 previously_was_overlay = is_overlay;
2730 if (!invalidated || !scrollbar || graphics_layer)
2731 return new_visual_rect;
2733 context.painting_layer->SetNeedsRepaint();
2734 ObjectPaintInvalidator(box).InvalidateDisplayItemClient(
2735 *scrollbar, PaintInvalidationReason::kScrollControl);
2736 if (scrollbar->IsCustomScrollbar()) {
2737 ToLayoutScrollbar(scrollbar)
2738 ->InvalidateDisplayItemClientsOfScrollbarParts();
2741 return new_visual_rect;
2744 void PaintLayerScrollableArea::InvalidatePaintOfScrollControlsIfNeeded(
2745 const PaintInvalidatorContext& context) {
2746 LayoutBox& box = *GetLayoutBox();
2747 SetHorizontalScrollbarVisualRect(InvalidatePaintOfScrollbarIfNeeded(
2748 HorizontalScrollbar(), LayerForHorizontalScrollbar(),
2749 horizontal_scrollbar_previously_was_overlay_,
2750 horizontal_scrollbar_visual_rect_,
2751 HorizontalScrollbarNeedsPaintInvalidation(), box, context));
2752 SetVerticalScrollbarVisualRect(InvalidatePaintOfScrollbarIfNeeded(
2753 VerticalScrollbar(), LayerForVerticalScrollbar(),
2754 vertical_scrollbar_previously_was_overlay_,
2755 vertical_scrollbar_visual_rect_,
2756 VerticalScrollbarNeedsPaintInvalidation(), box, context));
2758 LayoutRect scroll_corner_and_resizer_visual_rect =
2759 ScrollControlVisualRect(ScrollCornerAndResizerRect(), box, context,
2760 scroll_corner_and_resizer_visual_rect_);
2761 const LayoutBoxModelObject& paint_invalidation_container =
2762 *context.paint_invalidation_container;
2763 if (InvalidatePaintOfScrollControlIfNeeded(
2764 scroll_corner_and_resizer_visual_rect,
2765 scroll_corner_and_resizer_visual_rect_,
2766 ScrollCornerNeedsPaintInvalidation(), box,
2767 paint_invalidation_container)) {
2768 SetScrollCornerAndResizerVisualRect(scroll_corner_and_resizer_visual_rect);
2769 if (LayoutScrollbarPart* scroll_corner = ScrollCorner()) {
2770 ObjectPaintInvalidator(*scroll_corner)
2771 .InvalidateDisplayItemClientsIncludingNonCompositingDescendants(
2772 PaintInvalidationReason::kScrollControl);
2774 if (LayoutScrollbarPart* resizer = Resizer()) {
2775 ObjectPaintInvalidator(*resizer)
2776 .InvalidateDisplayItemClientsIncludingNonCompositingDescendants(
2777 PaintInvalidationReason::kScrollControl);
2781 ClearNeedsPaintInvalidationForScrollControls();
2784 void PaintLayerScrollableArea::ClearPreviousVisualRects() {
2785 SetHorizontalScrollbarVisualRect(LayoutRect());
2786 SetVerticalScrollbarVisualRect(LayoutRect());
2787 SetScrollCornerAndResizerVisualRect(LayoutRect());
2790 void PaintLayerScrollableArea::SetHorizontalScrollbarVisualRect(
2791 const LayoutRect& rect) {
2792 horizontal_scrollbar_visual_rect_ = rect;
2793 if (Scrollbar* scrollbar = HorizontalScrollbar())
2794 scrollbar->SetVisualRect(rect);
2797 void PaintLayerScrollableArea::SetVerticalScrollbarVisualRect(
2798 const LayoutRect& rect) {
2799 vertical_scrollbar_visual_rect_ = rect;
2800 if (Scrollbar* scrollbar = VerticalScrollbar())
2801 scrollbar->SetVisualRect(rect);
2804 void PaintLayerScrollableArea::SetScrollCornerAndResizerVisualRect(
2805 const LayoutRect& rect) {
2806 scroll_corner_and_resizer_visual_rect_ = rect;
2807 if (LayoutScrollbarPart* scroll_corner = ScrollCorner())
2808 scroll_corner->GetMutableForPainting().FirstFragment().SetVisualRect(rect);
2809 if (LayoutScrollbarPart* resizer = Resizer())
2810 resizer->GetMutableForPainting().FirstFragment().SetVisualRect(rect);
2813 void PaintLayerScrollableArea::ScrollControlWasSetNeedsPaintInvalidation() {
2814 GetLayoutBox()->SetShouldCheckForPaintInvalidation();
2817 void PaintLayerScrollableArea::DidScrollWithScrollbar(
2819 ScrollbarOrientation orientation) {
2820 WebFeature scrollbar_use_uma;
2822 case kBackButtonStartPart:
2823 case kForwardButtonStartPart:
2824 case kBackButtonEndPart:
2825 case kForwardButtonEndPart:
2827 (orientation == kVerticalScrollbar
2828 ? WebFeature::kScrollbarUseVerticalScrollbarButton
2829 : WebFeature::kScrollbarUseHorizontalScrollbarButton);
2833 (orientation == kVerticalScrollbar
2834 ? WebFeature::kScrollbarUseVerticalScrollbarThumb
2835 : WebFeature::kScrollbarUseHorizontalScrollbarThumb);
2837 case kBackTrackPart:
2838 case kForwardTrackPart:
2840 (orientation == kVerticalScrollbar
2841 ? WebFeature::kScrollbarUseVerticalScrollbarTrack
2842 : WebFeature::kScrollbarUseHorizontalScrollbarTrack);
2848 UseCounter::Count(GetLayoutBox()->GetDocument(), scrollbar_use_uma);
2851 CompositorElementId PaintLayerScrollableArea::GetCompositorElementId() const {
2852 return CompositorElementIdFromUniqueObjectId(
2853 GetLayoutBox()->UniqueId(), CompositorElementIdNamespace::kScroll);
2857 PaintLayerScrollableArea::ScrollingBackgroundDisplayItemClient::VisualRect()
2859 const auto* box = scrollable_area_->GetLayoutBox();
2860 const auto& paint_offset = box->FirstFragment().PaintOffset();
2861 auto overflow_clip_rect =
2862 PixelSnappedIntRect(box->OverflowClipRect(paint_offset));
2863 auto scroll_size = scrollable_area_->PixelSnappedContentsSize(paint_offset);
2864 // Ensure scrolling contents are at least as large as the scroll clip
2865 scroll_size = scroll_size.ExpandedTo(overflow_clip_rect.Size());
2866 LayoutRect result(overflow_clip_rect.Location(), scroll_size);
2868 if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
2870 scrollable_area_->layer_->GraphicsLayerBacking()->VisualRect());
2877 PaintLayerScrollableArea::ScrollingBackgroundDisplayItemClient::DebugName()
2879 return "Scrolling background of " +
2880 scrollable_area_->GetLayoutBox()->DebugName();
2883 bool PaintLayerScrollableArea::ScrollingBackgroundDisplayItemClient::
2884 PaintedOutputOfObjectHasNoEffectRegardlessOfSize() const {
2885 return scrollable_area_->GetLayoutBox()
2886 ->PaintedOutputOfObjectHasNoEffectRegardlessOfSize();
2889 } // namespace blink