| OLD | NEW |
| 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 <stdint.h> | 7 #include <stdint.h> |
| 8 | 8 |
| 9 #include <algorithm> | 9 #include <algorithm> |
| 10 #include <map> | 10 #include <map> |
| (...skipping 29 matching lines...) Expand all Loading... |
| 40 #include "components/rappor/rappor_service_impl.h" | 40 #include "components/rappor/rappor_service_impl.h" |
| 41 #include "components/ukm/public/ukm_recorder.h" | 41 #include "components/ukm/public/ukm_recorder.h" |
| 42 | 42 |
| 43 namespace autofill { | 43 namespace autofill { |
| 44 namespace { | 44 namespace { |
| 45 | 45 |
| 46 const char kClientVersion[] = "6.1.1715.1442/en (GGLL)"; | 46 const char kClientVersion[] = "6.1.1715.1442/en (GGLL)"; |
| 47 const char kBillingMode[] = "billing"; | 47 const char kBillingMode[] = "billing"; |
| 48 const char kShippingMode[] = "shipping"; | 48 const char kShippingMode[] = "shipping"; |
| 49 | 49 |
| 50 // A form is considered to have a high prediction mismatch rate if the number of | |
| 51 // mismatches exceeds this threshold. | |
| 52 const int kNumberOfMismatchesThreshold = 3; | |
| 53 | |
| 54 // Only removing common name prefixes if we have a minimum number of fields and | 50 // Only removing common name prefixes if we have a minimum number of fields and |
| 55 // a minimum prefix length. These values are chosen to avoid cases such as two | 51 // a minimum prefix length. These values are chosen to avoid cases such as two |
| 56 // fields with "address1" and "address2" and be effective against web frameworks | 52 // fields with "address1" and "address2" and be effective against web frameworks |
| 57 // which prepend prefixes such as "ctl01$ctl00$MainContentRegion$" on all | 53 // which prepend prefixes such as "ctl01$ctl00$MainContentRegion$" on all |
| 58 // fields. | 54 // fields. |
| 59 const int kCommonNamePrefixRemovalFieldThreshold = 3; | 55 const int kCommonNamePrefixRemovalFieldThreshold = 3; |
| 60 const int kMinCommonNamePrefixLength = 16; | 56 const int kMinCommonNamePrefixLength = 16; |
| 61 | 57 |
| 62 // Helper for |EncodeUploadRequest()| that creates a bit field corresponding to | 58 // Helper for |EncodeUploadRequest()| that creates a bit field corresponding to |
| 63 // |available_field_types| and returns the hex representation as a string. | 59 // |available_field_types| and returns the hex representation as a string. |
| (...skipping 630 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 694 | 690 |
| 695 void FormStructure::LogQualityMetrics( | 691 void FormStructure::LogQualityMetrics( |
| 696 const base::TimeTicks& load_time, | 692 const base::TimeTicks& load_time, |
| 697 const base::TimeTicks& interaction_time, | 693 const base::TimeTicks& interaction_time, |
| 698 const base::TimeTicks& submission_time, | 694 const base::TimeTicks& submission_time, |
| 699 rappor::RapporServiceImpl* rappor_service, | 695 rappor::RapporServiceImpl* rappor_service, |
| 700 AutofillMetrics::FormInteractionsUkmLogger* form_interactions_ukm_logger, | 696 AutofillMetrics::FormInteractionsUkmLogger* form_interactions_ukm_logger, |
| 701 bool did_show_suggestions, | 697 bool did_show_suggestions, |
| 702 bool observed_submission) const { | 698 bool observed_submission) const { |
| 703 size_t num_detected_field_types = 0; | 699 size_t num_detected_field_types = 0; |
| 704 size_t num_server_mismatches = 0; | |
| 705 size_t num_heuristic_mismatches = 0; | |
| 706 size_t num_edited_autofilled_fields = 0; | 700 size_t num_edited_autofilled_fields = 0; |
| 707 bool did_autofill_all_possible_fields = true; | 701 bool did_autofill_all_possible_fields = true; |
| 708 bool did_autofill_some_possible_fields = false; | 702 bool did_autofill_some_possible_fields = false; |
| 709 | 703 |
| 710 // Determine the correct suffix for the metric, depending on whether or | 704 // Determine the correct suffix for the metric, depending on whether or |
| 711 // not a submission was observed. | 705 // not a submission was observed. |
| 712 const AutofillMetrics::QualityMetricType metric_type = | 706 const AutofillMetrics::QualityMetricType metric_type = |
| 713 observed_submission ? AutofillMetrics::TYPE_SUBMISSION | 707 observed_submission ? AutofillMetrics::TYPE_SUBMISSION |
| 714 : AutofillMetrics::TYPE_NO_SUBMISSION; | 708 : AutofillMetrics::TYPE_NO_SUBMISSION; |
| 715 | 709 |
| (...skipping 18 matching lines...) Expand all Loading... |
| 734 // Aliases for the field types predicted by heuristics, server and overall. | 728 // Aliases for the field types predicted by heuristics, server and overall. |
| 735 ServerFieldType heuristic_type = | 729 ServerFieldType heuristic_type = |
| 736 AutofillType(field->heuristic_type()).GetStorableType(); | 730 AutofillType(field->heuristic_type()).GetStorableType(); |
| 737 ServerFieldType server_type = | 731 ServerFieldType server_type = |
| 738 AutofillType(field->server_type()).GetStorableType(); | 732 AutofillType(field->server_type()).GetStorableType(); |
| 739 ServerFieldType predicted_type = field->Type().GetStorableType(); | 733 ServerFieldType predicted_type = field->Type().GetStorableType(); |
| 740 | 734 |
| 741 const ServerFieldTypeSet& field_types = field->possible_types(); | 735 const ServerFieldTypeSet& field_types = field->possible_types(); |
| 742 DCHECK(!field_types.empty()); | 736 DCHECK(!field_types.empty()); |
| 743 | 737 |
| 744 // If the field data is empty, or unrecognized, log whether or not autofill | 738 AutofillMetrics::LogHeuristicPredictionQualityMetrics( |
| 745 // predicted that it would be populated with an autofillable data type. | 739 field_types, heuristic_type, metric_type); |
| 746 bool has_empty_data = field_types.count(EMPTY_TYPE) != 0; | 740 AutofillMetrics::LogServerPredictionQualityMetrics(field_types, server_type, |
| 747 bool has_unrecognized_data = field_types.count(UNKNOWN_TYPE) != 0; | 741 metric_type); |
| 748 if (has_empty_data || has_unrecognized_data) { | 742 AutofillMetrics::LogOverallPredictionQualityMetrics( |
| 749 AutofillMetrics::FieldTypeQualityMetric match_empty_or_unknown = | 743 field_types, predicted_type, metric_type); |
| 750 has_empty_data ? AutofillMetrics::TYPE_MATCH_EMPTY | 744 |
| 751 : AutofillMetrics::TYPE_MATCH_UNKNOWN; | 745 if (field_types.count(EMPTY_TYPE) || field_types.count(UNKNOWN_TYPE)) |
| 752 AutofillMetrics::FieldTypeQualityMetric mismatch_empty_or_unknown = | |
| 753 has_empty_data ? AutofillMetrics::TYPE_MISMATCH_EMPTY | |
| 754 : AutofillMetrics::TYPE_MISMATCH_UNKNOWN; | |
| 755 ServerFieldType field_type = has_empty_data ? EMPTY_TYPE : UNKNOWN_TYPE; | |
| 756 AutofillMetrics::LogHeuristicTypePrediction( | |
| 757 (heuristic_type == UNKNOWN_TYPE ? match_empty_or_unknown | |
| 758 : mismatch_empty_or_unknown), | |
| 759 field_type, metric_type); | |
| 760 AutofillMetrics::LogServerTypePrediction( | |
| 761 (server_type == NO_SERVER_DATA ? match_empty_or_unknown | |
| 762 : mismatch_empty_or_unknown), | |
| 763 field_type, metric_type); | |
| 764 AutofillMetrics::LogOverallTypePrediction( | |
| 765 (predicted_type == UNKNOWN_TYPE ? match_empty_or_unknown | |
| 766 : mismatch_empty_or_unknown), | |
| 767 field_type, metric_type); | |
| 768 continue; | 746 continue; |
| 769 } | |
| 770 | 747 |
| 771 ++num_detected_field_types; | 748 ++num_detected_field_types; |
| 772 if (field->is_autofilled) | 749 if (field->is_autofilled) |
| 773 did_autofill_some_possible_fields = true; | 750 did_autofill_some_possible_fields = true; |
| 774 else | 751 else |
| 775 did_autofill_all_possible_fields = false; | 752 did_autofill_all_possible_fields = false; |
| 776 | |
| 777 // Collapse field types that Chrome treats as identical, e.g. home and | |
| 778 // billing address fields. | |
| 779 ServerFieldTypeSet collapsed_field_types; | |
| 780 for (const auto& it : field_types) { | |
| 781 // Since we currently only support US phone numbers, the (city code + main | |
| 782 // digits) number is almost always identical to the whole phone number. | |
| 783 // TODO(isherman): Improve this logic once we add support for | |
| 784 // international numbers. | |
| 785 if (it == PHONE_HOME_CITY_AND_NUMBER) | |
| 786 collapsed_field_types.insert(PHONE_HOME_WHOLE_NUMBER); | |
| 787 else | |
| 788 collapsed_field_types.insert(AutofillType(it).GetStorableType()); | |
| 789 } | |
| 790 | |
| 791 // Capture the field's type, if it is unambiguous. | |
| 792 ServerFieldType field_type = UNKNOWN_TYPE; | |
| 793 if (collapsed_field_types.size() == 1) | |
| 794 field_type = *collapsed_field_types.begin(); | |
| 795 | |
| 796 // Log heuristic, server, and overall type quality metrics. | |
| 797 if (heuristic_type == UNKNOWN_TYPE) { | |
| 798 AutofillMetrics::LogHeuristicTypePrediction(AutofillMetrics::TYPE_UNKNOWN, | |
| 799 field_type, metric_type); | |
| 800 } else if (field_types.count(heuristic_type)) { | |
| 801 AutofillMetrics::LogHeuristicTypePrediction(AutofillMetrics::TYPE_MATCH, | |
| 802 field_type, metric_type); | |
| 803 } else { | |
| 804 ++num_heuristic_mismatches; | |
| 805 AutofillMetrics::LogHeuristicTypePrediction( | |
| 806 AutofillMetrics::TYPE_MISMATCH, field_type, metric_type); | |
| 807 } | |
| 808 | |
| 809 if (server_type == NO_SERVER_DATA) { | |
| 810 AutofillMetrics::LogServerTypePrediction(AutofillMetrics::TYPE_UNKNOWN, | |
| 811 field_type, metric_type); | |
| 812 } else if (field_types.count(server_type)) { | |
| 813 AutofillMetrics::LogServerTypePrediction(AutofillMetrics::TYPE_MATCH, | |
| 814 field_type, metric_type); | |
| 815 } else { | |
| 816 ++num_server_mismatches; | |
| 817 AutofillMetrics::LogServerTypePrediction(AutofillMetrics::TYPE_MISMATCH, | |
| 818 field_type, metric_type); | |
| 819 } | |
| 820 | |
| 821 if (predicted_type == UNKNOWN_TYPE) { | |
| 822 AutofillMetrics::LogOverallTypePrediction(AutofillMetrics::TYPE_UNKNOWN, | |
| 823 field_type, metric_type); | |
| 824 } else if (field_types.count(predicted_type)) { | |
| 825 AutofillMetrics::LogOverallTypePrediction(AutofillMetrics::TYPE_MATCH, | |
| 826 field_type, metric_type); | |
| 827 } else { | |
| 828 AutofillMetrics::LogOverallTypePrediction(AutofillMetrics::TYPE_MISMATCH, | |
| 829 field_type, metric_type); | |
| 830 } | |
| 831 } | 753 } |
| 832 | 754 |
| 833 AutofillMetrics::LogNumberOfEditedAutofilledFields( | 755 AutofillMetrics::LogNumberOfEditedAutofilledFields( |
| 834 num_edited_autofilled_fields, observed_submission); | 756 num_edited_autofilled_fields, observed_submission); |
| 835 | 757 |
| 836 // We log "submission" and duration metrics if we are here after observing a | 758 // We log "submission" and duration metrics if we are here after observing a |
| 837 // submission event. | 759 // submission event. |
| 838 if (observed_submission) { | 760 if (observed_submission) { |
| 839 AutofillMetrics::AutofillFormSubmittedState state; | 761 AutofillMetrics::AutofillFormSubmittedState state; |
| 840 if (num_detected_field_types < kRequiredFieldsForPredictionRoutines) { | 762 if (num_detected_field_types < kRequiredFieldsForPredictionRoutines) { |
| 841 state = AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA; | 763 state = AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA; |
| 842 } else { | 764 } else { |
| 843 if (did_autofill_all_possible_fields) { | 765 if (did_autofill_all_possible_fields) { |
| 844 state = AutofillMetrics::FILLABLE_FORM_AUTOFILLED_ALL; | 766 state = AutofillMetrics::FILLABLE_FORM_AUTOFILLED_ALL; |
| 845 } else if (did_autofill_some_possible_fields) { | 767 } else if (did_autofill_some_possible_fields) { |
| 846 state = AutofillMetrics::FILLABLE_FORM_AUTOFILLED_SOME; | 768 state = AutofillMetrics::FILLABLE_FORM_AUTOFILLED_SOME; |
| 847 } else if (!did_show_suggestions) { | 769 } else if (!did_show_suggestions) { |
| 848 state = AutofillMetrics:: | 770 state = AutofillMetrics:: |
| 849 FILLABLE_FORM_AUTOFILLED_NONE_DID_NOT_SHOW_SUGGESTIONS; | 771 FILLABLE_FORM_AUTOFILLED_NONE_DID_NOT_SHOW_SUGGESTIONS; |
| 850 } else { | 772 } else { |
| 851 state = | 773 state = |
| 852 AutofillMetrics::FILLABLE_FORM_AUTOFILLED_NONE_DID_SHOW_SUGGESTIONS; | 774 AutofillMetrics::FILLABLE_FORM_AUTOFILLED_NONE_DID_SHOW_SUGGESTIONS; |
| 853 } | 775 } |
| 854 | 776 |
| 855 // Log some RAPPOR metrics for problematic cases. | |
| 856 if (num_server_mismatches >= kNumberOfMismatchesThreshold) { | |
| 857 rappor::SampleDomainAndRegistryFromGURL( | |
| 858 rappor_service, "Autofill.HighNumberOfServerMismatches", | |
| 859 source_url_); | |
| 860 } | |
| 861 if (num_heuristic_mismatches >= kNumberOfMismatchesThreshold) { | |
| 862 rappor::SampleDomainAndRegistryFromGURL( | |
| 863 rappor_service, "Autofill.HighNumberOfHeuristicMismatches", | |
| 864 source_url_); | |
| 865 } | |
| 866 | |
| 867 // Unlike the other times, the |submission_time| should always be | 777 // Unlike the other times, the |submission_time| should always be |
| 868 // available. | 778 // available. |
| 869 DCHECK(!submission_time.is_null()); | 779 DCHECK(!submission_time.is_null()); |
| 870 | 780 |
| 871 // The |load_time| might be unset, in the case that the form was | 781 // The |load_time| might be unset, in the case that the form was |
| 872 // dynamically | 782 // dynamically |
| 873 // added to the DOM. | 783 // added to the DOM. |
| 874 if (!load_time.is_null()) { | 784 if (!load_time.is_null()) { |
| 875 // Submission should always chronologically follow form load. | 785 // Submission should always chronologically follow form load. |
| 876 DCHECK(submission_time > load_time); | 786 DCHECK(submission_time > load_time); |
| (...skipping 20 matching lines...) Expand all Loading... |
| 897 } | 807 } |
| 898 } | 808 } |
| 899 if (form_interactions_ukm_logger->url() != source_url()) | 809 if (form_interactions_ukm_logger->url() != source_url()) |
| 900 form_interactions_ukm_logger->UpdateSourceURL(source_url()); | 810 form_interactions_ukm_logger->UpdateSourceURL(source_url()); |
| 901 AutofillMetrics::LogAutofillFormSubmittedState( | 811 AutofillMetrics::LogAutofillFormSubmittedState( |
| 902 state, form_interactions_ukm_logger); | 812 state, form_interactions_ukm_logger); |
| 903 } | 813 } |
| 904 } | 814 } |
| 905 | 815 |
| 906 void FormStructure::LogQualityMetricsBasedOnAutocomplete() const { | 816 void FormStructure::LogQualityMetricsBasedOnAutocomplete() const { |
| 817 const AutofillMetrics::QualityMetricType metric_type = |
| 818 AutofillMetrics::TYPE_AUTOCOMPLETE_BASED; |
| 907 for (const auto& field : fields_) { | 819 for (const auto& field : fields_) { |
| 908 if (field->html_type() != HTML_TYPE_UNSPECIFIED && | 820 if (field->html_type() != HTML_TYPE_UNSPECIFIED && |
| 909 field->html_type() != HTML_TYPE_UNRECOGNIZED) { | 821 field->html_type() != HTML_TYPE_UNRECOGNIZED) { |
| 910 // The type inferred by the autocomplete attribute. | 822 // The type inferred by the autocomplete attribute. |
| 911 AutofillType type(field->html_type(), field->html_mode()); | 823 ServerFieldTypeSet actual_field_type_set{ |
| 912 ServerFieldType actual_field_type = type.GetStorableType(); | 824 AutofillType(field->html_type(), field->html_mode()) |
| 825 .GetStorableType()}; |
| 913 | 826 |
| 914 const AutofillMetrics::QualityMetricType metric_type = | 827 AutofillMetrics::LogHeuristicPredictionQualityMetrics( |
| 915 AutofillMetrics::TYPE_AUTOCOMPLETE_BASED; | 828 actual_field_type_set, field->heuristic_type(), metric_type); |
| 916 // Log the quality of our heuristics predictions. | 829 AutofillMetrics::LogServerPredictionQualityMetrics( |
| 917 if (field->heuristic_type() == UNKNOWN_TYPE) { | 830 actual_field_type_set, field->server_type(), metric_type); |
| 918 AutofillMetrics::LogHeuristicTypePrediction( | |
| 919 AutofillMetrics::TYPE_UNKNOWN, actual_field_type, metric_type); | |
| 920 } else if (field->heuristic_type() == actual_field_type) { | |
| 921 AutofillMetrics::LogHeuristicTypePrediction( | |
| 922 AutofillMetrics::TYPE_MATCH, actual_field_type, metric_type); | |
| 923 } else { | |
| 924 AutofillMetrics::LogHeuristicTypePrediction( | |
| 925 AutofillMetrics::TYPE_MISMATCH, actual_field_type, metric_type); | |
| 926 } | |
| 927 | |
| 928 // Log the quality of our server predictions. | |
| 929 if (field->server_type() == NO_SERVER_DATA) { | |
| 930 AutofillMetrics::LogServerTypePrediction( | |
| 931 AutofillMetrics::TYPE_UNKNOWN, actual_field_type, metric_type); | |
| 932 } else if (field->server_type() == actual_field_type) { | |
| 933 AutofillMetrics::LogServerTypePrediction( | |
| 934 AutofillMetrics::TYPE_MATCH, actual_field_type, metric_type); | |
| 935 } else { | |
| 936 AutofillMetrics::LogServerTypePrediction( | |
| 937 AutofillMetrics::TYPE_MISMATCH, actual_field_type, metric_type); | |
| 938 } | |
| 939 } | 831 } |
| 940 } | 832 } |
| 941 } | 833 } |
| 942 | 834 |
| 943 void FormStructure::ParseFieldTypesFromAutocompleteAttributes() { | 835 void FormStructure::ParseFieldTypesFromAutocompleteAttributes() { |
| 944 const std::string kDefaultSection = "-default"; | 836 const std::string kDefaultSection = "-default"; |
| 945 | 837 |
| 946 has_author_specified_types_ = false; | 838 has_author_specified_types_ = false; |
| 947 has_author_specified_sections_ = false; | 839 has_author_specified_sections_ = false; |
| 948 has_author_specified_upi_vpa_hint_ = false; | 840 has_author_specified_upi_vpa_hint_ = false; |
| (...skipping 434 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1383 filtered_strings[0].at(prefix_len)) { | 1275 filtered_strings[0].at(prefix_len)) { |
| 1384 // Mismatch found. | 1276 // Mismatch found. |
| 1385 return filtered_strings[i].substr(0, prefix_len); | 1277 return filtered_strings[i].substr(0, prefix_len); |
| 1386 } | 1278 } |
| 1387 } | 1279 } |
| 1388 } | 1280 } |
| 1389 return filtered_strings[0]; | 1281 return filtered_strings[0]; |
| 1390 } | 1282 } |
| 1391 | 1283 |
| 1392 } // namespace autofill | 1284 } // namespace autofill |
| OLD | NEW |