*
* Other contributors:
* Robert O'Callahan <roc+@cs.cmu.edu>
- * David Baron <dbaron@fas.harvard.edu>
+ * David Baron <dbaron@dbaron.org>
* Christian Biesinger <cbiesinger@gmail.com>
* Randall Jesup <rjesup@wgate.com>
* Roland Mainz <roland.mainz@informatik.med.uni-giessen.de>
#include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
+#include <utility>
+
#include "base/numerics/checked_math.h"
-#include "base/single_thread_task_runner.h"
+#include "base/task/single_thread_task_runner.h"
+#include "cc/animation/animation_timeline.h"
+#include "cc/input/main_thread_scrolling_reason.h"
+#include "cc/input/snap_selection_strategy.h"
+#include "cc/layers/picture_layer.h"
+#include "third_party/blink/public/common/features.h"
+#include "third_party/blink/public/mojom/scroll/scroll_into_view_params.mojom-blink.h"
+#include "third_party/blink/public/mojom/scroll/scrollbar_mode.mojom-blink.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/public/platform/task_type.h"
-#include "third_party/blink/public/platform/web_scroll_into_view_params.h"
#include "third_party/blink/renderer/core/accessibility/ax_object_cache.h"
#include "third_party/blink/renderer/core/animation/scroll_timeline.h"
-#include "third_party/blink/renderer/core/css/pseudo_style_request.h"
+#include "third_party/blink/renderer/core/content_capture/content_capture_manager.h"
+#include "third_party/blink/renderer/core/css/color_scheme_flags.h"
+#include "third_party/blink/renderer/core/css/style_request.h"
#include "third_party/blink/renderer/core/dom/dom_node_ids.h"
#include "third_party/blink/renderer/core/dom/node.h"
+#include "third_party/blink/renderer/core/dom/node_computed_style.h"
#include "third_party/blink/renderer/core/dom/shadow_root.h"
#include "third_party/blink/renderer/core/editing/frame_selection.h"
#include "third_party/blink/renderer/core/editing/markers/document_marker_controller.h"
#include "third_party/blink/renderer/core/html/forms/text_control_element.h"
#include "third_party/blink/renderer/core/html/html_frame_owner_element.h"
#include "third_party/blink/renderer/core/input/event_handler.h"
+#include "third_party/blink/renderer/core/inspector/inspector_trace_events.h"
+#include "third_party/blink/renderer/core/layout/custom_scrollbar.h"
+#include "third_party/blink/renderer/core/layout/layout_custom_scrollbar_part.h"
#include "third_party/blink/renderer/core/layout/layout_embedded_content.h"
-#include "third_party/blink/renderer/core/layout/layout_flexible_box.h"
-#include "third_party/blink/renderer/core/layout/layout_scrollbar.h"
-#include "third_party/blink/renderer/core/layout/layout_scrollbar_part.h"
#include "third_party/blink/renderer/core/layout/layout_theme.h"
#include "third_party/blink/renderer/core/layout/layout_view.h"
#include "third_party/blink/renderer/core/layout/ng/legacy_layout_tree_walking.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
#include "third_party/blink/renderer/core/loader/document_loader.h"
#include "third_party/blink/renderer/core/page/chrome_client.h"
#include "third_party/blink/renderer/core/page/focus_controller.h"
#include "third_party/blink/renderer/core/page/page.h"
+#include "third_party/blink/renderer/core/page/scrolling/fragment_anchor.h"
#include "third_party/blink/renderer/core/page/scrolling/root_scroller_controller.h"
-#include "third_party/blink/renderer/core/page/scrolling/root_scroller_util.h"
#include "third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.h"
#include "third_party/blink/renderer/core/page/scrolling/snap_coordinator.h"
+#include "third_party/blink/renderer/core/page/scrolling/sticky_position_scrolling_constraints.h"
#include "third_party/blink/renderer/core/page/scrolling/top_document_root_scroller_controller.h"
-#include "third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h"
-#include "third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.h"
-#include "third_party/blink/renderer/core/paint/find_paint_offset_and_visual_rect_needing_update.h"
+#include "third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.h"
+#include "third_party/blink/renderer/core/paint/object_paint_invalidator.h"
#include "third_party/blink/renderer/core/paint/paint_invalidator.h"
+#include "third_party/blink/renderer/core/paint/paint_layer.h"
#include "third_party/blink/renderer/core/paint/paint_layer_fragment.h"
-#include "third_party/blink/renderer/platform/graphics/graphics_layer.h"
+#include "third_party/blink/renderer/core/scroll/scroll_alignment.h"
+#include "third_party/blink/renderer/core/scroll/scroll_animator_base.h"
+#include "third_party/blink/renderer/core/scroll/scrollable_area.h"
+#include "third_party/blink/renderer/core/scroll/scrollbar_theme.h"
+#include "third_party/blink/renderer/core/scroll/smooth_scroll_sequencer.h"
+#include "third_party/blink/renderer/core/view_transition/view_transition.h"
+#include "third_party/blink/renderer/core/view_transition/view_transition_utils.h"
#include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h"
-#include "third_party/blink/renderer/platform/scroll/scroll_alignment.h"
-#include "third_party/blink/renderer/platform/scroll/scroll_animator_base.h"
-#include "third_party/blink/renderer/platform/scroll/scrollbar_theme.h"
-#include "third_party/blink/renderer/platform/scroll/smooth_scroll_sequencer.h"
+#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
+#include "ui/base/ui_base_features.h"
+#include "ui/gfx/geometry/point_conversions.h"
namespace blink {
-namespace {
-
-// Default value is set to 15 as the default
-// minimum size used by firefox is 15x15.
-static const int kDefaultMinimumWidthForResizing = 15;
-static const int kDefaultMinimumHeightForResizing = 15;
-
-} // namespace
-
-static LayoutRect LocalToAbsolute(const LayoutBox& box, LayoutRect rect) {
- return LayoutRect(
- box.LocalToAbsoluteQuad(FloatQuad(FloatRect(rect)), kUseTransforms)
- .BoundingBox());
-}
+PaintLayerScrollableAreaRareData::PaintLayerScrollableAreaRareData() = default;
-static LayoutRect AbsoluteToLocal(const LayoutBox& box, LayoutRect rect) {
- return LayoutRect(
- box.AbsoluteToLocalQuad(FloatQuad(FloatRect(rect)), kUseTransforms)
- .BoundingBox());
+void PaintLayerScrollableAreaRareData::Trace(Visitor* visitor) const {
+ visitor->Trace(sticky_layers_);
}
-PaintLayerScrollableAreaRareData::PaintLayerScrollableAreaRareData() = default;
-
const int kResizerControlExpandRatioForTouch = 2;
PaintLayerScrollableArea::PaintLayerScrollableArea(PaintLayer& layer)
- : layer_(&layer),
+ : ScrollableArea(layer.GetLayoutBox()
+ ->GetDocument()
+ .GetPage()
+ ->GetAgentGroupScheduler()
+ .CompositorTaskRunner()),
+ layer_(&layer),
in_resize_mode_(false),
scrolls_overflow_(false),
- in_overflow_relayout_(false),
- allow_second_overflow_relayout_(false),
- needs_composited_scrolling_(false),
- rebuild_horizontal_scrollbar_layer_(false),
- rebuild_vertical_scrollbar_layer_(false),
needs_scroll_offset_clamp_(false),
needs_relayout_(false),
had_horizontal_scrollbar_before_relayout_(false),
had_vertical_scrollbar_before_relayout_(false),
+ had_resizer_before_relayout_(false),
scroll_origin_changed_(false),
+ is_scrollbar_freeze_root_(false),
+ is_horizontal_scrollbar_frozen_(false),
+ is_vertical_scrollbar_frozen_(false),
+ should_scroll_on_main_thread_(true),
scrollbar_manager_(*this),
+ has_last_committed_scroll_offset_(false),
scroll_corner_(nullptr),
resizer_(nullptr),
- scroll_anchor_(this),
- non_composited_main_thread_scrolling_reasons_(0),
- horizontal_scrollbar_previously_was_overlay_(false),
- vertical_scrollbar_previously_was_overlay_(false) {
- Node* node = GetLayoutBox()->GetNode();
- if (node && node->IsElementNode()) {
+ scroll_anchor_(this) {
+ if (auto* element = DynamicTo<Element>(GetLayoutBox()->GetNode())) {
// We save and restore only the scrollOffset as the other scroll values are
// recalculated.
- Element* element = ToElement(node);
scroll_offset_ = element->SavedLayerScrollOffset();
if (!scroll_offset_.IsZero())
GetScrollAnimator().SetCurrentOffset(scroll_offset_);
element->SetSavedLayerScrollOffset(ScrollOffset());
}
- UpdateResizerAreaSet();
+
+ if (!RuntimeEnabledFeatures::LayoutNewSnapLogicEnabled()) {
+ GetLayoutBox()->GetDocument().GetSnapCoordinator().AddSnapContainer(
+ *GetLayoutBox());
+ }
}
PaintLayerScrollableArea::~PaintLayerScrollableArea() {
- DCHECK(HasBeenDisposed());
+ CHECK(HasBeenDisposed());
+}
+
+PaintLayerScrollableArea* PaintLayerScrollableArea::FromNode(const Node& node) {
+ const LayoutBox* box = node.GetLayoutBox();
+ return box ? box->GetScrollableArea() : nullptr;
}
-void PaintLayerScrollableArea::DidScroll(const FloatPoint& position) {
- ScrollableArea::DidScroll(position);
+void PaintLayerScrollableArea::DidCompositorScroll(
+ const gfx::PointF& position) {
+ ScrollableArea::DidCompositorScroll(position);
// This should be alive if it receives composited scroll callbacks.
CHECK(!HasBeenDisposed());
}
-void PaintLayerScrollableArea::Dispose() {
+void PaintLayerScrollableArea::DisposeImpl() {
+ rare_data_.Clear();
+
+ if (!RuntimeEnabledFeatures::LayoutNewSnapLogicEnabled()) {
+ GetLayoutBox()->GetDocument().GetSnapCoordinator().RemoveSnapContainer(
+ *GetLayoutBox());
+ }
+
if (InResizeMode() && !GetLayoutBox()->DocumentBeingDestroyed()) {
if (LocalFrame* frame = GetLayoutBox()->GetFrame())
frame->GetEventHandler().ResizeScrollableAreaDestroyed();
if (LocalFrame* frame = GetLayoutBox()->GetFrame()) {
if (LocalFrameView* frame_view = frame->View()) {
- frame_view->RemoveScrollableArea(this);
+ frame_view->RemoveScrollAnchoringScrollableArea(this);
+ frame_view->RemoveUserScrollableArea(this);
frame_view->RemoveAnimatingScrollableArea(this);
+ if (RuntimeEnabledFeatures::LayoutNewSnapLogicEnabled()) {
+ frame_view->RemovePendingSnapUpdate(this);
+ }
}
}
- non_composited_main_thread_scrolling_reasons_ = 0;
-
- if (ScrollingCoordinator* scrolling_coordinator = GetScrollingCoordinator())
- scrolling_coordinator->WillDestroyScrollableArea(this);
-
if (!GetLayoutBox()->DocumentBeingDestroyed()) {
- Node* node = GetLayoutBox()->GetNode();
- // FIXME: Make setSavedLayerScrollOffset take DoubleSize. crbug.com/414283.
- if (node && node->IsElementNode())
- ToElement(node)->SetSavedLayerScrollOffset(scroll_offset_);
- }
-
- if (LocalFrame* frame = GetLayoutBox()->GetFrame()) {
- if (LocalFrameView* frame_view = frame->View())
- frame_view->RemoveResizerArea(*GetLayoutBox());
+ if (auto* element = DynamicTo<Element>(GetLayoutBox()->GetNode()))
+ element->SetSavedLayerScrollOffset(scroll_offset_);
}
// Note: it is not safe to call ScrollAnchor::clear if the document is being
if (SmoothScrollSequencer* sequencer = GetSmoothScrollSequencer())
sequencer->DidDisposeScrollableArea(*this);
+ RunScrollCompleteCallbacks(ScrollableArea::ScrollCompletionMode::kFinished);
+
layer_ = nullptr;
}
-bool PaintLayerScrollableArea::HasBeenDisposed() const {
- return !layer_;
+void PaintLayerScrollableArea::ApplyPendingHistoryRestoreScrollOffset() {
+ if (!pending_view_state_)
+ return;
+
+ // TODO(pnoland): attempt to restore the anchor in more places than this.
+ // Anchor-based restore should allow for earlier restoration.
+ bool did_restore = RestoreScrollAnchor(
+ {pending_view_state_->scroll_anchor_data_.selector_,
+ LayoutPoint(pending_view_state_->scroll_anchor_data_.offset_),
+ pending_view_state_->scroll_anchor_data_.simhash_});
+ if (!did_restore) {
+ SetScrollOffset(pending_view_state_->scroll_offset_,
+ mojom::blink::ScrollType::kProgrammatic,
+ mojom::blink::ScrollBehavior::kAuto);
+ }
+
+ pending_view_state_.reset();
+}
+
+void PaintLayerScrollableArea::SetTickmarksOverride(
+ Vector<gfx::Rect> tickmarks) {
+ EnsureRareData().tickmarks_override_ = std::move(tickmarks);
}
-void PaintLayerScrollableArea::Trace(blink::Visitor* visitor) {
+void PaintLayerScrollableArea::Trace(Visitor* visitor) const {
visitor->Trace(scrollbar_manager_);
+ visitor->Trace(scroll_corner_);
+ visitor->Trace(resizer_);
visitor->Trace(scroll_anchor_);
+ visitor->Trace(scrolling_background_display_item_client_);
+ visitor->Trace(scroll_corner_display_item_client_);
+ visitor->Trace(layer_);
+ visitor->Trace(rare_data_);
ScrollableArea::Trace(visitor);
}
return GetLayoutBox()->GetFrame()->ShouldThrottleRendering();
}
-PlatformChromeClient* PaintLayerScrollableArea::GetChromeClient() const {
+ChromeClient* PaintLayerScrollableArea::GetChromeClient() const {
if (HasBeenDisposed())
return nullptr;
if (Page* page = GetLayoutBox()->GetFrame()->GetPage())
const {
if (HasBeenDisposed())
return nullptr;
- if (Page* page = GetLayoutBox()->GetFrame()->GetPage())
- return page->GetSmoothScrollSequencer();
- return nullptr;
-}
-GraphicsLayer* PaintLayerScrollableArea::LayerForScrolling() const {
- return Layer()->HasCompositedLayerMapping()
- ? Layer()->GetCompositedLayerMapping()->ScrollingContentsLayer()
- : nullptr;
-}
-
-GraphicsLayer* PaintLayerScrollableArea::LayerForHorizontalScrollbar() const {
- // See crbug.com/343132.
- DisableCompositingQueryAsserts disabler;
-
- return Layer()->HasCompositedLayerMapping()
- ? Layer()
- ->GetCompositedLayerMapping()
- ->LayerForHorizontalScrollbar()
- : nullptr;
-}
-
-GraphicsLayer* PaintLayerScrollableArea::LayerForVerticalScrollbar() const {
- // See crbug.com/343132.
- DisableCompositingQueryAsserts disabler;
-
- return Layer()->HasCompositedLayerMapping()
- ? Layer()->GetCompositedLayerMapping()->LayerForVerticalScrollbar()
- : nullptr;
-}
-
-GraphicsLayer* PaintLayerScrollableArea::LayerForScrollCorner() const {
- // See crbug.com/343132.
- DisableCompositingQueryAsserts disabler;
-
- return Layer()->HasCompositedLayerMapping()
- ? Layer()->GetCompositedLayerMapping()->LayerForScrollCorner()
- : nullptr;
-}
-
-bool PaintLayerScrollableArea::ShouldUseIntegerScrollOffset() const {
- if (!HasBeenDisposed()) {
- Frame* frame = GetLayoutBox()->GetFrame();
- if (frame->GetSettings() &&
- !frame->GetSettings()->GetPreferCompositingToLCDTextEnabled())
- return true;
- }
-
- return ScrollableArea::ShouldUseIntegerScrollOffset();
+ return GetLayoutBox()->GetFrame()->GetSmoothScrollSequencer();
}
bool PaintLayerScrollableArea::IsActive() const {
int max_x,
int thickness) {
if (box.ShouldPlaceBlockDirectionScrollbarOnLogicalLeft())
- return min_x + box.StyleRef().BorderLeftWidth();
- return max_x - thickness - box.StyleRef().BorderRightWidth();
+ return min_x + box.StyleRef().BorderLeftWidth().ToFloat();
+ return max_x - thickness - box.StyleRef().BorderRightWidth().ToFloat();
}
-IntRect PaintLayerScrollableArea::PaintLayerScrollableArea::CornerRect(
- const IntRect& bounds) const {
+gfx::Rect PaintLayerScrollableArea::CornerRect() const {
int horizontal_thickness;
int vertical_thickness;
if (!VerticalScrollbar() && !HorizontalScrollbar()) {
- // FIXME: This isn't right. We need to know the thickness of custom
- // scrollbars even when they don't exist in order to set the resizer square
- // size properly.
- horizontal_thickness = GetPageScrollbarTheme().ScrollbarThickness();
+ // We need to know the thickness of custom scrollbars even when they don't
+ // exist in order to set the resizer square size properly.
+ horizontal_thickness = GetPageScrollbarTheme().ScrollbarThickness(
+ ScaleFromDIP(), EScrollbarWidth::kAuto);
vertical_thickness = horizontal_thickness;
} else if (VerticalScrollbar() && !HorizontalScrollbar()) {
horizontal_thickness = VerticalScrollbar()->ScrollbarThickness();
horizontal_thickness = VerticalScrollbar()->ScrollbarThickness();
vertical_thickness = HorizontalScrollbar()->ScrollbarThickness();
}
- return IntRect(CornerStart(*GetLayoutBox(), bounds.X(), bounds.MaxX(),
- horizontal_thickness),
- bounds.MaxY() - vertical_thickness -
- GetLayoutBox()->StyleRef().BorderBottomWidth(),
- horizontal_thickness, vertical_thickness);
+ gfx::Size border_box_size = PixelSnappedBorderBoxSize();
+ return gfx::Rect(CornerStart(*GetLayoutBox(), 0, border_box_size.width(),
+ horizontal_thickness),
+ border_box_size.height() - vertical_thickness -
+ GetLayoutBox()->StyleRef().BorderBottomWidth().ToFloat(),
+ horizontal_thickness, vertical_thickness);
}
-IntRect PaintLayerScrollableArea::ScrollCornerRect() const {
+gfx::Rect PaintLayerScrollableArea::ScrollCornerRect() const {
// We have a scrollbar corner when a scrollbar is visible and not filling the
// entire length of the box.
// This happens when:
// (b) Both scrollbars are present.
bool has_horizontal_bar = HorizontalScrollbar();
bool has_vertical_bar = VerticalScrollbar();
- bool has_resizer = GetLayoutBox()->Style()->Resize() != EResize::kNone;
+ bool has_resizer = GetLayoutBox()->CanResize();
if ((has_horizontal_bar && has_vertical_bar) ||
(has_resizer && (has_horizontal_bar || has_vertical_bar))) {
- return CornerRect(GetLayoutBox()->PixelSnappedBorderBoxRect(
- Layer()->SubpixelAccumulation()));
+ return CornerRect();
}
- return IntRect();
+ return gfx::Rect();
+}
+
+void PaintLayerScrollableArea::SetScrollCornerNeedsPaintInvalidation() {
+ ScrollableArea::SetScrollCornerNeedsPaintInvalidation();
}
-IntRect
+gfx::Rect
PaintLayerScrollableArea::ConvertFromScrollbarToContainingEmbeddedContentView(
const Scrollbar& scrollbar,
- const IntRect& scrollbar_rect) const {
+ const gfx::Rect& scrollbar_rect) const {
LayoutView* view = GetLayoutBox()->View();
if (!view)
return scrollbar_rect;
- IntRect rect = scrollbar_rect;
- rect.Move(ScrollbarOffset(scrollbar));
-
- return view->GetFrameView()->ConvertFromLayoutObject(*GetLayoutBox(), rect);
+ gfx::Rect rect = scrollbar_rect;
+ rect.Offset(ScrollbarOffset(scrollbar));
+ return ToPixelSnappedRect(
+ GetLayoutBox()->LocalToAbsoluteRect(PhysicalRect(rect)));
}
-IntPoint
+gfx::Point
PaintLayerScrollableArea::ConvertFromScrollbarToContainingEmbeddedContentView(
const Scrollbar& scrollbar,
- const IntPoint& scrollbar_point) const {
+ const gfx::Point& scrollbar_point) const {
LayoutView* view = GetLayoutBox()->View();
if (!view)
return scrollbar_point;
- IntPoint point = scrollbar_point;
- point.Move(ScrollbarOffset(scrollbar));
- return view->GetFrameView()->ConvertFromLayoutObject(*GetLayoutBox(), point);
+ gfx::Point point = scrollbar_point + ScrollbarOffset(scrollbar);
+ return ToRoundedPoint(
+ GetLayoutBox()->LocalToAbsolutePoint(PhysicalOffset(point)));
}
-IntPoint
+gfx::Point
PaintLayerScrollableArea::ConvertFromContainingEmbeddedContentViewToScrollbar(
const Scrollbar& scrollbar,
- const IntPoint& parent_point) const {
+ const gfx::Point& parent_point) const {
LayoutView* view = GetLayoutBox()->View();
if (!view)
return parent_point;
- IntPoint point = view->GetFrameView()->ConvertToLayoutObject(*GetLayoutBox(),
- parent_point);
-
- point.Move(-ScrollbarOffset(scrollbar));
+ gfx::Point point = ToRoundedPoint(
+ GetLayoutBox()->AbsoluteToLocalPoint(PhysicalOffset(parent_point)));
+ point -= ScrollbarOffset(scrollbar);
return point;
}
-IntPoint PaintLayerScrollableArea::ConvertFromRootFrame(
- const IntPoint& point_in_root_frame) const {
+gfx::Point PaintLayerScrollableArea::ConvertFromRootFrame(
+ const gfx::Point& point_in_root_frame) const {
LayoutView* view = GetLayoutBox()->View();
if (!view)
return point_in_root_frame;
return view->GetFrameView()->ConvertFromRootFrame(point_in_root_frame);
}
+gfx::Point PaintLayerScrollableArea::ConvertFromRootFrameToVisualViewport(
+ const gfx::Point& point_in_root_frame) const {
+ LocalFrameView* frame_view = GetLayoutBox()->GetFrameView();
+ DCHECK(frame_view);
+ const auto* page = frame_view->GetPage();
+ const auto& viewport = page->GetVisualViewport();
+ return viewport.RootFrameToViewport(point_in_root_frame);
+}
+
int PaintLayerScrollableArea::ScrollSize(
ScrollbarOrientation orientation) const {
- IntSize scroll_dimensions =
+ gfx::Vector2d scroll_dimensions =
MaximumScrollOffsetInt() - MinimumScrollOffsetInt();
- return (orientation == kHorizontalScrollbar) ? scroll_dimensions.Width()
- : scroll_dimensions.Height();
+ return (orientation == kHorizontalScrollbar) ? scroll_dimensions.x()
+ : scroll_dimensions.y();
}
void PaintLayerScrollableArea::UpdateScrollOffset(
const ScrollOffset& new_offset,
- ScrollType scroll_type) {
+ mojom::blink::ScrollType scroll_type) {
if (HasBeenDisposed() || GetScrollOffset() == new_offset)
return;
- bool offset_was_zero = scroll_offset_.IsZero();
+ TRACE_EVENT2("blink", "PaintLayerScrollableArea::UpdateScrollOffset", "x",
+ new_offset.x(), "y", new_offset.y());
+ TRACE_EVENT_INSTANT1("blink", "Type", TRACE_EVENT_SCOPE_THREAD, "type",
+ scroll_type);
+
+ LocalFrameView* frame_view = GetLayoutBox()->GetFrameView();
+ CHECK(frame_view);
+
+ // The ScrollOffsetTranslation paint property depends on the scroll offset.
+ // (see: PaintPropertyTreeBuilder::UpdateScrollAndScrollTranslation).
+ // Intersection observation cached rects affected by the scroll are not
+ // invalidated because it's hard to find all of them. Validity of cached
+ // rects is checked in IntersectionGeometry::PrepareComputeGeometry().
+ GetLayoutBox()->SetNeedsPaintPropertyUpdatePreservingCachedRects();
+ frame_view->UpdateIntersectionObservationStateOnScroll(new_offset -
+ scroll_offset_);
+
scroll_offset_ = new_offset;
LocalFrame* frame = GetLayoutBox()->GetFrame();
DCHECK(frame);
- LocalFrameView* frame_view = GetLayoutBox()->GetFrameView();
bool is_root_layer = Layer()->IsRootLayer();
- TRACE_EVENT1("devtools.timeline", "ScrollLayer", "data",
- InspectorScrollLayerEvent::Data(GetLayoutBox()));
-
- // FIXME(420741): Resolve circular dependency between scroll offset and
- // compositing state, and remove this disabler.
- DisableCompositingQueryAsserts disabler;
+ DEVTOOLS_TIMELINE_TRACE_EVENT(
+ "ScrollLayer", inspector_scroll_layer_event::Data, GetLayoutBox());
// Update the positions of our child layers (if needed as only fixed layers
// should be impacted by a scroll).
if (!frame_view->IsInPerformLayout()) {
- // If we're in the middle of layout, we'll just update layers once layout
- // has finished.
- Layer()->UpdateLayerPositionsAfterOverflowScroll();
// Update regions, scrolling may change the clip of a particular region.
frame_view->UpdateDocumentAnnotatedRegions();
else
frame_view->SetNeedsUpdateGeometries();
}
- UpdateCompositingLayersAfterScroll();
- GetLayoutBox()->DispatchFakeMouseMoveEventSoon(frame->GetEventHandler());
+ if (auto* scrolling_coordinator = GetScrollingCoordinator()) {
+ if (!scrolling_coordinator->UpdateCompositorScrollOffset(*frame, *this)) {
+ GetLayoutBox()->GetFrameView()->SetPaintArtifactCompositorNeedsUpdate();
+ }
+ }
- if (scroll_type == kUserScroll || scroll_type == kCompositorScroll) {
+ if (scroll_type == mojom::blink::ScrollType::kUser ||
+ scroll_type == mojom::blink::ScrollType::kCompositor) {
Page* page = frame->GetPage();
if (page)
page->GetChromeClient().ClearToolTip(*frame);
}
- InvalidatePaintForScrollOffsetChange(offset_was_zero);
-
- // The scrollOffsetTranslation paint property depends on the scroll offset.
- // (see: PaintPropertyTreeBuilder::UpdateScrollAndScrollTranslation).
- GetLayoutBox()->SetNeedsPaintPropertyUpdate();
-
- // Schedule the scroll DOM event.
- if (GetLayoutBox()->GetNode()) {
- GetLayoutBox()->GetNode()->GetDocument().EnqueueScrollEventForNode(
- GetLayoutBox()->GetNode());
+ InvalidatePaintForScrollOffsetChange();
+
+ // Don't enqueue a scroll event yet for scroll reasons that are not about
+ // explicit changes to scroll. Instead, only do so at the time of the next
+ // lifecycle update, to avoid scroll events that are out of date or don't
+ // result in an actual scroll that is visible to the user. These scroll events
+ // will then be dispatched at the *subsequent* animation frame, because
+ // they happen after layout and therefore the next opportunity to fire the
+ // events is at the next lifecycle update (*).
+ //
+ // (*) https://html.spec.whatwg.org/C/#update-the-rendering steps
+ if (scroll_type == mojom::blink::ScrollType::kClamping ||
+ scroll_type == mojom::blink::ScrollType::kAnchoring) {
+ if (GetLayoutBox()->GetNode())
+ frame_view->SetNeedsEnqueueScrollEvent(this);
+ } else {
+ EnqueueScrollEventIfNeeded();
}
GetLayoutBox()->View()->ClearHitTestCache();
if (is_root_layer) {
frame_view->GetFrame().Loader().SaveScrollState();
frame_view->DidChangeScrollOffset();
- if (scroll_type == kCompositorScroll || scroll_type == kUserScroll) {
+ if (scroll_type == mojom::blink::ScrollType::kCompositor ||
+ scroll_type == mojom::blink::ScrollType::kUser) {
if (DocumentLoader* document_loader = frame->Loader().GetDocumentLoader())
document_loader->GetInitialScrollState().was_scrolled_by_user = true;
}
}
- if (IsExplicitScrollType(scroll_type)) {
- if (scroll_type != kCompositorScroll)
- ShowOverlayScrollbars();
- frame_view->ClearFragmentAnchor();
+ if (FragmentAnchor* anchor = frame_view->GetFragmentAnchor())
+ anchor->DidScroll(scroll_type);
+
+ if (IsExplicitScrollType(scroll_type) ||
+ scroll_type == mojom::blink::ScrollType::kScrollStart) {
+ ShowNonMacOverlayScrollbars();
GetScrollAnchor()->Clear();
}
-
+ if (ContentCaptureManager* manager = frame_view->GetFrame()
+ .LocalFrameRoot()
+ .GetOrResetContentCaptureManager()) {
+ manager->OnScrollPositionChanged();
+ }
if (AXObjectCache* cache =
GetLayoutBox()->GetDocument().ExistingAXObjectCache())
cache->HandleScrollPositionChanged(GetLayoutBox());
}
-void PaintLayerScrollableArea::InvalidatePaintForScrollOffsetChange(
- bool offset_was_zero) {
- if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) {
- // "background-attachment: local" causes the background of this element to
- // change position due to scroll so a paint invalidation is needed.
- // TODO(pdr): This invalidation can be removed if the local background
- // attachment is painted into the scrolling contents.
- if (ScrollsOverflow() &&
- GetLayoutBox()->Style()->BackgroundLayers().Attachment() ==
- EFillAttachment::kLocal) {
- GetLayoutBox()->SetShouldDoFullPaintInvalidation();
- return;
- }
-
- // TODO(pdr): If this is the root frame, descendants with fixed background
- // attachments need to be invalidated.
-
- // A scroll offset translation is still needed for overflow:hidden and there
- // is an optimization to only create this translation node when scroll
- // offset is non-zero (see: NeedsScrollOrScrollTranslation in
- // PaintPropertyTreeBuilder.cpp). Because of this optimization, gaining or
- // losing scroll offset can change whether a property exists and we have to
- // invalidate paint to ensure this property gets picked up in BlockPainter.
- bool needs_repaint_for_overflow_hidden =
- !ScrollsOverflow() && (offset_was_zero || GetScrollOffset().IsZero());
- // An invalidation is needed to ensure the interest rect is recalculated
- // so newly-scrolled-to items are repainted. We may want to set a flag on
- // PaintLayer to just check for interest rect changes instead of doing a
- // full repaint.
- bool needs_repaint_for_interest_rect = true;
- if (needs_repaint_for_overflow_hidden || needs_repaint_for_interest_rect) {
- Layer()->SetNeedsRepaint();
- return;
- }
+void PaintLayerScrollableArea::InvalidatePaintForScrollOffsetChange() {
+ InvalidatePaintForStickyDescendants();
- return;
+ auto* box = GetLayoutBox();
+ auto* frame_view = box->GetFrameView();
+ frame_view->InvalidateBackgroundAttachmentFixedDescendantsOnScroll(*box);
+ if (!box->BackgroundNeedsFullPaintInvalidation() &&
+ BackgroundNeedsRepaintOnScroll()) {
+ box->SetBackgroundNeedsFullPaintInvalidation();
}
- bool requires_paint_invalidation = true;
-
- LocalFrameView* frame_view = GetLayoutBox()->GetFrameView();
- bool is_root_layer = Layer()->IsRootLayer();
- if (GetLayoutBox()->View()->Compositor()->InCompositingMode()) {
- bool only_scrolled_composited_layers =
- ScrollsOverflow() && Layer()->IsAllScrollingContentComposited() &&
- GetLayoutBox()->Style()->BackgroundLayers().Attachment() !=
- EFillAttachment::kLocal;
-
- if (UsesCompositedScrolling() || only_scrolled_composited_layers)
- requires_paint_invalidation = false;
+ if (auto* compositor = frame_view->GetPaintArtifactCompositor()) {
+ if (compositor->ShouldAlwaysUpdateOnScroll()) {
+ compositor->SetNeedsUpdate();
+ }
}
+}
- if (requires_paint_invalidation || is_root_layer)
- frame_view->InvalidateBackgroundAttachmentFixedDescendants(*GetLayoutBox());
+// See the comment in .h about background-attachment:fixed.
+bool PaintLayerScrollableArea::BackgroundNeedsRepaintOnScroll() const {
+ const auto* box = GetLayoutBox();
+ auto background_paint_location = box->GetBackgroundPaintLocation();
+ bool background_paint_in_border_box =
+ background_paint_location & kBackgroundPaintInBorderBoxSpace;
+ bool background_paint_in_scrolling_contents =
+ background_paint_location & kBackgroundPaintInContentsSpace;
- if (!requires_paint_invalidation && is_root_layer) {
- // Some special invalidations for the root layer.
- if (frame_view->HasViewportConstrainedObjects()) {
- if (!frame_view->InvalidateViewportConstrainedObjects())
- requires_paint_invalidation = true;
- }
- InvalidatePaintForStickyDescendants();
+ const auto& background_layers = box->StyleRef().BackgroundLayers();
+ if (background_layers.AnyLayerHasLocalAttachmentImage() &&
+ background_paint_in_border_box) {
+ // Local-attachment background image scrolls, so needs invalidation if it
+ // paints in non-scrolling space.
+ return true;
}
-
- if (requires_paint_invalidation) {
- GetLayoutBox()->SetShouldDoFullPaintInvalidation();
- GetLayoutBox()->SetMayNeedPaintInvalidationSubtree();
+ if (background_layers.AnyLayerHasDefaultAttachmentImage() &&
+ background_paint_in_scrolling_contents) {
+ // Normal attachment background image doesn't scroll, so needs
+ // invalidation if it paints in scrolling contents.
+ return true;
}
+ if (background_layers.AnyLayerHasLocalAttachment() &&
+ background_layers.AnyLayerUsesContentBox() &&
+ background_paint_in_border_box &&
+ (box->PaddingLeft() || box->PaddingTop() || box->PaddingRight() ||
+ box->PaddingBottom())) {
+ // Local attachment content box background needs invalidation if there is
+ // padding because the content area can change on scroll (e.g. the top
+ // padding can disappear when the box scrolls to the bottom).
+ return true;
+ }
+ return false;
}
-IntSize PaintLayerScrollableArea::ScrollOffsetInt() const {
- return FlooredIntSize(scroll_offset_);
+gfx::Vector2d PaintLayerScrollableArea::ScrollOffsetInt() const {
+ return gfx::ToFlooredVector2d(scroll_offset_);
}
ScrollOffset PaintLayerScrollableArea::GetScrollOffset() const {
return scroll_offset_;
}
-IntSize PaintLayerScrollableArea::MinimumScrollOffsetInt() const {
- return ToIntSize(-ScrollOrigin());
+void PaintLayerScrollableArea::EnqueueScrollEventIfNeeded() {
+ if (scroll_offset_ == last_committed_scroll_offset_ &&
+ has_last_committed_scroll_offset_)
+ return;
+ last_committed_scroll_offset_ = scroll_offset_;
+ has_last_committed_scroll_offset_ = true;
+ if (HasBeenDisposed())
+ return;
+ // Schedule the scroll DOM event.
+ if (auto* node = EventTargetNode())
+ node->GetDocument().EnqueueScrollEventForNode(node);
+}
+
+gfx::Vector2d PaintLayerScrollableArea::MinimumScrollOffsetInt() const {
+ return -ScrollOrigin().OffsetFromOrigin();
}
-IntSize PaintLayerScrollableArea::MaximumScrollOffsetInt() const {
- if (!GetLayoutBox()->HasOverflowClip())
- return ToIntSize(-ScrollOrigin());
+gfx::Vector2d PaintLayerScrollableArea::MaximumScrollOffsetInt() const {
+ if (!GetLayoutBox() || !GetLayoutBox()->IsScrollContainer())
+ return -ScrollOrigin().OffsetFromOrigin();
- IntSize content_size = ContentsSize();
+ gfx::Size content_size = ContentsSize();
Page* page = GetLayoutBox()->GetDocument().GetPage();
DCHECK(page);
// The global root scroller should be clipped by the top LocalFrameView rather
// than it's overflow clipping box. This is to ensure that content exposed by
// hiding the URL bar at the bottom of the screen is visible.
- IntSize visible_size;
+ gfx::Size visible_size;
if (this == controller.RootScrollerArea()) {
visible_size = controller.RootScrollerVisibleArea();
} else {
- visible_size =
- PixelSnappedIntRect(GetLayoutBox()->OverflowClipRect(
- GetLayoutBox()->Location(),
- kIgnorePlatformAndCSSOverlayScrollbarSize))
- .Size();
+ visible_size = ToRoundedSize(
+ GetLayoutBox()
+ ->OverflowClipRect(PhysicalOffset(), kIgnoreOverlayScrollbarSize)
+ .size);
}
// TODO(skobes): We should really ASSERT that contentSize >= visibleSize
// when we are not the root layer, but we can't because contentSize is
// based on stale layout overflow data (http://crbug.com/576933).
- content_size = content_size.ExpandedTo(visible_size);
+ content_size.SetToMax(visible_size);
- return ToIntSize(-ScrollOrigin() + (content_size - visible_size));
+ return -ScrollOrigin().OffsetFromOrigin() +
+ gfx::Vector2d(content_size.width() - visible_size.width(),
+ content_size.height() - visible_size.height());
}
void PaintLayerScrollableArea::VisibleSizeChanged() {
- ShowOverlayScrollbars();
+ ShowNonMacOverlayScrollbars();
}
-LayoutRect PaintLayerScrollableArea::LayoutContentRect(
+PhysicalRect PaintLayerScrollableArea::LayoutContentRect(
IncludeScrollbarsInRect scrollbar_inclusion) const {
// LayoutContentRect is conceptually the same as the box's client rect.
- LayoutSize layer_size(Layer()->Size());
+ PhysicalSize layer_size = Size();
LayoutUnit border_width = GetLayoutBox()->BorderWidth();
LayoutUnit border_height = GetLayoutBox()->BorderHeight();
- LayoutUnit horizontal_scrollbar_height, vertical_scrollbar_width;
- if (scrollbar_inclusion == kExcludeScrollbars) {
- horizontal_scrollbar_height = LayoutUnit(
- HorizontalScrollbar() && !HorizontalScrollbar()->IsOverlayScrollbar()
- ? HorizontalScrollbar()->ScrollbarThickness()
- : 0);
- vertical_scrollbar_width = LayoutUnit(
- VerticalScrollbar() && !VerticalScrollbar()->IsOverlayScrollbar()
- ? VerticalScrollbar()->ScrollbarThickness()
- : 0);
- }
-
- return LayoutRect(
- LayoutPoint(ScrollPosition()),
- LayoutSize(
- layer_size.Width() - border_width - vertical_scrollbar_width,
- layer_size.Height() - border_height - horizontal_scrollbar_height)
- .ExpandedTo(LayoutSize()));
-}
-
-IntRect PaintLayerScrollableArea::VisibleContentRect(
+ PhysicalBoxStrut scrollbars;
+ if (scrollbar_inclusion == kExcludeScrollbars)
+ scrollbars = GetLayoutBox()->ComputeScrollbars();
+
+ PhysicalSize size(
+ layer_size.width - border_width - scrollbars.HorizontalSum(),
+ layer_size.height - border_height - scrollbars.VerticalSum());
+ size.ClampNegativeToZero();
+ return PhysicalRect(PhysicalOffset::FromPointFRound(ScrollPosition()), size);
+}
+
+gfx::Rect PaintLayerScrollableArea::VisibleContentRect(
IncludeScrollbarsInRect scrollbar_inclusion) const {
- LayoutRect layout_content_rect(LayoutContentRect(scrollbar_inclusion));
+ PhysicalRect layout_content_rect(LayoutContentRect(scrollbar_inclusion));
// TODO(szager): It's not clear that Floor() is the right thing to do here;
// what is the correct behavior for fractional scroll offsets?
- return IntRect(FlooredIntPoint(layout_content_rect.Location()),
- PixelSnappedIntSize(layout_content_rect.Size(),
- GetLayoutBox()->Location()));
+ gfx::Size size = ToRoundedSize(layout_content_rect.size);
+ return gfx::Rect(ToFlooredPoint(layout_content_rect.offset), size);
}
-LayoutRect PaintLayerScrollableArea::VisibleScrollSnapportRect(
+PhysicalRect PaintLayerScrollableArea::VisibleScrollSnapportRect(
IncludeScrollbarsInRect scrollbar_inclusion) const {
const ComputedStyle* style = GetLayoutBox()->Style();
- LayoutRect layout_content_rect(LayoutContentRect(scrollbar_inclusion));
- layout_content_rect.MoveBy(LayoutPoint(-ScrollOrigin()));
- LayoutRectOutsets padding(MinimumValueForLength(style->ScrollPaddingTop(),
- layout_content_rect.Height()),
- MinimumValueForLength(style->ScrollPaddingRight(),
- layout_content_rect.Width()),
- MinimumValueForLength(style->ScrollPaddingBottom(),
- layout_content_rect.Height()),
- MinimumValueForLength(style->ScrollPaddingLeft(),
- layout_content_rect.Width()));
+ PhysicalRect layout_content_rect(LayoutContentRect(scrollbar_inclusion));
+ layout_content_rect.Move(PhysicalOffset(-ScrollOrigin().OffsetFromOrigin()));
+ PhysicalBoxStrut padding(MinimumValueForLength(style->ScrollPaddingTop(),
+ layout_content_rect.Height()),
+ MinimumValueForLength(style->ScrollPaddingRight(),
+ layout_content_rect.Width()),
+ MinimumValueForLength(style->ScrollPaddingBottom(),
+ layout_content_rect.Height()),
+ MinimumValueForLength(style->ScrollPaddingLeft(),
+ layout_content_rect.Width()));
layout_content_rect.Contract(padding);
return layout_content_rect;
}
-IntSize PaintLayerScrollableArea::ContentsSize() const {
- return IntSize(PixelSnappedScrollWidth(), PixelSnappedScrollHeight());
+gfx::Size PaintLayerScrollableArea::ContentsSize() const {
+ // We need to take into account of ClientLeft and ClientTop for
+ // PaintLayerScrollableAreaTest.NotScrollsOverflowWithScrollableScrollbar.
+ PhysicalOffset offset(GetLayoutBox()->ClientLeft(),
+ GetLayoutBox()->ClientTop());
+ // TODO(crbug.com/962299): The pixel snapping is incorrect in some cases.
+ return PixelSnappedContentsSize(offset);
+}
+
+gfx::Size PaintLayerScrollableArea::PixelSnappedContentsSize(
+ const PhysicalOffset& paint_offset) const {
+ PhysicalSize size = overflow_rect_.size;
+
+ // If we're capturing a transition snapshot, ensure the content size is
+ // considered at least as large as the container. Otherwise, the snapshot
+ // will be clipped by PendingLayer to the content size.
+ if (IsA<LayoutView>(GetLayoutBox())) {
+ if (auto* transition =
+ ViewTransitionUtils::GetTransition(GetLayoutBox()->GetDocument());
+ transition && transition->IsRootTransitioning()) {
+ PhysicalSize container_size(transition->GetSnapshotRootSize());
+ size.width = std::max(container_size.width, size.width);
+ size.height = std::max(container_size.height, size.height);
+ }
+ }
+
+ return ToPixelSnappedRect(PhysicalRect(paint_offset, size)).size();
}
void PaintLayerScrollableArea::ContentsResized() {
ScrollableArea::ContentsResized();
// Need to update the bounds of the scroll property.
GetLayoutBox()->SetNeedsPaintPropertyUpdate();
+ Layer()->SetNeedsCompositingInputsUpdate();
}
-IntPoint PaintLayerScrollableArea::LastKnownMousePosition() const {
- return GetLayoutBox()->GetFrame() ? GetLayoutBox()
- ->GetFrame()
- ->GetEventHandler()
- .LastKnownMousePositionInRootFrame()
- : IntPoint();
+gfx::Point PaintLayerScrollableArea::LastKnownMousePosition() const {
+ return GetLayoutBox()->GetFrame()
+ ? gfx::ToFlooredPoint(GetLayoutBox()
+ ->GetFrame()
+ ->GetEventHandler()
+ .LastKnownMousePositionInRootFrame())
+ : gfx::Point();
}
bool PaintLayerScrollableArea::ScrollAnimatorEnabled() const {
// are affected by overlay scrollbars.
layer_->GetLayoutObject().SetNeedsPaintPropertyUpdate();
- // TODO(chrishr): this should be able to be removed.
- layer_->ClearClipRects();
-
if (LayoutView* view = GetLayoutBox()->View())
view->ClearHitTestCache();
}
void PaintLayerScrollableArea::ScrollbarFrameRectChanged() {
- // Size of non-overlay scrollbar affects overflow clip rect.
- if (!HasOverlayScrollbars())
- GetLayoutBox()->SetNeedsPaintPropertyUpdate();
+ // TODO(crbug.com/1020913): This should be called only from layout once the
+ // bug is fixed.
+
+ // Size of non-overlay scrollbar affects overflow clip rect. size of overlay
+ // scrollbar effects hit testing rect excluding overlay scrollbars.
+ if (GetDocument()->Lifecycle().GetState() == DocumentLifecycle::kInPrePaint) {
+ // In pre-paint we avoid marking the ancestor chain as this might cause
+ // problems, see https://crbug.com/1377634. Note that we do not have
+ // automated test case for this, so if you when modifying this code, please
+ // verify that the test cases on the bug do not crash.
+ GetLayoutBox()
+ ->GetMutableForPainting()
+ .SetOnlyThisNeedsPaintPropertyUpdate();
+ return;
+ }
+
+ GetLayoutBox()->SetNeedsPaintPropertyUpdate();
}
bool PaintLayerScrollableArea::ScrollbarsCanBeActive() const {
return !!frame_view->GetFrame().GetDocument();
}
-IntRect PaintLayerScrollableArea::ScrollableAreaBoundingBox() const {
- if (LocalFrame* frame = GetLayoutBox()->GetFrame()) {
- if (LocalFrameView* local_root = frame->LocalFrameRoot().View()) {
- return local_root->RootFrameToDocument(frame->View()->ConvertToRootFrame(
- GetLayoutBox()->AbsoluteBoundingBoxRect(0)));
- }
- }
- return IntRect();
-}
-
void PaintLayerScrollableArea::RegisterForAnimation() {
if (HasBeenDisposed())
return;
bool PaintLayerScrollableArea::UserInputScrollable(
ScrollbarOrientation orientation) const {
+ if (orientation == kVerticalScrollbar &&
+ GetLayoutBox()->GetDocument().IsVerticalScrollEnforced()) {
+ return false;
+ }
+
if (GetLayoutBox()->IsIntrinsicallyScrollable(orientation))
return true;
- if (GetLayoutBox()->IsLayoutView()) {
+ if (IsA<LayoutView>(GetLayoutBox())) {
Document& document = GetLayoutBox()->GetDocument();
Element* fullscreen_element = Fullscreen::FullscreenElementFrom(document);
if (fullscreen_element && fullscreen_element != document.documentElement())
return false;
- ScrollbarMode h_mode;
- ScrollbarMode v_mode;
- ToLayoutView(GetLayoutBox())->CalculateScrollbarModes(h_mode, v_mode);
- ScrollbarMode mode =
+ mojom::blink::ScrollbarMode h_mode;
+ mojom::blink::ScrollbarMode v_mode;
+ To<LayoutView>(GetLayoutBox())->CalculateScrollbarModes(h_mode, v_mode);
+ mojom::blink::ScrollbarMode mode =
(orientation == kHorizontalScrollbar) ? h_mode : v_mode;
- return mode == kScrollbarAuto || mode == kScrollbarAlwaysOn;
+ return mode == mojom::blink::ScrollbarMode::kAuto ||
+ mode == mojom::blink::ScrollbarMode::kAlwaysOn;
}
EOverflow overflow_style = (orientation == kHorizontalScrollbar)
- ? GetLayoutBox()->Style()->OverflowX()
- : GetLayoutBox()->Style()->OverflowY();
+ ? GetLayoutBox()->StyleRef().OverflowX()
+ : GetLayoutBox()->StyleRef().OverflowY();
return (overflow_style == EOverflow::kScroll ||
overflow_style == EOverflow::kAuto ||
overflow_style == EOverflow::kOverlay);
// use the snapport rect to calculate the page step instead of the visible
// rect.
// [1] https://drafts.csswg.org/css-scroll-snap/#scroll-padding
- IntSize snapport_size = VisibleScrollSnapportRect().PixelSnappedSize();
- int length = (orientation == kHorizontalScrollbar) ? snapport_size.Width()
- : snapport_size.Height();
+ gfx::Size snapport_size = VisibleScrollSnapportRect().PixelSnappedSize();
+ int length = (orientation == kHorizontalScrollbar) ? snapport_size.width()
+ : snapport_size.height();
int min_page_step = static_cast<float>(length) *
ScrollableArea::MinFractionToStepWhenPaging();
int page_step = max(min_page_step, length - MaxOverlapBetweenPages());
return max(page_step, 1);
}
+bool PaintLayerScrollableArea::IsRootFrameLayoutViewport() const {
+ LocalFrame* frame = GetLayoutBox()->GetFrame();
+ if (!frame || !frame->View())
+ return false;
+
+ RootFrameViewport* root_frame_viewport =
+ frame->View()->GetRootFrameViewport();
+ if (!root_frame_viewport)
+ return false;
+
+ return &root_frame_viewport->LayoutViewport() == this;
+}
+
LayoutBox* PaintLayerScrollableArea::GetLayoutBox() const {
- return layer_->GetLayoutBox();
+ return layer_ ? layer_->GetLayoutBox() : nullptr;
}
PaintLayer* PaintLayerScrollableArea::Layer() const {
- return layer_;
+ return layer_.Get();
+}
+
+PhysicalSize PaintLayerScrollableArea::Size() const {
+ return layer_->IsRootLayer()
+ ? PhysicalSize(GetLayoutBox()->GetFrameView()->Size())
+ : GetLayoutBox()->Size();
}
LayoutUnit PaintLayerScrollableArea::ScrollWidth() const {
return overflow_rect_.Height();
}
-int PaintLayerScrollableArea::PixelSnappedScrollWidth() const {
- return SnapSizeToPixel(ScrollWidth(), GetLayoutBox()->ClientLeft() +
- GetLayoutBox()->Location().X());
-}
-
-int PaintLayerScrollableArea::PixelSnappedScrollHeight() const {
- return SnapSizeToPixel(ScrollHeight(), GetLayoutBox()->ClientTop() +
- GetLayoutBox()->Location().Y());
-}
-
void PaintLayerScrollableArea::UpdateScrollOrigin() {
// This should do nothing prior to first layout; the if-clause will catch
// that.
- if (OverflowRect().IsEmpty())
+ if (overflow_rect_.IsEmpty())
return;
- LayoutRect scrollable_overflow(overflow_rect_);
- scrollable_overflow.Move(-GetLayoutBox()->BorderLeft(),
- -GetLayoutBox()->BorderTop());
- IntPoint new_origin(-scrollable_overflow.PixelSnappedLocation() +
- GetLayoutBox()->OriginAdjustmentForScrollbars());
- if (new_origin != scroll_origin_)
+ PhysicalRect scrollable_overflow = overflow_rect_;
+ scrollable_overflow.Move(-PhysicalOffset(GetLayoutBox()->BorderLeft(),
+ GetLayoutBox()->BorderTop()));
+ gfx::Point new_origin = ToFlooredPoint(-scrollable_overflow.offset) +
+ GetLayoutBox()->OriginAdjustmentForScrollbars();
+ if (new_origin != scroll_origin_) {
scroll_origin_changed_ = true;
+ // ScrollOrigin affects paint offsets of the scrolling contents.
+ GetLayoutBox()->SetSubtreeShouldCheckForPaintInvalidation();
+ }
scroll_origin_ = new_origin;
}
void PaintLayerScrollableArea::UpdateScrollDimensions() {
- LayoutRect new_overflow_rect = GetLayoutBox()->LayoutOverflowRect();
- GetLayoutBox()->FlipForWritingMode(new_overflow_rect);
+ PhysicalRect new_overflow_rect = GetLayoutBox()->PhysicalLayoutOverflowRect();
// The layout viewport can be larger than the document's layout overflow when
// top controls are hidden. Expand the overflow here to ensure that our
// contents size >= visible size.
- new_overflow_rect.Unite(
- LayoutRect(new_overflow_rect.Location(),
- LayoutContentRect(kExcludeScrollbars).Size()));
+ new_overflow_rect.Unite(PhysicalRect(
+ new_overflow_rect.offset, LayoutContentRect(kExcludeScrollbars).size));
- if (overflow_rect_.Size() != new_overflow_rect.Size())
- ContentsResized();
+ bool resized = overflow_rect_.size != new_overflow_rect.size;
overflow_rect_ = new_overflow_rect;
+ if (resized)
+ ContentsResized();
UpdateScrollOrigin();
}
-void PaintLayerScrollableArea::UpdateScrollbarEnabledState() {
+void PaintLayerScrollableArea::UpdateScrollbarEnabledState(
+ bool is_horizontal_scrollbar_frozen,
+ bool is_vertical_scrollbar_frozen) {
bool force_disable =
GetPageScrollbarTheme().ShouldDisableInvisibleScrollbars() &&
ScrollbarsHiddenIfOverlay();
- if (HorizontalScrollbar())
- HorizontalScrollbar()->SetEnabled(HasHorizontalOverflow() &&
- !force_disable);
- if (VerticalScrollbar())
- VerticalScrollbar()->SetEnabled(HasVerticalOverflow() && !force_disable);
+ // Don't update the enabled state of a custom scrollbar if that scrollbar
+ // is frozen. Otherwise re-running the style cascade with the change in
+ // :disabled pseudo state matching for custom scrollbars can cause infinite
+ // loops in layout.
+ if (Scrollbar* horizontal_scrollbar = HorizontalScrollbar()) {
+ if (!horizontal_scrollbar->IsCustomScrollbar() ||
+ !is_horizontal_scrollbar_frozen) {
+ horizontal_scrollbar->SetEnabled(HasHorizontalOverflow() &&
+ !force_disable);
+ }
+ }
+
+ if (Scrollbar* vertical_scrollbar = VerticalScrollbar()) {
+ if (!vertical_scrollbar->IsCustomScrollbar() ||
+ !is_vertical_scrollbar_frozen) {
+ vertical_scrollbar->SetEnabled(HasVerticalOverflow() && !force_disable);
+ }
+ }
}
void PaintLayerScrollableArea::UpdateScrollbarProportions() {
if (Scrollbar* horizontal_scrollbar = HorizontalScrollbar())
- horizontal_scrollbar->SetProportion(VisibleWidth(), ContentsSize().Width());
+ horizontal_scrollbar->SetProportion(VisibleWidth(), ContentsSize().width());
if (Scrollbar* vertical_scrollbar = VerticalScrollbar())
- vertical_scrollbar->SetProportion(VisibleHeight(), ContentsSize().Height());
+ vertical_scrollbar->SetProportion(VisibleHeight(), ContentsSize().height());
}
void PaintLayerScrollableArea::SetScrollOffsetUnconditionally(
const ScrollOffset& offset,
- ScrollType scroll_type) {
+ mojom::blink::ScrollType scroll_type) {
CancelScrollAnimation();
ScrollOffsetChanged(offset, scroll_type);
}
void PaintLayerScrollableArea::UpdateAfterLayout() {
- bool scrollbars_are_frozen =
- (in_overflow_relayout_ && !allow_second_overflow_relayout_) ||
- FreezeScrollbarsScope::ScrollbarsAreFrozen();
- allow_second_overflow_relayout_ = false;
+ InvalidateAllStickyConstraints();
+
+ bool is_horizontal_scrollbar_frozen = IsHorizontalScrollbarFrozen();
+ bool is_vertical_scrollbar_frozen = IsVerticalScrollbarFrozen();
if (NeedsScrollbarReconstruction()) {
- SetHasHorizontalScrollbar(false);
- SetHasVerticalScrollbar(false);
+ RemoveScrollbarsForReconstruction();
+ // In case that DelayScrollOffsetClampScope prevented destruction of the
+ // scrollbars.
+ scrollbar_manager_.DestroyDetachedScrollbars();
}
UpdateScrollDimensions();
+ bool has_resizer = GetLayoutBox()->CanResize();
+ bool resizer_will_change = had_resizer_before_relayout_ != has_resizer;
+ had_resizer_before_relayout_ = has_resizer;
+
bool had_horizontal_scrollbar = HasHorizontalScrollbar();
bool had_vertical_scrollbar = HasVerticalScrollbar();
ComputeScrollbarExistence(needs_horizontal_scrollbar,
needs_vertical_scrollbar);
- // Removing auto scrollbars is a heuristic and can be incorrect if the content
- // size depends on the scrollbar size (e.g., sized with percentages). Removing
- // scrollbars can require two additional layout passes so this is only done on
- // the first layout (!in_overflow_layout).
- if (!in_overflow_relayout_ && !scrollbars_are_frozen &&
+ if (!is_horizontal_scrollbar_frozen && !is_vertical_scrollbar_frozen &&
TryRemovingAutoScrollbars(needs_horizontal_scrollbar,
needs_vertical_scrollbar)) {
needs_horizontal_scrollbar = needs_vertical_scrollbar = false;
- allow_second_overflow_relayout_ = true;
}
bool horizontal_scrollbar_should_change =
needs_vertical_scrollbar != had_vertical_scrollbar;
bool scrollbars_will_change =
- !scrollbars_are_frozen &&
- (horizontal_scrollbar_should_change || vertical_scrollbar_should_change);
+ (horizontal_scrollbar_should_change && !is_horizontal_scrollbar_frozen) ||
+ (vertical_scrollbar_should_change && !is_vertical_scrollbar_frozen);
if (scrollbars_will_change) {
SetHasHorizontalScrollbar(needs_horizontal_scrollbar);
SetHasVerticalScrollbar(needs_vertical_scrollbar);
- if (HasScrollbar())
- UpdateScrollCornerStyle();
+ // If we change scrollbars on the layout viewport, the visual viewport
+ // needs to update paint properties to account for the correct
+ // scrollbounds.
+ if (LocalFrameView* frame_view = GetLayoutBox()->GetFrameView()) {
+ VisualViewport& visual_viewport =
+ GetLayoutBox()->GetFrame()->GetPage()->GetVisualViewport();
+ if (this == frame_view->LayoutViewport() &&
+ visual_viewport.IsActiveViewport()) {
+ visual_viewport.SetNeedsPaintPropertyUpdate();
+ }
+ }
+
+ UpdateScrollCornerStyle();
Layer()->UpdateSelfPaintingLayer();
GetLayoutBox()->GetDocument().SetAnnotatedRegionsDirty(true);
// Our proprietary overflow: overlay value doesn't trigger a layout.
- // If the box is managed by LayoutNG, don't go here. We don't want to
- // re-enter the NG layout algorithm for this box from here.
if (((horizontal_scrollbar_should_change &&
- GetLayoutBox()->Style()->OverflowX() != EOverflow::kOverlay) ||
+ GetLayoutBox()->StyleRef().OverflowX() != EOverflow::kOverlay) ||
(vertical_scrollbar_should_change &&
- GetLayoutBox()->Style()->OverflowY() != EOverflow::kOverlay)) &&
- !IsManagedByLayoutNG(*GetLayoutBox())) {
+ GetLayoutBox()->StyleRef().OverflowY() != EOverflow::kOverlay))) {
if ((vertical_scrollbar_should_change &&
GetLayoutBox()->IsHorizontalWritingMode()) ||
(horizontal_scrollbar_should_change &&
!GetLayoutBox()->IsHorizontalWritingMode())) {
- GetLayoutBox()->SetPreferredLogicalWidthsDirty();
- }
- if (PreventRelayoutScope::RelayoutIsPrevented()) {
- // We're not doing re-layout right now, but we still want to
- // add the scrollbar to the logical width now, to facilitate parent
- // layout.
- GetLayoutBox()->UpdateLogicalWidth();
- PreventRelayoutScope::SetBoxNeedsLayout(*this, had_horizontal_scrollbar,
- had_vertical_scrollbar);
- } else {
- in_overflow_relayout_ = true;
- SubtreeLayoutScope layout_scope(*GetLayoutBox());
- layout_scope.SetNeedsLayout(
- GetLayoutBox(), LayoutInvalidationReason::kScrollbarChanged);
- if (GetLayoutBox()->IsLayoutBlock()) {
- LayoutBlock* block = ToLayoutBlock(GetLayoutBox());
- block->ScrollbarsChanged(horizontal_scrollbar_should_change,
- vertical_scrollbar_should_change);
- block->UpdateBlockLayout(true);
- } else {
- GetLayoutBox()->UpdateLayout();
- }
- in_overflow_relayout_ = false;
- scrollbar_manager_.DestroyDetachedScrollbars();
- }
- LayoutObject* parent = GetLayoutBox()->Parent();
- if (parent && parent->IsFlexibleBox()) {
- ToLayoutFlexibleBox(parent)->ClearCachedMainSizeForChild(
- *GetLayoutBox());
+ GetLayoutBox()->SetIntrinsicLogicalWidthsDirty();
}
+ // Just update the rectangles, in case scrollbars were added or
+ // removed. The calling code on the layout side has its own scrollbar
+ // change detection mechanism.
+ UpdateScrollDimensions();
}
+ } else if (!HasScrollbar() && resizer_will_change) {
+ Layer()->DirtyStackingContextZOrderLists();
}
- {
- // Hits in
- // compositing/overflow/automatically-opt-into-composited-scrolling-after-style-change.html.
- DisableCompositingQueryAsserts disabler;
+ if (RuntimeEnabledFeatures::LayoutNewSnapLogicEnabled()) {
+ EnqueueForSnapUpdateIfNeeded();
+ } else {
+ // The snap container data will be updated at the end of the layout update.
+ // If the data changes, then this will try to re-snap.
+ SetSnapContainerDataNeedsUpdate(true);
+ }
- UpdateScrollbarEnabledState();
+ {
+ UpdateScrollbarEnabledState(is_horizontal_scrollbar_frozen,
+ is_vertical_scrollbar_frozen);
UpdateScrollbarProportions();
}
- if (!scrollbars_are_frozen && HasOverlayScrollbars()) {
- if (!ScrollSize(kHorizontalScrollbar))
- SetHasHorizontalScrollbar(false);
- if (!ScrollSize(kVerticalScrollbar))
- SetHasVerticalScrollbar(false);
+ hypothetical_horizontal_scrollbar_thickness_ = 0;
+ if (NeedsHypotheticalScrollbarThickness(kHorizontalScrollbar)) {
+ hypothetical_horizontal_scrollbar_thickness_ =
+ ComputeHypotheticalScrollbarThickness(kHorizontalScrollbar, true);
+ }
+ hypothetical_vertical_scrollbar_thickness_ = 0;
+ if (NeedsHypotheticalScrollbarThickness(kVerticalScrollbar)) {
+ hypothetical_vertical_scrollbar_thickness_ =
+ ComputeHypotheticalScrollbarThickness(kVerticalScrollbar, true);
}
- ClampScrollOffsetAfterOverflowChange();
+ DelayableClampScrollOffsetAfterOverflowChange();
- if (!scrollbars_are_frozen) {
+ if (!is_horizontal_scrollbar_frozen || !is_vertical_scrollbar_frozen)
UpdateScrollableAreaSet();
- }
- DisableCompositingQueryAsserts disabler;
PositionOverflowControls();
+
+ if (RuntimeEnabledFeatures::CSSScrollStartEnabled()) {
+ if (IsApplyingScrollStart()) {
+ ApplyScrollStart();
+ }
+ }
+}
+
+Element* PaintLayerScrollableArea::GetElementForScrollStart() const {
+ if (!GetLayoutBox()) {
+ return nullptr;
+ }
+
+ const LayoutBox* box = GetLayoutBox();
+ if (auto* element = DynamicTo<Element>(box->GetNode())) {
+ return element;
+ }
+
+ Node* node = box->GetNode();
+ if (!node && box->Parent() && box->Parent()->IsFieldset()) {
+ return DynamicTo<Element>(box->Parent()->GetNode());
+ }
+
+ if (node && node->IsDocumentNode()) {
+ return GetLayoutBox()->GetDocument().documentElement();
+ }
+
+ return nullptr;
+}
+
+void PaintLayerScrollableArea::SetShouldCheckForPaintInvalidation() {
+ LayoutBox& box = *GetLayoutBox();
+ // This function may be called during pre-paint, and in such cases we cannot
+ // mark the ancestry for paint invalidation checking, since we may already be
+ // done with those objects, and never get to visit them again.
+ if (GetLayoutBox()->GetDocument().Lifecycle().GetState() ==
+ DocumentLifecycle::DocumentLifecycle::kInPrePaint) {
+ box.GetMutableForPainting().SetShouldCheckForPaintInvalidation();
+ } else {
+ box.SetShouldCheckForPaintInvalidation();
+ }
+}
+
+bool PaintLayerScrollableArea::IsApplyingScrollStart() const {
+ if (Element* element = GetElementForScrollStart()) {
+ if (element->HasBeenExplicitlyScrolled()) {
+ return false;
+ }
+ if (GetScrollStartTargets()) {
+ return true;
+ }
+ return !ScrollStartIsDefault();
+ }
+ return false;
+}
+
+void PaintLayerScrollableArea::StopApplyingScrollStart() {
+ if (Element* element = GetElementForScrollStart()) {
+ element->SetHasBeenExplicitlyScrolled();
+ }
+}
+
+void PaintLayerScrollableArea::DelayableClampScrollOffsetAfterOverflowChange() {
+ if (HasBeenDisposed())
+ return;
+ if (DelayScrollOffsetClampScope::ClampingIsDelayed()) {
+ DelayScrollOffsetClampScope::SetNeedsClamp(this);
+ return;
+ }
+ ClampScrollOffsetAfterOverflowChangeInternal();
}
void PaintLayerScrollableArea::ClampScrollOffsetAfterOverflowChange() {
+ ClampScrollOffsetAfterOverflowChangeInternal();
+}
+
+void PaintLayerScrollableArea::ClampScrollOffsetAfterOverflowChangeInternal() {
if (HasBeenDisposed())
return;
// did not change, but the scroll origin *did* change, we still need to notify
// the scrollbars to update their dimensions.
- if (DelayScrollOffsetClampScope::ClampingIsDelayed()) {
- DelayScrollOffsetClampScope::SetNeedsClamp(this);
- return;
+ const Document& document = GetLayoutBox()->GetDocument();
+ if (document.IsPrintingOrPaintingPreview()) {
+ // Scrollable elements may change size when generating layout for printing,
+ // which may require them to change the scroll position in order to keep the
+ // same content within view. In vertical-rl writing-mode, even the root
+ // frame may be attempted scrolled, because a viewport size change may
+ // affect scroll origin. Save all scroll offsets before clamping, so that
+ // everything can be restored the way it was after printing.
+ if (Node* node = EventTargetNode())
+ document.GetFrame()->EnsureSaveScrollOffset(*node);
}
UpdateScrollDimensions();
- if (ScrollOriginChanged())
+ if (ScrollOriginChanged()) {
SetScrollOffsetUnconditionally(ClampScrollOffset(GetScrollOffset()));
- else
- ScrollableArea::SetScrollOffset(GetScrollOffset(), kClampingScroll);
+ } else {
+ ScrollableArea::SetScrollOffset(GetScrollOffset(),
+ mojom::blink::ScrollType::kClamping);
+ }
SetNeedsScrollOffsetClamp(false);
ResetScrollOriginChanged();
// Being the global root scroller will affect clipping size due to browser
// controls behavior so we need to update compositing based on updated clip
// geometry.
- if (GetLayoutBox()->GetNode()->IsElementNode()) {
- ToElement(GetLayoutBox()->GetNode())->SetNeedsCompositingUpdate();
- GetLayoutBox()->SetNeedsPaintPropertyUpdate();
- }
+ Layer()->SetNeedsCompositingInputsUpdate();
+ GetLayoutBox()->SetNeedsPaintPropertyUpdate();
// On Android, where the VisualViewport supplies scrollbars, we need to
// remove the PLSA's scrollbars if we become the global root scroller.
SetHasHorizontalScrollbar(needs_horizontal_scrollbar);
SetHasVerticalScrollbar(needs_vertical_scrollbar);
}
+
+ // Recalculate the snap container data since the scrolling behaviour for this
+ // layout box changed (i.e. it either became the layout viewport or it
+ // is no longer the layout viewport).
+ if (RuntimeEnabledFeatures::LayoutNewSnapLogicEnabled()) {
+ if (!GetLayoutBox()->NeedsLayout()) {
+ EnqueueForSnapUpdateIfNeeded();
+ }
+ } else {
+ SetSnapContainerDataNeedsUpdate(true);
+ }
}
bool PaintLayerScrollableArea::ShouldPerformScrollAnchoring() const {
return scroll_anchor_.HasScroller() && GetLayoutBox() &&
- GetLayoutBox()->Style()->OverflowAnchor() != EOverflowAnchor::kNone &&
+ GetLayoutBox()->StyleRef().OverflowAnchor() !=
+ EOverflowAnchor::kNone &&
!GetLayoutBox()->GetDocument().FinishingOrIsPrinting();
}
scroll_anchor_.RestoreAnchor(serialized_anchor);
}
-FloatQuad PaintLayerScrollableArea::LocalToVisibleContentQuad(
- const FloatQuad& quad,
+gfx::QuadF PaintLayerScrollableArea::LocalToVisibleContentQuad(
+ const gfx::QuadF& quad,
const LayoutObject* local_object,
MapCoordinatesFlags flags) const {
LayoutBox* box = GetLayoutBox();
return GetLayoutBox()->GetFrame()->GetTaskRunner(TaskType::kInternalDefault);
}
-ScrollBehavior PaintLayerScrollableArea::ScrollBehaviorStyle() const {
- return GetLayoutBox()->Style()->GetScrollBehavior();
+mojom::blink::ScrollBehavior PaintLayerScrollableArea::ScrollBehaviorStyle()
+ const {
+ return GetLayoutBox()->StyleRef().GetScrollBehavior();
+}
+
+mojom::blink::ColorScheme PaintLayerScrollableArea::UsedColorSchemeScrollbars()
+ const {
+ if (RuntimeEnabledFeatures::UsedColorSchemeRootScrollbarsEnabled() &&
+ GetLayoutBox()->IsGlobalRootScroller() &&
+ !GetPageScrollbarTheme().UsesOverlayScrollbars()) {
+ const Document& document = GetLayoutBox()->GetDocument();
+ if (document.documentElement() &&
+ document.documentElement()->GetComputedStyle() &&
+ document.documentElement()->GetComputedStyle()->ColorScheme().empty() &&
+ document.GetStyleEngine().GetPageColorSchemes() ==
+ static_cast<ColorSchemeFlags>(ColorSchemeFlag::kNormal) &&
+ document.GetPreferredColorScheme() ==
+ mojom::blink::PreferredColorScheme::kDark) {
+ return mojom::blink::ColorScheme::kDark;
+ }
+ }
+
+ return GetLayoutBox()->StyleRef().UsedColorScheme();
}
bool PaintLayerScrollableArea::HasHorizontalOverflow() const {
// converse problem seems to happen much less frequently in practice, so we
// bias the logic towards preventing unwanted horizontal scrollbars, which
// are more common and annoying.
- LayoutUnit client_width =
- LayoutContentRect(kIncludeScrollbars).Width() -
- VerticalScrollbarWidth(kIgnorePlatformAndCSSOverlayScrollbarSize);
+ LayoutUnit client_width = LayoutContentRect(kIncludeScrollbars).Width() -
+ VerticalScrollbarWidth(kIgnoreOverlayScrollbarSize);
if (NeedsRelayout() && !HadVerticalScrollbarBeforeRelayout())
client_width += VerticalScrollbarWidth();
- LayoutUnit scroll_width(ScrollWidth());
- LayoutUnit box_x = GetLayoutBox()->Location().X();
- return SnapSizeToPixel(scroll_width, box_x) >
- SnapSizeToPixel(client_width, box_x);
+ return ScrollWidth().Round() > client_width.Round();
}
bool PaintLayerScrollableArea::HasVerticalOverflow() const {
LayoutUnit client_height =
LayoutContentRect(kIncludeScrollbars).Height() -
- HorizontalScrollbarHeight(kIgnorePlatformAndCSSOverlayScrollbarSize);
- LayoutUnit scroll_height(ScrollHeight());
- LayoutUnit box_y = GetLayoutBox()->Location().Y();
- return SnapSizeToPixel(scroll_height, box_y) >
- SnapSizeToPixel(client_height, box_y);
+ HorizontalScrollbarHeight(kIgnoreOverlayScrollbarSize);
+ return ScrollHeight().Round() > client_height.Round();
+}
+
+#if BUILDFLAG(IS_TIZEN_TV)
+void PaintLayerScrollableArea::ThumbPartFocusChanged(
+ ScrollbarOrientation orientation,
+ bool focused) {
+ if (LocalFrame* frame = GetLayoutBox()->GetFrame()) {
+ if (frame->GetSettings() &&
+ frame->GetSettings()->GetUseScrollbarThumbFocusNotifications())
+ frame->Client()->ThumbPartFocusChanged(orientation, focused);
+ }
}
+#endif
// This function returns true if the given box requires overflow scrollbars (as
-// opposed to the 'viewport' scrollbars managed by the PaintLayerCompositor).
-// FIXME: we should use the same scrolling machinery for both the viewport and
-// overflow. Currently, we need to avoid producing scrollbars here if they'll be
-// handled externally in the RLC.
+// opposed to the viewport scrollbars managed by VisualViewport).
static bool CanHaveOverflowScrollbars(const LayoutBox& box) {
return box.GetDocument().ViewportDefiningElement() != box.GetNode();
}
void PaintLayerScrollableArea::UpdateAfterStyleChange(
const ComputedStyle* old_style) {
// Don't do this on first style recalc, before layout has ever happened.
- if (!OverflowRect().Size().IsZero()) {
+ if (!overflow_rect_.size.IsZero())
UpdateScrollableAreaSet();
- }
- // Whenever background changes on the scrollable element, the scroll bar
- // overlay style might need to be changed to have contrast against the
- // background.
- // Skip the need scrollbar check, because we dont know do we need a scrollbar
- // when this method get called.
- Color old_background;
- if (old_style) {
- old_background =
- old_style->VisitedDependentColor(GetCSSPropertyBackgroundColor());
- }
- Color new_background = GetLayoutBox()->Style()->VisitedDependentColor(
- GetCSSPropertyBackgroundColor());
+ UpdateResizerStyle(old_style);
+
+ // The scrollbar overlay color theme depends on styles such as the background
+ // color and the used color scheme.
+ RecalculateScrollbarOverlayColorTheme();
- if (new_background != old_background) {
- RecalculateScrollbarOverlayColorTheme(new_background);
+ if (NeedsScrollbarReconstruction()) {
+ RemoveScrollbarsForReconstruction();
+ return;
}
bool needs_horizontal_scrollbar;
bool needs_vertical_scrollbar;
- // We add auto scrollbars only during layout to prevent spurious activations.
ComputeScrollbarExistence(needs_horizontal_scrollbar,
- needs_vertical_scrollbar, kForbidAddingAutoBars);
+ needs_vertical_scrollbar, kOverflowIndependent);
// Avoid some unnecessary computation if there were and will be no scrollbars.
if (!HasScrollbar() && !needs_horizontal_scrollbar &&
!needs_vertical_scrollbar)
return;
- bool horizontal_scrollbar_changed =
- SetHasHorizontalScrollbar(needs_horizontal_scrollbar);
- bool vertical_scrollbar_changed =
- SetHasVerticalScrollbar(needs_vertical_scrollbar);
+ SetHasHorizontalScrollbar(needs_horizontal_scrollbar);
+ SetHasVerticalScrollbar(needs_vertical_scrollbar);
- if (GetLayoutBox()->IsLayoutBlock() &&
- (horizontal_scrollbar_changed || vertical_scrollbar_changed)) {
- ToLayoutBlock(GetLayoutBox())
- ->ScrollbarsChanged(horizontal_scrollbar_changed,
- vertical_scrollbar_changed,
- LayoutBlock::ScrollbarChangeContext::kStyleChange);
- }
-
- // With overflow: scroll, scrollbars are always visible but may be disabled.
- // When switching to another value, we need to re-enable them (see bug 11985).
- if (HasHorizontalScrollbar() && old_style &&
- old_style->OverflowX() == EOverflow::kScroll &&
- GetLayoutBox()->Style()->OverflowX() != EOverflow::kScroll) {
- HorizontalScrollbar()->SetEnabled(true);
- }
-
- if (HasVerticalScrollbar() && old_style &&
- old_style->OverflowY() == EOverflow::kScroll &&
- GetLayoutBox()->Style()->OverflowY() != EOverflow::kScroll) {
- VerticalScrollbar()->SetEnabled(true);
- }
-
- // FIXME: Need to detect a swap from custom to native scrollbars (and vice
- // versa).
if (HorizontalScrollbar())
HorizontalScrollbar()->StyleChanged();
if (VerticalScrollbar())
VerticalScrollbar()->StyleChanged();
UpdateScrollCornerStyle();
- UpdateResizerAreaSet();
- UpdateResizerStyle(old_style);
-}
-void PaintLayerScrollableArea::UpdateAfterCompositingChange() {
- Layer()->UpdateScrollingStateAfterCompositingChange();
+ if (!old_style ||
+ old_style->UsedColorScheme() != UsedColorSchemeScrollbars() ||
+ old_style->ScrollbarThumbColorResolved() !=
+ GetLayoutBox()->StyleRef().ScrollbarThumbColorResolved() ||
+ old_style->ScrollbarTrackColorResolved() !=
+ GetLayoutBox()->StyleRef().ScrollbarTrackColorResolved()) {
+ SetScrollControlsNeedFullPaintInvalidation();
+ }
}
void PaintLayerScrollableArea::UpdateAfterOverflowRecalc() {
UpdateScrollDimensions();
UpdateScrollbarProportions();
+ UpdateScrollbarEnabledState();
bool needs_horizontal_scrollbar;
bool needs_vertical_scrollbar;
(GetLayoutBox()->HasAutoVerticalScrollbar() &&
vertical_scrollbar_should_change)) {
GetLayoutBox()->SetNeedsLayoutAndFullPaintInvalidation(
- LayoutInvalidationReason::kUnknown);
+ layout_invalidation_reason::kUnknown);
}
ClampScrollOffsetAfterOverflowChange();
+ UpdateScrollableAreaSet();
}
-IntRect PaintLayerScrollableArea::RectForHorizontalScrollbar(
- const IntRect& border_box_rect) const {
+gfx::Rect PaintLayerScrollableArea::RectForHorizontalScrollbar() const {
if (!HasHorizontalScrollbar())
- return IntRect();
+ return gfx::Rect();
- const IntRect& scroll_corner = ScrollCornerRect();
-
- return IntRect(
- HorizontalScrollbarStart(border_box_rect.X()),
- border_box_rect.MaxY() - GetLayoutBox()->BorderBottom().ToInt() -
+ const gfx::Rect& scroll_corner = ScrollCornerRect();
+ gfx::Size border_box_size = PixelSnappedBorderBoxSize();
+ return gfx::Rect(
+ HorizontalScrollbarStart(),
+ border_box_size.height() - GetLayoutBox()->BorderBottom().ToInt() -
HorizontalScrollbar()->ScrollbarThickness(),
- border_box_rect.Width() -
+ border_box_size.width() -
(GetLayoutBox()->BorderLeft() + GetLayoutBox()->BorderRight())
.ToInt() -
- scroll_corner.Width(),
+ scroll_corner.width(),
HorizontalScrollbar()->ScrollbarThickness());
}
-IntRect PaintLayerScrollableArea::RectForVerticalScrollbar(
- const IntRect& border_box_rect) const {
+gfx::Rect PaintLayerScrollableArea::RectForVerticalScrollbar() const {
if (!HasVerticalScrollbar())
- return IntRect();
-
- const IntRect& scroll_corner = ScrollCornerRect();
+ return gfx::Rect();
- return IntRect(
- VerticalScrollbarStart(border_box_rect.X(), border_box_rect.MaxX()),
- border_box_rect.Y() + GetLayoutBox()->BorderTop().ToInt(),
+ const gfx::Rect& scroll_corner = ScrollCornerRect();
+ return gfx::Rect(
+ VerticalScrollbarStart(), GetLayoutBox()->BorderTop().ToInt(),
VerticalScrollbar()->ScrollbarThickness(),
- border_box_rect.Height() -
+ PixelSnappedBorderBoxSize().height() -
(GetLayoutBox()->BorderTop() + GetLayoutBox()->BorderBottom())
.ToInt() -
- scroll_corner.Height());
+ scroll_corner.height());
}
-int PaintLayerScrollableArea::VerticalScrollbarStart(int min_x,
- int max_x) const {
+int PaintLayerScrollableArea::VerticalScrollbarStart() const {
if (GetLayoutBox()->ShouldPlaceBlockDirectionScrollbarOnLogicalLeft())
- return min_x + GetLayoutBox()->BorderLeft().ToInt();
- return max_x - GetLayoutBox()->BorderRight().ToInt() -
+ return GetLayoutBox()->BorderLeft().ToInt();
+ return PixelSnappedBorderBoxSize().width() -
+ GetLayoutBox()->BorderRight().ToInt() -
VerticalScrollbar()->ScrollbarThickness();
}
-int PaintLayerScrollableArea::HorizontalScrollbarStart(int min_x) const {
- int x = min_x + GetLayoutBox()->BorderLeft().ToInt();
- if (GetLayoutBox()->ShouldPlaceBlockDirectionScrollbarOnLogicalLeft())
- x += HasVerticalScrollbar()
- ? VerticalScrollbar()->ScrollbarThickness()
- : ResizerCornerRect(GetLayoutBox()->PixelSnappedBorderBoxRect(
- Layer()->SubpixelAccumulation()),
- kResizerForPointer)
- .Width();
+int PaintLayerScrollableArea::HorizontalScrollbarStart() const {
+ int x = GetLayoutBox()->BorderLeft().ToInt();
+ if (GetLayoutBox()->ShouldPlaceBlockDirectionScrollbarOnLogicalLeft()) {
+ x += HasVerticalScrollbar() ? VerticalScrollbar()->ScrollbarThickness()
+ : ResizerCornerRect(kResizerForPointer).width();
+ }
return x;
}
-IntSize PaintLayerScrollableArea::ScrollbarOffset(
+gfx::Vector2d PaintLayerScrollableArea::ScrollbarOffset(
const Scrollbar& scrollbar) const {
// TODO(szager): Factor out vertical offset calculation into other methods,
// for symmetry with *ScrollbarStart methods for horizontal offset.
if (&scrollbar == VerticalScrollbar()) {
- return IntSize(
- VerticalScrollbarStart(0, Layer()->PixelSnappedSize().Width()),
- GetLayoutBox()->BorderTop().ToInt());
+ return gfx::Vector2d(VerticalScrollbarStart(),
+ GetLayoutBox()->BorderTop().ToInt());
}
if (&scrollbar == HorizontalScrollbar()) {
- return IntSize(HorizontalScrollbarStart(0),
- GetLayoutBox()->BorderTop().ToInt() +
- VisibleContentRect(kIncludeScrollbars).Height() -
- HorizontalScrollbar()->ScrollbarThickness());
+ return gfx::Vector2d(HorizontalScrollbarStart(),
+ GetLayoutBox()->BorderTop().ToInt() +
+ VisibleContentRect(kIncludeScrollbars).height() -
+ HorizontalScrollbar()->ScrollbarThickness());
}
NOTREACHED();
- return IntSize();
+ return gfx::Vector2d();
}
static inline const LayoutObject& ScrollbarStyleSource(
const LayoutBox& layout_box) {
- if (layout_box.IsLayoutView()) {
+ if (IsA<LayoutView>(layout_box)) {
Document& doc = layout_box.GetDocument();
+
+ // If the layout box uses standard scrollbar styles use it as the style
+ // source.
+ if (layout_box.StyleRef().UsesStandardScrollbarStyle()) {
+ return layout_box;
+ }
+
+ // Legacy custom scrollbar styles on the document element or the <body> may
+ // apply to the viewport scrollbars. We don't propagate these styles to
+ // LayoutView in StyleResolver like we do for the standard CSS scrollbar
+ // styles because some conditions can only be checked here.
if (Settings* settings = doc.GetSettings()) {
+ LocalFrame* frame = layout_box.GetFrame();
+ DCHECK(frame);
+ DCHECK(frame->GetPage());
+
+ VisualViewport& viewport = frame->GetPage()->GetVisualViewport();
if (!settings->GetAllowCustomScrollbarInMainFrame() &&
- layout_box.GetFrame() && layout_box.GetFrame()->IsMainFrame())
+ frame->IsMainFrame() && viewport.IsActiveViewport()) {
return layout_box;
+ }
}
- // Try the <body> element first as a scrollbar source, but only if the body
+ // Try the <body> element as a scrollbar source, but only if the body
// can scroll.
Element* body = doc.body();
if (body && body->GetLayoutObject() && body->GetLayoutObject()->IsBox() &&
- body->GetLayoutObject()->Style()->HasPseudoStyle(kPseudoIdScrollbar))
+ body->GetLayoutObject()->StyleRef().HasCustomScrollbarStyle())
return *body->GetLayoutObject();
// If the <body> didn't have a custom style, then the root element might.
Element* doc_element = doc.documentElement();
if (doc_element && doc_element->GetLayoutObject() &&
- doc_element->GetLayoutObject()->Style()->HasPseudoStyle(
- kPseudoIdScrollbar))
+ doc_element->GetLayoutObject()->StyleRef().HasCustomScrollbarStyle() &&
+ !layout_box.StyleRef().UsesStandardScrollbarStyle()) {
return *doc_element->GetLayoutObject();
+ }
+ } else if (!layout_box.GetNode() && layout_box.Parent()) {
+ return *layout_box.Parent();
}
return layout_box;
}
int PaintLayerScrollableArea::HypotheticalScrollbarThickness(
+ ScrollbarOrientation orientation,
+ bool should_include_overlay_thickness) const {
+ DCHECK(NeedsHypotheticalScrollbarThickness(orientation));
+ // The cached values are updated after layout, use them if we're layout clean.
+ if (should_include_overlay_thickness &&
+ GetLayoutBox()->GetDocument().Lifecycle().GetState() >=
+ DocumentLifecycle::kLayoutClean) {
+ return orientation == kHorizontalScrollbar
+ ? hypothetical_horizontal_scrollbar_thickness_
+ : hypothetical_vertical_scrollbar_thickness_;
+ }
+ return ComputeHypotheticalScrollbarThickness(
+ orientation, should_include_overlay_thickness);
+}
+
+// Hypothetical scrollbar thickness is computed and cached during layout, but
+// only as needed to avoid a performance penalty. It is needed for every
+// LayoutView, to support frame view auto-sizing; and it's needed whenever CSS
+// scrollbar-gutter requires it.
+bool PaintLayerScrollableArea::NeedsHypotheticalScrollbarThickness(
ScrollbarOrientation orientation) const {
+ return GetLayoutBox()->IsLayoutView() ||
+ GetLayoutBox()->HasScrollbarGutters(orientation);
+}
+
+int PaintLayerScrollableArea::ComputeHypotheticalScrollbarThickness(
+ ScrollbarOrientation orientation,
+ bool should_include_overlay_thickness) const {
Scrollbar* scrollbar = orientation == kHorizontalScrollbar
? HorizontalScrollbar()
: VerticalScrollbar();
return scrollbar->ScrollbarThickness();
const LayoutObject& style_source = ScrollbarStyleSource(*GetLayoutBox());
- bool has_custom_scrollbar_style =
- style_source.StyleRef().HasPseudoStyle(kPseudoIdScrollbar);
- if (has_custom_scrollbar_style) {
- return LayoutScrollbar::HypotheticalScrollbarThickness(
- orientation, *GetLayoutBox(), style_source);
+ if (style_source.StyleRef().HasCustomScrollbarStyle()) {
+ return CustomScrollbar::HypotheticalScrollbarThickness(this, orientation,
+ &style_source);
}
- ScrollbarControlSize scrollbar_size = kRegularScrollbar;
- if (style_source.StyleRef().HasAppearance()) {
- scrollbar_size = LayoutTheme::GetTheme().ScrollbarControlSizeForPart(
- style_source.StyleRef().Appearance());
- }
ScrollbarTheme& theme = GetPageScrollbarTheme();
- if (theme.UsesOverlayScrollbars())
+ if (theme.UsesOverlayScrollbars() && !should_include_overlay_thickness)
return 0;
- int thickness = theme.ScrollbarThickness(scrollbar_size);
- return GetLayoutBox()
- ->GetDocument()
- .GetPage()
- ->GetChromeClient()
- .WindowToViewportScalar(thickness);
+ return theme.ScrollbarThickness(ScaleFromDIP(),
+ style_source.StyleRef().ScrollbarWidth());
}
bool PaintLayerScrollableArea::NeedsScrollbarReconstruction() const {
const LayoutObject& style_source = ScrollbarStyleSource(*GetLayoutBox());
bool needs_custom =
- style_source.IsBox() &&
- style_source.StyleRef().HasPseudoStyle(kPseudoIdScrollbar);
+ style_source.IsBox() && style_source.StyleRef().HasCustomScrollbarStyle();
Scrollbar* scrollbars[] = {HorizontalScrollbar(), VerticalScrollbar()};
if (scrollbar->IsCustomScrollbar() != needs_custom)
return true;
- if (needs_custom) {
- DCHECK(scrollbar->IsCustomScrollbar());
- // We have a custom scrollbar with a stale m_owner.
- if (ToLayoutScrollbar(scrollbar)->StyleSource()->GetLayoutObject() !=
- style_source) {
- return true;
- }
+ // We have a scrollbar with a stale style source.
+ if (scrollbar->StyleSource() != style_source) {
+ return true;
+ }
+ if (needs_custom) {
// Should use custom scrollbar and nothing should change.
continue;
}
if (current_theme != &scrollbar->GetTheme())
return true;
+
+ EScrollbarWidth current_width = scrollbar->CSSScrollbarWidth();
+ if (current_width != style_source.StyleRef().ScrollbarWidth()) {
+ return true;
+ }
}
return false;
}
DCHECK(GetLayoutBox()->GetFrame()->GetSettings());
if (VisualViewportSuppliesScrollbars() ||
!CanHaveOverflowScrollbars(*GetLayoutBox()) ||
- GetLayoutBox()->GetFrame()->GetSettings()->GetHideScrollbars()) {
+ GetLayoutBox()->GetFrame()->GetSettings()->GetHideScrollbars() ||
+ GetLayoutBox()->IsFieldset() || GetLayoutBox()->IsFrameSet() ||
+ GetLayoutBox()->StyleRef().ScrollbarWidth() == EScrollbarWidth::kNone) {
needs_horizontal_scrollbar = false;
needs_vertical_scrollbar = false;
return;
}
- needs_horizontal_scrollbar = GetLayoutBox()->ScrollsOverflowX();
- needs_vertical_scrollbar = GetLayoutBox()->ScrollsOverflowY();
+ mojom::blink::ScrollbarMode h_mode = mojom::blink::ScrollbarMode::kAuto;
+ mojom::blink::ScrollbarMode v_mode = mojom::blink::ScrollbarMode::kAuto;
- // Don't add auto scrollbars if the box contents aren't visible.
- if (GetLayoutBox()->HasAutoHorizontalScrollbar()) {
- if (option == kForbidAddingAutoBars)
- needs_horizontal_scrollbar &= HasHorizontalScrollbar();
- needs_horizontal_scrollbar &=
- GetLayoutBox()->IsRooted() && HasHorizontalOverflow() &&
- VisibleContentRect(kIncludeScrollbars).Height();
- }
+ // First, determine what behavior the scrollbars say they should have.
+ {
+ if (auto* layout_view = DynamicTo<LayoutView>(GetLayoutBox())) {
+ // LayoutView is special as there's various quirks and settings that
+ // style doesn't account for.
+ layout_view->CalculateScrollbarModes(h_mode, v_mode);
+ } else {
+ auto overflow_x = GetLayoutBox()->StyleRef().OverflowX();
+ if (overflow_x == EOverflow::kScroll) {
+ h_mode = mojom::blink::ScrollbarMode::kAlwaysOn;
+ } else if (overflow_x == EOverflow::kHidden ||
+ overflow_x == EOverflow::kVisible) {
+ h_mode = mojom::blink::ScrollbarMode::kAlwaysOff;
+ }
+
+ auto overflow_y = GetLayoutBox()->StyleRef().OverflowY();
+ if (overflow_y == EOverflow::kScroll) {
+ v_mode = mojom::blink::ScrollbarMode::kAlwaysOn;
+ } else if (overflow_y == EOverflow::kHidden ||
+ overflow_y == EOverflow::kVisible) {
+ v_mode = mojom::blink::ScrollbarMode::kAlwaysOff;
+ }
+ }
- if (GetLayoutBox()->HasAutoVerticalScrollbar()) {
- if (option == kForbidAddingAutoBars)
- needs_vertical_scrollbar &= HasVerticalScrollbar();
- needs_vertical_scrollbar &= GetLayoutBox()->IsRooted() &&
- HasVerticalOverflow() &&
- VisibleContentRect(kIncludeScrollbars).Width();
+ // Since overlay scrollbars (the fade-in/out kind, not overflow: overlay)
+ // only appear when scrolling, we don't create them if there isn't overflow
+ // to scroll. Thus, overlay scrollbars can't be "always on". i.e.
+ // |overlay:scroll| behaves like |overlay:auto|.
+ bool has_custom_scrollbar_style = ScrollbarStyleSource(*GetLayoutBox())
+ .StyleRef()
+ .HasCustomScrollbarStyle();
+ bool will_be_overlay = GetPageScrollbarTheme().UsesOverlayScrollbars() &&
+ !has_custom_scrollbar_style;
+ if (will_be_overlay) {
+ if (h_mode == mojom::blink::ScrollbarMode::kAlwaysOn)
+ h_mode = mojom::blink::ScrollbarMode::kAuto;
+ if (v_mode == mojom::blink::ScrollbarMode::kAlwaysOn)
+ v_mode = mojom::blink::ScrollbarMode::kAuto;
+ }
}
- if (GetLayoutBox()->IsLayoutView()) {
- ScrollbarMode h_mode;
- ScrollbarMode v_mode;
- ToLayoutView(GetLayoutBox())->CalculateScrollbarModes(h_mode, v_mode);
+ // By default, don't make any changes.
+ needs_horizontal_scrollbar = HasHorizontalScrollbar();
+ needs_vertical_scrollbar = HasVerticalScrollbar();
- // Look for the scrollbarModes and reset the needs Horizontal & vertical
- // Scrollbar values based on scrollbarModes, as during force style change
- // StyleResolver::styleForDocument returns documentStyle with no overflow
- // values, due to which we are destroying the scrollbars that were already
- // present.
- if (h_mode == kScrollbarAlwaysOn)
+ // If the behavior doesn't depend on overflow or any other information, we
+ // can set it now.
+ {
+ if (h_mode == mojom::blink::ScrollbarMode::kAlwaysOn)
needs_horizontal_scrollbar = true;
- else if (h_mode == kScrollbarAlwaysOff)
+ else if (h_mode == mojom::blink::ScrollbarMode::kAlwaysOff)
needs_horizontal_scrollbar = false;
- if (v_mode == kScrollbarAlwaysOn)
+
+ if (v_mode == mojom::blink::ScrollbarMode::kAlwaysOn)
needs_vertical_scrollbar = true;
- else if (v_mode == kScrollbarAlwaysOff)
+ else if (v_mode == mojom::blink::ScrollbarMode::kAlwaysOff)
needs_vertical_scrollbar = false;
}
+
+ // If this is being performed before layout, we want to only update scrollbar
+ // existence if its based on purely style based reasons.
+ if (option == kOverflowIndependent) {
+ return;
+ }
+
+ // If we have clean layout, we can make a decision on any scrollbars that
+ // depend on overflow.
+ {
+ if (h_mode == mojom::blink::ScrollbarMode::kAuto) {
+ // Don't add auto scrollbars if the box contents aren't visible.
+ needs_horizontal_scrollbar =
+ GetLayoutBox()->IsRooted() && HasHorizontalOverflow() &&
+ VisibleContentRect(kIncludeScrollbars).height();
+ }
+ if (v_mode == mojom::blink::ScrollbarMode::kAuto) {
+ needs_vertical_scrollbar = GetLayoutBox()->IsRooted() &&
+ HasVerticalOverflow() &&
+ VisibleContentRect(kIncludeScrollbars).width();
+ }
+ }
}
bool PaintLayerScrollableArea::TryRemovingAutoScrollbars(
const bool& needs_horizontal_scrollbar,
const bool& needs_vertical_scrollbar) {
- // If scrollbars are removed but the content size depends on the scrollbars,
- // additional layouts will be required to size the content. Therefore, only
- // remove auto scrollbars for the initial layout pass.
- DCHECK(!in_overflow_relayout_);
-
if (!needs_horizontal_scrollbar && !needs_vertical_scrollbar)
return false;
- if (GetLayoutBox()->IsLayoutView()) {
- ScrollbarMode h_mode;
- ScrollbarMode v_mode;
- ToLayoutView(GetLayoutBox())->CalculateScrollbarModes(h_mode, v_mode);
- if (h_mode != kScrollbarAuto || v_mode != kScrollbarAuto)
+ if (auto* layout_view = DynamicTo<LayoutView>(GetLayoutBox())) {
+ mojom::blink::ScrollbarMode h_mode;
+ mojom::blink::ScrollbarMode v_mode;
+ layout_view->CalculateScrollbarModes(h_mode, v_mode);
+ if (h_mode != mojom::blink::ScrollbarMode::kAuto ||
+ v_mode != mojom::blink::ScrollbarMode::kAuto)
return false;
- IntSize visible_size_with_scrollbars =
- VisibleContentRect(kIncludeScrollbars).Size();
- if (ScrollWidth() <= visible_size_with_scrollbars.Width() &&
- ScrollHeight() <= visible_size_with_scrollbars.Height()) {
+ gfx::Size visible_size_with_scrollbars =
+ VisibleContentRect(kIncludeScrollbars).size();
+ if (ScrollWidth() <= visible_size_with_scrollbars.width() &&
+ ScrollHeight() <= visible_size_with_scrollbars.height()) {
return true;
}
} else {
!GetLayoutBox()->HasAutoHorizontalScrollbar())
return false;
- LayoutSize client_size_with_scrollbars =
- LayoutContentRect(kIncludeScrollbars).Size();
- if (ScrollWidth() <= client_size_with_scrollbars.Width() &&
- ScrollHeight() <= client_size_with_scrollbars.Height()) {
+ PhysicalSize client_size_with_scrollbars =
+ LayoutContentRect(kIncludeScrollbars).size;
+ if (ScrollWidth() <= client_size_with_scrollbars.width &&
+ ScrollHeight() <= client_size_with_scrollbars.height) {
return true;
}
}
return false;
}
-bool PaintLayerScrollableArea::SetHasHorizontalScrollbar(bool has_scrollbar) {
- if (FreezeScrollbarsScope::ScrollbarsAreFrozen())
- return false;
+void PaintLayerScrollableArea::RemoveScrollbarsForReconstruction() {
+ if (!HasHorizontalScrollbar() && !HasVerticalScrollbar())
+ return;
+ if (HasHorizontalScrollbar()) {
+ SetScrollbarNeedsPaintInvalidation(kHorizontalScrollbar);
+ scrollbar_manager_.SetHasHorizontalScrollbar(false);
+ }
+ if (HasVerticalScrollbar()) {
+ SetScrollbarNeedsPaintInvalidation(kVerticalScrollbar);
+ scrollbar_manager_.SetHasVerticalScrollbar(false);
+ }
+ UpdateScrollCornerStyle();
+ UpdateScrollOrigin();
+
+ // Force an update since we know the scrollbars have changed things.
+ if (GetLayoutBox()->GetDocument().HasAnnotatedRegions())
+ GetLayoutBox()->GetDocument().SetAnnotatedRegionsDirty(true);
+}
+
+CompositorElementId PaintLayerScrollableArea::GetScrollCornerElementId() const {
+ CompositorElementId scrollable_element_id = GetScrollElementId();
+ DCHECK(scrollable_element_id);
+ return CompositorElementIdWithNamespace(
+ scrollable_element_id, CompositorElementIdNamespace::kScrollCorner);
+}
+
+void PaintLayerScrollableArea::SetHasHorizontalScrollbar(bool has_scrollbar) {
+ if (IsHorizontalScrollbarFrozen())
+ return;
if (has_scrollbar == HasHorizontalScrollbar())
- return false;
+ return;
SetScrollbarNeedsPaintInvalidation(kHorizontalScrollbar);
// Force an update since we know the scrollbars have changed things.
if (GetLayoutBox()->GetDocument().HasAnnotatedRegions())
GetLayoutBox()->GetDocument().SetAnnotatedRegionsDirty(true);
- return true;
}
-bool PaintLayerScrollableArea::SetHasVerticalScrollbar(bool has_scrollbar) {
- if (FreezeScrollbarsScope::ScrollbarsAreFrozen())
- return false;
+void PaintLayerScrollableArea::SetHasVerticalScrollbar(bool has_scrollbar) {
+ if (IsVerticalScrollbarFrozen())
+ return;
+
+ if (GetLayoutBox()->GetDocument().IsVerticalScrollEnforced()) {
+ // When the policy is enforced the contents of document cannot be scrolled.
+ // This would make rendering a scrollbar look strange
+ // (https://crbug.com/898151).
+ return;
+ }
if (has_scrollbar == HasVerticalScrollbar())
- return false;
+ return;
SetScrollbarNeedsPaintInvalidation(kVerticalScrollbar);
// Force an update since we know the scrollbars have changed things.
if (GetLayoutBox()->GetDocument().HasAnnotatedRegions())
GetLayoutBox()->GetDocument().SetAnnotatedRegionsDirty(true);
- return true;
}
int PaintLayerScrollableArea::VerticalScrollbarWidth(
OverlayScrollbarClipBehavior overlay_scrollbar_clip_behavior) const {
if (!HasVerticalScrollbar())
return 0;
- if (overlay_scrollbar_clip_behavior ==
- kIgnorePlatformAndCSSOverlayScrollbarSize &&
- GetLayoutBox()->Style()->OverflowY() == EOverflow::kOverlay) {
+ if (overlay_scrollbar_clip_behavior == kIgnoreOverlayScrollbarSize &&
+ GetLayoutBox()->StyleRef().OverflowY() == EOverflow::kOverlay) {
return 0;
}
- if ((overlay_scrollbar_clip_behavior == kIgnorePlatformOverlayScrollbarSize ||
- overlay_scrollbar_clip_behavior ==
- kIgnorePlatformAndCSSOverlayScrollbarSize ||
+ if ((overlay_scrollbar_clip_behavior == kIgnoreOverlayScrollbarSize ||
!VerticalScrollbar()->ShouldParticipateInHitTesting()) &&
VerticalScrollbar()->IsOverlayScrollbar()) {
return 0;
OverlayScrollbarClipBehavior overlay_scrollbar_clip_behavior) const {
if (!HasHorizontalScrollbar())
return 0;
- if (overlay_scrollbar_clip_behavior ==
- kIgnorePlatformAndCSSOverlayScrollbarSize &&
- GetLayoutBox()->Style()->OverflowX() == EOverflow::kOverlay) {
+ if (overlay_scrollbar_clip_behavior == kIgnoreOverlayScrollbarSize &&
+ GetLayoutBox()->StyleRef().OverflowX() == EOverflow::kOverlay) {
return 0;
}
- if ((overlay_scrollbar_clip_behavior == kIgnorePlatformOverlayScrollbarSize ||
- overlay_scrollbar_clip_behavior ==
- kIgnorePlatformAndCSSOverlayScrollbarSize ||
+ if ((overlay_scrollbar_clip_behavior == kIgnoreOverlayScrollbarSize ||
!HorizontalScrollbar()->ShouldParticipateInHitTesting()) &&
HorizontalScrollbar()->IsOverlayScrollbar()) {
return 0;
return HorizontalScrollbar()->ScrollbarThickness();
}
-void PaintLayerScrollableArea::SnapAfterScrollbarScrolling(
- ScrollbarOrientation orientation) {
- SnapCoordinator* snap_coordinator =
- GetLayoutBox()->GetDocument().GetSnapCoordinator();
- if (!snap_coordinator)
- return;
- snap_coordinator->PerformSnapping(*GetLayoutBox(),
- orientation == kHorizontalScrollbar,
- orientation == kVerticalScrollbar);
+const cc::SnapContainerData* PaintLayerScrollableArea::GetSnapContainerData()
+ const {
+ return RareData() && RareData()->snap_container_data_
+ ? &RareData()->snap_container_data_.value()
+ : nullptr;
}
-void PaintLayerScrollableArea::PositionOverflowControls() {
- if (!HasScrollbar() && !GetLayoutBox()->CanResize())
+void PaintLayerScrollableArea::SetSnapContainerData(
+ absl::optional<cc::SnapContainerData> data) {
+ EnsureRareData().snap_container_data_ = data;
+}
+
+bool PaintLayerScrollableArea::SetTargetSnapAreaElementIds(
+ cc::TargetSnapAreaElementIds snap_target_ids) {
+ if (!RareData() || !RareData()->snap_container_data_)
+ return false;
+ if (RareData()->snap_container_data_.value().SetTargetSnapAreaElementIds(
+ snap_target_ids)) {
+ GetLayoutBox()->SetNeedsPaintPropertyUpdate();
+ return true;
+ }
+ return false;
+}
+
+bool PaintLayerScrollableArea::SnapContainerDataNeedsUpdate() const {
+ return RareData() ? RareData()->snap_container_data_needs_update_ : false;
+}
+
+void PaintLayerScrollableArea::SetSnapContainerDataNeedsUpdate(
+ bool needs_update) {
+ DCHECK(!RuntimeEnabledFeatures::LayoutNewSnapLogicEnabled());
+ EnsureRareData().snap_container_data_needs_update_ = needs_update;
+ if (!needs_update)
return;
+ GetLayoutBox()
+ ->GetDocument()
+ .GetSnapCoordinator()
+ .SetAnySnapContainerDataNeedsUpdate(true);
+}
- const IntRect border_box =
- GetLayoutBox()->PixelSnappedBorderBoxRect(layer_->SubpixelAccumulation());
+absl::optional<gfx::PointF>
+PaintLayerScrollableArea::GetSnapPositionAndSetTarget(
+ const cc::SnapSelectionStrategy& strategy) {
+ if (!RareData() || !RareData()->snap_container_data_)
+ return absl::nullopt;
- if (Scrollbar* vertical_scrollbar = VerticalScrollbar())
- vertical_scrollbar->SetFrameRect(RectForVerticalScrollbar(border_box));
+ cc::SnapContainerData& data = RareData()->snap_container_data_.value();
+ if (!data.size())
+ return absl::nullopt;
- if (Scrollbar* horizontal_scrollbar = HorizontalScrollbar())
- horizontal_scrollbar->SetFrameRect(RectForHorizontalScrollbar(border_box));
+ // If the document has a focused element that is coincident with the snap
+ // target, update the snap target to point to the focused element. This
+ // ensures that we stay snapped to the focused element after a relayout.
+ // TODO(crbug.com/1199911): If the focused element is not a snap target but
+ // has an ancestor that is, perhaps the rule should be applied for the
+ // ancestor element.
+ CompositorElementId active_element_id = CompositorElementId();
+ if (auto* active_element = GetDocument()->ActiveElement()) {
+ active_element_id =
+ CompositorElementIdFromDOMNodeId(active_element->GetDomNodeId());
+ }
- const IntRect& scroll_corner = ScrollCornerRect();
- if (scroll_corner_)
- scroll_corner_->SetFrameRect(LayoutRect(scroll_corner));
+ absl::optional<gfx::PointF> snap_point;
+ cc::SnapPositionData snap =
+ data.FindSnapPosition(strategy, active_element_id);
+ if (snap.type != cc::SnapPositionData::Type::kNone) {
+ snap_point = gfx::PointF(snap.position.x(), snap.position.y());
+ }
- if (resizer_)
- resizer_->SetFrameRect(
- LayoutRect(ResizerCornerRect(border_box, kResizerForPointer)));
+ if (data.SetTargetSnapAreaElementIds(snap.target_element_ids)) {
+ GetLayoutBox()->SetNeedsPaintPropertyUpdate();
+ }
- // FIXME, this should eventually be removed, once we are certain that
- // composited controls get correctly positioned on a compositor update. For
- // now, conservatively leaving this unchanged.
- if (Layer()->HasCompositedLayerMapping())
- Layer()->GetCompositedLayerMapping()->PositionOverflowControlsLayers();
+ return snap_point;
}
-void PaintLayerScrollableArea::UpdateScrollCornerStyle() {
- if (!scroll_corner_ && !HasScrollbar())
- return;
- if (!scroll_corner_ && HasOverlayScrollbars())
+bool PaintLayerScrollableArea::HasOverflowControls() const {
+ // We do not need to check for ScrollCorner because it only exists iff there
+ // are scrollbars, see: |ScrollCornerRect| and |UpdateScrollCornerStyle|.
+ DCHECK(!ScrollCorner() || HasScrollbar());
+ return HasScrollbar() || GetLayoutBox()->CanResize();
+}
+
+bool PaintLayerScrollableArea::HasOverlayOverflowControls() const {
+ if (HasOverlayScrollbars())
+ return true;
+ if (!HasScrollbar() && GetLayoutBox()->CanResize())
+ return true;
+ if (GetLayoutBox()->StyleRef().OverflowX() == EOverflow::kOverlay ||
+ GetLayoutBox()->StyleRef().OverflowY() == EOverflow::kOverlay)
+ return true;
+ return false;
+}
+
+bool PaintLayerScrollableArea::NeedsScrollCorner() const {
+ // This is one of the differences between platform overlay scrollbars and
+ // overflow:overlay scrollbars: the former don't need scroll corner, while
+ // the latter do. HasOverlayScrollbars doesn't include overflow:overlay.
+ return HasScrollbar() && !HasOverlayScrollbars();
+}
+
+bool PaintLayerScrollableArea::ShouldOverflowControlsPaintAsOverlay() const {
+ if (HasOverlayOverflowControls())
+ return true;
+
+ // Frame and global root scroller (which can be a non-frame) scrollbars and
+ // corner also paint as overlay so that they appear on top of all content
+ // within their viewport. This is important for global root scrollers since
+ // these scrollbars' transform state is
+ // VisualViewport::TransformNodeForViewportScrollbars().
+ return layer_->IsRootLayer() ||
+ (GetLayoutBox() && GetLayoutBox()->IsGlobalRootScroller());
+}
+
+void PaintLayerScrollableArea::PositionOverflowControls() {
+ if (!HasOverflowControls())
return;
+ if (Scrollbar* vertical_scrollbar = VerticalScrollbar()) {
+ vertical_scrollbar->SetFrameRect(RectForVerticalScrollbar());
+ if (auto* custom_scrollbar = DynamicTo<CustomScrollbar>(vertical_scrollbar))
+ custom_scrollbar->PositionScrollbarParts();
+ }
+
+ if (Scrollbar* horizontal_scrollbar = HorizontalScrollbar()) {
+ horizontal_scrollbar->SetFrameRect(RectForHorizontalScrollbar());
+ if (auto* custom_scrollbar =
+ DynamicTo<CustomScrollbar>(horizontal_scrollbar))
+ custom_scrollbar->PositionScrollbarParts();
+ }
+
+ if (scroll_corner_) {
+ PhysicalRect rect(ScrollCornerRect());
+ scroll_corner_->SetOverriddenSize(rect.size);
+ // TODO(crbug.com/1020913): This should be part of PaintPropertyTreeBuilder
+ // when we support subpixel layout of overflow controls.
+ scroll_corner_->GetMutableForPainting().FirstFragment().SetPaintOffset(
+ rect.offset);
+ }
+
+ if (resizer_) {
+ PhysicalRect rect(ResizerCornerRect(kResizerForPointer));
+ resizer_->SetOverriddenSize(rect.size);
+ // TODO(crbug.com/1020913): This should be part of PaintPropertyTreeBuilder
+ // when we support subpixel layout of overflow controls.
+ resizer_->GetMutableForPainting().FirstFragment().SetPaintOffset(
+ rect.offset);
+ }
+}
+
+void PaintLayerScrollableArea::UpdateScrollCornerStyle() {
+ if (!NeedsScrollCorner()) {
+ if (scroll_corner_) {
+ scroll_corner_->Destroy();
+ scroll_corner_ = nullptr;
+ }
+ return;
+ }
const LayoutObject& style_source = ScrollbarStyleSource(*GetLayoutBox());
- scoped_refptr<ComputedStyle> corner =
- GetLayoutBox()->HasOverflowClip()
- ? style_source.GetUncachedPseudoStyle(
- PseudoStyleRequest(kPseudoIdScrollbarCorner),
- style_source.Style())
- : scoped_refptr<ComputedStyle>(nullptr);
+ bool uses_standard_scrollbar_style =
+ style_source.StyleRef().UsesStandardScrollbarStyle();
+ const ComputedStyle* corner =
+ (GetLayoutBox()->IsScrollContainer() && !uses_standard_scrollbar_style)
+ ? style_source.GetUncachedPseudoElementStyle(
+ StyleRequest(kPseudoIdScrollbarCorner, style_source.Style()))
+ : nullptr;
if (corner) {
if (!scroll_corner_) {
- scroll_corner_ = LayoutScrollbarPart::CreateAnonymous(
+ scroll_corner_ = LayoutCustomScrollbarPart::CreateAnonymous(
&GetLayoutBox()->GetDocument(), this);
- scroll_corner_->SetDangerousOneWayParent(GetLayoutBox());
}
- scroll_corner_->SetStyleWithWritingModeOfParent(std::move(corner));
+ scroll_corner_->SetStyle(std::move(corner));
} else if (scroll_corner_) {
scroll_corner_->Destroy();
scroll_corner_ = nullptr;
bool PaintLayerScrollableArea::HitTestOverflowControls(
HitTestResult& result,
- const IntPoint& local_point) {
- if (!HasScrollbar() && !GetLayoutBox()->CanResize())
+ const gfx::Point& local_point) {
+ if (!HasOverflowControls())
return false;
- IntRect resize_control_rect;
- if (GetLayoutBox()->Style()->Resize() != EResize::kNone) {
- resize_control_rect =
- ResizerCornerRect(GetLayoutBox()->PixelSnappedBorderBoxRect(
- Layer()->SubpixelAccumulation()),
- kResizerForPointer);
- if (resize_control_rect.Contains(local_point))
+ gfx::Rect resize_control_rect;
+ if (GetLayoutBox()->CanResize()) {
+ resize_control_rect = ResizerCornerRect(kResizerForPointer);
+ if (resize_control_rect.Contains(local_point)) {
+ result.SetIsOverResizer(true);
return true;
+ }
}
- int resize_control_size = max(resize_control_rect.Height(), 0);
+ int resize_control_size = max(resize_control_rect.height(), 0);
- IntRect visible_rect = VisibleContentRect(kIncludeScrollbars);
+ gfx::Rect visible_rect = VisibleContentRect(kIncludeScrollbars);
if (HasVerticalScrollbar() &&
VerticalScrollbar()->ShouldParticipateInHitTesting()) {
- LayoutRect v_bar_rect(
- VerticalScrollbarStart(0, Layer()->PixelSnappedSize().Width()),
- GetLayoutBox()->BorderTop().ToInt(),
- VerticalScrollbar()->ScrollbarThickness(),
- visible_rect.Height() -
- (HasHorizontalScrollbar()
- ? HorizontalScrollbar()->ScrollbarThickness()
- : resize_control_size));
+ gfx::Rect v_bar_rect(VerticalScrollbarStart(),
+ GetLayoutBox()->BorderTop().ToInt(),
+ VerticalScrollbar()->ScrollbarThickness(),
+ visible_rect.height() -
+ (HasHorizontalScrollbar()
+ ? HorizontalScrollbar()->ScrollbarThickness()
+ : resize_control_size));
if (v_bar_rect.Contains(local_point)) {
result.SetScrollbar(VerticalScrollbar());
return true;
}
}
- resize_control_size = max(resize_control_rect.Width(), 0);
+ resize_control_size = max(resize_control_rect.width(), 0);
if (HasHorizontalScrollbar() &&
HorizontalScrollbar()->ShouldParticipateInHitTesting()) {
// TODO(crbug.com/638981): Are the conversions to int intentional?
int h_scrollbar_thickness = HorizontalScrollbar()->ScrollbarThickness();
- LayoutRect h_bar_rect(
- HorizontalScrollbarStart(0),
- GetLayoutBox()->BorderTop().ToInt() + visible_rect.Height() -
+ gfx::Rect h_bar_rect(
+ HorizontalScrollbarStart(),
+ GetLayoutBox()->BorderTop().ToInt() + visible_rect.height() -
h_scrollbar_thickness,
- visible_rect.Width() - (HasVerticalScrollbar()
+ visible_rect.width() - (HasVerticalScrollbar()
? VerticalScrollbar()->ScrollbarThickness()
: resize_control_size),
h_scrollbar_thickness);
return false;
}
-IntRect PaintLayerScrollableArea::ResizerCornerRect(
- const IntRect& bounds,
+gfx::Rect PaintLayerScrollableArea::ResizerCornerRect(
ResizerHitTestType resizer_hit_test_type) const {
- if (GetLayoutBox()->Style()->Resize() == EResize::kNone)
- return IntRect();
- IntRect corner = CornerRect(bounds);
+ if (!GetLayoutBox()->CanResize())
+ return gfx::Rect();
+ gfx::Rect corner = CornerRect();
if (resizer_hit_test_type == kResizerForTouch) {
// We make the resizer virtually larger for touch hit testing. With the
// expanding ratio k = ResizerControlExpandRatioForTouch, we first move
// the resizer rect (of width w & height h), by (-w * (k-1), -h * (k-1)),
// then expand the rect by new_w/h = w/h * k.
- int expand_ratio = kResizerControlExpandRatioForTouch - 1;
- corner.Move(-corner.Width() * expand_ratio,
- -corner.Height() * expand_ratio);
- corner.Expand(corner.Width() * expand_ratio,
- corner.Height() * expand_ratio);
+ corner.Offset(-corner.width() * (kResizerControlExpandRatioForTouch - 1),
+ -corner.height() * (kResizerControlExpandRatioForTouch - 1));
+ corner.set_size(
+ gfx::Size(corner.width() * kResizerControlExpandRatioForTouch,
+ corner.height() * kResizerControlExpandRatioForTouch));
}
return corner;
}
-IntRect PaintLayerScrollableArea::ScrollCornerAndResizerRect() const {
- IntRect scroll_corner_and_resizer = ScrollCornerRect();
- if (scroll_corner_and_resizer.IsEmpty()) {
- scroll_corner_and_resizer =
- ResizerCornerRect(GetLayoutBox()->PixelSnappedBorderBoxRect(
- Layer()->SubpixelAccumulation()),
- kResizerForPointer);
- }
+gfx::Rect PaintLayerScrollableArea::ScrollCornerAndResizerRect() const {
+ gfx::Rect scroll_corner_and_resizer = ScrollCornerRect();
+ if (scroll_corner_and_resizer.IsEmpty())
+ return ResizerCornerRect(kResizerForPointer);
return scroll_corner_and_resizer;
}
-bool PaintLayerScrollableArea::IsPointInResizeControl(
- const IntPoint& absolute_point,
+bool PaintLayerScrollableArea::IsAbsolutePointInResizeControl(
+ const gfx::Point& absolute_point,
ResizerHitTestType resizer_hit_test_type) const {
- if (!GetLayoutBox()->CanResize())
+ if (GetLayoutBox()->StyleRef().Visibility() != EVisibility::kVisible ||
+ !GetLayoutBox()->CanResize())
return false;
- IntPoint local_point = RoundedIntPoint(GetLayoutBox()->AbsoluteToLocal(
- FloatPoint(absolute_point), kUseTransforms));
- IntRect local_bounds(IntPoint(), Layer()->PixelSnappedSize());
- return ResizerCornerRect(local_bounds, resizer_hit_test_type)
- .Contains(local_point);
+ gfx::Point local_point = ToRoundedPoint(
+ GetLayoutBox()->AbsoluteToLocalPoint(PhysicalOffset(absolute_point)));
+ return ResizerCornerRect(resizer_hit_test_type).Contains(local_point);
}
-bool PaintLayerScrollableArea::HitTestResizerInFragments(
- const PaintLayerFragments& layer_fragments,
- const HitTestLocation& hit_test_location) const {
- if (!GetLayoutBox()->CanResize())
- return false;
-
- if (layer_fragments.IsEmpty())
+bool PaintLayerScrollableArea::IsLocalPointInResizeControl(
+ const gfx::Point& local_point,
+ ResizerHitTestType resizer_hit_test_type) const {
+ if (GetLayoutBox()->StyleRef().Visibility() != EVisibility::kVisible ||
+ !GetLayoutBox()->CanResize())
return false;
- for (int i = layer_fragments.size() - 1; i >= 0; --i) {
- const PaintLayerFragment& fragment = layer_fragments.at(i);
- if (fragment.background_rect.Intersects(hit_test_location) &&
- ResizerCornerRect(PixelSnappedIntRect(fragment.layer_bounds),
- kResizerForPointer)
- .Contains(hit_test_location.RoundedPoint()))
- return true;
- }
-
- return false;
-}
-
-void PaintLayerScrollableArea::UpdateResizerAreaSet() {
- LocalFrame* frame = GetLayoutBox()->GetFrame();
- if (!frame)
- return;
- LocalFrameView* frame_view = frame->View();
- if (!frame_view)
- return;
- if (GetLayoutBox()->CanResize())
- frame_view->AddResizerArea(*GetLayoutBox());
- else
- frame_view->RemoveResizerArea(*GetLayoutBox());
+ return ResizerCornerRect(resizer_hit_test_type).Contains(local_point);
}
void PaintLayerScrollableArea::UpdateResizerStyle(
const ComputedStyle* old_style) {
- if (!RuntimeEnabledFeatures::SlimmingPaintV2Enabled() && old_style &&
- old_style->Resize() != GetLayoutBox()->StyleRef().Resize()) {
- // Invalidate the composited scroll corner layer on resize style change.
- if (auto* graphics_layer = LayerForScrollCorner())
- graphics_layer->SetNeedsDisplay();
- }
-
- if (!resizer_ && !GetLayoutBox()->CanResize())
+ // Change of resizer status affects HasOverlayOverflowControls(). Invalid
+ // z-order lists to refresh overflow control painting order.
+ bool had_resizer = old_style && old_style->HasResize();
+ bool needs_resizer = GetLayoutBox()->CanResize();
+ if (had_resizer != needs_resizer)
+ layer_->DirtyStackingContextZOrderLists();
+
+ if (!resizer_ && !needs_resizer)
return;
+ // Update custom resizer style.
const LayoutObject& style_source = ScrollbarStyleSource(*GetLayoutBox());
- scoped_refptr<ComputedStyle> resizer =
- GetLayoutBox()->HasOverflowClip()
- ? style_source.GetUncachedPseudoStyle(
- PseudoStyleRequest(kPseudoIdResizer), style_source.Style())
- : scoped_refptr<ComputedStyle>(nullptr);
+ const ComputedStyle* resizer =
+ GetLayoutBox()->IsScrollContainer()
+ ? style_source.GetUncachedPseudoElementStyle(
+ StyleRequest(kPseudoIdResizer, style_source.Style()))
+ : nullptr;
if (resizer) {
if (!resizer_) {
- resizer_ = LayoutScrollbarPart::CreateAnonymous(
+ resizer_ = LayoutCustomScrollbarPart::CreateAnonymous(
&GetLayoutBox()->GetDocument(), this);
- resizer_->SetDangerousOneWayParent(GetLayoutBox());
}
- resizer_->SetStyleWithWritingModeOfParent(std::move(resizer));
+ resizer_->SetStyle(std::move(resizer));
} else if (resizer_) {
resizer_->Destroy();
resizer_ = nullptr;
}
}
-void PaintLayerScrollableArea::InvalidateAllStickyConstraints() {
- if (PaintLayerScrollableAreaRareData* d = RareData()) {
- for (PaintLayer* sticky_layer : d->sticky_constraints_map_.Keys()) {
- if (sticky_layer->GetLayoutObject().Style()->GetPosition() ==
- EPosition::kSticky)
- sticky_layer->SetNeedsCompositingInputsUpdate();
+void PaintLayerScrollableArea::EnqueueForSnapUpdateIfNeeded() {
+ auto* box = GetLayoutBox();
+ // Not all PLSAs are scroll containers!
+ if (!box->IsScrollContainer()) {
+ return;
+ }
+
+ // Enqueue ourselves for a snap update if we have any snap-areas, or if we
+ // currently have snap-data (and it needs to be cleared).
+ for (const auto& fragment : box->PhysicalFragments()) {
+ if (fragment.SnapAreas() || GetSnapContainerData()) {
+ box->GetFrameView()->AddPendingSnapUpdate(this);
+ break;
}
- d->sticky_constraints_map_.clear();
}
}
-void PaintLayerScrollableArea::InvalidateStickyConstraintsFor(
- PaintLayer* layer,
- bool needs_compositing_update) {
- if (PaintLayerScrollableAreaRareData* d = RareData()) {
- d->sticky_constraints_map_.erase(layer);
- if (needs_compositing_update &&
- layer->GetLayoutObject().Style()->HasStickyConstrainedPosition())
- layer->SetNeedsCompositingInputsUpdate();
- }
+void PaintLayerScrollableArea::AddStickyLayer(PaintLayer* layer) {
+ UseCounter::Count(GetLayoutBox()->GetDocument(), WebFeature::kPositionSticky);
+ EnsureRareData().sticky_layers_.insert(layer);
}
-bool PaintLayerScrollableArea::HasStickyDescendants() const {
- if (const PaintLayerScrollableAreaRareData* d = RareData())
- return !d->sticky_constraints_map_.IsEmpty();
- return false;
+void PaintLayerScrollableArea::UpdateAllStickyConstraints() {
+ // TODO(ikilpatrick): Change `UpdateStickyPositionConstraints` return the
+ // sticky constraints object instead of performing a mutation.
+ for (const auto& fragment : GetLayoutBox()->PhysicalFragments()) {
+ if (auto* sticky_descendants = fragment.StickyDescendants()) {
+ for (auto& sticky_descendant : *sticky_descendants) {
+ sticky_descendant->UpdateStickyPositionConstraints();
+ }
+ }
+ }
}
-bool PaintLayerScrollableArea::HasNonCompositedStickyDescendants() const {
- if (const PaintLayerScrollableAreaRareData* d = RareData()) {
- for (const PaintLayer* sticky_layer : d->sticky_constraints_map_.Keys()) {
- if (sticky_layer->GetLayoutObject().IsSlowRepaintConstrainedObject())
- return true;
+void PaintLayerScrollableArea::InvalidateAllStickyConstraints() {
+ // Don't clear StickyConstraints for each LayoutObject of each layer in
+ // sticky_layers_ because sticky_layers_ may contain stale pointers.
+ // LayoutBoxModelObject::UpdateStickyPositionConstraints() will check both
+ // HasStickyLayer() of its containing scrollable area and its
+ // StickyConstraints() to see if its sticky constraints need update.
+ if (rare_data_)
+ rare_data_->sticky_layers_.clear();
+
+ if (!RuntimeEnabledFeatures::LayoutNewStickyLogicEnabled()) {
+ return;
+ }
+
+ // Enqueue ourselves for a sticky update if we have any sticky descendants.
+ auto* box = GetLayoutBox();
+ for (const auto& fragment : box->PhysicalFragments()) {
+ if (fragment.StickyDescendants()) {
+ box->GetFrameView()->AddPendingStickyUpdate(this);
+ break;
}
}
- return false;
}
void PaintLayerScrollableArea::InvalidatePaintForStickyDescendants() {
+ // If this is called during layout, sticky_layers_ may contain stale pointers.
+ // Return because we'll InvalidateAllStickyConstraints(), and we'll
+ // SetNeedsPaintPropertyUpdate() when updating sticky constraints.
+ if (GetLayoutBox()->NeedsLayout())
+ return;
+
if (PaintLayerScrollableAreaRareData* d = RareData()) {
- for (PaintLayer* sticky_layer : d->sticky_constraints_map_.Keys()) {
- sticky_layer->GetLayoutObject()
- .SetShouldDoFullPaintInvalidationIncludingNonCompositingDescendants();
+ for (PaintLayer* sticky_layer : d->sticky_layers_) {
+ auto& object = sticky_layer->GetLayoutObject();
+ object.SetNeedsPaintPropertyUpdate();
+ DCHECK(object.StickyConstraints());
+ object.StickyConstraints()->ComputeStickyOffset(ScrollPosition());
}
}
}
-IntSize PaintLayerScrollableArea::OffsetFromResizeCorner(
- const IntPoint& absolute_point) const {
+gfx::Vector2d PaintLayerScrollableArea::OffsetFromResizeCorner(
+ const gfx::Point& absolute_point) const {
// Currently the resize corner is either the bottom right corner or the bottom
// left corner.
// FIXME: This assumes the location is 0, 0. Is this guaranteed to always be
// the case?
- IntSize element_size = Layer()->PixelSnappedSize();
+ gfx::Size element_size = PixelSnappedBorderBoxSize();
if (GetLayoutBox()->ShouldPlaceBlockDirectionScrollbarOnLogicalLeft())
- element_size.SetWidth(0);
- IntPoint resizer_point = IntPoint(element_size);
- IntPoint local_point = RoundedIntPoint(GetLayoutBox()->AbsoluteToLocal(
- FloatPoint(absolute_point), kUseTransforms));
- return local_point - resizer_point;
-}
-
-LayoutSize PaintLayerScrollableArea::MinimumSizeForResizing(float zoom_factor) {
- LayoutUnit min_width =
- MinimumValueForLength(GetLayoutBox()->StyleRef().MinWidth(),
- GetLayoutBox()->ContainingBlock()->Size().Width());
- LayoutUnit min_height =
- MinimumValueForLength(GetLayoutBox()->StyleRef().MinHeight(),
- GetLayoutBox()->ContainingBlock()->Size().Height());
- min_width = std::max(LayoutUnit(min_width / zoom_factor),
- LayoutUnit(kDefaultMinimumWidthForResizing));
- min_height = std::max(LayoutUnit(min_height / zoom_factor),
- LayoutUnit(kDefaultMinimumHeightForResizing));
- return LayoutSize(min_width, min_height);
-}
-
-void PaintLayerScrollableArea::Resize(const IntPoint& pos,
- const LayoutSize& old_offset) {
+ element_size.set_width(0);
+ gfx::Point local_point = ToRoundedPoint(
+ GetLayoutBox()->AbsoluteToLocalPoint(PhysicalOffset(absolute_point)));
+ return gfx::Vector2d(local_point.x() - element_size.width(),
+ local_point.y() - element_size.height());
+}
+
+void PaintLayerScrollableArea::Resize(const gfx::Point& pos,
+ const gfx::Vector2d& old_offset) {
// FIXME: This should be possible on generated content but is not right now.
if (!InResizeMode() || !GetLayoutBox()->CanResize() ||
!GetLayoutBox()->GetNode())
return;
DCHECK(GetLayoutBox()->GetNode()->IsElementNode());
- Element* element = ToElement(GetLayoutBox()->GetNode());
+ auto* element = To<Element>(GetLayoutBox()->GetNode());
Document& document = element->GetDocument();
- float zoom_factor = GetLayoutBox()->Style()->EffectiveZoom();
+ float zoom_factor = GetLayoutBox()->StyleRef().EffectiveZoom();
- IntSize new_offset =
+ gfx::Vector2d new_offset =
OffsetFromResizeCorner(document.View()->ConvertFromRootFrame(pos));
- new_offset.SetWidth(new_offset.Width() / zoom_factor);
- new_offset.SetHeight(new_offset.Height() / zoom_factor);
+ new_offset.set_x(new_offset.x() / zoom_factor);
+ new_offset.set_y(new_offset.y() / zoom_factor);
- LayoutSize current_size = GetLayoutBox()->Size();
+ PhysicalSize current_size = GetLayoutBox()->Size();
current_size.Scale(1 / zoom_factor);
- LayoutSize adjusted_old_offset = LayoutSize(
- old_offset.Width() / zoom_factor, old_offset.Height() / zoom_factor);
+ PhysicalOffset adjusted_old_offset(old_offset);
+ adjusted_old_offset.Scale(1.f / zoom_factor);
if (GetLayoutBox()->ShouldPlaceBlockDirectionScrollbarOnLogicalLeft()) {
- new_offset.SetWidth(-new_offset.Width());
- adjusted_old_offset.SetWidth(-adjusted_old_offset.Width());
+ new_offset.set_x(-new_offset.x());
+ adjusted_old_offset.left = -adjusted_old_offset.left;
}
- LayoutSize difference((current_size + new_offset - adjusted_old_offset)
- .ExpandedTo(MinimumSizeForResizing(zoom_factor)) -
- current_size);
+ PhysicalOffset offset = PhysicalOffset(new_offset) - adjusted_old_offset;
+ PhysicalSize new_size(current_size.width + offset.left,
+ current_size.height + offset.top);
+
+ // Ensure the new size is at least as large as the resize corner.
+ gfx::SizeF corner_rect(CornerRect().size());
+ corner_rect.InvScale(zoom_factor);
+ new_size.width = std::max(new_size.width, LayoutUnit(corner_rect.width()));
+ new_size.height = std::max(new_size.height, LayoutUnit(corner_rect.height()));
+
+ PhysicalSize difference(new_size - current_size);
bool is_box_sizing_border =
- GetLayoutBox()->Style()->BoxSizing() == EBoxSizing::kBorderBox;
-
- EResize resize = GetLayoutBox()->Style()->Resize();
- if (resize != EResize::kVertical && difference.Width()) {
- if (element->IsFormControlElement()) {
- // Make implicit margins from the theme explicit (see
- // <http://bugs.webkit.org/show_bug.cgi?id=9547>).
- element->SetInlineStyleProperty(
- CSSPropertyMarginLeft, GetLayoutBox()->MarginLeft() / zoom_factor,
- CSSPrimitiveValue::UnitType::kPixels);
- element->SetInlineStyleProperty(
- CSSPropertyMarginRight, GetLayoutBox()->MarginRight() / zoom_factor,
- CSSPrimitiveValue::UnitType::kPixels);
- }
+ GetLayoutBox()->StyleRef().BoxSizing() == EBoxSizing::kBorderBox;
+
+ EResize resize = GetLayoutBox()->StyleRef().UsedResize();
+ if (resize != EResize::kVertical && difference.width) {
LayoutUnit base_width =
- GetLayoutBox()->Size().Width() -
+ GetLayoutBox()->Size().width -
(is_box_sizing_border ? LayoutUnit()
: GetLayoutBox()->BorderAndPaddingWidth());
base_width = LayoutUnit(base_width / zoom_factor);
- element->SetInlineStyleProperty(CSSPropertyWidth,
- RoundToInt(base_width + difference.Width()),
+ element->SetInlineStyleProperty(CSSPropertyID::kWidth,
+ RoundToInt(base_width + difference.width),
CSSPrimitiveValue::UnitType::kPixels);
}
- if (resize != EResize::kHorizontal && difference.Height()) {
- if (element->IsFormControlElement()) {
- // Make implicit margins from the theme explicit (see
- // <http://bugs.webkit.org/show_bug.cgi?id=9547>).
- element->SetInlineStyleProperty(CSSPropertyMarginTop,
- GetLayoutBox()->MarginTop() / zoom_factor,
- CSSPrimitiveValue::UnitType::kPixels);
- element->SetInlineStyleProperty(
- CSSPropertyMarginBottom, GetLayoutBox()->MarginBottom() / zoom_factor,
- CSSPrimitiveValue::UnitType::kPixels);
- }
+ if (resize != EResize::kHorizontal && difference.height) {
LayoutUnit base_height =
- GetLayoutBox()->Size().Height() -
+ GetLayoutBox()->Size().height -
(is_box_sizing_border ? LayoutUnit()
: GetLayoutBox()->BorderAndPaddingHeight());
base_height = LayoutUnit(base_height / zoom_factor);
- element->SetInlineStyleProperty(
- CSSPropertyHeight, RoundToInt(base_height + difference.Height()),
- CSSPrimitiveValue::UnitType::kPixels);
+ element->SetInlineStyleProperty(CSSPropertyID::kHeight,
+ RoundToInt(base_height + difference.height),
+ CSSPrimitiveValue::UnitType::kPixels);
}
- document.UpdateStyleAndLayout();
+ document.UpdateStyleAndLayout(DocumentUpdateReason::kSizeChange);
- // FIXME (Radar 4118564): We should also autoscroll the window as necessary to
+ // FIXME: We should also autoscroll the window as necessary to
// keep the point under the cursor in view.
}
-LayoutRect PaintLayerScrollableArea::ScrollIntoView(
- const LayoutRect& absolute_rect,
- const WebScrollIntoViewParams& params) {
- LayoutRect local_expose_rect =
- AbsoluteToLocal(*GetLayoutBox(), absolute_rect);
- LayoutSize border_origin_to_scroll_origin =
- LayoutSize(-GetLayoutBox()->BorderLeft(), -GetLayoutBox()->BorderTop()) +
- LayoutSize(GetScrollOffset());
+PhysicalRect PaintLayerScrollableArea::ScrollIntoView(
+ const PhysicalRect& absolute_rect,
+ const mojom::blink::ScrollIntoViewParamsPtr& params) {
+ // Ignore sticky position offsets for the purposes of scrolling elements into
+ // view. See https://www.w3.org/TR/css-position-3/#stickypos-scroll for
+ // details
+ const MapCoordinatesFlags flag =
+ (RuntimeEnabledFeatures::CSSPositionStickyStaticScrollPositionEnabled())
+ ? kIgnoreStickyOffset
+ : 0;
+
+ PhysicalRect local_expose_rect =
+ GetLayoutBox()->AbsoluteToLocalRect(absolute_rect, flag);
+ PhysicalOffset border_origin_to_scroll_origin(-GetLayoutBox()->BorderLeft(),
+ -GetLayoutBox()->BorderTop());
+ // There might be scroll bar between border_origin and scroll_origin.
+ gfx::Vector2d scroll_bar_adjustment =
+ GetLayoutBox()->OriginAdjustmentForScrollbars();
+ border_origin_to_scroll_origin.left -= scroll_bar_adjustment.x();
+ border_origin_to_scroll_origin.top -= scroll_bar_adjustment.y();
+ border_origin_to_scroll_origin +=
+ PhysicalOffset::FromVector2dFFloor(GetScrollOffset());
// Represent the rect in the container's scroll-origin coordinate.
local_expose_rect.Move(border_origin_to_scroll_origin);
- LayoutRect scroll_snapport_rect = VisibleScrollSnapportRect();
+ PhysicalRect scroll_snapport_rect = VisibleScrollSnapportRect();
ScrollOffset target_offset = ScrollAlignment::GetScrollOffsetToExpose(
- scroll_snapport_rect, local_expose_rect, params.GetScrollAlignmentX(),
- params.GetScrollAlignmentY(), GetScrollOffset());
+ scroll_snapport_rect, local_expose_rect, *params->align_x.get(),
+ *params->align_y.get(), GetScrollOffset());
ScrollOffset new_scroll_offset(
- ClampScrollOffset(RoundedIntSize(target_offset)));
+ ClampScrollOffset(gfx::ToRoundedVector2d(target_offset)));
ScrollOffset old_scroll_offset = GetScrollOffset();
- if (params.GetScrollType() == kUserScroll) {
+ if (params->type == mojom::blink::ScrollType::kUser) {
if (!UserInputScrollable(kHorizontalScrollbar))
- new_scroll_offset.SetWidth(old_scroll_offset.Width());
+ new_scroll_offset.set_x(old_scroll_offset.x());
if (!UserInputScrollable(kVerticalScrollbar))
- new_scroll_offset.SetHeight(old_scroll_offset.Height());
- }
- if (params.is_for_scroll_sequence) {
- DCHECK(params.GetScrollType() == kProgrammaticScroll ||
- params.GetScrollType() == kUserScroll);
- ScrollBehavior behavior =
- DetermineScrollBehavior(params.GetScrollBehavior(),
- GetLayoutBox()->Style()->GetScrollBehavior());
+ new_scroll_offset.set_y(old_scroll_offset.y());
+ }
+
+ gfx::PointF end_point = ScrollOffsetToPosition(new_scroll_offset);
+ std::unique_ptr<cc::SnapSelectionStrategy> strategy =
+ cc::SnapSelectionStrategy::CreateForEndPosition(end_point, true, true);
+ end_point = GetSnapPositionAndSetTarget(*strategy).value_or(end_point);
+ new_scroll_offset = ScrollPositionToOffset(end_point);
+
+ if (params->is_for_scroll_sequence) {
+ CHECK(GetSmoothScrollSequencer());
+ DCHECK(params->type == mojom::blink::ScrollType::kProgrammatic ||
+ params->type == mojom::blink::ScrollType::kUser);
+ mojom::blink::ScrollBehavior behavior = DetermineScrollBehavior(
+ params->behavior, GetLayoutBox()->StyleRef().GetScrollBehavior());
GetSmoothScrollSequencer()->QueueAnimation(this, new_scroll_offset,
behavior);
} else {
- SetScrollOffset(new_scroll_offset, params.GetScrollType(),
- kScrollBehaviorInstant);
+ SetScrollOffset(new_scroll_offset, params->type,
+ mojom::blink::ScrollBehavior::kInstant);
}
ScrollOffset scroll_offset_difference = new_scroll_offset - old_scroll_offset;
// The container hasn't performed the scroll yet if it's for scroll sequence.
// To calculate the result from the scroll, we move the |local_expose_rect| to
// the will-be-scrolled location.
- local_expose_rect.Move(-LayoutSize(scroll_offset_difference));
+ local_expose_rect.Move(
+ -PhysicalOffset::FromVector2dFRound(scroll_offset_difference));
// Represent the rects in the container's border-box coordinate.
local_expose_rect.Move(-border_origin_to_scroll_origin);
scroll_snapport_rect.Move(-border_origin_to_scroll_origin);
- LayoutRect intersect = Intersection(scroll_snapport_rect, local_expose_rect);
+ PhysicalRect intersect =
+ Intersection(scroll_snapport_rect, local_expose_rect);
if (intersect.IsEmpty() && !scroll_snapport_rect.IsEmpty() &&
!local_expose_rect.IsEmpty()) {
- return LocalToAbsolute(*GetLayoutBox(), local_expose_rect);
+ return GetLayoutBox()->LocalToAbsoluteRect(local_expose_rect, flag);
}
- intersect = LocalToAbsolute(*GetLayoutBox(), intersect);
+ intersect = GetLayoutBox()->LocalToAbsoluteRect(intersect, flag);
+
return intersect;
}
if (!frame_view)
return;
+ const bool has_horizontal_overflow = HasHorizontalOverflow();
+ const bool has_vertical_overflow = HasVerticalOverflow();
bool has_overflow =
!GetLayoutBox()->Size().IsZero() &&
- ((HasHorizontalOverflow() && GetLayoutBox()->ScrollsOverflowX()) ||
- (HasVerticalOverflow() && GetLayoutBox()->ScrollsOverflowY()));
+ ((has_horizontal_overflow && GetLayoutBox()->ScrollsOverflowX()) ||
+ (has_vertical_overflow && GetLayoutBox()->ScrollsOverflowY()));
+
+ bool overflows_in_block_direction = GetLayoutBox()->IsHorizontalWritingMode()
+ ? has_vertical_overflow
+ : has_horizontal_overflow;
- bool is_visible_to_hit_test = GetLayoutBox()->Style()->VisibleToHitTesting();
+ if (overflows_in_block_direction) {
+ DCHECK(CanHaveOverflowScrollbars(*GetLayoutBox()));
+ frame_view->AddScrollAnchoringScrollableArea(this);
+ } else {
+ frame_view->RemoveScrollAnchoringScrollableArea(this);
+ }
+
+ bool is_visible =
+ GetLayoutBox()->StyleRef().Visibility() == EVisibility::kVisible;
bool did_scroll_overflow = scrolls_overflow_;
- if (GetLayoutBox()->IsLayoutView()) {
- ScrollbarMode h_mode;
- ScrollbarMode v_mode;
- ToLayoutView(GetLayoutBox())->CalculateScrollbarModes(h_mode, v_mode);
- if (h_mode == kScrollbarAlwaysOff && v_mode == kScrollbarAlwaysOff)
+ if (auto* layout_view = DynamicTo<LayoutView>(GetLayoutBox())) {
+ mojom::blink::ScrollbarMode h_mode;
+ mojom::blink::ScrollbarMode v_mode;
+ layout_view->CalculateScrollbarModes(h_mode, v_mode);
+ if (h_mode == mojom::blink::ScrollbarMode::kAlwaysOff &&
+ v_mode == mojom::blink::ScrollbarMode::kAlwaysOff)
has_overflow = false;
}
- scrolls_overflow_ = has_overflow && is_visible_to_hit_test;
+
+ scrolls_overflow_ = has_overflow && is_visible;
if (did_scroll_overflow == ScrollsOverflow())
return;
+ // Change of scrolls_overflow may affect whether we create ScrollTranslation
+ // which is referenced from ScrollDisplayItem. Invalidate scrollbars (but not
+ // their parts) to repaint the display item.
+ if (auto* scrollbar = HorizontalScrollbar())
+ scrollbar->SetNeedsPaintInvalidation(kNoPart);
+ if (auto* scrollbar = VerticalScrollbar())
+ scrollbar->SetNeedsPaintInvalidation(kNoPart);
+
if (RuntimeEnabledFeatures::ImplicitRootScrollerEnabled() &&
scrolls_overflow_) {
- if (GetLayoutBox()->IsLayoutView()) {
+ if (IsA<LayoutView>(GetLayoutBox())) {
if (Element* owner = GetLayoutBox()->GetDocument().LocalOwner()) {
owner->GetDocument().GetRootScrollerController().ConsiderForImplicit(
*owner);
}
} else {
- GetLayoutBox()
- ->GetDocument()
- .GetRootScrollerController()
- .ConsiderForImplicit(*GetLayoutBox()->GetNode());
+ // In some cases, the LayoutBox may not be associated with a Node (e.g.
+ // <input> and <fieldset> can generate anonymous LayoutBoxes for their
+ // scrollers). We don't care about those cases for root scroller so
+ // simply avoid these. https://crbug.com/1125621.
+ if (GetLayoutBox()->GetNode()) {
+ GetLayoutBox()
+ ->GetDocument()
+ .GetRootScrollerController()
+ .ConsiderForImplicit(*GetLayoutBox()->GetNode());
+ }
}
}
// PaintPropertyTreeBuilder::updateScrollAndScrollTranslation).
GetLayoutBox()->SetNeedsPaintPropertyUpdate();
+ // Scroll hit test data depend on whether the box scrolls overflow.
+ // They are painted in the background phase
+ // (see: BoxPainter::PaintBoxDecorationBackground).
+ GetLayoutBox()->SetBackgroundNeedsFullPaintInvalidation();
+
if (scrolls_overflow_) {
DCHECK(CanHaveOverflowScrollbars(*GetLayoutBox()));
- frame_view->AddScrollableArea(this);
+ frame_view->AddUserScrollableArea(this);
} else {
- frame_view->RemoveScrollableArea(this);
+ frame_view->RemoveUserScrollableArea(this);
}
layer_->DidUpdateScrollsOverflow();
}
-void PaintLayerScrollableArea::UpdateCompositingLayersAfterScroll() {
- PaintLayerCompositor* compositor = GetLayoutBox()->View()->Compositor();
- if (!compositor->InCompositingMode())
- return;
-
- if (UsesCompositedScrolling()) {
- DCHECK(Layer()->HasCompositedLayerMapping());
- ScrollingCoordinator* scrolling_coordinator = GetScrollingCoordinator();
- bool handled_scroll =
- Layer()->IsRootLayer() && scrolling_coordinator &&
- scrolling_coordinator->UpdateCompositedScrollOffset(this);
-
- if (!handled_scroll) {
- Layer()->GetCompositedLayerMapping()->SetNeedsGraphicsLayerUpdate(
- kGraphicsLayerUpdateSubtree);
- compositor->SetNeedsCompositingUpdate(
- kCompositingUpdateAfterGeometryChange);
- }
-
- // Sticky constraints and paint property nodes need to be updated
- // to the new sticky locations.
- if (HasStickyDescendants())
- InvalidateAllStickyConstraints();
-
- // If we have fixed elements and we scroll the root layer we might
- // change compositing since the fixed elements might now overlap a
- // composited layer.
- if (Layer()->IsRootLayer()) {
- LocalFrame* frame = GetLayoutBox()->GetFrame();
- if (frame && frame->View() &&
- frame->View()->HasViewportConstrainedObjects()) {
- Layer()->SetNeedsCompositingInputsUpdate();
- }
- }
- } else {
- Layer()->SetNeedsCompositingInputsUpdate();
- }
-}
-
ScrollingCoordinator* PaintLayerScrollableArea::GetScrollingCoordinator()
const {
LocalFrame* frame = GetLayoutBox()->GetFrame();
}
bool PaintLayerScrollableArea::ShouldScrollOnMainThread() const {
- if (HasBeenDisposed())
- return true;
- if (LocalFrame* frame = GetLayoutBox()->GetFrame()) {
- if (frame->View()->GetMainThreadScrollingReasons())
- return true;
- }
- if (HasNonCompositedStickyDescendants())
- return true;
- return ScrollableArea::ShouldScrollOnMainThread();
+ DCHECK_GE(GetDocument()->Lifecycle().GetState(),
+ DocumentLifecycle::kPaintClean);
+ return HasBeenDisposed() || should_scroll_on_main_thread_;
}
-static bool LayerNodeMayNeedCompositedScrolling(const PaintLayer* layer) {
- // Don't force composite scroll for select or text input elements.
- if (Node* node = layer->GetLayoutObject().GetNode()) {
- if (IsHTMLSelectElement(node))
- return false;
- if (TextControlElement* text_control = EnclosingTextControl(node)) {
- if (IsHTMLInputElement(text_control)) {
- return false;
- }
- }
+void PaintLayerScrollableArea::SetShouldScrollOnMainThread(
+ bool scroll_on_main_thread) {
+ DCHECK_EQ(GetDocument()->Lifecycle().GetState(),
+ DocumentLifecycle::kPaintClean);
+ if (scroll_on_main_thread != should_scroll_on_main_thread_) {
+ should_scroll_on_main_thread_ = scroll_on_main_thread;
+ MainThreadScrollingDidChange();
}
- return true;
}
-bool PaintLayerScrollableArea::ComputeNeedsCompositedScrolling(
- const bool layer_has_been_composited,
- const PaintLayer* layer) {
- non_composited_main_thread_scrolling_reasons_ = 0;
-
- if (CompositingReasonFinder::RequiresCompositingForRootScroller(*layer))
+bool PaintLayerScrollableArea::PrefersNonCompositedScrolling() const {
+ if (RuntimeEnabledFeatures::PreferNonCompositedScrollingEnabled()) {
return true;
-
- // TODO(crbug.com/839341): Remove ScrollTimeline check once we support
- // main-thread AnimationWorklet and don't need to promote the scroll-source.
- Node* node = layer->GetLayoutObject().GetNode();
- if (!layer->ScrollsOverflow() &&
- !ScrollTimeline::HasActiveScrollTimeline(node)) {
- return false;
}
-
- if (layer->Size().IsEmpty())
- return false;
-
- if (!layer_has_been_composited &&
- !LayerNodeMayNeedCompositedScrolling(layer)) {
- return false;
- }
-
- bool needs_composited_scrolling = true;
-
- // TODO(flackr): Allow integer transforms as long as all of the ancestor
- // transforms are also integer.
- bool background_supports_lcd_text =
- RuntimeEnabledFeatures::CompositeOpaqueScrollersEnabled() &&
- layer->GetLayoutObject().Style()->IsStackingContext() &&
- layer->GetBackgroundPaintLocation(
- &non_composited_main_thread_scrolling_reasons_) &
- kBackgroundPaintInScrollingContents &&
- layer->BackgroundIsKnownToBeOpaqueInRect(
- ToLayoutBox(layer->GetLayoutObject()).PaddingBoxRect()) &&
- !layer->CompositesWithTransform() && !layer->CompositesWithOpacity();
-
- // TODO(crbug.com/839341): Remove ScrollTimeline check once we support
- // main-thread AnimationWorklet and don't need to promote the scroll-source.
- if (!ScrollTimeline::HasActiveScrollTimeline(node) &&
- !layer_has_been_composited &&
- !layer->Compositor()->PreferCompositingToLCDTextEnabled() &&
- !background_supports_lcd_text) {
- if (layer->CompositesWithOpacity()) {
- non_composited_main_thread_scrolling_reasons_ |=
- MainThreadScrollingReason::kHasOpacityAndLCDText;
- }
- if (layer->CompositesWithTransform()) {
- non_composited_main_thread_scrolling_reasons_ |=
- MainThreadScrollingReason::kHasTransformAndLCDText;
- }
- if (!layer->BackgroundIsKnownToBeOpaqueInRect(
- ToLayoutBox(layer->GetLayoutObject()).PaddingBoxRect())) {
- non_composited_main_thread_scrolling_reasons_ |=
- MainThreadScrollingReason::kBackgroundNotOpaqueInRectAndLCDText;
+ if (Node* node = GetLayoutBox()->GetNode()) {
+ if (IsA<HTMLSelectElement>(node)) {
+ return true;
}
- if (!layer->GetLayoutObject().Style()->IsStackingContext()) {
- non_composited_main_thread_scrolling_reasons_ |=
- MainThreadScrollingReason::kIsNotStackingContextAndLCDText;
+ if (TextControlElement* text_control = EnclosingTextControl(node)) {
+ if (IsA<HTMLInputElement>(text_control)) {
+ return true;
+ }
}
-
- needs_composited_scrolling = false;
}
-
- if (layer->GetLayoutObject().HasClip() ||
- layer->HasDescendantWithClipPath() || !!layer->ClipPathAncestor()) {
- non_composited_main_thread_scrolling_reasons_ |=
- MainThreadScrollingReason::kHasClipRelatedProperty;
- needs_composited_scrolling = false;
- }
-
- DCHECK(!(non_composited_main_thread_scrolling_reasons_ &
- ~MainThreadScrollingReason::kNonCompositedReasons));
- return needs_composited_scrolling;
+ return false;
}
-void PaintLayerScrollableArea::UpdateNeedsCompositedScrolling(
- bool layer_has_been_composited) {
- const bool needs_composited_scrolling =
- ComputeNeedsCompositedScrolling(layer_has_been_composited, Layer());
-
- if (static_cast<bool>(needs_composited_scrolling_) !=
- needs_composited_scrolling) {
- needs_composited_scrolling_ = needs_composited_scrolling;
- }
+bool PaintLayerScrollableArea::UsesCompositedScrolling() const {
+ return GetLayoutBox()->UsesCompositedScrolling();
}
bool PaintLayerScrollableArea::VisualViewportSuppliesScrollbars() const {
const TopDocumentRootScrollerController& controller =
GetLayoutBox()->GetDocument().GetPage()->GlobalRootScrollerController();
-
- return RootScrollerUtil::ScrollableAreaForRootScroller(
- controller.GlobalRootScroller()) == this;
+ return controller.RootScrollerArea() == this;
}
bool PaintLayerScrollableArea::ScheduleAnimation() {
- if (ChromeClient* client = ToChromeClient(GetChromeClient())) {
- client->ScheduleAnimation(GetLayoutBox()->GetFrame()->View());
+ if (ChromeClient* client =
+ GetLayoutBox()->GetFrameView()->GetChromeClient()) {
+ client->ScheduleAnimation(GetLayoutBox()->GetFrameView());
return true;
}
return false;
}
-void PaintLayerScrollableArea::ResetRebuildScrollbarLayerFlags() {
- rebuild_horizontal_scrollbar_layer_ = false;
- rebuild_vertical_scrollbar_layer_ = false;
-}
-
-CompositorAnimationHost* PaintLayerScrollableArea::GetCompositorAnimationHost()
+cc::AnimationHost* PaintLayerScrollableArea::GetCompositorAnimationHost()
const {
return layer_->GetLayoutObject().GetFrameView()->GetCompositorAnimationHost();
}
-CompositorAnimationTimeline*
+cc::AnimationTimeline*
PaintLayerScrollableArea::GetCompositorAnimationTimeline() const {
- return layer_->GetLayoutObject()
- .GetFrameView()
- ->GetCompositorAnimationTimeline();
+ return layer_->GetLayoutObject().GetFrameView()->GetScrollAnimationTimeline();
}
-void PaintLayerScrollableArea::GetTickmarks(Vector<IntRect>& tickmarks) const {
+bool PaintLayerScrollableArea::HasTickmarks() const {
+ if (RareData() && !RareData()->tickmarks_override_.empty())
+ return true;
+ return layer_->IsRootLayer() &&
+ To<LayoutView>(GetLayoutBox())->HasTickmarks();
+}
+
+Vector<gfx::Rect> PaintLayerScrollableArea::GetTickmarks() const {
+ if (RareData() && !RareData()->tickmarks_override_.empty())
+ return RareData()->tickmarks_override_;
if (layer_->IsRootLayer())
- tickmarks = ToLayoutView(GetLayoutBox())->GetTickmarks();
+ return To<LayoutView>(GetLayoutBox())->GetTickmarks();
+ return Vector<gfx::Rect>();
}
void PaintLayerScrollableArea::ScrollbarManager::SetHasHorizontalScrollbar(
bool has_scrollbar) {
if (has_scrollbar) {
- // This doesn't hit in any tests, but since the equivalent code in
- // setHasVerticalScrollbar does, presumably this code does as well.
- DisableCompositingQueryAsserts disabler;
if (!h_bar_) {
h_bar_ = CreateScrollbar(kHorizontalScrollbar);
h_bar_is_attached_ = 1;
void PaintLayerScrollableArea::ScrollbarManager::SetHasVerticalScrollbar(
bool has_scrollbar) {
if (has_scrollbar) {
- DisableCompositingQueryAsserts disabler;
if (!v_bar_) {
v_bar_ = CreateScrollbar(kVerticalScrollbar);
v_bar_is_attached_ = 1;
Scrollbar* scrollbar = nullptr;
const LayoutObject& style_source =
ScrollbarStyleSource(*ScrollableArea()->GetLayoutBox());
- bool has_custom_scrollbar_style =
- style_source.StyleRef().HasPseudoStyle(kPseudoIdScrollbar);
- if (has_custom_scrollbar_style) {
+ if (style_source.StyleRef().HasCustomScrollbarStyle()) {
DCHECK(style_source.GetNode() && style_source.GetNode()->IsElementNode());
- scrollbar = LayoutScrollbar::CreateCustomScrollbar(
- ScrollableArea(), orientation, ToElement(style_source.GetNode()));
+ scrollbar = MakeGarbageCollected<CustomScrollbar>(
+ ScrollableArea(), orientation, &style_source);
} else {
- ScrollbarControlSize scrollbar_size = kRegularScrollbar;
- if (style_source.StyleRef().HasAppearance()) {
- scrollbar_size = LayoutTheme::GetTheme().ScrollbarControlSizeForPart(
- style_source.StyleRef().Appearance());
- }
- scrollbar = Scrollbar::Create(ScrollableArea(), orientation, scrollbar_size,
- &ScrollableArea()
- ->GetLayoutBox()
- ->GetFrame()
- ->GetPage()
- ->GetChromeClient());
+ scrollbar = MakeGarbageCollected<Scrollbar>(ScrollableArea(), orientation,
+ &style_source);
}
ScrollableArea()->GetLayoutBox()->GetDocument().View()->AddScrollbar(
scrollbar);
return;
ScrollableArea()->SetScrollbarNeedsPaintInvalidation(orientation);
- if (orientation == kHorizontalScrollbar)
- ScrollableArea()->rebuild_horizontal_scrollbar_layer_ = true;
- else
- ScrollableArea()->rebuild_vertical_scrollbar_layer_ = true;
if (!scrollbar->IsCustomScrollbar())
ScrollableArea()->WillRemoveScrollbar(*scrollbar, orientation);
}
void PaintLayerScrollableArea::ScrollbarManager::Trace(
- blink::Visitor* visitor) {
+ blink::Visitor* visitor) const {
visitor->Trace(scrollable_area_);
visitor->Trace(h_bar_);
visitor->Trace(v_bar_);
}
-uint64_t PaintLayerScrollableArea::Id() const {
- return DOMNodeIds::IdForNode(GetLayoutBox()->GetNode());
-}
-
-int PaintLayerScrollableArea::PreventRelayoutScope::count_ = 0;
-SubtreeLayoutScope*
- PaintLayerScrollableArea::PreventRelayoutScope::layout_scope_ = nullptr;
-bool PaintLayerScrollableArea::PreventRelayoutScope::relayout_needed_ = false;
-PersistentHeapVector<Member<PaintLayerScrollableArea>>*
- PaintLayerScrollableArea::PreventRelayoutScope::needs_relayout_ = nullptr;
-
-PaintLayerScrollableArea::PreventRelayoutScope::PreventRelayoutScope(
- SubtreeLayoutScope& layout_scope) {
- if (!count_) {
- DCHECK(!layout_scope_);
- DCHECK(!needs_relayout_ || needs_relayout_->IsEmpty());
- layout_scope_ = &layout_scope;
- }
- count_++;
-}
-
-PaintLayerScrollableArea::PreventRelayoutScope::~PreventRelayoutScope() {
- if (--count_ == 0) {
- if (relayout_needed_) {
- for (auto scrollable_area : *needs_relayout_) {
- DCHECK(scrollable_area->NeedsRelayout());
- LayoutBox* box = scrollable_area->GetLayoutBox();
- layout_scope_->SetNeedsLayout(
- box, LayoutInvalidationReason::kScrollbarChanged);
- if (box->IsLayoutBlock()) {
- bool horizontal_scrollbar_changed =
- scrollable_area->HasHorizontalScrollbar() !=
- scrollable_area->HadHorizontalScrollbarBeforeRelayout();
- bool vertical_scrollbar_changed =
- scrollable_area->HasVerticalScrollbar() !=
- scrollable_area->HadVerticalScrollbarBeforeRelayout();
- if (horizontal_scrollbar_changed || vertical_scrollbar_changed) {
- ToLayoutBlock(box)->ScrollbarsChanged(horizontal_scrollbar_changed,
- vertical_scrollbar_changed);
- }
- }
- scrollable_area->SetNeedsRelayout(false);
- }
+int PaintLayerScrollableArea::FreezeScrollbarsScope::count_ = 0;
- needs_relayout_->clear();
- }
- layout_scope_ = nullptr;
+PaintLayerScrollableArea::FreezeScrollbarsRootScope::FreezeScrollbarsRootScope(
+ const LayoutBox& box,
+ bool freeze_horizontal,
+ bool freeze_vertical)
+ : scrollable_area_(box.GetScrollableArea()) {
+ if (scrollable_area_ && !FreezeScrollbarsScope::ScrollbarsAreFrozen() &&
+ (freeze_horizontal || freeze_vertical)) {
+ scrollable_area_->EstablishScrollbarRoot(freeze_horizontal,
+ freeze_vertical);
+ freezer_.emplace();
}
}
-void PaintLayerScrollableArea::PreventRelayoutScope::SetBoxNeedsLayout(
- PaintLayerScrollableArea& scrollable_area,
- bool had_horizontal_scrollbar,
- bool had_vertical_scrollbar) {
- DCHECK(count_);
- DCHECK(layout_scope_);
- if (scrollable_area.NeedsRelayout())
- return;
- scrollable_area.SetNeedsRelayout(true);
- scrollable_area.SetHadHorizontalScrollbarBeforeRelayout(
- had_horizontal_scrollbar);
- scrollable_area.SetHadVerticalScrollbarBeforeRelayout(had_vertical_scrollbar);
-
- relayout_needed_ = true;
- if (!needs_relayout_)
- needs_relayout_ =
- new PersistentHeapVector<Member<PaintLayerScrollableArea>>();
- needs_relayout_->push_back(&scrollable_area);
+PaintLayerScrollableArea::FreezeScrollbarsRootScope::
+ ~FreezeScrollbarsRootScope() {
+ if (scrollable_area_)
+ scrollable_area_->ClearScrollbarRoot();
}
-void PaintLayerScrollableArea::PreventRelayoutScope::ResetRelayoutNeeded() {
- DCHECK_EQ(count_, 0);
- DCHECK(!needs_relayout_ || needs_relayout_->IsEmpty());
- relayout_needed_ = false;
-}
-
-int PaintLayerScrollableArea::FreezeScrollbarsScope::count_ = 0;
-
int PaintLayerScrollableArea::DelayScrollOffsetClampScope::count_ = 0;
-PersistentHeapVector<Member<PaintLayerScrollableArea>>*
- PaintLayerScrollableArea::DelayScrollOffsetClampScope::needs_clamp_ =
- nullptr;
PaintLayerScrollableArea::DelayScrollOffsetClampScope::
DelayScrollOffsetClampScope() {
- if (!needs_clamp_)
- needs_clamp_ = new PersistentHeapVector<Member<PaintLayerScrollableArea>>();
- DCHECK(count_ > 0 || needs_clamp_->IsEmpty());
+ DCHECK(count_ > 0 || NeedsClampList().empty());
count_++;
}
PaintLayerScrollableArea* scrollable_area) {
if (!scrollable_area->NeedsScrollOffsetClamp()) {
scrollable_area->SetNeedsScrollOffsetClamp(true);
- needs_clamp_->push_back(scrollable_area);
+ NeedsClampList().push_back(scrollable_area);
}
}
void PaintLayerScrollableArea::DelayScrollOffsetClampScope::
ClampScrollableAreas() {
- for (auto& scrollable_area : *needs_clamp_)
+ for (auto& scrollable_area : NeedsClampList())
scrollable_area->ClampScrollOffsetAfterOverflowChange();
- delete needs_clamp_;
- needs_clamp_ = nullptr;
+ NeedsClampList().clear();
+}
+
+HeapVector<Member<PaintLayerScrollableArea>>&
+PaintLayerScrollableArea::DelayScrollOffsetClampScope::NeedsClampList() {
+ DEFINE_STATIC_LOCAL(
+ Persistent<HeapVector<Member<PaintLayerScrollableArea>>>,
+ needs_clamp_list,
+ (MakeGarbageCollected<HeapVector<Member<PaintLayerScrollableArea>>>()));
+ return *needs_clamp_list;
}
ScrollbarTheme& PaintLayerScrollableArea::GetPageScrollbarTheme() const {
return page->GetScrollbarTheme();
}
+void PaintLayerScrollableArea::DidAddScrollbar(
+ Scrollbar& scrollbar,
+ ScrollbarOrientation orientation) {
+ if (HasOverlayOverflowControls() ||
+ layer_->NeedsReorderOverlayOverflowControls()) {
+ // Z-order of existing or new recordered overflow controls is updated along
+ // with the z-order lists.
+ layer_->DirtyStackingContextZOrderLists();
+ }
+ ScrollableArea::DidAddScrollbar(scrollbar, orientation);
+}
+
void PaintLayerScrollableArea::WillRemoveScrollbar(
Scrollbar& scrollbar,
ScrollbarOrientation orientation) {
- if (!scrollbar.IsCustomScrollbar() &&
- !(orientation == kHorizontalScrollbar ? LayerForHorizontalScrollbar()
- : LayerForVerticalScrollbar())) {
+ if (layer_->NeedsReorderOverlayOverflowControls()) {
+ // Z-order of recordered overflow controls is updated along with the z-order
+ // lists.
+ layer_->DirtyStackingContextZOrderLists();
+ }
+
+ if (!scrollbar.IsCustomScrollbar()) {
ObjectPaintInvalidator(*GetLayoutBox())
.SlowSetPaintingLayerNeedsRepaintAndInvalidateDisplayItemClient(
scrollbar, PaintInvalidationReason::kScrollControl);
ScrollableArea::WillRemoveScrollbar(scrollbar, orientation);
}
-static LayoutRect ScrollControlVisualRect(
- const IntRect& scroll_control_rect,
- const LayoutBox& box,
- const PaintInvalidatorContext& context,
- const LayoutRect& previous_visual_rect) {
- LayoutRect visual_rect(scroll_control_rect);
-#if DCHECK_IS_ON()
- FindVisualRectNeedingUpdateScope finder(box, context, previous_visual_rect,
- visual_rect);
-#endif
- if (!context.NeedsVisualRectUpdate(box))
- return previous_visual_rect;
-
- // No need to apply any paint offset. Scroll controls paint in a different
- // transform space than their contained box (the scrollbarPaintOffset
- // transform node).
- return visual_rect;
-}
-
// Returns true if the scroll control is invalidated.
-static bool InvalidatePaintOfScrollControlIfNeeded(
- const LayoutRect& new_visual_rect,
- const LayoutRect& previous_visual_rect,
- bool needs_paint_invalidation,
- LayoutBox& box,
- const LayoutBoxModelObject& paint_invalidation_container) {
- bool should_invalidate_new_rect = needs_paint_invalidation;
- if (new_visual_rect != previous_visual_rect) {
- should_invalidate_new_rect = true;
- } else if (previous_visual_rect.IsEmpty()) {
+static bool ScrollControlNeedsPaintInvalidation(
+ const gfx::Rect& new_visual_rect,
+ const gfx::Rect& previous_visual_rect,
+ bool needs_paint_invalidation) {
+ if (new_visual_rect != previous_visual_rect)
+ return true;
+ if (previous_visual_rect.IsEmpty()) {
DCHECK(new_visual_rect.IsEmpty());
// Do not issue an empty invalidation.
- should_invalidate_new_rect = false;
+ return false;
+ }
+
+ return needs_paint_invalidation;
+}
+
+bool PaintLayerScrollableArea::ShouldDirectlyCompositeScrollbar(
+ const Scrollbar& scrollbar) const {
+ // Don't composite non-scrollable scrollbars.
+ // TODO(crbug.com/1020913): !ScrollsOverflow() should imply
+ // !scrollbar.Maximum(), but currently that isn't always true due to
+ // different or incorrect rounding methods for scroll geometries.
+ if (!ScrollsOverflow() || !scrollbar.Maximum()) {
+ return false;
}
+ if (scrollbar.IsCustomScrollbar()) {
+ return false;
+ }
+ // Compositing of scrollbar is decided in PaintArtifactCompositor. We assume
+ // compositing here so that paint invalidation will be skipped here. We'll
+ // invalidate raster if needed after paint, without paint invalidation.
+ return true;
+}
+
+void PaintLayerScrollableArea::EstablishScrollbarRoot(bool freeze_horizontal,
+ bool freeze_vertical) {
+ DCHECK(!FreezeScrollbarsScope::ScrollbarsAreFrozen());
+ is_scrollbar_freeze_root_ = true;
+ is_horizontal_scrollbar_frozen_ = freeze_horizontal;
+ is_vertical_scrollbar_frozen_ = freeze_vertical;
+}
- return should_invalidate_new_rect;
+void PaintLayerScrollableArea::ClearScrollbarRoot() {
+ is_scrollbar_freeze_root_ = false;
+ is_horizontal_scrollbar_frozen_ = false;
+ is_vertical_scrollbar_frozen_ = false;
}
-static LayoutRect InvalidatePaintOfScrollbarIfNeeded(
+void PaintLayerScrollableArea::InvalidatePaintOfScrollbarIfNeeded(
+ const PaintInvalidatorContext& context,
+ bool needs_paint_invalidation,
Scrollbar* scrollbar,
- GraphicsLayer* graphics_layer,
bool& previously_was_overlay,
- const LayoutRect& previous_visual_rect,
- bool needs_paint_invalidation_arg,
- LayoutBox& box,
- const PaintInvalidatorContext& context) {
+ bool& previously_was_directly_composited,
+ gfx::Rect& visual_rect) {
bool is_overlay = scrollbar && scrollbar->IsOverlayScrollbar();
- LayoutRect new_visual_rect;
- // Calculate visual rect of the scrollbar, except overlay composited
- // scrollbars because we invalidate the graphics layer only.
- if (scrollbar && !(graphics_layer && is_overlay)) {
- new_visual_rect = ScrollControlVisualRect(scrollbar->FrameRect(), box,
- context, previous_visual_rect);
- }
-
- bool needs_paint_invalidation = needs_paint_invalidation_arg;
- if (needs_paint_invalidation && graphics_layer) {
- // If the scrollbar needs paint invalidation but didn't change location/size
- // or the scrollbar is an overlay scrollbar (visual rect is empty),
- // invalidating the graphics layer is enough (which has been done in
- // ScrollableArea::setScrollbarNeedsPaintInvalidation()).
- // Otherwise invalidatePaintOfScrollControlIfNeeded() below will invalidate
- // the old and new location of the scrollbar on the box's paint invalidation
- // container to ensure newly expanded/shrunk areas of the box to be
- // invalidated.
- needs_paint_invalidation = false;
- DCHECK(!graphics_layer->DrawsContent() ||
- graphics_layer->GetPaintController().GetPaintArtifact().IsEmpty());
+ gfx::Rect new_visual_rect;
+ if (scrollbar) {
+ new_visual_rect = scrollbar->FrameRect();
+ // TODO(crbug.com/1020913): We should not round paint_offset but should
+ // consider subpixel accumulation when painting scrollbars.
+ new_visual_rect.Offset(
+ ToRoundedVector2d(context.fragment_data->PaintOffset()));
}
// Invalidate the box's display item client if the box's padding box size is
// affected by change of the non-overlay scrollbar width. We detect change of
- // visual rect size instead of change of scrollbar width change, which may
- // have some false-positives (e.g. the scrollbar changed length but not width)
- // but won't invalidate more than expected because in the false-positive case
- // the box must have changed size and have been invalidated.
- const LayoutBoxModelObject& paint_invalidation_container =
- *context.paint_invalidation_container;
- LayoutSize new_scrollbar_used_space_in_box;
+ // visual rect size instead of change of scrollbar width, which may have some
+ // false-positives (e.g. the scrollbar changed length but not width) but won't
+ // invalidate more than expected because in the false-positive case the box
+ // must have changed size and have been invalidated.
+ gfx::Size new_scrollbar_used_space_in_box;
if (!is_overlay)
- new_scrollbar_used_space_in_box = new_visual_rect.Size();
- LayoutSize previous_scrollbar_used_space_in_box;
+ new_scrollbar_used_space_in_box = new_visual_rect.size();
+ gfx::Size previous_scrollbar_used_space_in_box;
if (!previously_was_overlay)
- previous_scrollbar_used_space_in_box = previous_visual_rect.Size();
+ previous_scrollbar_used_space_in_box = visual_rect.size();
// The IsEmpty() check avoids invalidaiton in cases when the visual rect
// changes from (0,0 0x0) to (0,0 0x100).
previous_scrollbar_used_space_in_box.IsEmpty()) &&
new_scrollbar_used_space_in_box != previous_scrollbar_used_space_in_box) {
context.painting_layer->SetNeedsRepaint();
+ const auto& box = *GetLayoutBox();
ObjectPaintInvalidator(box).InvalidateDisplayItemClient(
- box, PaintInvalidationReason::kGeometry);
+ box, PaintInvalidationReason::kLayout);
}
- bool invalidated = InvalidatePaintOfScrollControlIfNeeded(
- new_visual_rect, previous_visual_rect, needs_paint_invalidation, box,
- paint_invalidation_container);
-
previously_was_overlay = is_overlay;
- if (!invalidated || !scrollbar || graphics_layer)
- return new_visual_rect;
+ if (scrollbar) {
+ bool directly_composited = ShouldDirectlyCompositeScrollbar(*scrollbar);
+ if (directly_composited != previously_was_directly_composited) {
+ needs_paint_invalidation = true;
+ previously_was_directly_composited = directly_composited;
+ } else if (directly_composited) {
+ // Don't invalidate directly composited scrollbar if the change is only
+ // inside of the scrollbar. ScrollbarDisplayItem will handle such change.
+ needs_paint_invalidation = false;
+ }
+ }
- context.painting_layer->SetNeedsRepaint();
- ObjectPaintInvalidator(box).InvalidateDisplayItemClient(
- *scrollbar, PaintInvalidationReason::kScrollControl);
- if (scrollbar->IsCustomScrollbar()) {
- ToLayoutScrollbar(scrollbar)
- ->InvalidateDisplayItemClientsOfScrollbarParts();
+ if (scrollbar &&
+ ScrollControlNeedsPaintInvalidation(new_visual_rect, visual_rect,
+ needs_paint_invalidation)) {
+ context.painting_layer->SetNeedsRepaint();
+ scrollbar->Invalidate(PaintInvalidationReason::kScrollControl);
+ if (auto* custom_scrollbar = DynamicTo<CustomScrollbar>(scrollbar))
+ custom_scrollbar->InvalidateDisplayItemClientsOfScrollbarParts();
}
- return new_visual_rect;
+ visual_rect = new_visual_rect;
}
void PaintLayerScrollableArea::InvalidatePaintOfScrollControlsIfNeeded(
const PaintInvalidatorContext& context) {
- LayoutBox& box = *GetLayoutBox();
- SetHorizontalScrollbarVisualRect(InvalidatePaintOfScrollbarIfNeeded(
- HorizontalScrollbar(), LayerForHorizontalScrollbar(),
- horizontal_scrollbar_previously_was_overlay_,
- horizontal_scrollbar_visual_rect_,
- HorizontalScrollbarNeedsPaintInvalidation(), box, context));
- SetVerticalScrollbarVisualRect(InvalidatePaintOfScrollbarIfNeeded(
- VerticalScrollbar(), LayerForVerticalScrollbar(),
+ if (context.subtree_flags & PaintInvalidatorContext::kSubtreeFullInvalidation)
+ SetScrollControlsNeedFullPaintInvalidation();
+
+ InvalidatePaintOfScrollbarIfNeeded(
+ context, HorizontalScrollbarNeedsPaintInvalidation(),
+ HorizontalScrollbar(), horizontal_scrollbar_previously_was_overlay_,
+ horizontal_scrollbar_previously_was_directly_composited_,
+ horizontal_scrollbar_visual_rect_);
+ InvalidatePaintOfScrollbarIfNeeded(
+ context, VerticalScrollbarNeedsPaintInvalidation(), VerticalScrollbar(),
vertical_scrollbar_previously_was_overlay_,
- vertical_scrollbar_visual_rect_,
- VerticalScrollbarNeedsPaintInvalidation(), box, context));
-
- LayoutRect scroll_corner_and_resizer_visual_rect =
- ScrollControlVisualRect(ScrollCornerAndResizerRect(), box, context,
- scroll_corner_and_resizer_visual_rect_);
- const LayoutBoxModelObject& paint_invalidation_container =
- *context.paint_invalidation_container;
- if (InvalidatePaintOfScrollControlIfNeeded(
- scroll_corner_and_resizer_visual_rect,
+ vertical_scrollbar_previously_was_directly_composited_,
+ vertical_scrollbar_visual_rect_);
+
+ gfx::Rect new_scroll_corner_and_resizer_visual_rect =
+ ScrollCornerAndResizerRect();
+ // TODO(crbug.com/1020913): We should not round paint_offset but should
+ // consider subpixel accumulation when painting scrollbars.
+ new_scroll_corner_and_resizer_visual_rect.Offset(
+ ToRoundedVector2d(context.fragment_data->PaintOffset()));
+ if (ScrollControlNeedsPaintInvalidation(
+ new_scroll_corner_and_resizer_visual_rect,
scroll_corner_and_resizer_visual_rect_,
- ScrollCornerNeedsPaintInvalidation(), box,
- paint_invalidation_container)) {
- SetScrollCornerAndResizerVisualRect(scroll_corner_and_resizer_visual_rect);
- if (LayoutScrollbarPart* scroll_corner = ScrollCorner()) {
+ ScrollCornerNeedsPaintInvalidation())) {
+ scroll_corner_and_resizer_visual_rect_ =
+ new_scroll_corner_and_resizer_visual_rect;
+ if (LayoutCustomScrollbarPart* scroll_corner = ScrollCorner()) {
+ DCHECK(!scroll_corner->PaintingLayer());
ObjectPaintInvalidator(*scroll_corner)
- .InvalidateDisplayItemClientsIncludingNonCompositingDescendants(
- PaintInvalidationReason::kScrollControl);
+ .InvalidateDisplayItemClient(*scroll_corner,
+ PaintInvalidationReason::kScrollControl);
}
- if (LayoutScrollbarPart* resizer = Resizer()) {
- ObjectPaintInvalidator(*resizer)
- .InvalidateDisplayItemClientsIncludingNonCompositingDescendants(
- PaintInvalidationReason::kScrollControl);
+ if (LayoutCustomScrollbarPart* resizer = Resizer()) {
+ DCHECK(!resizer->PaintingLayer());
+ ObjectPaintInvalidator(*resizer).InvalidateDisplayItemClient(
+ *resizer, PaintInvalidationReason::kScrollControl);
}
+
+ context.painting_layer->SetNeedsRepaint();
+ ObjectPaintInvalidator(*GetLayoutBox())
+ .InvalidateDisplayItemClient(GetScrollCornerDisplayItemClient(),
+ PaintInvalidationReason::kLayout);
}
ClearNeedsPaintInvalidationForScrollControls();
}
-void PaintLayerScrollableArea::ClearPreviousVisualRects() {
- SetHorizontalScrollbarVisualRect(LayoutRect());
- SetVerticalScrollbarVisualRect(LayoutRect());
- SetScrollCornerAndResizerVisualRect(LayoutRect());
-}
-
-void PaintLayerScrollableArea::SetHorizontalScrollbarVisualRect(
- const LayoutRect& rect) {
- horizontal_scrollbar_visual_rect_ = rect;
- if (Scrollbar* scrollbar = HorizontalScrollbar())
- scrollbar->SetVisualRect(rect);
-}
-
-void PaintLayerScrollableArea::SetVerticalScrollbarVisualRect(
- const LayoutRect& rect) {
- vertical_scrollbar_visual_rect_ = rect;
- if (Scrollbar* scrollbar = VerticalScrollbar())
- scrollbar->SetVisualRect(rect);
-}
-
-void PaintLayerScrollableArea::SetScrollCornerAndResizerVisualRect(
- const LayoutRect& rect) {
- scroll_corner_and_resizer_visual_rect_ = rect;
- if (LayoutScrollbarPart* scroll_corner = ScrollCorner())
- scroll_corner->GetMutableForPainting().FirstFragment().SetVisualRect(rect);
- if (LayoutScrollbarPart* resizer = Resizer())
- resizer->GetMutableForPainting().FirstFragment().SetVisualRect(rect);
-}
-
void PaintLayerScrollableArea::ScrollControlWasSetNeedsPaintInvalidation() {
- GetLayoutBox()->SetMayNeedPaintInvalidation();
+ SetShouldCheckForPaintInvalidation();
}
void PaintLayerScrollableArea::DidScrollWithScrollbar(
ScrollbarPart part,
- ScrollbarOrientation orientation) {
+ ScrollbarOrientation orientation,
+ WebInputEvent::Type type) {
WebFeature scrollbar_use_uma;
switch (part) {
- case kBackButtonStartPart:
- case kForwardButtonStartPart:
case kBackButtonEndPart:
+ case kForwardButtonStartPart:
+ UseCounter::Count(
+ GetLayoutBox()->GetDocument(),
+ WebFeature::kScrollbarUseScrollbarButtonReversedDirection);
+ [[fallthrough]];
+ case kBackButtonStartPart:
case kForwardButtonEndPart:
scrollbar_use_uma =
(orientation == kVerticalScrollbar
: WebFeature::kScrollbarUseHorizontalScrollbarButton);
break;
case kThumbPart:
- scrollbar_use_uma =
- (orientation == kVerticalScrollbar
- ? WebFeature::kScrollbarUseVerticalScrollbarThumb
- : WebFeature::kScrollbarUseHorizontalScrollbarThumb);
+ if (orientation == kVerticalScrollbar) {
+ scrollbar_use_uma =
+ (WebInputEvent::IsMouseEventType(type)
+ ? WebFeature::kVerticalScrollbarThumbScrollingWithMouse
+ : WebFeature::kVerticalScrollbarThumbScrollingWithTouch);
+ } else {
+ scrollbar_use_uma =
+ (WebInputEvent::IsMouseEventType(type)
+ ? WebFeature::kHorizontalScrollbarThumbScrollingWithMouse
+ : WebFeature::kHorizontalScrollbarThumbScrollingWithTouch);
+ }
break;
case kBackTrackPart:
case kForwardTrackPart:
return;
}
- UseCounter::Count(GetLayoutBox()->GetDocument(), scrollbar_use_uma);
+ Document& document = GetLayoutBox()->GetDocument();
+
+ UseCounter::Count(document, scrollbar_use_uma);
}
-CompositorElementId PaintLayerScrollableArea::GetCompositorElementId() const {
+CompositorElementId PaintLayerScrollableArea::GetScrollElementId() const {
return CompositorElementIdFromUniqueObjectId(
GetLayoutBox()->UniqueId(), CompositorElementIdNamespace::kScroll);
}
+gfx::Size PaintLayerScrollableArea::PixelSnappedBorderBoxSize() const {
+ // TODO(crbug.com/1020913): We use this method during
+ // PositionOverflowControls() even before the paint offset is updated.
+ // This can be fixed only after we support subpixels in overflow control
+ // geometry. For now we ensure correct pixel snapping of overflow controls by
+ // calling PositionOverflowControls() again when paint offset is updated.
+ return GetLayoutBox()->PixelSnappedBorderBoxSize(
+ GetLayoutBox()->FirstFragment().PaintOffset());
+}
+
+gfx::Rect PaintLayerScrollableArea::ScrollingBackgroundVisualRect(
+ const PhysicalOffset& paint_offset) const {
+ const auto* box = GetLayoutBox();
+ auto clip_rect = box->OverflowClipRect(paint_offset);
+ auto overflow_clip_rect = ToPixelSnappedRect(clip_rect);
+ auto scroll_size = PixelSnappedContentsSize(clip_rect.offset);
+ // Ensure scrolling contents are at least as large as the scroll clip
+ scroll_size.SetToMax(overflow_clip_rect.size());
+ gfx::Rect result(overflow_clip_rect.origin(), scroll_size);
+
+ // The HTML element of a document is special, in that it can have a transform,
+ // but the bounds of the painted area of the element still extends beyond
+ // its actual size to encompass the entire viewport canvas. This is
+ // accomplished in ViewPainter by starting with a rect in viewport canvas
+ // space that is equal to the size of the viewport canvas, then mapping it
+ // into the local border box space of the HTML element, and painting a rect
+ // equal to the bounding box of the result. We need to add in that mapped rect
+ // in such cases.
+ const Document& document = box->GetDocument();
+ if (IsA<LayoutView>(box) &&
+ (document.IsXMLDocument() || document.IsHTMLDocument())) {
+ if (const auto* document_element = document.documentElement()) {
+ if (const auto* document_element_object =
+ document_element->GetLayoutObject()) {
+ const auto& document_element_state =
+ document_element_object->FirstFragment().LocalBorderBoxProperties();
+ const auto& view_contents_state =
+ box->FirstFragment().ContentsProperties();
+ gfx::Rect result_in_view = result;
+ GeometryMapper::SourceToDestinationRect(
+ view_contents_state.Transform(), document_element_state.Transform(),
+ result_in_view);
+ result.Union(result_in_view);
+ }
+ }
+ }
+
+ return result;
+}
+
+String
+PaintLayerScrollableArea::ScrollingBackgroundDisplayItemClient::DebugName()
+ const {
+ return "Scrolling background of " +
+ scrollable_area_->GetLayoutBox()->DebugName();
+}
+
+DOMNodeId
+PaintLayerScrollableArea::ScrollingBackgroundDisplayItemClient::OwnerNodeId()
+ const {
+ return static_cast<const DisplayItemClient*>(scrollable_area_->GetLayoutBox())
+ ->OwnerNodeId();
+}
+
+String PaintLayerScrollableArea::ScrollCornerDisplayItemClient::DebugName()
+ const {
+ return "Scroll corner of " + scrollable_area_->GetLayoutBox()->DebugName();
+}
+
+DOMNodeId PaintLayerScrollableArea::ScrollCornerDisplayItemClient::OwnerNodeId()
+ const {
+ return static_cast<const DisplayItemClient*>(scrollable_area_->GetLayoutBox())
+ ->OwnerNodeId();
+}
+
} // namespace blink