Index: components/autofill/ios/browser/js_suggestion_manager.mm |
diff --git a/components/autofill/ios/browser/js_suggestion_manager.mm b/components/autofill/ios/browser/js_suggestion_manager.mm |
new file mode 100644 |
index 0000000000000000000000000000000000000000..51d2a62aacb8e221a33bed90037c7f6d1d47d3b8 |
--- /dev/null |
+++ b/components/autofill/ios/browser/js_suggestion_manager.mm |
@@ -0,0 +1,115 @@ |
+// 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 "components/autofill/ios/browser/js_suggestion_manager.h" |
+ |
+#include "base/format_macros.h" |
+#include "base/json/string_escape.h" |
+#include "base/logging.h" |
+#include "base/strings/sys_string_conversions.h" |
+#import "ios/web/public/web_state/js/crw_js_early_script_manager.h" |
+ |
+namespace { |
+// Santizies |str| and wraps it in quotes so it can be injected safely in |
+// JavaScript. |
+NSString* JSONEscape(NSString* str) { |
+ return base::SysUTF8ToNSString( |
+ base::GetQuotedJSONString(base::SysNSStringToUTF8(str))); |
+} |
+} // namespace |
+ |
+@implementation JsSuggestionManager |
+ |
+#pragma mark - |
+#pragma mark ProtectedMethods |
+ |
+- (NSString*)scriptPath { |
+ return @"suggestion_controller"; |
+} |
+ |
+- (NSString*)presenceBeacon { |
+ return @"__gCrWeb.suggestion"; |
+} |
+ |
+- (NSArray*)directDependencies { |
+ return @[ [CRWJSEarlyScriptManager class] ]; |
+} |
+ |
+- (void)selectNextElement { |
+ [self selectElementAfterForm:@"" field:@""]; |
+} |
+ |
+- (void)selectElementAfterForm:(NSString*)formName field:(NSString*)fieldName { |
+ NSString* selectNextElementJS = [NSString |
+ stringWithFormat:@"__gCrWeb.suggestion.selectNextElement(%@, %@)", |
+ JSONEscape(formName), JSONEscape(fieldName)]; |
+ [self evaluate:selectNextElementJS stringResultHandler:nil]; |
+} |
+ |
+- (void)selectPreviousElement { |
+ [self selectElementBeforeForm:@"" field:@""]; |
+} |
+ |
+- (void)selectElementBeforeForm:(NSString*)formName field:(NSString*)fieldName { |
+ NSString* selectPreviousElementJS = [NSString |
+ stringWithFormat:@"__gCrWeb.suggestion.selectPreviousElement(%@, %@)", |
+ JSONEscape(formName), JSONEscape(fieldName)]; |
+ [self evaluate:selectPreviousElementJS stringResultHandler:nil]; |
+} |
+ |
+- (void)fetchPreviousAndNextElementsPresenceWithCompletionHandler: |
+ (void (^)(BOOL, BOOL))completionHandler { |
+ [self fetchPreviousAndNextElementsPresenceForForm:@"" |
+ field:@"" |
+ completionHandler:completionHandler]; |
+} |
+ |
+- (void)fetchPreviousAndNextElementsPresenceForForm:(NSString*)formName |
+ field:(NSString*)fieldName |
+ completionHandler: |
+ (void (^)(BOOL, BOOL))completionHandler { |
+ DCHECK(completionHandler); |
+ DCHECK([self hasBeenInjected]); |
+ id stringResultHandler = ^(NSString* result, NSError* error) { |
+ // The result maybe an empty string here due to 2 reasons: |
+ // 1) When there is an exception running the JS |
+ // 2) There is a race when the page is changing due to which |
+ // JSSuggestionManager has not yet injected __gCrWeb.suggestion object |
+ // Handle this case gracefully. |
+ // TODO(shreyasv): Figure out / narrow down further why this occurs. |
+ // crbug.com/432217. |
+ // If a page has overridden Array.toString, the string returned may not |
+ // contain a ",", hence this is a defensive measure to early return. |
+ NSArray* components = [result componentsSeparatedByString:@","]; |
+ if (components.count != 2) { |
+ completionHandler(NO, NO); |
+ return; |
+ } |
+ |
+ DCHECK([components[0] isEqualToString:@"true"] || |
+ [components[0] isEqualToString:@"false"]); |
+ BOOL hasPreviousElement = [components[0] isEqualToString:@"true"]; |
+ DCHECK([components[1] isEqualToString:@"true"] || |
+ [components[1] isEqualToString:@"false"]); |
+ BOOL hasNextElement = [components[1] isEqualToString:@"true"]; |
+ completionHandler(hasPreviousElement, hasNextElement); |
+ }; |
+ NSString* escapedFormName = JSONEscape(formName); |
+ NSString* escapedFieldName = JSONEscape(fieldName); |
+ NSString* js = [NSString |
+ stringWithFormat:@"[__gCrWeb.suggestion.hasPreviousElement(%@, %@)," |
+ @"__gCrWeb.suggestion.hasNextElement(%@, %@)]" |
+ @".toString()", |
+ escapedFormName, escapedFieldName, escapedFormName, |
+ escapedFieldName]; |
+ [self evaluate:js stringResultHandler:stringResultHandler]; |
+} |
+ |
+- (void)closeKeyboard { |
+ // Deferred execution used because of a risk of crwebinvoke:// triggered |
+ // immediately by the loss of focus. |
+ [self deferredEvaluate:@"document.activeElement.blur()"]; |
+} |
+ |
+@end |