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;
};