Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 1 | // Copyright 2013 The Chromium Authors. All rights reserved. |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 5 | #include "components/autofill/content/browser/autocheckout_manager.h" |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 6 | |
| 7 | #include "base/basictypes.h" |
| 8 | #include "base/bind.h" |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 9 | #include "base/strings/utf_string_conversions.h" |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 10 | #include "components/autofill/content/browser/autocheckout_request_manager.h" |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 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" |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 18 | #include "components/autofill/core/browser/autofill_type.h" |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 19 | #include "components/autofill/core/browser/credit_card.h" |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 20 | #include "components/autofill/core/browser/form_structure.h" |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 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" |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 25 | #include "content/public/browser/browser_context.h" |
| 26 | #include "content/public/browser/browser_thread.h" |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 27 | #include "content/public/browser/render_view_host.h" |
| 28 | #include "content/public/browser/web_contents.h" |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 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" |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 33 | #include "ui/gfx/rect.h" |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 34 | #include "url/gurl.h" |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 35 | |
| 36 | using content::RenderViewHost; |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 37 | using content::WebContents; |
| 38 | |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 39 | namespace autofill { |
| 40 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 41 | namespace { |
| 42 | |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 43 | const char kGoogleAccountsUrl[] = "https://accounts.google.com/"; |
| 44 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 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; |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 49 | field.name = base::string16(); |
| 50 | field.value = base::string16(); |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 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")); |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 66 | formdata.fields.push_back(BuildField("billing address-line1")); |
| 67 | formdata.fields.push_back(BuildField("billing address-line2")); |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 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")); |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 74 | formdata.fields.push_back(BuildField("shipping address-line1")); |
| 75 | formdata.fields.push_back(BuildField("shipping address-line2")); |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 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 | |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 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; |
Torne (Richard Coles) | 5e3f23d | 2013-06-11 16:24:11 +0100 | [diff] [blame] | 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; |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 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; |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 101 | case AUTOCHECKOUT_STATUS_NUM_STATUS: |
| 102 | NOTREACHED(); |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 103 | } |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 104 | |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 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 | |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 144 | bool IsBillingGroup(FieldTypeGroup group) { |
Ben Murdoch | 2385ea3 | 2013-08-06 11:01:04 +0100 | [diff] [blame] | 145 | return group == ADDRESS_BILLING || |
| 146 | group == PHONE_BILLING || |
| 147 | group == NAME_BILLING; |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 148 | } |
| 149 | |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 150 | const char kTransactionIdNotSet[] = "transaction id not set"; |
| 151 | |
| 152 | } // namespace |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 153 | |
| 154 | AutocheckoutManager::AutocheckoutManager(AutofillManager* autofill_manager) |
| 155 | : autofill_manager_(autofill_manager), |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 156 | metric_logger_(new AutofillMetrics), |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 157 | should_show_bubble_(true), |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 158 | is_autocheckout_bubble_showing_(false), |
| 159 | in_autocheckout_flow_(false), |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 160 | should_preserve_dialog_(false), |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 161 | google_transaction_id_(kTransactionIdNotSet), |
| 162 | weak_ptr_factory_(this) {} |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 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) { |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 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()); |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 181 | |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 182 | for (size_t i = 0; i < form_structure->field_count(); ++i) { |
| 183 | const AutofillField* field = form_structure->field(i); |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 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, |
Torne (Richard Coles) | 5e3f23d | 2013-06-11 16:24:11 +0100 | [diff] [blame] | 199 | page_meta_data_->click_elements_before_form_fill, |
| 200 | page_meta_data_->click_elements_after_form_fill, |
Torne (Richard Coles) | b2df76e | 2013-05-13 16:52:09 +0100 | [diff] [blame] | 201 | page_meta_data_->proceed_element_descriptor)); |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 202 | // Record time taken for navigating current page. |
| 203 | RecordTimeTaken(page_meta_data_->current_page_number); |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 204 | } |
| 205 | |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 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 | |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 216 | DCHECK_NE(MISSING_FIELDMAPPING, status); |
| 217 | |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 218 | SetStepProgressForPage( |
| 219 | page_meta_data_->current_page_number, |
| 220 | (status == SUCCESS) ? AUTOCHECKOUT_STEP_COMPLETED : |
| 221 | AUTOCHECKOUT_STEP_FAILED); |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 222 | |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 223 | if (page_meta_data_->IsEndOfAutofillableFlow() || status != SUCCESS) |
| 224 | EndAutocheckout(status); |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 225 | } |
| 226 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 227 | void AutocheckoutManager::OnLoadedPageMetaData( |
| 228 | scoped_ptr<AutocheckoutPageMetaData> page_meta_data) { |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 229 | scoped_ptr<AutocheckoutPageMetaData> old_meta_data = page_meta_data_.Pass(); |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 230 | page_meta_data_ = page_meta_data.Pass(); |
| 231 | |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 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 | |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 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 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 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 | |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 257 | AutocheckoutStatus status = SUCCESS; |
| 258 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 259 | // Missing Autofill server results. |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 260 | if (!page_meta_data_.get()) { |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 261 | status = MISSING_FIELDMAPPING; |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 262 | } else if (IsStartOfAutofillableFlow()) { |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 263 | // Not possible unless Autocheckout failed to proceed. |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 264 | status = CANNOT_PROCEED; |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 265 | } else if (!page_meta_data_->IsInAutofillableFlow()) { |
| 266 | // Missing Autocheckout meta data in the Autofill server results. |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 267 | status = MISSING_FIELDMAPPING; |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 268 | } else if (page_meta_data_->current_page_number <= |
| 269 | old_meta_data->current_page_number) { |
| 270 | // Not possible unless Autocheckout failed to proceed. |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 271 | status = CANNOT_PROCEED; |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 272 | } |
| 273 | |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 274 | // Encountered an error during the Autocheckout flow, probably to |
| 275 | // do with a problem on the previous page. |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 276 | if (status != SUCCESS) { |
| 277 | SetStepProgressForPage(old_meta_data->current_page_number, |
| 278 | AUTOCHECKOUT_STEP_FAILED); |
| 279 | EndAutocheckout(status); |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 280 | return; |
| 281 | } |
| 282 | |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 283 | SetStepProgressForPage(page_meta_data_->current_page_number, |
| 284 | AUTOCHECKOUT_STEP_STARTED); |
| 285 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 286 | FillForms(); |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 287 | } |
| 288 | |
| 289 | void AutocheckoutManager::OnFormsSeen() { |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 290 | should_show_bubble_ = true; |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 291 | } |
| 292 | |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 293 | bool AutocheckoutManager::ShouldIgnoreAjax() { |
| 294 | return in_autocheckout_flow_ && page_meta_data_->ignore_ajax; |
| 295 | } |
| 296 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 297 | void AutocheckoutManager::MaybeShowAutocheckoutBubble( |
| 298 | const GURL& frame_url, |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 299 | const gfx::RectF& bounding_box) { |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 300 | if (!should_show_bubble_ || |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 301 | is_autocheckout_bubble_showing_ || |
| 302 | !IsStartOfAutofillableFlow()) |
| 303 | return; |
| 304 | |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 305 | base::Callback<void(const std::string&)> callback = base::Bind( |
| 306 | &AutocheckoutManager::ShowAutocheckoutBubble, |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 307 | weak_ptr_factory_.GetWeakPtr(), |
| 308 | frame_url, |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 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 | |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 323 | if (!request_context.get()) |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 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 | |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 333 | void AutocheckoutManager::ReturnAutocheckoutData( |
| 334 | const FormStructure* result, |
| 335 | const std::string& google_transaction_id) { |
| 336 | if (!result) { |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 337 | // When user cancels the dialog, |result| is NULL. |
| 338 | // TODO(): add AutocheckoutStatus.USER_CANCELLED, and call |
| 339 | // EndAutocheckout(USER_CANCELLED) instead. |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 340 | in_autocheckout_flow_ = false; |
| 341 | return; |
| 342 | } |
| 343 | |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 344 | latency_statistics_.clear(); |
| 345 | last_step_completion_timestamp_ = base::TimeTicks().Now(); |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 346 | google_transaction_id_ = google_transaction_id; |
| 347 | in_autocheckout_flow_ = true; |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 348 | should_preserve_dialog_ = true; |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 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) { |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 357 | const AutofillType& type = result->field(i)->Type(); |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 358 | const base::string16& value = result->field(i)->value; |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 359 | ServerFieldType server_type = type.GetStorableType(); |
| 360 | if (server_type == CREDIT_CARD_VERIFICATION_CODE) { |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 361 | cvv_ = result->field(i)->value; |
| 362 | continue; |
| 363 | } |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 364 | FieldTypeGroup group = type.group(); |
Ben Murdoch | 2385ea3 | 2013-08-06 11:01:04 +0100 | [diff] [blame] | 365 | if (group == CREDIT_CARD) { |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 366 | credit_card_->SetRawInfo(server_type, value); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 367 | // TODO(dgwallinga): Find a way of cleanly deprecating CREDIT_CARD_NAME. |
| 368 | // code.google.com/p/chromium/issues/detail?id=263498 |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 369 | if (server_type == CREDIT_CARD_NAME) |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 370 | billing_address_->SetRawInfo(NAME_BILLING_FULL, value); |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 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()); |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 376 | } else if (IsBillingGroup(group)) { |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 377 | billing_address_->SetRawInfo(server_type, value); |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 378 | } else { |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 379 | profile_->SetRawInfo(server_type, value); |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 380 | } |
| 381 | } |
| 382 | |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 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 | |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 389 | FillForms(); |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 390 | } |
| 391 | |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 392 | void AutocheckoutManager::set_metric_logger( |
| 393 | scoped_ptr<AutofillMetrics> metric_logger) { |
| 394 | metric_logger_ = metric_logger.Pass(); |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 395 | } |
| 396 | |
| 397 | void AutocheckoutManager::MaybeShowAutocheckoutDialog( |
| 398 | const GURL& frame_url, |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 399 | AutocheckoutBubbleState state) { |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 400 | is_autocheckout_bubble_showing_ = false; |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 401 | |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 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; |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 408 | |
| 409 | base::Callback<void(const FormStructure*, const std::string&)> callback = |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 410 | base::Bind(&AutocheckoutManager::ReturnAutocheckoutData, |
| 411 | weak_ptr_factory_.GetWeakPtr()); |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 412 | autofill_manager_->ShowRequestAutocompleteDialog(BuildAutocheckoutFormData(), |
| 413 | frame_url, |
| 414 | DIALOG_TYPE_AUTOCHECKOUT, |
| 415 | callback); |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 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 | } |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 424 | } |
| 425 | |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 426 | void AutocheckoutManager::ShowAutocheckoutBubble( |
| 427 | const GURL& frame_url, |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 428 | const gfx::RectF& bounding_box, |
| 429 | const std::string& cookies) { |
| 430 | DCHECK(thread_checker_.CalledOnValidThread()); |
| 431 | |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 432 | base::Callback<void(AutocheckoutBubbleState)> callback = base::Bind( |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 433 | &AutocheckoutManager::MaybeShowAutocheckoutDialog, |
| 434 | weak_ptr_factory_.GetWeakPtr(), |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 435 | frame_url); |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 436 | is_autocheckout_bubble_showing_ = |
| 437 | autofill_manager_->delegate()->ShowAutocheckoutBubble( |
| 438 | bounding_box, |
| 439 | cookies.find("LSID") != std::string::npos, |
| 440 | callback); |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 441 | } |
| 442 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 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 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 451 | void AutocheckoutManager::SetValue(const AutofillField& field, |
| 452 | FormFieldData* field_to_fill) { |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 453 | // No-op if Autofill server doesn't know about the field. |
| 454 | if (field.server_type() == NO_SERVER_DATA) |
| 455 | return; |
| 456 | |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 457 | const AutofillType& type = field.Type(); |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 458 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 459 | ServerFieldType server_type = type.GetStorableType(); |
| 460 | if (server_type == FIELD_WITH_DEFAULT_VALUE) { |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 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. |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 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> |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 479 | base::string16 default_value = UTF8ToUTF16(field.default_value()); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 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 | } |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 490 | return; |
| 491 | } |
| 492 | |
| 493 | // Handle verification code directly. |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 494 | if (server_type == CREDIT_CARD_VERIFICATION_CODE) { |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 495 | field_to_fill->value = cvv_; |
| 496 | return; |
| 497 | } |
| 498 | |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 499 | if (type.group() == CREDIT_CARD) { |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 500 | credit_card_->FillFormField( |
| 501 | field, 0, autofill_manager_->app_locale(), field_to_fill); |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 502 | } else if (IsBillingGroup(type.group())) { |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 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(), |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 527 | latency_statistics_, |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 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; |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 535 | } |
| 536 | |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 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 | |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 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 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 581 | } // namespace autofill |