diff options
Diffstat (limited to 'Source/WebCore/page/scrolling')
15 files changed, 917 insertions, 79 deletions
diff --git a/Source/WebCore/page/scrolling/ScrollingCoordinator.cpp b/Source/WebCore/page/scrolling/ScrollingCoordinator.cpp index e569b2c25..5c14d995d 100644 --- a/Source/WebCore/page/scrolling/ScrollingCoordinator.cpp +++ b/Source/WebCore/page/scrolling/ScrollingCoordinator.cpp @@ -25,8 +25,6 @@ #include "config.h" -#if ENABLE(THREADED_SCROLLING) - #include "ScrollingCoordinator.h" #include "Frame.h" @@ -35,49 +33,54 @@ #include "Page.h" #include "PlatformWheelEvent.h" #include "Region.h" +#include "RenderView.h" #include "ScrollAnimator.h" +#include "ScrollingTreeState.h" +#include <wtf/MainThread.h> + +#if USE(ACCELERATED_COMPOSITING) +#include "RenderLayerCompositor.h" +#endif + +#if ENABLE(THREADED_SCROLLING) #include "ScrollingThread.h" #include "ScrollingTree.h" -#include "ScrollingTreeState.h" #include <wtf/Functional.h> -#include <wtf/MainThread.h> #include <wtf/PassRefPtr.h> +#endif namespace WebCore { -PassRefPtr<ScrollingCoordinator> ScrollingCoordinator::create(Page* page) -{ - return adoptRef(new ScrollingCoordinator(page)); -} - ScrollingCoordinator::ScrollingCoordinator(Page* page) : m_page(page) - , m_scrollingTree(ScrollingTree::create(this)) +#if ENABLE(THREADED_SCROLLING) , m_scrollingTreeState(ScrollingTreeState::create()) + , m_scrollingTree(ScrollingTree::create(this)) , m_scrollingTreeStateCommitterTimer(this, &ScrollingCoordinator::scrollingTreeStateCommitterTimerFired) +#endif { } -ScrollingCoordinator::~ScrollingCoordinator() -{ - ASSERT(!m_page); - ASSERT(!m_scrollingTree); -} - void ScrollingCoordinator::pageDestroyed() { ASSERT(m_page); m_page = 0; +#if ENABLE(THREADED_SCROLLING) + m_scrollingTreeStateCommitterTimer.stop(); + // Invalidating the scrolling tree will break the reference cycle between the ScrollingCoordinator and ScrollingTree objects. ScrollingThread::dispatch(bind(&ScrollingTree::invalidate, m_scrollingTree.release())); +#endif } +#if ENABLE(THREADED_SCROLLING) ScrollingTree* ScrollingCoordinator::scrollingTree() const { ASSERT(m_scrollingTree); return m_scrollingTree.get(); } +#endif bool ScrollingCoordinator::coordinatesScrollingForFrameView(FrameView* frameView) const { @@ -88,37 +91,66 @@ bool ScrollingCoordinator::coordinatesScrollingForFrameView(FrameView* frameView if (frameView->frame() != m_page->mainFrame()) return false; - return true; + // We currently only support composited mode. +#if USE(ACCELERATED_COMPOSITING) + RenderView* renderView = m_page->mainFrame()->contentRenderer(); + if (!renderView) + return false; + return renderView->usesCompositing(); +#else + return false; +#endif } -void ScrollingCoordinator::frameViewLayoutUpdated(FrameView* frameView) +static Region computeNonFastScrollableRegion(FrameView* frameView) { - ASSERT(isMainThread()); - ASSERT(m_page); + Region nonFastScrollableRegion; - if (!coordinatesScrollingForFrameView(frameView)) - return; + HashSet<FrameView*> childFrameViews; + for (HashSet<RefPtr<Widget> >::const_iterator it = frameView->children()->begin(), end = frameView->children()->end(); it != end; ++it) { + if ((*it)->isFrameView()) + childFrameViews.add(static_cast<FrameView*>(it->get())); + } - // Compute the region of the page that we can't do fast scrolling for. This currently includes - // all scrollable areas, such as subframes, overflow divs and list boxes. - Region nonScrollableRegion; if (const FrameView::ScrollableAreaSet* scrollableAreas = frameView->scrollableAreas()) { for (FrameView::ScrollableAreaSet::const_iterator it = scrollableAreas->begin(), end = scrollableAreas->end(); it != end; ++it) { ScrollableArea* scrollableArea = *it; // Check if this area can be scrolled at all. + // If this scrollable area is a frame view that itself has scrollable areas, then we need to add it to the region. if ((!scrollableArea->horizontalScrollbar() || !scrollableArea->horizontalScrollbar()->enabled()) - && (!scrollableArea->verticalScrollbar() || !scrollableArea->verticalScrollbar()->enabled())) - continue; + && (!scrollableArea->verticalScrollbar() || !scrollableArea->verticalScrollbar()->enabled()) + && (!childFrameViews.contains(static_cast<FrameView*>(scrollableArea)) || !static_cast<FrameView*>(scrollableArea)->scrollableAreas())) + continue; - nonScrollableRegion.unite(scrollableArea->scrollableAreaBoundingBox()); + nonFastScrollableRegion.unite(scrollableArea->scrollableAreaBoundingBox()); } } - m_scrollingTreeState->setViewportRect(IntRect(IntPoint(), frameView->visibleContentRect().size())); - m_scrollingTreeState->setContentsSize(frameView->contentsSize()); - m_scrollingTreeState->setNonFastScrollableRegion(nonScrollableRegion); - scheduleTreeStateCommit(); + return nonFastScrollableRegion; +} + +void ScrollingCoordinator::frameViewLayoutUpdated(FrameView* frameView) +{ + ASSERT(isMainThread()); + ASSERT(m_page); + + // Compute the region of the page that we can't do fast scrolling for. This currently includes + // all scrollable areas, such as subframes, overflow divs and list boxes. We need to do this even if the + // frame view whose layout was updated is not the main frame. + Region nonFastScrollableRegion = computeNonFastScrollableRegion(m_page->mainFrame()->view()); + setNonFastScrollableRegion(nonFastScrollableRegion); + + if (!coordinatesScrollingForFrameView(frameView)) + return; + + setScrollParameters(frameView->horizontalScrollElasticity(), + frameView->verticalScrollElasticity(), + frameView->horizontalScrollbar() && frameView->horizontalScrollbar()->enabled(), + frameView->verticalScrollbar() && frameView->verticalScrollbar()->enabled(), + IntRect(IntPoint(), frameView->visibleContentRect().size()), + frameView->contentsSize()); + } void ScrollingCoordinator::frameViewWheelEventHandlerCountChanged(FrameView*) @@ -129,6 +161,91 @@ void ScrollingCoordinator::frameViewWheelEventHandlerCountChanged(FrameView*) recomputeWheelEventHandlerCount(); } +void ScrollingCoordinator::frameViewHasSlowRepaintObjectsDidChange(FrameView* frameView) +{ + ASSERT(isMainThread()); + ASSERT(m_page); + + if (!coordinatesScrollingForFrameView(frameView)) + return; + + updateShouldUpdateScrollLayerPositionOnMainThread(); +} + +void ScrollingCoordinator::frameViewHasFixedObjectsDidChange(FrameView* frameView) +{ + ASSERT(isMainThread()); + ASSERT(m_page); + + if (!coordinatesScrollingForFrameView(frameView)) + return; + + updateShouldUpdateScrollLayerPositionOnMainThread(); +} + +static GraphicsLayer* scrollLayerForFrameView(FrameView* frameView) +{ +#if USE(ACCELERATED_COMPOSITING) + Frame* frame = frameView->frame(); + if (!frame) + return 0; + + RenderView* renderView = frame->contentRenderer(); + if (!renderView) + return 0; + return renderView->compositor()->scrollLayer(); +#else + return 0; +#endif +} + +void ScrollingCoordinator::frameViewRootLayerDidChange(FrameView* frameView) +{ + ASSERT(isMainThread()); + ASSERT(m_page); + + if (frameView->frame() != m_page->mainFrame()) + return; + + frameViewLayoutUpdated(frameView); + recomputeWheelEventHandlerCount(); + updateShouldUpdateScrollLayerPositionOnMainThread(); + setScrollLayer(scrollLayerForFrameView(frameView)); +} + +bool ScrollingCoordinator::requestScrollPositionUpdate(FrameView* frameView, const IntPoint& scrollPosition) +{ + ASSERT(isMainThread()); + ASSERT(m_page); + + if (!coordinatesScrollingForFrameView(frameView)) + return false; + +#if ENABLE(THREADED_SCROLLING) + ScrollingThread::dispatch(bind(&ScrollingTree::setMainFrameScrollPosition, m_scrollingTree.get(), scrollPosition)); + return true; +#else + UNUSED_PARAM(scrollPosition); + return false; +#endif +} + +bool ScrollingCoordinator::handleWheelEvent(FrameView*, const PlatformWheelEvent& wheelEvent) +{ + ASSERT(isMainThread()); + ASSERT(m_page); + +#if ENABLE(THREADED_SCROLLING) + if (m_scrollingTree->willWheelEventStartSwipeGesture(wheelEvent)) + return false; + + ScrollingThread::dispatch(bind(&ScrollingTree::handleWheelEvent, m_scrollingTree.get(), wheelEvent)); +#else + UNUSED_PARAM(wheelEvent); +#endif + return true; +} + void ScrollingCoordinator::updateMainFrameScrollPosition(const IntPoint& scrollPosition) { ASSERT(isMainThread()); @@ -141,8 +258,25 @@ void ScrollingCoordinator::updateMainFrameScrollPosition(const IntPoint& scrollP return; frameView->setConstrainsScrollingToContentEdge(false); - frameView->scrollToOffsetWithoutAnimation(scrollPosition); + frameView->notifyScrollPositionChanged(scrollPosition); + frameView->setConstrainsScrollingToContentEdge(true); +} + +void ScrollingCoordinator::updateMainFrameScrollPositionAndScrollLayerPosition(const IntPoint& scrollPosition) +{ +#if USE(ACCELERATED_COMPOSITING) + FrameView* frameView = m_page->mainFrame()->view(); + + // Make sure to update the main frame scroll position before changing the scroll layer position, + // otherwise we'll introduce jittering on pages with slow repaint objects (like background-attachment: fixed). + frameView->updateCompositingLayers(); + frameView->setConstrainsScrollingToContentEdge(false); + frameView->notifyScrollPositionChanged(scrollPosition); frameView->setConstrainsScrollingToContentEdge(true); + + if (GraphicsLayer* scrollLayer = scrollLayerForFrameView(frameView)) + scrollLayer->setPosition(-frameView->scrollPosition()); +#endif } void ScrollingCoordinator::recomputeWheelEventHandlerCount() @@ -152,11 +286,60 @@ void ScrollingCoordinator::recomputeWheelEventHandlerCount() if (frame->document()) wheelEventHandlerCount += frame->document()->wheelEventHandlerCount(); } + setWheelEventHandlerCount(wheelEventHandlerCount); +} + +void ScrollingCoordinator::updateShouldUpdateScrollLayerPositionOnMainThread() +{ + FrameView* frameView = m_page->mainFrame()->view(); + + // FIXME: Having fixed objects on the page should not trigger the slow path. + setShouldUpdateScrollLayerPositionOnMainThread(frameView->hasSlowRepaintObjects() || frameView->hasFixedObjects()); +} + +#if ENABLE(THREADED_SCROLLING) +void ScrollingCoordinator::setScrollLayer(GraphicsLayer* scrollLayer) +{ + m_scrollingTreeState->setScrollLayer(scrollLayer); + scheduleTreeStateCommit(); +} +void ScrollingCoordinator::setNonFastScrollableRegion(const Region& region) +{ + m_scrollingTreeState->setNonFastScrollableRegion(region); + scheduleTreeStateCommit(); +} + +void ScrollingCoordinator::setScrollParameters(ScrollElasticity horizontalScrollElasticity, + ScrollElasticity verticalScrollElasticity, + bool hasEnabledHorizontalScrollbar, + bool hasEnabledVerticalScrollbar, + const IntRect& viewportRect, + const IntSize& contentsSize) +{ + m_scrollingTreeState->setHorizontalScrollElasticity(horizontalScrollElasticity); + m_scrollingTreeState->setVerticalScrollElasticity(verticalScrollElasticity); + m_scrollingTreeState->setHasEnabledHorizontalScrollbar(hasEnabledHorizontalScrollbar); + m_scrollingTreeState->setHasEnabledVerticalScrollbar(hasEnabledVerticalScrollbar); + + m_scrollingTreeState->setViewportRect(viewportRect); + m_scrollingTreeState->setContentsSize(contentsSize); + scheduleTreeStateCommit(); +} + + +void ScrollingCoordinator::setWheelEventHandlerCount(unsigned wheelEventHandlerCount) +{ m_scrollingTreeState->setWheelEventHandlerCount(wheelEventHandlerCount); scheduleTreeStateCommit(); } +void ScrollingCoordinator::setShouldUpdateScrollLayerPositionOnMainThread(bool shouldUpdateScrollLayerPositionOnMainThread) +{ + m_scrollingTreeState->setShouldUpdateScrollLayerPositionOnMainThread(shouldUpdateScrollLayerPositionOnMainThread); + scheduleTreeStateCommit(); +} + void ScrollingCoordinator::scheduleTreeStateCommit() { if (m_scrollingTreeStateCommitterTimer.isActive()) @@ -189,7 +372,6 @@ void ScrollingCoordinator::commitTreeState() OwnPtr<ScrollingTreeState> treeState = m_scrollingTreeState->commit(); ScrollingThread::dispatch(bind(&ScrollingTree::commitNewTreeState, m_scrollingTree.get(), treeState.release())); } +#endif } // namespace WebCore - -#endif // ENABLE(THREADED_SCROLLING) diff --git a/Source/WebCore/page/scrolling/ScrollingCoordinator.h b/Source/WebCore/page/scrolling/ScrollingCoordinator.h index eb597b8b0..00b289fe2 100644 --- a/Source/WebCore/page/scrolling/ScrollingCoordinator.h +++ b/Source/WebCore/page/scrolling/ScrollingCoordinator.h @@ -26,14 +26,16 @@ #ifndef ScrollingCoordinator_h #define ScrollingCoordinator_h -#if ENABLE(THREADED_SCROLLING) - #include "GraphicsLayer.h" #include "IntRect.h" +#include "ScrollTypes.h" #include "Timer.h" #include <wtf/Forward.h> + +#if ENABLE(THREADED_SCROLLING) #include <wtf/ThreadSafeRefCounted.h> #include <wtf/Threading.h> +#endif #if PLATFORM(MAC) #include <wtf/RetainPtr.h> @@ -45,11 +47,12 @@ class FrameView; class GraphicsLayer; class Page; class PlatformWheelEvent; -class ScrollingTree; +class Region; +class ScrollingCoordinatorPrivate; class ScrollingTreeState; -#if ENABLE(GESTURE_EVENTS) -class PlatformGestureEvent; +#if ENABLE(THREADED_SCROLLING) +class ScrollingTree; #endif class ScrollingCoordinator : public ThreadSafeRefCounted<ScrollingCoordinator> { @@ -59,7 +62,9 @@ public: void pageDestroyed(); +#if ENABLE(THREADED_SCROLLING) ScrollingTree* scrollingTree() const; +#endif // Return whether this scrolling coordinator handles scrolling for the given frame view. bool coordinatesScrollingForFrameView(FrameView*) const; @@ -71,37 +76,68 @@ public: // frame view's underlying document. void frameViewWheelEventHandlerCountChanged(FrameView*); - // Should be called whenever the scroll layer for the given frame view changes. - void frameViewScrollLayerDidChange(FrameView*, const GraphicsLayer*); + // Should be called whenever the slow repaint objects counter changes between zero and one. + void frameViewHasSlowRepaintObjectsDidChange(FrameView*); + + // Should be called whenever the fixed objects counter changes between zero and one. + // FIXME: This is a temporary workaround so that we'll fall into main thread scrolling mode + // if a page has fixed elements. The scrolling tree should know about the fixed elements + // so it can make sure they stay fixed even when we scroll on the scrolling thread. + void frameViewHasFixedObjectsDidChange(FrameView*); + + // Should be called whenever the root layer for the given frame view changes. + void frameViewRootLayerDidChange(FrameView*); // Should be called whenever the horizontal scrollbar layer for the given frame view changes. void frameViewHorizontalScrollbarLayerDidChange(FrameView*, GraphicsLayer* horizontalScrollbarLayer); - // Should be called whenever the horizontal scrollbar layer for the given frame view changes. + // Should be called whenever the vertical scrollbar layer for the given frame view changes. void frameViewVerticalScrollbarLayerDidChange(FrameView*, GraphicsLayer* verticalScrollbarLayer); + // Requests that the scrolling coordinator updates the scroll position of the given frame view. If this function returns true, it means that the + // position will be updated asynchronously. If it returns false, the caller should update the scrolling position itself. + bool requestScrollPositionUpdate(FrameView*, const IntPoint&); + + // Handle the wheel event on the scrolling thread. Returns whether the event was handled or not. + bool handleWheelEvent(FrameView*, const PlatformWheelEvent&); + // Dispatched by the scrolling tree whenever the main frame scroll position changes. void updateMainFrameScrollPosition(const IntPoint&); + // Dispatched by the scrolling tree whenever the main frame scroll position changes and the scroll layer position needs to be updated as well. + void updateMainFrameScrollPositionAndScrollLayerPosition(const IntPoint&); + private: explicit ScrollingCoordinator(Page*); void recomputeWheelEventHandlerCount(); + void updateShouldUpdateScrollLayerPositionOnMainThread(); + void setScrollLayer(GraphicsLayer*); + void setNonFastScrollableRegion(const Region&); + void setScrollParameters(ScrollElasticity horizontalScrollElasticity, ScrollElasticity verticalScrollElasticity, + bool hasEnabledHorizontalScrollbar, bool hasEnabledVerticalScrollbar, + const IntRect& viewportRect, const IntSize& contentsSize); + void setWheelEventHandlerCount(unsigned); + void setShouldUpdateScrollLayerPositionOnMainThread(bool); + + Page* m_page; + +#if ENABLE(THREADED_SCROLLING) void scheduleTreeStateCommit(); + void scrollingTreeStateCommitterTimerFired(Timer<ScrollingCoordinator>*); void commitTreeStateIfNeeded(); void commitTreeState(); - Page* m_page; - RefPtr<ScrollingTree> m_scrollingTree; - OwnPtr<ScrollingTreeState> m_scrollingTreeState; + RefPtr<ScrollingTree> m_scrollingTree; Timer<ScrollingCoordinator> m_scrollingTreeStateCommitterTimer; +#endif + + ScrollingCoordinatorPrivate* m_private; }; } // namespace WebCore -#endif // ENABLE(THREADED_SCROLLING) - #endif // ScrollingCoordinator_h diff --git a/Source/WebCore/page/scrolling/ScrollingCoordinatorNone.cpp b/Source/WebCore/page/scrolling/ScrollingCoordinatorNone.cpp new file mode 100644 index 000000000..d06d6bb34 --- /dev/null +++ b/Source/WebCore/page/scrolling/ScrollingCoordinatorNone.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2012 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include "ScrollingCoordinator.h" + +namespace WebCore { + +class ScrollingCoordinatorPrivate { +}; + +#if !ENABLE(THREADED_SCROLLING) +PassRefPtr<ScrollingCoordinator> ScrollingCoordinator::create(Page* page) +{ + return adoptRef(new ScrollingCoordinator(page)); +} + +ScrollingCoordinator::~ScrollingCoordinator() +{ + ASSERT(!m_page); +} + +void ScrollingCoordinator::frameViewHorizontalScrollbarLayerDidChange(FrameView*, GraphicsLayer*) +{ +} + +void ScrollingCoordinator::frameViewVerticalScrollbarLayerDidChange(FrameView*, GraphicsLayer*) +{ +} + +void ScrollingCoordinator::setScrollLayer(GraphicsLayer*) +{ +} + +void ScrollingCoordinator::setNonFastScrollableRegion(const Region&) +{ +} + +void ScrollingCoordinator::setScrollParameters(ScrollElasticity, ScrollElasticity, bool, bool, const IntRect&, const IntSize&) +{ +} + +void ScrollingCoordinator::setWheelEventHandlerCount(unsigned) +{ +} + +void ScrollingCoordinator::setShouldUpdateScrollLayerPositionOnMainThread(bool) +{ +} + +#endif // !ENABLE(THREADED_SCROLLING) + +} + diff --git a/Source/WebCore/page/scrolling/ScrollingThread.cpp b/Source/WebCore/page/scrolling/ScrollingThread.cpp index fc5a405da..f92969cc5 100644 --- a/Source/WebCore/page/scrolling/ScrollingThread.cpp +++ b/Source/WebCore/page/scrolling/ScrollingThread.cpp @@ -71,17 +71,16 @@ void ScrollingThread::createThreadIfNeeded() // Wait for the thread to initialize the run loop. { MutexLocker locker(m_initializeRunLoopConditionMutex); - +#if PLATFORM(MAC) while (!m_threadRunLoop) m_initializeRunLoopCondition.wait(m_initializeRunLoopConditionMutex); +#endif } } -void* ScrollingThread::threadCallback(void* scrollingThread) +void ScrollingThread::threadCallback(void* scrollingThread) { static_cast<ScrollingThread*>(scrollingThread)->threadBody(); - - return 0; } void ScrollingThread::threadBody() diff --git a/Source/WebCore/page/scrolling/ScrollingThread.h b/Source/WebCore/page/scrolling/ScrollingThread.h index 22671e4f5..d8dfa2de0 100644 --- a/Source/WebCore/page/scrolling/ScrollingThread.h +++ b/Source/WebCore/page/scrolling/ScrollingThread.h @@ -52,7 +52,7 @@ private: static ScrollingThread& shared(); void createThreadIfNeeded(); - static void* threadCallback(void* scrollingThread); + static void threadCallback(void* scrollingThread); void threadBody(); void dispatchFunctionsFromScrollingThread(); diff --git a/Source/WebCore/page/scrolling/ScrollingTree.cpp b/Source/WebCore/page/scrolling/ScrollingTree.cpp index bc1c50d9f..d58c62bd3 100644 --- a/Source/WebCore/page/scrolling/ScrollingTree.cpp +++ b/Source/WebCore/page/scrolling/ScrollingTree.cpp @@ -46,6 +46,10 @@ ScrollingTree::ScrollingTree(ScrollingCoordinator* scrollingCoordinator) : m_scrollingCoordinator(scrollingCoordinator) , m_rootNode(ScrollingTreeNode::create(this)) , m_hasWheelEventHandlers(false) + , m_canGoBack(false) + , m_canGoForward(false) + , m_mainFramePinnedToTheLeft(false) + , m_mainFramePinnedToTheRight(false) { } @@ -54,25 +58,36 @@ ScrollingTree::~ScrollingTree() ASSERT(!m_scrollingCoordinator); } -bool ScrollingTree::tryToHandleWheelEvent(const PlatformWheelEvent& wheelEvent) +ScrollingTree::EventResult ScrollingTree::tryToHandleWheelEvent(const PlatformWheelEvent& wheelEvent) { { MutexLocker lock(m_mutex); if (m_hasWheelEventHandlers) - return false; + return SendToMainThread; if (!m_nonFastScrollableRegion.isEmpty()) { // FIXME: This is not correct for non-default scroll origins. IntPoint position = wheelEvent.position(); position.moveBy(m_mainFrameScrollPosition); if (m_nonFastScrollableRegion.contains(position)) - return false; + return SendToMainThread; } } + if (willWheelEventStartSwipeGesture(wheelEvent)) + return DidNotHandleEvent; + ScrollingThread::dispatch(bind(&ScrollingTree::handleWheelEvent, this, wheelEvent)); - return true; + return DidHandleEvent; +} + +void ScrollingTree::updateBackForwardState(bool canGoBack, bool canGoForward) +{ + MutexLocker locker(m_swipeStateMutex); + + m_canGoBack = canGoBack; + m_canGoForward = canGoForward; } void ScrollingTree::handleWheelEvent(const PlatformWheelEvent& wheelEvent) @@ -82,13 +97,31 @@ void ScrollingTree::handleWheelEvent(const PlatformWheelEvent& wheelEvent) m_rootNode->handleWheelEvent(wheelEvent); } +void ScrollingTree::setMainFrameScrollPosition(const IntPoint& scrollPosition) +{ + ASSERT(ScrollingThread::isCurrentThread()); + + m_rootNode->setScrollPosition(scrollPosition); +} + +static void derefScrollingCoordinator(ScrollingCoordinator* scrollingCoordinator) +{ + ASSERT(isMainThread()); + + scrollingCoordinator->deref(); +} + void ScrollingTree::invalidate() { // Invalidate is dispatched by the ScrollingCoordinator class on the ScrollingThread // to break the reference cycle between ScrollingTree and ScrollingCoordinator when the // ScrollingCoordinator's page is destroyed. ASSERT(ScrollingThread::isCurrentThread()); - m_scrollingCoordinator = nullptr; + + // Since this can potentially be the last reference to the scrolling coordinator, + // we need to release it on the main thread since it has member variables (such as timers) + // that expect to be destroyed from the main thread. + callOnMainThread(bind(derefScrollingCoordinator, m_scrollingCoordinator.release().leakRef())); } void ScrollingTree::commitNewTreeState(PassOwnPtr<ScrollingTreeState> scrollingTreeState) @@ -107,6 +140,14 @@ void ScrollingTree::commitNewTreeState(PassOwnPtr<ScrollingTreeState> scrollingT m_rootNode->update(scrollingTreeState.get()); } +void ScrollingTree::setMainFramePinState(bool pinnedToTheLeft, bool pinnedToTheRight) +{ + MutexLocker locker(m_swipeStateMutex); + + m_mainFramePinnedToTheLeft = pinnedToTheLeft; + m_mainFramePinnedToTheRight = pinnedToTheRight; +} + void ScrollingTree::updateMainFrameScrollPosition(const IntPoint& scrollPosition) { if (!m_scrollingCoordinator) @@ -120,6 +161,50 @@ void ScrollingTree::updateMainFrameScrollPosition(const IntPoint& scrollPosition callOnMainThread(bind(&ScrollingCoordinator::updateMainFrameScrollPosition, m_scrollingCoordinator.get(), scrollPosition)); } +void ScrollingTree::updateMainFrameScrollPositionAndScrollLayerPosition(const IntPoint& scrollPosition) +{ + if (!m_scrollingCoordinator) + return; + + { + MutexLocker lock(m_mutex); + m_mainFrameScrollPosition = scrollPosition; + } + + callOnMainThread(bind(&ScrollingCoordinator::updateMainFrameScrollPositionAndScrollLayerPosition, m_scrollingCoordinator.get(), scrollPosition)); +} + +bool ScrollingTree::canGoBack() +{ + MutexLocker lock(m_swipeStateMutex); + + return m_canGoBack; +} + +bool ScrollingTree::canGoForward() +{ + MutexLocker lock(m_swipeStateMutex); + + return m_canGoForward; +} + +bool ScrollingTree::willWheelEventStartSwipeGesture(const PlatformWheelEvent& wheelEvent) +{ + if (wheelEvent.phase() != PlatformWheelEventPhaseBegan) + return false; + if (!wheelEvent.deltaX()) + return false; + + MutexLocker lock(m_swipeStateMutex); + + if (wheelEvent.deltaX() > 0 && m_mainFramePinnedToTheLeft && m_canGoBack) + return true; + if (wheelEvent.deltaX() < 0 && m_mainFramePinnedToTheRight && m_canGoForward) + return true; + + return false; +} + } // namespace WebCore #endif // ENABLE(THREADED_SCROLLING) diff --git a/Source/WebCore/page/scrolling/ScrollingTree.h b/Source/WebCore/page/scrolling/ScrollingTree.h index 76cbfda0a..00d2584c0 100644 --- a/Source/WebCore/page/scrolling/ScrollingTree.h +++ b/Source/WebCore/page/scrolling/ScrollingTree.h @@ -29,6 +29,7 @@ #if ENABLE(THREADED_SCROLLING) #include "Region.h" +#include <wtf/Functional.h> #include <wtf/OwnPtr.h> #include <wtf/PassOwnPtr.h> #include <wtf/PassRefPtr.h> @@ -52,18 +53,37 @@ public: static PassRefPtr<ScrollingTree> create(ScrollingCoordinator*); ~ScrollingTree(); + enum EventResult { + DidNotHandleEvent, + DidHandleEvent, + SendToMainThread + }; + // Can be called from any thread. Will try to handle the wheel event on the scrolling thread. // Returns true if the wheel event can be handled on the scrolling thread and false if the // event must be sent again to the WebCore event handler. - bool tryToHandleWheelEvent(const PlatformWheelEvent&); + EventResult tryToHandleWheelEvent(const PlatformWheelEvent&); + + // Can be called from any thread. Will update the back forward state of the page, used for rubber-banding. + void updateBackForwardState(bool canGoBack, bool canGoForward); // Must be called from the scrolling thread. Handles the wheel event. void handleWheelEvent(const PlatformWheelEvent&); + void setMainFrameScrollPosition(const IntPoint&); + void invalidate(); void commitNewTreeState(PassOwnPtr<ScrollingTreeState>); + void setMainFramePinState(bool pinnedToTheLeft, bool pinnedToTheRight); + void updateMainFrameScrollPosition(const IntPoint& scrollPosition); + void updateMainFrameScrollPositionAndScrollLayerPosition(const IntPoint& scrollPosition); + + bool canGoBack(); + bool canGoForward(); + + bool willWheelEventStartSwipeGesture(const PlatformWheelEvent&); private: explicit ScrollingTree(ScrollingCoordinator*); @@ -75,6 +95,12 @@ private: Region m_nonFastScrollableRegion; IntPoint m_mainFrameScrollPosition; bool m_hasWheelEventHandlers; + + Mutex m_swipeStateMutex; + bool m_canGoBack; + bool m_canGoForward; + bool m_mainFramePinnedToTheLeft; + bool m_mainFramePinnedToTheRight; }; } // namespace WebCore diff --git a/Source/WebCore/page/scrolling/ScrollingTreeNode.cpp b/Source/WebCore/page/scrolling/ScrollingTreeNode.cpp index bd94f00dc..8626b89ff 100644 --- a/Source/WebCore/page/scrolling/ScrollingTreeNode.cpp +++ b/Source/WebCore/page/scrolling/ScrollingTreeNode.cpp @@ -34,6 +34,11 @@ namespace WebCore { ScrollingTreeNode::ScrollingTreeNode(ScrollingTree* scrollingTree) : m_scrollingTree(scrollingTree) + , m_shouldUpdateScrollLayerPositionOnMainThread(false) + , m_horizontalScrollElasticity(ScrollElasticityNone) + , m_verticalScrollElasticity(ScrollElasticityNone) + , m_hasEnabledHorizontalScrollbar(false) + , m_hasEnabledVerticalScrollbar(false) { } @@ -48,6 +53,21 @@ void ScrollingTreeNode::update(ScrollingTreeState* state) if (state->changedProperties() & ScrollingTreeState::ContentsSize) m_contentsSize = state->contentsSize(); + + if (state->changedProperties() & ScrollingTreeState::ShouldUpdateScrollLayerPositionOnMainThread) + m_shouldUpdateScrollLayerPositionOnMainThread = state->shouldUpdateScrollLayerPositionOnMainThread(); + + if (state->changedProperties() & ScrollingTreeState::HorizontalScrollElasticity) + m_horizontalScrollElasticity = state->horizontalScrollElasticity(); + + if (state->changedProperties() & ScrollingTreeState::VerticalScrollElasticity) + m_verticalScrollElasticity = state->verticalScrollElasticity(); + + if (state->changedProperties() & ScrollingTreeState::HasEnabledHorizontalScrollbar) + m_hasEnabledHorizontalScrollbar = state->hasEnabledHorizontalScrollbar(); + + if (state->changedProperties() & ScrollingTreeState::HasEnabledVerticalScrollbar) + m_hasEnabledVerticalScrollbar = state->hasEnabledVerticalScrollbar(); } } // namespace WebCore diff --git a/Source/WebCore/page/scrolling/ScrollingTreeNode.h b/Source/WebCore/page/scrolling/ScrollingTreeNode.h index d26293a89..258783e7d 100644 --- a/Source/WebCore/page/scrolling/ScrollingTreeNode.h +++ b/Source/WebCore/page/scrolling/ScrollingTreeNode.h @@ -29,6 +29,7 @@ #if ENABLE(THREADED_SCROLLING) #include "IntRect.h" +#include "ScrollTypes.h" #include <wtf/PassOwnPtr.h> namespace WebCore { @@ -44,19 +45,37 @@ public: virtual void update(ScrollingTreeState*); virtual void handleWheelEvent(const PlatformWheelEvent&) = 0; + virtual void setScrollPosition(const IntPoint&) = 0; protected: explicit ScrollingTreeNode(ScrollingTree*); ScrollingTree* scrollingTree() const { return m_scrollingTree; } + const IntRect& viewportRect() const { return m_viewportRect; } const IntSize& contentsSize() const { return m_contentsSize; } + bool shouldUpdateScrollLayerPositionOnMainThread() const { return m_shouldUpdateScrollLayerPositionOnMainThread; } + + ScrollElasticity horizontalScrollElasticity() const { return m_horizontalScrollElasticity; } + ScrollElasticity verticalScrollElasticity() const { return m_verticalScrollElasticity; } + + bool hasEnabledHorizontalScrollbar() const { return m_hasEnabledHorizontalScrollbar; } + bool hasEnabledVerticalScrollbar() const { return m_hasEnabledVerticalScrollbar; } + private: ScrollingTree* m_scrollingTree; IntRect m_viewportRect; IntSize m_contentsSize; + + bool m_shouldUpdateScrollLayerPositionOnMainThread; + + ScrollElasticity m_horizontalScrollElasticity; + ScrollElasticity m_verticalScrollElasticity; + + bool m_hasEnabledHorizontalScrollbar; + bool m_hasEnabledVerticalScrollbar; }; } // namespace WebCore diff --git a/Source/WebCore/page/scrolling/ScrollingTreeState.cpp b/Source/WebCore/page/scrolling/ScrollingTreeState.cpp index c368152c3..ebf648d2b 100644 --- a/Source/WebCore/page/scrolling/ScrollingTreeState.cpp +++ b/Source/WebCore/page/scrolling/ScrollingTreeState.cpp @@ -38,6 +38,11 @@ PassOwnPtr<ScrollingTreeState> ScrollingTreeState::create() ScrollingTreeState::ScrollingTreeState() : m_changedProperties(0) , m_wheelEventHandlerCount(0) + , m_shouldUpdateScrollLayerPositionOnMainThread(false) + , m_horizontalScrollElasticity(ScrollElasticityNone) + , m_verticalScrollElasticity(ScrollElasticityNone) + , m_hasEnabledHorizontalScrollbar(false) + , m_hasEnabledVerticalScrollbar(false) { } @@ -81,6 +86,51 @@ void ScrollingTreeState::setWheelEventHandlerCount(unsigned wheelEventHandlerCou m_changedProperties |= WheelEventHandlerCount; } +void ScrollingTreeState::setShouldUpdateScrollLayerPositionOnMainThread(bool shouldUpdateScrollLayerPositionOnMainThread) +{ + if (m_shouldUpdateScrollLayerPositionOnMainThread == shouldUpdateScrollLayerPositionOnMainThread) + return; + + m_shouldUpdateScrollLayerPositionOnMainThread = shouldUpdateScrollLayerPositionOnMainThread; + m_changedProperties |= ShouldUpdateScrollLayerPositionOnMainThread; +} + +void ScrollingTreeState::setHorizontalScrollElasticity(ScrollElasticity horizontalScrollElasticity) +{ + if (m_horizontalScrollElasticity == horizontalScrollElasticity) + return; + + m_horizontalScrollElasticity = horizontalScrollElasticity; + m_changedProperties |= HorizontalScrollElasticity; +} + +void ScrollingTreeState::setVerticalScrollElasticity(ScrollElasticity verticalScrollElasticity) +{ + if (m_verticalScrollElasticity == verticalScrollElasticity) + return; + + m_verticalScrollElasticity = verticalScrollElasticity; + m_changedProperties |= VerticalScrollElasticity; +} + +void ScrollingTreeState::setHasEnabledHorizontalScrollbar(bool hasEnabledHorizontalScrollbar) +{ + if (m_hasEnabledHorizontalScrollbar == hasEnabledHorizontalScrollbar) + return; + + m_hasEnabledHorizontalScrollbar = hasEnabledHorizontalScrollbar; + m_changedProperties |= HasEnabledHorizontalScrollbar; +} + +void ScrollingTreeState::setHasEnabledVerticalScrollbar(bool hasEnabledVerticalScrollbar) +{ + if (m_hasEnabledVerticalScrollbar == hasEnabledVerticalScrollbar) + return; + + m_hasEnabledVerticalScrollbar = hasEnabledVerticalScrollbar; + m_changedProperties |= HasEnabledVerticalScrollbar; +} + PassOwnPtr<ScrollingTreeState> ScrollingTreeState::commit() { OwnPtr<ScrollingTreeState> treeState = adoptPtr(new ScrollingTreeState(*this)); diff --git a/Source/WebCore/page/scrolling/ScrollingTreeState.h b/Source/WebCore/page/scrolling/ScrollingTreeState.h index fde884922..97cc77a94 100644 --- a/Source/WebCore/page/scrolling/ScrollingTreeState.h +++ b/Source/WebCore/page/scrolling/ScrollingTreeState.h @@ -31,6 +31,7 @@ #include "GraphicsLayer.h" #include "IntRect.h" #include "Region.h" +#include "ScrollTypes.h" #include <wtf/PassOwnPtr.h> #if PLATFORM(MAC) @@ -53,7 +54,12 @@ public: ContentsSize = 1 << 1, NonFastScrollableRegion = 1 << 2, WheelEventHandlerCount = 1 << 3, - ScrollLayer = 1 << 4, + ShouldUpdateScrollLayerPositionOnMainThread = 1 << 4, + HorizontalScrollElasticity = 1 << 5, + VerticalScrollElasticity = 1 << 6, + HasEnabledHorizontalScrollbar = 1 << 7, + HasEnabledVerticalScrollbar = 1 << 8, + ScrollLayer = 1 << 9, }; bool hasChangedProperties() const { return m_changedProperties; } @@ -71,6 +77,21 @@ public: unsigned wheelEventHandlerCount() const { return m_wheelEventHandlerCount; } void setWheelEventHandlerCount(unsigned); + bool shouldUpdateScrollLayerPositionOnMainThread() const { return m_shouldUpdateScrollLayerPositionOnMainThread; } + void setShouldUpdateScrollLayerPositionOnMainThread(bool); + + ScrollElasticity horizontalScrollElasticity() const { return m_horizontalScrollElasticity; } + void setHorizontalScrollElasticity(ScrollElasticity); + + ScrollElasticity verticalScrollElasticity() const { return m_verticalScrollElasticity; } + void setVerticalScrollElasticity(ScrollElasticity); + + bool hasEnabledHorizontalScrollbar() const { return m_hasEnabledHorizontalScrollbar; } + void setHasEnabledHorizontalScrollbar(bool); + + bool hasEnabledVerticalScrollbar() const { return m_hasEnabledVerticalScrollbar; } + void setHasEnabledVerticalScrollbar(bool); + PlatformLayer* platformScrollLayer() const; void setScrollLayer(const GraphicsLayer*); @@ -89,6 +110,14 @@ private: unsigned m_wheelEventHandlerCount; + bool m_shouldUpdateScrollLayerPositionOnMainThread; + + ScrollElasticity m_horizontalScrollElasticity; + ScrollElasticity m_verticalScrollElasticity; + + bool m_hasEnabledHorizontalScrollbar; + bool m_hasEnabledVerticalScrollbar; + #if PLATFORM(MAC) RetainPtr<PlatformLayer> m_platformScrollLayer; #endif diff --git a/Source/WebCore/page/scrolling/chromium/ScrollingCoordinatorChromium.cpp b/Source/WebCore/page/scrolling/chromium/ScrollingCoordinatorChromium.cpp new file mode 100644 index 000000000..e590138a6 --- /dev/null +++ b/Source/WebCore/page/scrolling/chromium/ScrollingCoordinatorChromium.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2012 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include "ScrollingCoordinator.h" + +#include "LayerChromium.h" +#include "Region.h" + +namespace WebCore { + +class ScrollingCoordinatorPrivate { +WTF_MAKE_NONCOPYABLE(ScrollingCoordinatorPrivate); +public: + ScrollingCoordinatorPrivate() { } + ~ScrollingCoordinatorPrivate() { } + + void setScrollLayer(LayerChromium* layer) { m_scrollLayer = layer; } + LayerChromium* scrollLayer() const { return m_scrollLayer.get(); } + +private: + RefPtr<LayerChromium> m_scrollLayer; +}; + +PassRefPtr<ScrollingCoordinator> ScrollingCoordinator::create(Page* page) +{ + RefPtr<ScrollingCoordinator> coordinator(adoptRef(new ScrollingCoordinator(page))); + coordinator->m_private = new ScrollingCoordinatorPrivate; + return coordinator.release(); +} + +ScrollingCoordinator::~ScrollingCoordinator() +{ + ASSERT(!m_page); + delete m_private; +} + +void ScrollingCoordinator::frameViewHorizontalScrollbarLayerDidChange(FrameView*, GraphicsLayer* horizontalScrollbarLayer) +{ + // FIXME: Implement! +} + +void ScrollingCoordinator::frameViewVerticalScrollbarLayerDidChange(FrameView*, GraphicsLayer* verticalScrollbarLayer) +{ + // FIXME: Implement! +} + +void ScrollingCoordinator::setScrollLayer(GraphicsLayer* scrollLayer) +{ + m_private->setScrollLayer(scrollLayer ? scrollLayer->platformLayer() : 0); +} + +void ScrollingCoordinator::setNonFastScrollableRegion(const Region&) +{ + // FIXME: Implement! +} + +void ScrollingCoordinator::setScrollParameters(ScrollElasticity horizontalScrollElasticity, ScrollElasticity verticalScrollElasticity, + bool hasEnabledHorizontalScrollbar, bool hasEnabledVerticalScrollbar, + const IntRect& viewportRect, const IntSize& contentsSize) +{ + // FIXME: Implement! +} + +void ScrollingCoordinator::setWheelEventHandlerCount(unsigned wheelEventHandlerCount) +{ + if (LayerChromium* layer = m_private->scrollLayer()) + layer->setHaveWheelEventHandlers(wheelEventHandlerCount > 0); +} + +void ScrollingCoordinator::setShouldUpdateScrollLayerPositionOnMainThread(bool should) +{ + // FIXME: Implement! +} + +} diff --git a/Source/WebCore/page/scrolling/mac/ScrollingCoordinatorMac.mm b/Source/WebCore/page/scrolling/mac/ScrollingCoordinatorMac.mm index 31fa76683..94335b0b2 100644 --- a/Source/WebCore/page/scrolling/mac/ScrollingCoordinatorMac.mm +++ b/Source/WebCore/page/scrolling/mac/ScrollingCoordinatorMac.mm @@ -42,6 +42,20 @@ namespace WebCore { +class ScrollingCoordinatorPrivate { +}; + +PassRefPtr<ScrollingCoordinator> ScrollingCoordinator::create(Page* page) +{ + return adoptRef(new ScrollingCoordinator(page)); +} + +ScrollingCoordinator::~ScrollingCoordinator() +{ + ASSERT(!m_page); + ASSERT(!m_scrollingTree); +} + void ScrollingCoordinator::frameViewHorizontalScrollbarLayerDidChange(FrameView* frameView, GraphicsLayer*) { ASSERT(isMainThread()); @@ -64,18 +78,6 @@ void ScrollingCoordinator::frameViewVerticalScrollbarLayerDidChange(FrameView* f // FIXME: Implement. } -void ScrollingCoordinator::frameViewScrollLayerDidChange(FrameView* frameView, const GraphicsLayer* scrollLayer) -{ - ASSERT(isMainThread()); - ASSERT(m_page); - - if (frameView->frame() != m_page->mainFrame()) - return; - - m_scrollingTreeState->setScrollLayer(scrollLayer); - scheduleTreeStateCommit(); -} - } // namespace WebCore #endif // ENABLE(THREADED_SCROLLING) diff --git a/Source/WebCore/page/scrolling/mac/ScrollingTreeNodeMac.h b/Source/WebCore/page/scrolling/mac/ScrollingTreeNodeMac.h index b1ae6ee76..97b3d22bc 100644 --- a/Source/WebCore/page/scrolling/mac/ScrollingTreeNodeMac.h +++ b/Source/WebCore/page/scrolling/mac/ScrollingTreeNodeMac.h @@ -28,6 +28,7 @@ #if ENABLE(THREADED_SCROLLING) +#include "ScrollElasticityController.h" #include "ScrollingTreeNode.h" #include <wtf/RetainPtr.h> @@ -35,18 +36,44 @@ OBJC_CLASS CALayer; namespace WebCore { -class ScrollingTreeNodeMac : public ScrollingTreeNode { +class ScrollingTreeNodeMac : public ScrollingTreeNode, private ScrollElasticityControllerClient { public: explicit ScrollingTreeNodeMac(ScrollingTree*); + virtual ~ScrollingTreeNodeMac(); private: + // ScrollingTreeNode member functions. virtual void update(ScrollingTreeState*) OVERRIDE; virtual void handleWheelEvent(const PlatformWheelEvent&) OVERRIDE; + virtual void setScrollPosition(const IntPoint&) OVERRIDE; + + // ScrollElasticityController member functions. + virtual bool allowsHorizontalStretching() OVERRIDE; + virtual bool allowsVerticalStretching() OVERRIDE; + virtual IntSize stretchAmount() OVERRIDE; + virtual bool pinnedInDirection(const FloatSize&) OVERRIDE; + virtual bool canScrollHorizontally() OVERRIDE; + virtual bool canScrollVertically() OVERRIDE; + virtual bool shouldRubberBandInDirection(ScrollDirection) OVERRIDE; + virtual IntPoint absoluteScrollPosition() OVERRIDE; + virtual void immediateScrollBy(const FloatSize&) OVERRIDE; + virtual void immediateScrollByWithoutContentEdgeConstraints(const FloatSize&) OVERRIDE; + virtual void startSnapRubberbandTimer() OVERRIDE; + virtual void stopSnapRubberbandTimer() OVERRIDE; IntPoint scrollPosition() const; - void setScrollPosition(const IntPoint&); + void setScrollLayerPosition(const IntPoint&); + + IntPoint minimumScrollPosition() const; + IntPoint maximumScrollPosition() const; void scrollBy(const IntSize&); + void scrollByWithoutContentEdgeConstraints(const IntSize&); + + void updateMainFramePinState(const IntPoint& scrollPosition); + + ScrollElasticityController m_scrollElasticityController; + RetainPtr<CFRunLoopTimerRef> m_snapRubberbandTimer; RetainPtr<CALayer> m_scrollLayer; }; diff --git a/Source/WebCore/page/scrolling/mac/ScrollingTreeNodeMac.mm b/Source/WebCore/page/scrolling/mac/ScrollingTreeNodeMac.mm index 76a8e3eaf..966b2e18f 100644 --- a/Source/WebCore/page/scrolling/mac/ScrollingTreeNodeMac.mm +++ b/Source/WebCore/page/scrolling/mac/ScrollingTreeNodeMac.mm @@ -41,21 +41,175 @@ PassOwnPtr<ScrollingTreeNode> ScrollingTreeNode::create(ScrollingTree* scrolling ScrollingTreeNodeMac::ScrollingTreeNodeMac(ScrollingTree* scrollingTree) : ScrollingTreeNode(scrollingTree) + , m_scrollElasticityController(this) { } +ScrollingTreeNodeMac::~ScrollingTreeNodeMac() +{ + if (m_snapRubberbandTimer) + CFRunLoopTimerInvalidate(m_snapRubberbandTimer.get()); +} + void ScrollingTreeNodeMac::update(ScrollingTreeState* state) { ScrollingTreeNode::update(state); if (state->changedProperties() & ScrollingTreeState::ScrollLayer) m_scrollLayer = state->platformScrollLayer(); + + if (state->changedProperties() & (ScrollingTreeState::ScrollLayer | ScrollingTreeState::ContentsSize | ScrollingTreeState::ViewportRect)) + updateMainFramePinState(scrollPosition()); } void ScrollingTreeNodeMac::handleWheelEvent(const PlatformWheelEvent& wheelEvent) { - // FXIME: This needs to handle rubberbanding. - scrollBy(IntSize(-wheelEvent.deltaX(), -wheelEvent.deltaY())); + m_scrollElasticityController.handleWheelEvent(wheelEvent); +} + +void ScrollingTreeNodeMac::setScrollPosition(const IntPoint& scrollPosition) +{ + updateMainFramePinState(scrollPosition); + + if (shouldUpdateScrollLayerPositionOnMainThread()) { + scrollingTree()->updateMainFrameScrollPositionAndScrollLayerPosition(scrollPosition); + return; + } + + setScrollLayerPosition(scrollPosition); + scrollingTree()->updateMainFrameScrollPosition(scrollPosition); +} + +bool ScrollingTreeNodeMac::allowsHorizontalStretching() +{ + switch (horizontalScrollElasticity()) { + case ScrollElasticityAutomatic: + return hasEnabledHorizontalScrollbar() || !hasEnabledVerticalScrollbar(); + case ScrollElasticityNone: + return false; + case ScrollElasticityAllowed: + return true; + } + + ASSERT_NOT_REACHED(); + return false; +} + +bool ScrollingTreeNodeMac::allowsVerticalStretching() +{ + switch (verticalScrollElasticity()) { + case ScrollElasticityAutomatic: + return hasEnabledVerticalScrollbar() || !hasEnabledHorizontalScrollbar(); + case ScrollElasticityNone: + return false; + case ScrollElasticityAllowed: + return true; + } + + ASSERT_NOT_REACHED(); + return false; +} + +IntSize ScrollingTreeNodeMac::stretchAmount() +{ + IntSize stretch; + + if (scrollPosition().y() < minimumScrollPosition().y()) + stretch.setHeight(scrollPosition().y() - minimumScrollPosition().y()); + else if (scrollPosition().y() > maximumScrollPosition().y()) + stretch.setHeight(scrollPosition().y() - maximumScrollPosition().y()); + + if (scrollPosition().x() < minimumScrollPosition().x()) + stretch.setWidth(scrollPosition().x() - minimumScrollPosition().x()); + else if (scrollPosition().x() > maximumScrollPosition().x()) + stretch.setWidth(scrollPosition().x() - maximumScrollPosition().x()); + + return stretch; +} + +bool ScrollingTreeNodeMac::pinnedInDirection(const FloatSize& delta) +{ + FloatSize limitDelta; + + if (fabsf(delta.height()) >= fabsf(delta.width())) { + if (delta.height() < 0) { + // We are trying to scroll up. Make sure we are not pinned to the top + limitDelta.setHeight(scrollPosition().y() - minimumScrollPosition().y()); + } else { + // We are trying to scroll down. Make sure we are not pinned to the bottom + limitDelta.setHeight(maximumScrollPosition().y() - scrollPosition().y()); + } + } else if (delta.width()) { + if (delta.width() < 0) { + // We are trying to scroll left. Make sure we are not pinned to the left + limitDelta.setHeight(scrollPosition().x() - minimumScrollPosition().x()); + } else { + // We are trying to scroll right. Make sure we are not pinned to the right + limitDelta.setHeight(maximumScrollPosition().x() - scrollPosition().x()); + } + } + + if ((delta.width() || delta.height()) && (limitDelta.width() < 1 && limitDelta.height() < 1)) + return true; + + return false; +} + +bool ScrollingTreeNodeMac::canScrollHorizontally() +{ + return hasEnabledHorizontalScrollbar(); +} + +bool ScrollingTreeNodeMac::canScrollVertically() +{ + return hasEnabledVerticalScrollbar(); +} + +bool ScrollingTreeNodeMac::shouldRubberBandInDirection(ScrollDirection direction) +{ + if (direction == ScrollLeft) + return !scrollingTree()->canGoBack(); + if (direction == ScrollRight) + return !scrollingTree()->canGoForward(); + + ASSERT_NOT_REACHED(); + return false; +} + +IntPoint ScrollingTreeNodeMac::absoluteScrollPosition() +{ + return scrollPosition(); +} + +void ScrollingTreeNodeMac::immediateScrollBy(const FloatSize& offset) +{ + scrollBy(roundedIntSize(offset)); +} + +void ScrollingTreeNodeMac::immediateScrollByWithoutContentEdgeConstraints(const FloatSize& offset) +{ + scrollByWithoutContentEdgeConstraints(roundedIntSize(offset)); +} + +void ScrollingTreeNodeMac::startSnapRubberbandTimer() +{ + ASSERT(!m_snapRubberbandTimer); + + CFTimeInterval timerInterval = 1.0 / 60.0; + + m_snapRubberbandTimer = adoptCF(CFRunLoopTimerCreateWithHandler(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + timerInterval, timerInterval, 0, 0, ^(CFRunLoopTimerRef) { + m_scrollElasticityController.snapRubberBandTimerFired(); + })); + CFRunLoopAddTimer(CFRunLoopGetCurrent(), m_snapRubberbandTimer.get(), kCFRunLoopDefaultMode); +} + +void ScrollingTreeNodeMac::stopSnapRubberbandTimer() +{ + if (!m_snapRubberbandTimer) + return; + + CFRunLoopTimerInvalidate(m_snapRubberbandTimer.get()); + m_snapRubberbandTimer = nullptr; } IntPoint ScrollingTreeNodeMac::scrollPosition() const @@ -64,16 +218,49 @@ IntPoint ScrollingTreeNodeMac::scrollPosition() const return IntPoint(-scrollLayerPosition.x, -scrollLayerPosition.y); } -void ScrollingTreeNodeMac::setScrollPosition(const IntPoint& position) +void ScrollingTreeNodeMac::setScrollLayerPosition(const IntPoint& position) { + ASSERT(!shouldUpdateScrollLayerPositionOnMainThread()); m_scrollLayer.get().position = CGPointMake(-position.x(), -position.y()); } -void ScrollingTreeNodeMac::scrollBy(const IntSize &offset) +IntPoint ScrollingTreeNodeMac::minimumScrollPosition() const +{ + // FIXME: This should take the scroll origin into account. + return IntPoint(0, 0); +} + +IntPoint ScrollingTreeNodeMac::maximumScrollPosition() const +{ + // FIXME: This should take the scroll origin into account. + IntPoint position(contentsSize().width() - viewportRect().width(), + contentsSize().height() - viewportRect().height()); + + position.clampNegativeToZero(); + + return position; +} + +void ScrollingTreeNodeMac::scrollBy(const IntSize& offset) +{ + IntPoint newScrollPosition = scrollPosition() + offset; + newScrollPosition = newScrollPosition.shrunkTo(maximumScrollPosition()); + newScrollPosition = newScrollPosition.expandedTo(minimumScrollPosition()); + + setScrollPosition(newScrollPosition); +} + +void ScrollingTreeNodeMac::scrollByWithoutContentEdgeConstraints(const IntSize& offset) { setScrollPosition(scrollPosition() + offset); +} + +void ScrollingTreeNodeMac::updateMainFramePinState(const IntPoint& scrollPosition) +{ + bool pinnedToTheLeft = scrollPosition.x() <= minimumScrollPosition().x(); + bool pinnedToTheRight = scrollPosition.x() >= maximumScrollPosition().x(); - scrollingTree()->updateMainFrameScrollPosition(scrollPosition()); + scrollingTree()->setMainFramePinState(pinnedToTheLeft, pinnedToTheRight); } } // namespace WebCore |