Changeset 120444 in webkit


Ignore:
Timestamp:
Jun 15, 2012 5:45:58 AM (12 years ago)
Author:
commit-queue@webkit.org
Message:

[chromium] Allow scrolling non-root layers in the compositor thread
https://bugs.webkit.org/show_bug.cgi?id=73350

Patch by Sami Kyostila <skyostil@chromium.org> on 2012-06-15
Reviewed by James Robinson.

Source/WebCore:

This patch enables scrolling child layers in the compositor thread.
Scroll deltas are accumulated for each scrolled CCLayerImpl and
synchronized to the main thread.

If a layer has no room to scroll in a given direction, one of its
ancestor layers is scrolled instead if possible.

Layer hit testing code by Shawn Singh.

Added new unit tests to verify layer scrolling behavior:

CCLayerTreeHostCommonTest.verifySubtreeSearch
CCLayerTreeHostImplTest.clearRootRenderSurfaceAndScroll
CCLayerTreeHostImplTest.inhibitScrollAndPageScaleUpdatesWhileAnimatingPageScale
CCLayerTreeHostImplTest.inhibitScrollAndPageScaleUpdatesWhilePinchZooming
CCLayerTreeHostImplTest.replaceTreeWhileScrolling
CCLayerTreeHostImplTest.scrollBeforeRedraw
CCLayerTreeHostImplTest.scrollBlockedByContentLayer
CCLayerTreeHostImplTest.scrollChildAndChangePageScaleOnMainThread
CCLayerTreeHostImplTest.scrollChildBeyondLimit
CCLayerTreeHostImplTest.scrollChildCallsCommitAndRedraw
CCLayerTreeHostImplTest.scrollEventBubbling
CCLayerTreeHostImplTest.scrollMissesBackfacingChild
CCLayerTreeHostImplTest.scrollMissesChild
CCLayerTreeHostImplTest.scrollNonCompositedRoot
CCLayerTreeHostImplTest.scrollRootAndChangePageScaleOnImplThread
CCLayerTreeHostImplTest.scrollRootAndChangePageScaleOnMainThread
CCLayerTreeHostImplTest.scrollRootIgnored
CCLayerTreeHostImplTest.scrollWithoutRootLayer
CCLayerTreeHostTestScrollChildLayer
WebCompositorInputHandlerImplTest.gestureScrollOnMainThread

  • platform/graphics/chromium/LayerChromium.cpp:

(WebCore::LayerChromium::LayerChromium):
(WebCore::LayerChromium::setMaxScrollPosition):
(WebCore):
(WebCore::LayerChromium::scrollBy):
(WebCore::LayerChromium::pushPropertiesTo):

  • platform/graphics/chromium/LayerChromium.h:

(WebCore):
(LayerChromiumScrollDelegate):
(WebCore::LayerChromiumScrollDelegate::~LayerChromiumScrollDelegate):
(LayerChromium):
(WebCore::LayerChromium::maxScrollPosition):
(WebCore::LayerChromium::scrollable):
(WebCore::LayerChromium::setLayerScrollDelegate):

  • platform/graphics/chromium/cc/CCInputHandler.h:
  • platform/graphics/chromium/cc/CCLayerImpl.cpp:

(WebCore::CCLayerImpl::tryScroll):
(WebCore):
(WebCore::sortLayers):

  • platform/graphics/chromium/cc/CCLayerImpl.h:

(CCLayerImpl):

  • platform/graphics/chromium/cc/CCLayerTreeHost.cpp:

(WebCore::CCLayerTreeHost::finishCommitOnImplThread):
(WebCore::findFirstScrollableLayer):
(WebCore):
(WebCore::CCLayerTreeHost::applyScrollAndScale):

  • platform/graphics/chromium/cc/CCLayerTreeHostCommon.h:

(CCLayerTreeHostCommon):
(WebCore):
(WebCore::CCLayerTreeHostCommon::findLayerInSubtree):

  • platform/graphics/chromium/cc/CCLayerTreeHostImpl.cpp:

(WebCore::CCLayerTreeHostImpl::CCLayerTreeHostImpl):
(WebCore::CCLayerTreeHostImpl::~CCLayerTreeHostImpl):
(WebCore::CCLayerTreeHostImpl::startPageScaleAnimation):
(WebCore::CCLayerTreeHostImpl::calculateRenderSurfaceLayerList):
(WebCore::CCLayerTreeHostImpl::calculateRenderPasses):
(WebCore::CCLayerTreeHostImpl::contentSize):
(WebCore::CCLayerTreeHostImpl::prepareToDraw):
(WebCore::CCLayerTreeHostImpl::drawLayers):
(WebCore::findRootScrollLayer):
(WebCore):
(WebCore::findScrollLayerForContentLayer):
(WebCore::CCLayerTreeHostImpl::setRootLayer):
(WebCore::CCLayerTreeHostImpl::detachLayerTree):
(WebCore::adjustScrollsForPageScaleChange):
(WebCore::applyPageScaleDeltaToScrollLayers):
(WebCore::CCLayerTreeHostImpl::setPageScaleFactorAndLimits):
(WebCore::CCLayerTreeHostImpl::setPageScaleDelta):
(WebCore::CCLayerTreeHostImpl::updateMaxScrollPosition):
(WebCore::CCLayerTreeHostImpl::ensureRenderSurfaceLayerList):
(WebCore::CCLayerTreeHostImpl::clearCurrentlyScrollingLayer):
(WebCore::CCLayerTreeHostImpl::scrollBegin):
(WebCore::CCLayerTreeHostImpl::scrollBy):
(WebCore::CCLayerTreeHostImpl::scrollEnd):
(WebCore::CCLayerTreeHostImpl::pinchGestureUpdate):
(WebCore::CCLayerTreeHostImpl::computePinchZoomDeltas):
(WebCore::CCLayerTreeHostImpl::makeScrollAndScaleSet):
(WebCore::collectScrollDeltas):
(WebCore::CCLayerTreeHostImpl::processScrollDeltas):
(WebCore::CCLayerTreeHostImpl::animatePageScale):
(WebCore::CCLayerTreeHostImpl::animateLayers):
(WebCore::CCLayerTreeHostImpl::clearRenderSurfaces):

  • platform/graphics/chromium/cc/CCLayerTreeHostImpl.h:

(FrameData):
(CCLayerTreeHostImpl):
(WebCore::CCLayerTreeHostImpl::rootScrollLayer):

Source/WebKit/chromium:

New unit tests to verify layer scrolling behavior and the associated
utility functions.

  • src/WebCompositorInputHandlerImpl.cpp:

(WebKit::WebCompositorInputHandlerImpl::handleInputEventInternal):
(WebKit::WebCompositorInputHandlerImpl::handleGestureFling):

  • tests/CCLayerTreeHostCommonTest.cpp:
  • tests/CCLayerTreeHostImplTest.cpp:
  • tests/CCLayerTreeHostTest.cpp:

(WTF::MockContentLayerDelegate::paintContents):
(CCLayerTreeHostTestScrollChildLayer):
(WTF::CCLayerTreeHostTestScrollChildLayer::CCLayerTreeHostTestScrollChildLayer):
(WTF::CCLayerTreeHostTestScrollChildLayer::beginTest):
(WTF::CCLayerTreeHostTestScrollChildLayer::applyScrollAndScale):
(WTF::CCLayerTreeHostTestScrollChildLayer::beginCommitOnCCThread):
(WTF::CCLayerTreeHostTestScrollChildLayer::drawLayersOnCCThread):
(WTF::CCLayerTreeHostTestScrollChildLayer::afterTest):
(WTF):
(WTF::TEST_F):

  • tests/WebCompositorInputHandlerImplTest.cpp:

(WebKit::TEST_F):

Location:
trunk/Source
Files:
16 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r120433 r120444  
     12012-06-15  Sami Kyostila  <skyostil@chromium.org>
     2
     3        [chromium] Allow scrolling non-root layers in the compositor thread
     4        https://bugs.webkit.org/show_bug.cgi?id=73350
     5
     6        Reviewed by James Robinson.
     7
     8        This patch enables scrolling child layers in the compositor thread.
     9        Scroll deltas are accumulated for each scrolled CCLayerImpl and
     10        synchronized to the main thread.
     11
     12        If a layer has no room to scroll in a given direction, one of its
     13        ancestor layers is scrolled instead if possible.
     14
     15        Layer hit testing code by Shawn Singh.
     16
     17        Added new unit tests to verify layer scrolling behavior:
     18
     19            CCLayerTreeHostCommonTest.verifySubtreeSearch
     20            CCLayerTreeHostImplTest.clearRootRenderSurfaceAndScroll
     21            CCLayerTreeHostImplTest.inhibitScrollAndPageScaleUpdatesWhileAnimatingPageScale
     22            CCLayerTreeHostImplTest.inhibitScrollAndPageScaleUpdatesWhilePinchZooming
     23            CCLayerTreeHostImplTest.replaceTreeWhileScrolling
     24            CCLayerTreeHostImplTest.scrollBeforeRedraw
     25            CCLayerTreeHostImplTest.scrollBlockedByContentLayer
     26            CCLayerTreeHostImplTest.scrollChildAndChangePageScaleOnMainThread
     27            CCLayerTreeHostImplTest.scrollChildBeyondLimit
     28            CCLayerTreeHostImplTest.scrollChildCallsCommitAndRedraw
     29            CCLayerTreeHostImplTest.scrollEventBubbling
     30            CCLayerTreeHostImplTest.scrollMissesBackfacingChild
     31            CCLayerTreeHostImplTest.scrollMissesChild
     32            CCLayerTreeHostImplTest.scrollNonCompositedRoot
     33            CCLayerTreeHostImplTest.scrollRootAndChangePageScaleOnImplThread
     34            CCLayerTreeHostImplTest.scrollRootAndChangePageScaleOnMainThread
     35            CCLayerTreeHostImplTest.scrollRootIgnored
     36            CCLayerTreeHostImplTest.scrollWithoutRootLayer
     37            CCLayerTreeHostTestScrollChildLayer
     38            WebCompositorInputHandlerImplTest.gestureScrollOnMainThread
     39
     40        * platform/graphics/chromium/LayerChromium.cpp:
     41        (WebCore::LayerChromium::LayerChromium):
     42        (WebCore::LayerChromium::setMaxScrollPosition):
     43        (WebCore):
     44        (WebCore::LayerChromium::scrollBy):
     45        (WebCore::LayerChromium::pushPropertiesTo):
     46        * platform/graphics/chromium/LayerChromium.h:
     47        (WebCore):
     48        (LayerChromiumScrollDelegate):
     49        (WebCore::LayerChromiumScrollDelegate::~LayerChromiumScrollDelegate):
     50        (LayerChromium):
     51        (WebCore::LayerChromium::maxScrollPosition):
     52        (WebCore::LayerChromium::scrollable):
     53        (WebCore::LayerChromium::setLayerScrollDelegate):
     54        * platform/graphics/chromium/cc/CCInputHandler.h:
     55        * platform/graphics/chromium/cc/CCLayerImpl.cpp:
     56        (WebCore::CCLayerImpl::tryScroll):
     57        (WebCore):
     58        (WebCore::sortLayers):
     59        * platform/graphics/chromium/cc/CCLayerImpl.h:
     60        (CCLayerImpl):
     61        * platform/graphics/chromium/cc/CCLayerTreeHost.cpp:
     62        (WebCore::CCLayerTreeHost::finishCommitOnImplThread):
     63        (WebCore::findFirstScrollableLayer):
     64        (WebCore):
     65        (WebCore::CCLayerTreeHost::applyScrollAndScale):
     66        * platform/graphics/chromium/cc/CCLayerTreeHostCommon.h:
     67        (CCLayerTreeHostCommon):
     68        (WebCore):
     69        (WebCore::CCLayerTreeHostCommon::findLayerInSubtree):
     70        * platform/graphics/chromium/cc/CCLayerTreeHostImpl.cpp:
     71        (WebCore::CCLayerTreeHostImpl::CCLayerTreeHostImpl):
     72        (WebCore::CCLayerTreeHostImpl::~CCLayerTreeHostImpl):
     73        (WebCore::CCLayerTreeHostImpl::startPageScaleAnimation):
     74        (WebCore::CCLayerTreeHostImpl::calculateRenderSurfaceLayerList):
     75        (WebCore::CCLayerTreeHostImpl::calculateRenderPasses):
     76        (WebCore::CCLayerTreeHostImpl::contentSize):
     77        (WebCore::CCLayerTreeHostImpl::prepareToDraw):
     78        (WebCore::CCLayerTreeHostImpl::drawLayers):
     79        (WebCore::findRootScrollLayer):
     80        (WebCore):
     81        (WebCore::findScrollLayerForContentLayer):
     82        (WebCore::CCLayerTreeHostImpl::setRootLayer):
     83        (WebCore::CCLayerTreeHostImpl::detachLayerTree):
     84        (WebCore::adjustScrollsForPageScaleChange):
     85        (WebCore::applyPageScaleDeltaToScrollLayers):
     86        (WebCore::CCLayerTreeHostImpl::setPageScaleFactorAndLimits):
     87        (WebCore::CCLayerTreeHostImpl::setPageScaleDelta):
     88        (WebCore::CCLayerTreeHostImpl::updateMaxScrollPosition):
     89        (WebCore::CCLayerTreeHostImpl::ensureRenderSurfaceLayerList):
     90        (WebCore::CCLayerTreeHostImpl::clearCurrentlyScrollingLayer):
     91        (WebCore::CCLayerTreeHostImpl::scrollBegin):
     92        (WebCore::CCLayerTreeHostImpl::scrollBy):
     93        (WebCore::CCLayerTreeHostImpl::scrollEnd):
     94        (WebCore::CCLayerTreeHostImpl::pinchGestureUpdate):
     95        (WebCore::CCLayerTreeHostImpl::computePinchZoomDeltas):
     96        (WebCore::CCLayerTreeHostImpl::makeScrollAndScaleSet):
     97        (WebCore::collectScrollDeltas):
     98        (WebCore::CCLayerTreeHostImpl::processScrollDeltas):
     99        (WebCore::CCLayerTreeHostImpl::animatePageScale):
     100        (WebCore::CCLayerTreeHostImpl::animateLayers):
     101        (WebCore::CCLayerTreeHostImpl::clearRenderSurfaces):
     102        * platform/graphics/chromium/cc/CCLayerTreeHostImpl.h:
     103        (FrameData):
     104        (CCLayerTreeHostImpl):
     105        (WebCore::CCLayerTreeHostImpl::rootScrollLayer):
     106
    11072012-06-15  Jian Li  <jianli@chromium.org>
    2108
  • trunk/Source/WebCore/platform/graphics/chromium/LayerChromium.cpp

    r120360 r120444  
    9595    , m_contentsScale(1.0)
    9696    , m_layerAnimationDelegate(0)
     97    , m_layerScrollDelegate(0)
    9798{
    9899}
     
    396397}
    397398
     399void LayerChromium::setMaxScrollPosition(const IntSize& maxScrollPosition)
     400{
     401    if (m_maxScrollPosition == maxScrollPosition)
     402        return;
     403    m_maxScrollPosition = maxScrollPosition;
     404    setNeedsCommit();
     405}
     406
    398407void LayerChromium::setScrollable(bool scrollable)
    399408{
     
    427436    m_nonFastScrollableRegionChanged = true;
    428437    setNeedsCommit();
     438}
     439
     440void LayerChromium::scrollBy(const IntSize& scrollDelta)
     441{
     442    setScrollPosition(scrollPosition() + scrollDelta);
     443    if (m_layerScrollDelegate)
     444        m_layerScrollDelegate->didScroll(scrollDelta);
    429445}
    430446
     
    546562    layer->setPreserves3D(preserves3D());
    547563    layer->setScrollPosition(m_scrollPosition);
     564    layer->setMaxScrollPosition(m_maxScrollPosition);
    548565    layer->setSublayerTransform(m_sublayerTransform);
    549566    if (!transformIsAnimating())
  • trunk/Source/WebCore/platform/graphics/chromium/LayerChromium.h

    r120340 r120444  
    6666class ScrollbarLayerChromium;
    6767
     68// Delegate for handling scroll input for a LayerChromium.
     69class LayerChromiumScrollDelegate {
     70public:
     71    virtual void didScroll(const IntSize&) = 0;
     72
     73protected:
     74    virtual ~LayerChromiumScrollDelegate() { }
     75};
     76
    6877// Base class for composited layers. Special layer types are derived from
    6978// this class.
     
    152161    const IntPoint& scrollPosition() const { return m_scrollPosition; }
    153162
     163    void setMaxScrollPosition(const IntSize&);
     164    const IntSize& maxScrollPosition() const { return m_maxScrollPosition; }
     165
    154166    void setScrollable(bool);
     167    bool scrollable() const { return m_scrollable; }
    155168    void setShouldScrollOnMainThread(bool);
    156169    void setHaveWheelEventHandlers(bool);
     
    158171    void setNonFastScrollableRegion(const Region&);
    159172    void setNonFastScrollableRegionChanged() { m_nonFastScrollableRegionChanged = true; }
     173    void setLayerScrollDelegate(LayerChromiumScrollDelegate* layerScrollDelegate) { m_layerScrollDelegate = layerScrollDelegate; }
     174    void scrollBy(const IntSize&);
    160175
    161176    void setDrawCheckerboardForMissingTiles(bool);
     
    327342    IntRect m_scissorRect;
    328343    IntPoint m_scrollPosition;
     344    IntSize m_maxScrollPosition;
    329345    bool m_scrollable;
    330346    bool m_shouldScrollOnMainThread;
     
    379395
    380396    CCLayerAnimationDelegate* m_layerAnimationDelegate;
     397    LayerChromiumScrollDelegate* m_layerScrollDelegate;
    381398};
    382399
  • trunk/Source/WebCore/platform/graphics/chromium/cc/CCInputHandler.h

    r111270 r120444  
    4646    WTF_MAKE_NONCOPYABLE(CCInputHandlerClient);
    4747public:
    48     enum ScrollStatus { ScrollFailed, ScrollStarted, ScrollIgnored };
     48    enum ScrollStatus { ScrollOnMainThread, ScrollStarted, ScrollIgnored };
    4949    enum ScrollInputType { Gesture, Wheel };
    5050
    5151    // Attempt to start scrolling a layer at a given point in window
    5252    // coordinates. Returns ScrollStarted if the layer at the coordinates can
    53     // be scrolled, ScrollFailed if the scroll event should instead be
     53    // be scrolled, ScrollOnMainThread if the scroll event should instead be
    5454    // delegated to the main thread, or ScrollIgnored if there is nothing to
    5555    // be scrolled at the given coordinates.
  • trunk/Source/WebCore/platform/graphics/chromium/cc/CCLayerImpl.cpp

    r120340 r120444  
    3434#include "cc/CCDebugBorderDrawQuad.h"
    3535#include "cc/CCLayerSorter.h"
     36#include "cc/CCMathUtil.h"
    3637#include "cc/CCQuadCuller.h"
    3738#include "cc/CCSolidColorDrawQuad.h"
     
    187188}
    188189
     190CCInputHandlerClient::ScrollStatus CCLayerImpl::tryScroll(const IntPoint& viewportPoint, CCInputHandlerClient::ScrollInputType type) const
     191{
     192    if (shouldScrollOnMainThread()) {
     193        TRACE_EVENT0("cc", "CCLayerImpl::tryScroll: Failed shouldScrollOnMainThread");
     194        return CCInputHandlerClient::ScrollOnMainThread;
     195    }
     196
     197    if (!screenSpaceTransform().isInvertible()) {
     198        TRACE_EVENT0("cc", "CCLayerImpl::tryScroll: Ignored nonInvertibleTransform");
     199        return CCInputHandlerClient::ScrollIgnored;
     200    }
     201
     202    if (!nonFastScrollableRegion().isEmpty()) {
     203        bool clipped = false;
     204        FloatPoint hitTestPointInLocalSpace = CCMathUtil::projectPoint(screenSpaceTransform().inverse(), FloatPoint(viewportPoint), clipped);
     205        if (!clipped && nonFastScrollableRegion().contains(flooredIntPoint(hitTestPointInLocalSpace))) {
     206            TRACE_EVENT0("cc", "CCLayerImpl::tryScroll: Failed nonFastScrollableRegion");
     207            return CCInputHandlerClient::ScrollOnMainThread;
     208        }
     209    }
     210
     211    if (type == CCInputHandlerClient::Wheel && haveWheelEventHandlers()) {
     212        TRACE_EVENT0("cc", "CCLayerImpl::tryScroll: Failed wheelEventHandlers");
     213        return CCInputHandlerClient::ScrollOnMainThread;
     214    }
     215
     216    if (!scrollable()) {
     217        TRACE_EVENT0("cc", "CCLayerImpl::tryScroll: Ignored not scrollable");
     218        return CCInputHandlerClient::ScrollIgnored;
     219    }
     220
     221    return CCInputHandlerClient::ScrollStarted;
     222}
     223
    189224const IntRect CCLayerImpl::getDrawRect() const
    190225{
     
    239274void sortLayers(Vector<CCLayerImpl*>::iterator first, Vector<CCLayerImpl*>::iterator end, CCLayerSorter* layerSorter)
    240275{
    241     TRACE_EVENT("LayerRendererChromium::sortLayers", 0, 0);
     276    TRACE_EVENT0("cc", "LayerRendererChromium::sortLayers");
    242277    layerSorter->sort(first, end);
    243278}
  • trunk/Source/WebCore/platform/graphics/chromium/cc/CCLayerImpl.h

    r120340 r120444  
    3232#include "Region.h"
    3333#include "TextStream.h"
     34#include "cc/CCInputHandler.h"
    3435#include "cc/CCLayerAnimationController.h"
    3536#include "cc/CCRenderSurface.h"
     
    218219    void setDrawCheckerboardForMissingTiles(bool checkerboard) { m_drawCheckerboardForMissingTiles = checkerboard; }
    219220    bool drawCheckerboardForMissingTiles() const { return m_drawCheckerboardForMissingTiles; }
     221
     222    CCInputHandlerClient::ScrollStatus tryScroll(const IntPoint& viewportPoint, CCInputHandlerClient::ScrollInputType) const;
    220223
    221224    const IntRect& visibleLayerRect() const { return m_visibleLayerRect; }
  • trunk/Source/WebCore/platform/graphics/chromium/cc/CCLayerTreeHost.cpp

    r120360 r120444  
    243243    ASSERT(CCProxy::isImplThread());
    244244
    245     hostImpl->setRootLayer(TreeSynchronizer::synchronizeTrees(rootLayer(), hostImpl->releaseRootLayer(), hostImpl));
     245    hostImpl->setRootLayer(TreeSynchronizer::synchronizeTrees(rootLayer(), hostImpl->detachLayerTree(), hostImpl));
    246246
    247247    // We may have added an animation during the tree sync. This will cause both layer tree hosts
     
    619619}
    620620
     621static LayerChromium* findFirstScrollableLayer(LayerChromium* layer)
     622{
     623    if (!layer)
     624        return 0;
     625
     626    if (layer->scrollable())
     627        return layer;
     628
     629    for (size_t i = 0; i < layer->children().size(); ++i) {
     630        LayerChromium* found = findFirstScrollableLayer(layer->children()[i].get());
     631        if (found)
     632            return found;
     633    }
     634
     635    return 0;
     636}
     637
    621638void CCLayerTreeHost::applyScrollAndScale(const CCScrollAndScaleSet& info)
    622639{
    623     // FIXME: pushing scroll offsets to non-root layers not implemented
    624     if (!info.scrolls.size())
    625         return;
    626 
    627     ASSERT(info.scrolls.size() == 1);
    628     IntSize scrollDelta = info.scrolls[0].scrollDelta;
    629     m_client->applyScrollAndScale(scrollDelta, info.pageScaleDelta);
     640    if (!m_rootLayer)
     641        return;
     642
     643    LayerChromium* rootScrollLayer = findFirstScrollableLayer(m_rootLayer.get());
     644    IntSize rootScrollDelta;
     645
     646    for (size_t i = 0; i < info.scrolls.size(); ++i) {
     647        LayerChromium* layer = CCLayerTreeHostCommon::findLayerInSubtree(m_rootLayer.get(), info.scrolls[i].layerId);
     648        if (!layer)
     649            continue;
     650        if (layer == rootScrollLayer)
     651            rootScrollDelta += info.scrolls[i].scrollDelta;
     652        else
     653            layer->scrollBy(info.scrolls[i].scrollDelta);
     654    }
     655    if (!rootScrollDelta.isZero() || info.pageScaleDelta != 1)
     656        m_client->applyScrollAndScale(rootScrollDelta, info.pageScaleDelta);
    630657}
    631658
  • trunk/Source/WebCore/platform/graphics/chromium/cc/CCLayerTreeHostCommon.h

    r120261 r120444  
    5353    template<typename LayerType> static bool renderSurfaceContributesToTarget(LayerType*, int targetSurfaceLayerID);
    5454
     55    // Returns a layer with the given id if one exists in the subtree starting
     56    // from the given root layer (including mask and replica layers).
     57    template<typename LayerType> static LayerType* findLayerInSubtree(LayerType* rootLayer, int layerId);
     58
    5559    struct ScrollUpdateInfo {
    5660        int layerId;
     
    7882}
    7983
     84template<typename LayerType>
     85LayerType* CCLayerTreeHostCommon::findLayerInSubtree(LayerType* rootLayer, int layerId)
     86{
     87    if (rootLayer->id() == layerId)
     88        return rootLayer;
     89
     90    if (rootLayer->maskLayer() && rootLayer->maskLayer()->id() == layerId)
     91        return rootLayer->maskLayer();
     92
     93    if (rootLayer->replicaLayer() && rootLayer->replicaLayer()->id() == layerId)
     94        return rootLayer->replicaLayer();
     95
     96    for (size_t i = 0; i < rootLayer->children().size(); ++i) {
     97        if (LayerType* found = findLayerInSubtree(rootLayer->children()[i].get(), layerId))
     98            return found;
     99    }
     100    return 0;
     101}
     102
    80103} // namespace WebCore
    81104
  • trunk/Source/WebCore/platform/graphics/chromium/cc/CCLayerTreeHostImpl.cpp

    r120360 r120444  
    116116    , m_sourceFrameNumber(-1)
    117117    , m_frameNumber(0)
    118     , m_scrollLayerImpl(0)
     118    , m_rootScrollLayerImpl(0)
     119    , m_currentlyScrollingLayerImpl(0)
     120    , m_scrollingLayerIdFromPreviousTree(-1)
    119121    , m_settings(settings)
    120122    , m_visible(true)
     
    139141{
    140142    ASSERT(CCProxy::isImplThread());
    141     TRACE_EVENT("CCLayerTreeHostImpl::~CCLayerTreeHostImpl()", this, 0);
     143    TRACE_EVENT0("cc", "CCLayerTreeHostImpl::~CCLayerTreeHostImpl()");
    142144
    143145    if (m_rootLayerImpl)
     
    183185void CCLayerTreeHostImpl::startPageScaleAnimation(const IntSize& targetPosition, bool anchorPoint, float pageScale, double startTime, double duration)
    184186{
    185     if (!m_scrollLayerImpl)
    186         return;
    187 
    188     IntSize scrollTotal = flooredIntSize(m_scrollLayerImpl->scrollPosition() + m_scrollLayerImpl->scrollDelta());
     187    if (!m_rootScrollLayerImpl)
     188        return;
     189
     190    IntSize scrollTotal = flooredIntSize(m_rootScrollLayerImpl->scrollPosition() + m_rootScrollLayerImpl->scrollDelta());
    189191    scrollTotal.scale(m_pageScaleDelta);
    190192    float scaleTotal = m_pageScale * m_pageScaleDelta;
     
    236238{
    237239    ASSERT(renderSurfaceLayerList.isEmpty());
     240    ASSERT(m_rootLayerImpl);
    238241
    239242    renderSurfaceLayerList.append(m_rootLayerImpl.get());
     
    247250
    248251    {
    249         TRACE_EVENT("CCLayerTreeHostImpl::calcDrawEtc", this, 0);
     252        TRACE_EVENT0("cc", "CCLayerTreeHostImpl::calcDrawEtc");
    250253        WebTransformationMatrix identityMatrix;
    251254        WebTransformationMatrix deviceScaleTransform;
     
    270273    calculateRenderSurfaceLayerList(renderSurfaceLayerList);
    271274
    272     TRACE_EVENT1("webkit", "CCLayerTreeHostImpl::calculateRenderPasses", "renderSurfaceLayerList.size()", static_cast<long long unsigned>(renderSurfaceLayerList.size()));
     275    TRACE_EVENT1("cc", "CCLayerTreeHostImpl::calculateRenderPasses", "renderSurfaceLayerList.size()", static_cast<long long unsigned>(renderSurfaceLayerList.size()));
    273276
    274277    m_rootLayerImpl->setScissorRect(enclosingIntRect(m_rootScissorRect));
     
    375378    // TODO(aelias): Hardcoding the first child here is weird. Think of
    376379    // a cleaner way to get the contentBounds on the Impl side.
    377     if (!m_scrollLayerImpl || m_scrollLayerImpl->children().isEmpty())
     380    if (!m_rootScrollLayerImpl || m_rootScrollLayerImpl->children().isEmpty())
    378381        return IntSize();
    379     return m_scrollLayerImpl->children()[0]->contentBounds();
     382    return m_rootScrollLayerImpl->children()[0]->contentBounds();
    380383}
    381384
    382385bool CCLayerTreeHostImpl::prepareToDraw(FrameData& frame)
    383386{
    384     TRACE_EVENT("CCLayerTreeHostImpl::prepareToDraw", this, 0);
     387    TRACE_EVENT0("cc", "CCLayerTreeHostImpl::prepareToDraw");
    385388    ASSERT(canDraw());
    386389
     390    frame.renderSurfaceLayerList = &m_renderSurfaceLayerList;
    387391    frame.renderPasses.clear();
    388     frame.renderSurfaceLayerList.clear();
     392    frame.renderSurfaceLayerList->clear();
    389393    frame.willDrawLayers.clear();
    390394
    391     if (!calculateRenderPasses(frame.renderPasses, frame.renderSurfaceLayerList, frame.willDrawLayers))
     395    if (!calculateRenderPasses(frame.renderPasses, *frame.renderSurfaceLayerList, frame.willDrawLayers))
    392396        return false;
    393397
     
    403407void CCLayerTreeHostImpl::drawLayers(const FrameData& frame)
    404408{
    405     TRACE_EVENT("CCLayerTreeHostImpl::drawLayers", this, 0);
     409    TRACE_EVENT0("cc", "CCLayerTreeHostImpl::drawLayers");
    406410    ASSERT(canDraw());
    407411    ASSERT(!frame.renderPasses.isEmpty());
     
    427431
    428432    if (m_debugRectHistory->enabled(settings()))
    429         m_debugRectHistory->saveDebugRectsForCurrentFrame(m_rootLayerImpl.get(), frame.renderSurfaceLayerList, settings());
     433        m_debugRectHistory->saveDebugRectsForCurrentFrame(m_rootLayerImpl.get(), *frame.renderSurfaceLayerList, settings());
    430434
    431435    if (m_headsUpDisplay->enabled(settings()))
     
    491495}
    492496
    493 static CCLayerImpl* findScrollLayer(CCLayerImpl* layer)
     497static CCLayerImpl* findRootScrollLayer(CCLayerImpl* layer)
    494498{
    495499    if (!layer)
     
    500504
    501505    for (size_t i = 0; i < layer->children().size(); ++i) {
    502         CCLayerImpl* found = findScrollLayer(layer->children()[i].get());
     506        CCLayerImpl* found = findRootScrollLayer(layer->children()[i].get());
    503507        if (found)
    504508            return found;
     
    508512}
    509513
     514// Content layers can be either directly scrollable or contained in an outer
     515// scrolling layer which applies the scroll transform. Given a content layer,
     516// this function returns the associated scroll layer if any.
     517static CCLayerImpl* findScrollLayerForContentLayer(CCLayerImpl* layerImpl)
     518{
     519    if (!layerImpl)
     520        return 0;
     521
     522    if (layerImpl->scrollable())
     523        return layerImpl;
     524
     525    if (layerImpl->drawsContent() && layerImpl->parent() && layerImpl->parent()->scrollable())
     526        return layerImpl->parent();
     527
     528    return 0;
     529}
     530
    510531void CCLayerTreeHostImpl::setRootLayer(PassOwnPtr<CCLayerImpl> layer)
    511532{
    512533    m_rootLayerImpl = layer;
    513 
    514     // FIXME: Currently, this only finds the first scrollable layer.
    515     m_scrollLayerImpl = findScrollLayer(m_rootLayerImpl.get());
     534    m_rootScrollLayerImpl = findRootScrollLayer(m_rootLayerImpl.get());
     535    m_currentlyScrollingLayerImpl = 0;
     536
     537    if (m_rootLayerImpl && m_scrollingLayerIdFromPreviousTree != -1)
     538        m_currentlyScrollingLayerImpl = CCLayerTreeHostCommon::findLayerInSubtree(m_rootLayerImpl.get(), m_scrollingLayerIdFromPreviousTree);
     539
     540    m_scrollingLayerIdFromPreviousTree = -1;
     541}
     542
     543PassOwnPtr<CCLayerImpl> CCLayerTreeHostImpl::detachLayerTree()
     544{
     545    // Clear all data structures that have direct references to the layer tree.
     546    m_scrollingLayerIdFromPreviousTree = m_currentlyScrollingLayerImpl ? m_currentlyScrollingLayerImpl->id() : -1;
     547    m_currentlyScrollingLayerImpl = 0;
     548    m_renderSurfaceLayerList.clear();
     549
     550    return m_rootLayerImpl.release();
    516551}
    517552
     
    577612}
    578613
     614static void adjustScrollsForPageScaleChange(CCLayerImpl* layerImpl, float pageScaleChange)
     615{
     616    if (!layerImpl)
     617        return;
     618
     619    if (layerImpl->scrollable()) {
     620        // We need to convert impl-side scroll deltas to pageScale space.
     621        FloatSize scrollDelta = layerImpl->scrollDelta();
     622        scrollDelta.scale(pageScaleChange);
     623        layerImpl->setScrollDelta(scrollDelta);
     624    }
     625
     626    for (size_t i = 0; i < layerImpl->children().size(); ++i)
     627        adjustScrollsForPageScaleChange(layerImpl->children()[i].get(), pageScaleChange);
     628}
     629
     630static void applyPageScaleDeltaToScrollLayers(CCLayerImpl* layerImpl, float pageScaleDelta)
     631{
     632    if (!layerImpl)
     633        return;
     634
     635    if (layerImpl->scrollable())
     636        layerImpl->setPageScaleDelta(pageScaleDelta);
     637
     638    for (size_t i = 0; i < layerImpl->children().size(); ++i)
     639        applyPageScaleDeltaToScrollLayers(layerImpl->children()[i].get(), pageScaleDelta);
     640}
     641
    579642void CCLayerTreeHostImpl::setPageScaleFactorAndLimits(float pageScale, float minPageScale, float maxPageScale)
    580643{
     
    591654    m_pageScale = pageScale;
    592655
    593     adjustScrollsForPageScaleChange(pageScaleChange);
     656    if (pageScaleChange != 1)
     657        adjustScrollsForPageScaleChange(m_rootScrollLayerImpl, pageScaleChange);
    594658
    595659    // Clamp delta to limits and refresh display matrix.
    596660    setPageScaleDelta(m_pageScaleDelta / m_sentPageScaleDelta);
    597661    m_sentPageScaleDelta = 1;
    598     applyPageScaleDeltaToScrollLayer();
    599 }
    600 
    601 void CCLayerTreeHostImpl::adjustScrollsForPageScaleChange(float pageScaleChange)
    602 {
    603     if (pageScaleChange == 1)
    604         return;
    605 
    606     // We also need to convert impl-side scroll deltas to pageScale space.
    607     if (m_scrollLayerImpl) {
    608         FloatSize scrollDelta = m_scrollLayerImpl->scrollDelta();
    609         scrollDelta.scale(pageScaleChange);
    610         m_scrollLayerImpl->setScrollDelta(scrollDelta);
    611     }
     662    applyPageScaleDeltaToScrollLayers(m_rootScrollLayerImpl, m_pageScaleDelta);
    612663}
    613664
     
    627678
    628679    updateMaxScrollPosition();
    629     applyPageScaleDeltaToScrollLayer();
    630 }
    631 
    632 void CCLayerTreeHostImpl::applyPageScaleDeltaToScrollLayer()
    633 {
    634     if (m_scrollLayerImpl)
    635         m_scrollLayerImpl->setPageScaleDelta(m_pageScaleDelta);
     680    applyPageScaleDeltaToScrollLayers(m_rootScrollLayerImpl, m_pageScaleDelta);
    636681}
    637682
    638683void CCLayerTreeHostImpl::updateMaxScrollPosition()
    639684{
    640     if (!m_scrollLayerImpl || !m_scrollLayerImpl->children().size())
     685    if (!m_rootScrollLayerImpl || !m_rootScrollLayerImpl->children().size())
    641686        return;
    642687
    643688    FloatSize viewBounds = m_viewportSize;
    644     if (CCLayerImpl* clipLayer = m_scrollLayerImpl->parent()) {
     689    if (CCLayerImpl* clipLayer = m_rootScrollLayerImpl->parent()) {
    645690        if (clipLayer->masksToBounds())
    646691            viewBounds = clipLayer->bounds();
     
    656701    maxScroll.clampNegativeToZero();
    657702
    658     m_scrollLayerImpl->setMaxScrollPosition(maxScroll);
    659 
    660     // TODO(aelias): Also update sublayers.
     703    m_rootScrollLayerImpl->setMaxScrollPosition(maxScroll);
    661704}
    662705
     
    666709}
    667710
     711bool CCLayerTreeHostImpl::ensureRenderSurfaceLayerList()
     712{
     713    if (!m_rootLayerImpl)
     714        return false;
     715
     716    // We need both a non-empty render surface layer list and a root render
     717    // surface to be able to iterate over the visible layers.
     718    if (m_renderSurfaceLayerList.size() && m_rootLayerImpl->renderSurface())
     719        return true;
     720
     721    // If we are called after setRootLayer() but before prepareToDraw(), we need
     722    // to recalculate the visible layers. This prevents being unable to scroll
     723    // during part of a commit.
     724    m_renderSurfaceLayerList.clear();
     725    calculateRenderSurfaceLayerList(m_renderSurfaceLayerList);
     726
     727    return m_renderSurfaceLayerList.size();
     728}
     729
    668730CCInputHandlerClient::ScrollStatus CCLayerTreeHostImpl::scrollBegin(const IntPoint& viewportPoint, CCInputHandlerClient::ScrollInputType type)
    669731{
    670     // TODO: Check for scrollable sublayers.
    671     if (!m_scrollLayerImpl || !m_scrollLayerImpl->scrollable()) {
    672         TRACE_EVENT("scrollBegin Ignored no scrollable", this, 0);
     732    TRACE_EVENT0("cc", "CCLayerTreeHostImpl::scrollBegin");
     733
     734    ASSERT(!m_currentlyScrollingLayerImpl);
     735    clearCurrentlyScrollingLayer();
     736
     737    if (!ensureRenderSurfaceLayerList())
    673738        return ScrollIgnored;
    674     }
    675 
    676     if (m_scrollLayerImpl->shouldScrollOnMainThread()) {
    677         TRACE_EVENT("scrollBegin Failed shouldScrollOnMainThread", this, 0);
    678         return ScrollFailed;
    679     }
    680739
    681740    IntPoint deviceViewportPoint = viewportPoint;
    682741    deviceViewportPoint.scale(m_settings.deviceScaleFactor, m_settings.deviceScaleFactor);
    683742
    684     // The inverse of the screen space transform takes us from physical pixels to layout pixels.
    685     IntPoint scrollLayerPoint(m_scrollLayerImpl->screenSpaceTransform().inverse().mapPoint(deviceViewportPoint));
    686 
    687     if (m_scrollLayerImpl->nonFastScrollableRegion().contains(scrollLayerPoint)) {
    688         TRACE_EVENT("scrollBegin Failed nonFastScrollableRegion", this, 0);
    689         return ScrollFailed;
    690     }
    691 
    692     if (type == CCInputHandlerClient::Wheel && m_scrollLayerImpl->haveWheelEventHandlers()) {
    693         TRACE_EVENT("scrollBegin Failed wheelEventHandlers", this, 0);
    694         return ScrollFailed;
    695     }
    696 
    697     return ScrollStarted;
     743    // First find out which layer was hit from the saved list of visible layers
     744    // in the most recent frame.
     745    CCLayerImpl* layerImpl = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(viewportPoint, m_renderSurfaceLayerList);
     746
     747    // Walk up the hierarchy and look for a scrollable layer.
     748    CCLayerImpl* potentiallyScrollingLayerImpl = 0;
     749    for (; layerImpl; layerImpl = layerImpl->parent()) {
     750        // The content layer can also block attempts to scroll outside the main thread.
     751        if (layerImpl->tryScroll(deviceViewportPoint, type) == ScrollOnMainThread)
     752            return ScrollOnMainThread;
     753
     754        CCLayerImpl* scrollLayerImpl = findScrollLayerForContentLayer(layerImpl);
     755        if (!scrollLayerImpl)
     756            continue;
     757
     758        ScrollStatus status = scrollLayerImpl->tryScroll(viewportPoint, type);
     759
     760        // If any layer wants to divert the scroll event to the main thread, abort.
     761        if (status == ScrollOnMainThread)
     762            return ScrollOnMainThread;
     763
     764        if (status == ScrollStarted && !potentiallyScrollingLayerImpl)
     765            potentiallyScrollingLayerImpl = scrollLayerImpl;
     766    }
     767
     768    if (potentiallyScrollingLayerImpl) {
     769        m_currentlyScrollingLayerImpl = potentiallyScrollingLayerImpl;
     770        return ScrollStarted;
     771    }
     772    return ScrollIgnored;
    698773}
    699774
    700775void CCLayerTreeHostImpl::scrollBy(const IntSize& scrollDelta)
    701776{
    702     TRACE_EVENT("CCLayerTreeHostImpl::scrollBy", this, 0);
    703     if (!m_scrollLayerImpl)
    704         return;
    705 
    706     m_scrollLayerImpl->scrollBy(scrollDelta);
    707     m_client->setNeedsCommitOnImplThread();
    708     m_client->setNeedsRedrawOnImplThread();
     777    TRACE_EVENT0("cc", "CCLayerTreeHostImpl::scrollBy");
     778    if (!m_currentlyScrollingLayerImpl)
     779        return;
     780
     781    FloatSize pendingDelta(scrollDelta);
     782    pendingDelta.scale(1 / m_pageScaleDelta);
     783
     784    for (CCLayerImpl* layerImpl = m_currentlyScrollingLayerImpl; layerImpl && !pendingDelta.isZero(); layerImpl = layerImpl->parent()) {
     785        if (!layerImpl->scrollable())
     786            continue;
     787        FloatSize previousDelta(layerImpl->scrollDelta());
     788        layerImpl->scrollBy(pendingDelta);
     789        // Reset the pending scroll delta to zero if the layer was able to move along the requested
     790        // axis. This is to ensure it is possible to scroll exactly to the beginning or end of a
     791        // scroll area regardless of the scroll step. For diagonal scrolls this also avoids applying
     792        // the scroll on one axis to multiple layers.
     793        if (previousDelta.width() != layerImpl->scrollDelta().width())
     794            pendingDelta.setWidth(0);
     795        if (previousDelta.height() != layerImpl->scrollDelta().height())
     796            pendingDelta.setHeight(0);
     797    }
     798
     799    if (!scrollDelta.isZero() && pendingDelta.isEmpty()) {
     800        m_client->setNeedsCommitOnImplThread();
     801        m_client->setNeedsRedrawOnImplThread();
     802    }
     803}
     804
     805void CCLayerTreeHostImpl::clearCurrentlyScrollingLayer()
     806{
     807    m_currentlyScrollingLayerImpl = 0;
     808    m_scrollingLayerIdFromPreviousTree = -1;
    709809}
    710810
    711811void CCLayerTreeHostImpl::scrollEnd()
    712812{
     813    clearCurrentlyScrollingLayer();
    713814}
    714815
     
    722823                                             const IntPoint& anchor)
    723824{
    724     TRACE_EVENT("CCLayerTreeHostImpl::pinchGestureUpdate", this, 0);
    725 
    726     if (!m_scrollLayerImpl)
     825    TRACE_EVENT0("cc", "CCLayerTreeHostImpl::pinchGestureUpdate");
     826
     827    if (!m_rootScrollLayerImpl)
    727828        return;
    728829
     
    739840    m_previousPinchAnchor = anchor;
    740841
    741     m_scrollLayerImpl->scrollBy(roundedIntSize(move));
     842    m_rootScrollLayerImpl->scrollBy(roundedIntSize(move));
    742843    m_client->setNeedsCommitOnImplThread();
    743844    m_client->setNeedsRedrawOnImplThread();
     
    761862void CCLayerTreeHostImpl::computePinchZoomDeltas(CCScrollAndScaleSet* scrollInfo)
    762863{
    763     if (!m_scrollLayerImpl)
     864    if (!m_rootScrollLayerImpl)
    764865        return;
    765866
     
    773874    // Compute where the scroll offset/page scale would be if fully pinch-zoomed
    774875    // out from the anchor point.
    775     IntSize scrollBegin = flooredIntSize(m_scrollLayerImpl->scrollPosition() + m_scrollLayerImpl->scrollDelta());
     876    IntSize scrollBegin = flooredIntSize(m_rootScrollLayerImpl->scrollPosition() + m_rootScrollLayerImpl->scrollDelta());
    776877    scrollBegin.scale(m_pageScaleDelta);
    777878    float scaleBegin = m_pageScale * m_pageScaleDelta;
     
    793894void CCLayerTreeHostImpl::makeScrollAndScaleSet(CCScrollAndScaleSet* scrollInfo, const IntSize& scrollOffset, float pageScale)
    794895{
    795     if (!m_scrollLayerImpl)
     896    if (!m_rootScrollLayerImpl)
    796897        return;
    797898
    798899    CCLayerTreeHostCommon::ScrollUpdateInfo scroll;
    799     scroll.layerId = m_scrollLayerImpl->id();
    800     scroll.scrollDelta = scrollOffset - toSize(m_scrollLayerImpl->scrollPosition());
     900    scroll.layerId = m_rootScrollLayerImpl->id();
     901    scroll.scrollDelta = scrollOffset - toSize(m_rootScrollLayerImpl->scrollPosition());
    801902    scrollInfo->scrolls.append(scroll);
    802     m_scrollLayerImpl->setSentScrollDelta(scroll.scrollDelta);
     903    m_rootScrollLayerImpl->setSentScrollDelta(scroll.scrollDelta);
    803904    m_sentPageScaleDelta = scrollInfo->pageScaleDelta = pageScale / m_pageScale;
    804905}
    805906
     907static void collectScrollDeltas(CCScrollAndScaleSet* scrollInfo, CCLayerImpl* layerImpl)
     908{
     909    if (!layerImpl)
     910        return;
     911
     912    if (!layerImpl->scrollDelta().isZero()) {
     913        IntSize scrollDelta = flooredIntSize(layerImpl->scrollDelta());
     914        CCLayerTreeHostCommon::ScrollUpdateInfo scroll;
     915        scroll.layerId = layerImpl->id();
     916        scroll.scrollDelta = scrollDelta;
     917        scrollInfo->scrolls.append(scroll);
     918        layerImpl->setSentScrollDelta(scrollDelta);
     919    }
     920
     921    for (size_t i = 0; i < layerImpl->children().size(); ++i)
     922        collectScrollDeltas(scrollInfo, layerImpl->children()[i].get());
     923}
     924
    806925PassOwnPtr<CCScrollAndScaleSet> CCLayerTreeHostImpl::processScrollDeltas()
    807926{
    808927    OwnPtr<CCScrollAndScaleSet> scrollInfo = adoptPtr(new CCScrollAndScaleSet());
    809     bool didMove = m_scrollLayerImpl && (!m_scrollLayerImpl->scrollDelta().isZero() || m_pageScaleDelta != 1.0f);
    810     if (!didMove || m_pinchGestureActive || m_pageScaleAnimation) {
     928
     929    if (m_pinchGestureActive || m_pageScaleAnimation) {
    811930        m_sentPageScaleDelta = scrollInfo->pageScaleDelta = 1;
    812931        if (m_pinchGestureActive)
     
    817936    }
    818937
     938    collectScrollDeltas(scrollInfo.get(), m_rootLayerImpl.get());
    819939    m_sentPageScaleDelta = scrollInfo->pageScaleDelta = m_pageScaleDelta;
    820 
    821     // FIXME: track scrolls from layers other than the root
    822     CCLayerTreeHostCommon::ScrollUpdateInfo scroll;
    823     scroll.layerId = m_scrollLayerImpl->id();
    824     scroll.scrollDelta = flooredIntSize(m_scrollLayerImpl->scrollDelta());
    825     scrollInfo->scrolls.append(scroll);
    826 
    827     m_scrollLayerImpl->setSentScrollDelta(scroll.scrollDelta);
    828940
    829941    return scrollInfo.release();
     
    841953void CCLayerTreeHostImpl::animatePageScale(double monotonicTime)
    842954{
    843     if (!m_pageScaleAnimation || !m_scrollLayerImpl)
    844         return;
    845 
    846     IntSize scrollTotal = flooredIntSize(m_scrollLayerImpl->scrollPosition() + m_scrollLayerImpl->scrollDelta());
     955    if (!m_pageScaleAnimation || !m_rootScrollLayerImpl)
     956        return;
     957
     958    IntSize scrollTotal = flooredIntSize(m_rootScrollLayerImpl->scrollPosition() + m_rootScrollLayerImpl->scrollDelta());
    847959
    848960    setPageScaleDelta(m_pageScaleAnimation->pageScaleAtTime(monotonicTime) / m_pageScale);
    849961    IntSize nextScroll = m_pageScaleAnimation->scrollOffsetAtTime(monotonicTime);
    850962    nextScroll.scale(1 / m_pageScaleDelta);
    851     m_scrollLayerImpl->scrollBy(nextScroll - scrollTotal);
     963    m_rootScrollLayerImpl->scrollBy(nextScroll - scrollTotal);
    852964    m_client->setNeedsRedrawOnImplThread();
    853965
     
    863975        return;
    864976
    865     TRACE_EVENT("CCLayerTreeHostImpl::animateLayers", this, 0);
     977    TRACE_EVENT0("cc", "CCLayerTreeHostImpl::animateLayers");
    866978
    867979    OwnPtr<CCAnimationEventsVector> events(adoptPtr(new CCAnimationEventsVector));
     
    9071019{
    9081020    clearRenderSurfacesOnCCLayerImplRecursive(m_rootLayerImpl.get());
    909     // FIXME: If we have any RenderSurfaceLayerList saved, then make the list empty here.
     1021    m_renderSurfaceLayerList.clear();
    9101022}
    9111023
  • trunk/Source/WebCore/platform/graphics/chromium/cc/CCLayerTreeHostImpl.h

    r120360 r120444  
    8787    struct FrameData {
    8888        CCRenderPassList renderPasses;
    89         CCLayerList renderSurfaceLayerList;
     89        CCLayerList* renderSurfaceLayerList;
    9090        CCLayerList willDrawLayers;
    9191    };
     
    134134
    135135    void setRootLayer(PassOwnPtr<CCLayerImpl>);
    136     PassOwnPtr<CCLayerImpl> releaseRootLayer() { return m_rootLayerImpl.release(); }
    137136    CCLayerImpl* rootLayer() { return m_rootLayerImpl.get(); }
    138137
    139     CCLayerImpl* scrollLayer() const { return m_scrollLayerImpl; }
     138    // Release ownership of the current layer tree and replace it with an empty
     139    // tree. Returns the root layer of the detached tree.
     140    PassOwnPtr<CCLayerImpl> detachLayerTree();
     141
     142    CCLayerImpl* rootScrollLayer() const { return m_rootScrollLayerImpl; }
    140143
    141144    bool visible() const { return m_visible; }
     
    197200
    198201    void setPageScaleDelta(float);
    199     void applyPageScaleDeltaToScrollLayer();
    200     void adjustScrollsForPageScaleChange(float);
    201202    void updateMaxScrollPosition();
    202203    void trackDamageForAllSurfaces(CCLayerImpl* rootDrawLayer, const CCLayerList& renderSurfaceLayerList);
     
    211212    void sendDidLoseContextRecursive(CCLayerImpl*);
    212213    void clearRenderSurfaces();
     214    bool ensureRenderSurfaceLayerList();
     215    void clearCurrentlyScrollingLayer();
    213216
    214217    void dumpRenderSurfaces(TextStream&, int indent, const CCLayerImpl*) const;
     
    217220    OwnPtr<CCRenderer> m_layerRenderer;
    218221    OwnPtr<CCLayerImpl> m_rootLayerImpl;
    219     CCLayerImpl* m_scrollLayerImpl;
     222    CCLayerImpl* m_rootScrollLayerImpl;
     223    CCLayerImpl* m_currentlyScrollingLayerImpl;
     224    int m_scrollingLayerIdFromPreviousTree;
    220225    CCLayerTreeSettings m_settings;
    221226    IntSize m_viewportSize;
     
    249254    FloatRect m_rootScissorRect;
    250255
     256    // List of visible layers for the most recently prepared frame. Used for
     257    // rendering and input event hit testing.
     258    CCLayerList m_renderSurfaceLayerList;
     259
    251260    OwnPtr<CCFrameRateCounter> m_fpsCounter;
    252261    OwnPtr<CCDebugRectHistory> m_debugRectHistory;
  • trunk/Source/WebKit/chromium/ChangeLog

    r120423 r120444  
     12012-06-15  Sami Kyostila  <skyostil@chromium.org>
     2
     3        [chromium] Allow scrolling non-root layers in the compositor thread
     4        https://bugs.webkit.org/show_bug.cgi?id=73350
     5
     6        Reviewed by James Robinson.
     7
     8        New unit tests to verify layer scrolling behavior and the associated
     9        utility functions.
     10
     11        * src/WebCompositorInputHandlerImpl.cpp:
     12        (WebKit::WebCompositorInputHandlerImpl::handleInputEventInternal):
     13        (WebKit::WebCompositorInputHandlerImpl::handleGestureFling):
     14        * tests/CCLayerTreeHostCommonTest.cpp:
     15        * tests/CCLayerTreeHostImplTest.cpp:
     16        * tests/CCLayerTreeHostTest.cpp:
     17        (WTF::MockContentLayerDelegate::paintContents):
     18        (CCLayerTreeHostTestScrollChildLayer):
     19        (WTF::CCLayerTreeHostTestScrollChildLayer::CCLayerTreeHostTestScrollChildLayer):
     20        (WTF::CCLayerTreeHostTestScrollChildLayer::beginTest):
     21        (WTF::CCLayerTreeHostTestScrollChildLayer::applyScrollAndScale):
     22        (WTF::CCLayerTreeHostTestScrollChildLayer::beginCommitOnCCThread):
     23        (WTF::CCLayerTreeHostTestScrollChildLayer::drawLayersOnCCThread):
     24        (WTF::CCLayerTreeHostTestScrollChildLayer::afterTest):
     25        (WTF):
     26        (WTF::TEST_F):
     27        * tests/WebCompositorInputHandlerImplTest.cpp:
     28        (WebKit::TEST_F):
     29
    1302012-06-15  Hironori Bono  <hbono@chromium.org>
    231
  • trunk/Source/WebKit/chromium/src/WebCompositorInputHandlerImpl.cpp

    r112364 r120444  
    192192            // event to the main thread. Change back to DropEvent once we have synchronization bugs sorted out.
    193193            return DidNotHandle;
    194         case CCInputHandlerClient::ScrollFailed:
     194        case CCInputHandlerClient::ScrollOnMainThread:
    195195            return DidNotHandle;
    196196        }
     
    207207            m_gestureScrollStarted = true;
    208208            return DidHandle;
    209         case CCInputHandlerClient::ScrollFailed:
     209        case CCInputHandlerClient::ScrollOnMainThread:
    210210            return DidNotHandle;
    211211        case CCInputHandlerClient::ScrollIgnored:
     
    279279        return DidHandle;
    280280    }
    281     case CCInputHandlerClient::ScrollFailed: {
    282         TRACE_EVENT_INSTANT0("cc", "WebCompositorInputHandlerImpl::handleGestureFling::failed");
     281    case CCInputHandlerClient::ScrollOnMainThread: {
     282        TRACE_EVENT_INSTANT0("cc", "WebCompositorInputHandlerImpl::handleGestureFling::scrollOnMainThread");
    283283        return DidNotHandle;
    284284    }
  • trunk/Source/WebKit/chromium/tests/CCLayerTreeHostCommonTest.cpp

    r120380 r120444  
    36633663}
    36643664
     3665TEST(CCLayerTreeHostCommonTest, verifySubtreeSearch)
     3666{
     3667    RefPtr<LayerChromium> root = LayerChromium::create();
     3668    RefPtr<LayerChromium> child = LayerChromium::create();
     3669    RefPtr<LayerChromium> grandChild = LayerChromium::create();
     3670    RefPtr<LayerChromium> maskLayer = LayerChromium::create();
     3671    RefPtr<LayerChromium> replicaLayer = LayerChromium::create();
     3672
     3673    grandChild->setReplicaLayer(replicaLayer.get());
     3674    child->addChild(grandChild.get());
     3675    child->setMaskLayer(maskLayer.get());
     3676    root->addChild(child.get());
     3677
     3678    int nonexistentId = -1;
     3679    EXPECT_EQ(root, CCLayerTreeHostCommon::findLayerInSubtree(root.get(), root->id()));
     3680    EXPECT_EQ(child, CCLayerTreeHostCommon::findLayerInSubtree(root.get(), child->id()));
     3681    EXPECT_EQ(grandChild, CCLayerTreeHostCommon::findLayerInSubtree(root.get(), grandChild->id()));
     3682    EXPECT_EQ(maskLayer, CCLayerTreeHostCommon::findLayerInSubtree(root.get(), maskLayer->id()));
     3683    EXPECT_EQ(replicaLayer, CCLayerTreeHostCommon::findLayerInSubtree(root.get(), replicaLayer->id()));
     3684    EXPECT_EQ(0, CCLayerTreeHostCommon::findLayerInSubtree(root.get(), nonexistentId));
     3685}
    36653686
    36663687// FIXME:
  • trunk/Source/WebKit/chromium/tests/CCLayerTreeHostImplTest.cpp

    r120360 r120444  
    140140    }
    141141
     142    static PassOwnPtr<CCLayerImpl> createScrollableLayer(int id, const FloatPoint& position, const IntSize& size)
     143    {
     144        OwnPtr<CCLayerImpl> layer = CCLayerImpl::create(id);
     145        layer->setScrollable(true);
     146        layer->setDrawsContent(true);
     147        layer->setPosition(position);
     148        layer->setBounds(size);
     149        layer->setContentBounds(size);
     150        layer->setMaxScrollPosition(IntSize(size.width() * 2, size.height() * 2));
     151        return layer.release();
     152    }
     153
     154    void initializeLayerRendererAndDrawFrame()
     155    {
     156        m_hostImpl->initializeLayerRenderer(createContext(), UnthrottledUploader);
     157        CCLayerTreeHostImpl::FrameData frame;
     158        EXPECT_TRUE(m_hostImpl->prepareToDraw(frame));
     159        m_hostImpl->drawLayers(frame);
     160        m_hostImpl->didDrawAllLayers(frame);
     161    }
     162
    142163protected:
    143164    PassRefPtr<CCGraphicsContext> createContext()
     
    224245TEST_F(CCLayerTreeHostImplTest, scrollRootCallsCommitAndRedraw)
    225246{
    226     {
    227         OwnPtr<CCLayerImpl> root = CCLayerImpl::create(0);
    228         root->setScrollable(true);
    229         root->setScrollPosition(IntPoint(0, 0));
    230         root->setMaxScrollPosition(IntSize(100, 100));
    231         m_hostImpl->setRootLayer(root.release());
    232     }
     247    setupScrollAndContentsLayers(IntSize(100, 100));
     248    m_hostImpl->setViewportSize(IntSize(50, 50));
     249    initializeLayerRendererAndDrawFrame();
    233250
    234251    EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(0, 0), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollStarted);
     
    239256}
    240257
     258TEST_F(CCLayerTreeHostImplTest, scrollWithoutRootLayer)
     259{
     260    // We should not crash when trying to scroll an empty layer tree.
     261    EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(0, 0), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollIgnored);
     262}
     263
     264TEST_F(CCLayerTreeHostImplTest, replaceTreeWhileScrolling)
     265{
     266    const int scrollLayerId = 0;
     267
     268    setupScrollAndContentsLayers(IntSize(100, 100));
     269    m_hostImpl->setViewportSize(IntSize(50, 50));
     270    initializeLayerRendererAndDrawFrame();
     271
     272    // We should not crash if the tree is replaced while we are scrolling.
     273    EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(0, 0), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollStarted);
     274    m_hostImpl->detachLayerTree();
     275
     276    setupScrollAndContentsLayers(IntSize(100, 100));
     277
     278    // We should still be scrolling, because the scrolled layer also exists in the new tree.
     279    IntSize scrollDelta(0, 10);
     280    m_hostImpl->scrollBy(scrollDelta);
     281    m_hostImpl->scrollEnd();
     282    OwnPtr<CCScrollAndScaleSet> scrollInfo = m_hostImpl->processScrollDeltas();
     283    expectContains(*scrollInfo, scrollLayerId, scrollDelta);
     284}
     285
     286TEST_F(CCLayerTreeHostImplTest, clearRootRenderSurfaceAndScroll)
     287{
     288    setupScrollAndContentsLayers(IntSize(100, 100));
     289    m_hostImpl->setViewportSize(IntSize(50, 50));
     290    initializeLayerRendererAndDrawFrame();
     291
     292    // We should be able to scroll even if the root layer loses its render surface after the most
     293    // recent render.
     294    m_hostImpl->rootLayer()->clearRenderSurface();
     295    EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(0, 0), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollStarted);
     296}
     297
    241298TEST_F(CCLayerTreeHostImplTest, wheelEventHandlers)
    242299{
    243     {
    244         OwnPtr<CCLayerImpl> root = CCLayerImpl::create(0);
    245         root->setScrollable(true);
    246         root->setScrollPosition(IntPoint(0, 0));
    247         root->setMaxScrollPosition(IntSize(100, 100));
    248         m_hostImpl->setRootLayer(root.release());
    249     }
     300    setupScrollAndContentsLayers(IntSize(100, 100));
     301    m_hostImpl->setViewportSize(IntSize(50, 50));
     302    initializeLayerRendererAndDrawFrame();
    250303    CCLayerImpl* root = m_hostImpl->rootLayer();
    251304
    252305    root->setHaveWheelEventHandlers(true);
     306
    253307    // With registered event handlers, wheel scrolls have to go to the main thread.
    254     EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(0, 0), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollFailed);
     308    EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(0, 0), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollOnMainThread);
    255309
    256310    // But gesture scrolls can still be handled.
     
    260314TEST_F(CCLayerTreeHostImplTest, shouldScrollOnMainThread)
    261315{
    262     OwnPtr<CCLayerImpl> root = CCLayerImpl::create(0);
    263     root->setScrollable(true);
    264     root->setScrollPosition(IntPoint(0, 0));
    265     root->setMaxScrollPosition(IntSize(100, 100));
     316    setupScrollAndContentsLayers(IntSize(100, 100));
     317    m_hostImpl->setViewportSize(IntSize(50, 50));
     318    initializeLayerRendererAndDrawFrame();
     319    CCLayerImpl* root = m_hostImpl->rootLayer();
     320
    266321    root->setShouldScrollOnMainThread(true);
    267     m_hostImpl->setRootLayer(root.release());
    268     EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(0, 0), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollFailed);
    269     EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(0, 0), CCInputHandlerClient::Gesture), CCInputHandlerClient::ScrollFailed);
     322
     323    EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(0, 0), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollOnMainThread);
     324    EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(0, 0), CCInputHandlerClient::Gesture), CCInputHandlerClient::ScrollOnMainThread);
    270325}
    271326
    272327TEST_F(CCLayerTreeHostImplTest, nonFastScrollableRegionBasic)
    273328{
    274     OwnPtr<CCLayerImpl> root = CCLayerImpl::create(0);
    275     root->setScrollable(true);
    276     root->setScrollPosition(IntPoint(0, 0));
    277     root->setMaxScrollPosition(IntSize(100, 100));
     329    setupScrollAndContentsLayers(IntSize(200, 200));
     330    m_hostImpl->setViewportSize(IntSize(100, 100));
     331    initializeLayerRendererAndDrawFrame();
     332    CCLayerImpl* root = m_hostImpl->rootLayer();
     333
    278334    root->setNonFastScrollableRegion(IntRect(0, 0, 50, 50));
    279     m_hostImpl->setRootLayer(root.release());
     335
    280336    // All scroll types inside the non-fast scrollable region should fail.
    281     EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(25, 25), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollFailed);
    282     EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(25, 25), CCInputHandlerClient::Gesture), CCInputHandlerClient::ScrollFailed);
     337    EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(25, 25), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollOnMainThread);
     338    EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(25, 25), CCInputHandlerClient::Gesture), CCInputHandlerClient::ScrollOnMainThread);
    283339
    284340    // All scroll types outside this region should succeed.
     
    293349TEST_F(CCLayerTreeHostImplTest, nonFastScrollableRegionWithOffset)
    294350{
    295     OwnPtr<CCLayerImpl> root = CCLayerImpl::create(0);
    296     root->setScrollable(true);
    297     root->setScrollPosition(IntPoint(0, 0));
    298     root->setMaxScrollPosition(IntSize(100, 100));
     351    setupScrollAndContentsLayers(IntSize(200, 200));
     352    m_hostImpl->setViewportSize(IntSize(100, 100));
     353    CCLayerImpl* root = m_hostImpl->rootLayer();
     354
    299355    root->setNonFastScrollableRegion(IntRect(0, 0, 50, 50));
    300356    root->setPosition(FloatPoint(-25, 0));
    301     m_hostImpl->setRootLayer(root.release());
    302     CCLayerTreeHostImpl::FrameData frame;
    303     EXPECT_TRUE(m_hostImpl->prepareToDraw(frame));
    304     m_hostImpl->drawLayers(frame); // Update draw transforms so we can correctly map points into layer space.
     357    initializeLayerRendererAndDrawFrame();
    305358
    306359    // This point would fall into the non-fast scrollable region except that we've moved the layer down by 25 pixels.
     
    310363
    311364    // This point is still inside the non-fast region.
    312     EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(10, 10), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollFailed);
     365    EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(10, 10), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollOnMainThread);
    313366}
    314367
     
    317370    setupScrollAndContentsLayers(IntSize(100, 100));
    318371    m_hostImpl->setViewportSize(IntSize(50, 50));
    319 
    320     CCLayerImpl* scrollLayer = m_hostImpl->scrollLayer();
     372    initializeLayerRendererAndDrawFrame();
     373
     374    CCLayerImpl* scrollLayer = m_hostImpl->rootScrollLayer();
    321375    ASSERT(scrollLayer);
    322376
     
    397451    setupScrollAndContentsLayers(IntSize(100, 100));
    398452    m_hostImpl->setViewportSize(IntSize(50, 50));
    399 
    400     CCLayerImpl* scrollLayer = m_hostImpl->scrollLayer();
     453    initializeLayerRendererAndDrawFrame();
     454
     455    CCLayerImpl* scrollLayer = m_hostImpl->rootScrollLayer();
    401456    ASSERT(scrollLayer);
    402457
     
    440495        expectContains(*scrollInfo, scrollLayer->id(), IntSize(-50, -50));
    441496    }
     497}
     498
     499TEST_F(CCLayerTreeHostImplTest, inhibitScrollAndPageScaleUpdatesWhilePinchZooming)
     500{
     501    setupScrollAndContentsLayers(IntSize(100, 100));
     502    m_hostImpl->setViewportSize(IntSize(50, 50));
     503    initializeLayerRendererAndDrawFrame();
     504
     505    CCLayerImpl* scrollLayer = m_hostImpl->rootScrollLayer();
     506    ASSERT(scrollLayer);
     507
     508    const float minPageScale = 0.5, maxPageScale = 4;
     509
     510    // Pinch zoom in.
     511    {
     512        // Start a pinch in gesture at the bottom right corner of the viewport.
     513        const float zoomInDelta = 2;
     514        m_hostImpl->setPageScaleFactorAndLimits(1, minPageScale, maxPageScale);
     515        m_hostImpl->pinchGestureBegin();
     516        m_hostImpl->pinchGestureUpdate(zoomInDelta, IntPoint(50, 50));
     517
     518        // Because we are pinch zooming in, we shouldn't get any scroll or page
     519        // scale deltas.
     520        OwnPtr<CCScrollAndScaleSet> scrollInfo = m_hostImpl->processScrollDeltas();
     521        EXPECT_EQ(scrollInfo->pageScaleDelta, 1);
     522        EXPECT_EQ(scrollInfo->scrolls.size(), 0u);
     523
     524        // Once the gesture ends, we get the final scroll and page scale values.
     525        m_hostImpl->pinchGestureEnd();
     526        scrollInfo = m_hostImpl->processScrollDeltas();
     527        EXPECT_EQ(scrollInfo->pageScaleDelta, zoomInDelta);
     528        expectContains(*scrollInfo, scrollLayer->id(), IntSize(25, 25));
     529    }
     530
     531    // Pinch zoom out.
     532    {
     533        // Start a pinch out gesture at the bottom right corner of the viewport.
     534        const float zoomOutDelta = 0.75;
     535        m_hostImpl->setPageScaleFactorAndLimits(1, minPageScale, maxPageScale);
     536        m_hostImpl->pinchGestureBegin();
     537        m_hostImpl->pinchGestureUpdate(zoomOutDelta, IntPoint(50, 50));
     538
     539        // Since we are pinch zooming out, we should get an update to zoom all
     540        // the way out to the minimum page scale.
     541        OwnPtr<CCScrollAndScaleSet> scrollInfo = m_hostImpl->processScrollDeltas();
     542        EXPECT_EQ(scrollInfo->pageScaleDelta, minPageScale);
     543        expectContains(*scrollInfo, scrollLayer->id(), IntSize(0, 0));
     544
     545        // Once the gesture ends, we get the final scroll and page scale values.
     546        m_hostImpl->pinchGestureEnd();
     547        scrollInfo = m_hostImpl->processScrollDeltas();
     548        EXPECT_EQ(scrollInfo->pageScaleDelta, zoomOutDelta);
     549        expectContains(*scrollInfo, scrollLayer->id(), IntSize(8, 8));
     550    }
     551}
     552
     553TEST_F(CCLayerTreeHostImplTest, inhibitScrollAndPageScaleUpdatesWhileAnimatingPageScale)
     554{
     555    setupScrollAndContentsLayers(IntSize(100, 100));
     556    m_hostImpl->setViewportSize(IntSize(50, 50));
     557    initializeLayerRendererAndDrawFrame();
     558
     559    CCLayerImpl* scrollLayer = m_hostImpl->rootScrollLayer();
     560    ASSERT(scrollLayer);
     561
     562    const float minPageScale = 0.5, maxPageScale = 4;
     563    const double startTime = 1;
     564    const double duration = 0.1;
     565    const double halfwayThroughAnimation = startTime + duration / 2;
     566    const double endTime = startTime + duration;
     567
     568    // Start a page scale animation.
     569    const float pageScaleDelta = 2;
     570    m_hostImpl->setPageScaleFactorAndLimits(1, minPageScale, maxPageScale);
     571    m_hostImpl->startPageScaleAnimation(IntSize(50, 50), false, pageScaleDelta, startTime, duration);
     572
     573    // We should immediately get the final zoom and scroll values for the
     574    // animation.
     575    m_hostImpl->animate(halfwayThroughAnimation, halfwayThroughAnimation);
     576    OwnPtr<CCScrollAndScaleSet> scrollInfo = m_hostImpl->processScrollDeltas();
     577    EXPECT_EQ(scrollInfo->pageScaleDelta, pageScaleDelta);
     578    expectContains(*scrollInfo, scrollLayer->id(), IntSize(25, 25));
     579
     580    // Scrolling during the animation is ignored.
     581    const IntSize scrollDelta(0, 10);
     582    EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(25, 25), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollStarted);
     583    m_hostImpl->scrollBy(scrollDelta);
     584    m_hostImpl->scrollEnd();
     585
     586    // The final page scale and scroll deltas should match what we got
     587    // earlier.
     588    m_hostImpl->animate(endTime, endTime);
     589    scrollInfo = m_hostImpl->processScrollDeltas();
     590    EXPECT_EQ(scrollInfo->pageScaleDelta, pageScaleDelta);
     591    expectContains(*scrollInfo, scrollLayer->id(), IntSize(25, 25));
    442592}
    443593
     
    656806    m_hostImpl->drawLayers(frame);
    657807    m_hostImpl->didDrawAllLayers(frame);
     808}
     809
     810TEST_F(CCLayerTreeHostImplTest, scrollRootIgnored)
     811{
     812    OwnPtr<CCLayerImpl> root = CCLayerImpl::create(0);
     813    root->setScrollable(false);
     814    m_hostImpl->setRootLayer(root.release());
     815    initializeLayerRendererAndDrawFrame();
     816
     817    // Scroll event is ignored because layer is not scrollable.
     818    EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(0, 0), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollIgnored);
     819    EXPECT_FALSE(m_didRequestRedraw);
     820    EXPECT_FALSE(m_didRequestCommit);
     821}
     822
     823TEST_F(CCLayerTreeHostImplTest, scrollNonCompositedRoot)
     824{
     825    // Test the configuration where a non-composited root layer is embedded in a
     826    // scrollable outer layer.
     827    IntSize surfaceSize(10, 10);
     828
     829    OwnPtr<CCLayerImpl> contentLayer = CCLayerImpl::create(1);
     830    contentLayer->setIsNonCompositedContent(true);
     831    contentLayer->setDrawsContent(true);
     832    contentLayer->setPosition(IntPoint(5, 5));
     833    contentLayer->setBounds(surfaceSize);
     834    contentLayer->setContentBounds(IntSize(surfaceSize.width() * 2, surfaceSize.height() * 2));
     835
     836    OwnPtr<CCLayerImpl> scrollLayer = CCLayerImpl::create(0);
     837    scrollLayer->setScrollable(true);
     838    scrollLayer->setMaxScrollPosition(surfaceSize);
     839    scrollLayer->addChild(contentLayer.release());
     840
     841    m_hostImpl->setRootLayer(scrollLayer.release());
     842    m_hostImpl->setViewportSize(surfaceSize);
     843    initializeLayerRendererAndDrawFrame();
     844
     845    EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(5, 5), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollStarted);
     846    m_hostImpl->scrollBy(IntSize(0, 10));
     847    m_hostImpl->scrollEnd();
     848    EXPECT_TRUE(m_didRequestRedraw);
     849    EXPECT_TRUE(m_didRequestCommit);
     850}
     851
     852TEST_F(CCLayerTreeHostImplTest, scrollChildCallsCommitAndRedraw)
     853{
     854    IntSize surfaceSize(10, 10);
     855    OwnPtr<CCLayerImpl> root = CCLayerImpl::create(0);
     856    root->addChild(createScrollableLayer(1, FloatPoint(5, 5), surfaceSize));
     857    m_hostImpl->setRootLayer(root.release());
     858    m_hostImpl->setViewportSize(surfaceSize);
     859    initializeLayerRendererAndDrawFrame();
     860
     861    EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(5, 5), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollStarted);
     862    m_hostImpl->scrollBy(IntSize(0, 10));
     863    m_hostImpl->scrollEnd();
     864    EXPECT_TRUE(m_didRequestRedraw);
     865    EXPECT_TRUE(m_didRequestCommit);
     866}
     867
     868TEST_F(CCLayerTreeHostImplTest, scrollMissesChild)
     869{
     870    IntSize surfaceSize(10, 10);
     871    OwnPtr<CCLayerImpl> root = CCLayerImpl::create(0);
     872    root->addChild(createScrollableLayer(1, FloatPoint(5, 5), surfaceSize));
     873    m_hostImpl->setRootLayer(root.release());
     874    m_hostImpl->setViewportSize(surfaceSize);
     875    initializeLayerRendererAndDrawFrame();
     876
     877    // Scroll event is ignored because the input coordinate is outside the layer boundaries.
     878    EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(15, 5), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollIgnored);
     879    EXPECT_FALSE(m_didRequestRedraw);
     880    EXPECT_FALSE(m_didRequestCommit);
     881}
     882
     883TEST_F(CCLayerTreeHostImplTest, scrollMissesBackfacingChild)
     884{
     885    IntSize surfaceSize(10, 10);
     886    OwnPtr<CCLayerImpl> root = CCLayerImpl::create(0);
     887    OwnPtr<CCLayerImpl> child = createScrollableLayer(1, FloatPoint(5, 5), surfaceSize);
     888    m_hostImpl->setViewportSize(surfaceSize);
     889
     890    WebTransformationMatrix matrix;
     891    matrix.rotate3d(180, 0, 0);
     892    child->setTransform(matrix);
     893    child->setDoubleSided(false);
     894
     895    root->addChild(child.release());
     896    m_hostImpl->setRootLayer(root.release());
     897    initializeLayerRendererAndDrawFrame();
     898
     899    // Scroll event is ignored because the scrollable layer is not facing the viewer and there is
     900    // nothing scrollable behind it.
     901    EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(5, 5), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollIgnored);
     902    EXPECT_FALSE(m_didRequestRedraw);
     903    EXPECT_FALSE(m_didRequestCommit);
     904}
     905
     906TEST_F(CCLayerTreeHostImplTest, scrollBlockedByContentLayer)
     907{
     908    IntSize surfaceSize(10, 10);
     909    OwnPtr<CCLayerImpl> contentLayer = createScrollableLayer(0, FloatPoint(5, 5), surfaceSize);
     910    contentLayer->setShouldScrollOnMainThread(true);
     911    contentLayer->setScrollable(false);
     912
     913    OwnPtr<CCLayerImpl> scrollLayer = createScrollableLayer(1, FloatPoint(5, 5), surfaceSize);
     914    scrollLayer->addChild(contentLayer.release());
     915
     916    m_hostImpl->setRootLayer(scrollLayer.release());
     917    m_hostImpl->setViewportSize(surfaceSize);
     918    initializeLayerRendererAndDrawFrame();
     919
     920    // Scrolling fails because the content layer is asking to be scrolled on the main thread.
     921    EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(5, 5), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollOnMainThread);
     922}
     923
     924TEST_F(CCLayerTreeHostImplTest, scrollRootAndChangePageScaleOnMainThread)
     925{
     926    IntSize surfaceSize(10, 10);
     927    float pageScale = 2;
     928    OwnPtr<CCLayerImpl> root = createScrollableLayer(0, FloatPoint(5, 5), surfaceSize);
     929    m_hostImpl->setRootLayer(root.release());
     930    m_hostImpl->setViewportSize(surfaceSize);
     931    initializeLayerRendererAndDrawFrame();
     932
     933    IntSize scrollDelta(0, 10);
     934    IntSize expectedScrollDelta(scrollDelta);
     935    IntSize expectedMaxScroll(m_hostImpl->rootLayer()->maxScrollPosition());
     936    EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(5, 5), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollStarted);
     937    m_hostImpl->scrollBy(scrollDelta);
     938    m_hostImpl->scrollEnd();
     939
     940    // Set new page scale from main thread.
     941    m_hostImpl->setPageScaleFactorAndLimits(pageScale, pageScale, pageScale);
     942
     943    // The scale should apply to the scroll delta.
     944    expectedScrollDelta.scale(pageScale);
     945    OwnPtr<CCScrollAndScaleSet> scrollInfo = m_hostImpl->processScrollDeltas();
     946    expectContains(*scrollInfo.get(), 0, expectedScrollDelta);
     947
     948    // The scroll range should also have been updated.
     949    EXPECT_EQ(m_hostImpl->rootLayer()->maxScrollPosition(), expectedMaxScroll);
     950
     951    // The page scale delta remains constant because the impl thread did not scale.
     952    EXPECT_EQ(m_hostImpl->rootLayer()->pageScaleDelta(), 1);
     953}
     954
     955TEST_F(CCLayerTreeHostImplTest, scrollRootAndChangePageScaleOnImplThread)
     956{
     957    IntSize surfaceSize(10, 10);
     958    float pageScale = 2;
     959    OwnPtr<CCLayerImpl> root = createScrollableLayer(0, FloatPoint(5, 5), surfaceSize);
     960    m_hostImpl->setRootLayer(root.release());
     961    m_hostImpl->setViewportSize(surfaceSize);
     962    m_hostImpl->setPageScaleFactorAndLimits(1, 1, pageScale);
     963    initializeLayerRendererAndDrawFrame();
     964
     965    IntSize scrollDelta(0, 10);
     966    IntSize expectedScrollDelta(scrollDelta);
     967    IntSize expectedMaxScroll(m_hostImpl->rootLayer()->maxScrollPosition());
     968    EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(5, 5), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollStarted);
     969    m_hostImpl->scrollBy(scrollDelta);
     970    m_hostImpl->scrollEnd();
     971
     972    // Set new page scale on impl thread by pinching.
     973    m_hostImpl->pinchGestureBegin();
     974    m_hostImpl->pinchGestureUpdate(pageScale, IntPoint());
     975    m_hostImpl->pinchGestureEnd();
     976
     977    // The scroll delta is not scaled because the main thread did not scale.
     978    OwnPtr<CCScrollAndScaleSet> scrollInfo = m_hostImpl->processScrollDeltas();
     979    expectContains(*scrollInfo.get(), 0, expectedScrollDelta);
     980
     981    // The scroll range should also have been updated.
     982    EXPECT_EQ(m_hostImpl->rootLayer()->maxScrollPosition(), expectedMaxScroll);
     983
     984    // The page scale delta should match the new scale on the impl side.
     985    EXPECT_EQ(m_hostImpl->rootLayer()->pageScaleDelta(), pageScale);
     986}
     987
     988TEST_F(CCLayerTreeHostImplTest, scrollChildAndChangePageScaleOnMainThread)
     989{
     990    IntSize surfaceSize(10, 10);
     991    OwnPtr<CCLayerImpl> root = CCLayerImpl::create(0);
     992    // Also mark the root scrollable so it becomes the root scroll layer.
     993    root->setScrollable(true);
     994    root->addChild(createScrollableLayer(1, FloatPoint(5, 5), surfaceSize));
     995    m_hostImpl->setRootLayer(root.release());
     996    m_hostImpl->setViewportSize(surfaceSize);
     997    initializeLayerRendererAndDrawFrame();
     998
     999    CCLayerImpl* child = m_hostImpl->rootLayer()->children()[0].get();
     1000
     1001    IntSize scrollDelta(0, 10);
     1002    IntSize expectedScrollDelta(scrollDelta);
     1003    IntSize expectedMaxScroll(child->maxScrollPosition());
     1004    EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(5, 5), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollStarted);
     1005    m_hostImpl->scrollBy(scrollDelta);
     1006    m_hostImpl->scrollEnd();
     1007
     1008    float pageScale = 2;
     1009    m_hostImpl->setPageScaleFactorAndLimits(pageScale, 1, pageScale);
     1010
     1011    // The scale should apply to the scroll delta.
     1012    expectedScrollDelta.scale(pageScale);
     1013    OwnPtr<CCScrollAndScaleSet> scrollInfo = m_hostImpl->processScrollDeltas();
     1014    expectContains(*scrollInfo.get(), 1, expectedScrollDelta);
     1015
     1016    // The scroll range should not have changed.
     1017    EXPECT_EQ(child->maxScrollPosition(), expectedMaxScroll);
     1018
     1019    // The page scale delta remains constant because the impl thread did not scale.
     1020    EXPECT_EQ(child->pageScaleDelta(), 1);
     1021}
     1022
     1023TEST_F(CCLayerTreeHostImplTest, scrollChildBeyondLimit)
     1024{
     1025    // Scroll a child layer beyond its maximum scroll range and make sure the
     1026    // parent layer is scrolled on the axis on which the child was unable to
     1027    // scroll.
     1028    IntSize surfaceSize(10, 10);
     1029    OwnPtr<CCLayerImpl> root = createScrollableLayer(0, FloatPoint(5, 5), surfaceSize);
     1030
     1031    OwnPtr<CCLayerImpl> grandChild = createScrollableLayer(2, FloatPoint(5, 5), surfaceSize);
     1032    grandChild->setScrollPosition(IntPoint(0, 5));
     1033
     1034    OwnPtr<CCLayerImpl> child = createScrollableLayer(1, FloatPoint(5, 5), surfaceSize);
     1035    child->setScrollPosition(IntPoint(3, 0));
     1036    child->addChild(grandChild.release());
     1037
     1038    root->addChild(child.release());
     1039    m_hostImpl->setRootLayer(root.release());
     1040    m_hostImpl->setViewportSize(surfaceSize);
     1041    initializeLayerRendererAndDrawFrame();
     1042    {
     1043        IntSize scrollDelta(-3, -7);
     1044        EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(5, 5), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollStarted);
     1045        m_hostImpl->scrollBy(scrollDelta);
     1046        m_hostImpl->scrollEnd();
     1047
     1048        OwnPtr<CCScrollAndScaleSet> scrollInfo = m_hostImpl->processScrollDeltas();
     1049
     1050        // The grand child should have scrolled up to its limit.
     1051        CCLayerImpl* child = m_hostImpl->rootLayer()->children()[0].get();
     1052        CCLayerImpl* grandChild = child->children()[0].get();
     1053        expectContains(*scrollInfo.get(), grandChild->id(), IntSize(0, -5));
     1054
     1055        // The child should have only scrolled on the other axis.
     1056        expectContains(*scrollInfo.get(), child->id(), IntSize(-3, 0));
     1057    }
     1058}
     1059
     1060TEST_F(CCLayerTreeHostImplTest, scrollEventBubbling)
     1061{
     1062    // When we try to scroll a non-scrollable child layer, the scroll delta
     1063    // should be applied to one of its ancestors if possible.
     1064    IntSize surfaceSize(10, 10);
     1065    OwnPtr<CCLayerImpl> root = createScrollableLayer(0, FloatPoint(5, 5), surfaceSize);
     1066    OwnPtr<CCLayerImpl> child = createScrollableLayer(1, FloatPoint(5, 5), surfaceSize);
     1067
     1068    child->setScrollable(false);
     1069    root->addChild(child.release());
     1070
     1071    m_hostImpl->setRootLayer(root.release());
     1072    m_hostImpl->setViewportSize(surfaceSize);
     1073    initializeLayerRendererAndDrawFrame();
     1074    {
     1075        IntSize scrollDelta(0, 4);
     1076        EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(5, 5), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollStarted);
     1077        m_hostImpl->scrollBy(scrollDelta);
     1078        m_hostImpl->scrollEnd();
     1079
     1080        OwnPtr<CCScrollAndScaleSet> scrollInfo = m_hostImpl->processScrollDeltas();
     1081
     1082        // Only the root should have scrolled.
     1083        ASSERT_EQ(scrollInfo->scrolls.size(), 1u);
     1084        expectContains(*scrollInfo.get(), m_hostImpl->rootLayer()->id(), scrollDelta);
     1085    }
     1086}
     1087
     1088TEST_F(CCLayerTreeHostImplTest, scrollBeforeRedraw)
     1089{
     1090    IntSize surfaceSize(10, 10);
     1091    m_hostImpl->setRootLayer(createScrollableLayer(0, FloatPoint(5, 5), surfaceSize));
     1092    m_hostImpl->setViewportSize(surfaceSize);
     1093
     1094    // Draw one frame and then immediately rebuild the layer tree to mimic a tree synchronization.
     1095    initializeLayerRendererAndDrawFrame();
     1096    m_hostImpl->detachLayerTree();
     1097    m_hostImpl->setRootLayer(createScrollableLayer(0, FloatPoint(5, 5), surfaceSize));
     1098
     1099    // Scrolling should still work even though we did not draw yet.
     1100    EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(5, 5), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollStarted);
    6581101}
    6591102
  • trunk/Source/WebKit/chromium/tests/CCLayerTreeHostTest.cpp

    r120360 r120444  
    22212221SINGLE_AND_MULTI_THREAD_TEST_F(CCLayerTreeHostTestLayerAddedWithAnimation)
    22222222
     2223class CCLayerTreeHostTestScrollChildLayer : public CCLayerTreeHostTest, public LayerChromiumScrollDelegate {
     2224public:
     2225    CCLayerTreeHostTestScrollChildLayer()
     2226        : m_scrollAmount(2, 1)
     2227    {
     2228    }
     2229
     2230    virtual void beginTest() OVERRIDE
     2231    {
     2232        m_layerTreeHost->setViewportSize(IntSize(10, 10));
     2233        m_rootScrollLayer = ContentLayerChromium::create(&m_mockDelegate);
     2234        m_rootScrollLayer->setBounds(IntSize(10, 10));
     2235        m_rootScrollLayer->setIsDrawable(true);
     2236        m_rootScrollLayer->setScrollable(true);
     2237        m_rootScrollLayer->setMaxScrollPosition(IntSize(100, 100));
     2238        m_layerTreeHost->rootLayer()->addChild(m_rootScrollLayer);
     2239        m_childLayer = ContentLayerChromium::create(&m_mockDelegate);
     2240        m_childLayer->setLayerScrollDelegate(this);
     2241        m_childLayer->setBounds(IntSize(50, 50));
     2242        m_childLayer->setIsDrawable(true);
     2243        m_childLayer->setScrollable(true);
     2244        m_childLayer->setMaxScrollPosition(IntSize(100, 100));
     2245        m_rootScrollLayer->addChild(m_childLayer);
     2246        postSetNeedsCommitToMainThread();
     2247    }
     2248
     2249    virtual void didScroll(const IntSize& scrollDelta) OVERRIDE
     2250    {
     2251        m_reportedScrollAmount = scrollDelta;
     2252    }
     2253
     2254    virtual void applyScrollAndScale(const IntSize& scrollDelta, float) OVERRIDE
     2255    {
     2256        IntPoint position = m_rootScrollLayer->scrollPosition();
     2257        m_rootScrollLayer->setScrollPosition(position + scrollDelta);
     2258    }
     2259
     2260    virtual void beginCommitOnCCThread(CCLayerTreeHostImpl* impl) OVERRIDE
     2261    {
     2262        EXPECT_EQ(m_rootScrollLayer->scrollPosition(), IntPoint());
     2263        if (!m_layerTreeHost->frameNumber())
     2264            EXPECT_EQ(m_childLayer->scrollPosition(), IntPoint());
     2265        else
     2266            EXPECT_EQ(m_childLayer->scrollPosition(), IntPoint() + m_scrollAmount);
     2267    }
     2268
     2269    virtual void drawLayersOnCCThread(CCLayerTreeHostImpl* impl) OVERRIDE
     2270    {
     2271        if (impl->frameNumber() == 1) {
     2272            EXPECT_EQ(impl->scrollBegin(IntPoint(5, 5), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollStarted);
     2273            impl->scrollBy(m_scrollAmount);
     2274            impl->scrollEnd();
     2275        } else if (impl->frameNumber() == 2)
     2276            endTest();
     2277    }
     2278
     2279    virtual void afterTest() OVERRIDE
     2280    {
     2281        EXPECT_EQ(m_scrollAmount, m_reportedScrollAmount);
     2282    }
     2283
     2284private:
     2285    const IntSize m_scrollAmount;
     2286    IntSize m_reportedScrollAmount;
     2287    MockContentLayerDelegate m_mockDelegate;
     2288    RefPtr<LayerChromium> m_childLayer;
     2289    RefPtr<LayerChromium> m_rootScrollLayer;
     2290};
     2291
     2292TEST_F(CCLayerTreeHostTestScrollChildLayer, runMultiThread)
     2293{
     2294    runTest(true);
     2295}
     2296
    22232297} // namespace
  • trunk/Source/WebKit/chromium/tests/WebCompositorInputHandlerImplTest.cpp

    r112364 r120444  
    203203}
    204204
    205 TEST_F(WebCompositorInputHandlerImplTest, gestureScrollFailed)
     205TEST_F(WebCompositorInputHandlerImplTest, gestureScrollOnMainThread)
    206206{
    207207    // We should send all events to the widget for this gesture.
     
    210210
    211211    EXPECT_CALL(m_mockCCInputHandlerClient, scrollBegin(::testing::_, ::testing::_))
    212         .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollFailed));
     212        .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollOnMainThread));
    213213
    214214    gesture.type = WebInputEvent::GestureScrollBegin;
     
    307307
    308308    EXPECT_CALL(m_mockCCInputHandlerClient, scrollBegin(testing::_, testing::_))
    309         .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollFailed));
     309        .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollOnMainThread));
    310310
    311311    gesture.type = WebInputEvent::GestureFlingStart;
     
    388388    EXPECT_CALL(m_mockCCInputHandlerClient, scheduleAnimation());
    389389    EXPECT_CALL(m_mockCCInputHandlerClient, scrollBegin(testing::_, testing::_))
    390         .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollFailed));
     390        .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollOnMainThread));
    391391    EXPECT_CALL(m_mockCCInputHandlerClient, scrollBy(testing::_)).Times(0);
    392392    EXPECT_CALL(m_mockCCInputHandlerClient, scrollEnd()).Times(0);
     
    470470    EXPECT_CALL(m_mockCCInputHandlerClient, scheduleAnimation());
    471471    EXPECT_CALL(m_mockCCInputHandlerClient, scrollBegin(testing::_, testing::_))
    472         .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollFailed));
     472        .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollOnMainThread));
    473473    EXPECT_CALL(m_mockCCInputHandlerClient, scrollBy(testing::_)).Times(0);
    474474    EXPECT_CALL(m_mockCCInputHandlerClient, scrollEnd()).Times(0);
     
    547547    EXPECT_CALL(m_mockCCInputHandlerClient, scheduleAnimation());
    548548    EXPECT_CALL(m_mockCCInputHandlerClient, scrollBegin(testing::_, testing::_))
    549         .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollFailed));
     549        .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollOnMainThread));
    550550    EXPECT_CALL(m_mockCCInputHandlerClient, scrollBy(testing::_)).Times(0);
    551551    EXPECT_CALL(m_mockCCInputHandlerClient, scrollEnd()).Times(0);
Note: See TracChangeset for help on using the changeset viewer.