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

Side by Side Diff: components/autofill/ios/browser/resources/autofill_controller.js

Issue 1007813002: [iOS] Upstream autofill component code (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Added dconnelly to OWNERS Created 5 years, 9 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
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 // Installs Autofill management functions on the |__gCrWeb| object.
6 //
7 // It scans the DOM, extracting and storing forms and returns a JSON string
8 // representing an array of objects, each of which represents an Autofill form
9 // with information about a form to be filled and/or submitted and it can be
10 // translated to struct FormData
11 // (chromium/src/components/autofill/common/form_data.h) for further processing.
12
13 /**
14 * Namespace for this file. It depends on |__gCrWeb| having already been
15 * injected.
16 */
17 __gCrWeb['autofill'] = {};
18
19 /**
20 * The maximum length allowed for form data.
21 *
22 * This variable is from
23 * chromium/src/components/autofill/renderer/form_autofill_util.cc
24 */
25 __gCrWeb.autofill.MAX_DATA_LENGTH = 1024;
26
27 /**
28 * The maximum number of form fields we are willing to parse, due to
29 * computational costs. Several examples of forms with lots of fields that are
30 * not relevant to Autofill: (1) the Netflix queue; (2) the Amazon wishlist;
31 * (3) router configuration pages; and (4) other configuration pages, e.g. for
32 * Google code project settings.
33 *
34 * This variable is from
35 * chromium/src/components/autofill/renderer/form_autofill_util.h
36 */
37 __gCrWeb.autofill.MAX_PARSEABE_FIELDS = 100;
38
39 /**
40 * A bit field mask for form or form element requirements for requirement
41 * none.
42 *
43 * This variable is from enum RequirementsMask in
44 * chromium/src/components/autofill/renderer/form_autofill_util.h
45 */
46 __gCrWeb.autofill.REQUIREMENTS_MASK_NONE = 0;
47
48 /**
49 * A bit field mask for form or form element requirements for requirement
50 * autocomplete != off.
51 *
52 * This variable is from enum RequirementsMask in
53 * chromium/src/components/autofill/renderer/form_autofill_util.h
54 */
55 __gCrWeb.autofill.REQUIREMENTS_MASK_REQUIRE_AUTOCOMPLETE = 1;
56
57 /**
58 * A bit field mask to extract data from WebFormControlElement for
59 * extracting none value.
60 *
61 * This variable is from enum ExtractMask in
62 * chromium/src/components/autofill/renderer/form_autofill_util.h
63 */
64 __gCrWeb.autofill.EXTRACT_MASK_NONE = 0;
65
66 /**
67 * A bit field mask to extract data from WebFormControlElement for
68 * extracting value from WebFormControlElement.
69 *
70 * This variable is from enum ExtractMask in
71 * chromium/src/components/autofill/renderer/form_autofill_util.h
72 */
73 __gCrWeb.autofill.EXTRACT_MASK_VALUE = 1 << 0;
74
75 /**
76 * A bit field mask to extract data from WebFormControlElement for
77 * extracting option text from WebFormSelectElement. Only valid when
78 * EXTRACT_MASK_VALUE is set. This is used for form submission where human
79 * readable value is captured.
80 *
81 * This variable is from enum ExtractMask in
82 * chromium/src/components/autofill/renderer/form_autofill_util.h
83 */
84 __gCrWeb.autofill.EXTRACT_MASK_OPTION_TEXT = 1 << 1;
85
86 /**
87 * A bit field mask to extract data from WebFormControlElement for
88 * extracting options from WebFormControlElement.
89 *
90 * This variable is from enum ExtractMask in
91 * chromium/src/components/autofill/renderer/form_autofill_util.h
92 */
93 __gCrWeb.autofill.EXTRACT_MASK_OPTIONS = 1 << 2;
94
95 /**
96 * The last element that was autofilled.
97 */
98 __gCrWeb.autofill.lastAutoFilledElement = null;
99
100 /**
101 * Scans DOM and returns a JSON string representation of forms and form
102 * extraction results.
103 * @param {int} requiredFields The minimum number of fields forms must have to
104 * be extracted.
105 * @param {int} requirements The requirements mask for forms, e.g. autocomplete
106 * attribute state.
107 * @return {string} A JSON encoded object with object['forms'] containing the
108 * forms data and object['has_more_forms'] indicating if there are more
109 * forms to extract.
110 */
111 __gCrWeb.autofill['extractForms'] = function(requiredFields, requirements) {
112 var forms = [];
113 // Protect against custom implementation of Array.toJSON in host pages.
114 forms.toJSON = null;
115 // TODO(chenyu): check if any preparation is needed for information such as
116 // user_submitted or the one added in core.js is sufficient.
117 var hasMoreForms = __gCrWeb.autofill.extractFormsAndFormElements(
118 window,
119 requiredFields,
120 requirements,
121 forms);
122 var results = new __gCrWeb.common.JSONSafeObject;
123 results['has_more_forms'] = hasMoreForms;
124 results['forms'] = forms;
125 return __gCrWeb.stringify(results);
126 };
127
128 /**
129 * Fills data into the active form field.
130 *
131 * @param {Object} data The data to fill in.
132 */
133 __gCrWeb.autofill['fillActiveFormField'] = function(data) {
134 var activeElement = document.activeElement;
135 if (data['name'] !== __gCrWeb['common'].nameForAutofill(activeElement)) {
136 return;
137 }
138 __gCrWeb.autofill.lastAutoFilledElement = activeElement;
139 __gCrWeb.autofill.fillFormField(data, activeElement);
140 };
141
142 /**
143 * Fills a number of fields in the same named form.
144 *
145 * @param {Object} data The data to fill in.
146 */
147 __gCrWeb.autofill['fillForm'] = function(data) {
148 var form = __gCrWeb.common.getFormElementFromIdentifier(data.formName);
149 var controlElements = __gCrWeb.common.getFormControlElements(form);
150 for (var i = 0; i < controlElements.length; ++i) {
151 var element = controlElements[i];
152 if (!__gCrWeb.autofill.isAutofillableElement(element)) {
153 continue;
154 }
155 var value = data.fields[__gCrWeb['common'].nameForAutofill(element)];
156 if (value) {
157 element.value = value;
158 }
159 }
160 };
161
162 /**
163 * Dispatch an autocomplete event to the named form.
164 *
165 * @param {String} name Identifier for form element (from getFormIdentifier).
166 */
167 __gCrWeb.autofill['dispatchAutocompleteEvent'] = function(name) {
168 var formElement = __gCrWeb.common.getFormElementFromIdentifier(name);
169 var event = new CustomEvent('autocomplete', {});
170 formElement.dispatchEvent(event);
171 };
172
173 /**
174 * Dispatch an autocomplete error event to the named form.
175 *
176 * @param {String} name Identifier for form element (from getFormIdentifier).
177 * @param {String} reason Reason to supply in event.reason; one of 'cancel',
178 * 'invalid' or 'disabled'; see requestAutocomplete spec.
179 */
180 __gCrWeb.autofill['dispatchAutocompleteErrorEvent'] = function(name, reason) {
181 var formElement = __gCrWeb.common.getFormElementFromIdentifier(name);
182 var event = new CustomEvent('autocompleteerror', {});
183 event.reason = reason;
184 formElement.dispatchEvent(event);
185 };
186
187 /**
188 * Dispatch an autocomplete error event to the named form.
189 *
190 * @param {String} name Identifier for form element (from getFormIdentifier).
191 * @param {String} reason Reason to supply in event.reason; one of 'cancel',
192 * 'invalid' or 'disabled'; see requestAutocomplete spec.
193 */
194 __gCrWeb.autofill['dispatchAutocompleteErrorEvent'] = function(name, reason) {
195 var formElement = __gCrWeb.common.getFormElementFromIdentifier(name);
196 var event = new CustomEvent('autocompleteerror', {});
197 event.reason = reason;
198 formElement.dispatchEvent(event);
199 };
200
201 /**
202 * Scans the DOM in |frame| extracting and storing forms. Fills |forms| with
203 * extracted forms. Returns true if there are unextracted forms due to
204 * |minimumRequiredFields| limit, else false.
205 *
206 * This method is based on the logic in method
207 *
208 * bool FormCache::ExtractFormsAndFormElements(
209 * const WebFrame& frame,
210 * size_t minimum_required_fields,
211 * std::vector<FormData>* forms,
212 * std::vector<WebFormElement>* web_form_elements)
213 *
214 * in chromium/src/components/autofill/renderer/form_cache.cc.
215 *
216 * The difference is in this implementation, no form elements are returned in
217 * |web_form_elements|, and cache is not considered. Initial values of select
218 * and checkable elements are not recorded at the momemt.
219 *
220 * @param {Object} frame A window or a frame containing forms from which the
221 * data will be extracted.
222 * @param {int} minimumRequiredFields The minimum number of fields a form should
223 * contain for autofill.
224 * @param {int} requirements The requirements mask for forms, e.g. autocomplete
225 * attribute state.
226 * @param {Object} forms Forms that will be filled in data of forms in frame.
227 * @return {boolean} Whether there are unextracted forms due to
228 * |minimumRequiredFields| limit.
229 */
230 __gCrWeb.autofill.extractFormsAndFormElements = function(
231 frame, minimumRequiredFields, requirements, forms) {
232 if (!frame) {
233 return false;
234 }
235 var doc = frame.document;
236 if (!doc) {
237 return false;
238 }
239
240 var webForms = doc.forms;
241
242 var numFieldsSeen = 0;
243 var hasSkippedForms = false;
244 for (var formIndex = 0; formIndex < webForms.length; ++formIndex) {
245 var formElement = webForms[formIndex];
246 var controlElements = [];
247 __gCrWeb.autofill.extractAutofillableElements(
248 formElement, requirements, controlElements);
249 var numEditableElements = 0;
250 for (var elementIndex = 0; elementIndex < controlElements.length;
251 ++elementIndex) {
252 var element = controlElements[elementIndex];
253 if (!__gCrWeb.autofill.isCheckableElement(element)) {
254 ++numEditableElements;
255 }
256 }
257
258 // To avoid overly expensive computation, we impose a minimum number of
259 // allowable fields. The corresponding maximum number of allowable
260 // fields is imposed by webFormElementToFormData().
261 if (numEditableElements < minimumRequiredFields) {
262 hasSkippedForms = true;
263 continue;
264 }
265
266 var extractMask = __gCrWeb.autofill.EXTRACT_MASK_VALUE |
267 __gCrWeb.autofill.EXTRACT_MASK_OPTIONS;
268 var form = new __gCrWeb['common'].JSONSafeObject;
269 if (!__gCrWeb.autofill.webFormElementToFormData(
270 frame, formElement, null, requirements, extractMask, form)) {
271 continue;
272 }
273 numFieldsSeen += form['fields'].length;
274 if (numFieldsSeen > __gCrWeb.autofill.MAX_PARSEABE_FIELDS) {
275 break;
276 }
277
278 if (form.fields.length >= minimumRequiredFields) {
279 forms.push(form);
280 } else {
281 hasSkippedForms = true;
282 }
283 }
284
285 // Recursively invoke for all frames/iframes.
286 var frames = frame.frames;
287 for (var i = 0; i < frames.length; i++) {
288 var hasSkippedInframe = __gCrWeb.autofill.extractFormsAndFormElements(
289 frames[i], minimumRequiredFields, requirements, forms);
290 hasSkippedForms = hasSkippedForms || hasSkippedInframe;
291 }
292 return hasSkippedForms;
293 };
294
295 /**
296 * Fills |form| with the form data object corresponding to the |formElement|.
297 * If |field| is non-NULL, also fills |field| with the FormField object
298 * corresponding to the |formControlElement|.
299 * |extract_mask| controls what data is extracted.
300 * Returns true if |form| is filled out; it's possible that the |formElement|
301 * won't meet the |requirements|. Also returns false if there are no fields or
302 * too many fields in the |form|.
303 *
304 * It is based on the logic in
305 * bool WebFormElementToFormData(
306 * const blink::WebFormElement& form_element,
307 * const blink::WebFormControlElement& form_control_element,
308 * RequirementsMask requirements,
309 * ExtractMask extract_mask,
310 * FormData* form,
311 * FormFieldData* field)
312 * in chromium/src/components/autofill/renderer/form_autofill_util.cc
313 *
314 * @param {Element} frame The frame where the formElement is in.
315 * @param {Element} formElement The form element that will be processed.
316 * @param {Element} formControlElement A control element in formElment, the
317 * FormField of which will be returned in field.
318 * @param {int} requirements The requirement on formElement autocompletion.
319 * @param {int} extractMask Mask controls what data is extracted from
320 * formElement.
321 * @param {Object} form Form to fill in the FormData information of formElement.
322 * @param {Object} field Field to fill in the form field information of
323 * formControlElement.
324 * @return {boolean} Whether there are fields and not too many fields in the
325 * form.
326 */
327 __gCrWeb.autofill.webFormElementToFormData = function(
328 frame, formElement, formControlElement, requirements, extractMask, form,
329 field) {
330 if (!frame) {
331 return false;
332 }
333
334 if ((requirements &
335 __gCrWeb.autofill.REQUIREMENTS_MASK_REQUIRE_AUTOCOMPLETE) &&
336 !__gCrWeb['common'].autoComplete(formElement)) {
337 return false;
338 }
339
340 form['name'] = __gCrWeb.common.getFormIdentifier(formElement);
341 var method = formElement.getAttribute('method');
342 if (method) {
343 form['method'] = method;
344 }
345 form['origin'] = __gCrWeb.common.removeQueryAndReferenceFromURL(
346 frame.location.href);
347 form['action'] = __gCrWeb.common.absoluteURL(
348 frame.document,
349 formElement.getAttribute('action'));
350 // TODO(chenyu): fill form['userSubmitted'] (Issue 231264).
351
352 // Note different from form_autofill_utill.cc version of this method, which
353 // computes |form.action| using document.completeURL(form_element.action())
354 // and falls back to formElement.action() if the computed action is invalid,
355 // here the action returned by |__gCrWeb.common.absoluteURL| is always
356 // valid, which is computed by creating a <a> element, and we don't check if
357 // the action is valid.
358
359 // A map from a FormFieldData's name to the FormFieldData itself.
360 var nameMap = {};
361
362 // The extracted FormFields.
363 var formFields = [];
364
365 var controlElements = __gCrWeb['common'].getFormControlElements(formElement);
366
367 // A vector of bools that indicate whether each element in |controlElements|
368 // meets the requirements and thus will be in the resulting |form|.
369 var fieldsExtracted = [];
370
371 for (var i = 0; i < controlElements.length; ++i) {
372 fieldsExtracted[i] = false;
373
374 var controlElement = controlElements[i];
375 if (!__gCrWeb.autofill.isAutofillableElement(controlElement)) {
376 continue;
377 }
378
379 if (requirements &
380 __gCrWeb.autofill.REQUIREMENTS_MASK_REQUIRE_AUTOCOMPLETE &&
381 __gCrWeb.autofill.isAutofillableInputElement(controlElement) &&
382 !__gCrWeb.autofill.satisfiesRequireAutocomplete(
383 controlElement, false)) {
384 continue;
385 }
386
387 // Create a new FormFieldData, fill it out and map it to the field's name.
388 var formField = new __gCrWeb['common'].JSONSafeObject;
389 __gCrWeb.autofill.webFormControlElementToFormField(
390 controlElement, extractMask, formField);
391 formFields.push(formField);
392 nameMap[formField['name']] = formField;
393 fieldsExtracted[i] = true;
394
395 // To avoid overly expensive computation, we impose a maximum number of
396 // allowable fields.
397 if (formFields.length > __gCrWeb.autofill.MAX_PARSEABE_FIELDS) {
398 return false;
399 }
400 }
401
402 // If we failed to extract any fields, give up.
403 if (formFields.length === 0) {
404 return false;
405 }
406
407 // Loop through the label elements inside the form element. For each label
408 // element, get the corresponding form control element, use the form control
409 // element's name as a key into the <name, FormFieldData> nameMap to find the
410 // previously created FormFieldData and set the FormFieldData's label.
411 var labels = formElement.getElementsByTagName('label');
412 for (var index = 0; index < labels.length; ++index) {
413 var label = labels[index];
414 if (!label.control && !label.htmlFor) {
415 continue;
416 }
417 var fieldElement = label.control;
418 var elementName;
419 if (!fieldElement) {
420 // Sometimes site authors will incorrectly specify the corresponding
421 // field element's name rather than its id, so we compensate here.
422 elementName = label.htmlFor;
423 } else if (fieldElement.form != formElement ||
424 fieldElement.type === 'hidden') {
425 continue;
426 } else {
427 elementName = __gCrWeb['common'].nameForAutofill(fieldElement);
428 }
429
430 var fieldElementData = nameMap[elementName];
431 if (fieldElementData) {
432 if (!fieldElementData['label']) {
433 fieldElementData['label'] = '';
434 }
435 var labelText = __gCrWeb.autofill.findChildText(label);
436 // Concatenate labels because some sites might have multiple label
437 // candidates.
438 if (fieldElementData['label'].length > 0 &&
439 labelText.length > 0) {
440 fieldElementData['label'] += ' ';
441 }
442 fieldElementData['label'] += labelText;
443 }
444 }
445
446 // Loop through the form control elements, extracting the label text from
447 // the DOM. We use the |fieldsExtracted| vector to make sure we assign the
448 // extracted label to the correct field, as it's possible |form_fields| will
449 // not contain all of the elements in |control_elements|.
450 for (var i = 0, fieldIdx = 0;
451 i < controlElements.length && fieldIdx < formFields.length; ++i) {
452 // This field didn't meet the requirements, so don't try to find a label
453 // for it.
454 if (!fieldsExtracted[i])
455 continue;
456
457 var controlElement = controlElements[i];
458 var fieldLabel = formFields[fieldIdx]['label'];
459 if (!fieldLabel || fieldLabel.length === 0) {
460 formFields[fieldIdx]['label'] =
461 __gCrWeb.autofill.inferLabelForElement(controlElement);
462 }
463 if (controlElement === formControlElement)
464 field = formField;
465 ++fieldIdx;
466 }
467
468 form['fields'] = formFields;
469 // Protect against custom implementation of Array.toJSON in host pages.
470 form['fields'].toJSON = null;
471 return true;
472 };
473
474 /**
475 * Returns is the tag of an |element| is tag.
476 *
477 * It is based on the logic in
478 * bool HasTagName(const WebNode& node, const blink::WebString& tag)
479 * in chromium/src/components/autofill/renderer/form_autofill_util.cc.
480 *
481 * @param {Element} node Node to examine.
482 * @param {string} tag Tag name.
483 * @return {boolean} Whether the tag of node is tag.
484 */
485 __gCrWeb.autofill.hasTagName = function(node, tag) {
486 return node.nodeType === document.ELEMENT_NODE &&
487 node.tagName === tag.toUpperCase();
488 };
489
490 /**
491 * Checks if an element is autofillable.
492 *
493 * It is based on the logic in
494 * bool IsAutofillableElement(const WebFormControlElement& element)
495 * in chromium/src/components/autofill/renderer/form_autofill_util.cc.
496 *
497 * @param {Element} element An element to examine.
498 * @return {boolean} Whether element is one of the element types that can be
499 * autofilled.
500 */
501 __gCrWeb.autofill.isAutofillableElement = function(element) {
502 return __gCrWeb.autofill.isAutofillableInputElement(element) ||
503 __gCrWeb.autofill.isSelectElement(element) ||
504 __gCrWeb.autofill.isTextAreaElement(element);
505 };
506
507 /**
508 * Check whether the given field satisfies the
509 * __gCrWeb.autofill.REQUIREMENTS_MASK_REQUIRE_AUTOCOMPLETE requirement. When
510 * Autocheckout is enabled, all fields are considered to satisfy this
511 * requirement.
512 *
513 * It is based on the logic in
514 * bool SatisfiesRequireAutocomplete(const WebInputElement& input_element)
515 * in chromium/src/components/autofill/renderer/form_autofill_util.cc.
516 *
517 * @param {Element} element The element to be examined.
518 * @param {boolean} isExperimentalFormFillingEnabled Boolean from
519 * switches::kEnableExperimentalFormFilling.
520 * @return {boolean} Whether the inputElement satisfies the requirement.
521 */
522 __gCrWeb.autofill.satisfiesRequireAutocomplete = function(
523 element, isExperimentalFormFillingEnabled) {
524 return __gCrWeb.common.autoComplete(element) ||
525 isExperimentalFormFillingEnabled;
526 };
527
528 /**
529 * Trims whitespace from the start of the input string.
530 * Simplified version of string_util::TrimWhitespace.
531 * @param {string} input String to trim.
532 * @return {string} The |input| string without leading whitespace.
533 */
534 __gCrWeb.autofill.trimWhitespaceLeading = function(input) {
535 return input.replace(/^\s+/gm, '');
536 };
537
538 /**
539 * Trims whitespace from the end of the input string.
540 * Simplified version of string_util::TrimWhitespace.
541 * @param {string} input String to trim.
542 * @return {string} The |input| string without trailing whitespace.
543 */
544 __gCrWeb.autofill.trimWhitespaceTrailing = function(input) {
545 return input.replace(/\s+$/gm, '');
546 };
547
548 /**
549 * Appends |suffix| to |prefix| so that any intermediary whitespace is collapsed
550 * to a single space. If |force_whitespace| is true, then the resulting string
551 * is guaranteed to have a space between |prefix| and |suffix|. Otherwise, the
552 * result includes a space only if |prefix| has trailing whitespace or |suffix|
553 * has leading whitespace.
554 *
555 * A few examples:
556 * CombineAndCollapseWhitespace('foo', 'bar', false) -> 'foobar'
557 * CombineAndCollapseWhitespace('foo', 'bar', true) -> 'foo bar'
558 * CombineAndCollapseWhitespace('foo ', 'bar', false) -> 'foo bar'
559 * CombineAndCollapseWhitespace('foo', ' bar', false) -> 'foo bar'
560 * CombineAndCollapseWhitespace('foo', ' bar', true) -> 'foo bar'
561 * CombineAndCollapseWhitespace('foo ', ' bar', false) -> 'foo bar'
562 * CombineAndCollapseWhitespace(' foo', 'bar ', false) -> ' foobar '
563 * CombineAndCollapseWhitespace(' foo', 'bar ', true) -> ' foo bar '
564 *
565 * It is based on the logic in
566 * const string16 CombineAndCollapseWhitespace(const string16& prefix,
567 * const string16& suffix,
568 * bool force_whitespace)
569 * @param {string} prefix The prefix string in the string combination.
570 * @param {string} suffix The suffix string in the string combination.
571 * @param {boolean} forceWhitespace A boolean indicating if whitespace should
572 * be added as separator in the combination.
573 * @return {string} The combined string.
574 */
575 __gCrWeb.autofill.combineAndCollapseWhitespace = function(
576 prefix, suffix, forceWhitespace) {
577 var prefixTrimmed = __gCrWeb.autofill.trimWhitespaceTrailing(prefix);
578 var prefixTrailingWhitespace = prefixTrimmed != prefix;
579 var suffixTrimmed = __gCrWeb.autofill.trimWhitespaceLeading(suffix);
580 var suffixLeadingWhitespace = suffixTrimmed != suffix;
581 if (prefixTrailingWhitespace || suffixLeadingWhitespace || forceWhitespace) {
582 return prefixTrimmed + ' ' + suffixTrimmed;
583 } else {
584 return prefixTrimmed + suffixTrimmed;
585 }
586 };
587
588 /**
589 * This is a helper function for the findChildText() function (see below).
590 * Search depth is limited with the |depth| parameter.
591 * Based on form_autofill_util::findChildTextInner().
592 * @param {Element} node The node to fetch the text content from.
593 * @param {int} depth The maximum depth to descend on the DOM.
594 * @return {string} The discovered and adapted string.
595 */
596 __gCrWeb.autofill.findChildTextInner = function(node, depth) {
597 if (depth <= 0 || !node) {
598 return '';
599 }
600
601 // Skip over comments.
602 if (node.nodeType === document.COMMENT_NODE) {
603 return __gCrWeb.autofill.findChildTextInner(node.nextSibling, depth - 1);
604 }
605
606 if (node.nodeType !== document.ELEMENT_NODE &&
607 node.nodeType !== document.TEXT_NODE) {
608 return '';
609 }
610
611 // Ignore elements known not to contain inferable labels.
612 if (node.nodeType === document.ELEMENT_NODE) {
613 if (node.tagName === 'OPTION' ||
614 node.tagName === 'SCRIPT' ||
615 node.tagName === 'NOSCRIPT' ||
616 (__gCrWeb.common.isFormControlElement(node) &&
617 __gCrWeb.autofill.isAutofillableElement(node))) {
618 return '';
619 }
620 }
621
622 // Extract the text exactly at this node.
623 var nodeText = __gCrWeb.autofill.nodeValue(node);
624 if (node.nodeType === document.TEXT_NODE && !nodeText) {
625 // In the C++ version, this text node would have been stripped completely.
626 // Just pass the buck.
627 return __gCrWeb.autofill.findChildTextInner(node.nextSibling, depth);
628 }
629
630 // Recursively compute the children's text.
631 // Preserve inter-element whitespace separation.
632 var childText =
633 __gCrWeb.autofill.findChildTextInner(node.firstChild, depth - 1);
634 var addSpace = node.nodeType === document.TEXT_NODE && !nodeText;
635 // Emulate apparently incorrect Chromium behavior tracked in crbug 239819.
636 addSpace = false;
637 nodeText = __gCrWeb.autofill.combineAndCollapseWhitespace(nodeText,
638 childText, addSpace);
639
640 // Recursively compute the siblings' text.
641 // Again, preserve inter-element whitespace separation.
642 var siblingText =
643 __gCrWeb.autofill.findChildTextInner(node.nextSibling, depth - 1);
644 addSpace = node.nodeType === document.TEXT_NODE && !nodeText;
645 // Emulate apparently incorrect Chromium behavior tracked in crbug 239819.
646 addSpace = false;
647 nodeText = __gCrWeb.autofill.combineAndCollapseWhitespace(nodeText,
648 siblingText, addSpace);
649
650 return nodeText;
651 };
652
653 /**
654 * Returns the aggregated values of the descendants of |element| that are
655 * non-empty text nodes.
656 *
657 * It is based on the logic in
658 * string16 FindChildText(const WebNode& node)
659 * chromium/src/components/autofill/renderer/form_autofill_util.cc, which is a
660 * faster alternative to |innerText()| for performance critical operations.
661 *
662 * @param {Element} node A node of which the child text will be return.
663 * @return {string} The child text.
664 */
665 __gCrWeb.autofill.findChildText = function(node) {
666 if (node.nodeType === document.TEXT_NODE)
667 return __gCrWeb.autofill.nodeValue(node);
668 var child = node.firstChild;
669
670 var kChildSearchDepth = 10;
671 var nodeText = __gCrWeb.autofill.findChildTextInner(child, kChildSearchDepth);
672 nodeText = nodeText.trim();
673 return nodeText;
674 };
675
676 /**
677 * Helper for |InferLabelForElement()| that infers a label, if possible, from
678 * a previous sibling of |element|,
679 * e.g. Some Text <input ...>
680 * or Some <span>Text</span> <input ...>
681 * or <p>Some Text</p><input ...>
682 * or <label>Some Text</label> <input ...>
683 * or Some Text <img><input ...>
684 * or <b>Some Text</b><br/> <input ...>.
685 *
686 * It is based on the logic in
687 * string16 InferLabelFromPrevious(const WebFormControlElement& element)
688 * in chromium/src/components/autofill/renderer/form_autofill_util.cc.
689 *
690 * @param {Element} element An element to examine.
691 * @return {string} The label of element.
692 */
693 __gCrWeb.autofill.inferLabelFromPrevious = function(element) {
694 var inferredLabel = '';
695 var previous = element;
696 if (!previous) {
697 return '';
698 }
699
700 while (true) {
701 previous = previous.previousSibling;
702 if (!previous) {
703 break;
704 }
705
706 // Skip over comments.
707 var nodeType = previous.nodeType;
708 if (nodeType === document.COMMENT_NODE_NODE) {
709 continue;
710 }
711
712 // Otherwise, only consider normal HTML elements and their contents.
713 if (nodeType != document.TEXT_NODE &&
714 nodeType != document.ELEMENT_NODE) {
715 break;
716 }
717
718 // A label might be split across multiple "lightweight" nodes.
719 // Coalesce any text contained in multiple consecutive
720 // (a) plain text nodes or
721 // (b) inline HTML elements that are essentially equivalent to text nodes.
722 if (nodeType === document.TEXT_NODE ||
723 __gCrWeb.autofill.hasTagName(previous, 'b') ||
724 __gCrWeb.autofill.hasTagName(previous, 'strong') ||
725 __gCrWeb.autofill.hasTagName(previous, 'span') ||
726 __gCrWeb.autofill.hasTagName(previous, 'font')) {
727 var value = __gCrWeb.autofill.findChildText(previous);
728 // A text node's value will be empty if it is for a line break.
729 var addSpace = nodeType === document.TEXT_NODE &&
730 value.length === 0;
731 inferredLabel =
732 __gCrWeb.autofill.combineAndCollapseWhitespace(
733 value, inferredLabel, addSpace);
734 continue;
735 }
736
737 // If we have identified a partial label and have reached a non-lightweight
738 // element, consider the label to be complete.
739 var trimmedLabel = inferredLabel.trim();
740 if (trimmedLabel.length > 0) {
741 break;
742 }
743
744 // <img> and <br> tags often appear between the input element and its
745 // label text, so skip over them.
746 if (__gCrWeb.autofill.hasTagName(previous, 'img') ||
747 __gCrWeb.autofill.hasTagName(previous, 'br')) {
748 continue;
749 }
750
751 // We only expect <p> and <label> tags to contain the full label text.
752 if (__gCrWeb.autofill.hasTagName(previous, 'p') ||
753 __gCrWeb.autofill.hasTagName(previous, 'label')) {
754 inferredLabel = __gCrWeb.autofill.findChildText(previous);
755 }
756 break;
757 }
758 return inferredLabel.trim();
759 };
760
761 /**
762 * Helper for |InferLabelForElement()| that infers a label, if possible, from
763 * the placeholder attribute.
764 *
765 * It is based on the logic in
766 * string16 InferLabelFromPlaceholder(const WebFormControlElement& element)
767 * in chromium/src/components/autofill/renderer/form_autofill_util.cc.
768 *
769 * @param {Element} element An element to examine.
770 * @return {string} The label of element.
771 */
772 __gCrWeb.autofill.inferLabelFromPlaceholder = function(element) {
773 if (!element || !element.placeholder) {
774 return '';
775 }
776
777 return element.placeholder;
778 };
779
780 /**
781 * Helper for |InferLabelForElement()| that infers a label, if possible, from
782 * enclosing list item, e.g.
783 * <li>Some Text<input ...><input ...><input ...></li>
784 *
785 * It is based on the logic in
786 * string16 InferLabelFromListItem(const WebFormControlElement& element)
787 * in chromium/src/components/autofill/renderer/form_autofill_util.cc.
788 *
789 * @param {Element} element An element to examine.
790 * @return {string} The label of element.
791 */
792 __gCrWeb.autofill.inferLabelFromListItem = function(element) {
793 if (!element) {
794 return '';
795 }
796
797 var parent = element.parentNode;
798 while (parent &&
799 parent.nodeType === document.ELEMENT_NODE &&
800 !__gCrWeb.autofill.hasTagName(parent, 'li')) {
801 parent = parent.parentNode;
802 }
803
804 if (parent && __gCrWeb.autofill.hasTagName(parent, 'li'))
805 return __gCrWeb.autofill.findChildText(parent);
806
807 return '';
808 };
809
810 /**
811 * Helper for |InferLabelForElement()| that infers a label, if possible, from
812 * surrounding table structure,
813 * e.g. <tr><td>Some Text</td><td><input ...></td></tr>
814 * or <tr><th>Some Text</th><td><input ...></td></tr>
815 * or <tr><td><b>Some Text</b></td><td><b><input ...></b></td></tr>
816 * or <tr><th><b>Some Text</b></th><td><b><input ...></b></td></tr>
817 *
818 * It is based on the logic in
819 * string16 InferLabelFromTableColumn(const WebFormControlElement& element)
820 * in chromium/src/components/autofill/renderer/form_autofill_util.cc.
821 *
822 * @param {Element} element An element to examine.
823 * @return {string} The label of element.
824 */
825 __gCrWeb.autofill.inferLabelFromTableColumn = function(element) {
826 if (!element) {
827 return '';
828 }
829
830 var parent = element.parentNode;
831 while (parent &&
832 parent.nodeType === document.ELEMENT_NODE &&
833 !__gCrWeb.autofill.hasTagName(parent, 'td')) {
834 parent = parent.parentNode;
835 }
836
837 if (!parent) {
838 return '';
839 }
840
841 // Check all previous siblings, skipping non-element nodes, until we find a
842 // non-empty text block.
843 var inferredLabel = '';
844 var previous = parent.previousSibling;
845 while (inferredLabel.length === 0 && previous) {
846 if (__gCrWeb.autofill.hasTagName(previous, 'td') ||
847 __gCrWeb.autofill.hasTagName(previous, 'th')) {
848 inferredLabel = __gCrWeb.autofill.findChildText(previous);
849 }
850 previous = previous.previousSibling;
851 }
852
853 return inferredLabel;
854 };
855
856 /**
857 * Helper for |InferLabelForElement()| that infers a label, if possible, from
858 * surrounding table structure,
859 * e.g. <tr><td>Some Text</td></tr><tr><td><input ...></td></tr>
860 *
861 * It is based on the logic in
862 * string16 InferLabelFromTableRow(const WebFormControlElement& element)
863 * in chromium/src/components/autofill/renderer/form_autofill_util.cc.
864 *
865 * @param {Element} element An element to examine.
866 * @return {string} The label of element.
867 */
868 __gCrWeb.autofill.inferLabelFromTableRow = function(element) {
869 if (!element) {
870 return '';
871 }
872
873 var parent = element.parentNode;
874 while (parent &&
875 parent.nodeType === document.ELEMENT_NODE &&
876 !__gCrWeb.autofill.hasTagName(parent, 'tr')) {
877 parent = parent.parentNode;
878 }
879
880 if (!parent) {
881 return '';
882 }
883
884 var inferredLabel = '';
885 // Check all previous siblings, skipping non-element nodes, until we find a
886 // non-empty text block.
887 var previous = parent.previousSibling;
888 while (inferredLabel.length === 0 && previous) {
889 if (__gCrWeb.autofill.hasTagName(previous, 'tr')) {
890 inferredLabel = __gCrWeb.autofill.findChildText(previous);
891 }
892 previous = previous.previousSibling;
893 }
894 return inferredLabel;
895 };
896
897 /**
898 * Helper for |InferLabelForElement()| that infers a label, if possible, from
899 * a surrounding div table,
900 * e.g. <div>Some Text<span><input ...></span></div>
901 * e.g. <div>Some Text</div><div><input ...></div>
902 *
903 * It is based on the logic in
904 * string16 InferLabelFromDivTable(const WebFormControlElement& element)
905 * in chromium/src/components/autofill/renderer/form_autofill_util.cc.
906 *
907 * @param {Element} element An element to examine.
908 * @return {string} The label of element.
909 */
910 __gCrWeb.autofill.inferLabelFromDivTable = function(element) {
911 if (!element) {
912 return '';
913 }
914
915 var node = element.parentNode;
916 var lookingForParent = true;
917
918 // Search the sibling and parent <div>s until we find a candidate label.
919 var inferredLabel = '';
920 while (inferredLabel.length === 0 && node) {
921 if (__gCrWeb.autofill.hasTagName(node, 'div')) {
922 lookingForParent = false;
923 inferredLabel = __gCrWeb.autofill.findChildText(node);
924 } else if (lookingForParent &&
925 (__gCrWeb.autofill.hasTagName(node, 'table') ||
926 __gCrWeb.autofill.hasTagName(node, 'fieldset'))) {
927 // If the element is in a table or fieldset, its label most likely is too.
928 break;
929 }
930
931 if (!node.previousSibling) {
932 // If there are no more siblings, continue walking up the tree.
933 lookingForParent = true;
934 }
935
936 if (lookingForParent) {
937 node = node.parentNode;
938 } else {
939 node = node.previousSibling;
940 }
941 }
942
943 return inferredLabel;
944 };
945
946 /**
947 * Helper for |InferLabelForElement()| that infers a label, if possible, from
948 * a surrounding definition list,
949 * e.g. <dl><dt>Some Text</dt><dd><input ...></dd></dl>
950 * e.g. <dl><dt><b>Some Text</b></dt><dd><b><input ...></b></dd></dl>
951 *
952 * It is based on the logic in
953 * string16 InferLabelFromDefinitionList(
954 * const WebFormControlElement& element)
955 * in chromium/src/components/autofill/renderer/form_autofill_util.cc.
956 *
957 * @param {Element} element An element to examine.
958 * @return {string} The label of element.
959 */
960 __gCrWeb.autofill.inferLabelFromDefinitionList = function(element) {
961 if (!element) {
962 return '';
963 }
964
965 var parent = element.parentNode;
966 while (parent &&
967 parent.nodeType === document.ELEMENT_NODE &&
968 !__gCrWeb.autofill.hasTagName(parent, 'dd')) {
969 parent = parent.parentNode;
970 }
971
972 if (!parent || !__gCrWeb.autofill.hasTagName(parent, 'dd')) {
973 return '';
974 }
975
976 // Skip by any intervening text nodes.
977 var previous = parent.previousSibling;
978 while (previous &&
979 previous.nodeType === document.TEXT_NODE) {
980 previous = previous.previousSibling;
981 }
982
983 if (!previous || !__gCrWeb.autofill.hasTagName(previous, 'dt'))
984 return '';
985
986 return __gCrWeb.autofill.findChildText(previous);
987 };
988
989 /**
990 * Infers corresponding label for |element| from surrounding context in the DOM,
991 * e.g. the contents of the preceding <p> tag or text element.
992 *
993 * It is based on the logic in
994 * string16 InferLabelForElement(const WebFormControlElement& element)
995 * in chromium/src/components/autofill/renderer/form_autofill_util.cc.
996 *
997 * @param {Element} element An element to examine.
998 * @return {string} The label of element.
999 */
1000 __gCrWeb.autofill.inferLabelForElement = function(element) {
1001 var inferredLabel = __gCrWeb.autofill.inferLabelFromPrevious(element);
1002 if (inferredLabel.length > 0) {
1003 return inferredLabel;
1004 }
1005
1006 // If we didn't find a label, check for the placeholder case.
1007 inferredLabel = __gCrWeb.autofill.inferLabelFromPlaceholder(element);
1008 if (inferredLabel.length > 0) {
1009 return inferredLabel;
1010 }
1011
1012 // If we didn't find a label, check for list item case.
1013 inferredLabel = __gCrWeb.autofill.inferLabelFromListItem(element);
1014 if (inferredLabel.length > 0) {
1015 return inferredLabel;
1016 }
1017
1018 // If we didn't find a label, check for table cell case.
1019 inferredLabel = __gCrWeb.autofill.inferLabelFromTableColumn(element);
1020 if (inferredLabel.length > 0) {
1021 return inferredLabel;
1022 }
1023
1024 // If we didn't find a label, check for table row case.
1025 inferredLabel = __gCrWeb.autofill.inferLabelFromTableRow(element);
1026 if (inferredLabel.length > 0) {
1027 return inferredLabel;
1028 }
1029
1030 // If we didn't find a label, check for definition list case.
1031 inferredLabel = __gCrWeb.autofill.inferLabelFromDefinitionList(element);
1032 if (inferredLabel.length > 0) {
1033 return inferredLabel;
1034 }
1035
1036 // If we didn't find a label, check for div table case.
1037 return __gCrWeb.autofill.inferLabelFromDivTable(element);
1038 };
1039
1040 /**
1041 * Fills |field| data with the values of the <option> elements present in
1042 * |selectElement|.
1043 *
1044 * It is based on the logic in
1045 * void GetOptionStringsFromElement(const WebSelectElement& select_element,
1046 * std::vector<string16>* option_values,
1047 * std::vector<string16>* option_contents)
1048 * in chromium/src/components/autofill/renderer/form_autofill_util.cc.
1049 *
1050 * @param {Element} selectElement A select element from which option data are
1051 * extracted.
1052 * @param {Object} field A field that will contain the extracted option
1053 * information.
1054 */
1055 __gCrWeb.autofill.getOptionStringsFromElement = function(
1056 selectElement, field) {
1057 field['option_values'] = [];
1058 // Protect against custom implementation of Array.toJSON in host pages.
1059 field['option_values'].toJSON = null;
1060 field['option_contents'] = [];
1061 field['option_contents'].toJSON = null;
1062 var options = selectElement.options;
1063 for (var i = 0; i < options.length; ++i) {
1064 var option = options[i];
1065 field['option_values'].push(option['value']);
1066 field['option_contents'].push(option['text']);
1067 }
1068 };
1069
1070 /**
1071 * Sets the |field|'s value to the value in |data|.
1072 * Also sets the "autofilled" attribute.
1073 *
1074 * It is based on the logic in
1075 * void FillFormField(const FormFieldData& data,
1076 * bool is_initiating_node,
1077 * blink::WebFormControlElement* field)
1078 * in chromium/src/components/autofill/renderer/form_autofill_util.cc.
1079 *
1080 * Different from FillFormField(), is_initiating_node is not considered in
1081 * this implementation.
1082 *
1083 * @param {Object} data Data that will be filled into field.
1084 * @param {Element} field The element to which data will be filled.
1085 */
1086 __gCrWeb.autofill.fillFormField = function(data, field) {
1087 // Nothing to fill.
1088 if (!data['value'] || data['value'].length === 0) {
1089 return;
1090 }
1091
1092 if (__gCrWeb.autofill.isTextInput(field) ||
1093 __gCrWeb.autofill.isTextAreaElement(field)) {
1094 var sanitizedValue = data['value'];
1095
1096 if (__gCrWeb.autofill.isTextInput(field)) {
1097 // If the 'max_length' attribute contains a negative value, the default
1098 // maxlength value is used.
1099 var maxLength = data['max_length'];
1100 if (maxLength < 0) {
1101 maxLength = __gCrWeb.autofill.MAX_DATA_LENGTH;
1102 }
1103 sanitizedValue = data['value'].substr(0, maxLength);
1104 }
1105
1106 __gCrWeb.common.setInputElementValue(sanitizedValue, field, true);
1107 field.isAutofilled = true;
1108 } else if (__gCrWeb.autofill.isSelectElement(field)) {
1109 if (field.value !== data['value']) {
1110 field.value = data['value'];
1111 __gCrWeb.common.createAndDispatchHTMLEvent(field, 'change', true, false);
1112 }
1113 } else {
1114 if (__gCrWeb.autofill.isCheckableElement(field)) {
1115 __gCrWeb.common.setInputElementChecked(data['is_checked'], field, true);
1116 }
1117 }
1118 };
1119
1120 /**
1121 * Returns true if |element| is a text input element.
1122 *
1123 * It is based on the logic in
1124 * bool IsTextInput(const blink::WebInputElement* element)
1125 * in chromium/src/components/autofill/renderer/form_autofill_util.h.
1126 *
1127 * @param {Element} element An element to examine.
1128 * @return {boolean} Whether element is a text input field.
1129 */
1130 __gCrWeb.autofill.isTextInput = function(element) {
1131 if (!element) {
1132 return false;
1133 }
1134 return __gCrWeb.common.isTextField(element);
1135 };
1136
1137 /**
1138 * Returns true if |element| is a 'select' element.
1139 *
1140 * It is based on the logic in
1141 * bool IsSelectElement(const blink::WebFormControlElement& element)
1142 * in chromium/src/components/autofill/renderer/form_autofill_util.h.
1143 *
1144 * @param {Element} element An element to examine.
1145 * @return {boolean} Whether element is a 'select' element.
1146 */
1147 __gCrWeb.autofill.isSelectElement = function(element) {
1148 if (!element) {
1149 return false;
1150 }
1151 return element.type === 'select-one';
1152 };
1153
1154 /**
1155 * Returns true if |element| is a 'textarea' element.
1156 *
1157 * It is based on the logic in
1158 * bool IsTextAreaElement(const blink::WebFormControlElement& element)
1159 * in chromium/src/components/autofill/renderer/form_autofill_util.h.
1160 *
1161 * @param {Element} element An element to examine.
1162 * @return {boolean} Whether element is a 'textarea' element.
1163 */
1164 __gCrWeb.autofill.isTextAreaElement = function(element) {
1165 if (!element) {
1166 return false;
1167 }
1168 return element.type === 'textarea';
1169 };
1170
1171 /**
1172 * Returns true if |element| is a checkbox or a radio button element.
1173 *
1174 * It is based on the logic in
1175 * bool IsCheckableElement(const blink::WebInputElement* element)
1176 * in chromium/src/components/autofill/renderer/form_autofill_util.h.
1177 *
1178 * @param {Element} element An element to examine.
1179 * @return {boolean} Whether element is a checkbox or a radio button.
1180 */
1181 __gCrWeb.autofill.isCheckableElement = function(element) {
1182 if (!element) {
1183 return false;
1184 }
1185 return element.type === 'checkbox' || element.type === 'radio';
1186 };
1187
1188 /**
1189 * Returns true if |element| is one of the input element types that can be
1190 * autofilled. {Text, Radiobutton, Checkbox}.
1191 *
1192 * It is based on the logic in
1193 * bool IsAutofillableInputElement(const blink::WebInputElement* element)
1194 * in chromium/src/components/autofill/renderer/form_autofill_util.h.
1195 *
1196 * @param {Element} element An element to examine.
1197 * @return {boolean} Whether element is one of the input element types that
1198 * can be autofilled.
1199 */
1200 __gCrWeb.autofill.isAutofillableInputElement = function(element) {
1201 return __gCrWeb.autofill.isTextInput(element) ||
1202 __gCrWeb.autofill.isCheckableElement(element);
1203 };
1204
1205 /**
1206 * Returns the nodeValue in a way similar to the C++ version of node.nodeValue,
1207 * used in src/components/autofill/renderer/form_autofill_util.h. Newlines and
1208 * tabs are stripped.
1209 *
1210 * @param {Element} element An element to examine.
1211 * @return {string} The text contained in |element|.
1212 */
1213 __gCrWeb.autofill.nodeValue = function(element) {
1214 return (element.nodeValue || '').replace(/[\n\t]/gm, '');
1215 };
1216
1217 /**
1218 * Returns the value in a way similar to the C++ version of node.value,
1219 * used in src/components/autofill/renderer/form_autofill_util.h. Newlines and
1220 * tabs are stripped.
1221 *
1222 * @param {Element} element An element to examine.
1223 * @return {string} The value for |element|.
1224 */
1225 __gCrWeb.autofill.value = function(element) {
1226 return (element.value || '').replace(/[\n\t]/gm, '');
1227 };
1228
1229 /**
1230 * Returns the auto-fillable form control elements in |formElement|.
1231 *
1232 * It is based on the logic in
1233 * void ExtractAutofillableElements(
1234 * const blink::WebFormElement& form_element,
1235 * RequirementsMask requirements,
1236 * std::vector<blink::WebFormControlElement>* autofillable_elements);
1237 * in chromium/src/components/autofill/renderer/form_autofill_util.h.
1238 *
1239 * @param {Element} formElement A form element to be processed.
1240 * @param {int} requirementsMask A mask on the requirement.
1241 * @param {Array.<Element>} autofillableElements The array of autofillable
1242 * elements.
1243 */
1244 __gCrWeb.autofill.extractAutofillableElements = function(
1245 formElement, requirementsMask, autofillableElements) {
1246 var controlElements = __gCrWeb.common.getFormControlElements(formElement);
1247
1248 for (var i = 0; i < controlElements.length; ++i) {
1249 var element = controlElements[i];
1250 if (!__gCrWeb.autofill.isAutofillableElement(element)) {
1251 continue;
1252 }
1253 if (requirementsMask &
1254 __gCrWeb.autofill.REQUIREMENTS_MASK_REQUIRE_AUTOCOMPLETE) {
1255 // Different from method void ExtractAutofillableElements() in
1256 // chromium/src/components/autofill/renderer/form_autofill_util.h, where
1257 // satisfiesRequireAutocomplete() check is only applied on input controls,
1258 // here satisfiesRequireAutocomplete() check is also applied on select
1259 // control element. This is based on the TODO in that file saying "WebKit
1260 // currently doesn't handle the autocomplete attribute for select control
1261 // elements, but it probably should."
1262 if (!__gCrWeb.autofill.satisfiesRequireAutocomplete(element, false)) {
1263 continue;
1264 }
1265 }
1266 autofillableElements.push(element);
1267 }
1268 };
1269
1270 /**
1271 * Fills out a FormField object from a given form control element.
1272 *
1273 * It is based on the logic in
1274 * void WebFormControlElementToFormField(
1275 * const blink::WebFormControlElement& element,
1276 * ExtractMask extract_mask,
1277 * FormFieldData* field);
1278 * in chromium/src/components/autofill/renderer/form_autofill_util.h.
1279 *
1280 * @param {Element} element The element to be processed.
1281 * @param {int} extractMask A bit field mask to extract data from |element|.
1282 * See the document on variable __gCrWeb.autofill.EXTRACT_MASK_NONE,
1283 * __gCrWeb.autofill.EXTRACT_MASK_VALUE,
1284 * __gCrWeb.autofill.EXTRACT_MASK_OPTION_TEXT and
1285 * __gCrWeb.autofill.EXTRACT_MASK_OPTIONS.
1286 * @param {Object} field Field to fill in the element information.
1287 */
1288 __gCrWeb.autofill.webFormControlElementToFormField = function(
1289 element, extractMask, field) {
1290 if (!field || !element) {
1291 return;
1292 }
1293 // The label is not officially part of a form control element; however, the
1294 // labels for all form control elements are scraped from the DOM and set in
1295 // form data.
1296 field['name'] = __gCrWeb['common'].nameForAutofill(element);
1297 field['form_control_type'] = element.type;
1298 var attribute = element.getAttribute('autocomplete');
1299 if (attribute) {
1300 field['autocomplete_attribute'] = attribute;
1301 }
1302 if (field['autocomplete_attribute'] != null &&
1303 field['autocomplete_attribute'].length >
1304 __gCrWeb.autofill.MAX_DATA_LENGTH) {
1305 // Discard overly long attribute values to avoid DOS-ing the browser
1306 // process. However, send over a default string to indicate that the
1307 // attribute was present.
1308 field['autocomplete_attribute'] = 'x-max-data-length-exceeded';
1309 }
1310
1311 if (!__gCrWeb.autofill.isAutofillableElement(element)) {
1312 return;
1313 }
1314
1315 if (__gCrWeb.autofill.isAutofillableInputElement(element) ||
1316 __gCrWeb.autofill.isTextAreaElement(element)) {
1317 field['is_autofilled'] = element.isAutofilled;
1318 field['should_autocomplete'] = __gCrWeb.common.autoComplete(element);
1319 // TODO(chenyu): compute this item properly.
1320 // field['is_focusable'] = element.isFocusable;
1321 }
1322
1323 if (__gCrWeb.autofill.isAutofillableInputElement(element)) {
1324 if (__gCrWeb.autofill.isTextInput(element)) {
1325 field['max_length'] = element.maxLength;
1326 }
1327 field['is_checkable'] = __gCrWeb.autofill.isCheckableElement(element);
1328 } else if (__gCrWeb.autofill.isTextAreaElement(element)) {
1329 // Nothing more to do in this case.
1330 } else if (extractMask & __gCrWeb.autofill.EXTRACT_MASK_OPTIONS) {
1331 __gCrWeb.autofill.getOptionStringsFromElement(element, field);
1332 }
1333
1334 if (!extractMask & __gCrWeb.autofill.EXTRACT_MASK_VALUE) {
1335 return;
1336 }
1337
1338 var value = __gCrWeb.autofill.value(element);
1339
1340 if (!__gCrWeb.autofill.isAutofillableInputElement(element)) {
1341 // Convert the |select_element| value to text if requested.
1342 if (extractMask & __gCrWeb.autofill.EXTRACT_MASK_OPTION_TEXT) {
1343 var options = element.options;
1344 for (var index = 0; index < options.length; ++index) {
1345 var optionElement = options[index];
1346 if (__gCrWeb.autofill.value(optionElement) === value) {
1347 value = optionElement.text;
1348 break;
1349 }
1350 }
1351 }
1352 }
1353
1354 // There is a constraint on the maximum data length in method
1355 // WebFormControlElementToFormField() in form_autofill_util.h in order to
1356 // prevent a malicious site from DOS'ing the browser: http://crbug.com/49332,
1357 // which isn't really meaningful here, but we need to follow the same logic to
1358 // get the same form signature wherever possible (to get the benefits of the
1359 // existing crowdsourced field detection corpus).
1360 if (value.length > __gCrWeb.autofill.MAX_DATA_LENGTH) {
1361 value = value.substr(0, __gCrWeb.autofill.MAX_DATA_LENGTH);
1362 }
1363 field['value'] = value;
1364 };
1365
1366 /**
1367 * For debugging purposes, annotate forms on the page with prediction data using
1368 * the placeholder attribute.
1369 *
1370 * @param {Object} data The form and field identifiers with their prection data.
1371 */
1372 __gCrWeb.autofill['fillPredictionData'] = function(data) {
1373
1374 for (formName in data) {
1375 var form = __gCrWeb.common.getFormElementFromIdentifier(formName);
1376 var formData = data[formName];
1377 var controlElements = __gCrWeb.common.getFormControlElements(form);
1378 for (var i = 0; i < controlElements.length; ++i) {
1379 var element = controlElements[i];
1380 if (!__gCrWeb.autofill.isAutofillableElement(element)) {
1381 continue;
1382 }
1383 var elementName = __gCrWeb['common'].nameForAutofill(element);
1384 var value = formData[elementName];
1385 if (value) {
1386 element.placeholder = value;
1387 }
1388 }
1389 }
1390 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698