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