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 | |
39 | namespace WebCore { |
40 | |
41 | ThreadedScrollingTree::ThreadedScrollingTree(AsyncScrollingCoordinator& scrollingCoordinator) |
42 | : m_scrollingCoordinator(&scrollingCoordinator) |
43 | { |
44 | } |
45 | |
46 | ThreadedScrollingTree::~ThreadedScrollingTree() |
47 | { |
48 | // invalidate() should have cleared m_scrollingCoordinator. |
49 | ASSERT(!m_scrollingCoordinator); |
50 | } |
51 | |
52 | ScrollingEventResult 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 | |
68 | ScrollingEventResult ThreadedScrollingTree::handleWheelEvent(const PlatformWheelEvent& wheelEvent) |
69 | { |
70 | ASSERT(ScrollingThread::isCurrentThread()); |
71 | return ScrollingTree::handleWheelEvent(wheelEvent); |
72 | } |
73 | |
74 | void 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 | |
88 | void ThreadedScrollingTree::commitTreeState(std::unique_ptr<ScrollingStateTree> scrollingStateTree) |
89 | { |
90 | ASSERT(ScrollingThread::isCurrentThread()); |
91 | ScrollingTree::commitTreeState(WTFMove(scrollingStateTree)); |
92 | |
93 | decrementPendingCommitCount(); |
94 | } |
95 | |
96 | void 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 | |
118 | void ThreadedScrollingTree::reportSynchronousScrollingReasonsChanged(MonotonicTime timestamp, SynchronousScrollingReasons reasons) |
119 | { |
120 | RunLoop::main().dispatch([scrollingCoordinator = m_scrollingCoordinator, timestamp, reasons] { |
121 | scrollingCoordinator->reportSynchronousScrollingReasonsChanged(timestamp, reasons); |
122 | }); |
123 | } |
124 | |
125 | void ThreadedScrollingTree::reportExposedUnfilledArea(MonotonicTime timestamp, unsigned unfilledArea) |
126 | { |
127 | RunLoop::main().dispatch([scrollingCoordinator = m_scrollingCoordinator, timestamp, unfilledArea] { |
128 | scrollingCoordinator->reportExposedUnfilledArea(timestamp, unfilledArea); |
129 | }); |
130 | } |
131 | |
132 | void ThreadedScrollingTree::incrementPendingCommitCount() |
133 | { |
134 | LockHolder commitLocker(m_pendingCommitCountMutex); |
135 | ++m_pendingCommitCount; |
136 | } |
137 | |
138 | void ThreadedScrollingTree::decrementPendingCommitCount() |
139 | { |
140 | LockHolder commitLocker(m_pendingCommitCountMutex); |
141 | ASSERT(m_pendingCommitCount > 0); |
142 | if (!--m_pendingCommitCount) |
143 | m_commitCondition.notifyOne(); |
144 | } |
145 | |
146 | void ThreadedScrollingTree::waitForPendingCommits() |
147 | { |
148 | ASSERT(isMainThread()); |
149 | |
150 | LockHolder commitLocker(m_pendingCommitCountMutex); |
151 | while (m_pendingCommitCount) |
152 | m_commitCondition.wait(m_pendingCommitCountMutex); |
153 | } |
154 | |
155 | void ThreadedScrollingTree::applyLayerPositions() |
156 | { |
157 | waitForPendingCommits(); |
158 | ScrollingTree::applyLayerPositions(); |
159 | } |
160 | |
161 | #if PLATFORM(COCOA) |
162 | void 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) |
174 | void 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 | |
184 | void 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 | |
194 | void 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 | |
204 | void 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 | |