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 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 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 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 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 229 bool WebAutofill::enabled() const 230 { 231 Page* page = mWebViewCore->mainFrame()->page(); 232 return page ? page->settings()->autoFillEnabled() : false; 233 } 234 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 261 bool WebAutofill::updateProfileLabel() 262 { 263 std::vector<AutofillProfile*> profiles; 264 profiles.push_back(mAutofillProfile.get()); 265 return AutofillProfile::AdjustInferredLabels(&profiles); 266 } 267 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 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