Add AsyncScrollingCoordinator, which is a base class for threaded and future remote ScrollingCoordinators
https://bugs.webkit.org/show_bug.cgi?id=126389
Source/WebCore:
Reviewed by Tim Horton.
Add AsyncScrollingCoordinator, a ScrollingCoordinator that knows about ScrollingStateTrees
and ScrollingTrees, but leaves it up to subclasses to decide when and how to commit.
* WebCore.xcodeproj/project.pbxproj: Added AsyncScrollingCoordinator.*
* page/scrolling/AsyncScrollingCoordinator.cpp: Copied from Source/WebCore/page/scrolling/mac/ScrollingCoordinatorMac.mm.
(WebCore::AsyncScrollingCoordinator::AsyncScrollingCoordinator):
(WebCore::AsyncScrollingCoordinator::~AsyncScrollingCoordinator):
(WebCore::AsyncScrollingCoordinator::frameViewLayoutUpdated):
(WebCore::AsyncScrollingCoordinator::frameViewRootLayerDidChange):
(WebCore::AsyncScrollingCoordinator::requestScrollPositionUpdate):
(WebCore::AsyncScrollingCoordinator::scrollableAreaScrollbarLayerDidChange):
(WebCore::AsyncScrollingCoordinator::attachToStateTree):
(WebCore::AsyncScrollingCoordinator::detachFromStateTree):
(WebCore::AsyncScrollingCoordinator::clearStateTree):
(WebCore::AsyncScrollingCoordinator::syncChildPositions):
(WebCore::AsyncScrollingCoordinator::ensureRootStateNodeForFrameView):
(WebCore::AsyncScrollingCoordinator::updateScrollingNode):
(WebCore::AsyncScrollingCoordinator::updateViewportConstrainedNode):
(WebCore::AsyncScrollingCoordinator::setScrollLayerForNode):
(WebCore::AsyncScrollingCoordinator::setCounterScrollingLayerForNode):
(WebCore::AsyncScrollingCoordinator::setHeaderLayerForNode):
(WebCore::AsyncScrollingCoordinator::setFooterLayerForNode):
(WebCore::AsyncScrollingCoordinator::setNonFastScrollableRegionForNode):
(WebCore::AsyncScrollingCoordinator::setWheelEventHandlerCountForNode):
(WebCore::AsyncScrollingCoordinator::setScrollBehaviorForFixedElementsForNode):
(WebCore::AsyncScrollingCoordinator::setScrollbarPaintersFromScrollbarsForNode):
(WebCore::AsyncScrollingCoordinator::setSynchronousScrollingReasons):
(WebCore::AsyncScrollingCoordinator::updateMainFrameScrollLayerPosition):
(WebCore::AsyncScrollingCoordinator::recomputeWheelEventHandlerCountForFrameView):
(WebCore::AsyncScrollingCoordinator::isRubberBandInProgress):
(WebCore::AsyncScrollingCoordinator::setScrollPinningBehavior):
(WebCore::AsyncScrollingCoordinator::scrollingStateTreeAsText):
* page/scrolling/AsyncScrollingCoordinator.h: Copied from Source/WebCore/page/scrolling/mac/ScrollingCoordinatorMac.h.
(WebCore::AsyncScrollingCoordinator::scrollingTree):
(WebCore::AsyncScrollingCoordinator::setScrollingTree):
(WebCore::AsyncScrollingCoordinator::scrollingStateTree):
(WebCore::AsyncScrollingCoordinator::releaseScrollingTree):
* page/scrolling/ScrollingCoordinator.h: Add casting support.
(WebCore::ScrollingCoordinator::isAsyncScrollingCoordinator):
* page/scrolling/ThreadedScrollingTree.h: commitNewTreeState() needs to be public.
* page/scrolling/mac/ScrollingCoordinatorMac.h:
* page/scrolling/mac/ScrollingCoordinatorMac.mm: Lots of code moved to AsyncScrollingCoordinator.
(WebCore::ScrollingCoordinatorMac::ScrollingCoordinatorMac):
(WebCore::ScrollingCoordinatorMac::~ScrollingCoordinatorMac):
(WebCore::ScrollingCoordinatorMac::pageDestroyed):
(WebCore::ScrollingCoordinatorMac::commitTreeStateIfNeeded):
(WebCore::ScrollingCoordinatorMac::handleWheelEvent):
(WebCore::ScrollingCoordinatorMac::scheduleTreeStateCommit):
(WebCore::ScrollingCoordinatorMac::commitTreeState):
(WebCore::ScrollingCoordinatorMac::updateTiledScrollingIndicator):
Source/WebKit2:
Reviewed by Tim Horton.
Add AsyncScrollingCoordinator, a ScrollingCoordinator that knows about ScrollingStateTrees
and ScrollingTrees, but leaves it up to subclasses to decide when and how to commit.
* WebProcess/WebPage/EventDispatcher.cpp:
(WebKit::EventDispatcher::addScrollingTreeForPage): The ScrollingTree is exposed on
AsyncScrollingCoordinator now, not ScrollingCoordinator, so we have to cast here.
* WebProcess/WebPage/WebPage.cpp: m_useThreadedScrolling -> m_useAsyncScrolling terminology change.
(WebKit::WebPage::WebPage):
(WebKit::WebPage::~WebPage):
* WebProcess/WebPage/WebPage.h:
* WebProcess/WebPage/mac/TiledCoreAnimationDrawingArea.mm:
(WebKit::TiledCoreAnimationDrawingArea::updatePreferences): Cast to AsyncScrollingCoordinator in order
to get to the ScrollingTree.
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@161212 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/WebCore/page/scrolling/AsyncScrollingCoordinator.cpp b/Source/WebCore/page/scrolling/AsyncScrollingCoordinator.cpp
new file mode 100644
index 0000000..700d510
--- /dev/null
+++ b/Source/WebCore/page/scrolling/AsyncScrollingCoordinator.cpp
@@ -0,0 +1,351 @@
+/*
+ * Copyright (C) 2014 Apple 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"
+
+#if ENABLE(ASYNC_SCROLLING)
+#include "AsyncScrollingCoordinator.h"
+
+#include "Frame.h"
+#include "FrameView.h"
+#include "GraphicsLayer.h"
+#include "MainFrame.h"
+#include "Page.h"
+#include "ScrollingConstraints.h"
+#include "ScrollingStateFixedNode.h"
+#include "ScrollingStateScrollingNode.h"
+#include "ScrollingStateStickyNode.h"
+#include "ScrollingStateTree.h"
+
+namespace WebCore {
+
+AsyncScrollingCoordinator::AsyncScrollingCoordinator(Page* page)
+ : ScrollingCoordinator(page)
+ , m_scrollingStateTree(ScrollingStateTree::create())
+{
+}
+
+AsyncScrollingCoordinator::~AsyncScrollingCoordinator()
+{
+}
+
+void AsyncScrollingCoordinator::frameViewLayoutUpdated(FrameView* frameView)
+{
+ ASSERT(isMainThread());
+ ASSERT(m_page);
+
+ // If there isn't a root node yet, don't do anything. We'll be called again after creating one.
+ if (!m_scrollingStateTree->rootStateNode())
+ return;
+
+ // 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(), IntPoint());
+
+ // In the future, we may want to have the ability to set non-fast scrolling regions for more than
+ // just the root node. But right now, this concept only applies to the root.
+ setNonFastScrollableRegionForNode(nonFastScrollableRegion, m_scrollingStateTree->rootStateNode());
+
+ if (!coordinatesScrollingForFrameView(frameView))
+ return;
+
+ ScrollingStateScrollingNode* node = toScrollingStateScrollingNode(m_scrollingStateTree->stateNodeForID(frameView->scrollLayerID()));
+ if (!node)
+ return;
+
+ Scrollbar* verticalScrollbar = frameView->verticalScrollbar();
+ Scrollbar* horizontalScrollbar = frameView->horizontalScrollbar();
+ setScrollbarPaintersFromScrollbarsForNode(verticalScrollbar, horizontalScrollbar, node);
+
+ node->setFrameScaleFactor(frameView->frame().frameScaleFactor());
+ node->setHeaderHeight(frameView->headerHeight());
+ node->setFooterHeight(frameView->footerHeight());
+
+ node->setScrollOrigin(frameView->scrollOrigin());
+ node->setViewportRect(IntRect(IntPoint(), frameView->visibleContentRect().size()));
+ node->setTotalContentsSize(frameView->totalContentsSize());
+
+ ScrollableAreaParameters scrollParameters;
+ scrollParameters.horizontalScrollElasticity = frameView->horizontalScrollElasticity();
+ scrollParameters.verticalScrollElasticity = frameView->verticalScrollElasticity();
+ scrollParameters.hasEnabledHorizontalScrollbar = horizontalScrollbar && horizontalScrollbar->enabled();
+ scrollParameters.hasEnabledVerticalScrollbar = verticalScrollbar && verticalScrollbar->enabled();
+ scrollParameters.horizontalScrollbarMode = frameView->horizontalScrollbarMode();
+ scrollParameters.verticalScrollbarMode = frameView->verticalScrollbarMode();
+
+ node->setScrollableAreaParameters(scrollParameters);
+ scheduleTreeStateCommit();
+}
+
+void AsyncScrollingCoordinator::frameViewRootLayerDidChange(FrameView* frameView)
+{
+ ASSERT(isMainThread());
+ ASSERT(m_page);
+
+ if (!coordinatesScrollingForFrameView(frameView))
+ return;
+
+ // If the root layer does not have a ScrollingStateNode, then we should create one.
+ ensureRootStateNodeForFrameView(frameView);
+ ASSERT(m_scrollingStateTree->rootStateNode());
+
+ ScrollingCoordinator::frameViewRootLayerDidChange(frameView);
+
+ ScrollingStateScrollingNode* node = toScrollingStateScrollingNode(m_scrollingStateTree->stateNodeForID(frameView->scrollLayerID()));
+ setScrollLayerForNode(scrollLayerForFrameView(frameView), node);
+ setCounterScrollingLayerForNode(counterScrollingLayerForFrameView(frameView), node);
+ setHeaderLayerForNode(headerLayerForFrameView(frameView), node);
+ setFooterLayerForNode(footerLayerForFrameView(frameView), node);
+ setScrollBehaviorForFixedElementsForNode(frameView->scrollBehaviorForFixedElements(), node);
+}
+
+bool AsyncScrollingCoordinator::requestScrollPositionUpdate(FrameView* frameView, const IntPoint& scrollPosition)
+{
+ ASSERT(isMainThread());
+ ASSERT(m_page);
+
+ if (!coordinatesScrollingForFrameView(frameView))
+ return false;
+
+ if (frameView->inProgrammaticScroll() || frameView->frame().document()->inPageCache())
+ updateMainFrameScrollPosition(scrollPosition, frameView->inProgrammaticScroll(), SetScrollingLayerPosition);
+
+ // If this frame view's document is being put into the page cache, we don't want to update our
+ // main frame scroll position. Just let the FrameView think that we did.
+ if (frameView->frame().document()->inPageCache())
+ return true;
+
+ ScrollingStateScrollingNode* stateNode = toScrollingStateScrollingNode(m_scrollingStateTree->stateNodeForID(frameView->scrollLayerID()));
+ if (!stateNode)
+ return false;
+
+ stateNode->setRequestedScrollPosition(scrollPosition, frameView->inProgrammaticScroll());
+ scheduleTreeStateCommit();
+ return true;
+}
+
+void AsyncScrollingCoordinator::scrollableAreaScrollbarLayerDidChange(ScrollableArea* scrollableArea, ScrollbarOrientation orientation)
+{
+ ASSERT(isMainThread());
+ ASSERT(m_page);
+
+ if (scrollableArea != static_cast<ScrollableArea*>(m_page->mainFrame().view()))
+ return;
+
+ if (orientation == VerticalScrollbar)
+ scrollableArea->verticalScrollbarLayerDidChange();
+ else
+ scrollableArea->horizontalScrollbarLayerDidChange();
+}
+
+ScrollingNodeID AsyncScrollingCoordinator::attachToStateTree(ScrollingNodeType nodeType, ScrollingNodeID newNodeID, ScrollingNodeID parentID)
+{
+ return m_scrollingStateTree->attachNode(nodeType, newNodeID, parentID);
+}
+
+void AsyncScrollingCoordinator::detachFromStateTree(ScrollingNodeID nodeID)
+{
+ m_scrollingStateTree->detachNode(nodeID);
+}
+
+void AsyncScrollingCoordinator::clearStateTree()
+{
+ m_scrollingStateTree->clear();
+}
+
+void AsyncScrollingCoordinator::syncChildPositions(const LayoutRect& viewportRect)
+{
+ if (!m_scrollingStateTree->rootStateNode())
+ return;
+
+ Vector<OwnPtr<ScrollingStateNode>>* children = m_scrollingStateTree->rootStateNode()->children();
+ if (!children)
+ return;
+
+ // FIXME: We'll have to traverse deeper into the tree at some point.
+ size_t size = children->size();
+ for (size_t i = 0; i < size; ++i) {
+ ScrollingStateNode* child = children->at(i).get();
+ child->syncLayerPositionForViewportRect(viewportRect);
+ }
+}
+
+void AsyncScrollingCoordinator::ensureRootStateNodeForFrameView(FrameView* frameView)
+{
+ ASSERT(frameView->scrollLayerID());
+ attachToStateTree(ScrollingNode, frameView->scrollLayerID(), 0);
+}
+
+void AsyncScrollingCoordinator::updateScrollingNode(ScrollingNodeID nodeID, GraphicsLayer* scrollLayer, GraphicsLayer* counterScrollingLayer)
+{
+ ScrollingStateScrollingNode* node = toScrollingStateScrollingNode(m_scrollingStateTree->stateNodeForID(nodeID));
+ ASSERT(node);
+ if (!node)
+ return;
+
+ node->setScrollLayer(scrollLayer);
+ node->setCounterScrollingLayer(counterScrollingLayer);
+ scheduleTreeStateCommit();
+}
+
+void AsyncScrollingCoordinator::updateViewportConstrainedNode(ScrollingNodeID nodeID, const ViewportConstraints& constraints, GraphicsLayer* graphicsLayer)
+{
+ ASSERT(supportsFixedPositionLayers());
+
+ ScrollingStateNode* node = m_scrollingStateTree->stateNodeForID(nodeID);
+ if (!node)
+ return;
+
+ switch (constraints.constraintType()) {
+ case ViewportConstraints::FixedPositionConstraint: {
+ ScrollingStateFixedNode* fixedNode = toScrollingStateFixedNode(node);
+ setScrollLayerForNode(graphicsLayer, fixedNode);
+ fixedNode->updateConstraints((const FixedPositionViewportConstraints&)constraints);
+ break;
+ }
+ case ViewportConstraints::StickyPositionConstraint: {
+ ScrollingStateStickyNode* stickyNode = toScrollingStateStickyNode(node);
+ setScrollLayerForNode(graphicsLayer, stickyNode);
+ stickyNode->updateConstraints((const StickyPositionViewportConstraints&)constraints);
+ break;
+ }
+ }
+ scheduleTreeStateCommit();
+}
+
+void AsyncScrollingCoordinator::setScrollLayerForNode(GraphicsLayer* scrollLayer, ScrollingStateNode* node)
+{
+ node->setScrollLayer(scrollLayer);
+ scheduleTreeStateCommit();
+}
+
+void AsyncScrollingCoordinator::setCounterScrollingLayerForNode(GraphicsLayer* layer, ScrollingStateScrollingNode* node)
+{
+ node->setCounterScrollingLayer(layer);
+ scheduleTreeStateCommit();
+}
+
+void AsyncScrollingCoordinator::setHeaderLayerForNode(GraphicsLayer* headerLayer, ScrollingStateScrollingNode* node)
+{
+ // Headers and footers are only supported on the root node.
+ ASSERT(node == m_scrollingStateTree->rootStateNode());
+
+ node->setHeaderLayer(headerLayer);
+ scheduleTreeStateCommit();
+}
+
+void AsyncScrollingCoordinator::setFooterLayerForNode(GraphicsLayer* footerLayer, ScrollingStateScrollingNode* node)
+{
+ // Headers and footers are only supported on the root node.
+ ASSERT(node == m_scrollingStateTree->rootStateNode());
+
+ node->setFooterLayer(footerLayer);
+ scheduleTreeStateCommit();
+}
+
+void AsyncScrollingCoordinator::setNonFastScrollableRegionForNode(const Region& region, ScrollingStateScrollingNode* node)
+{
+ node->setNonFastScrollableRegion(region);
+ scheduleTreeStateCommit();
+}
+
+void AsyncScrollingCoordinator::setWheelEventHandlerCountForNode(unsigned wheelEventHandlerCount, ScrollingStateScrollingNode* node)
+{
+ node->setWheelEventHandlerCount(wheelEventHandlerCount);
+ scheduleTreeStateCommit();
+}
+
+void AsyncScrollingCoordinator::setScrollBehaviorForFixedElementsForNode(ScrollBehaviorForFixedElements behaviorForFixed, ScrollingStateScrollingNode* node)
+{
+ node->setScrollBehaviorForFixedElements(behaviorForFixed);
+ scheduleTreeStateCommit();
+}
+
+// FIXME: not sure if this belongs here.
+void AsyncScrollingCoordinator::setScrollbarPaintersFromScrollbarsForNode(Scrollbar* verticalScrollbar, Scrollbar* horizontalScrollbar, ScrollingStateScrollingNode* node)
+{
+ node->setScrollbarPaintersFromScrollbars(verticalScrollbar, horizontalScrollbar);
+ scheduleTreeStateCommit();
+}
+
+void AsyncScrollingCoordinator::setSynchronousScrollingReasons(SynchronousScrollingReasons reasons)
+{
+ if (!m_scrollingStateTree->rootStateNode())
+ return;
+
+ // The FrameView's GraphicsLayer is likely to be out-of-synch with the PlatformLayer
+ // at this point. So we'll update it before we switch back to main thread scrolling
+ // in order to avoid layer positioning bugs.
+ if (reasons)
+ updateMainFrameScrollLayerPosition();
+ m_scrollingStateTree->rootStateNode()->setSynchronousScrollingReasons(reasons);
+ scheduleTreeStateCommit();
+}
+
+void AsyncScrollingCoordinator::updateMainFrameScrollLayerPosition()
+{
+ ASSERT(isMainThread());
+
+ if (!m_page)
+ return;
+
+ FrameView* frameView = m_page->mainFrame().view();
+ if (!frameView)
+ return;
+
+ if (GraphicsLayer* scrollLayer = scrollLayerForFrameView(frameView))
+ scrollLayer->setPosition(-frameView->scrollPosition());
+}
+
+void AsyncScrollingCoordinator::recomputeWheelEventHandlerCountForFrameView(FrameView* frameView)
+{
+ ScrollingStateScrollingNode* node = toScrollingStateScrollingNode(m_scrollingStateTree->stateNodeForID(frameView->scrollLayerID()));
+ if (!node)
+ return;
+ setWheelEventHandlerCountForNode(computeCurrentWheelEventHandlerCount(), node);
+}
+
+bool AsyncScrollingCoordinator::isRubberBandInProgress() const
+{
+ return scrollingTree()->isRubberBandInProgress();
+}
+
+void AsyncScrollingCoordinator::setScrollPinningBehavior(ScrollPinningBehavior pinning)
+{
+ scrollingTree()->setScrollPinningBehavior(pinning);
+}
+
+String AsyncScrollingCoordinator::scrollingStateTreeAsText() const
+{
+ if (m_scrollingStateTree->rootStateNode())
+ return m_scrollingStateTree->rootStateNode()->scrollingStateTreeAsText();
+
+ return String();
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(ASYNC_SCROLLING)