Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(9)

Side by Side Diff: ios/chrome/browser/autofill/form_input_accessory_view_controller.mm

Issue 1305433003: Make Autofill work again on iPads running iOS 9. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #import "ios/chrome/browser/autofill/form_input_accessory_view_controller.h" 5 #import "ios/chrome/browser/autofill/form_input_accessory_view_controller.h"
6 6
7 #include "base/ios/block_types.h" 7 #include "base/ios/block_types.h"
8 #include "base/ios/ios_util.h"
8 #include "base/mac/foundation_util.h" 9 #include "base/mac/foundation_util.h"
9 #include "base/mac/scoped_block.h" 10 #include "base/mac/scoped_block.h"
10 #include "base/mac/scoped_nsobject.h" 11 #include "base/mac/scoped_nsobject.h"
11 #include "base/memory/scoped_ptr.h" 12 #include "base/memory/scoped_ptr.h"
12 #include "base/strings/sys_string_conversions.h"
13 #include "base/strings/utf_string_conversions.h"
14 #import "components/autofill/ios/browser/js_suggestion_manager.h" 13 #import "components/autofill/ios/browser/js_suggestion_manager.h"
15 #import "ios/chrome/browser/autofill/form_input_accessory_view.h" 14 #import "ios/chrome/browser/autofill/form_input_accessory_view.h"
16 #import "ios/chrome/browser/passwords/password_generation_utils.h" 15 #import "ios/chrome/browser/passwords/password_generation_utils.h"
16 #include "ios/chrome/browser/ui/ui_util.h"
17 #include "ios/web/public/test/crw_test_js_injection_receiver.h" 17 #include "ios/web/public/test/crw_test_js_injection_receiver.h"
18 #include "ios/web/public/url_scheme_util.h" 18 #include "ios/web/public/url_scheme_util.h"
19 #import "ios/web/public/web_state/crw_web_view_proxy.h" 19 #import "ios/web/public/web_state/crw_web_view_proxy.h"
20 #include "ios/web/public/web_state/url_verification_constants.h" 20 #include "ios/web/public/web_state/url_verification_constants.h"
21 #include "ios/web/public/web_state/web_state.h" 21 #include "ios/web/public/web_state/web_state.h"
22 #include "url/gurl.h" 22 #include "url/gurl.h"
23 23
24 namespace autofill { 24 namespace autofill {
25 NSString* const kFormSuggestionAssistButtonPreviousElement = @"previousTap"; 25 NSString* const kFormSuggestionAssistButtonPreviousElement = @"previousTap";
26 NSString* const kFormSuggestionAssistButtonNextElement = @"nextTap"; 26 NSString* const kFormSuggestionAssistButtonNextElement = @"nextTap";
27 NSString* const kFormSuggestionAssistButtonDone = @"done"; 27 NSString* const kFormSuggestionAssistButtonDone = @"done";
28 CGFloat const kInputAccessoryHeight = 44.0f;
28 } // namespace autofill 29 } // namespace autofill
29 30
30 namespace { 31 namespace {
31 32
32 // Finds all views of a particular kind if class |klass| in the subview 33 // Finds all views of a particular kind if class |klass| in the subview
33 // hierarchy of the given |root| view. 34 // hierarchy of the given |root| view.
34 NSArray* FindDescendantsOfClass(UIView* root, Class klass) { 35 NSArray* FindDescendantsOfClass(UIView* root, Class klass) {
35 DCHECK(root); 36 DCHECK(root);
36 NSMutableArray* viewsToExamine = [NSMutableArray arrayWithObject:root]; 37 NSMutableArray* viewsToExamine = [NSMutableArray arrayWithObject:root];
37 NSMutableArray* descendants = [NSMutableArray array]; 38 NSMutableArray* descendants = [NSMutableArray array];
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
86 87
87 // Computes the frame of each part of the accessory view of the keyboard. It is 88 // Computes the frame of each part of the accessory view of the keyboard. It is
88 // assumed that the keyboard has either two parts (when it is split) or one part 89 // assumed that the keyboard has either two parts (when it is split) or one part
89 // (when it is merged). 90 // (when it is merged).
90 // 91 //
91 // If there are two parts, the frame of the left part is returned in 92 // If there are two parts, the frame of the left part is returned in
92 // |leftFrame| and the frame of the right part is returned in |rightFrame|. 93 // |leftFrame| and the frame of the right part is returned in |rightFrame|.
93 // If there is only one part, the frame is returned in |leftFrame| and 94 // If there is only one part, the frame is returned in |leftFrame| and
94 // |rightFrame| has size zero. 95 // |rightFrame| has size zero.
95 // 96 //
96 // Heuristics are used to compute this information. It returns true if the 97 // Heuristics are used to compute this information. It returns false if the
97 // number of |inputAccessoryView.subviews| is not 2. 98 // number of |inputAccessoryView.subviews| is not 2.
98 bool ComputeFramesOfKeyboardParts(UIView* inputAccessoryView, 99 bool ComputeFramesOfKeyboardParts(UIView* inputAccessoryView,
99 CGRect* leftFrame, 100 CGRect* leftFrame,
100 CGRect* rightFrame) { 101 CGRect* rightFrame) {
101 // It is observed (on iOS 6) there are always two subviews in the original 102 // It is observed (on iOS 6) there are always two subviews in the original
102 // input accessory view. When the keyboard is split, each subview represents 103 // input accessory view. When the keyboard is split, each subview represents
103 // one part of the accesssary view of the keyboard. When the keyboard is 104 // one part of the accesssary view of the keyboard. When the keyboard is
104 // merged, one subview has the same frame as that of the whole accessory view 105 // merged, one subview has the same frame as that of the whole accessory view
105 // and the other has zero size with the screen width as origin.x. 106 // and the other has zero size with the screen width as origin.x.
106 // The computation here is based on this observation. 107 // The computation here is based on this observation.
(...skipping 16 matching lines...) Expand all
123 124
124 } // namespace 125 } // namespace
125 126
126 @interface FormInputAccessoryViewController () 127 @interface FormInputAccessoryViewController ()
127 128
128 // Allows injection of the JsSuggestionManager. 129 // Allows injection of the JsSuggestionManager.
129 - (instancetype)initWithWebState:(web::WebState*)webState 130 - (instancetype)initWithWebState:(web::WebState*)webState
130 JSSuggestionManager:(JsSuggestionManager*)JSSuggestionManager 131 JSSuggestionManager:(JsSuggestionManager*)JSSuggestionManager
131 providers:(NSArray*)providers; 132 providers:(NSArray*)providers;
132 133
133 // Called when the keyboard did change frame. 134 // Called when the keyboard will or did change frame.
134 - (void)keyboardDidChangeFrame:(NSNotification*)notification; 135 - (void)keyboardWillOrDidChangeFrame:(NSNotification*)notification;
135 136
136 // Called when the keyboard is dismissed. 137 // Called when the keyboard is dismissed.
137 - (void)keyboardDidHide:(NSNotification*)notification; 138 - (void)keyboardDidHide:(NSNotification*)notification;
138 139
139 // Hides the subviews in |accessoryView|. 140 // Hides the subviews in |accessoryView|.
140 - (void)hideSubviewsInOriginalAccessoryView:(UIView*)accessoryView; 141 - (void)hideSubviewsInOriginalAccessoryView:(UIView*)accessoryView;
141 142
142 // Attempts to execute/tap/send-an-event-to the iOS built-in "next" and 143 // Attempts to execute/tap/send-an-event-to the iOS built-in "next" and
143 // "previous" form assist controls. Returns NO if this attempt failed, YES 144 // "previous" form assist controls. Returns NO if this attempt failed, YES
144 // otherwise. [HACK] 145 // otherwise. [HACK]
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
208 self = [super init]; 209 self = [super init];
209 if (self) { 210 if (self) {
210 _JSSuggestionManager.reset([JSSuggestionManager retain]); 211 _JSSuggestionManager.reset([JSSuggestionManager retain]);
211 _hiddenOriginalSubviews.reset([[NSMutableArray alloc] init]); 212 _hiddenOriginalSubviews.reset([[NSMutableArray alloc] init]);
212 _webStateObserverBridge.reset( 213 _webStateObserverBridge.reset(
213 new web::WebStateObserverBridge(webState, self)); 214 new web::WebStateObserverBridge(webState, self));
214 _providers.reset([providers copy]); 215 _providers.reset([providers copy]);
215 // There is no defined relation on the timing of JavaScript events and 216 // There is no defined relation on the timing of JavaScript events and
216 // keyboard showing up. So it is necessary to listen to the keyboard 217 // keyboard showing up. So it is necessary to listen to the keyboard
217 // notification to make sure the keyboard is updated. 218 // notification to make sure the keyboard is updated.
219 if (base::ios::IsRunningOnIOS9OrLater() && IsIPadIdiom()) {
220 [[NSNotificationCenter defaultCenter]
221 addObserver:self
222 selector:@selector(keyboardWillOrDidChangeFrame:)
223 name:UIKeyboardWillChangeFrameNotification
224 object:nil];
225 }
218 [[NSNotificationCenter defaultCenter] 226 [[NSNotificationCenter defaultCenter]
219 addObserver:self 227 addObserver:self
220 selector:@selector(keyboardDidChangeFrame:) 228 selector:@selector(keyboardWillOrDidChangeFrame:)
221 name:UIKeyboardDidChangeFrameNotification 229 name:UIKeyboardDidChangeFrameNotification
222 object:nil]; 230 object:nil];
223 [[NSNotificationCenter defaultCenter] 231 [[NSNotificationCenter defaultCenter]
224 addObserver:self 232 addObserver:self
225 selector:@selector(keyboardDidHide:) 233 selector:@selector(keyboardDidHide:)
226 name:UIKeyboardDidHideNotification 234 name:UIKeyboardDidHideNotification
227 object:nil]; 235 object:nil];
228 } 236 }
229 return self; 237 return self;
230 } 238 }
(...skipping 15 matching lines...) Expand all
246 - (void)hideSubviewsInOriginalAccessoryView:(UIView*)accessoryView { 254 - (void)hideSubviewsInOriginalAccessoryView:(UIView*)accessoryView {
247 for (UIView* subview in [accessoryView subviews]) { 255 for (UIView* subview in [accessoryView subviews]) {
248 if (!subview.hidden) { 256 if (!subview.hidden) {
249 [_hiddenOriginalSubviews addObject:subview]; 257 [_hiddenOriginalSubviews addObject:subview];
250 subview.hidden = YES; 258 subview.hidden = YES;
251 } 259 }
252 } 260 }
253 } 261 }
254 262
255 - (void)showCustomInputAccessoryView:(UIView*)view { 263 - (void)showCustomInputAccessoryView:(UIView*)view {
256 [self restoreDefaultInputAccessoryView]; 264 if (base::ios::IsRunningOnIOS9OrLater() && IsIPadIdiom()) {
257 CGRect leftFrame; 265 // On iPads running iOS 9 or later, there's no inputAccessoryView available
258 CGRect rightFrame; 266 // so we attach the custom view directly to the keyboard view instead.
259 UIView* inputAccessoryView = [self.webViewProxy getKeyboardAccessory]; 267 [_customAccessoryView removeFromSuperview];
260 if (ComputeFramesOfKeyboardParts(inputAccessoryView, &leftFrame, 268 UIViewController* rootViewController =
261 &rightFrame)) { 269 [UIApplication sharedApplication].keyWindow.rootViewController;
262 [self hideSubviewsInOriginalAccessoryView:inputAccessoryView]; 270
271 // If the keyboard isn't visible or another controller has been presented
272 // over the webview (as in the case of the credit card unmask dialog), don't
273 // show the custom view.
274 if (CGRectIntersection([UIScreen mainScreen].bounds, _keyboardFrame)
275 .size.height == 0 ||
276 CGRectEqualToRect(_keyboardFrame, CGRectZero) ||
277 [rootViewController presentedViewController]) {
278 _customAccessoryView.reset();
279 return;
280 }
281
282 CGFloat height = autofill::kInputAccessoryHeight;
283 CGRect frame = CGRectMake(_keyboardFrame.origin.x, -height,
284 _keyboardFrame.size.width, height);
263 _customAccessoryView.reset( 285 _customAccessoryView.reset(
264 [[FormInputAccessoryView alloc] initWithFrame:inputAccessoryView.frame 286 [[FormInputAccessoryView alloc] initWithFrame:frame customView:view]);
265 delegate:self 287 [[self getKeyboardView] addSubview:_customAccessoryView];
justincohen 2015/08/19 20:34:19 Is there a way we can DCHECK if something changes
Justin Donnelly 2015/08/20 16:38:01 Done.
266 customView:view 288 } else {
267 leftFrame:leftFrame 289 // On all other versions, the custom view replaces the default UI of the
268 rightFrame:rightFrame]); 290 // inputAccessoryView.
269 [inputAccessoryView addSubview:_customAccessoryView]; 291 [self restoreDefaultInputAccessoryView];
292 CGRect leftFrame;
293 CGRect rightFrame;
294 UIView* inputAccessoryView = [self.webViewProxy getKeyboardAccessory];
295 if (ComputeFramesOfKeyboardParts(inputAccessoryView, &leftFrame,
296 &rightFrame)) {
297 [self hideSubviewsInOriginalAccessoryView:inputAccessoryView];
298 _customAccessoryView.reset([[FormInputAccessoryView alloc]
299 initWithFrame:inputAccessoryView.frame
300 delegate:self
301 customView:view
302 leftFrame:leftFrame
303 rightFrame:rightFrame]);
304 [inputAccessoryView addSubview:_customAccessoryView];
305 }
270 } 306 }
271 } 307 }
272 308
273 - (void)restoreDefaultInputAccessoryView { 309 - (void)restoreDefaultInputAccessoryView {
274 [_customAccessoryView removeFromSuperview]; 310 [_customAccessoryView removeFromSuperview];
275 _customAccessoryView.reset(); 311 _customAccessoryView.reset();
276 for (UIView* subview in _hiddenOriginalSubviews.get()) { 312 for (UIView* subview in _hiddenOriginalSubviews.get()) {
277 subview.hidden = NO; 313 subview.hidden = NO;
278 } 314 }
279 [_hiddenOriginalSubviews removeAllObjects]; 315 [_hiddenOriginalSubviews removeAllObjects];
(...skipping 194 matching lines...) Expand 10 before | Expand all | Expand 10 after
474 webState:webState 510 webState:webState
475 accessoryViewUpdateBlock:readyCompletion]; 511 accessoryViewUpdateBlock:readyCompletion];
476 }; 512 };
477 513
478 // Run all the blocks in |findProviderBlocks| until one invokes its 514 // Run all the blocks in |findProviderBlocks| until one invokes its
479 // completion with YES. The first one to do so will be passed to 515 // completion with YES. The first one to do so will be passed to
480 // |onProviderFound|. 516 // |onProviderFound|.
481 passwords::RunSearchPipeline(findProviderBlocks, onProviderFound); 517 passwords::RunSearchPipeline(findProviderBlocks, onProviderFound);
482 } 518 }
483 519
484 - (void)keyboardDidChangeFrame:(NSNotification*)notification { 520 - (UIView*)getKeyboardView {
521 NSArray* windows = [UIApplication sharedApplication].windows;
522 if (windows.count < 2)
523 return nil;
524
525 UIWindow* window = windows[1];
526 for (UIView* subview in window.subviews) {
527 if ([NSStringFromClass([subview class])
528 isEqualToString:@"UIPeripheralHost"]) {
529 return subview;
530 }
531 if ([NSStringFromClass([subview class])
532 isEqualToString:@"UIInputSetContainerView"]) {
533 for (UIView* subsubview in subview.subviews) {
534 if ([NSStringFromClass([subsubview class])
535 isEqualToString:@"UIInputSetHostView"]) {
536 return subsubview;
537 }
538 }
539 }
540 }
541
542 return nil;
543 }
544
545 - (void)keyboardWillOrDidChangeFrame:(NSNotification*)notification {
485 if (!self.webState || !_currentProvider) 546 if (!self.webState || !_currentProvider)
486 return; 547 return;
487 CGRect keyboardFrame = 548 CGRect keyboardFrame =
488 [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue]; 549 [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
489 // With iOS8 (beta) this method can be called even when the rect has not 550 // With iOS8 (beta) this method can be called even when the rect has not
490 // changed. When this is detected we exit early. 551 // changed. When this is detected we exit early.
491 if (CGRectEqualToRect(CGRectIntegral(_keyboardFrame), 552 if (CGRectEqualToRect(CGRectIntegral(_keyboardFrame),
492 CGRectIntegral(keyboardFrame))) { 553 CGRectIntegral(keyboardFrame))) {
493 return; 554 return;
494 } 555 }
495 _keyboardFrame = keyboardFrame; 556 _keyboardFrame = keyboardFrame;
496 [_currentProvider resizeAccessoryView]; 557 [_currentProvider resizeAccessoryView];
497 } 558 }
498 559
499 - (void)keyboardDidHide:(NSNotification*)notification { 560 - (void)keyboardDidHide:(NSNotification*)notification {
500 _keyboardFrame = CGRectZero; 561 _keyboardFrame = CGRectZero;
501 } 562 }
502 563
503 @end 564 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698