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

Side by Side Diff: components/autofill/core/browser/form_structure.cc

Issue 23033016: Remove autocheckout code. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Even more deletes, and Ilya review. Created 7 years, 3 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 "components/autofill/core/browser/form_structure.h" 5 #include "components/autofill/core/browser/form_structure.h"
6 6
7 #include <utility> 7 #include <utility>
8 8
9 #include "base/basictypes.h" 9 #include "base/basictypes.h"
10 #include "base/command_line.h" 10 #include "base/command_line.h"
11 #include "base/logging.h" 11 #include "base/logging.h"
12 #include "base/memory/scoped_ptr.h" 12 #include "base/memory/scoped_ptr.h"
13 #include "base/sha1.h" 13 #include "base/sha1.h"
14 #include "base/strings/string_number_conversions.h" 14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/string_util.h" 15 #include "base/strings/string_util.h"
16 #include "base/strings/stringprintf.h" 16 #include "base/strings/stringprintf.h"
17 #include "base/strings/utf_string_conversions.h" 17 #include "base/strings/utf_string_conversions.h"
18 #include "base/time/time.h" 18 #include "base/time/time.h"
19 #include "components/autofill/content/browser/autocheckout_page_meta_data.h"
20 #include "components/autofill/core/browser/autofill_metrics.h" 19 #include "components/autofill/core/browser/autofill_metrics.h"
21 #include "components/autofill/core/browser/autofill_type.h" 20 #include "components/autofill/core/browser/autofill_type.h"
22 #include "components/autofill/core/browser/autofill_xml_parser.h" 21 #include "components/autofill/core/browser/autofill_xml_parser.h"
23 #include "components/autofill/core/browser/field_types.h" 22 #include "components/autofill/core/browser/field_types.h"
24 #include "components/autofill/core/browser/form_field.h" 23 #include "components/autofill/core/browser/form_field.h"
25 #include "components/autofill/core/common/autofill_constants.h" 24 #include "components/autofill/core/common/autofill_constants.h"
26 #include "components/autofill/core/common/form_data.h" 25 #include "components/autofill/core/common/form_data.h"
27 #include "components/autofill/core/common/form_data_predictions.h" 26 #include "components/autofill/core/common/form_data_predictions.h"
28 #include "components/autofill/core/common/form_field_data.h" 27 #include "components/autofill/core/common/form_field_data.h"
29 #include "components/autofill/core/common/form_field_data_predictions.h" 28 #include "components/autofill/core/common/form_field_data_predictions.h"
(...skipping 11 matching lines...) Expand all
41 const char kAttributeAutofillType[] = "autofilltype"; 40 const char kAttributeAutofillType[] = "autofilltype";
42 const char kAttributeClientVersion[] = "clientversion"; 41 const char kAttributeClientVersion[] = "clientversion";
43 const char kAttributeDataPresent[] = "datapresent"; 42 const char kAttributeDataPresent[] = "datapresent";
44 const char kAttributeFieldID[] = "fieldid"; 43 const char kAttributeFieldID[] = "fieldid";
45 const char kAttributeFieldType[] = "fieldtype"; 44 const char kAttributeFieldType[] = "fieldtype";
46 const char kAttributeFormSignature[] = "formsignature"; 45 const char kAttributeFormSignature[] = "formsignature";
47 const char kAttributeName[] = "name"; 46 const char kAttributeName[] = "name";
48 const char kAttributeSignature[] = "signature"; 47 const char kAttributeSignature[] = "signature";
49 const char kAttributeUrlprefixSignature[] = "urlprefixsignature"; 48 const char kAttributeUrlprefixSignature[] = "urlprefixsignature";
50 const char kAcceptedFeaturesExperiment[] = "e"; // e=experiments 49 const char kAcceptedFeaturesExperiment[] = "e"; // e=experiments
51 const char kAcceptedFeaturesAutocheckoutExperiment[] = "a,e"; // a=autocheckout
52 const char kClientVersion[] = "6.1.1715.1442/en (GGLL)"; 50 const char kClientVersion[] = "6.1.1715.1442/en (GGLL)";
53 const char kXMLDeclaration[] = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"; 51 const char kXMLDeclaration[] = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
54 const char kXMLElementAutofillQuery[] = "autofillquery"; 52 const char kXMLElementAutofillQuery[] = "autofillquery";
55 const char kXMLElementAutofillUpload[] = "autofillupload"; 53 const char kXMLElementAutofillUpload[] = "autofillupload";
56 const char kXMLElementFieldAssignments[] = "fieldassignments"; 54 const char kXMLElementFieldAssignments[] = "fieldassignments";
57 const char kXMLElementField[] = "field"; 55 const char kXMLElementField[] = "field";
58 const char kXMLElementFields[] = "fields"; 56 const char kXMLElementFields[] = "fields";
59 const char kXMLElementForm[] = "form"; 57 const char kXMLElementForm[] = "form";
60 const char kBillingMode[] = "billing"; 58 const char kBillingMode[] = "billing";
61 const char kShippingMode[] = "shipping"; 59 const char kShippingMode[] = "shipping";
(...skipping 255 matching lines...) Expand 10 before | Expand all | Expand 10 after
317 if (status != U_ZERO_ERROR) { 315 if (status != U_ZERO_ERROR) {
318 DVLOG(1) << "Couldn't strip digits in " << UTF16ToUTF8(input); 316 DVLOG(1) << "Couldn't strip digits in " << UTF16ToUTF8(input);
319 return UTF16ToUTF8(input); 317 return UTF16ToUTF8(input);
320 } 318 }
321 319
322 return return_string; 320 return return_string;
323 } 321 }
324 322
325 } // namespace 323 } // namespace
326 324
327 FormStructure::FormStructure(const FormData& form, 325 FormStructure::FormStructure(const FormData& form)
328 const std::string& autocheckout_url_prefix)
329 : form_name_(form.name), 326 : form_name_(form.name),
330 source_url_(form.origin), 327 source_url_(form.origin),
331 target_url_(form.action), 328 target_url_(form.action),
332 autofill_count_(0), 329 autofill_count_(0),
333 active_field_count_(0), 330 active_field_count_(0),
334 upload_required_(USE_UPLOAD_RATES), 331 upload_required_(USE_UPLOAD_RATES),
335 server_experiment_id_("no server response"), 332 server_experiment_id_("no server response"),
336 has_author_specified_types_(false), 333 has_author_specified_types_(false) {
337 autocheckout_url_prefix_(autocheckout_url_prefix),
338 filled_by_autocheckout_(false) {
339 // Copy the form fields. 334 // Copy the form fields.
340 std::map<base::string16, size_t> unique_names; 335 std::map<base::string16, size_t> unique_names;
341 for (std::vector<FormFieldData>::const_iterator field = 336 for (std::vector<FormFieldData>::const_iterator field =
342 form.fields.begin(); 337 form.fields.begin();
343 field != form.fields.end(); field++) { 338 field != form.fields.end(); field++) {
344 339
345 if (!ShouldSkipField(*field)) { 340 if (!ShouldSkipField(*field)) {
346 // Add all supported form fields (including with empty names) to the 341 // Add all supported form fields (including with empty names) to the
347 // signature. This is a requirement for Autofill servers. 342 // signature. This is a requirement for Autofill servers.
348 form_signature_field_names_.append("&"); 343 form_signature_field_names_.append("&");
(...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after
488 encoded_xml->clear(); 483 encoded_xml->clear();
489 encoded_signatures->clear(); 484 encoded_signatures->clear();
490 encoded_signatures->reserve(forms.size()); 485 encoded_signatures->reserve(forms.size());
491 486
492 // Set up the <autofillquery> element and attributes. 487 // Set up the <autofillquery> element and attributes.
493 buzz::XmlElement autofill_request_xml( 488 buzz::XmlElement autofill_request_xml(
494 (buzz::QName(kXMLElementAutofillQuery))); 489 (buzz::QName(kXMLElementAutofillQuery)));
495 autofill_request_xml.SetAttr(buzz::QName(kAttributeClientVersion), 490 autofill_request_xml.SetAttr(buzz::QName(kAttributeClientVersion),
496 kClientVersion); 491 kClientVersion);
497 492
498 // autocheckout_url_prefix tells the Autofill server where the forms in the
499 // request came from, and the the Autofill server checks internal status and
500 // decide to enable Autocheckout or not and may return Autocheckout related
501 // data in the response accordingly.
502 // There is no page/frame level object associated with FormStructure that
503 // we could extract URL prefix from. But, all the forms should come from the
504 // same frame, so they should have the same Autocheckout URL prefix. Thus we
505 // use URL prefix from the first form with Autocheckout enabled.
506 std::string autocheckout_url_prefix;
507
508 // Some badly formatted web sites repeat forms - detect that and encode only 493 // Some badly formatted web sites repeat forms - detect that and encode only
509 // one form as returned data would be the same for all the repeated forms. 494 // one form as returned data would be the same for all the repeated forms.
510 std::set<std::string> processed_forms; 495 std::set<std::string> processed_forms;
511 for (ScopedVector<FormStructure>::const_iterator it = forms.begin(); 496 for (ScopedVector<FormStructure>::const_iterator it = forms.begin();
512 it != forms.end(); 497 it != forms.end();
513 ++it) { 498 ++it) {
514 std::string signature((*it)->FormSignature()); 499 std::string signature((*it)->FormSignature());
515 if (processed_forms.find(signature) != processed_forms.end()) 500 if (processed_forms.find(signature) != processed_forms.end())
516 continue; 501 continue;
517 processed_forms.insert(signature); 502 processed_forms.insert(signature);
518 scoped_ptr<buzz::XmlElement> encompassing_xml_element( 503 scoped_ptr<buzz::XmlElement> encompassing_xml_element(
519 new buzz::XmlElement(buzz::QName(kXMLElementForm))); 504 new buzz::XmlElement(buzz::QName(kXMLElementForm)));
520 encompassing_xml_element->SetAttr(buzz::QName(kAttributeSignature), 505 encompassing_xml_element->SetAttr(buzz::QName(kAttributeSignature),
521 signature); 506 signature);
522 507
523 if (!(*it)->EncodeFormRequest(FormStructure::QUERY, 508 if (!(*it)->EncodeFormRequest(FormStructure::QUERY,
524 encompassing_xml_element.get())) 509 encompassing_xml_element.get()))
525 continue; // Malformed form, skip it. 510 continue; // Malformed form, skip it.
526 511
527 if ((*it)->IsAutocheckoutEnabled()) {
528 if (autocheckout_url_prefix.empty()) {
529 autocheckout_url_prefix = (*it)->autocheckout_url_prefix_;
530 } else {
531 // Making sure all the forms in the request has the same url_prefix.
532 DCHECK_EQ(autocheckout_url_prefix, (*it)->autocheckout_url_prefix_);
533 }
534 }
535
536 autofill_request_xml.AddElement(encompassing_xml_element.release()); 512 autofill_request_xml.AddElement(encompassing_xml_element.release());
537 encoded_signatures->push_back(signature); 513 encoded_signatures->push_back(signature);
538 } 514 }
539 515
540 if (!encoded_signatures->size()) 516 if (!encoded_signatures->size())
541 return false; 517 return false;
542 518
543 if (autocheckout_url_prefix.empty()) { 519 autofill_request_xml.SetAttr(buzz::QName(kAttributeAcceptedFeatures),
544 autofill_request_xml.SetAttr(buzz::QName(kAttributeAcceptedFeatures), 520 kAcceptedFeaturesExperiment);
545 kAcceptedFeaturesExperiment);
546 } else {
547 autofill_request_xml.SetAttr(buzz::QName(kAttributeAcceptedFeatures),
548 kAcceptedFeaturesAutocheckoutExperiment);
549 autofill_request_xml.SetAttr(buzz::QName(kAttributeUrlprefixSignature),
550 Hash64Bit(autocheckout_url_prefix));
551 }
552 521
553 // Obtain the XML structure as a string. 522 // Obtain the XML structure as a string.
554 *encoded_xml = kXMLDeclaration; 523 *encoded_xml = kXMLDeclaration;
555 *encoded_xml += autofill_request_xml.Str().c_str(); 524 *encoded_xml += autofill_request_xml.Str().c_str();
556 525
557 return true; 526 return true;
558 } 527 }
559 528
560 // static 529 // static
561 void FormStructure::ParseQueryResponse( 530 void FormStructure::ParseQueryResponse(
562 const std::string& response_xml, 531 const std::string& response_xml,
563 const std::vector<FormStructure*>& forms, 532 const std::vector<FormStructure*>& forms,
564 autofill::AutocheckoutPageMetaData* page_meta_data,
565 const AutofillMetrics& metric_logger) { 533 const AutofillMetrics& metric_logger) {
566 metric_logger.LogServerQueryMetric(AutofillMetrics::QUERY_RESPONSE_RECEIVED); 534 metric_logger.LogServerQueryMetric(AutofillMetrics::QUERY_RESPONSE_RECEIVED);
567 535
568 // Parse the field types from the server response to the query. 536 // Parse the field types from the server response to the query.
569 std::vector<AutofillServerFieldInfo> field_infos; 537 std::vector<AutofillServerFieldInfo> field_infos;
570 UploadRequired upload_required; 538 UploadRequired upload_required;
571 std::string experiment_id; 539 std::string experiment_id;
572 AutofillQueryXmlParser parse_handler(&field_infos, 540 AutofillQueryXmlParser parse_handler(&field_infos,
573 &upload_required, 541 &upload_required,
574 &experiment_id, 542 &experiment_id);
575 page_meta_data);
576 buzz::XmlParser parser(&parse_handler); 543 buzz::XmlParser parser(&parse_handler);
577 parser.Parse(response_xml.c_str(), response_xml.length(), true); 544 parser.Parse(response_xml.c_str(), response_xml.length(), true);
578 if (!parse_handler.succeeded()) 545 if (!parse_handler.succeeded())
579 return; 546 return;
580 547
581 metric_logger.LogServerQueryMetric(AutofillMetrics::QUERY_RESPONSE_PARSED); 548 metric_logger.LogServerQueryMetric(AutofillMetrics::QUERY_RESPONSE_PARSED);
582 metric_logger.LogServerExperimentIdForQuery(experiment_id); 549 metric_logger.LogServerExperimentIdForQuery(experiment_id);
583 550
584 bool heuristics_detected_fillable_field = false; 551 bool heuristics_detected_fillable_field = false;
585 bool query_response_overrode_heuristics = false; 552 bool query_response_overrode_heuristics = false;
(...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after
685 host = source_url_.host(); 652 host = source_url_.host();
686 } 653 }
687 654
688 std::string form_string = scheme + "://" + host + "&" + 655 std::string form_string = scheme + "://" + host + "&" +
689 UTF16ToUTF8(form_name_) + 656 UTF16ToUTF8(form_name_) +
690 form_signature_field_names_; 657 form_signature_field_names_;
691 658
692 return Hash64Bit(form_string); 659 return Hash64Bit(form_string);
693 } 660 }
694 661
695 bool FormStructure::IsAutocheckoutEnabled() const {
696 return !autocheckout_url_prefix_.empty();
697 }
698
699 bool FormStructure::ShouldSkipField(const FormFieldData& field) const { 662 bool FormStructure::ShouldSkipField(const FormFieldData& field) const {
700 return (field.is_checkable || field.form_control_type == "password") && 663 return (field.is_checkable || field.form_control_type == "password");
701 !IsAutocheckoutEnabled();
702 }
703
704 size_t FormStructure::RequiredFillableFields() const {
705 return IsAutocheckoutEnabled() ? 0 : kRequiredAutofillFields;
706 } 664 }
707 665
708 bool FormStructure::IsAutofillable(bool require_method_post) const { 666 bool FormStructure::IsAutofillable(bool require_method_post) const {
709 if (autofill_count() < RequiredFillableFields()) 667 if (autofill_count() < kRequiredAutofillFields)
710 return false; 668 return false;
711 669
712 return ShouldBeParsed(require_method_post); 670 return ShouldBeParsed(require_method_post);
713 } 671 }
714 672
715 void FormStructure::UpdateAutofillCount() { 673 void FormStructure::UpdateAutofillCount() {
716 autofill_count_ = 0; 674 autofill_count_ = 0;
717 for (std::vector<AutofillField*>::const_iterator iter = begin(); 675 for (std::vector<AutofillField*>::const_iterator iter = begin();
718 iter != end(); ++iter) { 676 iter != end(); ++iter) {
719 AutofillField* field = *iter; 677 AutofillField* field = *iter;
720 if (field && field->IsFieldFillable()) 678 if (field && field->IsFieldFillable())
721 ++autofill_count_; 679 ++autofill_count_;
722 } 680 }
723 } 681 }
724 682
725 bool FormStructure::ShouldBeParsed(bool require_method_post) const { 683 bool FormStructure::ShouldBeParsed(bool require_method_post) const {
726 if (active_field_count() < RequiredFillableFields()) 684 if (active_field_count() < kRequiredAutofillFields)
727 return false; 685 return false;
728 686
729 // Rule out http(s)://*/search?... 687 // Rule out http(s)://*/search?...
730 // e.g. http://www.google.com/search?q=... 688 // e.g. http://www.google.com/search?q=...
731 // http://search.yahoo.com/search?p=... 689 // http://search.yahoo.com/search?p=...
732 if (target_url_.path() == "/search") 690 if (target_url_.path() == "/search")
733 return false; 691 return false;
734 692
735 if (!IsAutocheckoutEnabled()) { 693 bool has_text_field = false;
736 // Make sure there is at least one text field when Autocheckout is 694 for (std::vector<AutofillField*>::const_iterator it = begin();
737 // not enabled. 695 it != end() && !has_text_field; ++it) {
738 bool has_text_field = false; 696 has_text_field |= (*it)->form_control_type != "select-one";
739 for (std::vector<AutofillField*>::const_iterator it = begin();
740 it != end() && !has_text_field; ++it) {
741 has_text_field |= (*it)->form_control_type != "select-one";
742 }
743 if (!has_text_field)
744 return false;
745 } 697 }
698 if (!has_text_field)
699 return false;
746 700
747 return !require_method_post || (method_ == POST); 701 return !require_method_post || (method_ == POST);
748 } 702 }
749 703
750 bool FormStructure::ShouldBeCrowdsourced() const { 704 bool FormStructure::ShouldBeCrowdsourced() const {
751 // Allow all forms in Autocheckout flow to be crowdsourced. 705 return !has_author_specified_types_ && ShouldBeParsed(true);
752 return (!has_author_specified_types_ && ShouldBeParsed(true)) ||
753 IsAutocheckoutEnabled();
754 } 706 }
755 707
756 void FormStructure::UpdateFromCache(const FormStructure& cached_form) { 708 void FormStructure::UpdateFromCache(const FormStructure& cached_form) {
757 // Map from field signatures to cached fields. 709 // Map from field signatures to cached fields.
758 std::map<std::string, const AutofillField*> cached_fields; 710 std::map<std::string, const AutofillField*> cached_fields;
759 for (size_t i = 0; i < cached_form.field_count(); ++i) { 711 for (size_t i = 0; i < cached_form.field_count(); ++i) {
760 const AutofillField* field = cached_form.field(i); 712 const AutofillField* field = cached_form.field(i);
761 cached_fields[field->FieldSignature()] = field; 713 cached_fields[field->FieldSignature()] = field;
762 } 714 }
763 715
(...skipping 11 matching lines...) Expand all
775 field->value = base::string16(); 727 field->value = base::string16();
776 } 728 }
777 729
778 field->set_heuristic_type(cached_field->second->heuristic_type()); 730 field->set_heuristic_type(cached_field->second->heuristic_type());
779 field->set_server_type(cached_field->second->server_type()); 731 field->set_server_type(cached_field->second->server_type());
780 } 732 }
781 } 733 }
782 734
783 UpdateAutofillCount(); 735 UpdateAutofillCount();
784 736
785 filled_by_autocheckout_ = cached_form.filled_by_autocheckout();
786 server_experiment_id_ = cached_form.server_experiment_id(); 737 server_experiment_id_ = cached_form.server_experiment_id();
787 738
788 // The form signature should match between query and upload requests to the 739 // The form signature should match between query and upload requests to the
789 // server. On many websites, form elements are dynamically added, removed, or 740 // server. On many websites, form elements are dynamically added, removed, or
790 // rearranged via JavaScript between page load and form submission, so we 741 // rearranged via JavaScript between page load and form submission, so we
791 // copy over the |form_signature_field_names_| corresponding to the query 742 // copy over the |form_signature_field_names_| corresponding to the query
792 // request. 743 // request.
793 DCHECK_EQ(cached_form.form_name_, form_name_); 744 DCHECK_EQ(cached_form.form_name_, form_name_);
794 DCHECK_EQ(cached_form.source_url_, source_url_); 745 DCHECK_EQ(cached_form.source_url_, source_url_);
795 DCHECK_EQ(cached_form.target_url_, target_url_); 746 DCHECK_EQ(cached_form.target_url_, target_url_);
(...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after
922 AutofillMetrics::NOT_AUTOFILLED_SERVER_TYPE_MATCH, 873 AutofillMetrics::NOT_AUTOFILLED_SERVER_TYPE_MATCH,
923 experiment_id); 874 experiment_id);
924 } else { 875 } else {
925 metric_logger.LogQualityMetric( 876 metric_logger.LogQualityMetric(
926 AutofillMetrics::NOT_AUTOFILLED_SERVER_TYPE_MISMATCH, 877 AutofillMetrics::NOT_AUTOFILLED_SERVER_TYPE_MISMATCH,
927 experiment_id); 878 experiment_id);
928 } 879 }
929 } 880 }
930 } 881 }
931 882
932 if (num_detected_field_types < RequiredFillableFields()) { 883 if (num_detected_field_types < kRequiredAutofillFields) {
933 metric_logger.LogUserHappinessMetric( 884 metric_logger.LogUserHappinessMetric(
934 AutofillMetrics::SUBMITTED_NON_FILLABLE_FORM); 885 AutofillMetrics::SUBMITTED_NON_FILLABLE_FORM);
935 } else { 886 } else {
936 if (did_autofill_all_possible_fields) { 887 if (did_autofill_all_possible_fields) {
937 metric_logger.LogUserHappinessMetric( 888 metric_logger.LogUserHappinessMetric(
938 AutofillMetrics::SUBMITTED_FILLABLE_FORM_AUTOFILLED_ALL); 889 AutofillMetrics::SUBMITTED_FILLABLE_FORM_AUTOFILLED_ALL);
939 } else if (did_autofill_some_possible_fields) { 890 } else if (did_autofill_some_possible_fields) {
940 metric_logger.LogUserHappinessMetric( 891 metric_logger.LogUserHappinessMetric(
941 AutofillMetrics::SUBMITTED_FILLABLE_FORM_AUTOFILLED_SOME); 892 AutofillMetrics::SUBMITTED_FILLABLE_FORM_AUTOFILLED_SOME);
942 } else { 893 } else {
(...skipping 307 matching lines...) Expand 10 before | Expand all | Expand 10 after
1250 field != fields_.end(); ++field) { 1201 field != fields_.end(); ++field) {
1251 FieldTypeGroup field_type_group = (*field)->Type().group(); 1202 FieldTypeGroup field_type_group = (*field)->Type().group();
1252 if (field_type_group == CREDIT_CARD) 1203 if (field_type_group == CREDIT_CARD)
1253 (*field)->set_section((*field)->section() + "-cc"); 1204 (*field)->set_section((*field)->section() + "-cc");
1254 else 1205 else
1255 (*field)->set_section((*field)->section() + "-default"); 1206 (*field)->set_section((*field)->section() + "-default");
1256 } 1207 }
1257 } 1208 }
1258 1209
1259 } // namespace autofill 1210 } // namespace autofill
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698