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.
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
14 /** @typedef {HTMLInputElement|HTMLTextAreaElement|HTMLSelectElement} */
15 var FormControlElement;
21 * form_control_type: string,
22 * autocomplete_attributes: string,
24 * is_autofilled: boolean,
25 * is_checkable: boolean,
26 * is_focusable: boolean,
27 * should_autocomplete: boolean,
29 * option_contents: Array<string>,
30 * option_values: Array<string>
33 var AutofillFormFieldData;
41 * fields: Array<AutofillFormFieldData>
47 * Namespace for this file. It depends on |__gCrWeb| having already been
50 __gCrWeb['autofill'] = {};
53 * The maximum length allowed for form data.
55 * This variable is from AutofillTable::kMaxDataLength in
56 * chromium/src/components/autofill/core/browser/webdata/autofill_table.h
60 __gCrWeb.autofill.MAX_DATA_LENGTH = 1024;
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.
69 * This variable is |kMaxParseableFields| from
70 * chromium/src/components/autofill/content/renderer/form_autofill_util.h
74 __gCrWeb.autofill.MAX_PARSEABLE_FIELDS = 100;
77 * A bit field mask to extract data from WebFormControlElement for
78 * extracting none value.
80 * This variable is from enum ExtractMask in
81 * chromium/src/components/autofill/content/renderer/form_autofill_util.h
85 __gCrWeb.autofill.EXTRACT_MASK_NONE = 0;
88 * A bit field mask to extract data from WebFormControlElement for
89 * extracting value from WebFormControlElement.
91 * This variable is from enum ExtractMask in
92 * chromium/src/components/autofill/content/renderer/form_autofill_util.h
96 __gCrWeb.autofill.EXTRACT_MASK_VALUE = 1 << 0;
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
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
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
130 __gCrWeb.autofill.ROLE_ATTRIBUTE_PRESENTATION = 0;
133 * The last element that was autofilled.
137 __gCrWeb.autofill.lastAutoFilledElement = null;
140 * The last element that was active (used to restore focus if necessary).
144 __gCrWeb.autofill.lastActiveElement = null;
147 * Whether CSS for autofilled elements has been injected into the page.
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
171 * @param {number} extractMask Mask controls what data is extracted from
173 * @param {Array<AutofillFormFieldData>} formFields The extracted form fields.
174 * @param {Array<boolean>} fieldsExtracted Indicates whether the fields were
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
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)) {
193 // Create a new AutofillFormFieldData, fill it out and map it to the
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
204 if (formFields.length > __gCrWeb.autofill.MAX_PARSEABLE_FIELDS) {
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.
218 function isVisibleNode_(node) {
222 if (node.nodeType === Node.ELEMENT_NODE) {
223 var style = window.getComputedStyle(/** @type {Element} */(node));
224 if (style.visibility == 'hidden' || style.display == 'none')
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
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
255 * @param {Array<?AutofillFormFieldData>} elementArray The extracted fields.
257 function matchLabelsAndFields_(labels, formElement, controlElements,
259 for (var index = 0; index < labels.length; ++index) {
260 var label = labels[index];
261 var fieldElement = label.control;
262 var fieldData = null;
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;
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;
274 var currentFieldData = elementArray[elementIndex];
275 if (currentFieldData && currentFieldData['name'] === elementName) {
276 if (fieldData !== null) {
280 fieldData = currentFieldData;
284 } else if (fieldElement.form != formElement ||
285 fieldElement.type === 'hidden') {
288 // Typical case: look up |fieldData| in |elementArray|.
289 for (var elementIndex = 0; elementIndex < elementArray.length;
291 if (controlElements[elementIndex] === fieldElement) {
292 fieldData = elementArray[elementIndex];
301 if (!('label' in fieldData)) {
302 fieldData['label'] = '';
304 var labelText = __gCrWeb.autofill.findChildText(label);
305 // Concatenate labels because some sites might have multiple label
307 if (fieldData['label'].length > 0 && labelText.length > 0) {
308 fieldData['label'] += ' ';
310 fieldData['label'] += labelText;
315 * Common function shared by webFormElementToFormData() and
316 * unownedFormElementsAndFieldSetsToFormData(). Either pass in:
317 * 1) |formElement|, |formControlElement| and an empty |fieldsets|.
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,
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
339 * @param {number} extractMask Mask controls what data is extracted from
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
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.
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,
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);
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);
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
391 if (!fieldsExtracted[i])
394 var controlElement = controlElements[i];
395 var fieldLabel = formFields[fieldIdx]['label'];
397 formFields[fieldIdx]['label'] =
398 __gCrWeb.autofill.inferLabelForElement(controlElement);
400 if (controlElement === formControlElement)
401 field = formFields[fieldIdx];
405 form['fields'] = formFields;
406 // Protect against custom implementation of Array.toJSON in host pages.
407 form['fields'].toJSON = null;
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
418 * @return {string} A JSON encoded object with object['forms'] containing the
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.
431 __gCrWeb.autofill['storeActiveElement'] = function() {
432 __gCrWeb.autofill.lastActiveElement = document.activeElement;
436 * Clears the current active element by setting it to null.
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
447 * @param {AutofillFormFieldData} data The data to fill in.
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;
456 if (data['name'] !== __gCrWeb['common'].nameForAutofill(activeElement)) {
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.
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;' +
482 document.head.appendChild(style);
483 __gCrWeb.autofill.styleInjected = true;
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);
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)) {
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) {
511 // Don't fill field if source value is empty or missing.
512 var value = data.fields[fieldName];
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,
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);
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;
543 evt.target.removeEventListener('reset', formResetListener);
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).
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)
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,
577 } else if (__gCrWeb.autofill.isCheckableElement(element)) {
578 // TODO(bondd): Handle checkable elements. They aren't properly supported
579 // by iOS Autofill yet.
582 element.removeAttribute('chrome-autofilled');
583 element.isAutofilled = false;
588 * Scans the DOM in |frame| extracting and storing forms. Fills |forms| with
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
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
608 * @param {number} minimumRequiredFields The minimum number of fields a form
609 * should contain for autofill.
610 * @return {Array<AutofillFormData>} The extracted forms.
612 __gCrWeb.autofill.extractNewForms = function(minimumRequiredFields) {
614 // Protect against custom implementation of Array.toJSON in host pages.
615 /** @suppress {checkTypes} */(function() { forms.toJSON = null; })();
617 extractFormsAndFormElements_(window, minimumRequiredFields, 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
631 function extractFormsAndFormElements_(
632 frame, minimumRequiredFields, forms) {
636 var doc = frame.document;
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;
653 var element = controlElements[elementIndex];
654 if (!__gCrWeb.autofill.isCheckableElement(element)) {
655 ++numEditableElements;
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) {
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 */)) {
674 numFieldsSeen += form['fields'].length;
675 if (numFieldsSeen > __gCrWeb.autofill.MAX_PARSEABLE_FIELDS) {
679 if (form.fields.length >= minimumRequiredFields) {
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);
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,
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
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
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
722 __gCrWeb.autofill.webFormElementToFormData = function(
723 frame, formElement, formControlElement, extractMask, form, field) {
728 form['name'] = __gCrWeb.common.getFormIdentifier(formElement);
729 var method = formElement.getAttribute('method');
731 form['method'] = method;
733 form['origin'] = __gCrWeb.common.removeQueryAndReferenceFromURL(
734 frame.location.href);
735 form['action'] = __gCrWeb.common.absoluteURL(
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.
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
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.
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.
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.
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.
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;
841 return prefixTrimmed + suffixTrimmed;
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.
856 __gCrWeb.autofill.findChildTextInner = function(node, depth, divsToSkip) {
857 if (depth <= 0 || !node) {
861 // Skip over comments.
862 if (node.nodeType === Node.COMMENT_NODE) {
863 return __gCrWeb.autofill.findChildTextInner(node.nextSibling, depth - 1,
867 if (node.nodeType !== Node.ELEMENT_NODE && node.nodeType !== Node.TEXT_NODE) {
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') {
878 if (__gCrWeb.common.isFormControlElement(node)) {
879 var input = /** @type {FormControlElement} */ (node);
880 if (__gCrWeb.autofill.isAutofillableElement(input)) {
886 if (node.tagName === 'DIV') {
887 for (var i = 0; i < divsToSkip.length; ++i) {
888 if (node === divsToSkip[i]) {
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,
903 // Recursively compute the children's text.
904 // Preserve inter-element whitespace separation.
905 var childText = __gCrWeb.autofill.findChildTextInner(node.firstChild,
908 var addSpace = node.nodeType === Node.TEXT_NODE && !nodeText;
909 // Emulate apparently incorrect Chromium behavior tracked in crbug 239819.
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,
919 addSpace = node.nodeType === Node.TEXT_NODE && !nodeText;
920 // Emulate apparently incorrect Chromium behavior tracked in crbug 239819.
922 nodeText = __gCrWeb.autofill.combineAndCollapseWhitespace(nodeText,
923 siblingText, addSpace);
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.
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,
949 nodeText = nodeText.trim();
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
963 * @param {Node} node A node of which the child text will be return.
964 * @return {string} The child text.
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,
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.
983 __gCrWeb.autofill.inferLabelFromSibling = function(element, forward) {
984 var inferredLabel = '';
985 var sibling = element;
992 sibling = sibling.nextSibling;
994 sibling = sibling.previousSibling;
1001 // Skip over comments.
1002 var nodeType = sibling.nodeType;
1003 if (nodeType === Node.COMMENT_NODE) {
1007 // Otherwise, only consider normal HTML elements and their contents.
1008 if (nodeType != Node.TEXT_NODE && nodeType != Node.ELEMENT_NODE) {
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;
1025 __gCrWeb.autofill.combineAndCollapseWhitespace(
1026 value, inferredLabel, addSpace);
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) {
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')) {
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);
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.
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.
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.
1101 __gCrWeb.autofill.inferLabelFromPlaceholder = function(element) {
1102 if (!element || !element.placeholder) {
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.
1121 __gCrWeb.autofill.inferLabelFromListItem = function(element) {
1126 var parentNode = element.parentNode;
1127 while (parentNode &&
1128 parentNode.nodeType === Node.ELEMENT_NODE &&
1129 !__gCrWeb.autofill.hasTagName(parentNode, 'li')) {
1130 parentNode = parentNode.parentNode;
1133 if (parentNode && __gCrWeb.autofill.hasTagName(parentNode, 'li'))
1134 return __gCrWeb.autofill.findChildText(parentNode);
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.
1154 __gCrWeb.autofill.inferLabelFromTableColumn = function(element) {
1159 var parentNode = element.parentNode;
1160 while (parentNode &&
1161 parentNode.nodeType === Node.ELEMENT_NODE &&
1162 !__gCrWeb.autofill.hasTagName(parentNode, 'td')) {
1163 parentNode = parentNode.parentNode;
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);
1179 previous = previous.previousSibling;
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.
1202 __gCrWeb.autofill.inferLabelFromTableRow = function(element) {
1207 var cell = element.parentNode;
1209 if (cell.nodeType === Node.ELEMENT_NODE &&
1210 __gCrWeb.autofill.hasTagName(cell, 'td')) {
1213 cell = cell.parentNode;
1216 // Not in a cell - bail out.
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;
1233 cellIterator = cellIterator.previousSibling;
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;
1243 cellIterator = cellIterator.nextSibling;
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;
1262 // Now find the previous row.
1263 var rowIt = parentNode.previousSibling;
1265 if (rowIt.nodeType === Node.ELEMENT_NODE &&
1266 __gCrWeb.autofill.hasTagName(parentNode, 'tr')) {
1269 rowIt = rowIt.previousSibling;
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.
1275 var matchingCell = null;
1276 var prevRowCount = 0;
1277 var prevRowIt = rowIt.firstChild;
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;
1288 prevRowCount += span;
1291 prevRowIt = prevRowIt.nextSibling;
1293 if (cellCount === prevRowCount && matchingCell) {
1294 var inferredLabel = __gCrWeb.autofill.findChildText(matchingCell);
1295 if (inferredLabel.length > 0) {
1296 return inferredLabel;
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);
1310 previous = previous.previousSibling;
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.
1326 __gCrWeb.autofill.isTraversableContainerElement = function(node) {
1327 if (node.nodeType !== Node.ELEMENT_NODE) {
1331 var tagName = /** @type {Element} */(node).tagName;
1332 return (tagName === "DD" ||
1333 tagName === "DIV" ||
1334 tagName === "FIELDSET" ||
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.
1356 __gCrWeb.autofill.inferLabelFromDivTable = function(element) {
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) {
1371 __gCrWeb.autofill.findChildTextWithIgnoreList(node, divsToSkip);
1373 inferredLabel = __gCrWeb.autofill.findChildText(node);
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) {
1381 for (var i = 0; i < divsToSkip.length; ++i) {
1382 if (node === divsToSkip[i]) {
1388 divsToSkip.push(node);
1393 lookingForParent = false;
1394 } else if (!lookingForParent &&
1395 __gCrWeb.autofill.hasTagName(node, 'label')) {
1396 if (!node.control) {
1397 inferredLabel = __gCrWeb.autofill.findChildText(node);
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.
1405 if (!node.previousSibling) {
1406 // If there are no more siblings, continue walking up the tree.
1407 lookingForParent = true;
1410 if (lookingForParent) {
1411 node = node.parentNode;
1413 node = node.previousSibling;
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.
1434 __gCrWeb.autofill.inferLabelFromDefinitionList = function(element) {
1439 var parentNode = element.parentNode;
1440 while (parentNode &&
1441 parentNode.nodeType === Node.ELEMENT_NODE &&
1442 !__gCrWeb.autofill.hasTagName(parentNode, 'dd')) {
1443 parentNode = parentNode.parentNode;
1446 if (!parentNode || !__gCrWeb.autofill.hasTagName(parentNode, 'dd')) {
1450 // Skip by any intervening text nodes.
1451 var previous = parentNode.previousSibling;
1452 while (previous && previous.nodeType === Node.TEXT_NODE) {
1453 previous = previous.previousSibling;
1456 if (!previous || !__gCrWeb.autofill.hasTagName(previous, 'dt'))
1459 return __gCrWeb.autofill.findChildText(previous);
1463 * Returns the element type for all ancestor nodes in CAPS, starting with the
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.
1474 __gCrWeb.autofill.ancestorTagNames = function(element) {
1476 var parentNode = element.parentNode;
1477 while (parentNode) {
1478 if (parentNode.nodeType === Node.ELEMENT_NODE)
1479 tagNames.push(parentNode.tagName);
1480 parentNode = parentNode.parentNode;
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.
1496 __gCrWeb.autofill.inferLabelForElement = function(element) {
1498 if (__gCrWeb.autofill.isCheckableElement(element)) {
1499 inferredLabel = __gCrWeb.autofill.inferLabelFromNext(element);
1500 if (inferredLabel.length > 0) {
1501 return inferredLabel;
1505 inferredLabel = __gCrWeb.autofill.inferLabelFromPrevious(element);
1506 if (inferredLabel.length > 0) {
1507 return inferredLabel;
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;
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) {
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") {
1541 if (inferredLabel.length > 0) {
1546 return inferredLabel;
1550 * Fills |field| data with the values of the <option> elements present in
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
1561 * @param {Object} field A field that will contain the extracted option
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']);
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.
1595 __gCrWeb.autofill.fillFormField = function(data, field) {
1597 if (!data['value'] || data['value'].length === 0) {
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;
1612 sanitizedValue = data['value'].substr(0, maxLength);
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);
1623 if (__gCrWeb.autofill.isCheckableElement(field)) {
1624 __gCrWeb.common.setInputElementChecked(data['is_checked'], field, true);
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.
1639 __gCrWeb.autofill.isTextInput = function(element) {
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.
1656 __gCrWeb.autofill.isSelectElement = function(element) {
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.
1673 __gCrWeb.autofill.isTextAreaElement = function(element) {
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.
1690 __gCrWeb.autofill.isCheckableElement = function(element) {
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.
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|.
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|.
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.
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)) {
1758 autofillableElements.push(element);
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.
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
1798 __gCrWeb.autofill.webFormControlElementToFormField = function(
1799 element, extractMask, field) {
1800 if (!field || !element) {
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
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;
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';
1821 var role_attribute = element.getAttribute('role');
1822 if (role_attribute && role_attribute.toLowerCase() == 'presentation') {
1823 field['role'] = __gCrWeb.autofill.ROLE_ATTRIBUTE_PRESENTATION;
1826 if (!__gCrWeb.autofill.isAutofillableElement(element)) {
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);
1839 if (__gCrWeb.autofill.isAutofillableInputElement(element)) {
1840 if (__gCrWeb.autofill.isTextInput(element)) {
1841 field['max_length'] = element.maxLength;
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);
1850 if (!extractMask & __gCrWeb.autofill.EXTRACT_MASK_VALUE) {
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;
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);
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.
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)) {
1899 var elementName = __gCrWeb['common'].nameForAutofill(element);
1900 var value = formData[elementName];
1902 element.placeholder = value;