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

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

Issue 1022463002: [iOS] Upstream files in //ios/chrome/browser/autofill (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebase Created 5 years, 9 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
(Empty)
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
3 // found in the LICENSE file.
4
5 #import "ios/chrome/browser/autofill/form_suggestion_controller.h"
6
7 #include "base/ios/weak_nsobject.h"
8 #include "base/mac/foundation_util.h"
9 #include "base/mac/scoped_block.h"
10 #include "base/mac/scoped_nsobject.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/strings/sys_string_conversions.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "components/autofill/core/browser/autofill_popup_delegate.h"
15 #import "components/autofill/ios/browser/form_suggestion.h"
16 #import "ios/chrome/browser/autofill/form_input_accessory_view_controller.h"
17 #import "ios/chrome/browser/autofill/form_suggestion_provider.h"
18 #import "ios/chrome/browser/autofill/form_suggestion_view.h"
19 #import "ios/chrome/browser/passwords/password_generation_utils.h"
20 #include "ios/web/public/url_scheme_util.h"
21 #import "ios/web/public/web_state/crw_web_view_proxy.h"
22 #import "ios/web/public/web_state/js/crw_js_injection_receiver.h"
23 #import "ios/web/public/web_state/web_state.h"
24
25 namespace {
26
27 // Struct that describes suggestion state.
28 struct AutofillSuggestionState {
29 AutofillSuggestionState(const std::string& form_name,
30 const std::string& field_name,
31 const std::string& typed_value);
32 // The name of the form for autofill.
33 std::string form_name;
34 // The name of the field for autofill.
35 std::string field_name;
36 // The user-typed value in the field.
37 std::string typed_value;
38 // The suggestions for the form field. An array of |FormSuggestion|.
39 base::scoped_nsobject<NSArray> suggestions;
40 };
41
42 AutofillSuggestionState::AutofillSuggestionState(const std::string& form_name,
43 const std::string& field_name,
44 const std::string& typed_value)
45 : form_name(form_name), field_name(field_name), typed_value(typed_value) {
46 }
47
48 } // namespace
49
50 @interface FormSuggestionController () <FormInputAccessoryViewProvider> {
51 // Form navigation delegate.
52 base::WeakNSProtocol<id<FormInputAccessoryViewDelegate>> _delegate;
53
54 // Callback to update the accessory view.
55 base::mac::ScopedBlock<AccessoryViewReadyCompletion> completionHandler_;
56
57 // Autofill suggestion state.
58 scoped_ptr<AutofillSuggestionState> _suggestionState;
59
60 // Providers for suggestions, sorted according to the order in which
61 // they should be asked for suggestions, with highest priority in front.
62 base::scoped_nsobject<NSArray> _suggestionProviders;
63
64 // Access to WebView from the CRWWebController.
65 base::scoped_nsprotocol<id<CRWWebViewProxy>> _webViewProxy;
66 }
67
68 // Returns an autoreleased input accessory view that shows |suggestions|.
69 - (UIView*)suggestionViewWithSuggestions:(NSArray*)suggestions;
70
71 // Updates keyboard for |suggestionState|.
72 - (void)updateKeyboard:(AutofillSuggestionState*)suggestionState;
73
74 // Updates keyboard with |suggestions|.
75 - (void)updateKeyboardWithSuggestions:(NSArray*)suggestions;
76
77 // Clears state in between page loads.
78 - (void)resetSuggestionState;
79
80 // Finds a FormSuggestionProvider that can supply suggestions for the specified
81 // form, requests them, and updates the view accordingly.
82 - (void)retrieveSuggestionsForFormNamed:(const std::string&)formName
83 fieldName:(const std::string&)fieldName
84 type:(const std::string&)type
85 webState:(web::WebState*)webState;
86
87 @end
88
89 @implementation FormSuggestionController {
90 // Bridge to observe the web state from Objective-C.
91 scoped_ptr<web::WebStateObserverBridge> _webStateObserverBridge;
92
93 // Manager for FormSuggestion JavaScripts.
94 base::scoped_nsobject<JsSuggestionManager> _jsSuggestionManager;
95
96 // The provider for the current set of suggestions.
97 __weak id<FormSuggestionProvider> _provider;
98 }
99
100 - (instancetype)initWithWebState:(web::WebState*)webState
101 providers:(NSArray*)providers
102 JsSuggestionManager:(JsSuggestionManager*)jsSuggestionManager {
103 self = [super init];
104 if (self) {
105 _webStateObserverBridge.reset(
106 new web::WebStateObserverBridge(webState, self));
107 _webViewProxy.reset([webState->GetWebViewProxy() retain]);
108 _jsSuggestionManager.reset([jsSuggestionManager retain]);
109 _suggestionProviders.reset([providers copy]);
110 }
111 return self;
112 }
113
114 - (instancetype)initWithWebState:(web::WebState*)webState
115 providers:(NSArray*)providers {
116 JsSuggestionManager* jsSuggestionManager =
117 base::mac::ObjCCast<JsSuggestionManager>(
118 [webState->GetJSInjectionReceiver()
119 instanceOfClass:[JsSuggestionManager class]]);
120 return [self initWithWebState:webState
121 providers:providers
122 JsSuggestionManager:jsSuggestionManager];
123 }
124
125 - (void)detachFromWebState {
126 _webStateObserverBridge.reset();
127 }
128
129 #pragma mark -
130 #pragma mark CRWWebStateObserver
131
132 - (void)webStateDestroyed:(web::WebState*)webState {
133 [self detachFromWebState];
134 }
135
136 - (void)pageLoaded:(web::WebState*)webState {
137 [self processPage:webState];
138 }
139
140 - (void)processPage:(web::WebState*)webState {
141 [self resetSuggestionState];
142
143 web::URLVerificationTrustLevel trustLevel =
144 web::URLVerificationTrustLevel::kNone;
145 const GURL pageURL(webState->GetCurrentURL(&trustLevel));
146 if (trustLevel != web::URLVerificationTrustLevel::kAbsolute) {
147 DLOG(WARNING) << "Page load not handled on untrusted page";
148 return;
149 }
150
151 if (web::UrlHasWebScheme(pageURL) && webState->ContentIsHTML())
152 [_jsSuggestionManager inject];
153 }
154
155 - (void)setWebViewProxy:(id<CRWWebViewProxy>)webViewProxy {
156 _webViewProxy.reset([webViewProxy retain]);
157 }
158
159 - (void)retrieveSuggestionsForFormNamed:(const std::string&)formName
160 fieldName:(const std::string&)fieldName
161 type:(const std::string&)type
162 webState:(web::WebState*)webState {
163 base::WeakNSObject<FormSuggestionController> weakSelf(self);
164 base::scoped_nsobject<NSString> strongFormName(
165 [base::SysUTF8ToNSString(formName) copy]);
166 base::scoped_nsobject<NSString> strongFieldName(
167 [base::SysUTF8ToNSString(fieldName) copy]);
168 base::scoped_nsobject<NSString> strongType(
169 [base::SysUTF8ToNSString(type) copy]);
170 base::scoped_nsobject<NSString> strongValue(
171 [base::SysUTF8ToNSString(_suggestionState.get()->typed_value) copy]);
172
173 // Build a block for each provider that will invoke its completion with YES
174 // if the provider can provide suggestions for the specified form/field/type
175 // and NO otherwise.
176 base::scoped_nsobject<NSMutableArray> findProviderBlocks(
177 [[NSMutableArray alloc] init]);
178 for (NSUInteger i = 0; i < [_suggestionProviders count]; i++) {
179 base::mac::ScopedBlock<passwords::PipelineBlock> block(
180 ^(void (^completion)(BOOL success)) {
181 // Access all the providers through |self| to guarantee that both
182 // |self| and all the providers exist when the block is executed.
183 // |_suggestionProviders| is immutable, so the subscripting is
184 // always valid.
185 base::scoped_nsobject<FormSuggestionController> strongSelf(
186 [weakSelf retain]);
187 if (!strongSelf)
188 return;
189 id<FormSuggestionProvider> provider =
190 strongSelf.get()->_suggestionProviders[i];
191 [provider checkIfSuggestionsAvailableForForm:strongFormName
192 field:strongFieldName
193 type:strongType
194 typedValue:strongValue
195 webState:webState
196 completionHandler:completion];
197 },
198 base::scoped_policy::RETAIN);
199 [findProviderBlocks addObject:block];
200 }
201
202 // Once the suggestions are retrieved, update the suggestions UI.
203 SuggestionsReadyCompletion readyCompletion =
204 ^(NSArray* suggestions, id<FormSuggestionProvider> provider) {
205 [weakSelf onSuggestionsReady:suggestions provider:provider];
206 };
207
208 // Once a provider is found, use it to retrieve suggestions.
209 passwords::PipelineCompletionBlock completion = ^(NSUInteger providerIndex) {
210 if (providerIndex == NSNotFound)
211 return;
212 base::scoped_nsobject<FormSuggestionController> strongSelf(
213 [weakSelf retain]);
214 if (!strongSelf)
215 return;
216 id<FormSuggestionProvider> provider =
217 strongSelf.get()->_suggestionProviders[providerIndex];
218 [provider retrieveSuggestionsForForm:strongFormName
219 field:strongFieldName
220 type:strongType
221 typedValue:strongValue
222 webState:webState
223 completionHandler:readyCompletion];
224 };
225
226 // Run all the blocks in |findProviderBlocks| until one invokes its
227 // completion with YES. The first one to do so will be passed to
228 // |onProviderFound|.
229 passwords::RunSearchPipeline(findProviderBlocks, completion);
230 }
231
232 - (void)onSuggestionsReady:(NSArray*)suggestions
233 provider:(id<FormSuggestionProvider>)provider {
234 // TODO(ios): crbug.com/249916. If we can also pass in the form/field for
235 // which |sugguestions| are, we should check here if |suggestions| are for
236 // the current active element. If not, reset |_suggestionState|.
237 if (!_suggestionState) {
238 // The suggestion state was reset in between the call to Autofill API (e.g.
239 // OnQueryFormFieldAutofill) and this method being called back. Results are
240 // therefore no longer relevant.
241 return;
242 }
243
244 _provider = provider;
245 _suggestionState->suggestions.reset([suggestions copy]);
246 [self updateKeyboard:_suggestionState.get()];
247 }
248
249 - (void)resetSuggestionState {
250 _provider = nil;
251 _suggestionState.reset();
252 }
253
254 - (void)clearSuggestions {
255 // Note that other parts of the suggestionsState are not reset.
256 if (!_suggestionState.get())
257 return;
258 _suggestionState->suggestions.reset([[NSArray alloc] init]);
259 [self updateKeyboard:_suggestionState.get()];
260 }
261
262 - (void)updateKeyboard:(AutofillSuggestionState*)suggestionState {
263 if (!_suggestionState) {
264 if (completionHandler_)
265 completionHandler_.get()(nil, self);
266 } else {
267 [self updateKeyboardWithSuggestions:_suggestionState->suggestions];
268 }
269 }
270
271 - (void)updateKeyboardWithSuggestions:(NSArray*)suggestions {
272 if (completionHandler_)
273 completionHandler_.get()([self suggestionViewWithSuggestions:suggestions],
274 self);
275 }
276
277 - (UIView*)suggestionViewWithSuggestions:(NSArray*)suggestions {
278 base::scoped_nsobject<FormSuggestionView> view([[FormSuggestionView alloc]
279 initWithFrame:[_webViewProxy getKeyboardAccessory].frame
280 client:self
281 suggestions:suggestions]);
282 return view.autorelease();
283 }
284
285 - (void)didSelectSuggestion:(FormSuggestion*)suggestion {
286 if (!_suggestionState)
287 return;
288
289 // Send the suggestion to the provider and advance the cursor.
290 base::WeakNSObject<FormSuggestionController> weakSelf(self);
291 [_provider
292 didSelectSuggestion:suggestion
293 forField:base::SysUTF8ToNSString(_suggestionState->field_name)
294 form:base::SysUTF8ToNSString(_suggestionState->form_name)
295 completionHandler:^{
296 [[weakSelf accessoryViewDelegate] selectNextElement];
297 }];
298 _provider = nil;
299 }
300
301 - (id<FormInputAccessoryViewProvider>)accessoryViewProvider {
302 return self;
303 }
304
305 #pragma mark FormInputAccessoryViewProvider
306
307 - (id<FormInputAccessoryViewDelegate>)accessoryViewDelegate {
308 return _delegate.get();
309 }
310
311 - (void)setAccessoryViewDelegate:(id<FormInputAccessoryViewDelegate>)delegate {
312 _delegate.reset(delegate);
313 }
314
315 - (void)checkIfAccessoryViewAvailableForFormNamed:(const std::string&)formName
316 fieldName:(const std::string&)fieldName
317 webState:(web::WebState*)webState
318 completionHandler:
319 (AccessoryViewAvailableCompletion)
320 completionHandler {
321 [self processPage:webState];
322 completionHandler(YES);
323 }
324
325 - (void)retrieveAccessoryViewForFormNamed:(const std::string&)formName
326 fieldName:(const std::string&)fieldName
327 value:(const std::string&)value
328 type:(const std::string&)type
329 webState:(web::WebState*)webState
330 completionHandler:
331 (AccessoryViewReadyCompletion)completionHandler {
332 _suggestionState.reset(
333 new AutofillSuggestionState(formName, fieldName, value));
334 completionHandler([self suggestionViewWithSuggestions:@[]], self);
335 completionHandler_.reset([completionHandler copy]);
336 [self retrieveSuggestionsForFormNamed:formName
337 fieldName:fieldName
338 type:type
339 webState:webState];
340 }
341
342 - (void)inputAccessoryViewControllerDidReset:
343 (FormInputAccessoryViewController*)controller {
344 completionHandler_.reset();
345 [self resetSuggestionState];
346 }
347
348 - (void)resizeAccessoryView {
349 [self updateKeyboard:_suggestionState.get()];
350 }
351
352 @end
OLDNEW
« no previous file with comments | « ios/chrome/browser/autofill/form_suggestion_controller.h ('k') | ios/chrome/browser/autofill/form_suggestion_label.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698