• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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