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