blob: 0818252e56f72731ae7261b05156399e372ac8b8 [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 "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"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
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";
}
- (void)selectNextElement {
[self selectElementAfterForm:@"" field:@""];
}
- (void)selectElementAfterForm:(NSString*)formName field:(NSString*)fieldName {
NSString* selectNextElementJS = [NSString
stringWithFormat:@"__gCrWeb.suggestion.selectNextElement(%@, %@)",
JSONEscape(formName), JSONEscape(fieldName)];
[self executeJavaScript:selectNextElementJS completionHandler: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 executeJavaScript:selectPreviousElementJS completionHandler: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]);
NSString* escapedFormName = JSONEscape(formName);
NSString* escapedFieldName = JSONEscape(fieldName);
NSString* JS = [NSString
stringWithFormat:@"[__gCrWeb.suggestion.hasPreviousElement(%@, %@),"
@"__gCrWeb.suggestion.hasNextElement(%@, %@)]"
@".toString()",
escapedFormName, escapedFieldName, escapedFormName,
escapedFieldName];
[self executeJavaScript:JS completionHandler:^(id 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);
}];
}
- (void)closeKeyboard {
[self executeJavaScript:@"document.activeElement.blur()"
completionHandler:nil];
}
@end