Home | History | Annotate | Download | only in browser
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "components/autofill/content/browser/autocheckout_manager.h"
      6 
      7 #include "base/basictypes.h"
      8 #include "base/bind.h"
      9 #include "base/strings/utf_string_conversions.h"
     10 #include "components/autofill/content/browser/autocheckout_request_manager.h"
     11 #include "components/autofill/content/browser/autocheckout_statistic.h"
     12 #include "components/autofill/content/browser/autocheckout_steps.h"
     13 #include "components/autofill/core/browser/autofill_country.h"
     14 #include "components/autofill/core/browser/autofill_field.h"
     15 #include "components/autofill/core/browser/autofill_manager.h"
     16 #include "components/autofill/core/browser/autofill_metrics.h"
     17 #include "components/autofill/core/browser/autofill_profile.h"
     18 #include "components/autofill/core/browser/autofill_type.h"
     19 #include "components/autofill/core/browser/credit_card.h"
     20 #include "components/autofill/core/browser/form_structure.h"
     21 #include "components/autofill/core/common/autofill_messages.h"
     22 #include "components/autofill/core/common/form_data.h"
     23 #include "components/autofill/core/common/form_field_data.h"
     24 #include "components/autofill/core/common/web_element_descriptor.h"
     25 #include "content/public/browser/browser_context.h"
     26 #include "content/public/browser/browser_thread.h"
     27 #include "content/public/browser/render_view_host.h"
     28 #include "content/public/browser/web_contents.h"
     29 #include "net/cookies/cookie_options.h"
     30 #include "net/cookies/cookie_store.h"
     31 #include "net/url_request/url_request_context.h"
     32 #include "net/url_request/url_request_context_getter.h"
     33 #include "ui/gfx/rect.h"
     34 #include "url/gurl.h"
     35 
     36 using content::RenderViewHost;
     37 using content::WebContents;
     38 
     39 namespace autofill {
     40 
     41 namespace {
     42 
     43 const char kGoogleAccountsUrl[] = "https://accounts.google.com/";
     44 
     45 // Build FormFieldData based on the supplied |autocomplete_attribute|. Will
     46 // fill rest of properties with default values.
     47 FormFieldData BuildField(const std::string& autocomplete_attribute) {
     48   FormFieldData field;
     49   field.name = base::string16();
     50   field.value = base::string16();
     51   field.autocomplete_attribute = autocomplete_attribute;
     52   field.form_control_type = "text";
     53   return field;
     54 }
     55 
     56 // Build Autocheckout specific form data to be consumed by
     57 // AutofillDialogController to show the Autocheckout specific UI.
     58 FormData BuildAutocheckoutFormData() {
     59   FormData formdata;
     60   formdata.fields.push_back(BuildField("email"));
     61   formdata.fields.push_back(BuildField("cc-name"));
     62   formdata.fields.push_back(BuildField("cc-number"));
     63   formdata.fields.push_back(BuildField("cc-exp-month"));
     64   formdata.fields.push_back(BuildField("cc-exp-year"));
     65   formdata.fields.push_back(BuildField("cc-csc"));
     66   formdata.fields.push_back(BuildField("billing address-line1"));
     67   formdata.fields.push_back(BuildField("billing address-line2"));
     68   formdata.fields.push_back(BuildField("billing locality"));
     69   formdata.fields.push_back(BuildField("billing region"));
     70   formdata.fields.push_back(BuildField("billing country"));
     71   formdata.fields.push_back(BuildField("billing postal-code"));
     72   formdata.fields.push_back(BuildField("billing tel"));
     73   formdata.fields.push_back(BuildField("shipping name"));
     74   formdata.fields.push_back(BuildField("shipping address-line1"));
     75   formdata.fields.push_back(BuildField("shipping address-line2"));
     76   formdata.fields.push_back(BuildField("shipping locality"));
     77   formdata.fields.push_back(BuildField("shipping region"));
     78   formdata.fields.push_back(BuildField("shipping country"));
     79   formdata.fields.push_back(BuildField("shipping postal-code"));
     80   formdata.fields.push_back(BuildField("shipping tel"));
     81   return formdata;
     82 }
     83 
     84 AutofillMetrics::AutocheckoutBuyFlowMetric AutocheckoutStatusToUmaMetric(
     85     AutocheckoutStatus status) {
     86   switch (status) {
     87     case SUCCESS:
     88       return AutofillMetrics::AUTOCHECKOUT_BUY_FLOW_SUCCESS;
     89     case MISSING_FIELDMAPPING:
     90       return AutofillMetrics::AUTOCHECKOUT_BUY_FLOW_MISSING_FIELDMAPPING;
     91     case MISSING_CLICK_ELEMENT_BEFORE_FORM_FILLING:
     92       return AutofillMetrics::
     93           AUTOCHECKOUT_BUY_FLOW_MISSING_CLICK_ELEMENT_BEFORE_FORM_FILLING;
     94     case MISSING_CLICK_ELEMENT_AFTER_FORM_FILLING:
     95       return AutofillMetrics::
     96           AUTOCHECKOUT_BUY_FLOW_MISSING_CLICK_ELEMENT_AFTER_FORM_FILLING;
     97     case MISSING_ADVANCE:
     98       return AutofillMetrics::AUTOCHECKOUT_BUY_FLOW_MISSING_ADVANCE_ELEMENT;
     99     case CANNOT_PROCEED:
    100       return AutofillMetrics::AUTOCHECKOUT_BUY_FLOW_CANNOT_PROCEED;
    101     case AUTOCHECKOUT_STATUS_NUM_STATUS:
    102       NOTREACHED();
    103   }
    104 
    105   NOTREACHED();
    106   return AutofillMetrics::NUM_AUTOCHECKOUT_BUY_FLOW_METRICS;
    107 }
    108 
    109 // Callback for retrieving Google Account cookies. |callback| is passed the
    110 // retrieved cookies and posted back to the UI thread. |cookies| is any Google
    111 // Account cookies.
    112 void GetGoogleCookiesCallback(
    113     const base::Callback<void(const std::string&)>& callback,
    114     const std::string& cookies) {
    115   content::BrowserThread::PostTask(content::BrowserThread::UI,
    116                                    FROM_HERE,
    117                                    base::Bind(callback, cookies));
    118 }
    119 
    120 // Gets Google Account cookies. Must be called on the IO thread.
    121 // |request_context_getter| is a getter for the current request context.
    122 // |callback| is called when retrieving cookies is completed.
    123 void GetGoogleCookies(
    124     scoped_refptr<net::URLRequestContextGetter> request_context_getter,
    125     const base::Callback<void(const std::string&)>& callback) {
    126   net::URLRequestContext* url_request_context =
    127       request_context_getter->GetURLRequestContext();
    128   if (!url_request_context)
    129     return;
    130 
    131   net::CookieStore* cookie_store = url_request_context->cookie_store();
    132 
    133   base::Callback<void(const std::string&)> cookie_callback = base::Bind(
    134       &GetGoogleCookiesCallback,
    135       callback);
    136 
    137   net::CookieOptions cookie_options;
    138   cookie_options.set_include_httponly();
    139   cookie_store->GetCookiesWithOptionsAsync(GURL(kGoogleAccountsUrl),
    140                                            cookie_options,
    141                                            cookie_callback);
    142 }
    143 
    144 bool IsBillingGroup(FieldTypeGroup group) {
    145   return group == ADDRESS_BILLING ||
    146          group == PHONE_BILLING ||
    147          group == NAME_BILLING;
    148 }
    149 
    150 const char kTransactionIdNotSet[] = "transaction id not set";
    151 
    152 }  // namespace
    153 
    154 AutocheckoutManager::AutocheckoutManager(AutofillManager* autofill_manager)
    155     : autofill_manager_(autofill_manager),
    156       metric_logger_(new AutofillMetrics),
    157       should_show_bubble_(true),
    158       is_autocheckout_bubble_showing_(false),
    159       in_autocheckout_flow_(false),
    160       should_preserve_dialog_(false),
    161       google_transaction_id_(kTransactionIdNotSet),
    162       weak_ptr_factory_(this) {}
    163 
    164 AutocheckoutManager::~AutocheckoutManager() {
    165 }
    166 
    167 void AutocheckoutManager::FillForms() {
    168   // |page_meta_data_| should have been set by OnLoadedPageMetaData.
    169   DCHECK(page_meta_data_);
    170 
    171   // Fill the forms on the page with data given by user.
    172   std::vector<FormData> filled_forms;
    173   const std::vector<FormStructure*>& form_structures =
    174     autofill_manager_->GetFormStructures();
    175   for (std::vector<FormStructure*>::const_iterator iter =
    176            form_structures.begin(); iter != form_structures.end(); ++iter) {
    177     FormStructure* form_structure = *iter;
    178     form_structure->set_filled_by_autocheckout(true);
    179     FormData form = form_structure->ToFormData();
    180     DCHECK_EQ(form_structure->field_count(), form.fields.size());
    181 
    182     for (size_t i = 0; i < form_structure->field_count(); ++i) {
    183       const AutofillField* field = form_structure->field(i);
    184       SetValue(*field, &form.fields[i]);
    185     }
    186 
    187     filled_forms.push_back(form);
    188   }
    189 
    190   // Send filled forms along with proceed descriptor to renderer.
    191   RenderViewHost* host =
    192       autofill_manager_->GetWebContents()->GetRenderViewHost();
    193   if (!host)
    194     return;
    195 
    196   host->Send(new AutofillMsg_FillFormsAndClick(
    197       host->GetRoutingID(),
    198       filled_forms,
    199       page_meta_data_->click_elements_before_form_fill,
    200       page_meta_data_->click_elements_after_form_fill,
    201       page_meta_data_->proceed_element_descriptor));
    202   // Record time taken for navigating current page.
    203   RecordTimeTaken(page_meta_data_->current_page_number);
    204 }
    205 
    206 void AutocheckoutManager::OnAutocheckoutPageCompleted(
    207     AutocheckoutStatus status) {
    208   if (!in_autocheckout_flow_)
    209     return;
    210 
    211   DVLOG(2) << "OnAutocheckoutPageCompleted, page_no: "
    212            << page_meta_data_->current_page_number
    213            << " status: "
    214            << status;
    215 
    216   DCHECK_NE(MISSING_FIELDMAPPING, status);
    217 
    218   SetStepProgressForPage(
    219       page_meta_data_->current_page_number,
    220       (status == SUCCESS) ? AUTOCHECKOUT_STEP_COMPLETED :
    221           AUTOCHECKOUT_STEP_FAILED);
    222 
    223   if (page_meta_data_->IsEndOfAutofillableFlow() || status != SUCCESS)
    224     EndAutocheckout(status);
    225 }
    226 
    227 void AutocheckoutManager::OnLoadedPageMetaData(
    228     scoped_ptr<AutocheckoutPageMetaData> page_meta_data) {
    229   scoped_ptr<AutocheckoutPageMetaData> old_meta_data = page_meta_data_.Pass();
    230   page_meta_data_ = page_meta_data.Pass();
    231 
    232   // If there is no click element in the last page, then it's the real last page
    233   // of the flow, and the dialog will be closed when the page navigates.
    234   // Otherwise, the dialog should be preserved for the page loaded by the click
    235   // element on the last page of the flow.
    236   // Note, |should_preserve_dialog_| has to be computed at this point because
    237   // |in_autocheckout_flow_| may change after |OnLoadedPageMetaData| is called.
    238   should_preserve_dialog_ = in_autocheckout_flow_ ||
    239       (old_meta_data.get() &&
    240        old_meta_data->IsEndOfAutofillableFlow() &&
    241        old_meta_data->proceed_element_descriptor.retrieval_method !=
    242            WebElementDescriptor::NONE);
    243 
    244   // Don't log that the bubble could be displayed if the user entered an
    245   // Autocheckout flow and sees the first page of the flow again due to an
    246   // error.
    247   if (IsStartOfAutofillableFlow() && !in_autocheckout_flow_) {
    248     metric_logger_->LogAutocheckoutBubbleMetric(
    249         AutofillMetrics::BUBBLE_COULD_BE_DISPLAYED);
    250   }
    251 
    252   // On the first page of an Autocheckout flow, when this function is called the
    253   // user won't have opted into the flow yet.
    254   if (!in_autocheckout_flow_)
    255     return;
    256 
    257   AutocheckoutStatus status = SUCCESS;
    258 
    259   // Missing Autofill server results.
    260   if (!page_meta_data_.get()) {
    261     status = MISSING_FIELDMAPPING;
    262   } else if (IsStartOfAutofillableFlow()) {
    263     // Not possible unless Autocheckout failed to proceed.
    264     status = CANNOT_PROCEED;
    265   } else if (!page_meta_data_->IsInAutofillableFlow()) {
    266     // Missing Autocheckout meta data in the Autofill server results.
    267     status = MISSING_FIELDMAPPING;
    268   } else if (page_meta_data_->current_page_number <=
    269                  old_meta_data->current_page_number) {
    270     // Not possible unless Autocheckout failed to proceed.
    271     status = CANNOT_PROCEED;
    272   }
    273 
    274   // Encountered an error during the Autocheckout flow, probably to
    275   // do with a problem on the previous page.
    276   if (status != SUCCESS) {
    277     SetStepProgressForPage(old_meta_data->current_page_number,
    278                            AUTOCHECKOUT_STEP_FAILED);
    279     EndAutocheckout(status);
    280     return;
    281   }
    282 
    283   SetStepProgressForPage(page_meta_data_->current_page_number,
    284                          AUTOCHECKOUT_STEP_STARTED);
    285 
    286   FillForms();
    287 }
    288 
    289 void AutocheckoutManager::OnFormsSeen() {
    290   should_show_bubble_ = true;
    291 }
    292 
    293 bool AutocheckoutManager::ShouldIgnoreAjax() {
    294   return in_autocheckout_flow_ && page_meta_data_->ignore_ajax;
    295 }
    296 
    297 void AutocheckoutManager::MaybeShowAutocheckoutBubble(
    298     const GURL& frame_url,
    299     const gfx::RectF& bounding_box) {
    300   if (!should_show_bubble_ ||
    301       is_autocheckout_bubble_showing_ ||
    302       !IsStartOfAutofillableFlow())
    303     return;
    304 
    305   base::Callback<void(const std::string&)> callback = base::Bind(
    306       &AutocheckoutManager::ShowAutocheckoutBubble,
    307       weak_ptr_factory_.GetWeakPtr(),
    308       frame_url,
    309       bounding_box);
    310 
    311   content::WebContents* web_contents = autofill_manager_->GetWebContents();
    312   if (!web_contents)
    313     return;
    314 
    315   content::BrowserContext* browser_context = web_contents->GetBrowserContext();
    316   if(!browser_context)
    317     return;
    318 
    319   scoped_refptr<net::URLRequestContextGetter> request_context =
    320       scoped_refptr<net::URLRequestContextGetter>(
    321           browser_context->GetRequestContext());
    322 
    323   if (!request_context.get())
    324     return;
    325 
    326   base::Closure task = base::Bind(&GetGoogleCookies, request_context, callback);
    327 
    328   content::BrowserThread::PostTask(content::BrowserThread::IO,
    329                                    FROM_HERE,
    330                                    task);
    331 }
    332 
    333 void AutocheckoutManager::ReturnAutocheckoutData(
    334     const FormStructure* result,
    335     const std::string& google_transaction_id) {
    336   if (!result) {
    337     // When user cancels the dialog, |result| is NULL.
    338     // TODO(): add AutocheckoutStatus.USER_CANCELLED, and call
    339     //         EndAutocheckout(USER_CANCELLED) instead.
    340     in_autocheckout_flow_ = false;
    341     return;
    342   }
    343 
    344   latency_statistics_.clear();
    345   last_step_completion_timestamp_ = base::TimeTicks().Now();
    346   google_transaction_id_ = google_transaction_id;
    347   in_autocheckout_flow_ = true;
    348   should_preserve_dialog_ = true;
    349   metric_logger_->LogAutocheckoutBuyFlowMetric(
    350       AutofillMetrics::AUTOCHECKOUT_BUY_FLOW_STARTED);
    351 
    352   profile_.reset(new AutofillProfile());
    353   credit_card_.reset(new CreditCard());
    354   billing_address_.reset(new AutofillProfile());
    355 
    356   for (size_t i = 0; i < result->field_count(); ++i) {
    357     const AutofillType& type = result->field(i)->Type();
    358     const base::string16& value = result->field(i)->value;
    359     ServerFieldType server_type = type.GetStorableType();
    360     if (server_type == CREDIT_CARD_VERIFICATION_CODE) {
    361       cvv_ = result->field(i)->value;
    362       continue;
    363     }
    364     FieldTypeGroup group = type.group();
    365     if (group == CREDIT_CARD) {
    366       credit_card_->SetRawInfo(server_type, value);
    367       // TODO(dgwallinga): Find a way of cleanly deprecating CREDIT_CARD_NAME.
    368       // code.google.com/p/chromium/issues/detail?id=263498
    369       if (server_type == CREDIT_CARD_NAME)
    370         billing_address_->SetRawInfo(NAME_BILLING_FULL, value);
    371     } else if (server_type == ADDRESS_HOME_COUNTRY) {
    372       if (IsBillingGroup(group))
    373         billing_address_->SetInfo(type, value, autofill_manager_->app_locale());
    374       else
    375         profile_->SetInfo(type, value, autofill_manager_->app_locale());
    376     } else if (IsBillingGroup(group)) {
    377       billing_address_->SetRawInfo(server_type, value);
    378     } else {
    379       profile_->SetRawInfo(server_type, value);
    380     }
    381   }
    382 
    383   // Page types only available in first-page meta data, so save
    384   // them for use later as we navigate.
    385   page_types_ = page_meta_data_->page_types;
    386   SetStepProgressForPage(page_meta_data_->current_page_number,
    387                          AUTOCHECKOUT_STEP_STARTED);
    388 
    389   FillForms();
    390 }
    391 
    392 void AutocheckoutManager::set_metric_logger(
    393     scoped_ptr<AutofillMetrics> metric_logger) {
    394   metric_logger_ = metric_logger.Pass();
    395 }
    396 
    397 void AutocheckoutManager::MaybeShowAutocheckoutDialog(
    398     const GURL& frame_url,
    399     AutocheckoutBubbleState state) {
    400   is_autocheckout_bubble_showing_ = false;
    401 
    402   // User has taken action on the bubble, don't offer bubble again.
    403   if (state != AUTOCHECKOUT_BUBBLE_IGNORED)
    404     should_show_bubble_ = false;
    405 
    406   if (state != AUTOCHECKOUT_BUBBLE_ACCEPTED)
    407     return;
    408 
    409   base::Callback<void(const FormStructure*, const std::string&)> callback =
    410       base::Bind(&AutocheckoutManager::ReturnAutocheckoutData,
    411                  weak_ptr_factory_.GetWeakPtr());
    412   autofill_manager_->ShowRequestAutocompleteDialog(BuildAutocheckoutFormData(),
    413                                                    frame_url,
    414                                                    DIALOG_TYPE_AUTOCHECKOUT,
    415                                                    callback);
    416 
    417   for (std::map<int, std::vector<AutocheckoutStepType> >::const_iterator
    418           it = page_meta_data_->page_types.begin();
    419       it != page_meta_data_->page_types.end(); ++it) {
    420     for (size_t i = 0; i < it->second.size(); ++i) {
    421       autofill_manager_->delegate()->AddAutocheckoutStep(it->second[i]);
    422     }
    423   }
    424 }
    425 
    426 void AutocheckoutManager::ShowAutocheckoutBubble(
    427     const GURL& frame_url,
    428     const gfx::RectF& bounding_box,
    429     const std::string& cookies) {
    430   DCHECK(thread_checker_.CalledOnValidThread());
    431 
    432   base::Callback<void(AutocheckoutBubbleState)> callback = base::Bind(
    433       &AutocheckoutManager::MaybeShowAutocheckoutDialog,
    434       weak_ptr_factory_.GetWeakPtr(),
    435       frame_url);
    436   is_autocheckout_bubble_showing_ =
    437       autofill_manager_->delegate()->ShowAutocheckoutBubble(
    438           bounding_box,
    439           cookies.find("LSID") != std::string::npos,
    440           callback);
    441 }
    442 
    443 bool AutocheckoutManager::IsStartOfAutofillableFlow() const {
    444   return page_meta_data_ && page_meta_data_->IsStartOfAutofillableFlow();
    445 }
    446 
    447 bool AutocheckoutManager::IsInAutofillableFlow() const {
    448   return page_meta_data_ && page_meta_data_->IsInAutofillableFlow();
    449 }
    450 
    451 void AutocheckoutManager::SetValue(const AutofillField& field,
    452                                    FormFieldData* field_to_fill) {
    453   // No-op if Autofill server doesn't know about the field.
    454   if (field.server_type() == NO_SERVER_DATA)
    455     return;
    456 
    457   const AutofillType& type = field.Type();
    458 
    459   ServerFieldType server_type = type.GetStorableType();
    460   if (server_type == FIELD_WITH_DEFAULT_VALUE) {
    461     // For a form with radio buttons, like:
    462     // <form>
    463     //   <input type="radio" name="sex" value="male">Male<br>
    464     //   <input type="radio" name="sex" value="female">Female
    465     // </form>
    466     // If the default value specified at the server is "female", then
    467     // Autofill server responds back with following field mappings
    468     //   (fieldtype: FIELD_WITH_DEFAULT_VALUE, value: "female")
    469     //   (fieldtype: FIELD_WITH_DEFAULT_VALUE, value: "female")
    470     // Note that, the field mapping is repeated twice to respond to both the
    471     // input elements with the same name/signature in the form.
    472     //
    473     // FIELD_WITH_DEFAULT_VALUE can also be used for selects, the correspondent
    474     // example of the radio buttons example above is:
    475     // <SELECT name="sex">
    476     //   <OPTION value="female">Female</OPTION>
    477     //   <OPTION value="male">Male</OPTION>
    478     // </SELECT>
    479     base::string16 default_value = UTF8ToUTF16(field.default_value());
    480     if (field.is_checkable) {
    481       // Mark the field checked if server says the default value of the field
    482       // to be this field's value.
    483       field_to_fill->is_checked = (field.value == default_value);
    484     } else if (field.form_control_type == "select-one") {
    485       field_to_fill->value = default_value;
    486     } else {
    487       // FIELD_WITH_DEFAULT_VALUE should not be used for other type of fields.
    488       NOTREACHED();
    489     }
    490     return;
    491   }
    492 
    493   // Handle verification code directly.
    494   if (server_type == CREDIT_CARD_VERIFICATION_CODE) {
    495     field_to_fill->value = cvv_;
    496     return;
    497   }
    498 
    499   if (type.group() == CREDIT_CARD) {
    500     credit_card_->FillFormField(
    501         field, 0, autofill_manager_->app_locale(), field_to_fill);
    502   } else if (IsBillingGroup(type.group())) {
    503     billing_address_->FillFormField(
    504         field, 0, autofill_manager_->app_locale(), field_to_fill);
    505   } else {
    506     profile_->FillFormField(
    507         field, 0, autofill_manager_->app_locale(), field_to_fill);
    508   }
    509 }
    510 
    511 void AutocheckoutManager::SendAutocheckoutStatus(AutocheckoutStatus status) {
    512   // To ensure stale data isn't being sent.
    513   DCHECK_NE(kTransactionIdNotSet, google_transaction_id_);
    514 
    515   AutocheckoutRequestManager::CreateForBrowserContext(
    516       autofill_manager_->GetWebContents()->GetBrowserContext());
    517   AutocheckoutRequestManager* autocheckout_request_manager =
    518       AutocheckoutRequestManager::FromBrowserContext(
    519           autofill_manager_->GetWebContents()->GetBrowserContext());
    520   // It is assumed that the domain Autocheckout starts on does not change
    521   // during the flow.  If this proves to be incorrect, the |source_url| from
    522   // AutofillDialogControllerImpl will need to be provided in its callback in
    523   // addition to the Google transaction id.
    524   autocheckout_request_manager->SendAutocheckoutStatus(
    525       status,
    526       autofill_manager_->GetWebContents()->GetURL(),
    527       latency_statistics_,
    528       google_transaction_id_);
    529 
    530   // Log the result of this Autocheckout flow to UMA.
    531   metric_logger_->LogAutocheckoutBuyFlowMetric(
    532       AutocheckoutStatusToUmaMetric(status));
    533 
    534   google_transaction_id_ = kTransactionIdNotSet;
    535 }
    536 
    537 void AutocheckoutManager::SetStepProgressForPage(
    538     int page_number,
    539     AutocheckoutStepStatus status) {
    540   if (page_types_.count(page_number) == 1) {
    541     for (size_t i = 0; i < page_types_[page_number].size(); ++i) {
    542       autofill_manager_->delegate()->UpdateAutocheckoutStep(
    543           page_types_[page_number][i], status);
    544     }
    545   }
    546 }
    547 
    548 void AutocheckoutManager::RecordTimeTaken(int page_number) {
    549   AutocheckoutStatistic statistic;
    550   statistic.page_number = page_number;
    551   if (page_types_.count(page_number) == 1) {
    552     for (size_t i = 0; i < page_types_[page_number].size(); ++i) {
    553       statistic.steps.push_back(page_types_[page_number][i]);
    554     }
    555   }
    556 
    557   statistic.time_taken =
    558       base::TimeTicks().Now() - last_step_completion_timestamp_;
    559   latency_statistics_.push_back(statistic);
    560 
    561   // Reset timestamp.
    562   last_step_completion_timestamp_ = base::TimeTicks().Now();
    563 }
    564 
    565 void AutocheckoutManager::EndAutocheckout(AutocheckoutStatus status) {
    566   DCHECK(in_autocheckout_flow_);
    567 
    568   DVLOG(2) << "EndAutocheckout at step: "
    569            << page_meta_data_->current_page_number
    570            << " with status: "
    571            << status;
    572 
    573   SendAutocheckoutStatus(status);
    574   if (status == SUCCESS)
    575     autofill_manager_->delegate()->OnAutocheckoutSuccess();
    576   else
    577     autofill_manager_->delegate()->OnAutocheckoutError();
    578   in_autocheckout_flow_ = false;
    579 }
    580 
    581 }  // namespace autofill
    582