blob: 2b207468e328cfafa7e8f46289b0e5d533b1eb33 [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"
#import <Foundation/Foundation.h>
#include <vector>
#include "base/bind.h"
#include "base/callback.h"
#include "base/check.h"
#include "base/format_macros.h"
#include "base/json/string_escape.h"
#include "base/strings/sys_string_conversions.h"
#include "base/values.h"
#import "components/autofill/ios/browser/autofill_util.h"
#include "ios/web/public/js_messaging/web_frame.h"
#include "ios/web/public/js_messaging/web_frames_manager.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace autofill {
JsSuggestionManager::JsSuggestionManager(web::WebState* web_state)
: web_state_(web_state), weak_ptr_factory_(this) {}
JsSuggestionManager::~JsSuggestionManager() = default;
// static
JsSuggestionManager* JsSuggestionManager::GetOrCreateForWebState(
web::WebState* web_state) {
JsSuggestionManager* helper = FromWebState(web_state);
if (!helper) {
CreateForWebState(web_state);
helper = FromWebState(web_state);
DCHECK(helper);
}
return helper;
}
void JsSuggestionManager::SelectNextElementInFrameWithID(
const std::string& frame_ID) {
SelectNextElementInFrameWithID(frame_ID, "", "");
}
void JsSuggestionManager::SelectNextElementInFrameWithID(
const std::string& frame_ID,
const std::string& form_name,
const std::string& field_name) {
std::vector<base::Value> parameters;
parameters.push_back(base::Value(form_name));
parameters.push_back(base::Value(field_name));
autofill::ExecuteJavaScriptFunction("suggestion.selectNextElement",
parameters, GetFrameWithFrameID(frame_ID),
autofill::JavaScriptResultCallback());
}
void JsSuggestionManager::SelectPreviousElementInFrameWithID(
const std::string& frame_ID) {
SelectPreviousElementInFrameWithID(frame_ID, "", "");
}
void JsSuggestionManager::SelectPreviousElementInFrameWithID(
const std::string& frame_ID,
const std::string& form_name,
const std::string& field_name) {
std::vector<base::Value> parameters;
parameters.push_back(base::Value(form_name));
parameters.push_back(base::Value(field_name));
autofill::ExecuteJavaScriptFunction("suggestion.selectPreviousElement",
parameters, GetFrameWithFrameID(frame_ID),
autofill::JavaScriptResultCallback());
}
void JsSuggestionManager::FetchPreviousAndNextElementsPresenceInFrameWithID(
const std::string& frame_ID,
base::OnceCallback<void(bool, bool)> completion_handler) {
FetchPreviousAndNextElementsPresenceInFrameWithID(
frame_ID, "", "", std::move(completion_handler));
}
void JsSuggestionManager::FetchPreviousAndNextElementsPresenceInFrameWithID(
const std::string& frame_ID,
const std::string& form_name,
const std::string& field_name,
base::OnceCallback<void(bool, bool)> completion_handler) {
DCHECK(completion_handler);
std::vector<base::Value> parameters;
parameters.push_back(base::Value(form_name));
parameters.push_back(base::Value(field_name));
autofill::ExecuteJavaScriptFunction(
"suggestion.hasPreviousNextElements", parameters,
GetFrameWithFrameID(frame_ID),
base::BindOnce(
&JsSuggestionManager::PreviousAndNextElementsPresenceResult,
weak_ptr_factory_.GetWeakPtr(), std::move(completion_handler)));
}
void JsSuggestionManager::PreviousAndNextElementsPresenceResult(
base::OnceCallback<void(bool, bool)> completion_handler,
const base::Value* res) {
NSString* result = nil;
if (res && res->is_string()) {
result = base::SysUTF8ToNSString(res->GetString());
}
// 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. 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) {
std::move(completion_handler).Run(false, false);
return;
}
DCHECK([components[0] isEqualToString:@"true"] ||
[components[0] isEqualToString:@"false"]);
bool has_previous_element = [components[0] isEqualToString:@"true"];
DCHECK([components[1] isEqualToString:@"true"] ||
[components[1] isEqualToString:@"false"]);
bool has_next_element = [components[1] isEqualToString:@"true"];
std::move(completion_handler).Run(has_previous_element, has_next_element);
}
void JsSuggestionManager::CloseKeyboardForFrameWithID(
const std::string& frame_ID) {
std::vector<base::Value> parameters;
autofill::ExecuteJavaScriptFunction("suggestion.blurActiveElement",
parameters, GetFrameWithFrameID(frame_ID),
autofill::JavaScriptResultCallback());
}
web::WebFrame* JsSuggestionManager::GetFrameWithFrameID(
const std::string& frame_ID) {
return web_state_->GetWebFramesManager()->GetFrameWithId(frame_ID);
}
WEB_STATE_USER_DATA_KEY_IMPL(JsSuggestionManager)
} // namspace autofill