Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / components / autofill / ios / browser / resources / autofill_controller.js
blobd8d022a3195a3b0674c022b4c1bac0f620d14d4b
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.
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/core/common/form_data.h) for further
12 // processing.
14 /** @typedef {HTMLInputElement|HTMLTextAreaElement|HTMLSelectElement} */
15 var FormControlElement;
17 /**
18   * @typedef {{
19   *   name: string,
20   *   value: string,
21   *   form_control_type: string,
22   *   autocomplete_attributes: string,
23   *   max_length: number,
24   *   is_autofilled: boolean,
25   *   is_checkable: boolean,
26   *   is_focusable: boolean,
27   *   should_autocomplete: boolean,
28   *   role: number,
29   *   option_contents: Array<string>,
30   *   option_values: Array<string>
31   * }}
32   */
33 var AutofillFormFieldData;
35 /**
36   * @typedef {{
37   *   name: string,
38   *   method: string,
39   *   origin: string,
40   *   action: string,
41   *   fields: Array<AutofillFormFieldData>
42   * }}
43   */
44 var AutofillFormData;
46 /**
47  * Namespace for this file. It depends on |__gCrWeb| having already been
48  * injected.
49  */
50 __gCrWeb['autofill'] = {};
52 /**
53  * The maximum length allowed for form data.
54  *
55  * This variable is from AutofillTable::kMaxDataLength in
56  * chromium/src/components/autofill/core/browser/webdata/autofill_table.h
57  *
58  * @const {number}
59  */
60 __gCrWeb.autofill.MAX_DATA_LENGTH = 1024;
62 /**
63  * The maximum number of form fields we are willing to parse, due to
64  * computational costs. Several examples of forms with lots of fields that are
65  * not relevant to Autofill: (1) the Netflix queue; (2) the Amazon wishlist;
66  * (3) router configuration pages; and (4) other configuration pages, e.g. for
67  * Google code project settings.
68  *
69  * This variable is |kMaxParseableFields| from
70  * chromium/src/components/autofill/content/renderer/form_autofill_util.h
71  *
72  * @const {number}
73  */
74 __gCrWeb.autofill.MAX_PARSEABLE_FIELDS = 100;
76 /**
77  * A bit field mask to extract data from WebFormControlElement for
78  * extracting none value.
79  *
80  * This variable is from enum ExtractMask in
81  * chromium/src/components/autofill/content/renderer/form_autofill_util.h
82  *
83  * @const {number}
84  */
85 __gCrWeb.autofill.EXTRACT_MASK_NONE = 0;
87 /**
88  * A bit field mask to extract data from WebFormControlElement for
89  * extracting value from WebFormControlElement.
90  *
91  * This variable is from enum ExtractMask in
92  * chromium/src/components/autofill/content/renderer/form_autofill_util.h
93  *
94  * @const {number}
95  */
96 __gCrWeb.autofill.EXTRACT_MASK_VALUE = 1 << 0;
98 /**
99  * A bit field mask to extract data from WebFormControlElement for
100  * extracting option text from WebFormSelectElement. Only valid when
101  * EXTRACT_MASK_VALUE is set. This is used for form submission where human
102  * readable value is captured.
104  * This variable is from enum ExtractMask in
105  * chromium/src/components/autofill/content/renderer/form_autofill_util.h
107  * @const {number}
108  */
109 __gCrWeb.autofill.EXTRACT_MASK_OPTION_TEXT = 1 << 1;
112  * A bit field mask to extract data from WebFormControlElement for
113  * extracting options from WebFormControlElement.
115  * This variable is from enum ExtractMask in
116  * chromium/src/components/autofill/content/renderer/form_autofill_util.h
118  * @const {number}
119  */
120 __gCrWeb.autofill.EXTRACT_MASK_OPTIONS = 1 << 2;
123  * A value for the "presentation" role.
125  * This variable is from enum RoleAttribute in
126  * chromium/src/components/autofill/core/common/form_field_data.h
128  * @const {number}
129  */
130 __gCrWeb.autofill.ROLE_ATTRIBUTE_PRESENTATION = 0;
133  * The last element that was autofilled.
135  * @type {Element}
136  */
137 __gCrWeb.autofill.lastAutoFilledElement = null;
140  * The last element that was active (used to restore focus if necessary).
142  * @type {Element}
143  */
144 __gCrWeb.autofill.lastActiveElement = null;
147  * Whether CSS for autofilled elements has been injected into the page.
149  * @type {boolean}
150  */
151 __gCrWeb.autofill.styleInjected = false;
154  * Extracts fields from |controlElements| with |extractMask| to |formFields|.
155  * The extracted fields are also placed in |elementArray|.
157  * It is based on the logic in
158  *     bool ExtractFieldsFromControlElements(
159  *         const WebVector<WebFormControlElement>& control_elements,
160  *         ExtractMask extract_mask,
161  *         ScopedVector<FormFieldData>* form_fields,
162  *         std::vector<bool>* fields_extracted,
163  *         std::map<WebFormControlElement, FormFieldData*>* element_map)
164  * in chromium/src/components/autofill/content/renderer/form_autofill_util.cc
166  * TODO(thestig): Make |element_map| a Map when Chrome makes iOS 8 and Safari 8
167  *                part of the minimal requirements.
169  * @param {Array<FormControlElement>} controlElements The control elements that
170  *     will be processed.
171  * @param {number} extractMask Mask controls what data is extracted from
172  *     controlElements.
173  * @param {Array<AutofillFormFieldData>} formFields The extracted form fields.
174  * @param {Array<boolean>} fieldsExtracted Indicates whether the fields were
175  *     extracted.
176  * @param {Array<?AutofillFormFieldData>} elementArray The extracted form
177  *     fields or null if a particular control has no corresponding field.
178  * @return {boolean} Whether there are fields and not too many fields in the
179  *     form.
180  */
181 function extractFieldsFromControlElements_(controlElements, extractMask,
182     formFields, fieldsExtracted, elementArray) {
183   for (var i = 0; i < controlElements.length; ++i) {
184     fieldsExtracted[i] = false;
185     elementArray[i] = null;
187     /** @type {FormControlElement} */
188     var controlElement = controlElements[i];
189     if (!__gCrWeb.autofill.isAutofillableElement(controlElement)) {
190       continue;
191     }
193     // Create a new AutofillFormFieldData, fill it out and map it to the
194     // field's name.
195     var formField = new __gCrWeb['common'].JSONSafeObject;
196     __gCrWeb.autofill.webFormControlElementToFormField(
197         controlElement, extractMask, formField);
198     formFields.push(formField);
199     elementArray[i] = formField;
200     fieldsExtracted[i] = true;
202     // To avoid overly expensive computation, we impose a maximum number of
203     // allowable fields.
204     if (formFields.length > __gCrWeb.autofill.MAX_PARSEABLE_FIELDS) {
205       return false;
206     }
207   }
209   return formFields.length > 0;
213  * Check if the node is visible.
215  * @param {Node} node The node to be processed.
216  * @return {boolean} Whether the node is visible or not.
217  */
218 function isVisibleNode_(node) {
219   if (!node)
220     return false;
222   if (node.nodeType === Node.ELEMENT_NODE) {
223     var style = window.getComputedStyle(/** @type {Element} */(node));
224     if (style.visibility == 'hidden' || style.display == 'none')
225       return false;
226   }
228   // Verify all ancestors are focusable.
229   return !node.parentNode || isVisibleNode_(node.parentNode);
233  * For each label element, get the corresponding form control element, use the
234  * form control element along with |controlElements| and |elementArray| to find
235  * the previously created AutofillFormFieldData and set the
236  * AutofillFormFieldData's label to the label.firstChild().nodeValue() of the
237  * label element.
239  * It is based on the logic in
240  *     void MatchLabelsAndFields(
241  *         const WebElementCollection& labels,
242  *         std::map<WebFormControlElement, FormFieldData*>* element_map);
243  * in chromium/src/components/autofill/content/renderer/form_autofill_util.cc
245  * This differs in that it takes a formElement field, instead of calling
246  * field_element.isFormControlElement().
248  * This also uses (|controlElements|, |elementArray|) because there is no
249  * guaranteeded Map support on iOS yet.
251  * @param {NodeList} labels The labels to match.
252  * @param {HTMLFormElement} formElement The form element being processed.
253  * @param {Array<FormControlElement>} controlElements The control elements that
254  *     were processed.
255  * @param {Array<?AutofillFormFieldData>} elementArray The extracted fields.
256  */
257 function matchLabelsAndFields_(labels, formElement, controlElements,
258     elementArray) {
259   for (var index = 0; index < labels.length; ++index) {
260     var label = labels[index];
261     var fieldElement = label.control;
262     var fieldData = null;
263     if (!fieldElement) {
264       // Sometimes site authors will incorrectly specify the corresponding
265       // field element's name rather than its id, so we compensate here.
266       var elementName = label.htmlFor;
267       if (!elementName)
268         continue;
269       // Look through the list for elements with this name. There can actually
270       // be more than one. In this case, the label may not be particularly
271       // useful, so just discard it.
272       for (var elementIndex = 0; elementIndex < elementArray.length;
273            ++elementIndex) {
274         var currentFieldData = elementArray[elementIndex];
275         if (currentFieldData && currentFieldData['name'] === elementName) {
276           if (fieldData !== null) {
277             fieldData = null;
278             break;
279           } else {
280             fieldData = currentFieldData;
281           }
282         }
283       }
284     } else if (fieldElement.form != formElement ||
285                    fieldElement.type === 'hidden') {
286       continue;
287     } else {
288       // Typical case: look up |fieldData| in |elementArray|.
289       for (var elementIndex = 0; elementIndex < elementArray.length;
290            ++elementIndex) {
291         if (controlElements[elementIndex] === fieldElement) {
292           fieldData = elementArray[elementIndex];
293           break;
294         }
295       }
296     }
298     if (!fieldData)
299       continue;
301     if (!('label' in fieldData)) {
302       fieldData['label'] = '';
303     }
304     var labelText = __gCrWeb.autofill.findChildText(label);
305     // Concatenate labels because some sites might have multiple label
306     // candidates.
307     if (fieldData['label'].length > 0 && labelText.length > 0) {
308       fieldData['label'] += ' ';
309     }
310     fieldData['label'] += labelText;
311   }
315  * Common function shared by webFormElementToFormData() and
316  * unownedFormElementsAndFieldSetsToFormData(). Either pass in:
317  * 1) |formElement|, |formControlElement| and an empty |fieldsets|.
318  * or
319  * 2) a non-empty |fieldsets|.
321  * It is based on the logic in
322  *     bool FormOrFieldsetsToFormData(
323  *         const blink::WebFormElement* form_element,
324  *         const blink::WebFormControlElement* form_control_element,
325  *         const std::vector<blink::WebElement>& fieldsets,
326  *         const WebVector<WebFormControlElement>& control_elements,
327  *         ExtractMask extract_mask,
328  *         FormData* form,
329  *         FormFieldData* field)
330  * in chromium/src/components/autofill/content/renderer/form_autofill_util.cc
332  * @param {HTMLFormElement} formElement The form element that will be processed.
333  * @param {FormControlElement} formControlElement A control element in
334  *     formElment, the FormField of which will be returned in field.
335  * @param {Array<Element>} fieldsets The fieldsets to look through if
336  *     formElement and formControlElement are not specified.
337  * @param {Array<FormControlElement>} controlElements The control elements that
338  *     will be processed.
339  * @param {number} extractMask Mask controls what data is extracted from
340  *     formElement.
341  * @param {AutofillFormData} form Form to fill in the AutofillFormData
342  *     information of formElement.
343  * @param {AutofillFormFieldData|null} field Field to fill in the form field
344  *     information of formControlElement.
345  * @return {boolean} Whether there are fields and not too many fields in the
346  *     form.
347  */
348 function formOrFieldsetsToFormData_(formElement, formControlElement,
349     fieldsets, controlElements, extractMask, form, field) {
350   // This should be a map from a control element to the AutofillFormFieldData.
351   // However, without Map support, it's just an Array of AutofillFormFieldData.
352   var elementArray = [];
354   // The extracted FormFields.
355   var formFields = [];
357   // A vector of bools that indicate whether each element in |controlElements|
358   // meets the requirements and thus will be in the resulting |form|.
359   var fieldsExtracted = [];
361   if (!extractFieldsFromControlElements_(controlElements, extractMask,
362                                          formFields, fieldsExtracted,
363                                          elementArray)) {
364     return false;
365   }
367   if (formElement) {
368     // Loop through the label elements inside the form element. For each label
369     // element, get the corresponding form control element, use the form control
370     // element along with |controlElements| and |elementArray| to find the
371     // previously created AutofillFormFieldData and set the
372     // AutofillFormFieldData's label.
373     var labels = formElement.getElementsByTagName('label');
374     matchLabelsAndFields_(labels, formElement, controlElements, elementArray);
375   } else {
376     // Same as the if block, but for all the labels in fieldset
377     for (var i = 0; i < fieldsets.length; ++i) {
378       var labels = fieldsets[i].getElementsByTagName('label');
379       matchLabelsAndFields_(labels, formElement, controlElements, elementArray);
380     }
381   }
383   // Loop through the form control elements, extracting the label text from
384   // the DOM.  We use the |fieldsExtracted| vector to make sure we assign the
385   // extracted label to the correct field, as it's possible |form_fields| will
386   // not contain all of the elements in |control_elements|.
387   for (var i = 0, fieldIdx = 0;
388        i < controlElements.length && fieldIdx < formFields.length; ++i) {
389     // This field didn't meet the requirements, so don't try to find a label
390     // for it.
391     if (!fieldsExtracted[i])
392       continue;
394     var controlElement = controlElements[i];
395     var fieldLabel = formFields[fieldIdx]['label'];
396     if (!fieldLabel) {
397       formFields[fieldIdx]['label'] =
398           __gCrWeb.autofill.inferLabelForElement(controlElement);
399     }
400     if (controlElement === formControlElement)
401       field = formFields[fieldIdx];
402     ++fieldIdx;
403   }
405   form['fields'] = formFields;
406   // Protect against custom implementation of Array.toJSON in host pages.
407   form['fields'].toJSON = null;
408   return true;
412  * Scans DOM and returns a JSON string representation of forms and form
413  * extraction results. This is just a wrapper around extractNewForms() to JSON
414  * encode the forms, for convenience.
416  * @param {number} requiredFields The minimum number of fields forms must have
417  *     to be extracted.
418  * @return {string} A JSON encoded object with object['forms'] containing the
419  *     forms data.
420  */
421 __gCrWeb.autofill['extractForms'] = function(requiredFields) {
422   var results = new __gCrWeb.common.JSONSafeObject;
423   results['forms'] = __gCrWeb.autofill.extractNewForms(requiredFields);
424   return __gCrWeb.stringify(results);
428  * Stores the current active element. This is used to make the element active
429  * again in case the web view loses focus when a dialog is presented over it.
430  */
431 __gCrWeb.autofill['storeActiveElement'] = function() {
432   __gCrWeb.autofill.lastActiveElement = document.activeElement;
436  * Clears the current active element by setting it to null.
437  */
438 __gCrWeb.autofill['clearActiveElement'] = function() {
439   __gCrWeb.autofill.lastActiveElement = null;
443  * Fills data into the active form field. The active form field is either
444  * document.activeElement or the value of lastActiveElement if that value is
445  * non-null.
447  * @param {AutofillFormFieldData} data The data to fill in.
448  */
449 __gCrWeb.autofill['fillActiveFormField'] = function(data) {
450   var activeElement = document.activeElement;
451   if (__gCrWeb.autofill.lastActiveElement) {
452     activeElement = __gCrWeb.autofill.lastActiveElement;
453     activeElement.focus();
454     __gCrWeb.autofill.lastActiveElement = null;
455   }
456   if (data['name'] !== __gCrWeb['common'].nameForAutofill(activeElement)) {
457     return;
458   }
459   __gCrWeb.autofill.lastAutoFilledElement = activeElement;
460   __gCrWeb.autofill.fillFormField(data, activeElement);
464  * Fills a number of fields in the same named form for full-form Autofill.
465  * Applies Autofill CSS (i.e. yellow background) to filled elements.
466  * Only empty fields will be filled, except that field named
467  * |forceFillFieldName| will always be filled even if non-empty.
469  * @param {Object} data Dictionary of data to fill in.
470  * @param {string} forceFillFieldName Named field will always be filled even if
471  *     non-empty. May be null.
472  */
473 __gCrWeb.autofill['fillForm'] = function(data, forceFillFieldName) {
474   // Inject CSS to style the autofilled elements with a yellow background.
475   if (!__gCrWeb.autofill.styleInjected) {
476     var style = document.createElement('style');
477     style.textContent = '[chrome-autofilled] {' +
478       'background-color:#FAFFBD !important;' +
479       'background-image:none !important;' +
480       'color:#000000 !important;' +
481       '}';
482     document.head.appendChild(style);
483     __gCrWeb.autofill.styleInjected = true;
484   }
486   // Remove Autofill styling when control element is edited.
487   var controlElementInputListener = function(evt) {
488     evt.target.removeAttribute('chrome-autofilled');
489     evt.target.isAutofilled = false;
490     evt.target.removeEventListener('input', controlElementInputListener);
491   };
493   var form = __gCrWeb.common.getFormElementFromIdentifier(data.formName);
494   var controlElements = __gCrWeb.common.getFormControlElements(form);
495   for (var i = 0; i < controlElements.length; ++i) {
496     var element = controlElements[i];
497     if (!__gCrWeb.autofill.isAutofillableElement(element)) {
498       continue;
499     }
500     var fieldName = __gCrWeb['common'].nameForAutofill(element);
502     // Skip non-empty fields unless this is the forceFillFieldName or it's a
503     // 'select-one' element. 'select-one' elements are always autofilled even
504     // if non-empty; see AutofillManager::FillOrPreviewDataModelForm().
505     if (element.value && element.value.length > 0 &&
506         !__gCrWeb.autofill.isSelectElement(element) &&
507         fieldName !== forceFillFieldName) {
508       continue;
509     }
511     // Don't fill field if source value is empty or missing.
512     var value = data.fields[fieldName];
513     if (!value)
514       continue;
516     if (__gCrWeb.autofill.isTextInput(element) ||
517         __gCrWeb.autofill.isTextAreaElement(element)) {
518       __gCrWeb.common.setInputElementValue(value, element, true);
519     } else if (__gCrWeb.autofill.isSelectElement(element)) {
520       if (element.value !== value) {
521         element.value = value;
522         __gCrWeb.common.createAndDispatchHTMLEvent(element, 'change', true,
523             false);
524       }
525     }
526     // TODO(bondd): Handle __gCrWeb.autofill.isCheckableElement(element) ==
527     // true. |is_checked| is not currently passed in by the caller.
529     element.setAttribute('chrome-autofilled');
530     element.isAutofilled = true;
531     element.addEventListener('input', controlElementInputListener);
532   }
534   // Remove Autofill styling when form receives 'reset' event.
535   // Individual control elements may be left with 'input' event listeners but
536   // they are harmless.
537   var formResetListener = function(evt) {
538     var controlElements = __gCrWeb.common.getFormControlElements(evt.target);
539     for (var i = 0; i < controlElements.length; ++i) {
540       controlElements[i].removeAttribute('chrome-autofilled');
541       controlElements[i].isAutofilled = false;
542     }
543     evt.target.removeEventListener('reset', formResetListener);
544   };
545   form.addEventListener('reset', formResetListener);
549  * Clear autofilled fields of the specified form. Fields that are not currently
550  * autofilled are not modified.
551  * Field contents are cleared, and Autofill flag and styling are removed.
552  * 'change' events are sent for fields whose contents changed.
553  * Based on FormCache::ClearFormWithElement().
555  * @param {string} formName Identifier for form element (from
556  *     getFormIdentifier).
557  */
558 __gCrWeb.autofill['clearAutofilledFields'] = function(formName) {
559   var form = __gCrWeb.common.getFormElementFromIdentifier(formName);
560   var controlElements = __gCrWeb.common.getFormControlElements(form);
561   for (var i = 0; i < controlElements.length; ++i) {
562     var element = controlElements[i];
563     if (!element.isAutofilled || element.disabled)
564       continue;
566     if (__gCrWeb.autofill.isTextInput(element) ||
567         __gCrWeb.autofill.isTextAreaElement(element)) {
568       __gCrWeb.common.setInputElementValue('', element, true);
569     } else if (__gCrWeb.autofill.isSelectElement(element)) {
570       // Reset to the first index.
571       // TODO(bondd): Store initial values and reset to the correct one here.
572       if (element.selectedIndex != 0) {
573         element.selectedIndex = 0;
574         __gCrWeb.common.createAndDispatchHTMLEvent(element, 'change', true,
575             false);
576       }
577     } else if (__gCrWeb.autofill.isCheckableElement(element)) {
578       // TODO(bondd): Handle checkable elements. They aren't properly supported
579       // by iOS Autofill yet.
580     }
582     element.removeAttribute('chrome-autofilled');
583     element.isAutofilled = false;
584   }
588  * Scans the DOM in |frame| extracting and storing forms. Fills |forms| with
589  * extracted forms.
591  * This method is based on the logic in method:
593  *     std::vector<FormData> ExtractNewForms();
595  * in chromium/src/components/autofill/content/renderer/form_cache.cc.
597  * The difference is in this implementation, the cache is not considered.
598  * Initial values of select and checkable elements are not recorded at the
599  * moment.
601  * This version still takes the minimumRequiredFields parameters. Whereas the
602  * C++ version does not.
604  * This version recursively scans its child frames. The C++ version does not
605  * because it has been converted to do only a single frame for Out Of Process
606  * Iframes.
608  * @param {number} minimumRequiredFields The minimum number of fields a form
609  *     should contain for autofill.
610  * @return {Array<AutofillFormData>} The extracted forms.
611  */
612 __gCrWeb.autofill.extractNewForms = function(minimumRequiredFields) {
613   var forms = [];
614   // Protect against custom implementation of Array.toJSON in host pages.
615   /** @suppress {checkTypes} */(function() { forms.toJSON = null; })();
617   extractFormsAndFormElements_(window, minimumRequiredFields, forms);
618   return forms;
622  * A helper function to implement extractNewForms().
624  * @param {HTMLFrameElement|Window} frame A window or a frame containing forms
625  *     from which the data will be extracted.
626  * @param {number} minimumRequiredFields The minimum number of fields a form
627  *     should contain for autofill.
628  * @param {Array<AutofillFormData>} forms Forms that will be filled in data of
629  *     forms in frame.
630  */
631 function extractFormsAndFormElements_(
632     frame, minimumRequiredFields, forms) {
633   if (!frame) {
634     return;
635   }
636   var doc = frame.document;
637   if (!doc) {
638     return;
639   }
641   /** @type {HTMLCollection} */
642   var webForms = doc.forms;
644   var numFieldsSeen = 0;
645   for (var formIndex = 0; formIndex < webForms.length; ++formIndex) {
646     /** @type {HTMLFormElement} */
647     var formElement = webForms[formIndex];
648     var controlElements =
649         __gCrWeb.autofill.extractAutofillableElementsInForm(formElement);
650     var numEditableElements = 0;
651     for (var elementIndex = 0; elementIndex < controlElements.length;
652          ++elementIndex) {
653       var element = controlElements[elementIndex];
654       if (!__gCrWeb.autofill.isCheckableElement(element)) {
655         ++numEditableElements;
656       }
657     }
659     // To avoid overly expensive computation, we impose a minimum number of
660     // allowable fields.  The corresponding maximum number of allowable
661     // fields is imposed by webFormElementToFormData().
662     if (numEditableElements < minimumRequiredFields &&
663         controlElements.length > 0) {
664       continue;
665     }
667     var extractMask = __gCrWeb.autofill.EXTRACT_MASK_VALUE |
668         __gCrWeb.autofill.EXTRACT_MASK_OPTIONS;
669     var form = new __gCrWeb['common'].JSONSafeObject;
670     if (!__gCrWeb.autofill.webFormElementToFormData(
671         frame, formElement, null, extractMask, form, null /* field */)) {
672       continue;
673     }
674     numFieldsSeen += form['fields'].length;
675     if (numFieldsSeen > __gCrWeb.autofill.MAX_PARSEABLE_FIELDS) {
676       break;
677     }
679     if (form.fields.length >= minimumRequiredFields) {
680       forms.push(form);
681     }
682   }
684   // Recursively invoke for all frames/iframes.
685   var frames = frame.frames;
686   for (var i = 0; i < frames.length; i++) {
687     extractFormsAndFormElements_(frames[i], minimumRequiredFields, forms);
688   }
692  * Fills |form| with the form data object corresponding to the |formElement|.
693  * If |field| is non-NULL, also fills |field| with the FormField object
694  * corresponding to the |formControlElement|.
695  * |extract_mask| controls what data is extracted.
696  * Returns true if |form| is filled out. Returns false if there are no fields or
697  * too many fields in the |form|.
699  * It is based on the logic in
700  *     bool WebFormElementToFormData(
701  *         const blink::WebFormElement& form_element,
702  *         const blink::WebFormControlElement& form_control_element,
703  *         ExtractMask extract_mask,
704  *         FormData* form,
705  *         FormFieldData* field)
706  * in chromium/src/components/autofill/content/renderer/form_autofill_util.cc
708  * @param {HTMLFrameElement|Window} frame The window or frame where the
709  *     formElement is in.
710  * @param {HTMLFormElement} formElement The form element that will be processed.
711  * @param {FormControlElement} formControlElement A control element in
712  *     formElment, the FormField of which will be returned in field.
713  * @param {number} extractMask Mask controls what data is extracted from
714  *     formElement.
715  * @param {AutofillFormData} form Form to fill in the AutofillFormData
716  *     information of formElement.
717  * @param {AutofillFormFieldData|null} field Field to fill in the form field
718  *     information of formControlElement.
719  * @return {boolean} Whether there are fields and not too many fields in the
720  *     form.
721  */
722 __gCrWeb.autofill.webFormElementToFormData = function(
723     frame, formElement, formControlElement, extractMask, form, field) {
724   if (!frame) {
725     return false;
726   }
728   form['name'] = __gCrWeb.common.getFormIdentifier(formElement);
729   var method = formElement.getAttribute('method');
730   if (method) {
731     form['method'] = method;
732   }
733   form['origin'] = __gCrWeb.common.removeQueryAndReferenceFromURL(
734       frame.location.href);
735   form['action'] = __gCrWeb.common.absoluteURL(
736       frame.document,
737       formElement.getAttribute('action'));
739   // Note different from form_autofill_util.cc version of this method, which
740   // computes |form.action| using document.completeURL(form_element.action())
741   // and falls back to formElement.action() if the computed action is invalid,
742   // here the action returned by |__gCrWeb.common.absoluteURL| is always
743   // valid, which is computed by creating a <a> element, and we don't check if
744   // the action is valid.
746   var controlElements = __gCrWeb['common'].getFormControlElements(formElement);
748   return formOrFieldsetsToFormData_(formElement, formControlElement,
749       [] /* fieldsets */, controlElements, extractMask, form, field);
753  * Returns is the tag of an |element| is tag.
755  * It is based on the logic in
756  *     bool HasTagName(const WebNode& node, const blink::WebString& tag)
757  * in chromium/src/components/autofill/content/renderer/form_autofill_util.cc.
759  * @param {Node} node Node to examine.
760  * @param {string} tag Tag name.
761  * @return {boolean} Whether the tag of node is tag.
762  */
763 __gCrWeb.autofill.hasTagName = function(node, tag) {
764   return node.nodeType === Node.ELEMENT_NODE &&
765          /** @type {Element} */(node).tagName === tag.toUpperCase();
769  * Checks if an element is autofillable.
771  * It is based on the logic in
772  *     bool IsAutofillableElement(const WebFormControlElement& element)
773  * in chromium/src/components/autofill/content/renderer/form_autofill_util.cc.
775  * @param {FormControlElement} element An element to examine.
776  * @return {boolean} Whether element is one of the element types that can be
777  *     autofilled.
778  */
779 __gCrWeb.autofill.isAutofillableElement = function(element) {
780   return __gCrWeb.autofill.isAutofillableInputElement(element) ||
781          __gCrWeb.autofill.isSelectElement(element) ||
782          __gCrWeb.autofill.isTextAreaElement(element);
786  * Trims whitespace from the start of the input string.
787  * Simplified version of string_util::TrimWhitespace.
788  * @param {string} input String to trim.
789  * @return {string} The |input| string without leading whitespace.
790  */
791 __gCrWeb.autofill.trimWhitespaceLeading = function(input) {
792   return input.replace(/^\s+/gm, '');
796  * Trims whitespace from the end of the input string.
797  * Simplified version of string_util::TrimWhitespace.
798  * @param {string} input String to trim.
799  * @return {string} The |input| string without trailing whitespace.
800  */
801 __gCrWeb.autofill.trimWhitespaceTrailing = function(input) {
802   return input.replace(/\s+$/gm, '');
806  * Appends |suffix| to |prefix| so that any intermediary whitespace is collapsed
807  * to a single space.  If |force_whitespace| is true, then the resulting string
808  * is guaranteed to have a space between |prefix| and |suffix|.  Otherwise, the
809  * result includes a space only if |prefix| has trailing whitespace or |suffix|
810  * has leading whitespace.
812  * A few examples:
813  *     CombineAndCollapseWhitespace('foo', 'bar', false)       -> 'foobar'
814  *     CombineAndCollapseWhitespace('foo', 'bar', true)        -> 'foo bar'
815  *     CombineAndCollapseWhitespace('foo ', 'bar', false)      -> 'foo bar'
816  *     CombineAndCollapseWhitespace('foo', ' bar', false)      -> 'foo bar'
817  *     CombineAndCollapseWhitespace('foo', ' bar', true)       -> 'foo bar'
818  *     CombineAndCollapseWhitespace('foo   ', '   bar', false) -> 'foo bar'
819  *     CombineAndCollapseWhitespace(' foo', 'bar ', false)     -> ' foobar '
820  *     CombineAndCollapseWhitespace(' foo', 'bar ', true)      -> ' foo bar '
822  * It is based on the logic in
823  * const string16 CombineAndCollapseWhitespace(const string16& prefix,
824  *                                             const string16& suffix,
825  *                                             bool force_whitespace)
826  * @param {string} prefix The prefix string in the string combination.
827  * @param {string} suffix The suffix string in the string combination.
828  * @param {boolean} forceWhitespace A boolean indicating if whitespace should
829  *     be added as separator in the combination.
830  * @return {string} The combined string.
831  */
832 __gCrWeb.autofill.combineAndCollapseWhitespace = function(
833     prefix, suffix, forceWhitespace) {
834   var prefixTrimmed = __gCrWeb.autofill.trimWhitespaceTrailing(prefix);
835   var prefixTrailingWhitespace = prefixTrimmed != prefix;
836   var suffixTrimmed = __gCrWeb.autofill.trimWhitespaceLeading(suffix);
837   var suffixLeadingWhitespace = suffixTrimmed != suffix;
838   if (prefixTrailingWhitespace || suffixLeadingWhitespace || forceWhitespace) {
839     return prefixTrimmed + ' ' + suffixTrimmed;
840   } else {
841     return prefixTrimmed + suffixTrimmed;
842   }
846  * This is a helper function for the findChildText() function (see below).
847  * Search depth is limited with the |depth| parameter.
849  * Based on form_autofill_util::FindChildTextInner().
851  * @param {Node} node The node to fetch the text content from.
852  * @param {number} depth The maximum depth to descend on the DOM.
853  * @param {Array<Node>} divsToSkip List of <div> tags to ignore if encountered.
854  * @return {string} The discovered and adapted string.
855  */
856 __gCrWeb.autofill.findChildTextInner = function(node, depth, divsToSkip) {
857   if (depth <= 0 || !node) {
858     return '';
859   }
861   // Skip over comments.
862   if (node.nodeType === Node.COMMENT_NODE) {
863     return __gCrWeb.autofill.findChildTextInner(node.nextSibling, depth - 1,
864                                                 divsToSkip);
865   }
867   if (node.nodeType !== Node.ELEMENT_NODE && node.nodeType !== Node.TEXT_NODE) {
868     return '';
869   }
871   // Ignore elements known not to contain inferable labels.
872   if (node.nodeType === Node.ELEMENT_NODE) {
873     if (node.tagName === 'OPTION' ||
874         node.tagName === 'SCRIPT' ||
875         node.tagName === 'NOSCRIPT') {
876       return '';
877     }
878     if (__gCrWeb.common.isFormControlElement(node)) {
879       var input = /** @type {FormControlElement} */ (node);
880       if (__gCrWeb.autofill.isAutofillableElement(input)) {
881         return '';
882       }
883     }
884   }
886   if (node.tagName === 'DIV') {
887     for (var i = 0; i < divsToSkip.length; ++i) {
888       if (node === divsToSkip[i]) {
889         return '';
890       }
891     }
892   }
894   // Extract the text exactly at this node.
895   var nodeText = __gCrWeb.autofill.nodeValue(node);
896   if (node.nodeType === Node.TEXT_NODE && !nodeText) {
897     // In the C++ version, this text node would have been stripped completely.
898     // Just pass the buck.
899     return __gCrWeb.autofill.findChildTextInner(node.nextSibling, depth,
900                                                 divsToSkip);
901   }
903   // Recursively compute the children's text.
904   // Preserve inter-element whitespace separation.
905   var childText = __gCrWeb.autofill.findChildTextInner(node.firstChild,
906                                                        depth - 1,
907                                                        divsToSkip);
908   var addSpace = node.nodeType === Node.TEXT_NODE && !nodeText;
909   // Emulate apparently incorrect Chromium behavior tracked in crbug 239819.
910   addSpace = false;
911   nodeText = __gCrWeb.autofill.combineAndCollapseWhitespace(nodeText,
912       childText, addSpace);
914   // Recursively compute the siblings' text.
915   // Again, preserve inter-element whitespace separation.
916   var siblingText = __gCrWeb.autofill.findChildTextInner(node.nextSibling,
917                                                          depth - 1,
918                                                          divsToSkip);
919   addSpace = node.nodeType === Node.TEXT_NODE && !nodeText;
920   // Emulate apparently incorrect Chromium behavior tracked in crbug 239819.
921   addSpace = false;
922   nodeText = __gCrWeb.autofill.combineAndCollapseWhitespace(nodeText,
923       siblingText, addSpace);
925   return nodeText;
929  * Same as findChildText() below, but with a list of div nodes to skip.
931  * It is based on the logic in
932  *    string16 FindChildTextWithIgnoreList(
933  *        const WebNode& node,
934  *        const std::set<WebNode>& divs_to_skip)
935  * in chromium/src/components/autofill/content/renderer/form_autofill_util.cc.
937  * @param {Node} node A node of which the child text will be return.
938  * @param {Array<Node>} divsToSkip List of <div> tags to ignore if encountered.
939  * @return {string} The child text.
940  */
941 __gCrWeb.autofill.findChildTextWithIgnoreList = function(node, divsToSkip) {
942   if (node.nodeType === Node.TEXT_NODE)
943     return __gCrWeb.autofill.nodeValue(node);
945   var child = node.firstChild;
946   var kChildSearchDepth = 10;
947   var nodeText = __gCrWeb.autofill.findChildTextInner(child, kChildSearchDepth,
948                                                       divsToSkip);
949   nodeText = nodeText.trim();
950   return nodeText;
954  * Returns the aggregated values of the descendants of |element| that are
955  * non-empty text nodes.
957  * It is based on the logic in
958  *    string16 FindChildText(const WebNode& node)
959  * chromium/src/components/autofill/content/renderer/form_autofill_util.cc,
960  * which is a faster alternative to |innerText()| for performance critical
961  * operations.
963  * @param {Node} node A node of which the child text will be return.
964  * @return {string} The child text.
965  */
966 __gCrWeb.autofill.findChildText = function(node) {
967   return __gCrWeb.autofill.findChildTextWithIgnoreList(node, []);
971  * Shared function for InferLabelFromPrevious() and InferLabelFromNext().
973  * It is based on the logic in
974  *     string16 InferLabelFromSibling(const WebFormControlElement& element,
975  *                                    bool forward)
976  * in chromium/src/components/autofill/content/renderer/form_autofill_util.cc.
978  * @param {FormControlElement} element An element to examine.
979  * @param {boolean} forward whether to search for the next or previous element.
980  * @return {string} The label of element or an empty string if there is no
981  *                  sibling or no label.
982  */
983 __gCrWeb.autofill.inferLabelFromSibling = function(element, forward) {
984   var inferredLabel = '';
985   var sibling = element;
986   if (!sibling) {
987     return '';
988   }
990   while (true) {
991     if (forward) {
992       sibling = sibling.nextSibling;
993     } else {
994       sibling = sibling.previousSibling;
995     }
997     if (!sibling) {
998       break;
999     }
1001     // Skip over comments.
1002     var nodeType = sibling.nodeType;
1003     if (nodeType === Node.COMMENT_NODE) {
1004       continue;
1005     }
1007     // Otherwise, only consider normal HTML elements and their contents.
1008     if (nodeType != Node.TEXT_NODE && nodeType != Node.ELEMENT_NODE) {
1009       break;
1010     }
1012     // A label might be split across multiple "lightweight" nodes.
1013     // Coalesce any text contained in multiple consecutive
1014     //  (a) plain text nodes or
1015     //  (b) inline HTML elements that are essentially equivalent to text nodes.
1016     if (nodeType === Node.TEXT_NODE ||
1017         __gCrWeb.autofill.hasTagName(sibling, 'b') ||
1018         __gCrWeb.autofill.hasTagName(sibling, 'strong') ||
1019         __gCrWeb.autofill.hasTagName(sibling, 'span') ||
1020         __gCrWeb.autofill.hasTagName(sibling, 'font')) {
1021       var value = __gCrWeb.autofill.findChildText(sibling);
1022       // A text node's value will be empty if it is for a line break.
1023       var addSpace = nodeType === Node.TEXT_NODE && value.length === 0;
1024       inferredLabel =
1025           __gCrWeb.autofill.combineAndCollapseWhitespace(
1026               value, inferredLabel, addSpace);
1027       continue;
1028     }
1030     // If we have identified a partial label and have reached a non-lightweight
1031     // element, consider the label to be complete.
1032     var trimmedLabel = inferredLabel.trim();
1033     if (trimmedLabel.length > 0) {
1034       break;
1035     }
1037     // <img> and <br> tags often appear between the input element and its
1038     // label text, so skip over them.
1039     if (__gCrWeb.autofill.hasTagName(sibling, 'img') ||
1040         __gCrWeb.autofill.hasTagName(sibling, 'br')) {
1041       continue;
1042     }
1044     // We only expect <p> and <label> tags to contain the full label text.
1045     if (__gCrWeb.autofill.hasTagName(sibling, 'p') ||
1046         __gCrWeb.autofill.hasTagName(sibling, 'label')) {
1047       inferredLabel = __gCrWeb.autofill.findChildText(sibling);
1048     }
1049     break;
1050   }
1051   return inferredLabel.trim();
1055  * Helper for |InferLabelForElement()| that infers a label, if possible, from
1056  * a previous sibling of |element|,
1057  * e.g. Some Text <input ...>
1058  * or   Some <span>Text</span> <input ...>
1059  * or   <p>Some Text</p><input ...>
1060  * or   <label>Some Text</label> <input ...>
1061  * or   Some Text <img><input ...>
1062  * or   <b>Some Text</b><br/> <input ...>.
1064  * It is based on the logic in
1065  *     string16 InferLabelFromPrevious(const WebFormControlElement& element)
1066  * in chromium/src/components/autofill/content/renderer/form_autofill_util.cc.
1068  * @param {FormControlElement} element An element to examine.
1069  * @return {string} The label of element.
1070  */
1071 __gCrWeb.autofill.inferLabelFromPrevious = function(element) {
1072   return __gCrWeb.autofill.inferLabelFromSibling(element, false);
1076  * Same as InferLabelFromPrevious(), but in the other direction.
1077  * Useful for cases like: <span><input type="checkbox">Label For Checkbox</span>
1079  * It is based on the logic in
1080  *     string16 InferLabelFromNext(const WebFormControlElement& element)
1081  * in chromium/src/components/autofill/content/renderer/form_autofill_util.cc.
1083  * @param {FormControlElement} element An element to examine.
1084  * @return {string} The label of element.
1085  */
1086 __gCrWeb.autofill.inferLabelFromNext = function(element) {
1087   return __gCrWeb.autofill.inferLabelFromSibling(element, true);
1091  * Helper for |InferLabelForElement()| that infers a label, if possible, from
1092  * the placeholder attribute.
1094  * It is based on the logic in
1095  *     string16 InferLabelFromPlaceholder(const WebFormControlElement& element)
1096  * in chromium/src/components/autofill/content/renderer/form_autofill_util.cc.
1098  * @param {FormControlElement} element An element to examine.
1099  * @return {string} The label of element.
1100  */
1101 __gCrWeb.autofill.inferLabelFromPlaceholder = function(element) {
1102   if (!element || !element.placeholder) {
1103     return '';
1104   }
1106   return element.placeholder;
1110  * Helper for |InferLabelForElement()| that infers a label, if possible, from
1111  * enclosing list item, e.g.
1112  *     <li>Some Text<input ...><input ...><input ...></li>
1114  * It is based on the logic in
1115  *     string16 InferLabelFromListItem(const WebFormControlElement& element)
1116  * in chromium/src/components/autofill/content/renderer/form_autofill_util.cc.
1118  * @param {FormControlElement} element An element to examine.
1119  * @return {string} The label of element.
1120  */
1121 __gCrWeb.autofill.inferLabelFromListItem = function(element) {
1122   if (!element) {
1123     return '';
1124   }
1126   var parentNode = element.parentNode;
1127   while (parentNode &&
1128          parentNode.nodeType === Node.ELEMENT_NODE &&
1129          !__gCrWeb.autofill.hasTagName(parentNode, 'li')) {
1130     parentNode = parentNode.parentNode;
1131   }
1133   if (parentNode && __gCrWeb.autofill.hasTagName(parentNode, 'li'))
1134     return __gCrWeb.autofill.findChildText(parentNode);
1136   return '';
1140  * Helper for |InferLabelForElement()| that infers a label, if possible, from
1141  * surrounding table structure,
1142  * e.g. <tr><td>Some Text</td><td><input ...></td></tr>
1143  * or   <tr><th>Some Text</th><td><input ...></td></tr>
1144  * or   <tr><td><b>Some Text</b></td><td><b><input ...></b></td></tr>
1145  * or   <tr><th><b>Some Text</b></th><td><b><input ...></b></td></tr>
1147  * It is based on the logic in
1148  *    string16 InferLabelFromTableColumn(const WebFormControlElement& element)
1149  * in chromium/src/components/autofill/content/renderer/form_autofill_util.cc.
1151  * @param {FormControlElement} element An element to examine.
1152  * @return {string} The label of element.
1153  */
1154 __gCrWeb.autofill.inferLabelFromTableColumn = function(element) {
1155   if (!element) {
1156     return '';
1157   }
1159   var parentNode = element.parentNode;
1160   while (parentNode &&
1161          parentNode.nodeType === Node.ELEMENT_NODE &&
1162          !__gCrWeb.autofill.hasTagName(parentNode, 'td')) {
1163     parentNode = parentNode.parentNode;
1164   }
1166   if (!parentNode) {
1167     return '';
1168   }
1170   // Check all previous siblings, skipping non-element nodes, until we find a
1171   // non-empty text block.
1172   var inferredLabel = '';
1173   var previous = parentNode.previousSibling;
1174   while (inferredLabel.length === 0 && previous) {
1175     if (__gCrWeb.autofill.hasTagName(previous, 'td') ||
1176         __gCrWeb.autofill.hasTagName(previous, 'th')) {
1177       inferredLabel = __gCrWeb.autofill.findChildText(previous);
1178     }
1179     previous = previous.previousSibling;
1180   }
1182   return inferredLabel;
1186  * Helper for |InferLabelForElement()| that infers a label, if possible, from
1187  * surrounding table structure,
1188  * e.g. <tr><td>Some Text</td></tr><tr><td><input ...></td></tr>
1190  * If there are multiple cells and the row with the input matches up with the
1191  * previous row, then look for a specific cell within the previous row.
1192  * e.g. <tr><td>Input 1 label</td><td>Input 2 label</td></tr>
1193  *  <tr><td><input name="input 1"></td><td><input name="input2"></td></tr>
1195  * It is based on the logic in
1196  *     string16 InferLabelFromTableRow(const WebFormControlElement& element)
1197  * in chromium/src/components/autofill/content/renderer/form_autofill_util.cc.
1199  * @param {FormControlElement} element An element to examine.
1200  * @return {string} The label of element.
1201  */
1202 __gCrWeb.autofill.inferLabelFromTableRow = function(element) {
1203   if (!element) {
1204     return '';
1205   }
1207   var cell = element.parentNode;
1208   while (cell) {
1209     if (cell.nodeType === Node.ELEMENT_NODE &&
1210         __gCrWeb.autofill.hasTagName(cell, 'td')) {
1211       break;
1212     }
1213     cell = cell.parentNode;
1214   }
1216   // Not in a cell - bail out.
1217   if (!cell) {
1218     return '';
1219   }
1221   // Count the cell holding |element|.
1222   var cellCount = cell.colSpan;
1223   var cellPosition = 0;
1224   var cellPositionEnd = cellCount - 1;
1226   // Count cells to the left to figure out |element|'s cell's position.
1227   var cellIterator = cell.previousSibling;
1228   while (cellIterator) {
1229     if (cellIterator.nodeType === Node.ELEMENT_NODE &&
1230         __gCrWeb.autofill.hasTagName(cellIterator, 'td')) {
1231       cellPosition += cellIterator.colSpan;
1232     }
1233     cellIterator = cellIterator.previousSibling;
1234   }
1236   // Count cells to the right.
1237   cellIterator = cell.nextSibling;
1238   while (cellIterator) {
1239     if (cellIterator.nodeType === Node.ELEMENT_NODE &&
1240         __gCrWeb.autofill.hasTagName(cellIterator, 'td')) {
1241       cellCount += cellIterator.colSpan;
1242     }
1243     cellIterator = cellIterator.nextSibling;
1244   }
1246   // Combine left + right.
1247   cellCount += cellPosition;
1248   cellPositionEnd += cellPosition
1250   // Find the current row.
1251   var parentNode = element.parentNode;
1252   while (parentNode &&
1253          parentNode.nodeType === Node.ELEMENT_NODE &&
1254          !__gCrWeb.autofill.hasTagName(parentNode, 'tr')) {
1255     parentNode = parentNode.parentNode;
1256   }
1258   if (!parentNode) {
1259     return '';
1260   }
1262   // Now find the previous row.
1263   var rowIt = parentNode.previousSibling;
1264   while (rowIt) {
1265     if (rowIt.nodeType === Node.ELEMENT_NODE &&
1266         __gCrWeb.autofill.hasTagName(parentNode, 'tr')) {
1267       break;
1268     }
1269     rowIt = rowIt.previousSibling;
1270   }
1272   // If there exists a previous row, check its cells and size. If they align
1273   // with the current row, infer the label from the cell above.
1274   if (rowIt) {
1275     var matchingCell = null;
1276     var prevRowCount = 0;
1277     var prevRowIt = rowIt.firstChild;
1278     while (prevRowIt) {
1279       if (prevRowIt.nodeType === Node.ELEMENT_NODE) {
1280         if (__gCrWeb.autofill.hasTagName(prevRowIt, 'td') ||
1281             __gCrWeb.autofill.hasTagName(prevRowIt, 'th')) {
1282           var span = prevRowIt.colSpan;
1283           var prevRowCountEnd = prevRowCount + span - 1;
1284           if (prevRowCount === cellPosition &&
1285               prevRowCountEnd === cellPositionEnd) {
1286             matchingCell = prevRowIt;
1287           }
1288           prevRowCount += span;
1289         }
1290       }
1291       prevRowIt = prevRowIt.nextSibling;
1292     }
1293     if (cellCount === prevRowCount && matchingCell) {
1294       var inferredLabel = __gCrWeb.autofill.findChildText(matchingCell);
1295       if (inferredLabel.length > 0) {
1296         return inferredLabel;
1297       }
1298     }
1299   }
1301   // If there is no previous row, or if the previous row and current row do not
1302   // align, check all previous siblings, skipping non-element nodes, until we
1303   // find a non-empty text block.
1304   var inferredLabel = '';
1305   var previous = parentNode.previousSibling;
1306   while (inferredLabel.length === 0 && previous) {
1307     if (__gCrWeb.autofill.hasTagName(previous, 'tr')) {
1308       inferredLabel = __gCrWeb.autofill.findChildText(previous);
1309     }
1310     previous = previous.previousSibling;
1311   }
1312   return inferredLabel;
1316  * Returns true if |node| is an element and it is a container type that
1317  * inferLabelForElement() can traverse.
1319  * It is based on the logic in
1320  *     bool IsTraversableContainerElement(const WebNode& node);
1321  * in chromium/src/components/autofill/content/renderer/form_autofill_util.cc.
1323  * @param {!Node} node The node to be examined.
1324  * @return {boolean} Whether it can be traversed.
1325  */
1326 __gCrWeb.autofill.isTraversableContainerElement = function(node) {
1327   if (node.nodeType !== Node.ELEMENT_NODE) {
1328     return false;
1329   }
1331   var tagName = /** @type {Element} */(node).tagName;
1332   return (tagName === "DD" ||
1333           tagName === "DIV" ||
1334           tagName === "FIELDSET" ||
1335           tagName === "LI" ||
1336           tagName === "TD" ||
1337           tagName === "TABLE");
1341  * Helper for |InferLabelForElement()| that infers a label, if possible, from
1342  * a surrounding div table,
1343  * e.g. <div>Some Text<span><input ...></span></div>
1344  * e.g. <div>Some Text</div><div><input ...></div>
1346  * Because this is already traversing the <div> structure, if it finds a <label>
1347  * sibling along the way, infer from that <label>.
1349  * It is based on the logic in
1350  *    string16 InferLabelFromDivTable(const WebFormControlElement& element)
1351  * in chromium/src/components/autofill/content/renderer/form_autofill_util.cc.
1353  * @param {FormControlElement} element An element to examine.
1354  * @return {string} The label of element.
1355  */
1356 __gCrWeb.autofill.inferLabelFromDivTable = function(element) {
1357   if (!element) {
1358     return '';
1359   }
1361   var node = element.parentNode;
1362   var lookingForParent = true;
1363   var divsToSkip = [];
1365   // Search the sibling and parent <div>s until we find a candidate label.
1366   var inferredLabel = '';
1367   while (inferredLabel.length === 0 && node) {
1368     if (__gCrWeb.autofill.hasTagName(node, 'div')) {
1369       if (lookingForParent) {
1370         inferredLabel =
1371             __gCrWeb.autofill.findChildTextWithIgnoreList(node, divsToSkip);
1372       } else {
1373         inferredLabel = __gCrWeb.autofill.findChildText(node);
1374       }
1375       // Avoid sibling DIVs that contain autofillable fields.
1376       if (!lookingForParent && inferredLabel.length > 0) {
1377         var resultElement = node.querySelector('input, select, textarea');
1378         if (resultElement) {
1379           inferredLabel = '';
1380           var addDiv = true;
1381           for (var i = 0; i < divsToSkip.length; ++i) {
1382             if (node === divsToSkip[i]) {
1383               addDiv = false;
1384               break;
1385             }
1386           }
1387           if (addDiv) {
1388             divsToSkip.push(node);
1389           }
1390         }
1391       }
1393       lookingForParent = false;
1394     } else if (!lookingForParent &&
1395                __gCrWeb.autofill.hasTagName(node, 'label')) {
1396       if (!node.control) {
1397         inferredLabel = __gCrWeb.autofill.findChildText(node);
1398       }
1399     } else if (lookingForParent &&
1400                __gCrWeb.autofill.isTraversableContainerElement(node)) {
1401       // If the element is in a non-div container, its label most likely is too.
1402       break;
1403     }
1405     if (!node.previousSibling) {
1406       // If there are no more siblings, continue walking up the tree.
1407       lookingForParent = true;
1408     }
1410     if (lookingForParent) {
1411       node = node.parentNode;
1412     } else {
1413       node = node.previousSibling;
1414     }
1415   }
1417   return inferredLabel;
1421  * Helper for |InferLabelForElement()| that infers a label, if possible, from
1422  * a surrounding definition list,
1423  * e.g. <dl><dt>Some Text</dt><dd><input ...></dd></dl>
1424  * e.g. <dl><dt><b>Some Text</b></dt><dd><b><input ...></b></dd></dl>
1426  * It is based on the logic in
1427  *    string16 InferLabelFromDefinitionList(
1428  *        const WebFormControlElement& element)
1429  * in chromium/src/components/autofill/content/renderer/form_autofill_util.cc.
1431  * @param {FormControlElement} element An element to examine.
1432  * @return {string} The label of element.
1433  */
1434 __gCrWeb.autofill.inferLabelFromDefinitionList = function(element) {
1435   if (!element) {
1436     return '';
1437   }
1439   var parentNode = element.parentNode;
1440   while (parentNode &&
1441          parentNode.nodeType === Node.ELEMENT_NODE &&
1442          !__gCrWeb.autofill.hasTagName(parentNode, 'dd')) {
1443     parentNode = parentNode.parentNode;
1444   }
1446   if (!parentNode || !__gCrWeb.autofill.hasTagName(parentNode, 'dd')) {
1447     return '';
1448   }
1450   // Skip by any intervening text nodes.
1451   var previous = parentNode.previousSibling;
1452   while (previous && previous.nodeType === Node.TEXT_NODE) {
1453     previous = previous.previousSibling;
1454   }
1456   if (!previous || !__gCrWeb.autofill.hasTagName(previous, 'dt'))
1457     return '';
1459   return __gCrWeb.autofill.findChildText(previous);
1463  * Returns the element type for all ancestor nodes in CAPS, starting with the
1464  * parent node.
1466  * It is based on the logic in
1467  *    std::vector<std::string> AncestorTagNames(
1468  *        const WebFormControlElement& element);
1469  * in chromium/src/components/autofill/content/renderer/form_autofill_util.cc.
1471  * @param {FormControlElement} element An element to examine.
1472  * @return {Array} The element types for all ancestors.
1473  */
1474 __gCrWeb.autofill.ancestorTagNames = function(element) {
1475   var tagNames = [];
1476   var parentNode = element.parentNode;
1477   while (parentNode) {
1478     if (parentNode.nodeType === Node.ELEMENT_NODE)
1479       tagNames.push(parentNode.tagName);
1480     parentNode = parentNode.parentNode;
1481   }
1482   return tagNames;
1486  * Infers corresponding label for |element| from surrounding context in the DOM,
1487  * e.g. the contents of the preceding <p> tag or text element.
1489  * It is based on the logic in
1490  *    string16 InferLabelForElement(const WebFormControlElement& element)
1491  * in chromium/src/components/autofill/content/renderer/form_autofill_util.cc.
1493  * @param {FormControlElement} element An element to examine.
1494  * @return {string} The label of element.
1495  */
1496 __gCrWeb.autofill.inferLabelForElement = function(element) {
1497   var inferredLabel;
1498   if (__gCrWeb.autofill.isCheckableElement(element)) {
1499     inferredLabel = __gCrWeb.autofill.inferLabelFromNext(element);
1500     if (inferredLabel.length > 0) {
1501       return inferredLabel;
1502     }
1503   }
1505   inferredLabel = __gCrWeb.autofill.inferLabelFromPrevious(element);
1506   if (inferredLabel.length > 0) {
1507     return inferredLabel;
1508   }
1510   // If we didn't find a label, check for the placeholder case.
1511   inferredLabel = __gCrWeb.autofill.inferLabelFromPlaceholder(element);
1512   if (inferredLabel.length > 0) {
1513     return inferredLabel;
1514   }
1516   // For all other searches that involve traversing up the tree, the search
1517   // order is based on which tag is the closest ancestor to |element|.
1518   var tagNames = __gCrWeb.autofill.ancestorTagNames(element);
1519   var seenTagNames = {};
1520   for (var index = 0; index < tagNames.length; ++index) {
1521     var tagName = tagNames[index];
1522     if (tagName in seenTagNames) {
1523       continue;
1524     }
1526     seenTagNames[tagName] = true;
1527     if (tagName === "DIV") {
1528       inferredLabel = __gCrWeb.autofill.inferLabelFromDivTable(element);
1529     } else if (tagName === "TD") {
1530       inferredLabel = __gCrWeb.autofill.inferLabelFromTableColumn(element);
1531       if (inferredLabel.length === 0)
1532         inferredLabel = __gCrWeb.autofill.inferLabelFromTableRow(element);
1533     } else if (tagName === "DD") {
1534       inferredLabel = __gCrWeb.autofill.inferLabelFromDefinitionList(element);
1535     } else if (tagName === "LI") {
1536       inferredLabel = __gCrWeb.autofill.inferLabelFromListItem(element);
1537     } else if (tagName === "FIELDSET") {
1538       break;
1539     }
1541     if (inferredLabel.length > 0) {
1542       break;
1543     }
1544   }
1546   return inferredLabel;
1550  * Fills |field| data with the values of the <option> elements present in
1551  * |selectElement|.
1553  * It is based on the logic in
1554  *     void GetOptionStringsFromElement(const WebSelectElement& select_element,
1555  *                                      std::vector<string16>* option_values,
1556  *                                      std::vector<string16>* option_contents)
1557  * in chromium/src/components/autofill/content/renderer/form_autofill_util.cc.
1559  * @param {Element} selectElement A select element from which option data are
1560  *     extracted.
1561  * @param {Object} field A field that will contain the extracted option
1562  *     information.
1563  */
1564 __gCrWeb.autofill.getOptionStringsFromElement = function(
1565     selectElement, field) {
1566   field['option_values'] = [];
1567   // Protect against custom implementation of Array.toJSON in host pages.
1568   field['option_values'].toJSON = null;
1569   field['option_contents'] = [];
1570   field['option_contents'].toJSON = null;
1571   var options = selectElement.options;
1572   for (var i = 0; i < options.length; ++i) {
1573     var option = options[i];
1574     field['option_values'].push(option['value']);
1575     field['option_contents'].push(option['text']);
1576   }
1580  * Sets the |field|'s value to the value in |data|.
1581  * Also sets the "autofilled" attribute.
1583  * It is based on the logic in
1584  *     void FillFormField(const FormFieldData& data,
1585  *                        bool is_initiating_node,
1586  *                        blink::WebFormControlElement* field)
1587  * in chromium/src/components/autofill/content/renderer/form_autofill_util.cc.
1589  * Different from FillFormField(), is_initiating_node is not considered in
1590  * this implementation.
1592  * @param {AutofillFormFieldData} data Data that will be filled into field.
1593  * @param {FormControlElement} field The element to which data will be filled.
1594  */
1595 __gCrWeb.autofill.fillFormField = function(data, field) {
1596   // Nothing to fill.
1597   if (!data['value'] || data['value'].length === 0) {
1598     return;
1599   }
1601   if (__gCrWeb.autofill.isTextInput(field) ||
1602       __gCrWeb.autofill.isTextAreaElement(field)) {
1603     var sanitizedValue = data['value'];
1605     if (__gCrWeb.autofill.isTextInput(field)) {
1606       // If the 'max_length' attribute contains a negative value, the default
1607       // maxlength value is used.
1608       var maxLength = data['max_length'];
1609       if (maxLength < 0) {
1610         maxLength = __gCrWeb.autofill.MAX_DATA_LENGTH;
1611       }
1612       sanitizedValue = data['value'].substr(0, maxLength);
1613     }
1615     __gCrWeb.common.setInputElementValue(sanitizedValue, field, true);
1616     field.isAutofilled = true;
1617   } else if (__gCrWeb.autofill.isSelectElement(field)) {
1618     if (field.value !== data['value']) {
1619       field.value = data['value'];
1620       __gCrWeb.common.createAndDispatchHTMLEvent(field, 'change', true, false);
1621     }
1622   } else {
1623     if (__gCrWeb.autofill.isCheckableElement(field)) {
1624       __gCrWeb.common.setInputElementChecked(data['is_checked'], field, true);
1625     }
1626   }
1630  * Returns true if |element| is a text input element.
1632  * It is based on the logic in
1633  *     bool IsTextInput(const blink::WebInputElement* element)
1634  * in chromium/src/components/autofill/content/renderer/form_autofill_util.h.
1636  * @param {FormControlElement} element An element to examine.
1637  * @return {boolean} Whether element is a text input field.
1638  */
1639 __gCrWeb.autofill.isTextInput = function(element) {
1640   if (!element) {
1641     return false;
1642   }
1643   return __gCrWeb.common.isTextField(element);
1647  * Returns true if |element| is a 'select' element.
1649  * It is based on the logic in
1650  *     bool IsSelectElement(const blink::WebFormControlElement& element)
1651  * in chromium/src/components/autofill/content/renderer/form_autofill_util.h.
1653  * @param {FormControlElement} element An element to examine.
1654  * @return {boolean} Whether element is a 'select' element.
1655  */
1656 __gCrWeb.autofill.isSelectElement = function(element) {
1657   if (!element) {
1658     return false;
1659   }
1660   return element.type === 'select-one';
1664  * Returns true if |element| is a 'textarea' element.
1666  * It is based on the logic in
1667  *     bool IsTextAreaElement(const blink::WebFormControlElement& element)
1668  * in chromium/src/components/autofill/content/renderer/form_autofill_util.h.
1670  * @param {FormControlElement} element An element to examine.
1671  * @return {boolean} Whether element is a 'textarea' element.
1672  */
1673 __gCrWeb.autofill.isTextAreaElement = function(element) {
1674   if (!element) {
1675     return false;
1676   }
1677   return element.type === 'textarea';
1681  * Returns true if |element| is a checkbox or a radio button element.
1683  * It is based on the logic in
1684  *     bool IsCheckableElement(const blink::WebInputElement* element)
1685  * in chromium/src/components/autofill/content/renderer/form_autofill_util.h.
1687  * @param {FormControlElement} element An element to examine.
1688  * @return {boolean} Whether element is a checkbox or a radio button.
1689  */
1690 __gCrWeb.autofill.isCheckableElement = function(element) {
1691   if (!element) {
1692     return false;
1693   }
1694   return element.type === 'checkbox' || element.type === 'radio';
1698  * Returns true if |element| is one of the input element types that can be
1699  * autofilled. {Text, Radiobutton, Checkbox}.
1701  * It is based on the logic in
1702  *    bool IsAutofillableInputElement(const blink::WebInputElement* element)
1703  * in chromium/src/components/autofill/content/renderer/form_autofill_util.h.
1705  * @param {FormControlElement} element An element to examine.
1706  * @return {boolean} Whether element is one of the input element types that
1707  *     can be autofilled.
1708  */
1709 __gCrWeb.autofill.isAutofillableInputElement = function(element) {
1710   return __gCrWeb.autofill.isTextInput(element) ||
1711          __gCrWeb.autofill.isCheckableElement(element);
1715  * Returns the nodeValue in a way similar to the C++ version of node.nodeValue,
1716  * used in src/components/autofill/content/renderer/form_autofill_util.h.
1717  * Newlines and tabs are stripped.
1719  * @param {Node} node A node to examine.
1720  * @return {string} The text contained in |element|.
1721  */
1722 __gCrWeb.autofill.nodeValue = function(node) {
1723   return (node.nodeValue || '').replace(/[\n\t]/gm, '');
1727  * Returns the value in a way similar to the C++ version of node.value,
1728  * used in src/components/autofill/content/renderer/form_autofill_util.h.
1729  * Newlines and tabs are stripped.
1731  * @param {Element} element An element to examine.
1732  * @return {string} The value for |element|.
1733  */
1734 __gCrWeb.autofill.value = function(element) {
1735   return (element.value || '').replace(/[\n\t]/gm, '');
1739  * Returns the auto-fillable form control elements in |formElement|.
1741  * It is based on the logic in:
1742  *     std::vector<blink::WebFormControlElement>
1743  *     ExtractAutofillableElementsFromSet(
1744  *         const WebVector<WebFormControlElement>& control_elements);
1745  * in chromium/src/components/autofill/content/renderer/form_autofill_util.h.
1747  * @param {Array<FormControlElement>} controlElements Set of control elements.
1748  * @return {Array<FormControlElement>} The array of autofillable elements.
1749  */
1750 __gCrWeb.autofill.extractAutofillableElementsFromSet =
1751     function(controlElements) {
1752   var autofillableElements = [];
1753   for (var i = 0; i < controlElements.length; ++i) {
1754     var element = controlElements[i];
1755     if (!__gCrWeb.autofill.isAutofillableElement(element)) {
1756       continue;
1757     }
1758     autofillableElements.push(element);
1759   }
1760   return autofillableElements;
1764  * Returns all the auto-fillable form control elements in |formElement|.
1766  * It is based on the logic in
1767  *     void ExtractAutofillableElementsInForm(
1768  *         const blink::WebFormElement& form_element);
1769  * in chromium/src/components/autofill/content/renderer/form_autofill_util.h.
1771  * @param {HTMLFormElement} formElement A form element to be processed.
1772  * @return {Array<FormControlElement>} The array of autofillable elements.
1773  */
1774 __gCrWeb.autofill.extractAutofillableElementsInForm = function(formElement) {
1775   var controlElements = __gCrWeb.common.getFormControlElements(formElement);
1776   return __gCrWeb.autofill.extractAutofillableElementsFromSet(controlElements);
1780  * Fills out a FormField object from a given form control element.
1782  * It is based on the logic in
1783  *     void WebFormControlElementToFormField(
1784  *         const blink::WebFormControlElement& element,
1785  *         ExtractMask extract_mask,
1786  *         FormFieldData* field);
1787  * in chromium/src/components/autofill/content/renderer/form_autofill_util.h.
1789  * @param {FormControlElement} element The element to be processed.
1790  * @param {number} extractMask A bit field mask to extract data from |element|.
1791  *     See the document on variable __gCrWeb.autofill.EXTRACT_MASK_NONE,
1792  *     __gCrWeb.autofill.EXTRACT_MASK_VALUE,
1793  *     __gCrWeb.autofill.EXTRACT_MASK_OPTION_TEXT and
1794  *     __gCrWeb.autofill.EXTRACT_MASK_OPTIONS.
1795  * @param {AutofillFormFieldData} field Field to fill in the element
1796  *     information.
1797  */
1798 __gCrWeb.autofill.webFormControlElementToFormField = function(
1799     element, extractMask, field) {
1800   if (!field || !element) {
1801     return;
1802   }
1803   // The label is not officially part of a form control element; however, the
1804   // labels for all form control elements are scraped from the DOM and set in
1805   // form data.
1806   field['name'] = __gCrWeb['common'].nameForAutofill(element);
1807   field['form_control_type'] = element.type;
1808   var autocomplete_attribute = element.getAttribute('autocomplete');
1809   if (autocomplete_attribute) {
1810     field['autocomplete_attribute'] = autocomplete_attribute;
1811   }
1812   if (field['autocomplete_attribute'] != null &&
1813       field['autocomplete_attribute'].length >
1814           __gCrWeb.autofill.MAX_DATA_LENGTH) {
1815     // Discard overly long attribute values to avoid DOS-ing the browser
1816     // process. However, send over a default string to indicate that the
1817     // attribute was present.
1818     field['autocomplete_attribute'] = 'x-max-data-length-exceeded';
1819   }
1821   var role_attribute = element.getAttribute('role');
1822   if (role_attribute && role_attribute.toLowerCase() == 'presentation') {
1823     field['role'] = __gCrWeb.autofill.ROLE_ATTRIBUTE_PRESENTATION;
1824   }
1826   if (!__gCrWeb.autofill.isAutofillableElement(element)) {
1827     return;
1828   }
1830   if (__gCrWeb.autofill.isAutofillableInputElement(element) ||
1831           __gCrWeb.autofill.isTextAreaElement(element) ||
1832           __gCrWeb.autofill.isSelectElement(element)) {
1833     field['is_autofilled'] = element.isAutofilled;
1834     field['should_autocomplete'] = __gCrWeb.common.autoComplete(element);
1835     field['is_focusable'] = !element.disabled && !element.readOnly &&
1836         element.tabIndex >= 0 && isVisibleNode_(element);
1837   }
1839   if (__gCrWeb.autofill.isAutofillableInputElement(element)) {
1840     if (__gCrWeb.autofill.isTextInput(element)) {
1841       field['max_length'] = element.maxLength;
1842     }
1843     field['is_checkable'] = __gCrWeb.autofill.isCheckableElement(element);
1844   } else if (__gCrWeb.autofill.isTextAreaElement(element)) {
1845     // Nothing more to do in this case.
1846   } else if (extractMask & __gCrWeb.autofill.EXTRACT_MASK_OPTIONS) {
1847     __gCrWeb.autofill.getOptionStringsFromElement(element, field);
1848   }
1850   if (!extractMask & __gCrWeb.autofill.EXTRACT_MASK_VALUE) {
1851     return;
1852   }
1854   var value = __gCrWeb.autofill.value(element);
1856   if (!__gCrWeb.autofill.isAutofillableInputElement(element)) {
1857     // Convert the |select_element| value to text if requested.
1858     if (extractMask & __gCrWeb.autofill.EXTRACT_MASK_OPTION_TEXT) {
1859       var options = element.options;
1860       for (var index = 0; index < options.length; ++index) {
1861         var optionElement = options[index];
1862         if (__gCrWeb.autofill.value(optionElement) === value) {
1863           value = optionElement.text;
1864           break;
1865         }
1866       }
1867     }
1868   }
1870   // There is a constraint on the maximum data length in method
1871   // WebFormControlElementToFormField() in form_autofill_util.h in order to
1872   // prevent a malicious site from DOS'ing the browser: http://crbug.com/49332,
1873   // which isn't really meaningful here, but we need to follow the same logic to
1874   // get the same form signature wherever possible (to get the benefits of the
1875   // existing crowdsourced field detection corpus).
1876   if (value.length > __gCrWeb.autofill.MAX_DATA_LENGTH) {
1877     value = value.substr(0, __gCrWeb.autofill.MAX_DATA_LENGTH);
1878   }
1879   field['value'] = value;
1883  * For debugging purposes, annotate forms on the page with prediction data using
1884  * the placeholder attribute.
1886  * @param {Object<AutofillFormData>} data The form and field identifiers with
1887  *     their prediction data.
1888  */
1889 __gCrWeb.autofill['fillPredictionData'] = function(data) {
1890   for (var formName in data) {
1891     var form = __gCrWeb.common.getFormElementFromIdentifier(formName);
1892     var formData = data[formName];
1893     var controlElements = __gCrWeb.common.getFormControlElements(form);
1894     for (var i = 0; i < controlElements.length; ++i) {
1895       var element = controlElements[i];
1896       if (!__gCrWeb.autofill.isAutofillableElement(element)) {
1897         continue;
1898       }
1899       var elementName = __gCrWeb['common'].nameForAutofill(element);
1900       var value = formData[elementName];
1901       if (value) {
1902         element.placeholder = value;
1903       }
1904     }
1905   }