Home | History | Annotate | Download | only in autofill
      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