2 * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc. All rights reserved.
4 * Portions are Copyright (C) 1998 Netscape Communications Corporation.
7 * Robert O'Callahan <roc+@cs.cmu.edu>
8 * David Baron <dbaron@fas.harvard.edu>
9 * Christian Biesinger <cbiesinger@web.de>
10 * Randall Jesup <rjesup@wgate.com>
11 * Roland Mainz <roland.mainz@informatik.med.uni-giessen.de>
12 * Josh Soref <timeless@mac.com>
13 * Boris Zbarsky <bzbarsky@mit.edu>
15 * This library is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU Lesser General Public
17 * License as published by the Free Software Foundation; either
18 * version 2.1 of the License, or (at your option) any later version.
20 * This library is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * Lesser General Public License for more details.
25 * You should have received a copy of the GNU Lesser General Public
26 * License along with this library; if not, write to the Free Software
27 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
29 * Alternatively, the contents of this file may be used under the terms
30 * of either the Mozilla Public License Version 1.1, found at
31 * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public
32 * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html
33 * (the "GPL"), in which case the provisions of the MPL or the GPL are
34 * applicable instead of those above. If you wish to allow use of your
35 * version of this file only under the terms of one of those two
36 * licenses (the MPL or the GPL) and not to allow others to use your
37 * version of this file under the LGPL, indicate your decision by
38 * deletingthe provisions above and replace them with the notice and
39 * other provisions required by the MPL or the GPL, as the case may be.
40 * If you do not delete the provisions above, a recipient may use your
41 * version of this file under any of the LGPL, the MPL or the GPL.
45 #include "core/paint/DeprecatedPaintLayerScrollableArea.h"
47 #include "core/css/PseudoStyleRequest.h"
48 #include "core/dom/AXObjectCache.h"
49 #include "core/dom/Node.h"
50 #include "core/dom/shadow/ShadowRoot.h"
51 #include "core/editing/FrameSelection.h"
52 #include "core/frame/FrameHost.h"
53 #include "core/frame/FrameView.h"
54 #include "core/frame/LocalFrame.h"
55 #include "core/frame/Settings.h"
56 #include "core/html/HTMLFrameOwnerElement.h"
57 #include "core/input/EventHandler.h"
58 #include "core/layout/LayoutGeometryMap.h"
59 #include "core/layout/LayoutPart.h"
60 #include "core/layout/LayoutScrollbar.h"
61 #include "core/layout/LayoutScrollbarPart.h"
62 #include "core/layout/LayoutTheme.h"
63 #include "core/layout/LayoutView.h"
64 #include "core/layout/compositing/CompositedDeprecatedPaintLayerMapping.h"
65 #include "core/layout/compositing/DeprecatedPaintLayerCompositor.h"
66 #include "core/page/ChromeClient.h"
67 #include "core/page/FocusController.h"
68 #include "core/page/Page.h"
69 #include "core/page/scrolling/ScrollingCoordinator.h"
70 #include "core/paint/DeprecatedPaintLayerFragment.h"
71 #include "platform/PlatformGestureEvent.h"
72 #include "platform/PlatformMouseEvent.h"
73 #include "platform/graphics/GraphicsLayer.h"
74 #include "platform/graphics/paint/DrawingRecorder.h"
75 #include "platform/scroll/ScrollAnimator.h"
76 #include "platform/scroll/ScrollbarTheme.h"
77 #include "public/platform/Platform.h"
81 const int ResizerControlExpandRatioForTouch
= 2;
83 DeprecatedPaintLayerScrollableArea::DeprecatedPaintLayerScrollableArea(DeprecatedPaintLayer
& layer
)
85 , m_inResizeMode(false)
86 , m_scrollsOverflow(false)
87 , m_inOverflowRelayout(false)
88 , m_nextTopmostScrollChild(0)
89 , m_topmostScrollChild(0)
90 , m_needsCompositedScrolling(false)
91 , m_scrollCorner(nullptr)
94 , m_hasBeenDisposed(false)
97 Node
* node
= box().node();
98 if (node
&& node
->isElementNode()) {
99 // We save and restore only the scrollOffset as the other scroll values are recalculated.
100 Element
* element
= toElement(node
);
101 m_scrollOffset
= element
->savedLayerScrollOffset();
102 if (!m_scrollOffset
.isZero())
103 scrollAnimator()->setCurrentPosition(FloatPoint(m_scrollOffset
.width(), m_scrollOffset
.height()));
104 element
->setSavedLayerScrollOffset(IntSize());
106 updateResizerAreaSet();
109 DeprecatedPaintLayerScrollableArea::~DeprecatedPaintLayerScrollableArea()
111 ASSERT(m_hasBeenDisposed
);
114 void DeprecatedPaintLayerScrollableArea::dispose()
116 if (inResizeMode() && !box().documentBeingDestroyed()) {
117 if (LocalFrame
* frame
= box().frame())
118 frame
->eventHandler().resizeScrollableAreaDestroyed();
121 if (LocalFrame
* frame
= box().frame()) {
122 if (FrameView
* frameView
= frame
->view()) {
123 frameView
->removeScrollableArea(this);
124 frameView
->removeAnimatingScrollableArea(this);
128 if (box().frame() && box().frame()->page()) {
129 if (ScrollingCoordinator
* scrollingCoordinator
= box().frame()->page()->scrollingCoordinator())
130 scrollingCoordinator
->willDestroyScrollableArea(this);
133 if (!box().documentBeingDestroyed()) {
134 Node
* node
= box().node();
135 // FIXME: Make setSavedLayerScrollOffset take DoubleSize. crbug.com/414283.
136 if (node
&& node
->isElementNode())
137 toElement(node
)->setSavedLayerScrollOffset(flooredIntSize(m_scrollOffset
));
140 if (LocalFrame
* frame
= box().frame()) {
141 if (FrameView
* frameView
= frame
->view())
142 frameView
->removeResizerArea(box());
145 destroyScrollbar(HorizontalScrollbar
);
146 destroyScrollbar(VerticalScrollbar
);
149 m_scrollCorner
->destroy();
151 m_resizer
->destroy();
153 clearScrollAnimators();
156 m_hasBeenDisposed
= true;
160 DEFINE_TRACE(DeprecatedPaintLayerScrollableArea
)
162 visitor
->trace(m_hBar
);
163 visitor
->trace(m_vBar
);
164 ScrollableArea::trace(visitor
);
167 HostWindow
* DeprecatedPaintLayerScrollableArea::hostWindow() const
169 if (Page
* page
= box().frame()->page())
170 return &page
->chromeClient();
174 GraphicsLayer
* DeprecatedPaintLayerScrollableArea::layerForScrolling() const
176 return layer()->hasCompositedDeprecatedPaintLayerMapping() ? layer()->compositedDeprecatedPaintLayerMapping()->scrollingContentsLayer() : 0;
179 GraphicsLayer
* DeprecatedPaintLayerScrollableArea::layerForHorizontalScrollbar() const
181 // See crbug.com/343132.
182 DisableCompositingQueryAsserts disabler
;
184 return layer()->hasCompositedDeprecatedPaintLayerMapping() ? layer()->compositedDeprecatedPaintLayerMapping()->layerForHorizontalScrollbar() : 0;
187 GraphicsLayer
* DeprecatedPaintLayerScrollableArea::layerForVerticalScrollbar() const
189 // See crbug.com/343132.
190 DisableCompositingQueryAsserts disabler
;
192 return layer()->hasCompositedDeprecatedPaintLayerMapping() ? layer()->compositedDeprecatedPaintLayerMapping()->layerForVerticalScrollbar() : 0;
195 GraphicsLayer
* DeprecatedPaintLayerScrollableArea::layerForScrollCorner() const
197 // See crbug.com/343132.
198 DisableCompositingQueryAsserts disabler
;
200 return layer()->hasCompositedDeprecatedPaintLayerMapping() ? layer()->compositedDeprecatedPaintLayerMapping()->layerForScrollCorner() : 0;
203 void DeprecatedPaintLayerScrollableArea::invalidateScrollbarRect(Scrollbar
* scrollbar
, const IntRect
& rect
)
205 // See crbug.com/343132.
206 DisableCompositingQueryAsserts disabler
;
208 ASSERT(scrollbar
== m_hBar
.get() || scrollbar
== m_vBar
.get());
209 ASSERT(scrollbar
== m_hBar
.get() ? !layerForHorizontalScrollbar() : !layerForVerticalScrollbar());
211 IntRect scrollRect
= rect
;
212 // If we are not yet inserted into the tree, there is no need to issue paint invaldiations.
213 if (!box().isLayoutView() && !box().parent())
216 if (scrollbar
== m_vBar
.get())
217 scrollRect
.move(verticalScrollbarStart(0, box().size().width()), box().borderTop());
219 scrollRect
.move(horizontalScrollbarStart(0), box().size().height() - box().borderBottom() - scrollbar
->height());
221 if (scrollRect
.isEmpty())
224 box().invalidateDisplayItemClient(*scrollbar
);
226 LayoutRect paintInvalidationRect
= LayoutRect(scrollRect
);
227 box().flipForWritingMode(paintInvalidationRect
);
229 IntRect intRect
= pixelSnappedIntRect(paintInvalidationRect
);
231 if (box().frameView()->isInPerformLayout()) {
232 addScrollbarDamage(scrollbar
, intRect
);
234 // FIXME: We should not allow paint invalidation out of paint invalidation state. crbug.com/457415
235 DisablePaintInvalidationStateAsserts disabler
;
236 // We have invalidated the displayItemClient of the scrollbar, but for now we still need to
237 // invalidate the rectangles to trigger repaints.
238 box().invalidatePaintRectangleNotInvalidatingDisplayItemClients(LayoutRect(intRect
));
242 void DeprecatedPaintLayerScrollableArea::invalidateScrollCornerRect(const IntRect
& rect
)
244 ASSERT(!layerForScrollCorner());
246 if (m_scrollCorner
) {
247 // FIXME: We should not allow paint invalidation out of paint invalidation state. crbug.com/457415
248 DisablePaintInvalidationStateAsserts disabler
;
249 m_scrollCorner
->invalidatePaintRectangle(LayoutRect(rect
));
250 box().invalidateDisplayItemClientForNonCompositingDescendantsOf(*m_scrollCorner
);
252 box().invalidateDisplayItemClient(box());
255 m_resizer
->invalidatePaintRectangle(LayoutRect(rect
));
256 box().invalidateDisplayItemClientForNonCompositingDescendantsOf(*m_resizer
);
260 bool DeprecatedPaintLayerScrollableArea::shouldUseIntegerScrollOffset() const
262 Frame
* frame
= box().frame();
263 if (frame
->settings() && !frame
->settings()->preferCompositingToLCDTextEnabled())
266 return ScrollableArea::shouldUseIntegerScrollOffset();
269 bool DeprecatedPaintLayerScrollableArea::isActive() const
271 Page
* page
= box().frame()->page();
272 return page
&& page
->focusController().isActive();
275 bool DeprecatedPaintLayerScrollableArea::isScrollCornerVisible() const
277 return !scrollCornerRect().isEmpty();
280 static int cornerStart(const ComputedStyle
& style
, int minX
, int maxX
, int thickness
)
282 if (style
.shouldPlaceBlockDirectionScrollbarOnLogicalLeft())
283 return minX
+ style
.borderLeftWidth();
284 return maxX
- thickness
- style
.borderRightWidth();
287 static IntRect
cornerRect(const ComputedStyle
& style
, const Scrollbar
* horizontalScrollbar
, const Scrollbar
* verticalScrollbar
, const IntRect
& bounds
)
289 int horizontalThickness
;
290 int verticalThickness
;
291 if (!verticalScrollbar
&& !horizontalScrollbar
) {
292 // FIXME: This isn't right. We need to know the thickness of custom scrollbars
293 // even when they don't exist in order to set the resizer square size properly.
294 horizontalThickness
= ScrollbarTheme::theme()->scrollbarThickness();
295 verticalThickness
= horizontalThickness
;
296 } else if (verticalScrollbar
&& !horizontalScrollbar
) {
297 horizontalThickness
= verticalScrollbar
->width();
298 verticalThickness
= horizontalThickness
;
299 } else if (horizontalScrollbar
&& !verticalScrollbar
) {
300 verticalThickness
= horizontalScrollbar
->height();
301 horizontalThickness
= verticalThickness
;
303 horizontalThickness
= verticalScrollbar
->width();
304 verticalThickness
= horizontalScrollbar
->height();
306 return IntRect(cornerStart(style
, bounds
.x(), bounds
.maxX(), horizontalThickness
),
307 bounds
.maxY() - verticalThickness
- style
.borderBottomWidth(),
308 horizontalThickness
, verticalThickness
);
312 IntRect
DeprecatedPaintLayerScrollableArea::scrollCornerRect() const
314 // We have a scrollbar corner when a scrollbar is visible and not filling the entire length of the box.
315 // This happens when:
316 // (a) A resizer is present and at least one scrollbar is present
317 // (b) Both scrollbars are present.
318 bool hasHorizontalBar
= horizontalScrollbar();
319 bool hasVerticalBar
= verticalScrollbar();
320 bool hasResizer
= box().style()->resize() != RESIZE_NONE
;
321 if ((hasHorizontalBar
&& hasVerticalBar
) || (hasResizer
&& (hasHorizontalBar
|| hasVerticalBar
)))
322 return cornerRect(box().styleRef(), horizontalScrollbar(), verticalScrollbar(), box().pixelSnappedBorderBoxRect());
326 IntRect
DeprecatedPaintLayerScrollableArea::convertFromScrollbarToContainingView(const Scrollbar
* scrollbar
, const IntRect
& scrollbarRect
) const
328 LayoutView
* view
= box().view();
330 return scrollbarRect
;
332 IntRect rect
= scrollbarRect
;
333 rect
.move(scrollbarOffset(scrollbar
));
335 return view
->frameView()->convertFromLayoutObject(box(), rect
);
338 IntRect
DeprecatedPaintLayerScrollableArea::convertFromContainingViewToScrollbar(const Scrollbar
* scrollbar
, const IntRect
& parentRect
) const
340 LayoutView
* view
= box().view();
344 IntRect rect
= view
->frameView()->convertToLayoutObject(box(), parentRect
);
345 rect
.move(-scrollbarOffset(scrollbar
));
349 IntPoint
DeprecatedPaintLayerScrollableArea::convertFromScrollbarToContainingView(const Scrollbar
* scrollbar
, const IntPoint
& scrollbarPoint
) const
351 LayoutView
* view
= box().view();
353 return scrollbarPoint
;
355 IntPoint point
= scrollbarPoint
;
356 point
.move(scrollbarOffset(scrollbar
));
357 return view
->frameView()->convertFromLayoutObject(box(), point
);
360 IntPoint
DeprecatedPaintLayerScrollableArea::convertFromContainingViewToScrollbar(const Scrollbar
* scrollbar
, const IntPoint
& parentPoint
) const
362 LayoutView
* view
= box().view();
366 IntPoint point
= view
->frameView()->convertToLayoutObject(box(), parentPoint
);
368 point
.move(-scrollbarOffset(scrollbar
));
372 int DeprecatedPaintLayerScrollableArea::scrollSize(ScrollbarOrientation orientation
) const
374 IntSize scrollDimensions
= maximumScrollPosition() - minimumScrollPosition();
375 return (orientation
== HorizontalScrollbar
) ? scrollDimensions
.width() : scrollDimensions
.height();
378 void DeprecatedPaintLayerScrollableArea::setScrollOffset(const IntPoint
& newScrollOffset
, ScrollType scrollType
)
380 setScrollOffset(DoublePoint(newScrollOffset
), scrollType
);
383 void DeprecatedPaintLayerScrollableArea::setScrollOffset(const DoublePoint
& newScrollOffset
, ScrollType
)
385 if (scrollOffset() == toDoubleSize(newScrollOffset
))
388 DoubleSize scrollDelta
= scrollOffset() - toDoubleSize(newScrollOffset
);
389 m_scrollOffset
= toDoubleSize(newScrollOffset
);
391 LocalFrame
* frame
= box().frame();
394 RefPtrWillBeRawPtr
<FrameView
> frameView
= box().frameView();
396 TRACE_EVENT1("devtools.timeline", "ScrollLayer", "data", InspectorScrollLayerEvent::data(&box()));
398 // FIXME(420741): Resolve circular dependency between scroll offset and
399 // compositing state, and remove this disabler.
400 DisableCompositingQueryAsserts disabler
;
402 // Update the positions of our child layers (if needed as only fixed layers should be impacted by a scroll).
403 // We don't update compositing layers, because we need to do a deep update from the compositing ancestor.
404 if (!frameView
->isInPerformLayout()) {
405 // If we're in the middle of layout, we'll just update layers once layout has finished.
406 layer()->updateLayerPositionsAfterOverflowScroll(scrollDelta
);
407 // Update regions, scrolling may change the clip of a particular region.
408 frameView
->updateAnnotatedRegions();
409 frameView
->setNeedsUpdateWidgetPositions();
410 updateCompositingLayersAfterScroll();
413 const LayoutBoxModelObject
* paintInvalidationContainer
= box().containerForPaintInvalidation();
414 // The caret rect needs to be invalidated after scrolling
415 frame
->selection().setCaretRectNeedsUpdate();
417 FloatQuad quadForFakeMouseMoveEvent
= FloatQuad(FloatRect(layer()->layoutObject()->previousPaintInvalidationRectIncludingCompositedScrolling(*paintInvalidationContainer
)));
419 quadForFakeMouseMoveEvent
= paintInvalidationContainer
->localToAbsoluteQuad(quadForFakeMouseMoveEvent
);
420 frame
->eventHandler().dispatchFakeMouseMoveEventSoonInQuad(quadForFakeMouseMoveEvent
);
422 bool requiresPaintInvalidation
= true;
424 if (box().view()->compositor()->inCompositingMode()) {
425 bool onlyScrolledCompositedLayers
= scrollsOverflow()
426 && !layer()->hasVisibleNonLayerContent()
427 && !layer()->hasNonCompositedChild()
428 && !layer()->hasBlockSelectionGapBounds()
429 && box().style()->backgroundLayers().attachment() != LocalBackgroundAttachment
;
431 if (usesCompositedScrolling() || onlyScrolledCompositedLayers
)
432 requiresPaintInvalidation
= false;
435 // Only the root layer can overlap non-composited fixed-position elements.
436 if (!requiresPaintInvalidation
&& layer()->isRootLayer() && frameView
->hasViewportConstrainedObjects()) {
437 if (!frameView
->invalidateViewportConstrainedObjects())
438 requiresPaintInvalidation
= true;
441 // Just schedule a full paint invalidation of our object.
442 // FIXME: This invalidation will be unnecessary in slimming paint phase 2.
443 if (requiresPaintInvalidation
) {
444 box().setShouldDoFullPaintInvalidationIncludingNonCompositingDescendants();
445 frameView
->setFrameTimingRequestsDirty(true);
448 // Schedule the scroll DOM event.
450 box().node()->document().enqueueScrollEventForNode(box().node());
452 if (AXObjectCache
* cache
= box().document().existingAXObjectCache())
453 cache
->handleScrollPositionChanged(&box());
454 box().view()->clearHitTestCache();
456 // Inform the FrameLoader of the new scroll position, so it can be restored when navigating back.
457 if (layer()->isRootLayer())
458 frameView
->frame().loader().saveScrollState();
460 // All scrolls clear the scroll anchor.
461 frameView
->clearScrollAnchor();
464 IntPoint
DeprecatedPaintLayerScrollableArea::scrollPosition() const
466 return IntPoint(flooredIntSize(m_scrollOffset
));
469 DoublePoint
DeprecatedPaintLayerScrollableArea::scrollPositionDouble() const
471 return DoublePoint(m_scrollOffset
);
474 IntPoint
DeprecatedPaintLayerScrollableArea::minimumScrollPosition() const
476 return -scrollOrigin();
479 IntPoint
DeprecatedPaintLayerScrollableArea::maximumScrollPosition() const
483 if (layer()->isRootLayer()) {
484 FrameView
* frameView
= box().frameView();
485 contentSize
= frameView
->contentsSize();
486 visibleSize
= frameView
->visibleContentSize(ExcludeScrollbars
) + frameView
->topControlsSize();
487 } else if (box().hasOverflowClip()) {
488 contentSize
= IntSize(pixelSnappedScrollWidth(), pixelSnappedScrollHeight());
489 visibleSize
= enclosingIntRect(box().clientBoxRect()).size();
491 return -scrollOrigin() + (contentSize
- visibleSize
);
494 IntRect
DeprecatedPaintLayerScrollableArea::visibleContentRect(IncludeScrollbarsInRect scrollbarInclusion
) const
496 int verticalScrollbarWidth
= 0;
497 int horizontalScrollbarHeight
= 0;
498 if (scrollbarInclusion
== IncludeScrollbars
) {
499 verticalScrollbarWidth
= (verticalScrollbar() && !verticalScrollbar()->isOverlayScrollbar()) ? verticalScrollbar()->width() : 0;
500 horizontalScrollbarHeight
= (horizontalScrollbar() && !horizontalScrollbar()->isOverlayScrollbar()) ? horizontalScrollbar()->height() : 0;
503 return IntRect(IntPoint(scrollXOffset(), scrollYOffset()),
504 IntSize(max(0, layer()->size().width() - verticalScrollbarWidth
), max(0, layer()->size().height() - horizontalScrollbarHeight
)));
507 int DeprecatedPaintLayerScrollableArea::visibleHeight() const
509 return layer()->size().height();
512 int DeprecatedPaintLayerScrollableArea::visibleWidth() const
514 return layer()->size().width();
517 IntSize
DeprecatedPaintLayerScrollableArea::contentsSize() const
519 return IntSize(scrollWidth(), scrollHeight());
522 IntPoint
DeprecatedPaintLayerScrollableArea::lastKnownMousePosition() const
524 return box().frame() ? box().frame()->eventHandler().lastKnownMousePosition() : IntPoint();
527 bool DeprecatedPaintLayerScrollableArea::scrollAnimatorEnabled() const
529 if (Settings
* settings
= box().frame()->settings())
530 return settings
->scrollAnimatorEnabled();
534 bool DeprecatedPaintLayerScrollableArea::shouldSuspendScrollAnimations() const
536 LayoutView
* view
= box().view();
539 return view
->frameView()->shouldSuspendScrollAnimations();
542 void DeprecatedPaintLayerScrollableArea::scrollbarVisibilityChanged()
544 if (LayoutView
* view
= box().view())
545 return view
->clearHitTestCache();
548 bool DeprecatedPaintLayerScrollableArea::scrollbarsCanBeActive() const
550 LayoutView
* view
= box().view();
553 return view
->frameView()->scrollbarsCanBeActive();
556 IntRect
DeprecatedPaintLayerScrollableArea::scrollableAreaBoundingBox() const
558 return box().absoluteBoundingBoxRect();
561 void DeprecatedPaintLayerScrollableArea::registerForAnimation()
563 if (LocalFrame
* frame
= box().frame()) {
564 if (FrameView
* frameView
= frame
->view())
565 frameView
->addAnimatingScrollableArea(this);
569 void DeprecatedPaintLayerScrollableArea::deregisterForAnimation()
571 if (LocalFrame
* frame
= box().frame()) {
572 if (FrameView
* frameView
= frame
->view())
573 frameView
->removeAnimatingScrollableArea(this);
577 bool DeprecatedPaintLayerScrollableArea::userInputScrollable(ScrollbarOrientation orientation
) const
579 if (box().isIntrinsicallyScrollable(orientation
))
582 EOverflow overflowStyle
= (orientation
== HorizontalScrollbar
) ?
583 box().style()->overflowX() : box().style()->overflowY();
584 return (overflowStyle
== OSCROLL
|| overflowStyle
== OAUTO
|| overflowStyle
== OOVERLAY
);
587 bool DeprecatedPaintLayerScrollableArea::shouldPlaceVerticalScrollbarOnLeft() const
589 return box().style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft();
592 int DeprecatedPaintLayerScrollableArea::pageStep(ScrollbarOrientation orientation
) const
594 int length
= (orientation
== HorizontalScrollbar
) ?
595 box().pixelSnappedClientWidth() : box().pixelSnappedClientHeight();
596 int minPageStep
= static_cast<float>(length
) * ScrollableArea::minFractionToStepWhenPaging();
597 int pageStep
= max(minPageStep
, length
- ScrollableArea::maxOverlapBetweenPages());
599 return max(pageStep
, 1);
602 LayoutBox
& DeprecatedPaintLayerScrollableArea::box() const
604 return *m_layer
.layoutBox();
607 DeprecatedPaintLayer
* DeprecatedPaintLayerScrollableArea::layer() const
612 LayoutUnit
DeprecatedPaintLayerScrollableArea::scrollWidth() const
614 return m_overflowRect
.width();
617 LayoutUnit
DeprecatedPaintLayerScrollableArea::scrollHeight() const
619 return m_overflowRect
.height();
622 int DeprecatedPaintLayerScrollableArea::pixelSnappedScrollWidth() const
624 return snapSizeToPixel(scrollWidth(), box().clientLeft() + box().location().x());
627 int DeprecatedPaintLayerScrollableArea::pixelSnappedScrollHeight() const
629 return snapSizeToPixel(scrollHeight(), box().clientTop() + box().location().y());
632 void DeprecatedPaintLayerScrollableArea::computeScrollDimensions()
634 m_overflowRect
= box().layoutOverflowRect();
635 box().flipForWritingMode(m_overflowRect
);
637 int scrollableLeftOverflow
= m_overflowRect
.x() - box().borderLeft() - (box().style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft() ? box().verticalScrollbarWidth() : 0);
638 int scrollableTopOverflow
= m_overflowRect
.y() - box().borderTop();
639 setScrollOrigin(IntPoint(-scrollableLeftOverflow
, -scrollableTopOverflow
));
642 void DeprecatedPaintLayerScrollableArea::scrollToPosition(const DoublePoint
& scrollPosition
, ScrollOffsetClamping clamp
, ScrollBehavior scrollBehavior
)
644 cancelProgrammaticScrollAnimation();
646 DoublePoint newScrollPosition
= clamp
== ScrollOffsetClamped
? clampScrollPosition(scrollPosition
) : scrollPosition
;
647 if (newScrollPosition
!= scrollPositionDouble())
648 ScrollableArea::setScrollPosition(newScrollPosition
, ProgrammaticScroll
, scrollBehavior
);
651 void DeprecatedPaintLayerScrollableArea::updateScrollDimensions(DoubleSize
& scrollOffset
, bool& autoHorizontalScrollBarChanged
, bool& autoVerticalScrollBarChanged
)
653 ASSERT(box().hasOverflowClip());
655 if (needsScrollbarReconstruction()) {
657 destroyScrollbar(HorizontalScrollbar
);
659 destroyScrollbar(VerticalScrollbar
);
662 scrollOffset
= adjustedScrollOffset();
663 computeScrollDimensions();
664 bool hasHorizontalOverflow
= this->hasHorizontalOverflow();
665 bool hasVerticalOverflow
= this->hasVerticalOverflow();
666 if (hasOverlayScrollbars()) {
667 if (!scrollSize(HorizontalScrollbar
))
668 setHasHorizontalScrollbar(false);
669 if (!scrollSize(VerticalScrollbar
))
670 setHasVerticalScrollbar(false);
673 // overflow:auto may need to lay out again if scrollbars got added/removed.
674 autoHorizontalScrollBarChanged
= (box().hasAutoHorizontalScrollbar() && (hasHorizontalScrollbar() != hasHorizontalOverflow
)) || (box().style()->overflowX() == OSCROLL
&& !horizontalScrollbar());
675 autoVerticalScrollBarChanged
= (box().hasAutoVerticalScrollbar() && (hasVerticalScrollbar() != hasVerticalOverflow
)) || (box().style()->overflowY() == OSCROLL
&& !verticalScrollbar());
676 if (!visualViewportSuppliesScrollbars() && (autoHorizontalScrollBarChanged
|| autoVerticalScrollBarChanged
)) {
677 if (box().hasAutoHorizontalScrollbar() || (box().style()->overflowX() == OSCROLL
&& !horizontalScrollbar()))
678 setHasHorizontalScrollbar(box().style()->overflowX() == OSCROLL
? true : hasHorizontalOverflow
);
679 if (box().hasAutoVerticalScrollbar() || (box().style()->overflowY() == OSCROLL
&& !verticalScrollbar()))
680 setHasVerticalScrollbar(box().style()->overflowY() == OSCROLL
? true : hasVerticalOverflow
);
684 void DeprecatedPaintLayerScrollableArea::finalizeScrollDimensions(const DoubleSize
& originalScrollOffset
, bool autoHorizontalScrollBarChanged
, bool autoVerticalScrollBarChanged
)
686 ASSERT(box().hasOverflowClip());
688 // Layout may cause us to be at an invalid scroll position. In this case we need
689 // to pull our scroll offsets back to the max (or push them up to the min).
690 DoublePoint clampedScrollPosition
= clampScrollPosition(scrollPositionDouble());
691 if (clampedScrollPosition
!= scrollPositionDouble())
692 scrollToPosition(clampedScrollPosition
);
694 if (originalScrollOffset
!= adjustedScrollOffset()) {
695 DoublePoint
origin(scrollOrigin());
696 scrollPositionChanged(-origin
+ adjustedScrollOffset(), ProgrammaticScroll
);
699 bool hasHorizontalOverflow
= this->hasHorizontalOverflow();
700 bool hasVerticalOverflow
= this->hasVerticalOverflow();
703 // Hits in compositing/overflow/automatically-opt-into-composited-scrolling-after-style-change.html.
704 DisableCompositingQueryAsserts disabler
;
706 // overflow:scroll should just enable/disable.
707 if (box().style()->overflowX() == OSCROLL
&& horizontalScrollbar())
708 horizontalScrollbar()->setEnabled(hasHorizontalOverflow
);
709 if (box().style()->overflowY() == OSCROLL
&& verticalScrollbar())
710 verticalScrollbar()->setEnabled(hasVerticalOverflow
);
713 if (!visualViewportSuppliesScrollbars() && (autoHorizontalScrollBarChanged
|| autoVerticalScrollBarChanged
)) {
714 if (hasVerticalOverflow
|| hasHorizontalOverflow
)
715 updateScrollCornerStyle();
717 layer()->updateSelfPaintingLayer();
719 // Force an update since we know the scrollbars have changed things.
720 if (box().document().hasAnnotatedRegions())
721 box().document().setAnnotatedRegionsDirty(true);
723 // Our proprietary overflow: overlay value doesn't trigger a layout.
724 if ((autoHorizontalScrollBarChanged
&& box().style()->overflowX() != OOVERLAY
) || (autoVerticalScrollBarChanged
&& box().style()->overflowY() != OOVERLAY
)) {
725 if (!m_inOverflowRelayout
) {
726 m_inOverflowRelayout
= true;
727 SubtreeLayoutScope
layoutScope(box());
728 layoutScope
.setNeedsLayout(&box(), LayoutInvalidationReason::ScrollbarChanged
);
729 if (box().isLayoutBlock()) {
730 LayoutBlock
& block
= toLayoutBlock(box());
731 block
.scrollbarsChanged(autoHorizontalScrollBarChanged
, autoVerticalScrollBarChanged
);
732 block
.layoutBlock(true);
736 m_inOverflowRelayout
= false;
742 // Hits in compositing/overflow/automatically-opt-into-composited-scrolling-after-style-change.html.
743 DisableCompositingQueryAsserts disabler
;
745 // Set up the range (and page step/line step).
746 if (Scrollbar
* horizontalScrollbar
= this->horizontalScrollbar()) {
747 int clientWidth
= box().pixelSnappedClientWidth();
748 horizontalScrollbar
->setProportion(clientWidth
, overflowRect().width());
750 if (Scrollbar
* verticalScrollbar
= this->verticalScrollbar()) {
751 int clientHeight
= box().pixelSnappedClientHeight();
752 verticalScrollbar
->setProportion(clientHeight
, overflowRect().height());
756 if (hasOverlayScrollbars()) {
757 if (!scrollSize(HorizontalScrollbar
))
758 setHasHorizontalScrollbar(false);
759 if (!scrollSize(VerticalScrollbar
))
760 setHasVerticalScrollbar(false);
763 bool hasOverflow
= hasScrollableHorizontalOverflow() || hasScrollableVerticalOverflow();
764 updateScrollableAreaSet(hasOverflow
);
766 DisableCompositingQueryAsserts disabler
;
767 positionOverflowControls();
770 void DeprecatedPaintLayerScrollableArea::updateAfterLayout()
772 DoubleSize originalScrollOffset
;
773 bool autoHorizontalScrollBarChanged
;
774 bool autoVerticalScrollBarChanged
;
775 updateScrollDimensions(originalScrollOffset
, autoHorizontalScrollBarChanged
, autoVerticalScrollBarChanged
);
776 finalizeScrollDimensions(originalScrollOffset
, autoHorizontalScrollBarChanged
, autoVerticalScrollBarChanged
);
779 ScrollBehavior
DeprecatedPaintLayerScrollableArea::scrollBehaviorStyle() const
781 return box().style()->scrollBehavior();
784 bool DeprecatedPaintLayerScrollableArea::hasHorizontalOverflow() const
786 return pixelSnappedScrollWidth() > box().pixelSnappedClientWidth();
789 bool DeprecatedPaintLayerScrollableArea::hasVerticalOverflow() const
791 return pixelSnappedScrollHeight() > box().pixelSnappedClientHeight();
794 bool DeprecatedPaintLayerScrollableArea::hasScrollableHorizontalOverflow() const
796 return hasHorizontalOverflow() && box().scrollsOverflowX();
799 bool DeprecatedPaintLayerScrollableArea::hasScrollableVerticalOverflow() const
801 return hasVerticalOverflow() && box().scrollsOverflowY();
804 static bool overflowRequiresScrollbar(EOverflow overflow
)
806 return overflow
== OSCROLL
;
809 static bool overflowDefinesAutomaticScrollbar(EOverflow overflow
)
811 return overflow
== OAUTO
|| overflow
== OOVERLAY
;
814 // This function returns true if the given box requires overflow scrollbars (as
815 // opposed to the 'viewport' scrollbars managed by the DeprecatedPaintLayerCompositor).
816 // FIXME: we should use the same scrolling machinery for both the viewport and
817 // overflow. Currently, we need to avoid producing scrollbars here if they'll be
818 // handled externally in the RLC.
819 static bool canHaveOverflowScrollbars(const LayoutBox
& box
)
821 bool rootLayerScrolls
= box
.document().settings() && box
.document().settings()->rootLayerScrolls();
822 return (rootLayerScrolls
|| !box
.isLayoutView()) && box
.document().viewportDefiningElement() != box
.node();
825 void DeprecatedPaintLayerScrollableArea::updateAfterStyleChange(const ComputedStyle
* oldStyle
)
827 // Don't do this on first style recalc, before layout has ever happened.
828 if (!overflowRect().size().isZero())
829 updateScrollableAreaSet(hasScrollableHorizontalOverflow() || hasScrollableVerticalOverflow());
831 if (!canHaveOverflowScrollbars(box()))
834 // Avoid drawing two sets of scrollbars when one is provided by the visual viewport.
835 if (visualViewportSuppliesScrollbars()) {
836 setHasHorizontalScrollbar(false);
837 setHasVerticalScrollbar(false);
841 EOverflow overflowX
= box().style()->overflowX();
842 EOverflow overflowY
= box().style()->overflowY();
844 // To avoid doing a relayout in updateScrollbarsAfterLayout, we try to keep any automatic scrollbar that was already present.
845 bool needsHorizontalScrollbar
= (hasHorizontalScrollbar() && overflowDefinesAutomaticScrollbar(overflowX
)) || overflowRequiresScrollbar(overflowX
);
846 bool needsVerticalScrollbar
= (hasVerticalScrollbar() && overflowDefinesAutomaticScrollbar(overflowY
)) || overflowRequiresScrollbar(overflowY
);
847 setHasHorizontalScrollbar(needsHorizontalScrollbar
);
848 setHasVerticalScrollbar(needsVerticalScrollbar
);
850 // With overflow: scroll, scrollbars are always visible but may be disabled.
851 // When switching to another value, we need to re-enable them (see bug 11985).
852 if (needsHorizontalScrollbar
&& oldStyle
&& oldStyle
->overflowX() == OSCROLL
&& overflowX
!= OSCROLL
) {
853 ASSERT(hasHorizontalScrollbar());
854 m_hBar
->setEnabled(true);
857 if (needsVerticalScrollbar
&& oldStyle
&& oldStyle
->overflowY() == OSCROLL
&& overflowY
!= OSCROLL
) {
858 ASSERT(hasVerticalScrollbar());
859 m_vBar
->setEnabled(true);
862 // FIXME: Need to detect a swap from custom to native scrollbars (and vice versa).
864 m_hBar
->styleChanged();
866 m_vBar
->styleChanged();
868 updateScrollCornerStyle();
869 updateResizerAreaSet();
870 updateResizerStyle();
873 bool DeprecatedPaintLayerScrollableArea::updateAfterCompositingChange()
875 layer()->updateScrollingStateAfterCompositingChange();
876 const bool layersChanged
= m_topmostScrollChild
!= m_nextTopmostScrollChild
;
877 m_topmostScrollChild
= m_nextTopmostScrollChild
;
878 m_nextTopmostScrollChild
= nullptr;
879 return layersChanged
;
882 void DeprecatedPaintLayerScrollableArea::updateAfterOverflowRecalc()
884 computeScrollDimensions();
885 if (Scrollbar
* horizontalScrollbar
= this->horizontalScrollbar()) {
886 int clientWidth
= box().pixelSnappedClientWidth();
887 horizontalScrollbar
->setProportion(clientWidth
, overflowRect().width());
889 if (Scrollbar
* verticalScrollbar
= this->verticalScrollbar()) {
890 int clientHeight
= box().pixelSnappedClientHeight();
891 verticalScrollbar
->setProportion(clientHeight
, overflowRect().height());
894 bool hasHorizontalOverflow
= this->hasHorizontalOverflow();
895 bool hasVerticalOverflow
= this->hasVerticalOverflow();
896 bool autoHorizontalScrollBarChanged
= box().hasAutoHorizontalScrollbar() && (hasHorizontalScrollbar() != hasHorizontalOverflow
);
897 bool autoVerticalScrollBarChanged
= box().hasAutoVerticalScrollbar() && (hasVerticalScrollbar() != hasVerticalOverflow
);
898 if (autoHorizontalScrollBarChanged
|| autoVerticalScrollBarChanged
)
899 box().setNeedsLayoutAndFullPaintInvalidation(LayoutInvalidationReason::Unknown
);
902 IntRect
DeprecatedPaintLayerScrollableArea::rectForHorizontalScrollbar(const IntRect
& borderBoxRect
) const
907 const IntRect
& scrollCorner
= scrollCornerRect();
909 return IntRect(horizontalScrollbarStart(borderBoxRect
.x()),
910 borderBoxRect
.maxY() - box().borderBottom() - m_hBar
->height(),
911 borderBoxRect
.width() - (box().borderLeft() + box().borderRight()) - scrollCorner
.width(),
915 IntRect
DeprecatedPaintLayerScrollableArea::rectForVerticalScrollbar(const IntRect
& borderBoxRect
) const
920 const IntRect
& scrollCorner
= scrollCornerRect();
922 return IntRect(verticalScrollbarStart(borderBoxRect
.x(), borderBoxRect
.maxX()),
923 borderBoxRect
.y() + box().borderTop(),
925 borderBoxRect
.height() - (box().borderTop() + box().borderBottom()) - scrollCorner
.height());
928 LayoutUnit
DeprecatedPaintLayerScrollableArea::verticalScrollbarStart(int minX
, int maxX
) const
930 if (box().style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft())
931 return minX
+ box().borderLeft();
932 return maxX
- box().borderRight() - m_vBar
->width();
935 LayoutUnit
DeprecatedPaintLayerScrollableArea::horizontalScrollbarStart(int minX
) const
937 int x
= minX
+ box().borderLeft();
938 if (box().style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft())
939 x
+= m_vBar
? m_vBar
->width() : resizerCornerRect(box().pixelSnappedBorderBoxRect(), ResizerForPointer
).width();
943 IntSize
DeprecatedPaintLayerScrollableArea::scrollbarOffset(const Scrollbar
* scrollbar
) const
945 if (scrollbar
== m_vBar
.get())
946 return IntSize(verticalScrollbarStart(0, box().size().width()), box().borderTop());
948 if (scrollbar
== m_hBar
.get())
949 return IntSize(horizontalScrollbarStart(0), box().size().height() - box().borderBottom() - scrollbar
->height());
951 ASSERT_NOT_REACHED();
955 static inline LayoutObject
* layoutObjectForScrollbar(LayoutObject
& layoutObject
)
957 if (Node
* node
= layoutObject
.node()) {
958 if (layoutObject
.isLayoutView()) {
959 Document
& doc
= node
->document();
960 if (Settings
* settings
= doc
.settings()) {
961 if (!settings
->allowCustomScrollbarInMainFrame() && layoutObject
.frame() && layoutObject
.frame()->isMainFrame())
962 return &layoutObject
;
965 // Try the <body> element first as a scrollbar source.
966 Element
* body
= doc
.body();
967 if (body
&& body
->layoutObject() && body
->layoutObject()->style()->hasPseudoStyle(SCROLLBAR
))
968 return body
->layoutObject();
970 // If the <body> didn't have a custom style, then the root element might.
971 Element
* docElement
= doc
.documentElement();
972 if (docElement
&& docElement
->layoutObject() && docElement
->layoutObject()->style()->hasPseudoStyle(SCROLLBAR
))
973 return docElement
->layoutObject();
975 // If we have an owning ipage/LocalFrame element, then it can set the custom scrollbar also.
976 LayoutPart
* frameLayoutObject
= node
->document().frame()->ownerLayoutObject();
977 if (frameLayoutObject
&& frameLayoutObject
->style()->hasPseudoStyle(SCROLLBAR
))
978 return frameLayoutObject
;
980 if (ShadowRoot
* shadowRoot
= node
->containingShadowRoot()) {
981 if (shadowRoot
->type() == ShadowRootType::UserAgent
)
982 return shadowRoot
->host()->layoutObject();
986 return &layoutObject
;
989 bool DeprecatedPaintLayerScrollableArea::needsScrollbarReconstruction() const
991 LayoutObject
* actualLayoutObject
= layoutObjectForScrollbar(box());
992 bool shouldUseCustom
= actualLayoutObject
->isBox() && actualLayoutObject
->style()->hasPseudoStyle(SCROLLBAR
);
993 bool hasAnyScrollbar
= hasScrollbar();
994 bool hasCustom
= (m_hBar
&& m_hBar
->isCustomScrollbar()) || (m_vBar
&& m_vBar
->isCustomScrollbar());
995 return hasAnyScrollbar
&& (shouldUseCustom
!= hasCustom
);
998 PassRefPtrWillBeRawPtr
<Scrollbar
> DeprecatedPaintLayerScrollableArea::createScrollbar(ScrollbarOrientation orientation
)
1000 RefPtrWillBeRawPtr
<Scrollbar
> widget
= nullptr;
1001 LayoutObject
* actualLayoutObject
= layoutObjectForScrollbar(box());
1002 bool hasCustomScrollbarStyle
= actualLayoutObject
->isBox() && actualLayoutObject
->style()->hasPseudoStyle(SCROLLBAR
);
1003 if (hasCustomScrollbarStyle
) {
1004 widget
= LayoutScrollbar::createCustomScrollbar(this, orientation
, actualLayoutObject
->node());
1006 ScrollbarControlSize scrollbarSize
= RegularScrollbar
;
1007 if (actualLayoutObject
->style()->hasAppearance())
1008 scrollbarSize
= LayoutTheme::theme().scrollbarControlSizeForPart(actualLayoutObject
->style()->appearance());
1009 widget
= Scrollbar::create(this, orientation
, scrollbarSize
);
1010 if (orientation
== HorizontalScrollbar
)
1011 didAddScrollbar(widget
.get(), HorizontalScrollbar
);
1013 didAddScrollbar(widget
.get(), VerticalScrollbar
);
1015 box().document().view()->addChild(widget
.get());
1016 return widget
.release();
1019 void DeprecatedPaintLayerScrollableArea::destroyScrollbar(ScrollbarOrientation orientation
)
1021 RefPtrWillBeMember
<Scrollbar
>& scrollbar
= orientation
== HorizontalScrollbar
? m_hBar
: m_vBar
;
1025 if (!scrollbar
->isCustomScrollbar())
1026 willRemoveScrollbar(scrollbar
.get(), orientation
);
1028 toFrameView(scrollbar
->parent())->removeChild(scrollbar
.get());
1029 scrollbar
->disconnectFromScrollableArea();
1030 scrollbar
= nullptr;
1033 void DeprecatedPaintLayerScrollableArea::setHasHorizontalScrollbar(bool hasScrollbar
)
1035 if (hasScrollbar
== hasHorizontalScrollbar())
1039 // This doesn't hit in any tests, but since the equivalent code in setHasVerticalScrollbar
1040 // does, presumably this code does as well.
1041 DisableCompositingQueryAsserts disabler
;
1042 m_hBar
= createScrollbar(HorizontalScrollbar
);
1044 if (!layerForHorizontalScrollbar())
1045 m_hBar
->invalidate();
1046 // Otherwise we will remove the layer and just need recompositing.
1048 destroyScrollbar(HorizontalScrollbar
);
1051 // Destroying or creating one bar can cause our scrollbar corner to come and go. We need to update the opposite scrollbar's style.
1053 m_hBar
->styleChanged();
1055 m_vBar
->styleChanged();
1057 // These are valid because we want to invalidate display item clients on the current backing.
1058 DisablePaintInvalidationStateAsserts paintInvalidationAssertDisabler
;
1059 DisableCompositingQueryAsserts compositingAssertDisabler
;
1060 invalidateScrollCorner(scrollCornerRect());
1062 // Force an update since we know the scrollbars have changed things.
1063 if (box().document().hasAnnotatedRegions())
1064 box().document().setAnnotatedRegionsDirty(true);
1067 void DeprecatedPaintLayerScrollableArea::setHasVerticalScrollbar(bool hasScrollbar
)
1069 if (hasScrollbar
== hasVerticalScrollbar())
1073 // Hits in compositing/overflow/automatically-opt-into-composited-scrolling-after-style-change.html
1074 DisableCompositingQueryAsserts disabler
;
1075 m_vBar
= createScrollbar(VerticalScrollbar
);
1077 if (!layerForVerticalScrollbar())
1078 m_vBar
->invalidate();
1079 // Otherwise we will remove the layer and just need recompositing.
1081 destroyScrollbar(VerticalScrollbar
);
1084 // Destroying or creating one bar can cause our scrollbar corner to come and go. We need to update the opposite scrollbar's style.
1086 m_hBar
->styleChanged();
1088 m_vBar
->styleChanged();
1090 // These are valid because we want to invalidate display item clients on the current backing.
1091 DisablePaintInvalidationStateAsserts paintInvalidationAssertDisabler
;
1092 DisableCompositingQueryAsserts compositingAssertDisabler
;
1093 invalidateScrollCorner(scrollCornerRect());
1095 // Force an update since we know the scrollbars have changed things.
1096 if (box().document().hasAnnotatedRegions())
1097 box().document().setAnnotatedRegionsDirty(true);
1100 int DeprecatedPaintLayerScrollableArea::verticalScrollbarWidth(OverlayScrollbarSizeRelevancy relevancy
) const
1102 if (!m_vBar
|| (m_vBar
->isOverlayScrollbar() && (relevancy
== IgnoreOverlayScrollbarSize
|| !m_vBar
->shouldParticipateInHitTesting())))
1104 return m_vBar
->width();
1107 int DeprecatedPaintLayerScrollableArea::horizontalScrollbarHeight(OverlayScrollbarSizeRelevancy relevancy
) const
1109 if (!m_hBar
|| (m_hBar
->isOverlayScrollbar() && (relevancy
== IgnoreOverlayScrollbarSize
|| !m_hBar
->shouldParticipateInHitTesting())))
1111 return m_hBar
->height();
1114 void DeprecatedPaintLayerScrollableArea::positionOverflowControls()
1116 if (!hasScrollbar() && !box().canResize())
1119 const IntRect borderBox
= box().pixelSnappedBorderBoxRect();
1120 if (Scrollbar
* verticalScrollbar
= this->verticalScrollbar())
1121 verticalScrollbar
->setFrameRect(rectForVerticalScrollbar(borderBox
));
1123 if (Scrollbar
* horizontalScrollbar
= this->horizontalScrollbar())
1124 horizontalScrollbar
->setFrameRect(rectForHorizontalScrollbar(borderBox
));
1126 const IntRect
& scrollCorner
= scrollCornerRect();
1128 m_scrollCorner
->setFrameRect(LayoutRect(scrollCorner
));
1131 m_resizer
->setFrameRect(LayoutRect(resizerCornerRect(borderBox
, ResizerForPointer
)));
1133 // FIXME, this should eventually be removed, once we are certain that composited
1134 // controls get correctly positioned on a compositor update. For now, conservatively
1135 // leaving this unchanged.
1136 if (layer()->hasCompositedDeprecatedPaintLayerMapping())
1137 layer()->compositedDeprecatedPaintLayerMapping()->positionOverflowControlsLayers();
1140 void DeprecatedPaintLayerScrollableArea::updateScrollCornerStyle()
1142 if (!m_scrollCorner
&& !hasScrollbar())
1144 if (!m_scrollCorner
&& hasOverlayScrollbars())
1147 LayoutObject
* actualLayoutObject
= layoutObjectForScrollbar(box());
1148 RefPtr
<ComputedStyle
> corner
= box().hasOverflowClip() ? actualLayoutObject
->getUncachedPseudoStyle(PseudoStyleRequest(SCROLLBAR_CORNER
), actualLayoutObject
->style()) : PassRefPtr
<ComputedStyle
>(nullptr);
1150 if (!m_scrollCorner
) {
1151 m_scrollCorner
= LayoutScrollbarPart::createAnonymous(&box().document());
1152 m_scrollCorner
->setDangerousOneWayParent(&box());
1154 m_scrollCorner
->setStyle(corner
.release());
1155 } else if (m_scrollCorner
) {
1156 m_scrollCorner
->destroy();
1157 m_scrollCorner
= nullptr;
1161 bool DeprecatedPaintLayerScrollableArea::hitTestOverflowControls(HitTestResult
& result
, const IntPoint
& localPoint
)
1163 if (!hasScrollbar() && !box().canResize())
1166 IntRect resizeControlRect
;
1167 if (box().style()->resize() != RESIZE_NONE
) {
1168 resizeControlRect
= resizerCornerRect(box().pixelSnappedBorderBoxRect(), ResizerForPointer
);
1169 if (resizeControlRect
.contains(localPoint
))
1173 int resizeControlSize
= max(resizeControlRect
.height(), 0);
1174 if (m_vBar
&& m_vBar
->shouldParticipateInHitTesting()) {
1175 LayoutRect
vBarRect(verticalScrollbarStart(0, box().size().width()),
1178 box().size().height() - (box().borderTop() + box().borderBottom()) - (m_hBar
? m_hBar
->height() : resizeControlSize
));
1179 if (vBarRect
.contains(localPoint
)) {
1180 result
.setScrollbar(m_vBar
.get());
1185 resizeControlSize
= max(resizeControlRect
.width(), 0);
1186 if (m_hBar
&& m_hBar
->shouldParticipateInHitTesting()) {
1187 LayoutRect
hBarRect(horizontalScrollbarStart(0),
1188 box().size().height() - box().borderBottom() - m_hBar
->height(),
1189 box().size().width() - (box().borderLeft() + box().borderRight()) - (m_vBar
? m_vBar
->width() : resizeControlSize
),
1191 if (hBarRect
.contains(localPoint
)) {
1192 result
.setScrollbar(m_hBar
.get());
1197 // FIXME: We should hit test the m_scrollCorner and pass it back through the result.
1202 IntRect
DeprecatedPaintLayerScrollableArea::resizerCornerRect(const IntRect
& bounds
, ResizerHitTestType resizerHitTestType
) const
1204 if (box().style()->resize() == RESIZE_NONE
)
1206 IntRect corner
= cornerRect(box().styleRef(), horizontalScrollbar(), verticalScrollbar(), bounds
);
1208 if (resizerHitTestType
== ResizerForTouch
) {
1209 // We make the resizer virtually larger for touch hit testing. With the
1210 // expanding ratio k = ResizerControlExpandRatioForTouch, we first move
1211 // the resizer rect (of width w & height h), by (-w * (k-1), -h * (k-1)),
1212 // then expand the rect by new_w/h = w/h * k.
1213 int expandRatio
= ResizerControlExpandRatioForTouch
- 1;
1214 corner
.move(-corner
.width() * expandRatio
, -corner
.height() * expandRatio
);
1215 corner
.expand(corner
.width() * expandRatio
, corner
.height() * expandRatio
);
1221 IntRect
DeprecatedPaintLayerScrollableArea::scrollCornerAndResizerRect() const
1223 IntRect scrollCornerAndResizer
= scrollCornerRect();
1224 if (scrollCornerAndResizer
.isEmpty())
1225 scrollCornerAndResizer
= resizerCornerRect(box().pixelSnappedBorderBoxRect(), ResizerForPointer
);
1226 return scrollCornerAndResizer
;
1229 bool DeprecatedPaintLayerScrollableArea::isPointInResizeControl(const IntPoint
& absolutePoint
, ResizerHitTestType resizerHitTestType
) const
1231 if (!box().canResize())
1234 IntPoint localPoint
= roundedIntPoint(box().absoluteToLocal(absolutePoint
, UseTransforms
));
1235 IntRect
localBounds(0, 0, box().pixelSnappedWidth(), box().pixelSnappedHeight());
1236 return resizerCornerRect(localBounds
, resizerHitTestType
).contains(localPoint
);
1239 bool DeprecatedPaintLayerScrollableArea::hitTestResizerInFragments(const DeprecatedPaintLayerFragments
& layerFragments
, const HitTestLocation
& hitTestLocation
) const
1241 if (!box().canResize())
1244 if (layerFragments
.isEmpty())
1247 for (int i
= layerFragments
.size() - 1; i
>= 0; --i
) {
1248 const DeprecatedPaintLayerFragment
& fragment
= layerFragments
.at(i
);
1249 if (fragment
.backgroundRect
.intersects(hitTestLocation
) && resizerCornerRect(pixelSnappedIntRect(fragment
.layerBounds
), ResizerForPointer
).contains(hitTestLocation
.roundedPoint()))
1256 void DeprecatedPaintLayerScrollableArea::updateResizerAreaSet()
1258 LocalFrame
* frame
= box().frame();
1261 FrameView
* frameView
= frame
->view();
1264 if (box().canResize())
1265 frameView
->addResizerArea(box());
1267 frameView
->removeResizerArea(box());
1270 void DeprecatedPaintLayerScrollableArea::updateResizerStyle()
1272 if (!m_resizer
&& !box().canResize())
1275 LayoutObject
* actualLayoutObject
= layoutObjectForScrollbar(box());
1276 RefPtr
<ComputedStyle
> resizer
= box().hasOverflowClip() ? actualLayoutObject
->getUncachedPseudoStyle(PseudoStyleRequest(RESIZER
), actualLayoutObject
->style()) : PassRefPtr
<ComputedStyle
>(nullptr);
1279 m_resizer
= LayoutScrollbarPart::createAnonymous(&box().document());
1280 m_resizer
->setDangerousOneWayParent(&box());
1282 m_resizer
->setStyle(resizer
.release());
1283 } else if (m_resizer
) {
1284 m_resizer
->destroy();
1285 m_resizer
= nullptr;
1289 IntSize
DeprecatedPaintLayerScrollableArea::offsetFromResizeCorner(const IntPoint
& absolutePoint
) const
1291 // Currently the resize corner is either the bottom right corner or the bottom left corner.
1292 // FIXME: This assumes the location is 0, 0. Is this guaranteed to always be the case?
1293 IntSize elementSize
= layer()->size();
1294 if (box().style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft())
1295 elementSize
.setWidth(0);
1296 IntPoint resizerPoint
= IntPoint(elementSize
);
1297 IntPoint localPoint
= roundedIntPoint(box().absoluteToLocal(absolutePoint
, UseTransforms
));
1298 return localPoint
- resizerPoint
;
1301 void DeprecatedPaintLayerScrollableArea::resize(const PlatformEvent
& evt
, const LayoutSize
& oldOffset
)
1303 // FIXME: This should be possible on generated content but is not right now.
1304 if (!inResizeMode() || !box().canResize() || !box().node())
1307 ASSERT(box().node()->isElementNode());
1308 Element
* element
= toElement(box().node());
1310 Document
& document
= element
->document();
1313 const PlatformGestureEvent
* gevt
= 0;
1315 switch (evt
.type()) {
1316 case PlatformEvent::MouseMoved
:
1317 if (!document
.frame()->eventHandler().mousePressed())
1319 pos
= static_cast<const PlatformMouseEvent
*>(&evt
)->position();
1321 case PlatformEvent::GestureScrollUpdate
:
1322 pos
= static_cast<const PlatformGestureEvent
*>(&evt
)->position();
1323 gevt
= static_cast<const PlatformGestureEvent
*>(&evt
);
1324 pos
= gevt
->position();
1325 pos
.move(gevt
->deltaX(), gevt
->deltaY());
1328 ASSERT_NOT_REACHED();
1331 float zoomFactor
= box().style()->effectiveZoom();
1333 IntSize newOffset
= offsetFromResizeCorner(document
.view()->rootFrameToContents(pos
));
1334 newOffset
.setWidth(newOffset
.width() / zoomFactor
);
1335 newOffset
.setHeight(newOffset
.height() / zoomFactor
);
1337 LayoutSize currentSize
= box().size();
1338 currentSize
.scale(1 / zoomFactor
);
1339 LayoutSize minimumSize
= element
->minimumSizeForResizing().shrunkTo(currentSize
);
1340 element
->setMinimumSizeForResizing(minimumSize
);
1342 LayoutSize adjustedOldOffset
= LayoutSize(oldOffset
.width() / zoomFactor
, oldOffset
.height() / zoomFactor
);
1343 if (box().style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft()) {
1344 newOffset
.setWidth(-newOffset
.width());
1345 adjustedOldOffset
.setWidth(-adjustedOldOffset
.width());
1348 LayoutSize
difference((currentSize
+ newOffset
- adjustedOldOffset
).expandedTo(minimumSize
) - currentSize
);
1350 bool isBoxSizingBorder
= box().style()->boxSizing() == BORDER_BOX
;
1352 EResize resize
= box().style()->resize();
1353 if (resize
!= RESIZE_VERTICAL
&& difference
.width()) {
1354 if (element
->isFormControlElement()) {
1355 // Make implicit margins from the theme explicit (see <http://bugs.webkit.org/show_bug.cgi?id=9547>).
1356 element
->setInlineStyleProperty(CSSPropertyMarginLeft
, box().marginLeft() / zoomFactor
, CSSPrimitiveValue::UnitType::Pixels
);
1357 element
->setInlineStyleProperty(CSSPropertyMarginRight
, box().marginRight() / zoomFactor
, CSSPrimitiveValue::UnitType::Pixels
);
1359 LayoutUnit baseWidth
= box().size().width() - (isBoxSizingBorder
? LayoutUnit() : box().borderAndPaddingWidth());
1360 baseWidth
= baseWidth
/ zoomFactor
;
1361 element
->setInlineStyleProperty(CSSPropertyWidth
, roundToInt(baseWidth
+ difference
.width()), CSSPrimitiveValue::UnitType::Pixels
);
1364 if (resize
!= RESIZE_HORIZONTAL
&& difference
.height()) {
1365 if (element
->isFormControlElement()) {
1366 // Make implicit margins from the theme explicit (see <http://bugs.webkit.org/show_bug.cgi?id=9547>).
1367 element
->setInlineStyleProperty(CSSPropertyMarginTop
, box().marginTop() / zoomFactor
, CSSPrimitiveValue::UnitType::Pixels
);
1368 element
->setInlineStyleProperty(CSSPropertyMarginBottom
, box().marginBottom() / zoomFactor
, CSSPrimitiveValue::UnitType::Pixels
);
1370 LayoutUnit baseHeight
= box().size().height() - (isBoxSizingBorder
? LayoutUnit() : box().borderAndPaddingHeight());
1371 baseHeight
= baseHeight
/ zoomFactor
;
1372 element
->setInlineStyleProperty(CSSPropertyHeight
, roundToInt(baseHeight
+ difference
.height()), CSSPrimitiveValue::UnitType::Pixels
);
1375 document
.updateLayout();
1377 // FIXME (Radar 4118564): We should also autoscroll the window as necessary to keep the point under the cursor in view.
1380 LayoutRect
DeprecatedPaintLayerScrollableArea::scrollIntoView(const LayoutRect
& rect
, const ScrollAlignment
& alignX
, const ScrollAlignment
& alignY
)
1382 LayoutRect
localExposeRect(box().absoluteToLocalQuad(FloatQuad(FloatRect(rect
)), UseTransforms
).boundingBox());
1383 localExposeRect
.move(-box().borderLeft(), -box().borderTop());
1384 LayoutRect
layerBounds(0, 0, box().clientWidth(), box().clientHeight());
1385 LayoutRect r
= ScrollAlignment::getRectToExpose(layerBounds
, localExposeRect
, alignX
, alignY
);
1387 DoublePoint clampedScrollPosition
= clampScrollPosition(scrollPositionDouble() + roundedIntSize(r
.location()));
1388 if (clampedScrollPosition
== scrollPositionDouble())
1391 DoubleSize oldScrollOffset
= adjustedScrollOffset();
1392 scrollToPosition(clampedScrollPosition
);
1393 DoubleSize scrollOffsetDifference
= adjustedScrollOffset() - oldScrollOffset
;
1394 localExposeRect
.move(-LayoutSize(scrollOffsetDifference
));
1395 return LayoutRect(box().localToAbsoluteQuad(FloatQuad(FloatRect(localExposeRect
)), UseTransforms
).boundingBox());
1398 void DeprecatedPaintLayerScrollableArea::updateScrollableAreaSet(bool hasOverflow
)
1400 LocalFrame
* frame
= box().frame();
1404 FrameView
* frameView
= frame
->view();
1408 // FIXME: Does this need to be fixed later for OOPI?
1409 bool isVisibleToHitTest
= box().visibleToHitTesting();
1410 if (HTMLFrameOwnerElement
* owner
= frame
->deprecatedLocalOwner())
1411 isVisibleToHitTest
&= owner
->layoutObject() && owner
->layoutObject()->visibleToHitTesting();
1413 bool didScrollOverflow
= m_scrollsOverflow
;
1415 m_scrollsOverflow
= hasOverflow
&& isVisibleToHitTest
;
1416 if (didScrollOverflow
== scrollsOverflow())
1419 if (m_scrollsOverflow
) {
1420 ASSERT(canHaveOverflowScrollbars(box()));
1421 frameView
->addScrollableArea(this);
1423 frameView
->removeScrollableArea(this);
1427 void DeprecatedPaintLayerScrollableArea::updateCompositingLayersAfterScroll()
1429 DeprecatedPaintLayerCompositor
* compositor
= box().view()->compositor();
1430 if (compositor
->inCompositingMode()) {
1431 if (usesCompositedScrolling()) {
1432 ASSERT(layer()->hasCompositedDeprecatedPaintLayerMapping());
1433 layer()->compositedDeprecatedPaintLayerMapping()->setNeedsGraphicsLayerUpdate(GraphicsLayerUpdateSubtree
);
1434 compositor
->setNeedsCompositingUpdate(CompositingUpdateAfterGeometryChange
);
1436 layer()->setNeedsCompositingInputsUpdate();
1441 bool DeprecatedPaintLayerScrollableArea::usesCompositedScrolling() const
1443 // Scroll form controls on the main thread so they exhibit correct touch scroll event bubbling
1444 if (box().isIntrinsicallyScrollable(VerticalScrollbar
) || box().isIntrinsicallyScrollable(HorizontalScrollbar
))
1447 // See https://codereview.chromium.org/176633003/ for the tests that fail without this disabler.
1448 DisableCompositingQueryAsserts disabler
;
1449 return layer()->hasCompositedDeprecatedPaintLayerMapping() && layer()->compositedDeprecatedPaintLayerMapping()->scrollingLayer();
1452 static bool layerNeedsCompositedScrolling(DeprecatedPaintLayerScrollableArea::LCDTextMode mode
, const DeprecatedPaintLayer
* layer
)
1454 if (mode
== DeprecatedPaintLayerScrollableArea::ConsiderLCDText
&& !layer
->compositor()->preferCompositingToLCDTextEnabled())
1457 return layer
->scrollsOverflow()
1458 && !layer
->hasDescendantWithClipPath()
1459 && !layer
->hasAncestorWithClipPath()
1460 && !layer
->layoutObject()->style()->hasBorderRadius();
1463 void DeprecatedPaintLayerScrollableArea::updateNeedsCompositedScrolling(LCDTextMode mode
)
1465 const bool needsCompositedScrolling
= layerNeedsCompositedScrolling(mode
, layer());
1466 if (static_cast<bool>(m_needsCompositedScrolling
) != needsCompositedScrolling
) {
1467 m_needsCompositedScrolling
= needsCompositedScrolling
;
1468 layer()->didUpdateNeedsCompositedScrolling();
1472 void DeprecatedPaintLayerScrollableArea::setTopmostScrollChild(DeprecatedPaintLayer
* scrollChild
)
1474 // We only want to track the topmost scroll child for scrollable areas with
1475 // overlay scrollbars.
1476 if (!hasOverlayScrollbars())
1478 m_nextTopmostScrollChild
= scrollChild
;
1481 bool DeprecatedPaintLayerScrollableArea::visualViewportSuppliesScrollbars() const
1483 if (!layer()->isRootLayer())
1486 LocalFrame
* frame
= box().frame();
1487 if (!frame
|| !frame
->isMainFrame() || !frame
->settings())
1490 return frame
->settings()->viewportMetaEnabled();
1493 } // namespace blink