1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #import "ios/web/web_state/crw_web_view_proxy_impl.h"
7 #include "base/ios/ios_util.h"
8 #include "base/ios/weak_nsobject.h"
9 #include "base/mac/scoped_nsobject.h"
10 #import "ios/web/public/web_state/crw_web_view_scroll_view_proxy.h"
11 #import "ios/web/public/web_state/ui/crw_content_view.h"
12 #import "ios/web/web_state/ui/crw_web_controller.h"
16 // Returns the first responder in the subviews of |view|, or nil if no view in
17 // the subtree is the first responder.
18 UIView* GetFirstResponderSubview(UIView* view) {
19 if ([view isFirstResponder])
22 for (UIView* subview in [view subviews]) {
23 UIView* firstResponder = GetFirstResponderSubview(subview);
25 return firstResponder;
33 @interface CRWWebViewScrollViewProxy (ContentInsetsAlgebra)
34 // Adds the given insets to the current content insets and scroll indicator
35 // insets of the receiver.
36 - (void)cr_addInsets:(UIEdgeInsets)insets;
37 // Removes the given insets to the current content insets and scroll indicator
38 // insets of the receiver.
39 - (void)cr_removeInsets:(UIEdgeInsets)insets;
42 @implementation CRWWebViewScrollViewProxy (ContentInsetsAlgebra)
44 - (void)cr_addInsets:(UIEdgeInsets)insets {
45 if (UIEdgeInsetsEqualToEdgeInsets(insets, UIEdgeInsetsZero))
48 UIEdgeInsets currentInsets = [self contentInset];
49 currentInsets.top += insets.top;
50 currentInsets.left += insets.left;
51 currentInsets.bottom += insets.bottom;
52 currentInsets.right += insets.right;
53 [self setContentInset:currentInsets];
54 [self setScrollIndicatorInsets:currentInsets];
57 - (void)cr_removeInsets:(UIEdgeInsets)insets {
58 UIEdgeInsets negativeInsets = UIEdgeInsetsZero;
59 negativeInsets.top = -insets.top;
60 negativeInsets.left = -insets.left;
61 negativeInsets.bottom = -insets.bottom;
62 negativeInsets.right = -insets.right;
63 [self cr_addInsets:negativeInsets];
68 @implementation CRWWebViewProxyImpl {
69 base::WeakNSObject<CRWContentView> _contentView;
70 base::WeakNSObject<CRWWebController> _webController;
71 base::scoped_nsobject<NSMutableDictionary> _registeredInsets;
72 // The WebViewScrollViewProxy is a wrapper around the web view's
73 // UIScrollView to give components access in a limited and controlled manner.
74 base::scoped_nsobject<CRWWebViewScrollViewProxy> _contentViewScrollViewProxy;
77 - (instancetype)initWithWebController:(CRWWebController*)webController {
80 DCHECK(webController);
81 _registeredInsets.reset([[NSMutableDictionary alloc] init]);
82 _webController.reset(webController);
83 _contentViewScrollViewProxy.reset([[CRWWebViewScrollViewProxy alloc] init]);
88 - (CRWWebViewScrollViewProxy*)scrollViewProxy {
89 return _contentViewScrollViewProxy.get();
93 return [_contentView bounds];
97 return [_contentView frame];
100 - (NSArray*)gestureRecognizers {
101 return [_contentView gestureRecognizers];
104 - (web::WebViewType)webViewType {
105 return [_webController webViewType];
108 - (void)registerInsets:(UIEdgeInsets)insets forCaller:(id)caller {
109 NSValue* callerValue = [NSValue valueWithNonretainedObject:caller];
110 if ([_registeredInsets objectForKey:callerValue])
111 [self unregisterInsetsForCaller:caller];
112 [self.scrollViewProxy cr_addInsets:insets];
113 [_registeredInsets setObject:[NSValue valueWithUIEdgeInsets:insets]
117 - (void)unregisterInsetsForCaller:(id)caller {
118 NSValue* callerValue = [NSValue valueWithNonretainedObject:caller];
119 NSValue* insetsValue = [_registeredInsets objectForKey:callerValue];
120 [self.scrollViewProxy cr_removeInsets:[insetsValue UIEdgeInsetsValue]];
121 [_registeredInsets removeObjectForKey:callerValue];
124 - (CRWContentView*)contentView {
125 return _contentView.get();
128 - (void)setContentView:(CRWContentView*)contentView {
129 _contentView.reset(contentView);
130 [_contentViewScrollViewProxy setScrollView:contentView.scrollView];
133 - (void)addSubview:(UIView*)view {
134 return [_contentView addSubview:view];
137 - (BOOL)hasSearchableTextContent {
138 return _contentView != nil && [_webController contentIsHTML];
141 - (UIView*)getKeyboardAccessory {
142 return [self keyboardAccessory];
145 - (UIView*)keyboardAccessory {
148 UIView* firstResponder = GetFirstResponderSubview(_contentView);
149 return firstResponder.inputAccessoryView;
152 #if defined(__IPHONE_9_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_9_0
153 - (UITextInputAssistantItem*)inputAssistantItem {
154 DCHECK(base::ios::IsRunningOnIOS9OrLater())
155 << "Cannot retrieve inputAssistantItem on iOS versions earlier than 9.";
158 UIView* firstResponder = GetFirstResponderSubview(_contentView);
159 return firstResponder.inputAssistantItem;
163 - (BOOL)keyboardDisplayRequiresUserAction {
164 return [_webController keyboardDisplayRequiresUserAction];
167 - (void)setKeyboardDisplayRequiresUserAction:
168 (BOOL)keyboardDisplayRequiresUserAction {
170 setKeyboardDisplayRequiresUserAction:keyboardDisplayRequiresUserAction];