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