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     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