Split computeScrollDimensions() out of RenderLayer

This change also moves 2 members of RenderLayer to
RenderLayerScrollableArea and a lot of related code to the
new class. In particular, the after-layout and after-style-change
notification had to be added.

BUG=260899

Review URL: https://chromiumcodereview.appspot.com/22893055

git-svn-id: svn://svn.chromium.org/blink/trunk@157192 bbb929c8-8fbe-4397-9dbb-9b2b20218538
diff --git a/Source/core/accessibility/AccessibilityRenderObject.cpp b/Source/core/accessibility/AccessibilityRenderObject.cpp
index aae67d2..579d3b9 100644
--- a/Source/core/accessibility/AccessibilityRenderObject.cpp
+++ b/Source/core/accessibility/AccessibilityRenderObject.cpp
@@ -1677,7 +1677,7 @@
         return;
 
     RenderLayer* layer = box->layer();
-    layer->scrollToOffset(toIntSize(point), RenderLayer::ScrollOffsetClamped);
+    layer->scrollToOffset(toIntSize(point), ScrollOffsetClamped);
 }
 
 //
diff --git a/Source/core/html/TextFieldInputType.cpp b/Source/core/html/TextFieldInputType.cpp
index 8cc69ea..c6c0b23 100644
--- a/Source/core/html/TextFieldInputType.cpp
+++ b/Source/core/html/TextFieldInputType.cpp
@@ -187,7 +187,7 @@
             if (RenderBox* innerTextRenderer = innerTextElement()->renderBox()) {
                 if (RenderLayer* innerLayer = innerTextRenderer->layer()) {
                     IntSize scrollOffset(!renderTextControl->style()->isLeftToRightDirection() ? innerLayer->scrollWidth() : 0, 0);
-                    innerLayer->scrollToOffset(scrollOffset, RenderLayer::ScrollOffsetClamped);
+                    innerLayer->scrollToOffset(scrollOffset, ScrollOffsetClamped);
                 }
             }
 
diff --git a/Source/core/rendering/RenderBox.cpp b/Source/core/rendering/RenderBox.cpp
index 90bedf1..66f69da 100644
--- a/Source/core/rendering/RenderBox.cpp
+++ b/Source/core/rendering/RenderBox.cpp
@@ -474,13 +474,13 @@
 void RenderBox::setScrollLeft(int newLeft)
 {
     if (hasOverflowClip())
-        layer()->scrollToXOffset(newLeft, RenderLayer::ScrollOffsetClamped);
+        layer()->scrollToXOffset(newLeft, ScrollOffsetClamped);
 }
 
 void RenderBox::setScrollTop(int newTop)
 {
     if (hasOverflowClip())
-        layer()->scrollToYOffset(newTop, RenderLayer::ScrollOffsetClamped);
+        layer()->scrollToYOffset(newTop, ScrollOffsetClamped);
 }
 
 void RenderBox::absoluteRects(Vector<IntRect>& rects, const LayoutPoint& accumulatedOffset) const
diff --git a/Source/core/rendering/RenderLayer.cpp b/Source/core/rendering/RenderLayer.cpp
index ad7140e..957df9c 100644
--- a/Source/core/rendering/RenderLayer.cpp
+++ b/Source/core/rendering/RenderLayer.cpp
@@ -52,10 +52,8 @@
 #include "core/dom/Document.h"
 #include "core/dom/DocumentEventQueue.h"
 #include "core/dom/shadow/ShadowRoot.h"
-#include "core/editing/FrameSelection.h"
 #include "core/html/HTMLFrameElement.h"
 #include "core/html/HTMLFrameOwnerElement.h"
-#include "core/inspector/InspectorInstrumentation.h"
 #include "core/page/EventHandler.h"
 #include "core/page/FocusController.h"
 #include "core/page/Frame.h"
@@ -125,7 +123,6 @@
 
 RenderLayer::RenderLayer(RenderLayerModelObject* renderer)
     : m_inResizeMode(false)
-    , m_scrollDimensionsDirty(true)
     , m_normalFlowListDirty(true)
     , m_hasSelfPaintingLayerDescendant(false)
     , m_hasSelfPaintingLayerDescendantDirty(false)
@@ -2178,7 +2175,7 @@
 
     if (renderer()->hasOverflowClip() && !restrictedByLineClamp) {
         IntSize newScrollOffset = adjustedScrollOffset() + delta;
-        scrollToOffset(newScrollOffset, clamp);
+        m_scrollableArea->scrollToOffset(newScrollOffset, clamp);
 
         // If this layer can't do the scroll we ask the next layer up that can scroll to try
         IntSize remainingScrollOffset = newScrollOffset - adjustedScrollOffset();
@@ -2200,90 +2197,9 @@
     }
 }
 
-IntSize RenderLayer::clampScrollOffset(const IntSize& scrollOffset) const
-{
-    RenderBox* box = renderBox();
-    ASSERT(box);
-
-    int maxX = scrollWidth() - box->pixelSnappedClientWidth();
-    int maxY = scrollHeight() - box->pixelSnappedClientHeight();
-
-    int x = max(min(scrollOffset.width(), maxX), 0);
-    int y = max(min(scrollOffset.height(), maxY), 0);
-    return IntSize(x, y);
-}
-
 void RenderLayer::scrollToOffset(const IntSize& scrollOffset, ScrollOffsetClamping clamp)
 {
-    IntSize newScrollOffset = clamp == ScrollOffsetClamped ? clampScrollOffset(scrollOffset) : scrollOffset;
-    if (newScrollOffset != adjustedScrollOffset())
-        m_scrollableArea->scrollToOffsetWithoutAnimation(-scrollOrigin() + newScrollOffset);
-}
-
-void RenderLayer::setScrollOffset(const IntPoint& newScrollOffset)
-{
-    RenderBox* box = renderBox();
-    if (!box)
-        return;
-
-    if (!box->isMarquee()) {
-        // Ensure that the dimensions will be computed if they need to be (for overflow:hidden blocks).
-        if (m_scrollDimensionsDirty)
-            computeScrollDimensions();
-    }
-
-    if (m_scrollableArea->scrollOffset() == toIntSize(newScrollOffset))
-        return;
-    m_scrollableArea->setScrollOffset(toIntSize(newScrollOffset));
-
-    Frame* frame = renderer()->frame();
-    InspectorInstrumentation::willScrollLayer(renderer());
-
-    RenderView* view = renderer()->view();
-
-    // We should have a RenderView if we're trying to scroll.
-    ASSERT(view);
-
-    // Update the positions of our child layers (if needed as only fixed layers should be impacted by a scroll).
-    // We don't update compositing layers, because we need to do a deep update from the compositing ancestor.
-    bool inLayout = view ? view->frameView()->isInLayout() : false;
-    if (!inLayout) {
-        // If we're in the middle of layout, we'll just update layers once layout has finished.
-        updateLayerPositionsAfterOverflowScroll();
-        if (view) {
-            // Update regions, scrolling may change the clip of a particular region.
-            view->frameView()->updateAnnotatedRegions();
-            view->updateWidgetPositions();
-        }
-
-        updateCompositingLayersAfterScroll();
-    }
-
-    RenderLayerModelObject* repaintContainer = renderer()->containerForRepaint();
-    if (frame) {
-        // The caret rect needs to be invalidated after scrolling
-        frame->selection().setCaretRectNeedsUpdate();
-
-        FloatQuad quadForFakeMouseMoveEvent = FloatQuad(m_repaintRect);
-        if (repaintContainer)
-            quadForFakeMouseMoveEvent = repaintContainer->localToAbsoluteQuad(quadForFakeMouseMoveEvent);
-        frame->eventHandler()->dispatchFakeMouseMoveEventSoonInQuad(quadForFakeMouseMoveEvent);
-    }
-
-    bool requiresRepaint = true;
-
-    if (compositor()->inCompositingMode() && usesCompositedScrolling())
-        requiresRepaint = false;
-
-    // Just schedule a full repaint of our object.
-    if (view && requiresRepaint)
-        renderer()->repaintUsingContainer(repaintContainer, pixelSnappedIntRect(m_repaintRect));
-
-    // Schedule the scroll DOM event.
-    if (renderer()->node())
-        renderer()->node()->document().eventQueue()->enqueueOrDispatchScrollEvent(renderer()->node(), DocumentEventQueue::ScrollEventElementTarget);
-
-    InspectorInstrumentation::didScrollLayer(renderer());
+    m_scrollableArea->scrollToOffset(scrollOffset, clamp);
 }
 
 static inline bool frameElementAndViewPermitScroll(HTMLFrameElementBase* frameElementBase, FrameView* frameView)
@@ -2330,10 +2246,10 @@
         LayoutRect layerBounds(0, 0, box->clientWidth(), box->clientHeight());
         LayoutRect r = getRectToExpose(layerBounds, localExposeRect, alignX, alignY);
 
-        IntSize clampedScrollOffset = clampScrollOffset(adjustedScrollOffset() + toIntSize(roundedIntRect(r).location()));
+        IntSize clampedScrollOffset = m_scrollableArea->clampScrollOffset(adjustedScrollOffset() + toIntSize(roundedIntRect(r).location()));
         if (clampedScrollOffset != adjustedScrollOffset()) {
             IntSize oldScrollOffset = adjustedScrollOffset();
-            scrollToOffset(clampedScrollOffset);
+            m_scrollableArea->scrollToOffset(clampedScrollOffset);
             IntSize scrollOffsetDifference = adjustedScrollOffset() - oldScrollOffset;
             localExposeRect.move(-scrollOffsetDifference);
             newRect = LayoutRect(box->localToAbsoluteQuad(FloatQuad(FloatRect(localExposeRect)), UseTransforms).boundingBox());
@@ -2576,24 +2492,10 @@
 
 int RenderLayer::scrollSize(ScrollbarOrientation orientation) const
 {
-    IntSize scrollDimensions = maximumScrollPosition() - minimumScrollPosition();
+    IntSize scrollDimensions = scrollableArea()->maximumScrollPosition() - scrollableArea()->minimumScrollPosition();
     return (orientation == HorizontalScrollbar) ? scrollDimensions.width() : scrollDimensions.height();
 }
 
-IntPoint RenderLayer::minimumScrollPosition() const
-{
-    return -scrollOrigin();
-}
-
-IntPoint RenderLayer::maximumScrollPosition() const
-{
-    RenderBox* box = renderBox();
-    if (!box || !box->hasOverflowClip())
-        return -scrollOrigin();
-
-    return -scrollOrigin() + enclosingIntRect(m_overflowRect).size() - enclosingIntRect(box->clientBoxRect()).size();
-}
-
 IntSize RenderLayer::overhangAmount() const
 {
     return IntSize();
@@ -2733,11 +2635,6 @@
     return point;
 }
 
-IntSize RenderLayer::contentsSize() const
-{
-    return IntSize(scrollWidth(), scrollHeight());
-}
-
 int RenderLayer::visibleHeight() const
 {
     return m_layerSize.height();
@@ -3035,57 +2932,12 @@
 
 int RenderLayer::scrollWidth() const
 {
-    ASSERT(renderBox());
-    if (m_scrollDimensionsDirty)
-        const_cast<RenderLayer*>(this)->computeScrollDimensions();
-    return snapSizeToPixel(m_overflowRect.width(), renderBox()->clientLeft() + renderBox()->x());
+    return m_scrollableArea->scrollWidth();
 }
 
 int RenderLayer::scrollHeight() const
 {
-    ASSERT(renderBox());
-    if (m_scrollDimensionsDirty)
-        const_cast<RenderLayer*>(this)->computeScrollDimensions();
-    return snapSizeToPixel(m_overflowRect.height(), renderBox()->clientTop() + renderBox()->y());
-}
-
-void RenderLayer::computeScrollDimensions()
-{
-    RenderBox* box = renderBox();
-    ASSERT(box);
-
-    m_scrollDimensionsDirty = false;
-
-    m_overflowRect = box->layoutOverflowRect();
-    box->flipForWritingMode(m_overflowRect);
-
-    int scrollableLeftOverflow = m_overflowRect.x() - box->borderLeft();
-    int scrollableTopOverflow = m_overflowRect.y() - box->borderTop();
-    m_scrollableArea->setScrollOrigin(IntPoint(-scrollableLeftOverflow, -scrollableTopOverflow));
-}
-
-bool RenderLayer::hasScrollableHorizontalOverflow() const
-{
-    return hasHorizontalOverflow() && renderBox()->scrollsOverflowX();
-}
-
-bool RenderLayer::hasScrollableVerticalOverflow() const
-{
-    return hasVerticalOverflow() && renderBox()->scrollsOverflowY();
-}
-
-bool RenderLayer::hasHorizontalOverflow() const
-{
-    ASSERT(!m_scrollDimensionsDirty);
-
-    return scrollWidth() > renderBox()->pixelSnappedClientWidth();
-}
-
-bool RenderLayer::hasVerticalOverflow() const
-{
-    ASSERT(!m_scrollDimensionsDirty);
-
-    return scrollHeight() > renderBox()->pixelSnappedClientHeight();
+    return m_scrollableArea->scrollHeight();
 }
 
 void RenderLayer::updateScrollbarsAfterLayout()
@@ -3097,8 +2949,8 @@
     if (box->style()->appearance() == ListboxPart)
         return;
 
-    bool hasHorizontalOverflow = this->hasHorizontalOverflow();
-    bool hasVerticalOverflow = this->hasVerticalOverflow();
+    bool hasHorizontalOverflow = m_scrollableArea->hasHorizontalOverflow();
+    bool hasVerticalOverflow = m_scrollableArea->hasVerticalOverflow();
 
     // overflow:scroll should just enable/disable.
     if (renderer()->style()->overflowX() == OSCROLL)
@@ -3144,14 +2996,14 @@
     // Set up the range (and page step/line step).
     if (m_hBar) {
         int clientWidth = box->pixelSnappedClientWidth();
-        m_hBar->setProportion(clientWidth, m_overflowRect.width());
+        m_hBar->setProportion(clientWidth, m_scrollableArea->overflowRect().width());
     }
     if (m_vBar) {
         int clientHeight = box->pixelSnappedClientHeight();
-        m_vBar->setProportion(clientHeight, m_overflowRect.height());
+        m_vBar->setProportion(clientHeight, m_scrollableArea->overflowRect().height());
     }
 
-    updateScrollableAreaSet(hasScrollableHorizontalOverflow() || hasScrollableVerticalOverflow());
+    updateScrollableAreaSet(m_scrollableArea->hasScrollableHorizontalOverflow() || m_scrollableArea->hasScrollableVerticalOverflow());
 }
 
 void RenderLayer::updateScrollInfoAfterLayout()
@@ -3160,24 +3012,9 @@
     if (!box)
         return;
 
-    m_scrollDimensionsDirty = true;
-    IntSize originalScrollOffset = adjustedScrollOffset();
-
-    computeScrollDimensions();
-
-    if (!box->isMarquee()) {
-        // Layout may cause us to be at an invalid scroll position. In this case we need
-        // to pull our scroll offsets back to the max (or push them up to the min).
-        IntSize clampedScrollOffset = clampScrollOffset(adjustedScrollOffset());
-        if (clampedScrollOffset != adjustedScrollOffset())
-            scrollToOffset(clampedScrollOffset);
-    }
-
+    m_scrollableArea->updateAfterLayout();
     updateScrollbarsAfterLayout();
 
-    if (originalScrollOffset != adjustedScrollOffset())
-        scrollableArea()->scrollToOffsetWithoutAnimation(-scrollOrigin() + adjustedScrollOffset());
-
     // Composited scrolling may need to be enabled or disabled if the amount of overflow changed.
     if (renderer()->view() && compositor()->updateLayerCompositingState(this))
         compositor()->setCompositingLayersNeedRebuild();
@@ -5996,8 +5833,7 @@
         m_vBar->setEnabled(true);
     }
 
-    if (!m_scrollDimensionsDirty)
-        updateScrollableAreaSet(hasScrollableHorizontalOverflow() || hasScrollableVerticalOverflow());
+    m_scrollableArea->updateAfterStyleChange(oldStyle);
 }
 
 void RenderLayer::updateOutOfFlowPositioned(const RenderStyle* oldStyle)
@@ -6411,7 +6247,7 @@
             // For scrolling layers, rects are taken to be in the space of the contents.
             // We need to include both the entire contents, and also the bounding box
             // of the layer in the space of it's parent (eg. for border / scroll bars).
-            rect.append(m_overflowRect);
+            rect.append(m_scrollableArea->overflowRect());
             rects.set(this, rect);
             if (const RenderLayer* parentLayer = parent()) {
                 LayerHitTestRects::iterator iter = rects.find(parentLayer);
diff --git a/Source/core/rendering/RenderLayer.h b/Source/core/rendering/RenderLayer.h
index a404cae..6b944dd 100644
--- a/Source/core/rendering/RenderLayer.h
+++ b/Source/core/rendering/RenderLayer.h
@@ -383,17 +383,11 @@
 
     void panScrollFromPoint(const IntPoint&);
 
-    enum ScrollOffsetClamping {
-        ScrollOffsetUnclamped,
-        ScrollOffsetClamped
-    };
-
     // Scrolling methods for layers that can scroll their overflow.
     void scrollByRecursively(const IntSize&, ScrollOffsetClamping = ScrollOffsetUnclamped);
     void scrollToOffset(const IntSize&, ScrollOffsetClamping = ScrollOffsetUnclamped);
     void scrollToXOffset(int x, ScrollOffsetClamping clamp = ScrollOffsetUnclamped) { scrollToOffset(IntSize(x, scrollYOffset()), clamp); }
     void scrollToYOffset(int y, ScrollOffsetClamping clamp = ScrollOffsetUnclamped) { scrollToOffset(IntSize(scrollXOffset(), y), clamp); }
-
     void scrollRectToVisible(const LayoutRect&, const ScrollAlignment& alignX, const ScrollAlignment& alignY);
 
     LayoutRect getRectToExpose(const LayoutRect& visibleRect, const LayoutRect& exposeRect, const ScrollAlignment& alignX, const ScrollAlignment& alignY);
@@ -886,8 +880,6 @@
     typedef unsigned UpdateLayerPositionsAfterScrollFlags;
     void updateLayerPositionsAfterScroll(RenderGeometryMap*, UpdateLayerPositionsAfterScrollFlags = NoFlag);
 
-    IntSize clampScrollOffset(const IntSize&) const;
-
     void setNextSibling(RenderLayer* next) { m_next = next; }
     void setPreviousSibling(RenderLayer* prev) { m_previous = prev; }
     void setParent(RenderLayer* parent);
@@ -977,12 +969,6 @@
 
     bool listBackgroundIsKnownToBeOpaqueInRect(const Vector<RenderLayer*>*, const LayoutRect&) const;
 
-    void computeScrollDimensions();
-    bool hasHorizontalOverflow() const;
-    bool hasVerticalOverflow() const;
-    bool hasScrollableHorizontalOverflow() const;
-    bool hasScrollableVerticalOverflow() const;
-
     bool shouldBeNormalFlowOnly() const;
     bool shouldBeNormalFlowOnlyIgnoringCompositedScrolling() const;
 
@@ -1022,12 +1008,8 @@
     IntPoint convertFromScrollbarToContainingView(const Scrollbar*, const IntPoint&) const;
     IntPoint convertFromContainingViewToScrollbar(const Scrollbar*, const IntPoint&) const;
     int scrollSize(ScrollbarOrientation) const;
-    void setScrollOffset(const IntPoint&);
-    IntPoint minimumScrollPosition() const;
-    IntPoint maximumScrollPosition() const;
     int visibleHeight() const;
     int visibleWidth() const;
-    IntSize contentsSize() const;
     IntSize overhangAmount() const;
     IntPoint lastKnownMousePosition() const;
     bool shouldSuspendScrollAnimations() const;
@@ -1120,7 +1102,6 @@
     // Keeps track of whether the layer is currently resizing, so events can cause resizing to start and stop.
     unsigned m_inResizeMode : 1;
 
-    unsigned m_scrollDimensionsDirty : 1;
     unsigned m_zOrderListsDirty : 1;
     unsigned m_normalFlowListDirty: 1;
     unsigned m_isNormalFlowOnly : 1;
@@ -1206,9 +1187,6 @@
     // The layer's width/height
     IntSize m_layerSize;
 
-    // The width/height of our scrolled area.
-    LayoutRect m_overflowRect;
-
     // For layers with overflow, we have a pair of scrollbars.
     RefPtr<Scrollbar> m_hBar;
     RefPtr<Scrollbar> m_vBar;
diff --git a/Source/core/rendering/RenderLayerScrollableArea.cpp b/Source/core/rendering/RenderLayerScrollableArea.cpp
index acc68cb..4cf3890 100644
--- a/Source/core/rendering/RenderLayerScrollableArea.cpp
+++ b/Source/core/rendering/RenderLayerScrollableArea.cpp
@@ -44,16 +44,22 @@
 #include "config.h"
 #include "core/rendering/RenderLayer.h"
 
+#include "core/editing/FrameSelection.h"
+#include "core/inspector/InspectorInstrumentation.h"
+#include "core/page/EventHandler.h"
 #include "core/page/Frame.h"
 #include "core/page/FrameView.h"
 #include "core/page/Page.h"
 #include "core/page/scrolling/ScrollingCoordinator.h"
 #include "core/platform/ScrollAnimator.h"
+#include "core/rendering/RenderLayerCompositor.h"
+#include "core/rendering/RenderView.h"
 
 namespace WebCore {
 
 RenderLayerScrollableArea::RenderLayerScrollableArea(RenderLayer* layer)
     : m_layer(layer)
+    , m_scrollDimensionsDirty(true)
 {
     ScrollableArea::setConstrainsScrollingToContentEdge(false);
 
@@ -185,9 +191,67 @@
     return m_layer->scrollSize(orientation);
 }
 
-void RenderLayerScrollableArea::setScrollOffset(const IntPoint& offset)
+void RenderLayerScrollableArea::setScrollOffset(const IntPoint& newScrollOffset)
 {
-    m_layer->setScrollOffset(offset);
+    if (!toRenderBox(renderer())->isMarquee()) {
+        // Ensure that the dimensions will be computed if they need to be (for overflow:hidden blocks).
+        if (m_scrollDimensionsDirty)
+            computeScrollDimensions();
+    }
+
+    if (scrollOffset() == toIntSize(newScrollOffset))
+        return;
+
+    setScrollOffset(toIntSize(newScrollOffset));
+
+    Frame* frame = renderer()->frame();
+    InspectorInstrumentation::willScrollLayer(renderer());
+
+    RenderView* view = renderer()->view();
+
+    // We should have a RenderView if we're trying to scroll.
+    ASSERT(view);
+
+    // Update the positions of our child layers (if needed as only fixed layers should be impacted by a scroll).
+    // We don't update compositing layers, because we need to do a deep update from the compositing ancestor.
+    bool inLayout = view ? view->frameView()->isInLayout() : false;
+    if (!inLayout) {
+        // If we're in the middle of layout, we'll just update layers once layout has finished.
+        m_layer->updateLayerPositionsAfterOverflowScroll();
+        if (view) {
+            // Update regions, scrolling may change the clip of a particular region.
+            view->frameView()->updateAnnotatedRegions();
+            view->updateWidgetPositions();
+        }
+
+        m_layer->updateCompositingLayersAfterScroll();
+    }
+
+    RenderLayerModelObject* repaintContainer = renderer()->containerForRepaint();
+    if (frame) {
+        // The caret rect needs to be invalidated after scrolling
+        frame->selection().setCaretRectNeedsUpdate();
+
+        FloatQuad quadForFakeMouseMoveEvent = FloatQuad(m_layer->m_repaintRect);
+        if (repaintContainer)
+            quadForFakeMouseMoveEvent = repaintContainer->localToAbsoluteQuad(quadForFakeMouseMoveEvent);
+        frame->eventHandler()->dispatchFakeMouseMoveEventSoonInQuad(quadForFakeMouseMoveEvent);
+    }
+
+    bool requiresRepaint = true;
+
+    if (m_layer->compositor()->inCompositingMode() && m_layer->usesCompositedScrolling())
+        requiresRepaint = false;
+
+    // Just schedule a full repaint of our object.
+    if (view && requiresRepaint)
+        renderer()->repaintUsingContainer(repaintContainer, pixelSnappedIntRect(m_layer->m_repaintRect));
+
+    // Schedule the scroll DOM event.
+    if (renderer()->node())
+        renderer()->node()->document().eventQueue()->enqueueOrDispatchScrollEvent(renderer()->node(), DocumentEventQueue::ScrollEventElementTarget);
+
+    InspectorInstrumentation::didScrollLayer(renderer());
 }
 
 IntPoint RenderLayerScrollableArea::scrollPosition() const
@@ -197,12 +261,17 @@
 
 IntPoint RenderLayerScrollableArea::minimumScrollPosition() const
 {
-    return m_layer->minimumScrollPosition();
+    return -scrollOrigin();
 }
 
 IntPoint RenderLayerScrollableArea::maximumScrollPosition() const
 {
-    return m_layer->maximumScrollPosition();
+    RenderBox* box = toRenderBox(renderer());
+
+    if (!box->hasOverflowClip())
+        return -scrollOrigin();
+
+    return -scrollOrigin() + enclosingIntRect(m_overflowRect).size() - enclosingIntRect(box->clientBoxRect()).size();
 }
 
 IntRect RenderLayerScrollableArea::visibleContentRect(VisibleContentRectIncludesScrollbars scrollbarInclusion) const
@@ -230,7 +299,7 @@
 
 IntSize RenderLayerScrollableArea::contentsSize() const
 {
-    return m_layer->contentsSize();
+    return IntSize(scrollWidth(), scrollHeight());
 }
 
 IntSize RenderLayerScrollableArea::overhangAmount() const
@@ -275,4 +344,102 @@
     return m_layer->renderer();
 }
 
+int RenderLayerScrollableArea::scrollWidth() const
+{
+    RenderBox* box = toRenderBox(renderer());
+    if (m_scrollDimensionsDirty)
+        const_cast<RenderLayerScrollableArea*>(this)->computeScrollDimensions();
+    return snapSizeToPixel(m_overflowRect.width(), box->clientLeft() + box->x());
+}
+
+int RenderLayerScrollableArea::scrollHeight() const
+{
+    RenderBox* box = toRenderBox(renderer());
+    if (m_scrollDimensionsDirty)
+        const_cast<RenderLayerScrollableArea*>(this)->computeScrollDimensions();
+    return snapSizeToPixel(m_overflowRect.height(), box->clientTop() + box->y());
+}
+
+void RenderLayerScrollableArea::computeScrollDimensions()
+{
+    RenderBox* box = toRenderBox(renderer());
+
+    m_scrollDimensionsDirty = false;
+
+    m_overflowRect = box->layoutOverflowRect();
+    box->flipForWritingMode(m_overflowRect);
+
+    int scrollableLeftOverflow = m_overflowRect.x() - box->borderLeft();
+    int scrollableTopOverflow = m_overflowRect.y() - box->borderTop();
+    setScrollOrigin(IntPoint(-scrollableLeftOverflow, -scrollableTopOverflow));
+}
+
+void RenderLayerScrollableArea::scrollToOffset(const IntSize& scrollOffset, ScrollOffsetClamping clamp)
+{
+    IntSize newScrollOffset = clamp == ScrollOffsetClamped ? clampScrollOffset(scrollOffset) : scrollOffset;
+    if (newScrollOffset != adjustedScrollOffset())
+        scrollToOffsetWithoutAnimation(-scrollOrigin() + newScrollOffset);
+}
+
+void RenderLayerScrollableArea::updateAfterLayout()
+{
+    m_scrollDimensionsDirty = true;
+    IntSize originalScrollOffset = adjustedScrollOffset();
+
+    computeScrollDimensions();
+
+    if (!toRenderBox(renderer())->isMarquee()) {
+        // Layout may cause us to be at an invalid scroll position. In this case we need
+        // to pull our scroll offsets back to the max (or push them up to the min).
+        IntSize clampedScrollOffset = clampScrollOffset(adjustedScrollOffset());
+        if (clampedScrollOffset != adjustedScrollOffset())
+            scrollToOffset(clampedScrollOffset);
+    }
+
+    if (originalScrollOffset != adjustedScrollOffset())
+        scrollToOffsetWithoutAnimation(-scrollOrigin() + adjustedScrollOffset());
+}
+
+bool RenderLayerScrollableArea::hasHorizontalOverflow() const
+{
+    ASSERT(!m_scrollDimensionsDirty);
+
+    return scrollWidth() > toRenderBox(renderer())->pixelSnappedClientWidth();
+}
+
+bool RenderLayerScrollableArea::hasVerticalOverflow() const
+{
+    ASSERT(!m_scrollDimensionsDirty);
+
+    return scrollHeight() > toRenderBox(renderer())->pixelSnappedClientHeight();
+}
+
+bool RenderLayerScrollableArea::hasScrollableHorizontalOverflow() const
+{
+    return hasHorizontalOverflow() && toRenderBox(renderer())->scrollsOverflowX();
+}
+
+bool RenderLayerScrollableArea::hasScrollableVerticalOverflow() const
+{
+    return hasVerticalOverflow() && toRenderBox(renderer())->scrollsOverflowY();
+}
+
+void RenderLayerScrollableArea::updateAfterStyleChange(const RenderStyle*)
+{
+    if (!m_scrollDimensionsDirty)
+        m_layer->updateScrollableAreaSet(hasScrollableHorizontalOverflow() || hasScrollableVerticalOverflow());
+}
+
+IntSize RenderLayerScrollableArea::clampScrollOffset(const IntSize& scrollOffset) const
+{
+    RenderBox* box = toRenderBox(renderer());
+
+    int maxX = scrollWidth() - box->pixelSnappedClientWidth();
+    int maxY = scrollHeight() - box->pixelSnappedClientHeight();
+
+    int x = std::max(std::min(scrollOffset.width(), maxX), 0);
+    int y = std::max(std::min(scrollOffset.height(), maxY), 0);
+    return IntSize(x, y);
+}
+
 } // Namespace WebCore
diff --git a/Source/core/rendering/RenderLayerScrollableArea.h b/Source/core/rendering/RenderLayerScrollableArea.h
index 215ef83..f000407 100644
--- a/Source/core/rendering/RenderLayerScrollableArea.h
+++ b/Source/core/rendering/RenderLayerScrollableArea.h
@@ -53,6 +53,11 @@
     ResizerForTouch
 };
 
+enum ScrollOffsetClamping {
+    ScrollOffsetUnclamped,
+    ScrollOffsetClamped
+};
+
 class RenderLayer;
 class RenderLayerModelObject;
 
@@ -105,13 +110,39 @@
 
     IntSize scrollOffset() const { return m_scrollOffset; }
 
+    // FIXME: We shouldn't allow access to m_overflowRect outside this class.
+    LayoutRect overflowRect() const { return m_overflowRect; }
+
+    void scrollToOffset(const IntSize& scrollOffset, ScrollOffsetClamping = ScrollOffsetUnclamped);
+
+    void updateAfterLayout();
+    void updateAfterStyleChange(const RenderStyle*);
+
 private:
+    bool hasHorizontalOverflow() const;
+    bool hasVerticalOverflow() const;
+    bool hasScrollableHorizontalOverflow() const;
+    bool hasScrollableVerticalOverflow() const;
+
+    int scrollWidth() const;
+    int scrollHeight() const;
+
+    void computeScrollDimensions();
+
+    IntSize clampScrollOffset(const IntSize&) const;
+    IntSize adjustedScrollOffset() const { return IntSize(scrollXOffset(), scrollYOffset()); }
+
     void setScrollOffset(const IntSize& scrollOffset) { m_scrollOffset = scrollOffset; }
 
     RenderLayerModelObject* renderer() const;
 
     RenderLayer* m_layer;
 
+    unsigned m_scrollDimensionsDirty : 1;
+
+    // The width/height of our scrolled area.
+    LayoutRect m_overflowRect;
+
     // This is the (scroll) offset from scrollOrigin().
     IntSize m_scrollOffset;
 };