1 /*
2 * Copyright 2010, The Android Open Source Project
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26 #include "config.h"
27 #include "WebAutofill.h"
28
29 #if ENABLE(WEB_AUTOFILL)
30
31 #include "AutoFillHostAndroid.h"
32 #include "Frame.h"
33 #include "FormData.h"
34 #include "FormManagerAndroid.h"
35 #include "FrameLoader.h"
36 #include "HTMLFormControlElement.h"
37 #include "MainThreadProxy.h"
38 #include "Node.h"
39 #include "Page.h"
40 #include "Settings.h"
41 #include "WebFrame.h"
42 #include "WebRequestContext.h"
43 #include "WebUrlLoaderClient.h"
44 #include "WebViewCore.h"
45
46 #define NO_PROFILE_SET 0
47 #define FORM_NOT_AUTOFILLABLE -1
48
49 namespace android
50 {
WebAutofill()51 WebAutofill::WebAutofill()
52 : mQueryId(1)
53 , mWebViewCore(0)
54 , mLastSearchDomVersion(0)
55 , mParsingForms(false)
56 {
57 mTabContents = new TabContents();
58 setEmptyProfile();
59 }
60
init()61 void WebAutofill::init()
62 {
63 if (mAutofillManager)
64 return;
65
66 mFormManager = new FormManager();
67 // We use the WebView's WebRequestContext, which may be a private browsing context.
68 ASSERT(mWebViewCore);
69 mAutofillManager = new AutofillManager(mTabContents.get());
70 mAutofillHost = new AutoFillHostAndroid(this);
71 mTabContents->SetProfileRequestContext(new AndroidURLRequestContextGetter(mWebViewCore->webRequestContext(), WebUrlLoaderClient::ioThread()));
72 mTabContents->SetAutoFillHost(mAutofillHost.get());
73 }
74
~WebAutofill()75 WebAutofill::~WebAutofill()
76 {
77 cleanUpQueryMap();
78 mUniqueIdMap.clear();
79 }
80
cleanUpQueryMap()81 void WebAutofill::cleanUpQueryMap()
82 {
83 for (AutofillQueryFormDataMap::iterator it = mQueryMap.begin(); it != mQueryMap.end(); it++)
84 delete it->second;
85 mQueryMap.clear();
86 }
87
searchDocument(WebCore::Frame * frame)88 void WebAutofill::searchDocument(WebCore::Frame* frame)
89 {
90 if (!enabled())
91 return;
92
93 MutexLocker lock(mFormsSeenMutex);
94
95 init();
96
97 cleanUpQueryMap();
98 mUniqueIdMap.clear();
99 mForms.clear();
100 mQueryId = 1;
101
102 ASSERT(mFormManager);
103 ASSERT(mAutofillManager);
104
105 mAutofillManager->Reset();
106 mFormManager->Reset();
107
108 mFormManager->ExtractForms(frame);
109 mFormManager->GetFormsInFrame(frame, FormManager::REQUIRE_AUTOCOMPLETE, &mForms);
110
111 // Needs to be done on a Chrome thread as it will make a URL request to the Autofill server.
112 // TODO: Use our own Autofill thread instead of the IO thread.
113 // TODO: For now, block here. Would like to make this properly async.
114 base::Thread* thread = WebUrlLoaderClient::ioThread();
115 mParsingForms = true;
116 thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(this, &WebAutofill::formsSeenImpl));
117 while (mParsingForms)
118 mFormsSeenCondition.wait(mFormsSeenMutex);
119 }
120
121 // Called on the Chromium IO thread.
formsSeenImpl()122 void WebAutofill::formsSeenImpl()
123 {
124 MutexLocker lock(mFormsSeenMutex);
125 mAutofillManager->OnFormsSeenWrapper(mForms);
126 mParsingForms = false;
127 mFormsSeenCondition.signal();
128 }
129
formFieldFocused(WebCore::HTMLFormControlElement * formFieldElement)130 void WebAutofill::formFieldFocused(WebCore::HTMLFormControlElement* formFieldElement)
131 {
132 if (!enabled()) {
133 // In case that we've just been disabled and the last time we got autofill
134 // suggestions we told Java about them, clear that bit Java side now
135 // we're disabled.
136 mWebViewCore->setWebTextViewAutoFillable(FORM_NOT_AUTOFILLABLE, string16());
137 return;
138 }
139
140 ASSERT(formFieldElement);
141
142 Document* doc = formFieldElement->document();
143 Frame* frame = doc->frame();
144
145 // FIXME: Autofill only works in main frame for now. Should consider
146 // child frames.
147 if (frame != frame->page()->mainFrame())
148 return;
149
150 unsigned domVersion = doc->domTreeVersion();
151 ASSERT(domVersion > 0);
152
153 if (mLastSearchDomVersion != domVersion) {
154 // Need to extract forms as DOM version has changed since the last time
155 // we searched.
156 searchDocument(formFieldElement->document()->frame());
157 mLastSearchDomVersion = domVersion;
158 }
159
160 ASSERT(mFormManager);
161
162 // Get the FormField from the Node.
163 webkit_glue::FormField* formField = new webkit_glue::FormField;
164 FormManager::HTMLFormControlElementToFormField(formFieldElement, FormManager::EXTRACT_NONE, formField);
165 formField->label = FormManager::LabelForElement(*formFieldElement);
166
167 webkit_glue::FormData* form = new webkit_glue::FormData;
168 mFormManager->FindFormWithFormControlElement(formFieldElement, FormManager::REQUIRE_AUTOCOMPLETE, form);
169 mQueryMap[mQueryId] = new FormDataAndField(form, formField);
170
171 bool suggestions = mAutofillManager->OnQueryFormFieldAutoFillWrapper(*form, *formField);
172
173 mQueryId++;
174 if (!suggestions) {
175 ASSERT(mWebViewCore);
176 // Tell Java no autofill suggestions for this form.
177 mWebViewCore->setWebTextViewAutoFillable(FORM_NOT_AUTOFILLABLE, string16());
178 return;
179 }
180 }
181
querySuccessful(const string16 & value,const string16 & label,int uniqueId)182 void WebAutofill::querySuccessful(const string16& value, const string16& label, int uniqueId)
183 {
184 if (!enabled())
185 return;
186
187 // Store the unique ID for the query and inform java that autofill suggestions for this form are available.
188 // Pass java the queryId so that it can pass it back if the user decides to use autofill.
189 mUniqueIdMap[mQueryId] = uniqueId;
190
191 ASSERT(mWebViewCore);
192 mWebViewCore->setWebTextViewAutoFillable(mQueryId, mAutofillProfile->Label());
193 }
194
fillFormFields(int queryId)195 void WebAutofill::fillFormFields(int queryId)
196 {
197 if (!enabled())
198 return;
199
200 webkit_glue::FormData* form = mQueryMap[queryId]->form();
201 webkit_glue::FormField* field = mQueryMap[queryId]->field();
202 ASSERT(form);
203 ASSERT(field);
204
205 AutofillQueryToUniqueIdMap::iterator iter = mUniqueIdMap.find(queryId);
206 if (iter == mUniqueIdMap.end()) {
207 // The user has most likely tried to Autofill the form again without
208 // refocussing the form field. The UI should protect against this
209 // but stop here to be certain.
210 return;
211 }
212 mAutofillManager->OnFillAutoFillFormDataWrapper(queryId, *form, *field, iter->second);
213 mUniqueIdMap.erase(iter);
214 }
215
fillFormInPage(int queryId,const webkit_glue::FormData & form)216 void WebAutofill::fillFormInPage(int queryId, const webkit_glue::FormData& form)
217 {
218 if (!enabled())
219 return;
220
221 // FIXME: Pass a pointer to the Node that triggered the Autofill flow here instead of 0.
222 // The consquence of passing 0 is that we should always fail the test in FormManader::ForEachMathcingFormField():169
223 // that says "only overwrite an elements current value if the user triggered autofill through that element"
224 // for elements that have a value already. But by a quirk of Android text views we are OK. We should still
225 // fix this though.
226 mFormManager->FillForm(form, 0);
227 }
228
enabled() const229 bool WebAutofill::enabled() const
230 {
231 Page* page = mWebViewCore->mainFrame()->page();
232 return page ? page->settings()->autoFillEnabled() : false;
233 }
234
setProfile(const string16 & fullName,const string16 & emailAddress,const string16 & companyName,const string16 & addressLine1,const string16 & addressLine2,const string16 & city,const string16 & state,const string16 & zipCode,const string16 & country,const string16 & phoneNumber)235 void WebAutofill::setProfile(const string16& fullName, const string16& emailAddress, const string16& companyName,
236 const string16& addressLine1, const string16& addressLine2, const string16& city,
237 const string16& state, const string16& zipCode, const string16& country, const string16& phoneNumber)
238 {
239 if (!mAutofillProfile)
240 mAutofillProfile.set(new AutofillProfile());
241
242 // Update the profile.
243 // Constants for Autofill field types are found in external/chromium/chrome/browser/autofill/field_types.h.
244 mAutofillProfile->SetInfo(AutofillFieldType(NAME_FULL), fullName);
245 mAutofillProfile->SetInfo(AutofillFieldType(EMAIL_ADDRESS), emailAddress);
246 mAutofillProfile->SetInfo(AutofillFieldType(COMPANY_NAME), companyName);
247 mAutofillProfile->SetInfo(AutofillFieldType(ADDRESS_HOME_LINE1), addressLine1);
248 mAutofillProfile->SetInfo(AutofillFieldType(ADDRESS_HOME_LINE2), addressLine2);
249 mAutofillProfile->SetInfo(AutofillFieldType(ADDRESS_HOME_CITY), city);
250 mAutofillProfile->SetInfo(AutofillFieldType(ADDRESS_HOME_STATE), state);
251 mAutofillProfile->SetInfo(AutofillFieldType(ADDRESS_HOME_ZIP), zipCode);
252 mAutofillProfile->SetInfo(AutofillFieldType(ADDRESS_HOME_COUNTRY), country);
253 mAutofillProfile->SetInfo(AutofillFieldType(PHONE_HOME_WHOLE_NUMBER), phoneNumber);
254
255 std::vector<AutofillProfile> profiles;
256 profiles.push_back(*mAutofillProfile);
257 updateProfileLabel();
258 mTabContents->profile()->GetPersonalDataManager()->SetProfiles(&profiles);
259 }
260
updateProfileLabel()261 bool WebAutofill::updateProfileLabel()
262 {
263 std::vector<AutofillProfile*> profiles;
264 profiles.push_back(mAutofillProfile.get());
265 return AutofillProfile::AdjustInferredLabels(&profiles);
266 }
267
clearProfiles()268 void WebAutofill::clearProfiles()
269 {
270 if (!mAutofillProfile)
271 return;
272 // For now Chromium only ever knows about one profile, so we can just
273 // remove it. If we support multiple profiles in the future
274 // we need to remove them all here.
275 std::string profileGuid = mAutofillProfile->guid();
276 mTabContents->profile()->GetPersonalDataManager()->RemoveProfile(profileGuid);
277 setEmptyProfile();
278 }
279
setEmptyProfile()280 void WebAutofill::setEmptyProfile()
281 {
282 // Set an empty profile. This will ensure that when autofill is enabled,
283 // we will still search the document for autofillable forms and inform
284 // java of their presence so we can invite the user to set up
285 // their own profile.
286
287 // Chromium code will strip the values sent into the profile so we need them to be
288 // at least one non-whitespace character long. We need to set all fields of the
289 // profile to a non-empty string so that any field type can trigger the autofill
290 // suggestion. Autofill will not detect form fields if the profile value for that
291 // field is an empty string.
292 static const string16 empty = string16(ASCIIToUTF16("a"));
293 setProfile(empty, empty, empty, empty, empty, empty, empty, empty, empty, empty);
294 }
295
296 }
297
298 #endif
299