1/* 2 * Copyright (C) 2012, 2013, 2014 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 "ScrollingTree.h" 28 29#if ENABLE(ASYNC_SCROLLING) 30 31#include "PlatformWheelEvent.h" 32#include "ScrollingStateTree.h" 33#include "ScrollingTreeNode.h" 34#include "ScrollingTreeScrollingNode.h" 35#include <wtf/TemporaryChange.h> 36 37namespace WebCore { 38 39ScrollingTree::ScrollingTree() 40 : m_hasWheelEventHandlers(false) 41 , m_rubberBandsAtLeft(true) 42 , m_rubberBandsAtRight(true) 43 , m_rubberBandsAtTop(true) 44 , m_rubberBandsAtBottom(true) 45 , m_mainFramePinnedToTheLeft(true) 46 , m_mainFramePinnedToTheRight(true) 47 , m_mainFramePinnedToTheTop(true) 48 , m_mainFramePinnedToTheBottom(true) 49 , m_mainFrameIsRubberBanding(false) 50 , m_scrollPinningBehavior(DoNotPin) 51 , m_latchedNode(0) 52 , m_scrollingPerformanceLoggingEnabled(false) 53 , m_isHandlingProgrammaticScroll(false) 54 , m_fixedOrStickyNodeCount(0) 55{ 56} 57 58ScrollingTree::~ScrollingTree() 59{ 60} 61 62bool ScrollingTree::shouldHandleWheelEventSynchronously(const PlatformWheelEvent& wheelEvent) 63{ 64 // This method is invoked by the event handling thread 65 MutexLocker lock(m_mutex); 66 67 if (m_hasWheelEventHandlers) 68 return true; 69 70 bool shouldSetLatch = wheelEvent.shouldConsiderLatching(); 71 72 if (hasLatchedNode() && !shouldSetLatch) 73 return false; 74 75 if (shouldSetLatch) 76 m_latchedNode = 0; 77 78 if (!m_nonFastScrollableRegion.isEmpty()) { 79 // FIXME: This is not correct for non-default scroll origins. 80 FloatPoint position = wheelEvent.position(); 81 position.moveBy(m_mainFrameScrollPosition); 82 if (m_nonFastScrollableRegion.contains(roundedIntPoint(position))) 83 return true; 84 } 85 return false; 86} 87 88void ScrollingTree::setOrClearLatchedNode(const PlatformWheelEvent& wheelEvent, ScrollingNodeID nodeID) 89{ 90 if (wheelEvent.shouldConsiderLatching()) 91 setLatchedNode(nodeID); 92 else if (wheelEvent.shouldResetLatching()) 93 clearLatchedNode(); 94} 95 96void ScrollingTree::handleWheelEvent(const PlatformWheelEvent& wheelEvent) 97{ 98 if (m_rootNode) 99 toScrollingTreeScrollingNode(m_rootNode.get())->handleWheelEvent(wheelEvent); 100} 101 102void ScrollingTree::viewportChangedViaDelegatedScrolling(ScrollingNodeID nodeID, const WebCore::FloatRect& fixedPositionRect, double scale) 103{ 104 ScrollingTreeNode* node = nodeForID(nodeID); 105 if (!node) 106 return; 107 108 if (!node->isScrollingNode()) 109 return; 110 111 toScrollingTreeScrollingNode(node)->updateLayersAfterViewportChange(fixedPositionRect, scale); 112} 113 114void ScrollingTree::scrollPositionChangedViaDelegatedScrolling(ScrollingNodeID nodeID, const WebCore::FloatPoint& scrollPosition, bool inUserInteration) 115{ 116 ScrollingTreeNode* node = nodeForID(nodeID); 117 if (!node) 118 return; 119 120 if (node->nodeType() != OverflowScrollingNode) 121 return; 122 123 // Update descendant nodes 124 toScrollingTreeScrollingNode(node)->updateLayersAfterDelegatedScroll(scrollPosition); 125 126 // Update GraphicsLayers and scroll state. 127 scrollingTreeNodeDidScroll(nodeID, scrollPosition, inUserInteration ? SyncScrollingLayerPosition : SetScrollingLayerPosition); 128} 129 130void ScrollingTree::commitNewTreeState(PassOwnPtr<ScrollingStateTree> scrollingStateTree) 131{ 132 bool rootStateNodeChanged = scrollingStateTree->hasNewRootStateNode(); 133 134 ScrollingStateScrollingNode* rootNode = scrollingStateTree->rootStateNode(); 135 if (rootNode 136 && (rootStateNodeChanged 137 || rootNode->hasChangedProperty(ScrollingStateFrameScrollingNode::WheelEventHandlerCount) 138 || rootNode->hasChangedProperty(ScrollingStateFrameScrollingNode::NonFastScrollableRegion) 139 || rootNode->hasChangedProperty(ScrollingStateNode::ScrollLayer))) { 140 MutexLocker lock(m_mutex); 141 142 if (rootStateNodeChanged || rootNode->hasChangedProperty(ScrollingStateNode::ScrollLayer)) 143 m_mainFrameScrollPosition = FloatPoint(); 144 if (rootStateNodeChanged || rootNode->hasChangedProperty(ScrollingStateFrameScrollingNode::WheelEventHandlerCount)) 145 m_hasWheelEventHandlers = scrollingStateTree->rootStateNode()->wheelEventHandlerCount(); 146 if (rootStateNodeChanged || rootNode->hasChangedProperty(ScrollingStateFrameScrollingNode::NonFastScrollableRegion)) 147 m_nonFastScrollableRegion = scrollingStateTree->rootStateNode()->nonFastScrollableRegion(); 148 } 149 150 bool scrollRequestIsProgammatic = rootNode ? rootNode->requestedScrollPositionRepresentsProgrammaticScroll() : false; 151 TemporaryChange<bool> changeHandlingProgrammaticScroll(m_isHandlingProgrammaticScroll, scrollRequestIsProgammatic); 152 153 removeDestroyedNodes(*scrollingStateTree); 154 155 OrphanScrollingNodeMap orphanNodes; 156 updateTreeFromStateNode(rootNode, orphanNodes); 157} 158 159void ScrollingTree::updateTreeFromStateNode(const ScrollingStateNode* stateNode, OrphanScrollingNodeMap& orphanNodes) 160{ 161 if (!stateNode) { 162 m_nodeMap.clear(); 163 m_rootNode = nullptr; 164 return; 165 } 166 167 ScrollingNodeID nodeID = stateNode->scrollingNodeID(); 168 ScrollingNodeID parentNodeID = stateNode->parentNodeID(); 169 170 auto it = m_nodeMap.find(nodeID); 171 172 RefPtr<ScrollingTreeNode> node; 173 if (it != m_nodeMap.end()) 174 node = it->value; 175 else { 176 node = createScrollingTreeNode(stateNode->nodeType(), nodeID); 177 if (!parentNodeID) { 178 // This is the root node. Clear the node map. 179 ASSERT(stateNode->nodeType() == FrameScrollingNode); 180 m_rootNode = node; 181 m_nodeMap.clear(); 182 } 183 m_nodeMap.set(nodeID, node.get()); 184 } 185 186 if (parentNodeID) { 187 auto parentIt = m_nodeMap.find(parentNodeID); 188 ASSERT_WITH_SECURITY_IMPLICATION(parentIt != m_nodeMap.end()); 189 if (parentIt != m_nodeMap.end()) { 190 ScrollingTreeNode* parent = parentIt->value; 191 node->setParent(parent); 192 parent->appendChild(node); 193 } 194 } 195 196 node->updateBeforeChildren(*stateNode); 197 198 // Move all children into the orphanNodes map. Live ones will get added back as we recurse over children. 199 if (auto nodeChildren = node->children()) { 200 for (auto& childScrollingNode : *nodeChildren) { 201 childScrollingNode->setParent(nullptr); 202 orphanNodes.add(childScrollingNode->scrollingNodeID(), childScrollingNode); 203 } 204 nodeChildren->clear(); 205 } 206 207 // Now update the children if we have any. 208 if (auto children = stateNode->children()) { 209 for (auto& child : *children) 210 updateTreeFromStateNode(child.get(), orphanNodes); 211 } 212 213 node->updateAfterChildren(*stateNode); 214} 215 216void ScrollingTree::removeDestroyedNodes(const ScrollingStateTree& stateTree) 217{ 218 for (const auto& removedNodeID : stateTree.removedNodes()) { 219 m_nodeMap.remove(removedNodeID); 220 if (removedNodeID == m_latchedNode) 221 clearLatchedNode(); 222 } 223} 224 225ScrollingTreeNode* ScrollingTree::nodeForID(ScrollingNodeID nodeID) const 226{ 227 if (!nodeID) 228 return nullptr; 229 230 return m_nodeMap.get(nodeID); 231} 232 233void ScrollingTree::setMainFramePinState(bool pinnedToTheLeft, bool pinnedToTheRight, bool pinnedToTheTop, bool pinnedToTheBottom) 234{ 235 MutexLocker locker(m_swipeStateMutex); 236 237 m_mainFramePinnedToTheLeft = pinnedToTheLeft; 238 m_mainFramePinnedToTheRight = pinnedToTheRight; 239 m_mainFramePinnedToTheTop = pinnedToTheTop; 240 m_mainFramePinnedToTheBottom = pinnedToTheBottom; 241} 242 243FloatPoint ScrollingTree::mainFrameScrollPosition() 244{ 245 MutexLocker lock(m_mutex); 246 return m_mainFrameScrollPosition; 247} 248 249void ScrollingTree::setMainFrameScrollPosition(FloatPoint position) 250{ 251 MutexLocker lock(m_mutex); 252 m_mainFrameScrollPosition = position; 253} 254 255bool ScrollingTree::isPointInNonFastScrollableRegion(IntPoint p) 256{ 257 MutexLocker lock(m_mutex); 258 259 return m_nonFastScrollableRegion.contains(p); 260} 261 262bool ScrollingTree::isRubberBandInProgress() 263{ 264 MutexLocker lock(m_mutex); 265 266 return m_mainFrameIsRubberBanding; 267} 268 269void ScrollingTree::setMainFrameIsRubberBanding(bool isRubberBanding) 270{ 271 MutexLocker locker(m_mutex); 272 273 m_mainFrameIsRubberBanding = isRubberBanding; 274} 275 276void ScrollingTree::setCanRubberBandState(bool canRubberBandAtLeft, bool canRubberBandAtRight, bool canRubberBandAtTop, bool canRubberBandAtBottom) 277{ 278 MutexLocker locker(m_swipeStateMutex); 279 280 m_rubberBandsAtLeft = canRubberBandAtLeft; 281 m_rubberBandsAtRight = canRubberBandAtRight; 282 m_rubberBandsAtTop = canRubberBandAtTop; 283 m_rubberBandsAtBottom = canRubberBandAtBottom; 284} 285 286bool ScrollingTree::rubberBandsAtLeft() 287{ 288 MutexLocker lock(m_swipeStateMutex); 289 290 return m_rubberBandsAtLeft; 291} 292 293bool ScrollingTree::rubberBandsAtRight() 294{ 295 MutexLocker lock(m_swipeStateMutex); 296 297 return m_rubberBandsAtRight; 298} 299 300bool ScrollingTree::rubberBandsAtBottom() 301{ 302 MutexLocker lock(m_swipeStateMutex); 303 304 return m_rubberBandsAtBottom; 305} 306 307bool ScrollingTree::rubberBandsAtTop() 308{ 309 MutexLocker lock(m_swipeStateMutex); 310 311 return m_rubberBandsAtTop; 312} 313 314bool ScrollingTree::isHandlingProgrammaticScroll() 315{ 316 return m_isHandlingProgrammaticScroll; 317} 318 319void ScrollingTree::setScrollPinningBehavior(ScrollPinningBehavior pinning) 320{ 321 MutexLocker locker(m_swipeStateMutex); 322 323 m_scrollPinningBehavior = pinning; 324} 325 326ScrollPinningBehavior ScrollingTree::scrollPinningBehavior() 327{ 328 MutexLocker lock(m_swipeStateMutex); 329 330 return m_scrollPinningBehavior; 331} 332 333bool ScrollingTree::willWheelEventStartSwipeGesture(const PlatformWheelEvent& wheelEvent) 334{ 335 if (wheelEvent.phase() != PlatformWheelEventPhaseBegan) 336 return false; 337 338 MutexLocker lock(m_swipeStateMutex); 339 340 if (wheelEvent.deltaX() > 0 && m_mainFramePinnedToTheLeft && !m_rubberBandsAtLeft) 341 return true; 342 if (wheelEvent.deltaX() < 0 && m_mainFramePinnedToTheRight && !m_rubberBandsAtRight) 343 return true; 344 if (wheelEvent.deltaY() > 0 && m_mainFramePinnedToTheTop && !m_rubberBandsAtTop) 345 return true; 346 if (wheelEvent.deltaY() < 0 && m_mainFramePinnedToTheBottom && !m_rubberBandsAtBottom) 347 return true; 348 349 return false; 350} 351 352void ScrollingTree::setScrollingPerformanceLoggingEnabled(bool flag) 353{ 354 m_scrollingPerformanceLoggingEnabled = flag; 355} 356 357bool ScrollingTree::scrollingPerformanceLoggingEnabled() 358{ 359 return m_scrollingPerformanceLoggingEnabled; 360} 361 362ScrollingNodeID ScrollingTree::latchedNode() 363{ 364 MutexLocker locker(m_mutex); 365 return m_latchedNode; 366} 367 368void ScrollingTree::setLatchedNode(ScrollingNodeID node) 369{ 370 MutexLocker locker(m_mutex); 371 m_latchedNode = node; 372} 373 374void ScrollingTree::clearLatchedNode() 375{ 376 MutexLocker locker(m_mutex); 377 m_latchedNode = 0; 378} 379 380} // namespace WebCore 381 382#endif // ENABLE(ASYNC_SCROLLING) 383