1/* 2 * Copyright (C) 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#import "config.h" 27#import "ScrollingTreeOverflowScrollingNodeIOS.h" 28 29#if PLATFORM(IOS) 30#if ENABLE(ASYNC_SCROLLING) 31 32#import <QuartzCore/QuartzCore.h> 33#import <WebCore/BlockExceptions.h> 34#import <WebCore/ScrollingStateOverflowScrollingNode.h> 35#import <WebCore/ScrollingTree.h> 36#import <UIKit/UIPanGestureRecognizer.h> 37#import <UIKit/UIScrollView.h> 38#import <wtf/TemporaryChange.h> 39 40using namespace WebCore; 41 42@interface WKOverflowScrollViewDelegate : NSObject <UIScrollViewDelegate> { 43 WebKit::ScrollingTreeOverflowScrollingNodeIOS* _scrollingTreeNode; 44} 45 46@property (nonatomic, getter=_isInUserInteraction) BOOL inUserInteraction; 47 48- (instancetype)initWithScrollingTreeNode:(WebKit::ScrollingTreeOverflowScrollingNodeIOS*)node; 49 50@end 51 52@implementation WKOverflowScrollViewDelegate 53 54- (instancetype)initWithScrollingTreeNode:(WebKit::ScrollingTreeOverflowScrollingNodeIOS*)node 55{ 56 if ((self = [super init])) 57 _scrollingTreeNode = node; 58 59 return self; 60} 61 62- (void)scrollViewDidScroll:(UIScrollView *)scrollView 63{ 64 _scrollingTreeNode->scrollViewDidScroll(scrollView.contentOffset, _inUserInteraction); 65} 66 67- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView 68{ 69 _inUserInteraction = YES; 70 71 if (scrollView.panGestureRecognizer.state == UIGestureRecognizerStateBegan) 72 _scrollingTreeNode->overflowScrollViewWillStartPanGesture(); 73 _scrollingTreeNode->overflowScrollWillStart(); 74} 75 76- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)willDecelerate 77{ 78 if (_inUserInteraction && !willDecelerate) { 79 _inUserInteraction = NO; 80 _scrollingTreeNode->scrollViewDidScroll(scrollView.contentOffset, _inUserInteraction); 81 _scrollingTreeNode->overflowScrollDidEnd(); 82 } 83} 84 85- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView 86{ 87 if (_inUserInteraction) { 88 _inUserInteraction = NO; 89 _scrollingTreeNode->scrollViewDidScroll(scrollView.contentOffset, _inUserInteraction); 90 _scrollingTreeNode->overflowScrollDidEnd(); 91 } 92} 93 94@end 95 96namespace WebKit { 97 98PassRefPtr<ScrollingTreeOverflowScrollingNodeIOS> ScrollingTreeOverflowScrollingNodeIOS::create(WebCore::ScrollingTree& scrollingTree, WebCore::ScrollingNodeID nodeID) 99{ 100 return adoptRef(new ScrollingTreeOverflowScrollingNodeIOS(scrollingTree, nodeID)); 101} 102 103ScrollingTreeOverflowScrollingNodeIOS::ScrollingTreeOverflowScrollingNodeIOS(WebCore::ScrollingTree& scrollingTree, WebCore::ScrollingNodeID nodeID) 104 : ScrollingTreeOverflowScrollingNode(scrollingTree, nodeID) 105 , m_updatingFromStateNode(false) 106{ 107} 108 109ScrollingTreeOverflowScrollingNodeIOS::~ScrollingTreeOverflowScrollingNodeIOS() 110{ 111 BEGIN_BLOCK_OBJC_EXCEPTIONS 112 if (UIScrollView *scrollView = (UIScrollView *)[scrollLayer() delegate]) { 113 ASSERT([scrollView isKindOfClass:[UIScrollView self]]); 114 scrollView.delegate = nil; 115 } 116 END_BLOCK_OBJC_EXCEPTIONS 117} 118 119void ScrollingTreeOverflowScrollingNodeIOS::updateBeforeChildren(const WebCore::ScrollingStateNode& stateNode) 120{ 121 if (stateNode.hasChangedProperty(ScrollingStateScrollingNode::ScrollLayer)) { 122 BEGIN_BLOCK_OBJC_EXCEPTIONS 123 if (UIScrollView *scrollView = (UIScrollView *)[scrollLayer() delegate]) { 124 ASSERT([scrollView isKindOfClass:[UIScrollView self]]); 125 scrollView.delegate = nil; 126 } 127 END_BLOCK_OBJC_EXCEPTIONS 128 } 129 130 ScrollingTreeOverflowScrollingNode::updateBeforeChildren(stateNode); 131 132 const auto& scrollingStateNode = toScrollingStateOverflowScrollingNode(stateNode); 133 if (scrollingStateNode.hasChangedProperty(ScrollingStateNode::ScrollLayer)) 134 m_scrollLayer = scrollingStateNode.layer(); 135 136 if (scrollingStateNode.hasChangedProperty(ScrollingStateOverflowScrollingNode::ScrolledContentsLayer)) 137 m_scrolledContentsLayer = scrollingStateNode.scrolledContentsLayer(); 138} 139 140void ScrollingTreeOverflowScrollingNodeIOS::updateAfterChildren(const ScrollingStateNode& stateNode) 141{ 142 ScrollingTreeOverflowScrollingNode::updateAfterChildren(stateNode); 143 144 TemporaryChange<bool> updatingChange(m_updatingFromStateNode, true); 145 146 const auto& scrollingStateNode = toScrollingStateOverflowScrollingNode(stateNode); 147 148 if (scrollingStateNode.hasChangedProperty(ScrollingStateScrollingNode::ScrollLayer) 149 || scrollingStateNode.hasChangedProperty(ScrollingStateScrollingNode::TotalContentsSize) 150 || scrollingStateNode.hasChangedProperty(ScrollingStateScrollingNode::ReachableContentsSize) 151 || scrollingStateNode.hasChangedProperty(ScrollingStateScrollingNode::ScrollPosition) 152 || scrollingStateNode.hasChangedProperty(ScrollingStateScrollingNode::ScrollOrigin)) { 153 BEGIN_BLOCK_OBJC_EXCEPTIONS 154 UIScrollView *scrollView = (UIScrollView *)[scrollLayer() delegate]; 155 ASSERT([scrollView isKindOfClass:[UIScrollView self]]); 156 157 if (scrollingStateNode.hasChangedProperty(ScrollingStateScrollingNode::ScrollLayer)) { 158 if (!m_scrollViewDelegate) 159 m_scrollViewDelegate = adoptNS([[WKOverflowScrollViewDelegate alloc] initWithScrollingTreeNode:this]); 160 161 scrollView.scrollsToTop = NO; 162 scrollView.delegate = m_scrollViewDelegate.get(); 163 } 164 165 bool recomputeInsets = scrollingStateNode.hasChangedProperty(ScrollingStateScrollingNode::TotalContentsSize); 166 if (scrollingStateNode.hasChangedProperty(ScrollingStateScrollingNode::ReachableContentsSize)) { 167 scrollView.contentSize = scrollingStateNode.reachableContentsSize(); 168 recomputeInsets = true; 169 } 170 171 if ((scrollingStateNode.hasChangedProperty(ScrollingStateScrollingNode::ScrollPosition) 172 || scrollingStateNode.hasChangedProperty(ScrollingStateScrollingNode::ScrollOrigin)) 173 && ![m_scrollViewDelegate _isInUserInteraction]) { 174 scrollView.contentOffset = scrollingStateNode.scrollPosition() + scrollOrigin(); 175 recomputeInsets = true; 176 } 177 178 if (recomputeInsets) { 179 UIEdgeInsets insets = UIEdgeInsetsMake(0, 0, 0, 0); 180 // With RTL or bottom-to-top scrolling (non-zero origin), we need extra space on the left or top. 181 if (scrollOrigin().x()) 182 insets.left = reachableContentsSize().width() - totalContentsSize().width(); 183 184 if (scrollOrigin().y()) 185 insets.top = reachableContentsSize().height() - totalContentsSize().height(); 186 187 scrollView.contentInset = insets; 188 } 189 190 END_BLOCK_OBJC_EXCEPTIONS 191 } 192} 193 194void ScrollingTreeOverflowScrollingNodeIOS::updateLayersAfterAncestorChange(const ScrollingTreeNode& changedNode, const FloatRect& fixedPositionRect, const FloatSize& cumulativeDelta) 195{ 196 if (!m_children) 197 return; 198 199 FloatSize scrollDelta = lastCommittedScrollPosition() - scrollPosition(); 200 201 for (auto& child : *m_children) 202 child->updateLayersAfterAncestorChange(changedNode, fixedPositionRect, cumulativeDelta + scrollDelta); 203} 204 205FloatPoint ScrollingTreeOverflowScrollingNodeIOS::scrollPosition() const 206{ 207 BEGIN_BLOCK_OBJC_EXCEPTIONS 208 UIScrollView *scrollView = (UIScrollView *)[scrollLayer() delegate]; 209 ASSERT([scrollView isKindOfClass:[UIScrollView self]]); 210 return [scrollView contentOffset]; 211 END_BLOCK_OBJC_EXCEPTIONS 212} 213 214void ScrollingTreeOverflowScrollingNodeIOS::setScrollLayerPosition(const FloatPoint& scrollPosition) 215{ 216 [m_scrollLayer setPosition:CGPointMake(-scrollPosition.x() + scrollOrigin().x(), -scrollPosition.y() + scrollOrigin().y())]; 217 218 updateChildNodesAfterScroll(scrollPosition); 219} 220 221void ScrollingTreeOverflowScrollingNodeIOS::updateLayersAfterDelegatedScroll(const FloatPoint& scrollPosition) 222{ 223 updateChildNodesAfterScroll(scrollPosition); 224} 225 226void ScrollingTreeOverflowScrollingNodeIOS::updateChildNodesAfterScroll(const FloatPoint& scrollPosition) 227{ 228 if (!m_children) 229 return; 230 231 FloatRect fixedPositionRect = scrollingTree().fixedPositionRect(); 232 FloatSize scrollDelta = lastCommittedScrollPosition() - scrollPosition; 233 234 for (auto& child : *m_children) 235 child->updateLayersAfterAncestorChange(*this, fixedPositionRect, scrollDelta); 236} 237 238void ScrollingTreeOverflowScrollingNodeIOS::overflowScrollWillStart() 239{ 240 scrollingTree().scrollingTreeNodeWillStartScroll(); 241} 242 243void ScrollingTreeOverflowScrollingNodeIOS::overflowScrollDidEnd() 244{ 245 scrollingTree().scrollingTreeNodeDidEndScroll(); 246} 247 248void ScrollingTreeOverflowScrollingNodeIOS::overflowScrollViewWillStartPanGesture() 249{ 250 scrollingTree().scrollingTreeNodeWillStartPanGesture(); 251} 252 253void ScrollingTreeOverflowScrollingNodeIOS::scrollViewDidScroll(const FloatPoint& scrollPosition, bool inUserInteration) 254{ 255 if (m_updatingFromStateNode) 256 return; 257 258 scrollingTree().scrollPositionChangedViaDelegatedScrolling(scrollingNodeID(), scrollPosition, inUserInteration); 259} 260 261} // namespace WebCore 262 263#endif // ENABLE(ASYNC_SCROLLING) 264#endif // PLATFORM(IOS) 265