Move parseFontFaceDescriptor to CSSPropertyParser.cpp
[chromium-blink-merge.git] / third_party / WebKit / Source / core / paint / DeprecatedPaintLayerScrollableArea.cpp
blob7f23aa54fe791c3f63dc5aa6d0b9e115c2104ec2
1 /*
2 * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc. All rights reserved.
4 * Portions are Copyright (C) 1998 Netscape Communications Corporation.
6 * Other contributors:
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.
44 #include "config.h"
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"
79 namespace blink {
81 const int ResizerControlExpandRatioForTouch = 2;
83 DeprecatedPaintLayerScrollableArea::DeprecatedPaintLayerScrollableArea(DeprecatedPaintLayer& layer)
84 : m_layer(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)
92 , m_resizer(nullptr)
93 #if ENABLE(ASSERT)
94 , m_hasBeenDisposed(false)
95 #endif
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);
148 if (m_scrollCorner)
149 m_scrollCorner->destroy();
150 if (m_resizer)
151 m_resizer->destroy();
153 clearScrollAnimators();
155 #if ENABLE(ASSERT)
156 m_hasBeenDisposed = true;
157 #endif
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();
171 return nullptr;
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())
214 return;
216 if (scrollbar == m_vBar.get())
217 scrollRect.move(verticalScrollbarStart(0, box().size().width()), box().borderTop());
218 else
219 scrollRect.move(horizontalScrollbarStart(0), box().size().height() - box().borderBottom() - scrollbar->height());
221 if (scrollRect.isEmpty())
222 return;
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);
233 } else {
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);
251 } else {
252 box().invalidateDisplayItemClient(box());
254 if (m_resizer) {
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())
264 return true;
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;
302 } else {
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());
323 return IntRect();
326 IntRect DeprecatedPaintLayerScrollableArea::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntRect& scrollbarRect) const
328 LayoutView* view = box().view();
329 if (!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();
341 if (!view)
342 return parentRect;
344 IntRect rect = view->frameView()->convertToLayoutObject(box(), parentRect);
345 rect.move(-scrollbarOffset(scrollbar));
346 return rect;
349 IntPoint DeprecatedPaintLayerScrollableArea::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntPoint& scrollbarPoint) const
351 LayoutView* view = box().view();
352 if (!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();
363 if (!view)
364 return parentPoint;
366 IntPoint point = view->frameView()->convertToLayoutObject(box(), parentPoint);
368 point.move(-scrollbarOffset(scrollbar));
369 return point;
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))
386 return;
388 DoubleSize scrollDelta = scrollOffset() - toDoubleSize(newScrollOffset);
389 m_scrollOffset = toDoubleSize(newScrollOffset);
391 LocalFrame* frame = box().frame();
392 ASSERT(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.
449 if (box().node())
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
481 IntSize contentSize;
482 IntSize visibleSize;
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();
531 return false;
534 bool DeprecatedPaintLayerScrollableArea::shouldSuspendScrollAnimations() const
536 LayoutView* view = box().view();
537 if (!view)
538 return true;
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();
551 if (!view)
552 return false;
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))
580 return true;
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
609 return &m_layer;
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()) {
656 if (m_hBar)
657 destroyScrollbar(HorizontalScrollbar);
658 if (m_vBar)
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);
733 } else {
734 box().layout();
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()))
832 return;
834 // Avoid drawing two sets of scrollbars when one is provided by the visual viewport.
835 if (visualViewportSuppliesScrollbars()) {
836 setHasHorizontalScrollbar(false);
837 setHasVerticalScrollbar(false);
838 return;
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).
863 if (m_hBar)
864 m_hBar->styleChanged();
865 if (m_vBar)
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
904 if (!m_hBar)
905 return IntRect();
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(),
912 m_hBar->height());
915 IntRect DeprecatedPaintLayerScrollableArea::rectForVerticalScrollbar(const IntRect& borderBoxRect) const
917 if (!m_vBar)
918 return IntRect();
920 const IntRect& scrollCorner = scrollCornerRect();
922 return IntRect(verticalScrollbarStart(borderBoxRect.x(), borderBoxRect.maxX()),
923 borderBoxRect.y() + box().borderTop(),
924 m_vBar->width(),
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();
940 return x;
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();
952 return IntSize();
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());
1005 } else {
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);
1012 else
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;
1022 if (!scrollbar)
1023 return;
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())
1036 return;
1038 if (hasScrollbar) {
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);
1043 } else {
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.
1052 if (m_hBar)
1053 m_hBar->styleChanged();
1054 if (m_vBar)
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())
1070 return;
1072 if (hasScrollbar) {
1073 // Hits in compositing/overflow/automatically-opt-into-composited-scrolling-after-style-change.html
1074 DisableCompositingQueryAsserts disabler;
1075 m_vBar = createScrollbar(VerticalScrollbar);
1076 } else {
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.
1085 if (m_hBar)
1086 m_hBar->styleChanged();
1087 if (m_vBar)
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())))
1103 return 0;
1104 return m_vBar->width();
1107 int DeprecatedPaintLayerScrollableArea::horizontalScrollbarHeight(OverlayScrollbarSizeRelevancy relevancy) const
1109 if (!m_hBar || (m_hBar->isOverlayScrollbar() && (relevancy == IgnoreOverlayScrollbarSize || !m_hBar->shouldParticipateInHitTesting())))
1110 return 0;
1111 return m_hBar->height();
1114 void DeprecatedPaintLayerScrollableArea::positionOverflowControls()
1116 if (!hasScrollbar() && !box().canResize())
1117 return;
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();
1127 if (m_scrollCorner)
1128 m_scrollCorner->setFrameRect(LayoutRect(scrollCorner));
1130 if (m_resizer)
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())
1143 return;
1144 if (!m_scrollCorner && hasOverlayScrollbars())
1145 return;
1147 LayoutObject* actualLayoutObject = layoutObjectForScrollbar(box());
1148 RefPtr<ComputedStyle> corner = box().hasOverflowClip() ? actualLayoutObject->getUncachedPseudoStyle(PseudoStyleRequest(SCROLLBAR_CORNER), actualLayoutObject->style()) : PassRefPtr<ComputedStyle>(nullptr);
1149 if (corner) {
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())
1164 return false;
1166 IntRect resizeControlRect;
1167 if (box().style()->resize() != RESIZE_NONE) {
1168 resizeControlRect = resizerCornerRect(box().pixelSnappedBorderBoxRect(), ResizerForPointer);
1169 if (resizeControlRect.contains(localPoint))
1170 return true;
1173 int resizeControlSize = max(resizeControlRect.height(), 0);
1174 if (m_vBar && m_vBar->shouldParticipateInHitTesting()) {
1175 LayoutRect vBarRect(verticalScrollbarStart(0, box().size().width()),
1176 box().borderTop(),
1177 m_vBar->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());
1181 return true;
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),
1190 m_hBar->height());
1191 if (hBarRect.contains(localPoint)) {
1192 result.setScrollbar(m_hBar.get());
1193 return true;
1197 // FIXME: We should hit test the m_scrollCorner and pass it back through the result.
1199 return false;
1202 IntRect DeprecatedPaintLayerScrollableArea::resizerCornerRect(const IntRect& bounds, ResizerHitTestType resizerHitTestType) const
1204 if (box().style()->resize() == RESIZE_NONE)
1205 return IntRect();
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);
1218 return corner;
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())
1232 return false;
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())
1242 return false;
1244 if (layerFragments.isEmpty())
1245 return false;
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()))
1250 return true;
1253 return false;
1256 void DeprecatedPaintLayerScrollableArea::updateResizerAreaSet()
1258 LocalFrame* frame = box().frame();
1259 if (!frame)
1260 return;
1261 FrameView* frameView = frame->view();
1262 if (!frameView)
1263 return;
1264 if (box().canResize())
1265 frameView->addResizerArea(box());
1266 else
1267 frameView->removeResizerArea(box());
1270 void DeprecatedPaintLayerScrollableArea::updateResizerStyle()
1272 if (!m_resizer && !box().canResize())
1273 return;
1275 LayoutObject* actualLayoutObject = layoutObjectForScrollbar(box());
1276 RefPtr<ComputedStyle> resizer = box().hasOverflowClip() ? actualLayoutObject->getUncachedPseudoStyle(PseudoStyleRequest(RESIZER), actualLayoutObject->style()) : PassRefPtr<ComputedStyle>(nullptr);
1277 if (resizer) {
1278 if (!m_resizer) {
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())
1305 return;
1307 ASSERT(box().node()->isElementNode());
1308 Element* element = toElement(box().node());
1310 Document& document = element->document();
1312 IntPoint pos;
1313 const PlatformGestureEvent* gevt = 0;
1315 switch (evt.type()) {
1316 case PlatformEvent::MouseMoved:
1317 if (!document.frame()->eventHandler().mousePressed())
1318 return;
1319 pos = static_cast<const PlatformMouseEvent*>(&evt)->position();
1320 break;
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());
1326 break;
1327 default:
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())
1389 return rect;
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();
1401 if (!frame)
1402 return;
1404 FrameView* frameView = frame->view();
1405 if (!frameView)
1406 return;
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())
1417 return;
1419 if (m_scrollsOverflow) {
1420 ASSERT(canHaveOverflowScrollbars(box()));
1421 frameView->addScrollableArea(this);
1422 } else {
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);
1435 } else {
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))
1445 return false;
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())
1455 return false;
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())
1477 return;
1478 m_nextTopmostScrollChild = scrollChild;
1481 bool DeprecatedPaintLayerScrollableArea::visualViewportSuppliesScrollbars() const
1483 if (!layer()->isRootLayer())
1484 return false;
1486 LocalFrame* frame = box().frame();
1487 if (!frame || !frame->isMainFrame() || !frame->settings())
1488 return false;
1490 return frame->settings()->viewportMetaEnabled();
1493 } // namespace blink