Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / rendering / RenderLayerScrollableArea.cpp
1 /*
2  * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc. All rights reserved.
3  *
4  * Portions are Copyright (C) 1998 Netscape Communications Corporation.
5  *
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>
14  *
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.
19  *
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.
24  *
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
28  *
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.
42  */
43
44 #include "config.h"
45 #include "core/rendering/RenderLayer.h"
46
47 #include "core/accessibility/AXObjectCache.h"
48 #include "core/css/PseudoStyleRequest.h"
49 #include "core/dom/Node.h"
50 #include "core/dom/shadow/ShadowRoot.h"
51 #include "core/editing/FrameSelection.h"
52 #include "core/frame/FrameView.h"
53 #include "core/frame/LocalFrame.h"
54 #include "core/html/HTMLFrameOwnerElement.h"
55 #include "core/inspector/InspectorInstrumentation.h"
56 #include "core/page/Chrome.h"
57 #include "core/page/EventHandler.h"
58 #include "core/page/FocusController.h"
59 #include "core/page/Page.h"
60 #include "core/page/scrolling/ScrollingCoordinator.h"
61 #include "core/rendering/RenderGeometryMap.h"
62 #include "core/rendering/RenderScrollbar.h"
63 #include "core/rendering/RenderScrollbarPart.h"
64 #include "core/rendering/RenderTheme.h"
65 #include "core/rendering/RenderView.h"
66 #include "core/rendering/compositing/CompositedLayerMapping.h"
67 #include "core/rendering/compositing/RenderLayerCompositor.h"
68 #include "platform/PlatformGestureEvent.h"
69 #include "platform/PlatformMouseEvent.h"
70 #include "platform/graphics/GraphicsContextStateSaver.h"
71 #include "platform/graphics/GraphicsLayer.h"
72 #include "platform/scroll/ScrollAnimator.h"
73 #include "platform/scroll/ScrollbarTheme.h"
74 #include "public/platform/Platform.h"
75
76 namespace blink {
77
78 const int ResizerControlExpandRatioForTouch = 2;
79
80 RenderLayerScrollableArea::RenderLayerScrollableArea(RenderLayer& layer)
81     : m_layer(layer)
82     , m_inResizeMode(false)
83     , m_scrollsOverflow(false)
84     , m_scrollDimensionsDirty(true)
85     , m_inOverflowRelayout(false)
86     , m_nextTopmostScrollChild(0)
87     , m_topmostScrollChild(0)
88     , m_needsCompositedScrolling(false)
89     , m_scrollCorner(nullptr)
90     , m_resizer(nullptr)
91 {
92     ScrollableArea::setConstrainsScrollingToContentEdge(false);
93
94     Node* node = box().node();
95     if (node && node->isElementNode()) {
96         // We save and restore only the scrollOffset as the other scroll values are recalculated.
97         Element* element = toElement(node);
98         m_scrollOffset = element->savedLayerScrollOffset();
99         if (!m_scrollOffset.isZero())
100             scrollAnimator()->setCurrentPosition(FloatPoint(m_scrollOffset.width(), m_scrollOffset.height()));
101         element->setSavedLayerScrollOffset(IntSize());
102     }
103
104     updateResizerAreaSet();
105 }
106
107 RenderLayerScrollableArea::~RenderLayerScrollableArea()
108 {
109     if (inResizeMode() && !box().documentBeingDestroyed()) {
110         if (LocalFrame* frame = box().frame())
111             frame->eventHandler().resizeScrollableAreaDestroyed();
112     }
113
114     if (LocalFrame* frame = box().frame()) {
115         if (FrameView* frameView = frame->view()) {
116             frameView->removeScrollableArea(this);
117         }
118     }
119
120     if (box().frame() && box().frame()->page()) {
121         if (ScrollingCoordinator* scrollingCoordinator = box().frame()->page()->scrollingCoordinator())
122             scrollingCoordinator->willDestroyScrollableArea(this);
123     }
124
125     if (!box().documentBeingDestroyed()) {
126         Node* node = box().node();
127         if (node && node->isElementNode())
128             toElement(node)->setSavedLayerScrollOffset(m_scrollOffset);
129     }
130
131     if (LocalFrame* frame = box().frame()) {
132         if (FrameView* frameView = frame->view())
133             frameView->removeResizerArea(box());
134     }
135
136     destroyScrollbar(HorizontalScrollbar);
137     destroyScrollbar(VerticalScrollbar);
138
139     if (m_scrollCorner)
140         m_scrollCorner->destroy();
141     if (m_resizer)
142         m_resizer->destroy();
143 }
144
145 HostWindow* RenderLayerScrollableArea::hostWindow() const
146 {
147     if (Page* page = box().frame()->page())
148         return &page->chrome();
149     return nullptr;
150 }
151
152 GraphicsLayer* RenderLayerScrollableArea::layerForScrolling() const
153 {
154     return layer()->hasCompositedLayerMapping() ? layer()->compositedLayerMapping()->scrollingContentsLayer() : 0;
155 }
156
157 GraphicsLayer* RenderLayerScrollableArea::layerForHorizontalScrollbar() const
158 {
159     // See crbug.com/343132.
160     DisableCompositingQueryAsserts disabler;
161
162     return layer()->hasCompositedLayerMapping() ? layer()->compositedLayerMapping()->layerForHorizontalScrollbar() : 0;
163 }
164
165 GraphicsLayer* RenderLayerScrollableArea::layerForVerticalScrollbar() const
166 {
167     // See crbug.com/343132.
168     DisableCompositingQueryAsserts disabler;
169
170     return layer()->hasCompositedLayerMapping() ? layer()->compositedLayerMapping()->layerForVerticalScrollbar() : 0;
171 }
172
173 GraphicsLayer* RenderLayerScrollableArea::layerForScrollCorner() const
174 {
175     // See crbug.com/343132.
176     DisableCompositingQueryAsserts disabler;
177
178     return layer()->hasCompositedLayerMapping() ? layer()->compositedLayerMapping()->layerForScrollCorner() : 0;
179 }
180
181 void RenderLayerScrollableArea::invalidateScrollbarRect(Scrollbar* scrollbar, const IntRect& rect)
182 {
183     // See crbug.com/343132.
184     DisableCompositingQueryAsserts disabler;
185
186     if (scrollbar == m_vBar.get()) {
187         if (GraphicsLayer* layer = layerForVerticalScrollbar()) {
188             layer->setNeedsDisplayInRect(rect, WebInvalidationDebugAnnotationsScrollbar);
189             return;
190         }
191     } else {
192         if (GraphicsLayer* layer = layerForHorizontalScrollbar()) {
193             layer->setNeedsDisplayInRect(rect, WebInvalidationDebugAnnotationsScrollbar);
194             return;
195         }
196     }
197
198     IntRect scrollRect = rect;
199     // If we are not yet inserted into the tree, there is no need to issue paint invaldiations.
200     if (!box().parent())
201         return;
202
203     if (scrollbar == m_vBar.get())
204         scrollRect.move(verticalScrollbarStart(0, box().width()), box().borderTop());
205     else
206         scrollRect.move(horizontalScrollbarStart(0), box().height() - box().borderBottom() - scrollbar->height());
207
208     if (scrollRect.isEmpty())
209         return;
210
211     LayoutRect paintInvalidationRect = scrollRect;
212     box().flipForWritingMode(paintInvalidationRect);
213
214     IntRect intRect = pixelSnappedIntRect(paintInvalidationRect);
215
216     if (box().frameView()->isInPerformLayout())
217         addScrollbarDamage(scrollbar, intRect);
218     else
219         box().invalidatePaintRectangle(intRect);
220 }
221
222 void RenderLayerScrollableArea::invalidateScrollCornerRect(const IntRect& rect)
223 {
224     if (GraphicsLayer* layer = layerForScrollCorner()) {
225         layer->setNeedsDisplayInRect(rect, WebInvalidationDebugAnnotationsScrollbar);
226         return;
227     }
228
229     if (m_scrollCorner)
230         m_scrollCorner->invalidatePaintRectangle(rect);
231     if (m_resizer)
232         m_resizer->invalidatePaintRectangle(rect);
233 }
234
235 bool RenderLayerScrollableArea::isActive() const
236 {
237     Page* page = box().frame()->page();
238     return page && page->focusController().isActive();
239 }
240
241 bool RenderLayerScrollableArea::isScrollCornerVisible() const
242 {
243     return !scrollCornerRect().isEmpty();
244 }
245
246 static int cornerStart(const RenderStyle* style, int minX, int maxX, int thickness)
247 {
248     if (style->shouldPlaceBlockDirectionScrollbarOnLogicalLeft())
249         return minX + style->borderLeftWidth();
250     return maxX - thickness - style->borderRightWidth();
251 }
252
253 static IntRect cornerRect(const RenderStyle* style, const Scrollbar* horizontalScrollbar, const Scrollbar* verticalScrollbar, const IntRect& bounds)
254 {
255     int horizontalThickness;
256     int verticalThickness;
257     if (!verticalScrollbar && !horizontalScrollbar) {
258         // FIXME: This isn't right. We need to know the thickness of custom scrollbars
259         // even when they don't exist in order to set the resizer square size properly.
260         horizontalThickness = ScrollbarTheme::theme()->scrollbarThickness();
261         verticalThickness = horizontalThickness;
262     } else if (verticalScrollbar && !horizontalScrollbar) {
263         horizontalThickness = verticalScrollbar->width();
264         verticalThickness = horizontalThickness;
265     } else if (horizontalScrollbar && !verticalScrollbar) {
266         verticalThickness = horizontalScrollbar->height();
267         horizontalThickness = verticalThickness;
268     } else {
269         horizontalThickness = verticalScrollbar->width();
270         verticalThickness = horizontalScrollbar->height();
271     }
272     return IntRect(cornerStart(style, bounds.x(), bounds.maxX(), horizontalThickness),
273         bounds.maxY() - verticalThickness - style->borderBottomWidth(),
274         horizontalThickness, verticalThickness);
275 }
276
277
278 IntRect RenderLayerScrollableArea::scrollCornerRect() const
279 {
280     // We have a scrollbar corner when a scrollbar is visible and not filling the entire length of the box.
281     // This happens when:
282     // (a) A resizer is present and at least one scrollbar is present
283     // (b) Both scrollbars are present.
284     bool hasHorizontalBar = horizontalScrollbar();
285     bool hasVerticalBar = verticalScrollbar();
286     bool hasResizer = box().style()->resize() != RESIZE_NONE;
287     if ((hasHorizontalBar && hasVerticalBar) || (hasResizer && (hasHorizontalBar || hasVerticalBar)))
288         return cornerRect(box().style(), horizontalScrollbar(), verticalScrollbar(), box().pixelSnappedBorderBoxRect());
289     return IntRect();
290 }
291
292 IntRect RenderLayerScrollableArea::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntRect& scrollbarRect) const
293 {
294     RenderView* view = box().view();
295     if (!view)
296         return scrollbarRect;
297
298     IntRect rect = scrollbarRect;
299     rect.move(scrollbarOffset(scrollbar));
300
301     return view->frameView()->convertFromRenderer(box(), rect);
302 }
303
304 IntRect RenderLayerScrollableArea::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntRect& parentRect) const
305 {
306     RenderView* view = box().view();
307     if (!view)
308         return parentRect;
309
310     IntRect rect = view->frameView()->convertToRenderer(box(), parentRect);
311     rect.move(-scrollbarOffset(scrollbar));
312     return rect;
313 }
314
315 IntPoint RenderLayerScrollableArea::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntPoint& scrollbarPoint) const
316 {
317     RenderView* view = box().view();
318     if (!view)
319         return scrollbarPoint;
320
321     IntPoint point = scrollbarPoint;
322     point.move(scrollbarOffset(scrollbar));
323     return view->frameView()->convertFromRenderer(box(), point);
324 }
325
326 IntPoint RenderLayerScrollableArea::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntPoint& parentPoint) const
327 {
328     RenderView* view = box().view();
329     if (!view)
330         return parentPoint;
331
332     IntPoint point = view->frameView()->convertToRenderer(box(), parentPoint);
333
334     point.move(-scrollbarOffset(scrollbar));
335     return point;
336 }
337
338 int RenderLayerScrollableArea::scrollSize(ScrollbarOrientation orientation) const
339 {
340     IntSize scrollDimensions = maximumScrollPosition() - minimumScrollPosition();
341     return (orientation == HorizontalScrollbar) ? scrollDimensions.width() : scrollDimensions.height();
342 }
343
344 void RenderLayerScrollableArea::setScrollOffset(const IntPoint& newScrollOffset)
345 {
346     if (!box().isMarquee()) {
347         // Ensure that the dimensions will be computed if they need to be (for overflow:hidden blocks).
348         if (m_scrollDimensionsDirty)
349             computeScrollDimensions();
350     }
351
352     if (scrollOffset() == toIntSize(newScrollOffset))
353         return;
354
355     setScrollOffset(toIntSize(newScrollOffset));
356
357     LocalFrame* frame = box().frame();
358     ASSERT(frame);
359
360     RefPtr<FrameView> frameView = box().frameView();
361
362     TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "ScrollLayer", "data", InspectorScrollLayerEvent::data(&box()));
363     // FIXME(361045): remove InspectorInstrumentation calls once DevTools Timeline migrates to tracing.
364     InspectorInstrumentation::willScrollLayer(&box());
365
366     const RenderLayerModelObject* paintInvalidationContainer = box().containerForPaintInvalidation();
367
368     // Update the positions of our child layers (if needed as only fixed layers should be impacted by a scroll).
369     // We don't update compositing layers, because we need to do a deep update from the compositing ancestor.
370     if (!frameView->isInPerformLayout()) {
371         // If we're in the middle of layout, we'll just update layers once layout has finished.
372         layer()->clipper().clearClipRectsIncludingDescendants();
373         box().setPreviousPaintInvalidationRect(box().boundsRectForPaintInvalidation(paintInvalidationContainer));
374         // Update regions, scrolling may change the clip of a particular region.
375         frameView->updateAnnotatedRegions();
376         frameView->setNeedsUpdateWidgetPositions();
377         updateCompositingLayersAfterScroll();
378     }
379
380     // The caret rect needs to be invalidated after scrolling
381     frame->selection().setCaretRectNeedsUpdate();
382
383     FloatQuad quadForFakeMouseMoveEvent = FloatQuad(layer()->renderer()->previousPaintInvalidationRect());
384
385     quadForFakeMouseMoveEvent = paintInvalidationContainer->localToAbsoluteQuad(quadForFakeMouseMoveEvent);
386     frame->eventHandler().dispatchFakeMouseMoveEventSoonInQuad(quadForFakeMouseMoveEvent);
387
388     bool requiresPaintInvalidation = true;
389
390     if (!box().isMarquee() && box().view()->compositor()->inCompositingMode()) {
391         // Hits in virtual/gpu/fast/canvas/canvas-scroll-path-into-view.html.
392         DisableCompositingQueryAsserts disabler;
393         bool onlyScrolledCompositedLayers = scrollsOverflow()
394             && !layer()->hasVisibleNonLayerContent()
395             && !layer()->hasNonCompositedChild()
396             && !layer()->hasBlockSelectionGapBounds()
397             && box().style()->backgroundLayers().attachment() != LocalBackgroundAttachment;
398
399         if (usesCompositedScrolling() || onlyScrolledCompositedLayers)
400             requiresPaintInvalidation = false;
401     }
402
403     // Just schedule a full paint invalidation of our object.
404     if (requiresPaintInvalidation)
405         box().setShouldDoFullPaintInvalidation(true);
406
407     // Schedule the scroll DOM event.
408     if (box().node())
409         box().node()->document().enqueueScrollEventForNode(box().node());
410
411     if (AXObjectCache* cache = box().document().existingAXObjectCache())
412         cache->handleScrollPositionChanged(&box());
413
414     InspectorInstrumentation::didScrollLayer(&box());
415 }
416
417 IntPoint RenderLayerScrollableArea::scrollPosition() const
418 {
419     return IntPoint(m_scrollOffset);
420 }
421
422 IntPoint RenderLayerScrollableArea::minimumScrollPosition() const
423 {
424     return -scrollOrigin();
425 }
426
427 IntPoint RenderLayerScrollableArea::maximumScrollPosition() const
428 {
429     if (!box().hasOverflowClip())
430         return -scrollOrigin();
431     return -scrollOrigin() + IntPoint(pixelSnappedScrollWidth(), pixelSnappedScrollHeight()) - enclosingIntRect(box().clientBoxRect()).size();
432 }
433
434 IntRect RenderLayerScrollableArea::visibleContentRect(IncludeScrollbarsInRect scrollbarInclusion) const
435 {
436     int verticalScrollbarWidth = 0;
437     int horizontalScrollbarHeight = 0;
438     if (scrollbarInclusion == IncludeScrollbars) {
439         verticalScrollbarWidth = (verticalScrollbar() && !verticalScrollbar()->isOverlayScrollbar()) ? verticalScrollbar()->width() : 0;
440         horizontalScrollbarHeight = (horizontalScrollbar() && !horizontalScrollbar()->isOverlayScrollbar()) ? horizontalScrollbar()->height() : 0;
441     }
442
443     return IntRect(IntPoint(scrollXOffset(), scrollYOffset()),
444         IntSize(max(0, layer()->size().width() - verticalScrollbarWidth), max(0, layer()->size().height() - horizontalScrollbarHeight)));
445 }
446
447 int RenderLayerScrollableArea::visibleHeight() const
448 {
449     return layer()->size().height();
450 }
451
452 int RenderLayerScrollableArea::visibleWidth() const
453 {
454     return layer()->size().width();
455 }
456
457 IntSize RenderLayerScrollableArea::contentsSize() const
458 {
459     return IntSize(scrollWidth(), scrollHeight());
460 }
461
462 IntSize RenderLayerScrollableArea::overhangAmount() const
463 {
464     return IntSize();
465 }
466
467 IntPoint RenderLayerScrollableArea::lastKnownMousePosition() const
468 {
469     return box().frame() ? box().frame()->eventHandler().lastKnownMousePosition() : IntPoint();
470 }
471
472 bool RenderLayerScrollableArea::shouldSuspendScrollAnimations() const
473 {
474     RenderView* view = box().view();
475     if (!view)
476         return true;
477     return view->frameView()->shouldSuspendScrollAnimations();
478 }
479
480 bool RenderLayerScrollableArea::scrollbarsCanBeActive() const
481 {
482     RenderView* view = box().view();
483     if (!view)
484         return false;
485     return view->frameView()->scrollbarsCanBeActive();
486 }
487
488 IntRect RenderLayerScrollableArea::scrollableAreaBoundingBox() const
489 {
490     return box().absoluteBoundingBoxRect();
491 }
492
493 bool RenderLayerScrollableArea::userInputScrollable(ScrollbarOrientation orientation) const
494 {
495     if (box().isIntristicallyScrollable(orientation))
496         return true;
497
498     EOverflow overflowStyle = (orientation == HorizontalScrollbar) ?
499         box().style()->overflowX() : box().style()->overflowY();
500     return (overflowStyle == OSCROLL || overflowStyle == OAUTO || overflowStyle == OOVERLAY);
501 }
502
503 bool RenderLayerScrollableArea::shouldPlaceVerticalScrollbarOnLeft() const
504 {
505     return box().style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft();
506 }
507
508 int RenderLayerScrollableArea::pageStep(ScrollbarOrientation orientation) const
509 {
510     int length = (orientation == HorizontalScrollbar) ?
511         box().pixelSnappedClientWidth() : box().pixelSnappedClientHeight();
512     int minPageStep = static_cast<float>(length) * ScrollableArea::minFractionToStepWhenPaging();
513     int pageStep = max(minPageStep, length - ScrollableArea::maxOverlapBetweenPages());
514
515     return max(pageStep, 1);
516 }
517
518 RenderBox& RenderLayerScrollableArea::box() const
519 {
520     return *m_layer.renderBox();
521 }
522
523 RenderLayer* RenderLayerScrollableArea::layer() const
524 {
525     return &m_layer;
526 }
527
528 LayoutUnit RenderLayerScrollableArea::scrollWidth() const
529 {
530     if (m_scrollDimensionsDirty)
531         const_cast<RenderLayerScrollableArea*>(this)->computeScrollDimensions();
532     return m_overflowRect.width();
533 }
534
535 LayoutUnit RenderLayerScrollableArea::scrollHeight() const
536 {
537     if (m_scrollDimensionsDirty)
538         const_cast<RenderLayerScrollableArea*>(this)->computeScrollDimensions();
539     return m_overflowRect.height();
540 }
541
542 int RenderLayerScrollableArea::pixelSnappedScrollWidth() const
543 {
544     return snapSizeToPixel(scrollWidth(), box().clientLeft() + box().x());
545 }
546
547 int RenderLayerScrollableArea::pixelSnappedScrollHeight() const
548 {
549     return snapSizeToPixel(scrollHeight(), box().clientTop() + box().y());
550 }
551
552 void RenderLayerScrollableArea::computeScrollDimensions()
553 {
554     m_scrollDimensionsDirty = false;
555
556     m_overflowRect = box().layoutOverflowRect();
557     box().flipForWritingMode(m_overflowRect);
558
559     int scrollableLeftOverflow = m_overflowRect.x() - box().borderLeft() - (box().style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft() ? box().verticalScrollbarWidth() : 0);
560     int scrollableTopOverflow = m_overflowRect.y() - box().borderTop();
561     setScrollOrigin(IntPoint(-scrollableLeftOverflow, -scrollableTopOverflow));
562 }
563
564 void RenderLayerScrollableArea::scrollToOffset(const IntSize& scrollOffset, ScrollOffsetClamping clamp)
565 {
566     IntSize newScrollOffset = clamp == ScrollOffsetClamped ? clampScrollOffset(scrollOffset) : scrollOffset;
567     if (newScrollOffset != adjustedScrollOffset())
568         scrollToOffsetWithoutAnimation(-scrollOrigin() + newScrollOffset);
569 }
570
571 void RenderLayerScrollableArea::updateAfterLayout()
572 {
573     m_scrollDimensionsDirty = true;
574     IntSize originalScrollOffset = adjustedScrollOffset();
575
576     computeScrollDimensions();
577
578     if (!box().isMarquee()) {
579         // Layout may cause us to be at an invalid scroll position. In this case we need
580         // to pull our scroll offsets back to the max (or push them up to the min).
581         IntSize clampedScrollOffset = clampScrollOffset(adjustedScrollOffset());
582         if (clampedScrollOffset != adjustedScrollOffset())
583             scrollToOffset(clampedScrollOffset);
584     }
585
586     if (originalScrollOffset != adjustedScrollOffset())
587         scrollToOffsetWithoutAnimation(-scrollOrigin() + adjustedScrollOffset());
588
589     bool hasHorizontalOverflow = this->hasHorizontalOverflow();
590     bool hasVerticalOverflow = this->hasVerticalOverflow();
591
592     {
593         // Hits in compositing/overflow/automatically-opt-into-composited-scrolling-after-style-change.html.
594         DisableCompositingQueryAsserts disabler;
595
596         // overflow:scroll should just enable/disable.
597         if (box().style()->overflowX() == OSCROLL)
598             horizontalScrollbar()->setEnabled(hasHorizontalOverflow);
599         if (box().style()->overflowY() == OSCROLL)
600             verticalScrollbar()->setEnabled(hasVerticalOverflow);
601     }
602
603     // overflow:auto may need to lay out again if scrollbars got added/removed.
604     bool autoHorizontalScrollBarChanged = box().hasAutoHorizontalScrollbar() && (hasHorizontalScrollbar() != hasHorizontalOverflow);
605     bool autoVerticalScrollBarChanged = box().hasAutoVerticalScrollbar() && (hasVerticalScrollbar() != hasVerticalOverflow);
606
607     if (autoHorizontalScrollBarChanged || autoVerticalScrollBarChanged) {
608         if (box().hasAutoHorizontalScrollbar())
609             setHasHorizontalScrollbar(hasHorizontalOverflow);
610         if (box().hasAutoVerticalScrollbar())
611             setHasVerticalScrollbar(hasVerticalOverflow);
612
613         if (hasVerticalOverflow || hasHorizontalOverflow)
614             updateScrollCornerStyle();
615
616         layer()->updateSelfPaintingLayer();
617
618         // Force an update since we know the scrollbars have changed things.
619         if (box().document().hasAnnotatedRegions())
620             box().document().setAnnotatedRegionsDirty(true);
621
622         if (box().style()->overflowX() == OAUTO || box().style()->overflowY() == OAUTO) {
623             if (!m_inOverflowRelayout) {
624                 // Our proprietary overflow: overlay value doesn't trigger a layout.
625                 m_inOverflowRelayout = true;
626                 SubtreeLayoutScope layoutScope(box());
627                 layoutScope.setNeedsLayout(&box());
628                 if (box().isRenderBlock()) {
629                     RenderBlock& block = toRenderBlock(box());
630                     block.scrollbarsChanged(autoHorizontalScrollBarChanged, autoVerticalScrollBarChanged);
631                     block.layoutBlock(true);
632                 } else {
633                     box().layout();
634                 }
635                 m_inOverflowRelayout = false;
636             }
637         }
638     }
639
640     {
641         // Hits in compositing/overflow/automatically-opt-into-composited-scrolling-after-style-change.html.
642         DisableCompositingQueryAsserts disabler;
643
644         // Set up the range (and page step/line step).
645         if (Scrollbar* horizontalScrollbar = this->horizontalScrollbar()) {
646             int clientWidth = box().pixelSnappedClientWidth();
647             horizontalScrollbar->setProportion(clientWidth, overflowRect().width());
648         }
649         if (Scrollbar* verticalScrollbar = this->verticalScrollbar()) {
650             int clientHeight = box().pixelSnappedClientHeight();
651             verticalScrollbar->setProportion(clientHeight, overflowRect().height());
652         }
653     }
654
655     bool hasOverflow = hasScrollableHorizontalOverflow() || hasScrollableVerticalOverflow();
656     updateScrollableAreaSet(hasOverflow);
657
658     if (hasOverflow) {
659         DisableCompositingQueryAsserts disabler;
660         positionOverflowControls(IntSize());
661     }
662 }
663
664 bool RenderLayerScrollableArea::hasHorizontalOverflow() const
665 {
666     ASSERT(!m_scrollDimensionsDirty);
667
668     return pixelSnappedScrollWidth() > box().pixelSnappedClientWidth();
669 }
670
671 bool RenderLayerScrollableArea::hasVerticalOverflow() const
672 {
673     ASSERT(!m_scrollDimensionsDirty);
674
675     return pixelSnappedScrollHeight() > box().pixelSnappedClientHeight();
676 }
677
678 bool RenderLayerScrollableArea::hasScrollableHorizontalOverflow() const
679 {
680     return hasHorizontalOverflow() && box().scrollsOverflowX();
681 }
682
683 bool RenderLayerScrollableArea::hasScrollableVerticalOverflow() const
684 {
685     return hasVerticalOverflow() && box().scrollsOverflowY();
686 }
687
688 static bool overflowRequiresScrollbar(EOverflow overflow)
689 {
690     return overflow == OSCROLL;
691 }
692
693 static bool overflowDefinesAutomaticScrollbar(EOverflow overflow)
694 {
695     return overflow == OAUTO || overflow == OOVERLAY;
696 }
697
698 // This function returns true if the given box requires overflow scrollbars (as
699 // opposed to the 'viewport' scrollbars managed by the RenderLayerCompositor).
700 // FIXME: we should use the same scrolling machinery for both the viewport and
701 // overflow. Currently, we need to avoid producing scrollbars here if they'll be
702 // handled externally in the RLC.
703 static bool canHaveOverflowScrollbars(const RenderBox& box)
704 {
705     return !box.isRenderView() && box.document().viewportDefiningElement() != box.node();
706 }
707
708 void RenderLayerScrollableArea::updateAfterStyleChange(const RenderStyle* oldStyle)
709 {
710     if (!canHaveOverflowScrollbars(box()))
711         return;
712
713     if (!m_scrollDimensionsDirty)
714         updateScrollableAreaSet(hasScrollableHorizontalOverflow() || hasScrollableVerticalOverflow());
715
716     EOverflow overflowX = box().style()->overflowX();
717     EOverflow overflowY = box().style()->overflowY();
718
719     // To avoid doing a relayout in updateScrollbarsAfterLayout, we try to keep any automatic scrollbar that was already present.
720     bool needsHorizontalScrollbar = (hasHorizontalScrollbar() && overflowDefinesAutomaticScrollbar(overflowX)) || overflowRequiresScrollbar(overflowX);
721     bool needsVerticalScrollbar = (hasVerticalScrollbar() && overflowDefinesAutomaticScrollbar(overflowY)) || overflowRequiresScrollbar(overflowY);
722     setHasHorizontalScrollbar(needsHorizontalScrollbar);
723     setHasVerticalScrollbar(needsVerticalScrollbar);
724
725     // With overflow: scroll, scrollbars are always visible but may be disabled.
726     // When switching to another value, we need to re-enable them (see bug 11985).
727     if (needsHorizontalScrollbar && oldStyle && oldStyle->overflowX() == OSCROLL && overflowX != OSCROLL) {
728         ASSERT(hasHorizontalScrollbar());
729         m_hBar->setEnabled(true);
730     }
731
732     if (needsVerticalScrollbar && oldStyle && oldStyle->overflowY() == OSCROLL && overflowY != OSCROLL) {
733         ASSERT(hasVerticalScrollbar());
734         m_vBar->setEnabled(true);
735     }
736
737     // FIXME: Need to detect a swap from custom to native scrollbars (and vice versa).
738     if (m_hBar)
739         m_hBar->styleChanged();
740     if (m_vBar)
741         m_vBar->styleChanged();
742
743     updateScrollCornerStyle();
744     updateResizerAreaSet();
745     updateResizerStyle();
746 }
747
748 bool RenderLayerScrollableArea::updateAfterCompositingChange()
749 {
750     layer()->updateScrollingStateAfterCompositingChange();
751     const bool layersChanged = m_topmostScrollChild != m_nextTopmostScrollChild;
752     m_topmostScrollChild = m_nextTopmostScrollChild;
753     m_nextTopmostScrollChild = nullptr;
754     return layersChanged;
755 }
756
757 void RenderLayerScrollableArea::updateAfterOverflowRecalc()
758 {
759     computeScrollDimensions();
760     if (Scrollbar* horizontalScrollbar = this->horizontalScrollbar()) {
761         int clientWidth = box().pixelSnappedClientWidth();
762         horizontalScrollbar->setProportion(clientWidth, overflowRect().width());
763     }
764     if (Scrollbar* verticalScrollbar = this->verticalScrollbar()) {
765         int clientHeight = box().pixelSnappedClientHeight();
766         verticalScrollbar->setProportion(clientHeight, overflowRect().height());
767     }
768
769     bool hasHorizontalOverflow = this->hasHorizontalOverflow();
770     bool hasVerticalOverflow = this->hasVerticalOverflow();
771     bool autoHorizontalScrollBarChanged = box().hasAutoHorizontalScrollbar() && (hasHorizontalScrollbar() != hasHorizontalOverflow);
772     bool autoVerticalScrollBarChanged = box().hasAutoVerticalScrollbar() && (hasVerticalScrollbar() != hasVerticalOverflow);
773     if (autoHorizontalScrollBarChanged || autoVerticalScrollBarChanged)
774         box().setNeedsLayoutAndFullPaintInvalidation();
775 }
776
777 IntSize RenderLayerScrollableArea::clampScrollOffset(const IntSize& scrollOffset) const
778 {
779     int maxX = scrollWidth() - box().pixelSnappedClientWidth();
780     int maxY = scrollHeight() - box().pixelSnappedClientHeight();
781
782     int x = std::max(std::min(scrollOffset.width(), maxX), 0);
783     int y = std::max(std::min(scrollOffset.height(), maxY), 0);
784     return IntSize(x, y);
785 }
786
787 IntRect RenderLayerScrollableArea::rectForHorizontalScrollbar(const IntRect& borderBoxRect) const
788 {
789     if (!m_hBar)
790         return IntRect();
791
792     const IntRect& scrollCorner = scrollCornerRect();
793
794     return IntRect(horizontalScrollbarStart(borderBoxRect.x()),
795         borderBoxRect.maxY() - box().borderBottom() - m_hBar->height(),
796         borderBoxRect.width() - (box().borderLeft() + box().borderRight()) - scrollCorner.width(),
797         m_hBar->height());
798 }
799
800 IntRect RenderLayerScrollableArea::rectForVerticalScrollbar(const IntRect& borderBoxRect) const
801 {
802     if (!m_vBar)
803         return IntRect();
804
805     const IntRect& scrollCorner = scrollCornerRect();
806
807     return IntRect(verticalScrollbarStart(borderBoxRect.x(), borderBoxRect.maxX()),
808         borderBoxRect.y() + box().borderTop(),
809         m_vBar->width(),
810         borderBoxRect.height() - (box().borderTop() + box().borderBottom()) - scrollCorner.height());
811 }
812
813 LayoutUnit RenderLayerScrollableArea::verticalScrollbarStart(int minX, int maxX) const
814 {
815     if (box().style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft())
816         return minX + box().borderLeft();
817     return maxX - box().borderRight() - m_vBar->width();
818 }
819
820 LayoutUnit RenderLayerScrollableArea::horizontalScrollbarStart(int minX) const
821 {
822     int x = minX + box().borderLeft();
823     if (box().style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft())
824         x += m_vBar ? m_vBar->width() : resizerCornerRect(box().pixelSnappedBorderBoxRect(), ResizerForPointer).width();
825     return x;
826 }
827
828 IntSize RenderLayerScrollableArea::scrollbarOffset(const Scrollbar* scrollbar) const
829 {
830     if (scrollbar == m_vBar.get())
831         return IntSize(verticalScrollbarStart(0, box().width()), box().borderTop());
832
833     if (scrollbar == m_hBar.get())
834         return IntSize(horizontalScrollbarStart(0), box().height() - box().borderBottom() - scrollbar->height());
835
836     ASSERT_NOT_REACHED();
837     return IntSize();
838 }
839
840 static inline RenderObject* rendererForScrollbar(RenderObject& renderer)
841 {
842     if (Node* node = renderer.node()) {
843         if (ShadowRoot* shadowRoot = node->containingShadowRoot()) {
844             if (shadowRoot->type() == ShadowRoot::UserAgentShadowRoot)
845                 return shadowRoot->host()->renderer();
846         }
847     }
848
849     return &renderer;
850 }
851
852 PassRefPtr<Scrollbar> RenderLayerScrollableArea::createScrollbar(ScrollbarOrientation orientation)
853 {
854     RefPtr<Scrollbar> widget;
855     RenderObject* actualRenderer = rendererForScrollbar(box());
856     bool hasCustomScrollbarStyle = actualRenderer->isBox() && actualRenderer->style()->hasPseudoStyle(SCROLLBAR);
857     if (hasCustomScrollbarStyle) {
858         widget = RenderScrollbar::createCustomScrollbar(this, orientation, actualRenderer->node());
859     } else {
860         ScrollbarControlSize scrollbarSize = RegularScrollbar;
861         if (actualRenderer->style()->hasAppearance())
862             scrollbarSize = RenderTheme::theme().scrollbarControlSizeForPart(actualRenderer->style()->appearance());
863         widget = Scrollbar::create(this, orientation, scrollbarSize);
864         if (orientation == HorizontalScrollbar)
865             didAddScrollbar(widget.get(), HorizontalScrollbar);
866         else
867             didAddScrollbar(widget.get(), VerticalScrollbar);
868     }
869     box().document().view()->addChild(widget.get());
870     return widget.release();
871 }
872
873 void RenderLayerScrollableArea::destroyScrollbar(ScrollbarOrientation orientation)
874 {
875     RefPtr<Scrollbar>& scrollbar = orientation == HorizontalScrollbar ? m_hBar : m_vBar;
876     if (!scrollbar)
877         return;
878
879     if (!scrollbar->isCustomScrollbar())
880         willRemoveScrollbar(scrollbar.get(), orientation);
881
882     scrollbar->removeFromParent();
883     scrollbar->disconnectFromScrollableArea();
884     scrollbar = nullptr;
885 }
886
887 void RenderLayerScrollableArea::setHasHorizontalScrollbar(bool hasScrollbar)
888 {
889     if (hasScrollbar == hasHorizontalScrollbar())
890         return;
891
892     if (hasScrollbar) {
893         // This doesn't hit in any tests, but since the equivalent code in setHasVerticalScrollbar
894         // does, presumably this code does as well.
895         DisableCompositingQueryAsserts disabler;
896         m_hBar = createScrollbar(HorizontalScrollbar);
897     } else {
898         destroyScrollbar(HorizontalScrollbar);
899     }
900
901     // Destroying or creating one bar can cause our scrollbar corner to come and go. We need to update the opposite scrollbar's style.
902     if (m_hBar)
903         m_hBar->styleChanged();
904     if (m_vBar)
905         m_vBar->styleChanged();
906
907     // Force an update since we know the scrollbars have changed things.
908     if (box().document().hasAnnotatedRegions())
909         box().document().setAnnotatedRegionsDirty(true);
910 }
911
912 void RenderLayerScrollableArea::setHasVerticalScrollbar(bool hasScrollbar)
913 {
914     if (hasScrollbar == hasVerticalScrollbar())
915         return;
916
917     if (hasScrollbar) {
918         // Hits in compositing/overflow/automatically-opt-into-composited-scrolling-after-style-change.html
919         DisableCompositingQueryAsserts disabler;
920         m_vBar = createScrollbar(VerticalScrollbar);
921     } else {
922         destroyScrollbar(VerticalScrollbar);
923     }
924
925     // Destroying or creating one bar can cause our scrollbar corner to come and go. We need to update the opposite scrollbar's style.
926     if (m_hBar)
927         m_hBar->styleChanged();
928     if (m_vBar)
929         m_vBar->styleChanged();
930
931     // Force an update since we know the scrollbars have changed things.
932     if (box().document().hasAnnotatedRegions())
933         box().document().setAnnotatedRegionsDirty(true);
934 }
935
936 int RenderLayerScrollableArea::verticalScrollbarWidth(OverlayScrollbarSizeRelevancy relevancy) const
937 {
938     if (!m_vBar || (m_vBar->isOverlayScrollbar() && (relevancy == IgnoreOverlayScrollbarSize || !m_vBar->shouldParticipateInHitTesting())))
939         return 0;
940     return m_vBar->width();
941 }
942
943 int RenderLayerScrollableArea::horizontalScrollbarHeight(OverlayScrollbarSizeRelevancy relevancy) const
944 {
945     if (!m_hBar || (m_hBar->isOverlayScrollbar() && (relevancy == IgnoreOverlayScrollbarSize || !m_hBar->shouldParticipateInHitTesting())))
946         return 0;
947     return m_hBar->height();
948 }
949
950 void RenderLayerScrollableArea::positionOverflowControls(const IntSize& offsetFromRoot)
951 {
952     if (!hasScrollbar() && !box().canResize())
953         return;
954
955     const IntRect borderBox = box().pixelSnappedBorderBoxRect();
956     if (Scrollbar* verticalScrollbar = this->verticalScrollbar()) {
957         IntRect vBarRect = rectForVerticalScrollbar(borderBox);
958         vBarRect.move(offsetFromRoot);
959         verticalScrollbar->setFrameRect(vBarRect);
960     }
961
962     if (Scrollbar* horizontalScrollbar = this->horizontalScrollbar()) {
963         IntRect hBarRect = rectForHorizontalScrollbar(borderBox);
964         hBarRect.move(offsetFromRoot);
965         horizontalScrollbar->setFrameRect(hBarRect);
966     }
967
968     const IntRect& scrollCorner = scrollCornerRect();
969     if (m_scrollCorner)
970         m_scrollCorner->setFrameRect(scrollCorner);
971
972     if (m_resizer)
973         m_resizer->setFrameRect(resizerCornerRect(borderBox, ResizerForPointer));
974
975     // FIXME, this should eventually be removed, once we are certain that composited
976     // controls get correctly positioned on a compositor update. For now, conservatively
977     // leaving this unchanged.
978     if (layer()->hasCompositedLayerMapping())
979         layer()->compositedLayerMapping()->positionOverflowControlsLayers(offsetFromRoot);
980 }
981
982 void RenderLayerScrollableArea::updateScrollCornerStyle()
983 {
984     if (!m_scrollCorner && !hasScrollbar())
985         return;
986     if (!m_scrollCorner && hasOverlayScrollbars())
987         return;
988
989     RenderObject* actualRenderer = rendererForScrollbar(box());
990     RefPtr<RenderStyle> corner = box().hasOverflowClip() ? actualRenderer->getUncachedPseudoStyle(PseudoStyleRequest(SCROLLBAR_CORNER), actualRenderer->style()) : PassRefPtr<RenderStyle>(nullptr);
991     if (corner) {
992         if (!m_scrollCorner) {
993             m_scrollCorner = RenderScrollbarPart::createAnonymous(&box().document());
994             m_scrollCorner->setParent(&box());
995         }
996         m_scrollCorner->setStyle(corner.release());
997     } else if (m_scrollCorner) {
998         m_scrollCorner->destroy();
999         m_scrollCorner = nullptr;
1000     }
1001 }
1002
1003 void RenderLayerScrollableArea::paintOverflowControls(GraphicsContext* context, const IntPoint& paintOffset, const IntRect& damageRect, bool paintingOverlayControls)
1004 {
1005     // Don't do anything if we have no overflow.
1006     if (!box().hasOverflowClip())
1007         return;
1008
1009     IntPoint adjustedPaintOffset = paintOffset;
1010     if (paintingOverlayControls)
1011         adjustedPaintOffset = m_cachedOverlayScrollbarOffset;
1012
1013     // Move the scrollbar widgets if necessary. We normally move and resize widgets during layout,
1014     // but sometimes widgets can move without layout occurring (most notably when you scroll a
1015     // document that contains fixed positioned elements).
1016     positionOverflowControls(toIntSize(adjustedPaintOffset));
1017
1018     // Overlay scrollbars paint in a second pass through the layer tree so that they will paint
1019     // on top of everything else. If this is the normal painting pass, paintingOverlayControls
1020     // will be false, and we should just tell the root layer that there are overlay scrollbars
1021     // that need to be painted. That will cause the second pass through the layer tree to run,
1022     // and we'll paint the scrollbars then. In the meantime, cache tx and ty so that the
1023     // second pass doesn't need to re-enter the RenderTree to get it right.
1024     if (hasOverlayScrollbars() && !paintingOverlayControls) {
1025         m_cachedOverlayScrollbarOffset = paintOffset;
1026         // It's not necessary to do the second pass if the scrollbars paint into layers.
1027         if ((m_hBar && layerForHorizontalScrollbar()) || (m_vBar && layerForVerticalScrollbar()))
1028             return;
1029         IntRect localDamgeRect = damageRect;
1030         localDamgeRect.moveBy(-paintOffset);
1031         if (!overflowControlsIntersectRect(localDamgeRect))
1032             return;
1033
1034         RenderView* renderView = box().view();
1035
1036         RenderLayer* paintingRoot = layer()->enclosingLayerWithCompositedLayerMapping(IncludeSelf);
1037         if (!paintingRoot)
1038             paintingRoot = renderView->layer();
1039
1040         paintingRoot->setContainsDirtyOverlayScrollbars(true);
1041         return;
1042     }
1043
1044     // This check is required to avoid painting custom CSS scrollbars twice.
1045     if (paintingOverlayControls && !hasOverlayScrollbars())
1046         return;
1047
1048     // Now that we're sure the scrollbars are in the right place, paint them.
1049     if (m_hBar && !layerForHorizontalScrollbar())
1050         m_hBar->paint(context, damageRect);
1051     if (m_vBar && !layerForVerticalScrollbar())
1052         m_vBar->paint(context, damageRect);
1053
1054     if (layerForScrollCorner())
1055         return;
1056
1057     // We fill our scroll corner with white if we have a scrollbar that doesn't run all the way up to the
1058     // edge of the box.
1059     paintScrollCorner(context, adjustedPaintOffset, damageRect);
1060
1061     // Paint our resizer last, since it sits on top of the scroll corner.
1062     paintResizer(context, adjustedPaintOffset, damageRect);
1063 }
1064
1065 void RenderLayerScrollableArea::paintScrollCorner(GraphicsContext* context, const IntPoint& paintOffset, const IntRect& damageRect)
1066 {
1067     IntRect absRect = scrollCornerRect();
1068     absRect.moveBy(paintOffset);
1069     if (!absRect.intersects(damageRect))
1070         return;
1071
1072     if (m_scrollCorner) {
1073         m_scrollCorner->paintIntoRect(context, paintOffset, absRect);
1074         return;
1075     }
1076
1077     // We don't want to paint white if we have overlay scrollbars, since we need
1078     // to see what is behind it.
1079     if (!hasOverlayScrollbars())
1080         context->fillRect(absRect, Color::white);
1081 }
1082
1083 bool RenderLayerScrollableArea::hitTestOverflowControls(HitTestResult& result, const IntPoint& localPoint)
1084 {
1085     if (!hasScrollbar() && !box().canResize())
1086         return false;
1087
1088     IntRect resizeControlRect;
1089     if (box().style()->resize() != RESIZE_NONE) {
1090         resizeControlRect = resizerCornerRect(box().pixelSnappedBorderBoxRect(), ResizerForPointer);
1091         if (resizeControlRect.contains(localPoint))
1092             return true;
1093     }
1094
1095     int resizeControlSize = max(resizeControlRect.height(), 0);
1096     if (m_vBar && m_vBar->shouldParticipateInHitTesting()) {
1097         LayoutRect vBarRect(verticalScrollbarStart(0, box().width()),
1098             box().borderTop(),
1099             m_vBar->width(),
1100             box().height() - (box().borderTop() + box().borderBottom()) - (m_hBar ? m_hBar->height() : resizeControlSize));
1101         if (vBarRect.contains(localPoint)) {
1102             result.setScrollbar(m_vBar.get());
1103             return true;
1104         }
1105     }
1106
1107     resizeControlSize = max(resizeControlRect.width(), 0);
1108     if (m_hBar && m_hBar->shouldParticipateInHitTesting()) {
1109         LayoutRect hBarRect(horizontalScrollbarStart(0),
1110             box().height() - box().borderBottom() - m_hBar->height(),
1111             box().width() - (box().borderLeft() + box().borderRight()) - (m_vBar ? m_vBar->width() : resizeControlSize),
1112             m_hBar->height());
1113         if (hBarRect.contains(localPoint)) {
1114             result.setScrollbar(m_hBar.get());
1115             return true;
1116         }
1117     }
1118
1119     // FIXME: We should hit test the m_scrollCorner and pass it back through the result.
1120
1121     return false;
1122 }
1123
1124 IntRect RenderLayerScrollableArea::resizerCornerRect(const IntRect& bounds, ResizerHitTestType resizerHitTestType) const
1125 {
1126     if (box().style()->resize() == RESIZE_NONE)
1127         return IntRect();
1128     IntRect corner = cornerRect(box().style(), horizontalScrollbar(), verticalScrollbar(), bounds);
1129
1130     if (resizerHitTestType == ResizerForTouch) {
1131         // We make the resizer virtually larger for touch hit testing. With the
1132         // expanding ratio k = ResizerControlExpandRatioForTouch, we first move
1133         // the resizer rect (of width w & height h), by (-w * (k-1), -h * (k-1)),
1134         // then expand the rect by new_w/h = w/h * k.
1135         int expandRatio = ResizerControlExpandRatioForTouch - 1;
1136         corner.move(-corner.width() * expandRatio, -corner.height() * expandRatio);
1137         corner.expand(corner.width() * expandRatio, corner.height() * expandRatio);
1138     }
1139
1140     return corner;
1141 }
1142
1143 IntRect RenderLayerScrollableArea::scrollCornerAndResizerRect() const
1144 {
1145     IntRect scrollCornerAndResizer = scrollCornerRect();
1146     if (scrollCornerAndResizer.isEmpty())
1147         scrollCornerAndResizer = resizerCornerRect(box().pixelSnappedBorderBoxRect(), ResizerForPointer);
1148     return scrollCornerAndResizer;
1149 }
1150
1151 bool RenderLayerScrollableArea::overflowControlsIntersectRect(const IntRect& localRect) const
1152 {
1153     const IntRect borderBox = box().pixelSnappedBorderBoxRect();
1154
1155     if (rectForHorizontalScrollbar(borderBox).intersects(localRect))
1156         return true;
1157
1158     if (rectForVerticalScrollbar(borderBox).intersects(localRect))
1159         return true;
1160
1161     if (scrollCornerRect().intersects(localRect))
1162         return true;
1163
1164     if (resizerCornerRect(borderBox, ResizerForPointer).intersects(localRect))
1165         return true;
1166
1167     return false;
1168 }
1169
1170 void RenderLayerScrollableArea::paintResizer(GraphicsContext* context, const IntPoint& paintOffset, const IntRect& damageRect)
1171 {
1172     if (box().style()->resize() == RESIZE_NONE)
1173         return;
1174
1175     IntRect absRect = resizerCornerRect(box().pixelSnappedBorderBoxRect(), ResizerForPointer);
1176     absRect.moveBy(paintOffset);
1177     if (!absRect.intersects(damageRect))
1178         return;
1179
1180     if (m_resizer) {
1181         m_resizer->paintIntoRect(context, paintOffset, absRect);
1182         return;
1183     }
1184
1185     drawPlatformResizerImage(context, absRect);
1186
1187     // Draw a frame around the resizer (1px grey line) if there are any scrollbars present.
1188     // Clipping will exclude the right and bottom edges of this frame.
1189     if (!hasOverlayScrollbars() && hasScrollbar()) {
1190         GraphicsContextStateSaver stateSaver(*context);
1191         context->clip(absRect);
1192         IntRect largerCorner = absRect;
1193         largerCorner.setSize(IntSize(largerCorner.width() + 1, largerCorner.height() + 1));
1194         context->setStrokeColor(Color(217, 217, 217));
1195         context->setStrokeThickness(1.0f);
1196         context->setFillColor(Color::transparent);
1197         context->drawRect(largerCorner);
1198     }
1199 }
1200
1201 bool RenderLayerScrollableArea::isPointInResizeControl(const IntPoint& absolutePoint, ResizerHitTestType resizerHitTestType) const
1202 {
1203     if (!box().canResize())
1204         return false;
1205
1206     IntPoint localPoint = roundedIntPoint(box().absoluteToLocal(absolutePoint, UseTransforms));
1207     IntRect localBounds(0, 0, box().pixelSnappedWidth(), box().pixelSnappedHeight());
1208     return resizerCornerRect(localBounds, resizerHitTestType).contains(localPoint);
1209 }
1210
1211 bool RenderLayerScrollableArea::hitTestResizerInFragments(const LayerFragments& layerFragments, const HitTestLocation& hitTestLocation) const
1212 {
1213     if (!box().canResize())
1214         return false;
1215
1216     if (layerFragments.isEmpty())
1217         return false;
1218
1219     for (int i = layerFragments.size() - 1; i >= 0; --i) {
1220         const LayerFragment& fragment = layerFragments.at(i);
1221         if (fragment.backgroundRect.intersects(hitTestLocation) && resizerCornerRect(pixelSnappedIntRect(fragment.layerBounds), ResizerForPointer).contains(hitTestLocation.roundedPoint()))
1222             return true;
1223     }
1224
1225     return false;
1226 }
1227
1228 void RenderLayerScrollableArea::updateResizerAreaSet()
1229 {
1230     LocalFrame* frame = box().frame();
1231     if (!frame)
1232         return;
1233     FrameView* frameView = frame->view();
1234     if (!frameView)
1235         return;
1236     if (box().canResize())
1237         frameView->addResizerArea(box());
1238     else
1239         frameView->removeResizerArea(box());
1240 }
1241
1242 void RenderLayerScrollableArea::updateResizerStyle()
1243 {
1244     if (!m_resizer && !box().canResize())
1245         return;
1246
1247     RenderObject* actualRenderer = rendererForScrollbar(box());
1248     RefPtr<RenderStyle> resizer = box().hasOverflowClip() ? actualRenderer->getUncachedPseudoStyle(PseudoStyleRequest(RESIZER), actualRenderer->style()) : PassRefPtr<RenderStyle>(nullptr);
1249     if (resizer) {
1250         if (!m_resizer) {
1251             m_resizer = RenderScrollbarPart::createAnonymous(&box().document());
1252             m_resizer->setParent(&box());
1253         }
1254         m_resizer->setStyle(resizer.release());
1255     } else if (m_resizer) {
1256         m_resizer->destroy();
1257         m_resizer = nullptr;
1258     }
1259 }
1260
1261 void RenderLayerScrollableArea::drawPlatformResizerImage(GraphicsContext* context, IntRect resizerCornerRect)
1262 {
1263     float deviceScaleFactor = blink::deviceScaleFactor(box().frame());
1264
1265     RefPtr<Image> resizeCornerImage;
1266     IntSize cornerResizerSize;
1267     if (deviceScaleFactor >= 2) {
1268         DEFINE_STATIC_REF(Image, resizeCornerImageHiRes, (Image::loadPlatformResource("textAreaResizeCorner@2x")));
1269         resizeCornerImage = resizeCornerImageHiRes;
1270         cornerResizerSize = resizeCornerImage->size();
1271         cornerResizerSize.scale(0.5f);
1272     } else {
1273         DEFINE_STATIC_REF(Image, resizeCornerImageLoRes, (Image::loadPlatformResource("textAreaResizeCorner")));
1274         resizeCornerImage = resizeCornerImageLoRes;
1275         cornerResizerSize = resizeCornerImage->size();
1276     }
1277
1278     if (box().style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft()) {
1279         context->save();
1280         context->translate(resizerCornerRect.x() + cornerResizerSize.width(), resizerCornerRect.y() + resizerCornerRect.height() - cornerResizerSize.height());
1281         context->scale(-1.0, 1.0);
1282         context->drawImage(resizeCornerImage.get(), IntRect(IntPoint(), cornerResizerSize));
1283         context->restore();
1284         return;
1285     }
1286     IntRect imageRect(resizerCornerRect.maxXMaxYCorner() - cornerResizerSize, cornerResizerSize);
1287     context->drawImage(resizeCornerImage.get(), imageRect);
1288 }
1289
1290 IntSize RenderLayerScrollableArea::offsetFromResizeCorner(const IntPoint& absolutePoint) const
1291 {
1292     // Currently the resize corner is either the bottom right corner or the bottom left corner.
1293     // FIXME: This assumes the location is 0, 0. Is this guaranteed to always be the case?
1294     IntSize elementSize = layer()->size();
1295     if (box().style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft())
1296         elementSize.setWidth(0);
1297     IntPoint resizerPoint = IntPoint(elementSize);
1298     IntPoint localPoint = roundedIntPoint(box().absoluteToLocal(absolutePoint, UseTransforms));
1299     return localPoint - resizerPoint;
1300 }
1301
1302 void RenderLayerScrollableArea::resize(const PlatformEvent& evt, const LayoutSize& oldOffset)
1303 {
1304     // FIXME: This should be possible on generated content but is not right now.
1305     if (!inResizeMode() || !box().canResize() || !box().node())
1306         return;
1307
1308     ASSERT(box().node()->isElementNode());
1309     Element* element = toElement(box().node());
1310
1311     Document& document = element->document();
1312
1313     IntPoint pos;
1314     const PlatformGestureEvent* gevt = 0;
1315
1316     switch (evt.type()) {
1317     case PlatformEvent::MouseMoved:
1318         if (!document.frame()->eventHandler().mousePressed())
1319             return;
1320         pos = static_cast<const PlatformMouseEvent*>(&evt)->position();
1321         break;
1322     case PlatformEvent::GestureScrollUpdate:
1323     case PlatformEvent::GestureScrollUpdateWithoutPropagation:
1324         pos = static_cast<const PlatformGestureEvent*>(&evt)->position();
1325         gevt = static_cast<const PlatformGestureEvent*>(&evt);
1326         pos = gevt->position();
1327         pos.move(gevt->deltaX(), gevt->deltaY());
1328         break;
1329     default:
1330         ASSERT_NOT_REACHED();
1331     }
1332
1333     float zoomFactor = box().style()->effectiveZoom();
1334
1335     LayoutSize newOffset = offsetFromResizeCorner(document.view()->windowToContents(pos));
1336     newOffset.setWidth(newOffset.width() / zoomFactor);
1337     newOffset.setHeight(newOffset.height() / zoomFactor);
1338
1339     LayoutSize currentSize = LayoutSize(box().width() / zoomFactor, box().height() / zoomFactor);
1340     LayoutSize minimumSize = element->minimumSizeForResizing().shrunkTo(currentSize);
1341     element->setMinimumSizeForResizing(minimumSize);
1342
1343     LayoutSize adjustedOldOffset = LayoutSize(oldOffset.width() / zoomFactor, oldOffset.height() / zoomFactor);
1344     if (box().style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft()) {
1345         newOffset.setWidth(-newOffset.width());
1346         adjustedOldOffset.setWidth(-adjustedOldOffset.width());
1347     }
1348
1349     LayoutSize difference = (currentSize + newOffset - adjustedOldOffset).expandedTo(minimumSize) - currentSize;
1350
1351     bool isBoxSizingBorder = box().style()->boxSizing() == BORDER_BOX;
1352
1353     EResize resize = box().style()->resize();
1354     if (resize != RESIZE_VERTICAL && difference.width()) {
1355         if (element->isFormControlElement()) {
1356             // Make implicit margins from the theme explicit (see <http://bugs.webkit.org/show_bug.cgi?id=9547>).
1357             element->setInlineStyleProperty(CSSPropertyMarginLeft, box().marginLeft() / zoomFactor, CSSPrimitiveValue::CSS_PX);
1358             element->setInlineStyleProperty(CSSPropertyMarginRight, box().marginRight() / zoomFactor, CSSPrimitiveValue::CSS_PX);
1359         }
1360         LayoutUnit baseWidth = box().width() - (isBoxSizingBorder ? LayoutUnit() : box().borderAndPaddingWidth());
1361         baseWidth = baseWidth / zoomFactor;
1362         element->setInlineStyleProperty(CSSPropertyWidth, roundToInt(baseWidth + difference.width()), CSSPrimitiveValue::CSS_PX);
1363     }
1364
1365     if (resize != RESIZE_HORIZONTAL && difference.height()) {
1366         if (element->isFormControlElement()) {
1367             // Make implicit margins from the theme explicit (see <http://bugs.webkit.org/show_bug.cgi?id=9547>).
1368             element->setInlineStyleProperty(CSSPropertyMarginTop, box().marginTop() / zoomFactor, CSSPrimitiveValue::CSS_PX);
1369             element->setInlineStyleProperty(CSSPropertyMarginBottom, box().marginBottom() / zoomFactor, CSSPrimitiveValue::CSS_PX);
1370         }
1371         LayoutUnit baseHeight = box().height() - (isBoxSizingBorder ? LayoutUnit() : box().borderAndPaddingHeight());
1372         baseHeight = baseHeight / zoomFactor;
1373         element->setInlineStyleProperty(CSSPropertyHeight, roundToInt(baseHeight + difference.height()), CSSPrimitiveValue::CSS_PX);
1374     }
1375
1376     document.updateLayout();
1377
1378     // FIXME (Radar 4118564): We should also autoscroll the window as necessary to keep the point under the cursor in view.
1379 }
1380
1381 LayoutRect RenderLayerScrollableArea::exposeRect(const LayoutRect& rect, const ScrollAlignment& alignX, const ScrollAlignment& alignY)
1382 {
1383     LayoutRect localExposeRect(box().absoluteToLocalQuad(FloatQuad(FloatRect(rect)), UseTransforms).boundingBox());
1384     LayoutRect layerBounds(0, 0, box().clientWidth(), box().clientHeight());
1385     LayoutRect r = ScrollAlignment::getRectToExpose(layerBounds, localExposeRect, alignX, alignY);
1386
1387     IntSize clampedScrollOffset = clampScrollOffset(adjustedScrollOffset() + toIntSize(roundedIntRect(r).location()));
1388     if (clampedScrollOffset == adjustedScrollOffset())
1389         return rect;
1390
1391     IntSize oldScrollOffset = adjustedScrollOffset();
1392     scrollToOffset(clampedScrollOffset);
1393     IntSize scrollOffsetDifference = adjustedScrollOffset() - oldScrollOffset;
1394     localExposeRect.move(-scrollOffsetDifference);
1395     return LayoutRect(box().localToAbsoluteQuad(FloatQuad(FloatRect(localExposeRect)), UseTransforms).boundingBox());
1396 }
1397
1398 void RenderLayerScrollableArea::updateScrollableAreaSet(bool hasOverflow)
1399 {
1400     LocalFrame* frame = box().frame();
1401     if (!frame)
1402         return;
1403
1404     FrameView* frameView = frame->view();
1405     if (!frameView)
1406         return;
1407
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->renderer() && owner->renderer()->visibleToHitTesting();
1412
1413     bool didScrollOverflow = m_scrollsOverflow;
1414
1415     m_scrollsOverflow = hasOverflow && isVisibleToHitTest;
1416     if (didScrollOverflow == scrollsOverflow())
1417         return;
1418
1419     if (m_scrollsOverflow)
1420         frameView->addScrollableArea(this);
1421     else
1422         frameView->removeScrollableArea(this);
1423 }
1424
1425 void RenderLayerScrollableArea::updateCompositingLayersAfterScroll()
1426 {
1427     RenderLayerCompositor* compositor = box().view()->compositor();
1428     if (compositor->inCompositingMode()) {
1429         if (usesCompositedScrolling()) {
1430             DisableCompositingQueryAsserts disabler;
1431             ASSERT(layer()->hasCompositedLayerMapping());
1432             layer()->compositedLayerMapping()->setNeedsGraphicsLayerUpdate(GraphicsLayerUpdateSubtree);
1433             compositor->setNeedsCompositingUpdate(CompositingUpdateAfterGeometryChange);
1434         } else {
1435             layer()->setNeedsCompositingInputsUpdate();
1436         }
1437     }
1438 }
1439
1440 bool RenderLayerScrollableArea::usesCompositedScrolling() const
1441 {
1442     // Scroll form controls on the main thread so they exhibit correct touch scroll event bubbling
1443     if (box().isIntristicallyScrollable(VerticalScrollbar) || box().isIntristicallyScrollable(HorizontalScrollbar))
1444         return false;
1445
1446     // See https://codereview.chromium.org/176633003/ for the tests that fail without this disabler.
1447     DisableCompositingQueryAsserts disabler;
1448     return layer()->hasCompositedLayerMapping() && layer()->compositedLayerMapping()->scrollingLayer();
1449 }
1450
1451 static bool layerNeedsCompositedScrolling(const RenderLayer* layer)
1452 {
1453     return layer->scrollsOverflow()
1454         && layer->compositor()->preferCompositingToLCDTextEnabled()
1455         && !layer->hasDescendantWithClipPath()
1456         && !layer->hasAncestorWithClipPath()
1457         && !layer->renderer()->style()->hasBorderRadius();
1458 }
1459
1460 void RenderLayerScrollableArea::updateNeedsCompositedScrolling()
1461 {
1462     const bool needsCompositedScrolling = layerNeedsCompositedScrolling(layer());
1463     if (static_cast<bool>(m_needsCompositedScrolling) != needsCompositedScrolling) {
1464         m_needsCompositedScrolling = needsCompositedScrolling;
1465         layer()->didUpdateNeedsCompositedScrolling();
1466     }
1467 }
1468
1469 void RenderLayerScrollableArea::setTopmostScrollChild(RenderLayer* scrollChild)
1470 {
1471     // We only want to track the topmost scroll child for scrollable areas with
1472     // overlay scrollbars.
1473     if (!hasOverlayScrollbars())
1474         return;
1475     m_nextTopmostScrollChild = scrollChild;
1476 }
1477
1478 } // namespace blink