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

Side by Side Diff: chrome/browser/autofill/form_structure.cc

Issue 11198048: [Autofill] Update the autocomplete types implementation to match the current HTML spec. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix a test expectation Created 8 years, 2 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 (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 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/autofill/form_structure.h" 5 #include "chrome/browser/autofill/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/logging.h" 10 #include "base/logging.h"
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after
78 // Print all meaningfull bytes into a string. 78 // Print all meaningfull bytes into a string.
79 std::string data_presence; 79 std::string data_presence;
80 data_presence.reserve(data_end * 2 + 1); 80 data_presence.reserve(data_end * 2 + 1);
81 for (size_t i = 0; i < data_end; ++i) { 81 for (size_t i = 0; i < data_end; ++i) {
82 base::StringAppendF(&data_presence, "%02x", bit_field[i]); 82 base::StringAppendF(&data_presence, "%02x", bit_field[i]);
83 } 83 }
84 84
85 return data_presence; 85 return data_presence;
86 } 86 }
87 87
88 bool UpdateFromAutocompleteType(const string16& autocomplete_type, 88 // Returns |true| iff the |token| is a type hint for a contact field, as
89 AutofillField* field) { 89 // specified in the implementation section of http://is.gd/whatwg_autocomplete
90 if (autocomplete_type == ASCIIToUTF16("given-name")) { 90 // Note that "fax" and "pager" are intentionally ignored, as Chrome does not
91 field->set_heuristic_type(NAME_FIRST); 91 // support filling either type of information.
92 return true; 92 bool IsContactTypeHint(const string16& token) {
93 return
94 token == ASCIIToUTF16("home") ||
Evan Stade 2012/10/19 20:36:47 use LowerCaseEqualsASCII (here and many other plac
Evan Stade 2012/10/19 20:38:13 (meant to delete this comment)
95 token == ASCIIToUTF16("work") ||
96 token == ASCIIToUTF16("mobile") ||
97 token == ASCIIToUTF16("pager");
98 }
99
100 // Returns |true| iff the |token| is a type hint appropriate for a field of the
101 // given |field_type|, as specified in the implementation section of
102 // http://is.gd/whatwg_autocomplete
103 bool ContactTypeHintMatchesFieldType(const string16& token,
104 AutofillFieldType field_type) {
105 // The "home" and "work" type hints are only appropriate for email and phone
106 // number field types.
107 if (token == ASCIIToUTF16("home") || token == ASCIIToUTF16("work")) {
108 return field_type == EMAIL_ADDRESS ||
109 (field_type >= PHONE_HOME_NUMBER &&
Evan Stade 2012/10/19 20:36:47 why is it called PHONE_HOME if it applies to work
Ilya Sherman 2012/10/20 05:16:08 Historical reasons. We used to have more phone nu
110 field_type <= PHONE_HOME_WHOLE_NUMBER);
93 } 111 }
94 112
95 if (autocomplete_type == ASCIIToUTF16("middle-name")) { 113 // The "mobile" type hint is only appropriate for phone number field types.
96 field->set_heuristic_type(NAME_MIDDLE); 114 // Note that "fax" and "pager" are intentionally ignored, as Chrome does not
97 return true; 115 // support filling either type of information.
98 } 116 if (token == ASCIIToUTF16("mobile")) {
99 117 return field_type >= PHONE_HOME_NUMBER &&
100 if (autocomplete_type == ASCIIToUTF16("middle-initial")) { 118 field_type <= PHONE_HOME_WHOLE_NUMBER;
101 field->set_heuristic_type(NAME_MIDDLE_INITIAL);
102 return true;
103 }
104
105 if (autocomplete_type == ASCIIToUTF16("surname")) {
106 field->set_heuristic_type(NAME_LAST);
107 return true;
108 }
109
110 if (autocomplete_type == ASCIIToUTF16("full-name")) {
111 field->set_heuristic_type(NAME_FULL);
112 return true;
113 }
114
115 if (autocomplete_type == ASCIIToUTF16("street-address") ||
116 autocomplete_type == ASCIIToUTF16("address-line1")) {
117 field->set_heuristic_type(ADDRESS_HOME_LINE1);
118 return true;
119 }
120
121 if (autocomplete_type == ASCIIToUTF16("address-line2")) {
122 field->set_heuristic_type(ADDRESS_HOME_LINE2);
123 return true;
124 }
125
126 if (autocomplete_type == ASCIIToUTF16("locality") ||
127 autocomplete_type == ASCIIToUTF16("city")) {
128 field->set_heuristic_type(ADDRESS_HOME_CITY);
129 return true;
130 }
131
132 if (autocomplete_type == ASCIIToUTF16("administrative-area") ||
133 autocomplete_type == ASCIIToUTF16("state") ||
134 autocomplete_type == ASCIIToUTF16("province") ||
135 autocomplete_type == ASCIIToUTF16("region")) {
136 field->set_heuristic_type(ADDRESS_HOME_STATE);
137 return true;
138 }
139
140 if (autocomplete_type == ASCIIToUTF16("postal-code")) {
141 field->set_heuristic_type(ADDRESS_HOME_ZIP);
142 return true;
143 }
144
145 if (autocomplete_type == ASCIIToUTF16("country")) {
146 field->set_heuristic_type(ADDRESS_HOME_COUNTRY);
147 return true;
148 }
149
150 if (autocomplete_type == ASCIIToUTF16("organization")) {
151 field->set_heuristic_type(COMPANY_NAME);
152 return true;
153 }
154
155 if (autocomplete_type == ASCIIToUTF16("email")) {
156 field->set_heuristic_type(EMAIL_ADDRESS);
157 return true;
158 }
159
160 if (autocomplete_type == ASCIIToUTF16("phone-full")) {
161 field->set_heuristic_type(PHONE_HOME_WHOLE_NUMBER);
162 return true;
163 }
164
165 if (autocomplete_type == ASCIIToUTF16("phone-country-code")) {
166 field->set_heuristic_type(PHONE_HOME_COUNTRY_CODE);
167 return true;
168 }
169
170 if (autocomplete_type == ASCIIToUTF16("phone-national")) {
171 field->set_heuristic_type(PHONE_HOME_CITY_AND_NUMBER);
172 return true;
173 }
174
175 if (autocomplete_type == ASCIIToUTF16("phone-area-code")) {
176 field->set_heuristic_type(PHONE_HOME_CITY_CODE);
177 return true;
178 }
179
180 if (autocomplete_type == ASCIIToUTF16("phone-local")) {
181 field->set_heuristic_type(PHONE_HOME_NUMBER);
182 return true;
183 }
184
185 if (autocomplete_type == ASCIIToUTF16("phone-local-prefix")) {
186 field->set_heuristic_type(PHONE_HOME_NUMBER);
187 field->set_phone_part(AutofillField::PHONE_PREFIX);
188 return true;
189 }
190
191 if (autocomplete_type == ASCIIToUTF16("phone-local-suffix")) {
192 field->set_heuristic_type(PHONE_HOME_NUMBER);
193 field->set_phone_part(AutofillField::PHONE_SUFFIX);
194 return true;
195 }
196
197 if (autocomplete_type == ASCIIToUTF16("cc-full-name")) {
198 field->set_heuristic_type(CREDIT_CARD_NAME);
199 return true;
200 }
201
202 if (autocomplete_type == ASCIIToUTF16("cc-number")) {
203 field->set_heuristic_type(CREDIT_CARD_NUMBER);
204 return true;
205 }
206
207 if (autocomplete_type == ASCIIToUTF16("cc-exp-month")) {
208 field->set_heuristic_type(CREDIT_CARD_EXP_MONTH);
209 return true;
210 }
211
212 if (autocomplete_type == ASCIIToUTF16("cc-exp-year")) {
213 if (field->max_length == 2)
214 field->set_heuristic_type(CREDIT_CARD_EXP_2_DIGIT_YEAR);
215 else
216 field->set_heuristic_type(CREDIT_CARD_EXP_4_DIGIT_YEAR);
217 return true;
218 }
219
220 if (autocomplete_type == ASCIIToUTF16("cc-exp")) {
221 if (field->max_length == 5)
222 field->set_heuristic_type(CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR);
223 else
224 field->set_heuristic_type(CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR);
225 return true;
226 } 119 }
227 120
228 return false; 121 return false;
229 } 122 }
230 123
124 // Returns the Chrome Autofill-supported field type corresponding to the given
125 // |autocomplete_type|, if there is one, in the context of the given |field|.
126 // Chrome Autofill supports a subset of the field types listed at
127 // http://is.gd/whatwg_autocomplete
128 AutofillFieldType FieldTypeFromAutocompleteType(
129 const string16& autocomplete_type,
130 const AutofillField& field) {
131 if (autocomplete_type == ASCIIToUTF16("name"))
132 return NAME_FULL;
133
134 if (autocomplete_type == ASCIIToUTF16("given-name"))
135 return NAME_FIRST;
136
137 if (autocomplete_type == ASCIIToUTF16("additional-name")) {
138 if (field.max_length == 1)
139 return NAME_MIDDLE_INITIAL;
140 else
141 return NAME_MIDDLE;
142 }
143
144 if (autocomplete_type == ASCIIToUTF16("family-name"))
145 return NAME_LAST;
146
147 if (autocomplete_type == ASCIIToUTF16("honorific-suffix"))
148 return NAME_SUFFIX;
149
150 if (autocomplete_type == ASCIIToUTF16("organization"))
151 return COMPANY_NAME;
152
153 if (autocomplete_type == ASCIIToUTF16("street-address") ||
154 autocomplete_type == ASCIIToUTF16("address-line1"))
155 return ADDRESS_HOME_LINE1;
156
157 if (autocomplete_type == ASCIIToUTF16("address-line2"))
158 return ADDRESS_HOME_LINE2;
159
160 if (autocomplete_type == ASCIIToUTF16("locality"))
161 return ADDRESS_HOME_CITY;
162
163 if (autocomplete_type == ASCIIToUTF16("region"))
164 return ADDRESS_HOME_STATE;
165
166 if (autocomplete_type == ASCIIToUTF16("country"))
167 return ADDRESS_HOME_COUNTRY;
168
169 if (autocomplete_type == ASCIIToUTF16("postal-code"))
170 return ADDRESS_HOME_ZIP;
171
172 if (autocomplete_type == ASCIIToUTF16("cc-name"))
173 return CREDIT_CARD_NAME;
174
175 if (autocomplete_type == ASCIIToUTF16("cc-number"))
176 return CREDIT_CARD_NUMBER;
177
178 if (autocomplete_type == ASCIIToUTF16("cc-exp")) {
179 if (field.max_length == 5)
180 return CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR;
181 else
182 return CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR;
183 }
184
185 if (autocomplete_type == ASCIIToUTF16("cc-exp-month"))
186 return CREDIT_CARD_EXP_MONTH;
187
188 if (autocomplete_type == ASCIIToUTF16("cc-exp-year")) {
189 if (field.max_length == 2)
190 return CREDIT_CARD_EXP_2_DIGIT_YEAR;
191 else
192 return CREDIT_CARD_EXP_4_DIGIT_YEAR;
193 }
194
195 if (autocomplete_type == ASCIIToUTF16("tel"))
196 return PHONE_HOME_WHOLE_NUMBER;
197
198 if (autocomplete_type == ASCIIToUTF16("tel-country-code"))
199 return PHONE_HOME_COUNTRY_CODE;
200
201 if (autocomplete_type == ASCIIToUTF16("tel-national"))
202 return PHONE_HOME_CITY_AND_NUMBER;
203
204 if (autocomplete_type == ASCIIToUTF16("tel-area-code"))
205 return PHONE_HOME_CITY_CODE;
206
207 if (autocomplete_type == ASCIIToUTF16("tel-local"))
208 return PHONE_HOME_NUMBER;
209
210 if (autocomplete_type == ASCIIToUTF16("tel-local-prefix"))
211 return PHONE_HOME_NUMBER;
212
213 if (autocomplete_type == ASCIIToUTF16("tel-local-suffix"))
214 return PHONE_HOME_NUMBER;
215
216 if (autocomplete_type == ASCIIToUTF16("email"))
217 return EMAIL_ADDRESS;
218
219 return UNKNOWN_TYPE;
220 }
221
231 } // namespace 222 } // namespace
232 223
233 FormStructure::FormStructure(const FormData& form) 224 FormStructure::FormStructure(const FormData& form)
234 : form_name_(form.name), 225 : form_name_(form.name),
235 source_url_(form.origin), 226 source_url_(form.origin),
236 target_url_(form.action), 227 target_url_(form.action),
237 autofill_count_(0), 228 autofill_count_(0),
238 upload_required_(USE_UPLOAD_RATES), 229 upload_required_(USE_UPLOAD_RATES),
239 server_experiment_id_("no server response"), 230 server_experiment_id_("no server response"),
240 has_author_specified_types_(false) { 231 has_author_specified_types_(false) {
(...skipping 25 matching lines...) Expand all
266 } else { 257 } else {
267 // Either the method is 'get', or we don't know. In this case we default 258 // Either the method is 'get', or we don't know. In this case we default
268 // to GET. 259 // to GET.
269 method_ = GET; 260 method_ = GET;
270 } 261 }
271 } 262 }
272 263
273 FormStructure::~FormStructure() {} 264 FormStructure::~FormStructure() {}
274 265
275 void FormStructure::DetermineHeuristicTypes() { 266 void FormStructure::DetermineHeuristicTypes() {
276 // First, try to detect field types based on the fields' |autocompletetype| 267 // First, try to detect field types based on each field's |autocomplete|
277 // attributes. If there is at least one form field with this attribute, don't 268 // attribute value. If there is at least one form field that specifies an
278 // try to apply other heuristics to match fields in this form. 269 // autocomplete type hint, don't try to apply other heuristics to match fields
270 // in this form.
279 bool has_author_specified_sections; 271 bool has_author_specified_sections;
280 ParseAutocompletetypeAttributes(&has_author_specified_types_, 272 ParseFieldTypesFromAutocompleteAttributes(&has_author_specified_types_,
281 &has_author_specified_sections); 273 &has_author_specified_sections);
282 274
283 if (!has_author_specified_types_) { 275 if (!has_author_specified_types_) {
284 FieldTypeMap field_type_map; 276 FieldTypeMap field_type_map;
285 FormField::ParseFormFields(fields_.get(), &field_type_map); 277 FormField::ParseFormFields(fields_.get(), &field_type_map);
286 for (size_t index = 0; index < field_count(); index++) { 278 for (size_t index = 0; index < field_count(); index++) {
287 AutofillField* field = fields_[index]; 279 AutofillField* field = fields_[index];
288 FieldTypeMap::iterator iter = field_type_map.find(field->unique_name()); 280 FieldTypeMap::iterator iter = field_type_map.find(field->unique_name());
289 if (iter != field_type_map.end()) 281 if (iter != field_type_map.end())
290 field->set_heuristic_type(iter->second); 282 field->set_heuristic_type(iter->second);
291 } 283 }
(...skipping 579 matching lines...) Expand 10 before | Expand all | Expand 10 after
871 buzz::XmlElement *field_element = new buzz::XmlElement( 863 buzz::XmlElement *field_element = new buzz::XmlElement(
872 buzz::QName(kXMLElementField)); 864 buzz::QName(kXMLElementField));
873 field_element->SetAttr(buzz::QName(kAttributeSignature), 865 field_element->SetAttr(buzz::QName(kAttributeSignature),
874 field->FieldSignature()); 866 field->FieldSignature());
875 encompassing_xml_element->AddElement(field_element); 867 encompassing_xml_element->AddElement(field_element);
876 } 868 }
877 } 869 }
878 return true; 870 return true;
879 } 871 }
880 872
881 void FormStructure::ParseAutocompletetypeAttributes(bool* found_attribute, 873 void FormStructure::ParseFieldTypesFromAutocompleteAttributes(
882 bool* found_sections) { 874 bool* found_types,
883 *found_attribute = false; 875 bool* found_sections) {
876 *found_types = false;
884 *found_sections = false; 877 *found_sections = false;
885 for (std::vector<AutofillField*>::iterator field = fields_.begin(); 878 for (std::vector<AutofillField*>::iterator field = fields_.begin();
886 field != fields_.end(); ++field) { 879 field != fields_.end(); ++field) {
Evan Stade 2012/10/19 20:36:47 nit: can you call field iter, and then do Autofil
Ilya Sherman 2012/10/20 05:16:08 Done.
887 if ((*field)->autocomplete_type.empty()) 880 // Canonicalize the attribute value by trimming whitespace and converting to
881 // lowercase ASCII.
882 string16 autocomplete_attribute = (*field)->autocomplete_attribute;
Evan Stade 2012/10/19 20:36:47 see my note elsewhere that the field's attribute s
Ilya Sherman 2012/10/20 05:16:08 Done.
883 TrimWhitespace(autocomplete_attribute, TRIM_ALL, &autocomplete_attribute);
884 autocomplete_attribute = StringToLowerASCII(autocomplete_attribute);
885
886 // The autocomplete attribute is overloaded: it can specify either a field
887 // type hint or whether autocomplete should be enabled at all. Ignore the
888 // latter type of attribute value.
889 if (autocomplete_attribute.empty() ||
890 autocomplete_attribute == ASCIIToUTF16("on") ||
891 autocomplete_attribute == ASCIIToUTF16("off")) {
892 continue;
893 }
894
895 // Any other value, even it is invalid, is considered to be a type hint.
896 // This allows a website's author to specify an attribute like
897 // autocomplete="other" on a field to disable all Autofill heuristics for
Evan Stade 2012/10/19 20:36:47 don't follow this logic. Are you saying that autoc
Ilya Sherman 2012/10/20 05:16:08 The two are different. autocomplete="off" means d
898 // the form.
899 *found_types = true;
900
901 // Tokenize the attribute value. Per the spec, the tokens are parsed in
902 // reverse order.
903 std::vector<string16> tokens;
Evan Stade 2012/10/19 20:36:47 likewise, should be std::string
Ilya Sherman 2012/10/20 05:16:08 Done.
904 Tokenize(autocomplete_attribute, ASCIIToUTF16(" "), &tokens);
Evan Stade 2012/10/19 20:59:44 should this include other space characters, or wer
Ilya Sherman 2012/10/20 05:16:08 Done + added test coverage (to the advanced.html t
905
906 // The final token must be the field type.
907 // If it is not one of the known types, abort.
908 DCHECK(!tokens.empty());
909 string16 field_type_token = tokens.back();
910 tokens.pop_back();
911 AutofillFieldType field_type =
912 FieldTypeFromAutocompleteType(field_type_token, **field);
913 if (field_type == UNKNOWN_TYPE)
888 continue; 914 continue;
889 915
890 *found_attribute = true; 916 // The preceding token, if any, may be a type hint.
891 std::vector<string16> types; 917 if (!tokens.empty() && IsContactTypeHint(tokens.back())) {
892 Tokenize((*field)->autocomplete_type, ASCIIToUTF16(" "), &types); 918 // If it is, it must match the field type; otherwise, abort.
919 // Note that an invalid token invalidates the entire attribute value, even
920 // if the other tokens are valid.
921 if (!ContactTypeHintMatchesFieldType(tokens.back(), field_type))
922 continue;
893 923
894 // Look for a named section. 924 // Chrome Autofill ignores these type hints.
895 const string16 kSectionPrefix = ASCIIToUTF16("section-"); 925 tokens.pop_back();
896 if (!types.empty() && StartsWith(types.front(), kSectionPrefix, true)) {
897 *found_sections = true;
898 (*field)->set_section(types.front().substr(kSectionPrefix.size()));
899 } 926 }
900 927
901 // Look for specified types. 928 // The preceding token, if any, may be a fixed string that is either
902 for (std::vector<string16>::const_iterator type = types.begin(); 929 // "shipping" or "billing". Chrome Autofill treats these as implicit
903 type != types.end(); ++type) { 930 // section name suffixes.
904 if (UpdateFromAutocompleteType(*type, *field)) 931 if (!tokens.empty() &&
905 break; 932 (tokens.back() == ASCIIToUTF16("shipping") ||
933 tokens.back() == ASCIIToUTF16("billing"))) {
934 *found_sections = true;
935 (*field)->set_section(ASCIIToUTF16("-") + tokens.back());
936 tokens.pop_back();
937 } else {
938 // To prevent potential section name collisions, add a default suffix for
939 // other fields. Without this, 'autocomplete' attribute values
940 // "section--shipping street-address" and "shipping street-address" would
Evan Stade 2012/10/19 20:36:47 can you help me understand this section? It seems
Evan Stade 2012/10/19 20:57:27 Dan explained this to me. So is it intentional tha
Ilya Sherman 2012/10/20 05:16:08 Yes, it's intentional. We fill differently named
941 // be parsed identically.
942 (*field)->set_section(ASCIIToUTF16("-default"));
906 } 943 }
944
945 // The preceding token, if any, may be a named section.
946 const string16 kSectionPrefix = ASCIIToUTF16("section-");
947 if (!tokens.empty() && StartsWith(tokens.back(), kSectionPrefix, true)) {
948 *found_sections = true;
949 // Prepend this section name to the suffix set in the preceding block.
950 (*field)->set_section(
951 tokens.back().substr(kSectionPrefix.size()) + (*field)->section());
Evan Stade 2012/10/19 20:57:27 do you need to check for "section- " (i.e. with no
Ilya Sherman 2012/10/20 05:16:08 It's covered in the tests: we treat this as equiva
952 tokens.pop_back();
953 }
954
955 // No other tokens are allowed. If there are any remaining, abort.
Evan Stade 2012/10/19 20:36:47 if you are aborting, and you just set found_sectio
Ilya Sherman 2012/10/20 05:16:08 Good call. Fixed, and added test coverage. The f
956 if (!tokens.empty())
957 continue;
958
959 // No errors encountered while parsing!
960 // Update the |field|'s type based on what was parsed from the attribute.
961 (*field)->set_heuristic_type(field_type);
962 if (field_type_token == ASCIIToUTF16("tel-local-prefix"))
963 (*field)->set_phone_part(AutofillField::PHONE_PREFIX);
964 else if (field_type_token == ASCIIToUTF16("tel-local-suffix"))
965 (*field)->set_phone_part(AutofillField::PHONE_SUFFIX);
907 } 966 }
908 } 967 }
909 968
910 void FormStructure::IdentifySections(bool has_author_specified_sections) { 969 void FormStructure::IdentifySections(bool has_author_specified_sections) {
911 if (fields_.empty()) 970 if (fields_.empty())
912 return; 971 return;
913 972
914 if (!has_author_specified_sections) { 973 if (!has_author_specified_sections) {
915 // Name sections after the first field in the section. 974 // Name sections after the first field in the section.
916 string16 current_section = fields_.front()->unique_name(); 975 string16 current_section = fields_.front()->unique_name();
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
962 for (std::vector<AutofillField*>::iterator field = fields_.begin(); 1021 for (std::vector<AutofillField*>::iterator field = fields_.begin();
963 field != fields_.end(); ++field) { 1022 field != fields_.end(); ++field) {
964 AutofillType::FieldTypeGroup field_type_group = 1023 AutofillType::FieldTypeGroup field_type_group =
965 AutofillType((*field)->type()).group(); 1024 AutofillType((*field)->type()).group();
966 if (field_type_group == AutofillType::CREDIT_CARD) 1025 if (field_type_group == AutofillType::CREDIT_CARD)
967 (*field)->set_section((*field)->section() + ASCIIToUTF16("-cc")); 1026 (*field)->set_section((*field)->section() + ASCIIToUTF16("-cc"));
968 else 1027 else
969 (*field)->set_section((*field)->section() + ASCIIToUTF16("-default")); 1028 (*field)->set_section((*field)->section() + ASCIIToUTF16("-default"));
970 } 1029 }
971 } 1030 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698