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