03ca389a915a62567551ea6352a2fa0c9f755580
[WebKit-https.git] / Source / WebCore / page / scrolling / mac / ScrollingCoordinatorMac.mm
1 /*
2  * Copyright (C) 2011 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
28 #if ENABLE(THREADED_SCROLLING)
29
30 #import "ScrollingCoordinatorMac.h"
31
32 #include "Frame.h"
33 #include "FrameView.h"
34 #include "IntRect.h"
35 #include "Page.h"
36 #include "PlatformWheelEvent.h"
37 #include "PluginViewBase.h"
38 #include "Region.h"
39 #include "RenderLayerCompositor.h"
40 #include "RenderView.h"
41 #include "ScrollAnimator.h"
42 #include "ScrollingConstraints.h"
43 #include "ScrollingStateFixedNode.h"
44 #include "ScrollingStateScrollingNode.h"
45 #include "ScrollingStateTree.h"
46 #include "ScrollingThread.h"
47 #include "ScrollingTree.h"
48 #include "TiledBacking.h"
49
50 #include <wtf/Functional.h>
51 #include <wtf/MainThread.h>
52 #include <wtf/PassRefPtr.h>
53
54
55 namespace WebCore {
56
57 class ScrollingCoordinatorPrivate {
58 };
59
60 ScrollingCoordinatorMac::ScrollingCoordinatorMac(Page* page)
61     : ScrollingCoordinator(page)
62     , m_scrollingStateTree(ScrollingStateTree::create())
63     , m_scrollingTree(ScrollingTree::create(this))
64     , m_scrollingStateTreeCommitterTimer(this, &ScrollingCoordinatorMac::scrollingStateTreeCommitterTimerFired)
65 {
66 }
67
68 ScrollingCoordinatorMac::~ScrollingCoordinatorMac()
69 {
70     ASSERT(!m_scrollingTree);
71 }
72
73 void ScrollingCoordinatorMac::pageDestroyed()
74 {
75     ScrollingCoordinator::pageDestroyed();
76
77     m_scrollingStateTreeCommitterTimer.stop();
78
79     // Invalidating the scrolling tree will break the reference cycle between the ScrollingCoordinator and ScrollingTree objects.
80     ScrollingThread::dispatch(bind(&ScrollingTree::invalidate, m_scrollingTree.release()));
81 }
82
83 ScrollingTree* ScrollingCoordinatorMac::scrollingTree() const
84 {
85     ASSERT(m_scrollingTree);
86     return m_scrollingTree.get();
87 }
88
89 void ScrollingCoordinatorMac::commitTreeStateIfNeeded()
90 {
91     if (!m_scrollingStateTree->hasChangedProperties())
92         return;
93
94     commitTreeState();
95     m_scrollingStateTreeCommitterTimer.stop();
96 }
97
98 void ScrollingCoordinatorMac::frameViewLayoutUpdated(FrameView* frameView)
99 {
100     ASSERT(isMainThread());
101     ASSERT(m_page);
102
103     // Compute the region of the page that we can't do fast scrolling for. This currently includes
104     // all scrollable areas, such as subframes, overflow divs and list boxes. We need to do this even if the
105     // frame view whose layout was updated is not the main frame.
106     Region nonFastScrollableRegion = computeNonFastScrollableRegion(m_page->mainFrame(), IntPoint());
107
108     // In the future, we may want to have the ability to set non-fast scrolling regions for more than
109     // just the root node. But right now, this concept only applies to the root.
110     setNonFastScrollableRegionForNode(nonFastScrollableRegion, m_scrollingStateTree->rootStateNode());
111
112     if (!coordinatesScrollingForFrameView(frameView))
113         return;
114
115     ScrollingStateScrollingNode* node = toScrollingStateScrollingNode(stateNodeForID(frameView->scrollLayerID()));
116     if (!node)
117         return;
118
119     ScrollParameters scrollParameters;
120     scrollParameters.horizontalScrollElasticity = frameView->horizontalScrollElasticity();
121     scrollParameters.verticalScrollElasticity = frameView->verticalScrollElasticity();
122     scrollParameters.hasEnabledHorizontalScrollbar = frameView->horizontalScrollbar() && frameView->horizontalScrollbar()->enabled();
123     scrollParameters.hasEnabledVerticalScrollbar = frameView->verticalScrollbar() && frameView->verticalScrollbar()->enabled();
124     scrollParameters.horizontalScrollbarMode = frameView->horizontalScrollbarMode();
125     scrollParameters.verticalScrollbarMode = frameView->verticalScrollbarMode();
126
127     scrollParameters.scrollOrigin = frameView->scrollOrigin();
128     scrollParameters.viewportRect = IntRect(IntPoint(), frameView->visibleContentRect().size());
129     scrollParameters.contentsSize = frameView->contentsSize();
130
131     setScrollParametersForNode(scrollParameters, node);
132 }
133
134 void ScrollingCoordinatorMac::recomputeWheelEventHandlerCountForFrameView(FrameView* frameView)
135 {
136     ScrollingStateScrollingNode* node = toScrollingStateScrollingNode(stateNodeForID(frameView->scrollLayerID()));
137     if (!node)
138         return;
139     setWheelEventHandlerCountForNode(computeCurrentWheelEventHandlerCount(), node);
140 }
141
142 void ScrollingCoordinatorMac::frameViewRootLayerDidChange(FrameView* frameView)
143 {
144     ASSERT(isMainThread());
145     ASSERT(m_page);
146
147     if (!coordinatesScrollingForFrameView(frameView))
148         return;
149
150     // If the root layer does not have a ScrollingStateNode, then we should create one.
151     ensureRootStateNodeForFrameView(frameView);
152     ASSERT(m_scrollingStateTree->rootStateNode());
153
154     ScrollingCoordinator::frameViewRootLayerDidChange(frameView);
155
156     setScrollLayerForNode(scrollLayerForFrameView(frameView), stateNodeForID(frameView->scrollLayerID()));
157 }
158
159 void ScrollingCoordinatorMac::frameViewHorizontalScrollbarLayerDidChange(FrameView* frameView, GraphicsLayer*)
160 {
161     ASSERT(isMainThread());
162     ASSERT(m_page);
163
164     if (frameView->frame() != m_page->mainFrame())
165         return;
166
167     // FIXME: Implement.
168 }
169
170 void ScrollingCoordinatorMac::frameViewVerticalScrollbarLayerDidChange(FrameView* frameView, GraphicsLayer*)
171 {
172     ASSERT(isMainThread());
173     ASSERT(m_page);
174     
175     if (frameView->frame() != m_page->mainFrame())
176         return;
177
178     // FIXME: Implement.
179 }
180
181 bool ScrollingCoordinatorMac::requestScrollPositionUpdate(FrameView* frameView, const IntPoint& scrollPosition)
182 {
183     ASSERT(isMainThread());
184     ASSERT(m_page);
185
186     if (!coordinatesScrollingForFrameView(frameView))
187         return false;
188
189     if (frameView->inProgrammaticScroll() || frameView->frame()->document()->inPageCache())
190         updateMainFrameScrollPosition(scrollPosition, frameView->inProgrammaticScroll(), SetScrollingLayerPosition);
191
192     // If this frame view's document is being put into the page cache, we don't want to update our
193     // main frame scroll position. Just let the FrameView think that we did.
194     if (frameView->frame()->document()->inPageCache())
195         return true;
196
197     ScrollingStateScrollingNode* stateNode = toScrollingStateScrollingNode(stateNodeForID(frameView->scrollLayerID()));
198     if (!stateNode)
199         return false;
200
201     stateNode->setRequestedScrollPosition(scrollPosition, frameView->inProgrammaticScroll());
202     scheduleTreeStateCommit();
203     return true;
204 }
205
206 bool ScrollingCoordinatorMac::handleWheelEvent(FrameView*, const PlatformWheelEvent& wheelEvent)
207 {
208     ASSERT(isMainThread());
209     ASSERT(m_page);
210
211     if (m_scrollingTree->willWheelEventStartSwipeGesture(wheelEvent))
212         return false;
213
214     ScrollingThread::dispatch(bind(&ScrollingTree::handleWheelEvent, m_scrollingTree.get(), wheelEvent));
215
216     return true;
217 }
218
219 ScrollingNodeID ScrollingCoordinatorMac::attachToStateTree(ScrollingNodeType nodeType, ScrollingNodeID newNodeID, ScrollingNodeID parentID)
220 {
221     ASSERT(newNodeID);
222
223     if (stateNodeForID(newNodeID))
224         return newNodeID;
225
226     ScrollingStateNode* newNode;
227     if (!parentID) {
228         // If we're resetting the root node, we should clear the HashMap and destroy the current children.
229         clearStateTree();
230
231         m_scrollingStateTree->rootStateNode()->setScrollingNodeID(newNodeID);
232         newNode = m_scrollingStateTree->rootStateNode();
233     } else {
234         ScrollingStateNode* parent = stateNodeForID(parentID);
235         switch (nodeType) {
236         case FixedNode: {
237             ASSERT(supportsFixedPositionLayers());
238             OwnPtr<ScrollingStateFixedNode> fixedNode = ScrollingStateFixedNode::create(m_scrollingStateTree.get(), newNodeID);
239             newNode = fixedNode.get();
240             parent->appendChild(fixedNode.release());
241             break;
242         }
243         case ScrollingNode: {
244             // FIXME: We currently only support child nodes that are fixed.
245             ASSERT_NOT_REACHED();
246             OwnPtr<ScrollingStateScrollingNode> scrollingNode = ScrollingStateScrollingNode::create(m_scrollingStateTree.get(), newNodeID);
247             newNode = scrollingNode.get();
248             parent->appendChild(scrollingNode.release());
249             break;
250         }
251         default:
252             ASSERT_NOT_REACHED();
253         }
254     }
255
256     m_stateNodeMap.set(newNodeID, newNode);
257     return newNodeID;
258 }
259
260 void ScrollingCoordinatorMac::removeNode(ScrollingStateNode* node)
261 {
262     m_scrollingStateTree->removeNode(node);
263
264     // ScrollingStateTree::removeNode() will destroy children, so we have to make sure we remove those children
265     // from the HashMap.
266     const Vector<ScrollingNodeID>& removedNodes = m_scrollingStateTree->removedNodes();
267     size_t size = removedNodes.size();
268     for (size_t i = 0; i < size; ++i)
269         m_stateNodeMap.remove(removedNodes[i]);
270 }
271
272 void ScrollingCoordinatorMac::detachFromStateTree(ScrollingNodeID scrollLayerID)
273 {
274     if (!scrollLayerID)
275         return;
276
277     // The node may not be found if clearStateTree() was recently called.
278     ScrollingStateNode* node = m_stateNodeMap.take(scrollLayerID);
279     if (!node)
280         return;
281
282     removeNode(node);
283 }
284
285 void ScrollingCoordinatorMac::clearStateTree()
286 {
287     removeNode(m_scrollingStateTree->rootStateNode());
288 }
289
290 ScrollingStateNode* ScrollingCoordinatorMac::stateNodeForID(ScrollingNodeID scrollLayerID)
291 {
292     if (!scrollLayerID)
293         return 0;
294
295     HashMap<ScrollingNodeID, ScrollingStateNode*>::const_iterator it = m_stateNodeMap.find(scrollLayerID);
296     if (it == m_stateNodeMap.end())
297         return 0;
298
299     return it->value;
300 }
301
302 void ScrollingCoordinatorMac::ensureRootStateNodeForFrameView(FrameView* frameView)
303 {
304     attachToStateTree(ScrollingNode, frameView->scrollLayerID(), 0);
305 }
306
307 void ScrollingCoordinatorMac::setScrollLayerForNode(GraphicsLayer* scrollLayer, ScrollingStateNode* node)
308 {
309     node->setScrollLayer(scrollLayer);
310     scheduleTreeStateCommit();
311 }
312
313 void ScrollingCoordinatorMac::setNonFastScrollableRegionForNode(const Region& region, ScrollingStateScrollingNode* node)
314 {
315     node->setNonFastScrollableRegion(region);
316     scheduleTreeStateCommit();
317 }
318
319 void ScrollingCoordinatorMac::setScrollParametersForNode(const ScrollParameters& scrollParameters, ScrollingStateScrollingNode* node)
320 {
321     node->setHorizontalScrollElasticity(scrollParameters.horizontalScrollElasticity);
322     node->setVerticalScrollElasticity(scrollParameters.verticalScrollElasticity);
323     node->setHasEnabledHorizontalScrollbar(scrollParameters.hasEnabledHorizontalScrollbar);
324     node->setHasEnabledVerticalScrollbar(scrollParameters.hasEnabledVerticalScrollbar);
325     node->setHorizontalScrollbarMode(scrollParameters.horizontalScrollbarMode);
326     node->setVerticalScrollbarMode(scrollParameters.verticalScrollbarMode);
327
328     node->setScrollOrigin(scrollParameters.scrollOrigin);
329     node->setViewportRect(scrollParameters.viewportRect);
330     node->setContentsSize(scrollParameters.contentsSize);
331     scheduleTreeStateCommit();
332 }
333
334 void ScrollingCoordinatorMac::setWheelEventHandlerCountForNode(unsigned wheelEventHandlerCount, ScrollingStateScrollingNode* node)
335 {
336     node->setWheelEventHandlerCount(wheelEventHandlerCount);
337     scheduleTreeStateCommit();
338 }
339
340 void ScrollingCoordinatorMac::setShouldUpdateScrollLayerPositionOnMainThread(MainThreadScrollingReasons reasons)
341 {
342     // The FrameView's GraphicsLayer is likely to be out-of-synch with the PlatformLayer
343     // at this point. So we'll update it before we switch back to main thread scrolling
344     // in order to avoid layer positioning bugs.
345     if (reasons)
346         updateMainFrameScrollLayerPosition();
347     m_scrollingStateTree->rootStateNode()->setShouldUpdateScrollLayerPositionOnMainThread(reasons);
348     scheduleTreeStateCommit();
349 }
350
351 void ScrollingCoordinatorMac::updateMainFrameScrollLayerPosition()
352 {
353     ASSERT(isMainThread());
354
355     if (!m_page)
356         return;
357
358     FrameView* frameView = m_page->mainFrame()->view();
359     if (!frameView)
360         return;
361
362     if (GraphicsLayer* scrollLayer = scrollLayerForFrameView(frameView))
363         scrollLayer->setPosition(-frameView->scrollPosition());
364 }
365
366 void ScrollingCoordinatorMac::syncChildPositions(const LayoutRect& viewportRect)
367 {
368     Vector<OwnPtr<ScrollingStateNode> >* children = m_scrollingStateTree->rootStateNode()->children();
369     if (!children)
370         return;
371
372     // FIXME: We'll have to traverse deeper into the tree at some point.
373     size_t size = children->size();
374     for (size_t i = 0; i < size; ++i) {
375         ScrollingStateFixedNode* child = toScrollingStateFixedNode(children->at(i).get());
376         FloatPoint position = child->viewportConstraints().layerPositionForViewportRect(viewportRect);
377         child->graphicsLayer()->syncPosition(position);
378     }
379 }
380
381 void ScrollingCoordinatorMac::updateViewportConstrainedNode(ScrollingNodeID nodeID, const ViewportConstraints& constraints, GraphicsLayer* graphicsLayer)
382 {
383     ASSERT(supportsFixedPositionLayers());
384
385     // FIXME: We should support sticky position here!
386     if (constraints.constraintType() == ViewportConstraints::StickyPositionConstraint)
387         return;
388
389     ScrollingStateFixedNode* node = toScrollingStateFixedNode(stateNodeForID(nodeID));
390     setScrollLayerForNode(graphicsLayer, node);
391     node->updateConstraints((const FixedPositionViewportConstraints&)constraints);
392 }
393
394 void ScrollingCoordinatorMac::scheduleTreeStateCommit()
395 {
396     if (m_scrollingStateTreeCommitterTimer.isActive())
397         return;
398
399     if (!m_scrollingStateTree->hasChangedProperties())
400         return;
401
402     m_scrollingStateTreeCommitterTimer.startOneShot(0);
403 }
404
405 void ScrollingCoordinatorMac::scrollingStateTreeCommitterTimerFired(Timer<ScrollingCoordinatorMac>*)
406 {
407     commitTreeState();
408 }
409
410 void ScrollingCoordinatorMac::commitTreeState()
411 {
412     ASSERT(m_scrollingStateTree->hasChangedProperties());
413
414     OwnPtr<ScrollingStateTree> treeState = m_scrollingStateTree->commit();
415     ScrollingThread::dispatch(bind(&ScrollingTree::commitNewTreeState, m_scrollingTree.get(), treeState.release()));
416
417     FrameView* frameView = m_page->mainFrame()->view();
418     if (!frameView)
419         return;
420     
421     TiledBacking* tiledBacking = frameView->tiledBacking();
422     if (!tiledBacking)
423         return;
424
425     ScrollingModeIndication indicatorMode;
426     if (shouldUpdateScrollLayerPositionOnMainThread())
427         indicatorMode = MainThreadScrollingBecauseOfStyleIndictaion;
428     else if (scrollingTree() && scrollingTree()->hasWheelEventHandlers())
429         indicatorMode =  MainThreadScrollingBecauseOfEventHandlersIndication;
430     else
431         indicatorMode = ThreadedScrollingIndication;
432     
433     tiledBacking->setScrollingModeIndication(indicatorMode);
434 }
435
436 String ScrollingCoordinatorMac::scrollingStateTreeAsText() const
437 {
438     return m_scrollingStateTree->rootStateNode()->scrollingStateTreeAsText();
439 }
440
441 } // namespace WebCore
442
443 #endif // ENABLE(THREADED_SCROLLING)