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 { 51 WebAutofill::WebAutofill() 52 : mQueryId(1) 53 , mWebViewCore(0) 54 , mLastSearchDomVersion(0) 55 , mParsingForms(false) 56 { 57 mTabContents = new TabContents(); 58 setEmptyProfile(); 59 } 60 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 75 WebAutofill::~WebAutofill() 76 { 77 cleanUpQueryMap(); 78 mUniqueIdMap.clear(); 79 } 80 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 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. 122 void WebAutofill::formsSeenImpl() 123 { 124 MutexLocker lock(mFormsSeenMutex); 125 mAutofillManager->OnFormsSeenWrapper(mForms); 126 mParsingForms = false; 127 mFormsSeenCondition.signal(); 128 } 129 130 void WebAutofill::formFieldFocused(WebCore::HTMLFormControlElement* formFieldElement) 131 { 132 ASSERT(formFieldElement); 133 134 Document* doc = formFieldElement->document(); 135 Frame* frame = doc->frame(); 136 137 // FIXME: Autofill only works in main frame for now. Should consider 138 // child frames. 139 if (frame != frame->page()->mainFrame()) 140 return; 141 142 unsigned domVersion = doc->domTreeVersion(); 143 ASSERT(domVersion > 0); 144 145 if (mLastSearchDomVersion != domVersion) { 146 // Need to extract forms as DOM version has changed since the last time 147 // we searched. 148 searchDocument(formFieldElement->document()->frame()); 149 mLastSearchDomVersion = domVersion; 150 } 151 152 if (!enabled()) { 153 // In case that we've just been disabled and the last time we got autofill 154 // suggestions and told Java about them, clear that bit Java side now 155 // we're disabled. 156 mWebViewCore->setWebTextViewAutoFillable(FORM_NOT_AUTOFILLABLE, string16()); 157 return; 158 } 159 160 // Get the FormField from the Node. 161 webkit_glue::FormField* formField = new webkit_glue::FormField; 162 FormManager::HTMLFormControlElementToFormField(formFieldElement, FormManager::EXTRACT_NONE, formField); 163 formField->label = FormManager::LabelForElement(*formFieldElement); 164 165 webkit_glue::FormData* form = new webkit_glue::FormData; 166 mFormManager->FindFormWithFormControlElement(formFieldElement, FormManager::REQUIRE_AUTOCOMPLETE, form); 167 mQueryMap[mQueryId] = new FormDataAndField(form, formField); 168 169 bool suggestions = mAutofillManager->OnQueryFormFieldAutoFillWrapper(*form, *formField); 170 171 mQueryId++; 172 if (!suggestions) { 173 ASSERT(mWebViewCore); 174 // Tell Java no autofill suggestions for this form. 175 mWebViewCore->setWebTextViewAutoFillable(FORM_NOT_AUTOFILLABLE, string16()); 176 return; 177 } 178 } 179 180 void WebAutofill::querySuccessful(const string16& value, const string16& label, int uniqueId) 181 { 182 if (!enabled()) 183 return; 184 185 // Store the unique ID for the query and inform java that autofill suggestions for this form are available. 186 // Pass java the queryId so that it can pass it back if the user decides to use autofill. 187 mUniqueIdMap[mQueryId] = uniqueId; 188 189 ASSERT(mWebViewCore); 190 mWebViewCore->setWebTextViewAutoFillable(mQueryId, mAutofillProfile->Label()); 191 } 192 193 void WebAutofill::fillFormFields(int queryId) 194 { 195 if (!enabled()) 196 return; 197 198 webkit_glue::FormData* form = mQueryMap[queryId]->form(); 199 webkit_glue::FormField* field = mQueryMap[queryId]->field(); 200 ASSERT(form); 201 ASSERT(field); 202 203 AutofillQueryToUniqueIdMap::iterator iter = mUniqueIdMap.find(queryId); 204 if (iter == mUniqueIdMap.end()) { 205 // The user has most likely tried to Autofill the form again without 206 // refocussing the form field. The UI should protect against this 207 // but stop here to be certain. 208 return; 209 } 210 mAutofillManager->OnFillAutoFillFormDataWrapper(queryId, *form, *field, iter->second); 211 mUniqueIdMap.erase(iter); 212 } 213 214 void WebAutofill::fillFormInPage(int queryId, const webkit_glue::FormData& form) 215 { 216 if (!enabled()) 217 return; 218 219 // FIXME: Pass a pointer to the Node that triggered the Autofill flow here instead of 0. 220 // The consquence of passing 0 is that we should always fail the test in FormManader::ForEachMathcingFormField():169 221 // that says "only overwrite an elements current value if the user triggered autofill through that element" 222 // for elements that have a value already. But by a quirk of Android text views we are OK. We should still 223 // fix this though. 224 mFormManager->FillForm(form, 0); 225 } 226 227 bool WebAutofill::enabled() const 228 { 229 Page* page = mWebViewCore->mainFrame()->page(); 230 return page ? page->settings()->autoFillEnabled() : false; 231 } 232 233 void WebAutofill::setProfile(const string16& fullName, const string16& emailAddress, const string16& companyName, 234 const string16& addressLine1, const string16& addressLine2, const string16& city, 235 const string16& state, const string16& zipCode, const string16& country, const string16& phoneNumber) 236 { 237 if (!mAutofillProfile) 238 mAutofillProfile.set(new AutofillProfile()); 239 240 // Update the profile. 241 // Constants for Autofill field types are found in external/chromium/chrome/browser/autofill/field_types.h. 242 mAutofillProfile->SetInfo(AutofillFieldType(NAME_FULL), fullName); 243 mAutofillProfile->SetInfo(AutofillFieldType(EMAIL_ADDRESS), emailAddress); 244 mAutofillProfile->SetInfo(AutofillFieldType(COMPANY_NAME), companyName); 245 mAutofillProfile->SetInfo(AutofillFieldType(ADDRESS_HOME_LINE1), addressLine1); 246 mAutofillProfile->SetInfo(AutofillFieldType(ADDRESS_HOME_LINE2), addressLine2); 247 mAutofillProfile->SetInfo(AutofillFieldType(ADDRESS_HOME_CITY), city); 248 mAutofillProfile->SetInfo(AutofillFieldType(ADDRESS_HOME_STATE), state); 249 mAutofillProfile->SetInfo(AutofillFieldType(ADDRESS_HOME_ZIP), zipCode); 250 mAutofillProfile->SetInfo(AutofillFieldType(ADDRESS_HOME_COUNTRY), country); 251 mAutofillProfile->SetInfo(AutofillFieldType(PHONE_HOME_WHOLE_NUMBER), phoneNumber); 252 253 std::vector<AutofillProfile> profiles; 254 profiles.push_back(*mAutofillProfile); 255 updateProfileLabel(); 256 mTabContents->profile()->GetPersonalDataManager()->SetProfiles(&profiles); 257 } 258 259 bool WebAutofill::updateProfileLabel() 260 { 261 std::vector<AutofillProfile*> profiles; 262 profiles.push_back(mAutofillProfile.get()); 263 return AutofillProfile::AdjustInferredLabels(&profiles); 264 } 265 266 void WebAutofill::clearProfiles() 267 { 268 if (!mAutofillProfile) 269 return; 270 // For now Chromium only ever knows about one profile, so we can just 271 // remove it. If we support multiple profiles in the future 272 // we need to remove them all here. 273 std::string profileGuid = mAutofillProfile->guid(); 274 mTabContents->profile()->GetPersonalDataManager()->RemoveProfile(profileGuid); 275 setEmptyProfile(); 276 } 277 278 void WebAutofill::setEmptyProfile() 279 { 280 // Set an empty profile. This will ensure that when autofill is enabled, 281 // we will still search the document for autofillable forms and inform 282 // java of their presence so we can invite the user to set up 283 // their own profile. 284 285 // Chromium code will strip the values sent into the profile so we need them to be 286 // at least one non-whitespace character long. We need to set all fields of the 287 // profile to a non-empty string so that any field type can trigger the autofill 288 // suggestion. Autofill will not detect form fields if the profile value for that 289 // field is an empty string. 290 static const string16 empty = string16(ASCIIToUTF16("a")); 291 setProfile(empty, empty, empty, empty, empty, empty, empty, empty, empty, empty); 292 } 293 294 } 295 296 #endif 297