1/*
2 * Copyright (C) 2014-2015 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "ThreadedScrollingTree.h"
28
29#if ENABLE(ASYNC_SCROLLING)
30
31#include "AsyncScrollingCoordinator.h"
32#include "PlatformWheelEvent.h"
33#include "ScrollingThread.h"
34#include "ScrollingTreeFrameScrollingNode.h"
35#include "ScrollingTreeNode.h"
36#include "ScrollingTreeScrollingNode.h"
37#include <wtf/RunLoop.h>
38
39namespace WebCore {
40
41ThreadedScrollingTree::ThreadedScrollingTree(AsyncScrollingCoordinator& scrollingCoordinator)
42 : m_scrollingCoordinator(&scrollingCoordinator)
43{
44}
45
46ThreadedScrollingTree::~ThreadedScrollingTree()
47{
48 // invalidate() should have cleared m_scrollingCoordinator.
49 ASSERT(!m_scrollingCoordinator);
50}
51
52ScrollingEventResult ThreadedScrollingTree::tryToHandleWheelEvent(const PlatformWheelEvent& wheelEvent)
53{
54 if (shouldHandleWheelEventSynchronously(wheelEvent))
55 return ScrollingEventResult::SendToMainThread;
56
57 if (willWheelEventStartSwipeGesture(wheelEvent))
58 return ScrollingEventResult::DidNotHandleEvent;
59
60 RefPtr<ThreadedScrollingTree> protectedThis(this);
61 ScrollingThread::dispatch([protectedThis, wheelEvent] {
62 protectedThis->handleWheelEvent(wheelEvent);
63 });
64
65 return ScrollingEventResult::DidHandleEvent;
66}
67
68ScrollingEventResult ThreadedScrollingTree::handleWheelEvent(const PlatformWheelEvent& wheelEvent)
69{
70 ASSERT(ScrollingThread::isCurrentThread());
71 return ScrollingTree::handleWheelEvent(wheelEvent);
72}
73
74void ThreadedScrollingTree::invalidate()
75{
76 // Invalidate is dispatched by the ScrollingCoordinator class on the ScrollingThread
77 // to break the reference cycle between ScrollingTree and ScrollingCoordinator when the
78 // ScrollingCoordinator's page is destroyed.
79 ASSERT(ScrollingThread::isCurrentThread());
80
81 // Since this can potentially be the last reference to the scrolling coordinator,
82 // we need to release it on the main thread since it has member variables (such as timers)
83 // that expect to be destroyed from the main thread.
84 RunLoop::main().dispatch([scrollingCoordinator = WTFMove(m_scrollingCoordinator)] {
85 });
86}
87
88void ThreadedScrollingTree::commitTreeState(std::unique_ptr<ScrollingStateTree> scrollingStateTree)
89{
90 ASSERT(ScrollingThread::isCurrentThread());
91 ScrollingTree::commitTreeState(WTFMove(scrollingStateTree));
92
93 decrementPendingCommitCount();
94}
95
96void ThreadedScrollingTree::scrollingTreeNodeDidScroll(ScrollingTreeScrollingNode& node, ScrollingLayerPositionAction scrollingLayerPositionAction)
97{
98 if (!m_scrollingCoordinator)
99 return;
100
101 auto scrollPosition = node.currentScrollPosition();
102
103 if (node.isRootNode())
104 setMainFrameScrollPosition(scrollPosition);
105
106 if (isHandlingProgrammaticScroll())
107 return;
108
109 Optional<FloatPoint> layoutViewportOrigin;
110 if (is<ScrollingTreeFrameScrollingNode>(node))
111 layoutViewportOrigin = downcast<ScrollingTreeFrameScrollingNode>(node).layoutViewport().location();
112
113 RunLoop::main().dispatch([scrollingCoordinator = m_scrollingCoordinator, nodeID = node.scrollingNodeID(), scrollPosition, layoutViewportOrigin, scrollingLayerPositionAction] {
114 scrollingCoordinator->scheduleUpdateScrollPositionAfterAsyncScroll(nodeID, scrollPosition, layoutViewportOrigin, scrollingLayerPositionAction);
115 });
116}
117
118void ThreadedScrollingTree::reportSynchronousScrollingReasonsChanged(MonotonicTime timestamp, SynchronousScrollingReasons reasons)
119{
120 RunLoop::main().dispatch([scrollingCoordinator = m_scrollingCoordinator, timestamp, reasons] {
121 scrollingCoordinator->reportSynchronousScrollingReasonsChanged(timestamp, reasons);
122 });
123}
124
125void ThreadedScrollingTree::reportExposedUnfilledArea(MonotonicTime timestamp, unsigned unfilledArea)
126{
127 RunLoop::main().dispatch([scrollingCoordinator = m_scrollingCoordinator, timestamp, unfilledArea] {
128 scrollingCoordinator->reportExposedUnfilledArea(timestamp, unfilledArea);
129 });
130}
131
132void ThreadedScrollingTree::incrementPendingCommitCount()
133{
134 LockHolder commitLocker(m_pendingCommitCountMutex);
135 ++m_pendingCommitCount;
136}
137
138void ThreadedScrollingTree::decrementPendingCommitCount()
139{
140 LockHolder commitLocker(m_pendingCommitCountMutex);
141 ASSERT(m_pendingCommitCount > 0);
142 if (!--m_pendingCommitCount)
143 m_commitCondition.notifyOne();
144}
145
146void ThreadedScrollingTree::waitForPendingCommits()
147{
148 ASSERT(isMainThread());
149
150 LockHolder commitLocker(m_pendingCommitCountMutex);
151 while (m_pendingCommitCount)
152 m_commitCondition.wait(m_pendingCommitCountMutex);
153}
154
155void ThreadedScrollingTree::applyLayerPositions()
156{
157 waitForPendingCommits();
158 ScrollingTree::applyLayerPositions();
159}
160
161#if PLATFORM(COCOA)
162void ThreadedScrollingTree::currentSnapPointIndicesDidChange(ScrollingNodeID nodeID, unsigned horizontal, unsigned vertical)
163{
164 if (!m_scrollingCoordinator)
165 return;
166
167 RunLoop::main().dispatch([scrollingCoordinator = m_scrollingCoordinator, nodeID, horizontal, vertical] {
168 scrollingCoordinator->setActiveScrollSnapIndices(nodeID, horizontal, vertical);
169 });
170}
171#endif
172
173#if PLATFORM(MAC)
174void ThreadedScrollingTree::handleWheelEventPhase(PlatformWheelEventPhase phase)
175{
176 if (!m_scrollingCoordinator)
177 return;
178
179 RunLoop::main().dispatch([scrollingCoordinator = m_scrollingCoordinator, phase] {
180 scrollingCoordinator->handleWheelEventPhase(phase);
181 });
182}
183
184void ThreadedScrollingTree::setActiveScrollSnapIndices(ScrollingNodeID nodeID, unsigned horizontalIndex, unsigned verticalIndex)
185{
186 if (!m_scrollingCoordinator)
187 return;
188
189 RunLoop::main().dispatch([scrollingCoordinator = m_scrollingCoordinator, nodeID, horizontalIndex, verticalIndex] {
190 scrollingCoordinator->setActiveScrollSnapIndices(nodeID, horizontalIndex, verticalIndex);
191 });
192}
193
194void ThreadedScrollingTree::deferTestsForReason(WheelEventTestTrigger::ScrollableAreaIdentifier identifier, WheelEventTestTrigger::DeferTestTriggerReason reason)
195{
196 if (!m_scrollingCoordinator)
197 return;
198
199 RunLoop::main().dispatch([scrollingCoordinator = m_scrollingCoordinator, identifier, reason] {
200 scrollingCoordinator->deferTestsForReason(identifier, reason);
201 });
202}
203
204void ThreadedScrollingTree::removeTestDeferralForReason(WheelEventTestTrigger::ScrollableAreaIdentifier identifier, WheelEventTestTrigger::DeferTestTriggerReason reason)
205{
206 if (!m_scrollingCoordinator)
207 return;
208
209 RunLoop::main().dispatch([scrollingCoordinator = m_scrollingCoordinator, identifier, reason] {
210 scrollingCoordinator->removeTestDeferralForReason(identifier, reason);
211 });
212}
213
214#endif
215
216} // namespace WebCore
217
218#endif // ENABLE(ASYNC_SCROLLING)
219