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