1// Copyright 2018 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 "components/password_manager/ios/js_password_manager.h"
6
7#include "base/json/json_writer.h"
8#include "base/json/string_escape.h"
9#include "base/logging.h"
10#include "base/mac/foundation_util.h"
11#include "base/strings/sys_string_conversions.h"
12#include "base/values.h"
13#include "components/autofill/core/common/password_form_fill_data.h"
14#include "components/password_manager/ios/account_select_fill_data.h"
15
16#if !defined(__has_feature) || !__has_feature(objc_arc)
17#error "This file requires ARC support."
18#endif
19
20namespace password_manager {
21
22NSString* SerializeFillData(const GURL& origin,
23                            const base::string16& name,
24                            const base::string16& username_element,
25                            const base::string16& username_value,
26                            const base::string16& password_element,
27                            const base::string16& password_value) {
28  base::DictionaryValue rootDict;
29  rootDict.SetString("origin", origin.spec());
30  rootDict.SetString("name", name);
31
32  auto fieldList = std::make_unique<base::ListValue>();
33
34  auto usernameField = std::make_unique<base::DictionaryValue>();
35  usernameField->SetString("name", username_element);
36  usernameField->SetString("value", username_value);
37  fieldList->Append(std::move(usernameField));
38
39  auto passwordField = std::make_unique<base::DictionaryValue>();
40  passwordField->SetString("name", password_element);
41  passwordField->SetString("value", password_value);
42  fieldList->Append(std::move(passwordField));
43
44  rootDict.Set("fields", std::move(fieldList));
45
46  std::string jsonString;
47  base::JSONWriter::Write(rootDict, &jsonString);
48  return base::SysUTF8ToNSString(jsonString);
49}
50
51NSString* SerializePasswordFormFillData(
52    const autofill::PasswordFormFillData& formData) {
53  return SerializeFillData(
54      formData.origin, formData.name, formData.username_field.name,
55      formData.username_field.value, formData.password_field.name,
56      formData.password_field.value);
57}
58
59NSString* SerializeFillData(const password_manager::FillData& fillData) {
60  return SerializeFillData(fillData.origin, fillData.name,
61                           fillData.username_element, fillData.username_value,
62                           fillData.password_element, fillData.password_value);
63}
64
65}  // namespace password_manager
66
67namespace {
68// Sanitizes |JSONString| and wraps it in quotes so it can be injected safely in
69// JavaScript.
70NSString* JSONEscape(NSString* JSONString) {
71  return base::SysUTF8ToNSString(
72      base::GetQuotedJSONString(base::SysNSStringToUTF8(JSONString)));
73}
74}  // namespace
75
76@implementation JsPasswordManager {
77  // The injection receiver used to evaluate JavaScript.
78  __weak CRWJSInjectionReceiver* _receiver;
79}
80
81- (instancetype)initWithReceiver:(CRWJSInjectionReceiver*)receiver {
82  DCHECK(receiver);
83  self = [super init];
84  if (self) {
85    _receiver = receiver;
86  }
87  return self;
88}
89
90- (void)findPasswordFormsWithCompletionHandler:
91    (void (^)(NSString*))completionHandler {
92  DCHECK(completionHandler);
93  [_receiver executeJavaScript:@"__gCrWeb.passwords.findPasswordForms()"
94             completionHandler:^(id result, NSError*) {
95               completionHandler(base::mac::ObjCCastStrict<NSString>(result));
96             }];
97}
98
99- (void)extractForm:(NSString*)formName
100      completionHandler:(void (^)(NSString*))completionHandler {
101  DCHECK(completionHandler);
102  NSString* extra = [NSString
103      stringWithFormat:@"__gCrWeb.passwords.getPasswordFormDataAsString(%@)",
104                       JSONEscape(formName)];
105  [_receiver executeJavaScript:extra
106             completionHandler:^(id result, NSError*) {
107               completionHandler(base::mac::ObjCCastStrict<NSString>(result));
108             }];
109}
110
111- (void)fillPasswordForm:(NSString*)JSONString
112            withUsername:(NSString*)username
113                password:(NSString*)password
114       completionHandler:(void (^)(BOOL))completionHandler {
115  DCHECK(completionHandler);
116  NSString* script = [NSString
117      stringWithFormat:@"__gCrWeb.passwords.fillPasswordForm(%@, %@, %@)",
118                       JSONString, JSONEscape(username), JSONEscape(password)];
119  [_receiver executeJavaScript:script
120             completionHandler:^(id result, NSError*) {
121               completionHandler([result isEqual:@YES]);
122             }];
123}
124
125- (void)fillPasswordForm:(NSString*)formName
126        newPasswordIdentifier:(NSString*)newPasswordIdentifier
127    confirmPasswordIdentifier:(NSString*)confirmPasswordIdentifier
128            generatedPassword:(NSString*)generatedPassword
129            completionHandler:(void (^)(BOOL))completionHandler {
130  NSString* script = [NSString
131      stringWithFormat:@"__gCrWeb.passwords."
132                       @"fillPasswordFormWithGeneratedPassword(%@, %@, %@, %@)",
133                       JSONEscape(formName), JSONEscape(newPasswordIdentifier),
134                       JSONEscape(confirmPasswordIdentifier),
135                       JSONEscape(generatedPassword)];
136  [_receiver executeJavaScript:script
137             completionHandler:^(id result, NSError*) {
138               completionHandler([result isEqual:@YES]);
139             }];
140}
141
142@end
143