Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(250)

Side by Side Diff: chrome/browser/ui/autofill/autofill_dialog_controller_impl.cc

Issue 15500008: Persist the choice of AutofillDataModel when using the requestAutocomplete dialog. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: better comment Created 7 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/ui/autofill/autofill_dialog_controller_impl.h" 5 #include "chrome/browser/ui/autofill/autofill_dialog_controller_impl.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <string> 8 #include <string>
9 9
10 #include "base/bind.h" 10 #include "base/bind.h"
11 #include "base/logging.h" 11 #include "base/logging.h"
12 #include "base/prefs/pref_service.h" 12 #include "base/prefs/pref_service.h"
13 #include "base/string_util.h" 13 #include "base/string_util.h"
14 #include "base/strings/string_number_conversions.h" 14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/string_split.h" 15 #include "base/strings/string_split.h"
16 #include "base/time.h" 16 #include "base/time.h"
17 #include "base/utf_string_conversions.h" 17 #include "base/utf_string_conversions.h"
18 #include "chrome/browser/autofill/personal_data_manager_factory.h" 18 #include "chrome/browser/autofill/personal_data_manager_factory.h"
19 #include "chrome/browser/browser_process.h" 19 #include "chrome/browser/browser_process.h"
20 #include "chrome/browser/extensions/shell_window_registry.h" 20 #include "chrome/browser/extensions/shell_window_registry.h"
21 #include "chrome/browser/prefs/scoped_user_pref_update.h"
21 #include "chrome/browser/profiles/profile.h" 22 #include "chrome/browser/profiles/profile.h"
22 #include "chrome/browser/ui/autofill/autofill_dialog_view.h" 23 #include "chrome/browser/ui/autofill/autofill_dialog_view.h"
23 #include "chrome/browser/ui/autofill/data_model_wrapper.h" 24 #include "chrome/browser/ui/autofill/data_model_wrapper.h"
24 #include "chrome/browser/ui/base_window.h" 25 #include "chrome/browser/ui/base_window.h"
25 #include "chrome/browser/ui/browser.h" 26 #include "chrome/browser/ui/browser.h"
26 #include "chrome/browser/ui/browser_finder.h" 27 #include "chrome/browser/ui/browser_finder.h"
27 #include "chrome/browser/ui/browser_navigator.h" 28 #include "chrome/browser/ui/browser_navigator.h"
28 #include "chrome/browser/ui/browser_window.h" 29 #include "chrome/browser/ui/browser_window.h"
29 #include "chrome/browser/ui/extensions/native_app_window.h" 30 #include "chrome/browser/ui/extensions/native_app_window.h"
30 #include "chrome/browser/ui/extensions/shell_window.h" 31 #include "chrome/browser/ui/extensions/shell_window.h"
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
79 // special value to the server to just ask for the maximum so we don't need to 80 // special value to the server to just ask for the maximum so we don't need to
80 // hardcode it here (http://crbug.com/180731). TODO(dbeam): also maybe allow 81 // hardcode it here (http://crbug.com/180731). TODO(dbeam): also maybe allow
81 // users to give us this number via an <input> (http://crbug.com/180733). 82 // users to give us this number via an <input> (http://crbug.com/180733).
82 const int kCartMax = 1850; 83 const int kCartMax = 1850;
83 const char kCartCurrency[] = "USD"; 84 const char kCartCurrency[] = "USD";
84 85
85 const char kAddNewItemKey[] = "add-new-item"; 86 const char kAddNewItemKey[] = "add-new-item";
86 const char kManageItemsKey[] = "manage-items"; 87 const char kManageItemsKey[] = "manage-items";
87 const char kSameAsBillingKey[] = "same-as-billing"; 88 const char kSameAsBillingKey[] = "same-as-billing";
88 89
90 // Keys for the kAutofillDialogAutofillDefault pref dictionary (do not change
91 // these values).
92 const char kGuidPrefKey[] = "guid";
93 const char kVariantPrefKey[] = "variant";
94
89 // Returns true if |input| should be shown when |field_type| has been requested. 95 // Returns true if |input| should be shown when |field_type| has been requested.
90 bool InputTypeMatchesFieldType(const DetailInput& input, 96 bool InputTypeMatchesFieldType(const DetailInput& input,
91 AutofillFieldType field_type) { 97 AutofillFieldType field_type) {
92 // If any credit card expiration info is asked for, show both month and year 98 // If any credit card expiration info is asked for, show both month and year
93 // inputs. 99 // inputs.
94 if (field_type == CREDIT_CARD_EXP_4_DIGIT_YEAR || 100 if (field_type == CREDIT_CARD_EXP_4_DIGIT_YEAR ||
95 field_type == CREDIT_CARD_EXP_2_DIGIT_YEAR || 101 field_type == CREDIT_CARD_EXP_2_DIGIT_YEAR ||
96 field_type == CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR || 102 field_type == CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR ||
97 field_type == CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR || 103 field_type == CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR ||
98 field_type == CREDIT_CARD_EXP_MONTH) { 104 field_type == CREDIT_CARD_EXP_MONTH) {
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after
220 AutofillFieldType type) { 226 AutofillFieldType type) {
221 for (DetailOutputMap::const_iterator it = output.begin(); 227 for (DetailOutputMap::const_iterator it = output.begin();
222 it != output.end(); ++it) { 228 it != output.end(); ++it) {
223 if (it->first->type == type) 229 if (it->first->type == type)
224 return it->second; 230 return it->second;
225 } 231 }
226 NOTREACHED(); 232 NOTREACHED();
227 return string16(); 233 return string16();
228 } 234 }
229 235
236 // Returns a string descriptor for a DialogSection, for use with prefs (do not
237 // change these values).
238 std::string SectionToPrefString(DialogSection section) {
239 switch (section) {
240 case SECTION_CC:
241 return "cc";
242
243 case SECTION_BILLING:
244 return "billing";
245
246 case SECTION_CC_BILLING:
247 // The SECTION_CC_BILLING section isn't active when using Autofill.
248 NOTREACHED();
249 return std::string();
250
251 case SECTION_SHIPPING:
252 return "shipping";
253
254 case SECTION_EMAIL:
255 return "email";
256 }
257
258 NOTREACHED();
259 return std::string();
260 }
261
230 } // namespace 262 } // namespace
231 263
232 AutofillDialogController::~AutofillDialogController() {} 264 AutofillDialogController::~AutofillDialogController() {}
233 265
234 AutofillDialogControllerImpl::~AutofillDialogControllerImpl() { 266 AutofillDialogControllerImpl::~AutofillDialogControllerImpl() {
235 if (popup_controller_) 267 if (popup_controller_)
236 popup_controller_->Hide(); 268 popup_controller_->Hide();
237 269
238 GetMetricLogger().LogDialogInitialUserState( 270 GetMetricLogger().LogDialogInitialUserState(
239 GetDialogType(), initial_user_state_); 271 GetDialogType(), initial_user_state_);
(...skipping 18 matching lines...) Expand all
258 return autofill_dialog_controller->weak_ptr_factory_.GetWeakPtr(); 290 return autofill_dialog_controller->weak_ptr_factory_.GetWeakPtr();
259 } 291 }
260 292
261 // static 293 // static
262 void AutofillDialogControllerImpl::RegisterUserPrefs( 294 void AutofillDialogControllerImpl::RegisterUserPrefs(
263 user_prefs::PrefRegistrySyncable* registry) { 295 user_prefs::PrefRegistrySyncable* registry) {
264 registry->RegisterBooleanPref( 296 registry->RegisterBooleanPref(
265 ::prefs::kAutofillDialogPayWithoutWallet, 297 ::prefs::kAutofillDialogPayWithoutWallet,
266 kPayWithoutWalletDefault, 298 kPayWithoutWalletDefault,
267 user_prefs::PrefRegistrySyncable::SYNCABLE_PREF); 299 user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
300 registry->RegisterDictionaryPref(
301 ::prefs::kAutofillDialogAutofillDefault,
302 user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
268 } 303 }
269 304
270 void AutofillDialogControllerImpl::Show() { 305 void AutofillDialogControllerImpl::Show() {
271 dialog_shown_timestamp_ = base::Time::Now(); 306 dialog_shown_timestamp_ = base::Time::Now();
272 307
273 content::NavigationEntry* entry = contents_->GetController().GetActiveEntry(); 308 content::NavigationEntry* entry = contents_->GetController().GetActiveEntry();
274 const GURL& active_url = entry ? entry->GetURL() : contents_->GetURL(); 309 const GURL& active_url = entry ? entry->GetURL() : contents_->GetURL();
275 invoked_from_same_origin_ = active_url.GetOrigin() == source_url_.GetOrigin(); 310 invoked_from_same_origin_ = active_url.GetOrigin() == source_url_.GetOrigin();
276 311
277 // Log any relevant UI metrics and security exceptions. 312 // Log any relevant UI metrics and security exceptions.
(...skipping 653 matching lines...) Expand 10 before | Expand all | Expand 10 after
931 966
932 return scoped_ptr<DataModelWrapper>(); 967 return scoped_ptr<DataModelWrapper>();
933 } 968 }
934 969
935 if (section == SECTION_CC) { 970 if (section == SECTION_CC) {
936 CreditCard* card = GetManager()->GetCreditCardByGUID(item_key); 971 CreditCard* card = GetManager()->GetCreditCardByGUID(item_key);
937 DCHECK(card); 972 DCHECK(card);
938 return scoped_ptr<DataModelWrapper>(new AutofillCreditCardWrapper(card)); 973 return scoped_ptr<DataModelWrapper>(new AutofillCreditCardWrapper(card));
939 } 974 }
940 975
941 // Calculate the variant by looking at how many items come from the same
942 // data model.
943 size_t variant = 0;
944 for (int i = model->checked_item() - 1; i >= 0; --i) {
945 if (model->GetItemKeyAt(i) == item_key)
946 variant++;
947 else
948 break;
949 }
950
951 AutofillProfile* profile = GetManager()->GetProfileByGUID(item_key); 976 AutofillProfile* profile = GetManager()->GetProfileByGUID(item_key);
952 DCHECK(profile); 977 DCHECK(profile);
978 size_t variant = GetSelectedVariantForModel(*model);
953 return scoped_ptr<DataModelWrapper>( 979 return scoped_ptr<DataModelWrapper>(
954 new AutofillProfileWrapper(profile, variant)); 980 new AutofillProfileWrapper(profile, variant));
955 } 981 }
956 982
957 gfx::Image AutofillDialogControllerImpl::SuggestionIconForSection( 983 gfx::Image AutofillDialogControllerImpl::SuggestionIconForSection(
958 DialogSection section) { 984 DialogSection section) {
959 scoped_ptr<DataModelWrapper> model = CreateWrapper(section); 985 scoped_ptr<DataModelWrapper> model = CreateWrapper(section);
960 if (!model.get()) 986 if (!model.get())
961 return gfx::Image(); 987 return gfx::Image();
962 988
(...skipping 904 matching lines...) Expand 10 before | Expand all | Expand 10 after
1867 } 1893 }
1868 1894
1869 suggested_shipping_.AddKeyedItem( 1895 suggested_shipping_.AddKeyedItem(
1870 kAddNewItemKey, 1896 kAddNewItemKey,
1871 l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_ADD_SHIPPING_ADDRESS)); 1897 l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_ADD_SHIPPING_ADDRESS));
1872 suggested_shipping_.AddKeyedItem( 1898 suggested_shipping_.AddKeyedItem(
1873 kManageItemsKey, 1899 kManageItemsKey,
1874 l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_MANAGE_SHIPPING_ADDRESS)); 1900 l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_MANAGE_SHIPPING_ADDRESS));
1875 1901
1876 if (!IsPayingWithWallet()) { 1902 if (!IsPayingWithWallet()) {
1877 // When using Autofill, the default option is the first suggestion, if 1903 for (size_t i = SECTION_MIN; i <= SECTION_MAX; ++i) {
1878 // one exists. Otherwise it's the "Use shipping for billing" item. 1904 DialogSection section = static_cast<DialogSection>(i);
1879 const std::string& first_real_suggestion_item_key = 1905 if (!SectionIsActive(section))
1880 suggested_shipping_.GetItemKeyAt(1); 1906 continue;
1881 if (IsASuggestionItemKey(first_real_suggestion_item_key)) 1907
1882 suggested_shipping_.SetCheckedItem(first_real_suggestion_item_key); 1908 std::string default_guid;
1909 int default_variant;
1910 SuggestionsMenuModel* model = SuggestionsMenuModelForSection(section);
Ilya Sherman 2013/05/21 03:54:35 nit: Perhaps move this into the if-stmt, since it'
Evan Stade 2013/05/21 17:34:41 changed this code a bit.
1911 GetAutofillChoice(section, &default_guid, &default_variant);
1912 if (!default_guid.empty())
1913 model->SetCheckedItemNthWithKey(default_guid, default_variant + 1);
1914 }
1883 } 1915 }
1884 1916
1885 if (view_) 1917 if (view_)
1886 view_->ModelChanged(); 1918 view_->ModelChanged();
1887 1919
1888 for (size_t section = SECTION_MIN; section <= SECTION_MAX; ++section) { 1920 for (size_t section = SECTION_MIN; section <= SECTION_MAX; ++section) {
1889 PrepareDetailInputsForSection(static_cast<DialogSection>(section)); 1921 PrepareDetailInputsForSection(static_cast<DialogSection>(section));
1890 } 1922 }
1891 } 1923 }
1892 1924
(...skipping 30 matching lines...) Expand all
1923 if (wrapper) { 1955 if (wrapper) {
1924 // Only fill in data that is associated with this section. 1956 // Only fill in data that is associated with this section.
1925 const DetailInputs& inputs = RequestedFieldsForSection(section); 1957 const DetailInputs& inputs = RequestedFieldsForSection(section);
1926 wrapper->FillFormStructure(inputs, compare, &form_structure_); 1958 wrapper->FillFormStructure(inputs, compare, &form_structure_);
1927 1959
1928 // CVC needs special-casing because the CreditCard class doesn't store or 1960 // CVC needs special-casing because the CreditCard class doesn't store or
1929 // handle them. This isn't necessary when filling the combined CC and 1961 // handle them. This isn't necessary when filling the combined CC and
1930 // billing section as CVC comes from |full_wallet_| in this case. 1962 // billing section as CVC comes from |full_wallet_| in this case.
1931 if (section == SECTION_CC) 1963 if (section == SECTION_CC)
1932 SetCvcResult(view_->GetCvc()); 1964 SetCvcResult(view_->GetCvc());
1965
Ilya Sherman 2013/05/21 03:54:35 nit: Spurious diff
Evan Stade 2013/05/21 17:34:41 Done.
1933 } else { 1966 } else {
1934 // The user manually input data. If using Autofill, save the info as new or 1967 // The user manually input data. If using Autofill, save the info as new or
1935 // edited data. Always fill local data into |form_structure_|. 1968 // edited data. Always fill local data into |form_structure_|.
1936 DetailOutputMap output; 1969 DetailOutputMap output;
1937 view_->GetUserInput(section, &output); 1970 view_->GetUserInput(section, &output);
1938 1971
1939 if (section == SECTION_CC) { 1972 if (section == SECTION_CC) {
1940 CreditCard card; 1973 CreditCard card;
1941 FillFormGroupFromOutputs(output, &card); 1974 FillFormGroupFromOutputs(output, &card);
1942 1975
(...skipping 458 matching lines...) Expand 10 before | Expand all | Expand 10 after
2401 FillOutputForSectionWithComparator( 2434 FillOutputForSectionWithComparator(
2402 SECTION_CC, 2435 SECTION_CC,
2403 base::Bind(DetailInputMatchesShippingField)); 2436 base::Bind(DetailInputMatchesShippingField));
2404 FillOutputForSectionWithComparator( 2437 FillOutputForSectionWithComparator(
2405 SECTION_CC_BILLING, 2438 SECTION_CC_BILLING,
2406 base::Bind(DetailInputMatchesShippingField)); 2439 base::Bind(DetailInputMatchesShippingField));
2407 } else { 2440 } else {
2408 FillOutputForSection(SECTION_SHIPPING); 2441 FillOutputForSection(SECTION_SHIPPING);
2409 } 2442 }
2410 2443
2444 if (!IsPayingWithWallet()) {
2445 for (size_t i = SECTION_MIN; i <= SECTION_MAX; ++i) {
2446 DialogSection section = static_cast<DialogSection>(i);
2447 if (!SectionIsActive(section))
2448 continue;
2449
2450 SuggestionsMenuModel* model = SuggestionsMenuModelForSection(section);
2451 std::string item_key = model->GetItemKeyForCheckedItem();
2452 if (IsASuggestionItemKey(item_key) || item_key == kSameAsBillingKey) {
2453 int variant = GetSelectedVariantForModel(*model);
2454 PersistAutofillChoice(section, item_key, variant);
2455 }
2456 }
2457 }
2458
2411 callback_.Run(&form_structure_, !wallet_items_ ? std::string() : 2459 callback_.Run(&form_structure_, !wallet_items_ ? std::string() :
2412 wallet_items_->google_transaction_id()); 2460 wallet_items_->google_transaction_id());
2413 callback_ = base::Callback<void(const FormStructure*, const std::string&)>(); 2461 callback_ = base::Callback<void(const FormStructure*, const std::string&)>();
2414 2462
2415 LogOnFinishSubmitMetrics(); 2463 LogOnFinishSubmitMetrics();
2416 2464
2417 // On a successful submit, if the user manually selected "pay without wallet", 2465 // On a successful submit, if the user manually selected "pay without wallet",
2418 // stop trying to pay with Wallet on future runs of the dialog. 2466 // stop trying to pay with Wallet on future runs of the dialog.
2419 bool manually_selected_pay_without_wallet = 2467 bool manually_selected_pay_without_wallet =
2420 !account_chooser_model_.WalletIsSelected() && 2468 !account_chooser_model_.WalletIsSelected() &&
(...skipping 14 matching lines...) Expand all
2435 view_->UpdateNotificationArea(); 2483 view_->UpdateNotificationArea();
2436 break; 2484 break;
2437 2485
2438 case DIALOG_TYPE_REQUEST_AUTOCOMPLETE: 2486 case DIALOG_TYPE_REQUEST_AUTOCOMPLETE:
2439 // This may delete us. 2487 // This may delete us.
2440 Hide(); 2488 Hide();
2441 break; 2489 break;
2442 } 2490 }
2443 } 2491 }
2444 2492
2493 void AutofillDialogControllerImpl::PersistAutofillChoice(
2494 DialogSection section,
2495 const std::string& guid,
2496 int variant) {
2497 DCHECK(!IsPayingWithWallet());
2498 scoped_ptr<base::DictionaryValue> value(new base::DictionaryValue());
2499 value->SetString(kGuidPrefKey, guid);
2500 value->SetInteger(kVariantPrefKey, variant);
2501
2502 DictionaryPrefUpdate updater(profile()->GetPrefs(),
2503 ::prefs::kAutofillDialogAutofillDefault);
2504 base::DictionaryValue* autofill_choice = updater.Get();
2505 autofill_choice->Set(SectionToPrefString(section), value.release());
2506 }
2507
2508 void AutofillDialogControllerImpl::GetAutofillChoice(DialogSection section,
2509 std::string* guid,
2510 int* variant) {
2511 DCHECK(!IsPayingWithWallet());
2512 // The default choice is the first thing in the menu that is a suggestion
2513 // item.
2514 *variant = 0;
2515 SuggestionsMenuModel* model = SuggestionsMenuModelForSection(section);
2516 for (int i = 0; i < model->GetItemCount(); ++i) {
2517 if (IsASuggestionItemKey(model->GetItemKeyAt(i))) {
2518 *guid = model->GetItemKeyAt(i);
2519 break;
2520 }
2521 }
2522
2523 const base::DictionaryValue* choices = profile()->GetPrefs()->GetDictionary(
2524 ::prefs::kAutofillDialogAutofillDefault);
2525 if (!choices)
2526 return;
2527
2528 const base::DictionaryValue* choice = NULL;
2529 if (!choices->GetDictionary(SectionToPrefString(section), &choice))
2530 return;
2531
2532 choice->GetString(kGuidPrefKey, guid);
2533 choice->GetInteger(kVariantPrefKey, variant);
2534 }
2535
2536 size_t AutofillDialogControllerImpl::GetSelectedVariantForModel(
2537 const SuggestionsMenuModel& model) {
2538 size_t variant = 0;
2539 // Calculate the variant by looking at how many items come from the same
2540 // data model.
2541 for (int i = model.checked_item() - 1; i >= 0; --i) {
2542 if (model.GetItemKeyAt(i) == model.GetItemKeyForCheckedItem())
2543 variant++;
Ilya Sherman 2013/05/21 03:54:35 nit: ++variant
Evan Stade 2013/05/21 17:34:41 this is not a rule that I'm aware of. "For simple
Ilya Sherman 2013/05/21 23:20:43 I prefer to always use pre-increment because it's
2544 else
2545 break;
2546 }
2547 return variant;
2548 }
2549
2445 void AutofillDialogControllerImpl::LogOnFinishSubmitMetrics() { 2550 void AutofillDialogControllerImpl::LogOnFinishSubmitMetrics() {
2446 GetMetricLogger().LogDialogUiDuration( 2551 GetMetricLogger().LogDialogUiDuration(
2447 base::Time::Now() - dialog_shown_timestamp_, 2552 base::Time::Now() - dialog_shown_timestamp_,
2448 GetDialogType(), 2553 GetDialogType(),
2449 AutofillMetrics::DIALOG_ACCEPTED); 2554 AutofillMetrics::DIALOG_ACCEPTED);
2450 2555
2451 GetMetricLogger().LogDialogUiEvent( 2556 GetMetricLogger().LogDialogUiEvent(
2452 GetDialogType(), AutofillMetrics::DIALOG_UI_ACCEPTED); 2557 GetDialogType(), AutofillMetrics::DIALOG_UI_ACCEPTED);
2453 2558
2454 AutofillMetrics::DialogDismissalState dismissal_state; 2559 AutofillMetrics::DialogDismissalState dismissal_state;
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after
2538 AutofillMetrics::DIALOG_USER_SIGNED_IN_NO_WALLET_NO_AUTOFILL; 2643 AutofillMetrics::DIALOG_USER_SIGNED_IN_NO_WALLET_NO_AUTOFILL;
2539 } 2644 }
2540 2645
2541 // Has Wallet items. 2646 // Has Wallet items.
2542 return has_autofill_profiles ? 2647 return has_autofill_profiles ?
2543 AutofillMetrics::DIALOG_USER_SIGNED_IN_HAS_WALLET_HAS_AUTOFILL : 2648 AutofillMetrics::DIALOG_USER_SIGNED_IN_HAS_WALLET_HAS_AUTOFILL :
2544 AutofillMetrics::DIALOG_USER_SIGNED_IN_HAS_WALLET_NO_AUTOFILL; 2649 AutofillMetrics::DIALOG_USER_SIGNED_IN_HAS_WALLET_NO_AUTOFILL;
2545 } 2650 }
2546 2651
2547 } // namespace autofill 2652 } // namespace autofill
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698