blob: 94e4a57a1bb2e703e1da56244c156d955d2e0636 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/web/public/web_state/ui/crw_web_view_scroll_view_proxy.h"
#import <objc/runtime.h>
#include "base/auto_reset.h"
#import "base/ios/crb_protocol_observers.h"
#include "base/mac/foundation_util.h"
#import "base/mac/scoped_nsobject.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
@interface CRWWebViewScrollViewProxy () {
__weak UIScrollView* _scrollView;
base::scoped_nsobject<id> _observers;
}
// Returns the key paths that need to be observed for UIScrollView.
+ (NSArray*)scrollViewObserverKeyPaths;
// Adds and removes |self| as an observer for |scrollView| with key paths
// returned by |+scrollViewObserverKeyPaths|.
- (void)startObservingScrollView:(UIScrollView*)scrollView;
- (void)stopObservingScrollView:(UIScrollView*)scrollView;
@end
@implementation CRWWebViewScrollViewProxy
- (instancetype)init {
self = [super init];
if (self) {
Protocol* protocol = @protocol(CRWWebViewScrollViewProxyObserver);
_observers.reset([CRBProtocolObservers observersWithProtocol:protocol]);
}
return self;
}
- (void)dealloc {
[self stopObservingScrollView:_scrollView];
}
- (void)addGestureRecognizer:(UIGestureRecognizer*)gestureRecognizer {
[_scrollView addGestureRecognizer:gestureRecognizer];
}
- (void)removeGestureRecognizer:(UIGestureRecognizer*)gestureRecognizer {
[_scrollView removeGestureRecognizer:gestureRecognizer];
}
- (void)addObserver:(id<CRWWebViewScrollViewProxyObserver>)observer {
[_observers addObserver:observer];
}
- (void)removeObserver:(id<CRWWebViewScrollViewProxyObserver>)observer {
[_observers removeObserver:observer];
}
- (void)setScrollView:(UIScrollView*)scrollView {
if (_scrollView == scrollView)
return;
[_scrollView setDelegate:nil];
[self stopObservingScrollView:_scrollView];
DCHECK(!scrollView.delegate);
scrollView.delegate = self;
[self startObservingScrollView:scrollView];
_scrollView = scrollView;
[_observers webViewScrollViewProxyDidSetScrollView:self];
}
- (CGRect)frame {
return _scrollView ? [_scrollView frame] : CGRectZero;
}
- (BOOL)isScrollEnabled {
return [_scrollView isScrollEnabled];
}
- (void)setScrollEnabled:(BOOL)scrollEnabled {
[_scrollView setScrollEnabled:scrollEnabled];
}
- (BOOL)bounces {
return [_scrollView bounces];
}
- (void)setBounces:(BOOL)bounces {
[_scrollView setBounces:bounces];
}
- (BOOL)isDecelerating {
return [_scrollView isDecelerating];
}
- (BOOL)isDragging {
return [_scrollView isDragging];
}
- (BOOL)isZooming {
return [_scrollView isZooming];
}
- (CGFloat)zoomScale {
return [_scrollView zoomScale];
}
- (void)setContentOffset:(CGPoint)contentOffset {
[_scrollView setContentOffset:contentOffset];
}
- (CGPoint)contentOffset {
return _scrollView ? [_scrollView contentOffset] : CGPointZero;
}
- (void)setContentInset:(UIEdgeInsets)contentInset {
[_scrollView setContentInset:contentInset];
}
- (UIEdgeInsets)contentInset {
return _scrollView ? [_scrollView contentInset] : UIEdgeInsetsZero;
}
- (void)setScrollIndicatorInsets:(UIEdgeInsets)scrollIndicatorInsets {
[_scrollView setScrollIndicatorInsets:scrollIndicatorInsets];
}
- (UIEdgeInsets)scrollIndicatorInsets {
return _scrollView ? [_scrollView scrollIndicatorInsets] : UIEdgeInsetsZero;
}
- (void)setContentSize:(CGSize)contentSize {
[_scrollView setContentSize:contentSize];
}
- (CGSize)contentSize {
return _scrollView ? [_scrollView contentSize] : CGSizeZero;
}
- (void)setContentOffset:(CGPoint)contentOffset animated:(BOOL)animated {
[_scrollView setContentOffset:contentOffset animated:animated];
}
- (BOOL)scrollsToTop {
return [_scrollView scrollsToTop];
}
- (void)setScrollsToTop:(BOOL)scrollsToTop {
[_scrollView setScrollsToTop:scrollsToTop];
}
- (UIPanGestureRecognizer*)panGestureRecognizer {
return [_scrollView panGestureRecognizer];
}
- (NSArray*)gestureRecognizers {
return [_scrollView gestureRecognizers];
}
#pragma mark -
#pragma mark UIScrollViewDelegate callbacks
- (void)scrollViewDidScroll:(UIScrollView*)scrollView {
DCHECK_EQ(_scrollView, scrollView);
[_observers webViewScrollViewDidScroll:self];
}
- (void)scrollViewWillBeginDragging:(UIScrollView*)scrollView {
DCHECK_EQ(_scrollView, scrollView);
[_observers webViewScrollViewWillBeginDragging:self];
// TODO(crbug.com/555723) Remove this once the fix to
// https://bugs.webkit.org/show_bug.cgi?id=148086 makes it's way in to iOS.
scrollView.decelerationRate = UIScrollViewDecelerationRateNormal;
}
- (void)scrollViewWillEndDragging:(UIScrollView*)scrollView
withVelocity:(CGPoint)velocity
targetContentOffset:(inout CGPoint*)targetContentOffset {
DCHECK_EQ(_scrollView, scrollView);
[_observers webViewScrollViewWillEndDragging:self
withVelocity:velocity
targetContentOffset:targetContentOffset];
}
- (void)scrollViewDidEndDragging:(UIScrollView*)scrollView
willDecelerate:(BOOL)decelerate {
DCHECK_EQ(_scrollView, scrollView);
[_observers webViewScrollViewDidEndDragging:self willDecelerate:decelerate];
}
- (void)scrollViewDidEndDecelerating:(UIScrollView*)scrollView {
DCHECK_EQ(_scrollView, scrollView);
[_observers webViewScrollViewDidEndDecelerating:self];
}
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView*)scrollView {
DCHECK_EQ(_scrollView, scrollView);
[_observers webViewScrollViewDidEndScrollingAnimation:self];
}
- (BOOL)scrollViewShouldScrollToTop:(UIScrollView*)scrollView {
DCHECK_EQ(_scrollView, scrollView);
__block BOOL shouldScrollToTop = YES;
[_observers executeOnObservers:^(id observer) {
if ([observer respondsToSelector:@selector
(webViewScrollViewShouldScrollToTop:)]) {
shouldScrollToTop = shouldScrollToTop &&
[observer webViewScrollViewShouldScrollToTop:self];
}
}];
return shouldScrollToTop;
}
- (void)scrollViewDidZoom:(UIScrollView*)scrollView {
DCHECK_EQ(_scrollView, scrollView);
[_observers webViewScrollViewDidZoom:self];
}
- (void)scrollViewWillBeginZooming:(UIScrollView*)scrollView
withView:(UIView*)view {
DCHECK_EQ(_scrollView, scrollView);
[_observers webViewScrollViewWillBeginZooming:self];
}
#pragma mark -
+ (NSArray*)scrollViewObserverKeyPaths {
return @[ @"contentSize" ];
}
- (void)startObservingScrollView:(UIScrollView*)scrollView {
for (NSString* keyPath in [[self class] scrollViewObserverKeyPaths])
[scrollView addObserver:self forKeyPath:keyPath options:0 context:nil];
}
- (void)stopObservingScrollView:(UIScrollView*)scrollView {
for (NSString* keyPath in [[self class] scrollViewObserverKeyPaths])
[scrollView removeObserver:self forKeyPath:keyPath];
}
- (void)observeValueForKeyPath:(NSString*)keyPath
ofObject:(id)object
change:(NSDictionary*)change
context:(void*)context {
DCHECK_EQ(object, _scrollView);
if ([keyPath isEqualToString:@"contentSize"])
[_observers webViewScrollViewDidResetContentSize:self];
}
@end