blob: 7ba42f5a5b50f48d1b3bfd203d402042a79a73cc [file] [log] [blame]
// Copyright 2013 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/web_state/ui/crw_web_view_proxy_impl.h"
#import "ios/web/public/web_state/ui/crw_content_view.h"
#import "ios/web/public/web_state/ui/crw_web_view_scroll_view_proxy.h"
#import "ios/web/web_state/ui/crw_web_controller.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace {
// Returns the first responder in the subviews of |view|, or nil if no view in
// the subtree is the first responder.
UIView* GetFirstResponderSubview(UIView* view) {
if ([view isFirstResponder])
return view;
for (UIView* subview in [view subviews]) {
UIView* firstResponder = GetFirstResponderSubview(subview);
if (firstResponder)
return firstResponder;
}
return nil;
}
} // namespace
@interface CRWWebViewScrollViewProxy (ContentInsetsAlgebra)
// Adds the given insets to the current content insets and scroll indicator
// insets of the receiver.
- (void)cr_addInsets:(UIEdgeInsets)insets;
// Removes the given insets to the current content insets and scroll indicator
// insets of the receiver.
- (void)cr_removeInsets:(UIEdgeInsets)insets;
@end
@implementation CRWWebViewScrollViewProxy (ContentInsetsAlgebra)
- (void)cr_addInsets:(UIEdgeInsets)insets {
if (UIEdgeInsetsEqualToEdgeInsets(insets, UIEdgeInsetsZero))
return;
UIEdgeInsets currentInsets = [self contentInset];
currentInsets.top += insets.top;
currentInsets.left += insets.left;
currentInsets.bottom += insets.bottom;
currentInsets.right += insets.right;
[self setContentInset:currentInsets];
[self setScrollIndicatorInsets:currentInsets];
}
- (void)cr_removeInsets:(UIEdgeInsets)insets {
UIEdgeInsets negativeInsets = UIEdgeInsetsZero;
negativeInsets.top = -insets.top;
negativeInsets.left = -insets.left;
negativeInsets.bottom = -insets.bottom;
negativeInsets.right = -insets.right;
[self cr_addInsets:negativeInsets];
}
@end
@implementation CRWWebViewProxyImpl {
__weak CRWWebController* _webController;
NSMutableDictionary* _registeredInsets;
// The WebViewScrollViewProxy is a wrapper around the web view's
// UIScrollView to give components access in a limited and controlled manner.
CRWWebViewScrollViewProxy* _contentViewScrollViewProxy;
}
@synthesize contentView = _contentView;
- (instancetype)initWithWebController:(CRWWebController*)webController {
self = [super init];
if (self) {
DCHECK(webController);
_registeredInsets = [[NSMutableDictionary alloc] init];
_webController = webController;
_contentViewScrollViewProxy = [[CRWWebViewScrollViewProxy alloc] init];
}
return self;
}
- (CRWWebViewScrollViewProxy*)scrollViewProxy {
return _contentViewScrollViewProxy;
}
- (BOOL)allowsBackForwardNavigationGestures {
return _webController.allowsBackForwardNavigationGestures;
}
- (void)setAllowsBackForwardNavigationGestures:
(BOOL)allowsBackForwardNavigationGestures {
_webController.allowsBackForwardNavigationGestures =
allowsBackForwardNavigationGestures;
}
- (CGRect)bounds {
return [_contentView bounds];
}
- (CGRect)frame {
return [_contentView frame];
}
- (CGPoint)contentOffset {
return _contentView.contentOffset;
}
- (void)setContentOffset:(CGPoint)contentOffset {
_contentView.contentOffset = contentOffset;
}
- (UIEdgeInsets)contentInset {
return _contentView.contentInset;
}
- (void)setContentInset:(UIEdgeInsets)contentInset {
_contentView.contentInset = contentInset;
}
- (NSArray*)gestureRecognizers {
return [_contentView gestureRecognizers];
}
- (void)addGestureRecognizer:(UIGestureRecognizer*)gestureRecognizer {
[_contentView addGestureRecognizer:gestureRecognizer];
}
- (void)removeGestureRecognizer:(UIGestureRecognizer*)gestureRecognizer {
[_contentView removeGestureRecognizer:gestureRecognizer];
}
- (BOOL)shouldUseViewContentInset {
SEL shouldUseInsetSelector = @selector(shouldUseViewContentInset);
return [_contentView respondsToSelector:shouldUseInsetSelector] &&
[_contentView shouldUseViewContentInset];
}
- (void)setShouldUseViewContentInset:(BOOL)shouldUseViewContentInset {
if ([_contentView
respondsToSelector:@selector(setShouldUseViewContentInset:)]) {
[_contentView setShouldUseViewContentInset:shouldUseViewContentInset];
}
}
- (void)registerInsets:(UIEdgeInsets)insets forCaller:(id)caller {
NSValue* callerValue = [NSValue valueWithNonretainedObject:caller];
if ([_registeredInsets objectForKey:callerValue])
[self unregisterInsetsForCaller:caller];
[self.scrollViewProxy cr_addInsets:insets];
[_registeredInsets setObject:[NSValue valueWithUIEdgeInsets:insets]
forKey:callerValue];
}
- (void)unregisterInsetsForCaller:(id)caller {
NSValue* callerValue = [NSValue valueWithNonretainedObject:caller];
NSValue* insetsValue = [_registeredInsets objectForKey:callerValue];
[self.scrollViewProxy cr_removeInsets:[insetsValue UIEdgeInsetsValue]];
[_registeredInsets removeObjectForKey:callerValue];
}
- (void)setContentView:(CRWContentView*)contentView {
_contentView = contentView;
[_contentViewScrollViewProxy setScrollView:contentView.scrollView];
}
- (void)addSubview:(UIView*)view {
return [_contentView addSubview:view];
}
- (BOOL)hasSearchableTextContent {
return _contentView != nil && [_webController contentIsHTML];
}
- (UIView*)keyboardAccessory {
if (!_contentView)
return nil;
UIView* firstResponder = GetFirstResponderSubview(_contentView);
return firstResponder.inputAccessoryView;
}
- (BOOL)becomeFirstResponder {
return [_contentView becomeFirstResponder];
}
@end