| Index: ios/web/web_state/js/resources/common.js
|
| diff --git a/ios/web/web_state/js/resources/common.js b/ios/web/web_state/js/resources/common.js
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..7cf5baa8f76f54b1d0dbc4f4dda8fca945c06400
|
| --- /dev/null
|
| +++ b/ios/web/web_state/js/resources/common.js
|
| @@ -0,0 +1,579 @@
|
| +// Copyright 2013 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +// This file provides common methods that can be shared by other JavaScripts.
|
| +
|
| +/**
|
| + * Namespace for this file. It depends on |__gCrWeb| having already been
|
| + * injected. String 'common' is used in |__gCrWeb['common']| as it needs to be
|
| + * accessed in Objective-C code.
|
| + */
|
| +__gCrWeb['common'] = {};
|
| +
|
| +/* Beginning of anonymous object. */
|
| +new function() {
|
| + // JSON safe object to protect against custom implementation of Object.toJSON
|
| + // in host pages.
|
| + __gCrWeb['common'].JSONSafeObject = function JSONSafeObject() {
|
| + };
|
| +
|
| + /**
|
| + * Protect against custom implementation of Object.toJSON in host pages.
|
| + */
|
| + __gCrWeb['common'].JSONSafeObject.prototype.toJSON = null;
|
| +
|
| + /**
|
| + * Retain the original JSON.stringify method where possible to reduce the
|
| + * impact of sites overriding it
|
| + */
|
| + __gCrWeb.common.JSONStringify = JSON.stringify;
|
| +
|
| + /**
|
| + * Prefix used in references to form elements that have no 'id' or 'name'
|
| + */
|
| + __gCrWeb.common.kNamelessFormIDPrefix = 'gChrome~';
|
| +
|
| + /**
|
| + * Tests an element's visiblity. This test is expensive so should be used
|
| + * sparingly.
|
| + * @param {Element} element A DOM element.
|
| + * @return {boolean} true if the |element| is currently part of the visible
|
| + * DOM.
|
| + */
|
| + __gCrWeb.common.isElementVisible = function(element) {
|
| + while (element !== document) {
|
| + var style = window.getComputedStyle(element);
|
| + if (style.display === 'none' || style.visibility === 'hidden') {
|
| + return false;
|
| + }
|
| + // Move up the tree and test again.
|
| + element = element.parentNode;
|
| + }
|
| + // Test reached the top of the DOM without finding a concealed ancestor.
|
| + return true;
|
| + };
|
| +
|
| + /**
|
| + * Based on Element::isFormControlElement() (WebKit)
|
| + * @param {Element} element A DOM element.
|
| + * @return {boolean} true if the |element| is a form control element.
|
| + */
|
| + __gCrWeb.common.isFormControlElement = function(element) {
|
| + var tagName = element.tagName;
|
| + return (tagName === 'INPUT' ||
|
| + tagName === 'SELECT' ||
|
| + tagName === 'TEXTAREA');
|
| + };
|
| +
|
| + /**
|
| + * Detects focusable elements.
|
| + * @param {Element} element A DOM element.
|
| + * @return {boolean} true if the |element| is focusable.
|
| + */
|
| + __gCrWeb.common.isFocusable = function(element) {
|
| + // When the disabled or hidden attributes are present, controls do not
|
| + // receive focus.
|
| + if (element.hasAttribute('disabled') || element.hasAttribute('hidden'))
|
| + return false;
|
| + return __gCrWeb.common.isFormControlElement(element);
|
| + };
|
| +
|
| + /**
|
| + * Returns an array of control elements in a form.
|
| + *
|
| + * This method is based on the logic in method
|
| + * void WebFormElement::getFormControlElements(
|
| + * WebVector<WebFormControlElement>&) const
|
| + * in chromium/src/third_party/WebKit/Source/WebKit/chromium/src/
|
| + * WebFormElement.cpp.
|
| + *
|
| + * @param {Element} form A form element for which the control elements are
|
| + * returned.
|
| + * @return {Array.<Element>}
|
| + */
|
| + __gCrWeb.common.getFormControlElements = function(form) {
|
| + if (!form) {
|
| + return [];
|
| + }
|
| + var results = [];
|
| + // Get input and select elements from form.elements.
|
| + // TODO(chenyu): according to
|
| + // http://www.w3.org/TR/2011/WD-html5-20110525/forms.html, form.elements are
|
| + // the "listed elements whose form owner is the form element, with the
|
| + // exception of input elements whose type attribute is in the Image Button
|
| + // state, which must, for historical reasons, be excluded from this
|
| + // particular collection." In WebFormElement.cpp, this method is implemented
|
| + // by returning elements in form's associated elements that have tag 'INPUT'
|
| + // or 'SELECT'. Check if input Image Buttons are excluded in that
|
| + // implementation. Note for Autofill, as input Image Button is not
|
| + // considered as autofillable elements, there is no impact on Autofill
|
| + // feature.
|
| + var elements = form.elements;
|
| + for (var i = 0; i < elements.length; i++) {
|
| + if (__gCrWeb.common.isFormControlElement(elements[i])) {
|
| + results.push(elements[i]);
|
| + }
|
| + }
|
| + return results;
|
| + };
|
| +
|
| + /**
|
| + * Returns true if an element can be autocompleted.
|
| + *
|
| + * This method aims to provide the same logic as method
|
| + * bool autoComplete() const
|
| + * in chromium/src/third_party/WebKit/Source/WebKit/chromium/src/
|
| + * WebFormElement.cpp.
|
| + *
|
| + * @param {Element} element An element to check if it can be autocompleted.
|
| + * @return {boolean} true if element can be autocompleted.
|
| + */
|
| + __gCrWeb.common.autoComplete = function(element) {
|
| + if (!element) {
|
| + return false;
|
| + }
|
| + if (__gCrWeb.common.getLowerCaseAttribute(
|
| + element, 'autocomplete') == 'off') {
|
| + return false;
|
| + }
|
| + if (__gCrWeb.common.getLowerCaseAttribute(
|
| + element.form, 'autocomplete') == 'off') {
|
| + return false;
|
| + }
|
| + return true;
|
| + };
|
| +
|
| + /**
|
| + * Returns if an element is a text field.
|
| + * This returns true for all of textfield-looking types such as text,
|
| + * password, search, email, url, and number.
|
| + *
|
| + * This method aims to provide the same logic as method
|
| + * bool WebInputElement::isTextField() const
|
| + * in chromium/src/third_party/WebKit/Source/WebKit/chromium/src/
|
| + * WebInputElement.cpp, where this information is from
|
| + * bool HTMLInputElement::isTextField() const
|
| + * {
|
| + * return m_inputType->isTextField();
|
| + * }
|
| + * (chromium/src/third_party/WebKit/Source/WebCore/html/HTMLInputElement.cpp)
|
| + *
|
| + * The implementation here is based on the following:
|
| + *
|
| + * - Method bool InputType::isTextField() defaults to be false and it is
|
| + * override to return true only in HTMLInputElement's subclass
|
| + * TextFieldInputType (chromium/src/third_party/WebKit/Source/WebCore/html/
|
| + * TextFieldInputType.h).
|
| + *
|
| + * - The implementation here considers all the subclasses of
|
| + * TextFieldInputType: NumberInputType and BaseTextInputType, which has
|
| + * subclasses EmailInputType, PasswordInputType, SearchInputType,
|
| + * TelephoneInputType, TextInputType, URLInputType. (All these classes are
|
| + * defined in chromium/src/third_party/WebKit/Source/WebCore/html/)
|
| + *
|
| + * @param {Element} element An element to examine if it is a text field.
|
| + * @return {boolean} true if element has type=text.
|
| + */
|
| + __gCrWeb.common.isTextField = function(element) {
|
| + if (!element) {
|
| + return false;
|
| + }
|
| + if (element.type === 'hidden') {
|
| + return false;
|
| + }
|
| + return element.type === 'text' ||
|
| + element.type === 'email' ||
|
| + element.type === 'password' ||
|
| + element.type === 'search' ||
|
| + element.type === 'tel' ||
|
| + element.type === 'url' ||
|
| + element.type === 'number';
|
| + };
|
| +
|
| + /**
|
| + * Sets the checked value of an input and dispatches an change event if
|
| + * |shouldSendChangeEvent|.
|
| + *
|
| + * This is a simplified version of the implementation of
|
| + *
|
| + * void setChecked(bool nowChecked, TextFieldEventBehavior eventBehavior)
|
| + *
|
| + * in chromium/src/third_party/WebKit/Source/WebKit/chromium/src/
|
| + * WebInputElement.cpp, which calls
|
| + * void HTMLInputElement::setChecked(
|
| + * bool nowChecked, TextFieldEventBehavior eventBehavior)
|
| + * in chromium/src/third_party/WebKit/Source/core/html/HTMLInputElement.cpp.
|
| + *
|
| + * @param {boolean} nowChecked The new checked value of the input element.
|
| + * @param {Element} input The input element of which the value is set.
|
| + * @param {boolean} shouldSendChangeEvent Whether a change event should be
|
| + * dispatched.
|
| + */
|
| + __gCrWeb.common.setInputElementChecked = function(
|
| + nowChecked, input, shouldSendChangeEvent) {
|
| + var checkedChanged = input.checked !== nowChecked;
|
| + input.checked = nowChecked;
|
| + if (checkedChanged) {
|
| + __gCrWeb.common.createAndDispatchHTMLEvent(input, 'change', true, false);
|
| + }
|
| + };
|
| +
|
| + /**
|
| + * Sets the value of an input and dispatches an change event if
|
| + * |shouldSendChangeEvent|.
|
| + *
|
| + * It is based on the logic in
|
| + *
|
| + * void setValue(const WebString&, bool sendChangeEvent = false)
|
| + *
|
| + * in chromium/src/third_party/WebKit/Source/WebKit/chromium/src/
|
| + * WebInputElement.cpp, which calls
|
| + * void setValue(const String& value, TextFieldEventBehavior eventBehavior)
|
| + * in chromium/src/third_party/WebKit/Source/core/html/HTMLInputElement.cpp.
|
| + *
|
| + * @param {string} value The value the input element will be set.
|
| + * @param {Element} input The input element of which the value is set.
|
| + * @param {boolean} shouldSendChangeEvent Whether a change event should be
|
| + * dispatched.
|
| + */
|
| + __gCrWeb.common.setInputElementValue = function(
|
| + value, input, shouldSendChangeEvent) {
|
| + // In HTMLInputElement.cpp there is a check on canSetValue(value), which
|
| + // returns false only for file input. As file input is not relevant for
|
| + // autofill and this method is only used for autofill for now, there is no
|
| + // such check in this implementation.
|
| + var sanitizedValue = __gCrWeb.common.sanitizeValueForInputElement(
|
| + value, input);
|
| + var valueChanged = sanitizedValue !== input.value;
|
| + input.value = sanitizedValue;
|
| + if (valueChanged) {
|
| + __gCrWeb.common.createAndDispatchHTMLEvent(input, 'change', true, false);
|
| + }
|
| + };
|
| +
|
| + /**
|
| + * Returns a sanitized value of proposedValue for a given input element type.
|
| + * The logic is based on
|
| + *
|
| + * String sanitizeValue(const String&) const
|
| + *
|
| + * in chromium/src/third_party/WebKit/Source/core/html/InputType.h
|
| + *
|
| + * @param {string} proposedValue The proposed value.
|
| + * @param {Element} element The element for which the proposedValue is to be
|
| + * sanitized.
|
| + * @return {string} The sanitized value.
|
| + */
|
| + __gCrWeb.common.sanitizeValueForInputElement = function(
|
| + proposedValue, element) {
|
| + if (!proposedValue) {
|
| + return null;
|
| + }
|
| +
|
| + // Method HTMLInputElement::sanitizeValue() calls InputType::sanitizeValue()
|
| + // (chromium/src/third_party/WebKit/Source/core/html/InputType.cpp) for
|
| + // non-null proposedValue. InputType::sanitizeValue() returns the original
|
| + // proposedValue by default and it is overridden in classes
|
| + // BaseDateAndTimeInputType, ColorInputType, RangeInputType and
|
| + // TextFieldInputType (all are in
|
| + // chromium/src/third_party/WebKit/Source/core/html/). Currently only
|
| + // TextFieldInputType is relevant and sanitizeValue() for other types of
|
| + // input elements has not been implemented.
|
| + if (__gCrWeb.common.isTextField(element)) {
|
| + return __gCrWeb.common.sanitizeValueForTextFieldInputType(
|
| + proposedValue, element);
|
| + }
|
| + return proposedValue;
|
| + };
|
| +
|
| + /**
|
| + * Returns a sanitized value for a text field.
|
| + *
|
| + * The logic is based on |String sanitizeValue(const String&)|
|
| + * in chromium/src/third_party/WebKit/Source/core/html/TextFieldInputType.h
|
| + * Note this method is overridden in EmailInputType and NumberInputType.
|
| + *
|
| + * @param {string} proposedValue The proposed value.
|
| + * @param {Element} element The element for which the proposedValue is to be
|
| + * sanitized.
|
| + * @return {string} The sanitized value.
|
| + */
|
| + __gCrWeb.common.sanitizeValueForTextFieldInputType = function(
|
| + proposedValue, element) {
|
| + var textFieldElementType = element.type;
|
| + if (textFieldElementType === 'email') {
|
| + return __gCrWeb.common.sanitizeValueForEmailInputType(
|
| + proposedValue, element);
|
| + } else if (textFieldElementType === 'number') {
|
| + return __gCrWeb.common.sanitizeValueForNumberInputType(proposedValue);
|
| + }
|
| + var valueWithLineBreakRemoved = proposedValue.replace(/(\r\n|\n|\r)/gm, '');
|
| + // TODO(chenyu): Should we also implement numCharactersInGraphemeClusters()
|
| + // in chromium/src/third_party/WebKit/Source/core/platform/text/
|
| + // TextBreakIterator.cpp and call it here when computing newLength?
|
| + // Different from the implementation in TextFieldInputType.h, where a limit
|
| + // on the text length is considered due to
|
| + // https://bugs.webkit.org/show_bug.cgi?id=14536, no such limit is
|
| + // considered here for now.
|
| + var newLength = valueWithLineBreakRemoved.length;
|
| + // This logic is from method String limitLength() in TextFieldInputType.h
|
| + for (var i = 0; i < newLength; ++i) {
|
| + var current = valueWithLineBreakRemoved[i];
|
| + if (current < ' ' && current != '\t') {
|
| + newLength = i;
|
| + break;
|
| + }
|
| + }
|
| + return valueWithLineBreakRemoved.substring(0, newLength);
|
| + };
|
| +
|
| + /**
|
| + * Returns the sanitized value for an email input.
|
| + *
|
| + * The logic is based on
|
| + *
|
| + * String EmailInputType::sanitizeValue(const String& proposedValue) const
|
| + *
|
| + * in chromium/src/third_party/WebKit/Source/core/html/EmailInputType.cpp
|
| + *
|
| + * @param {string} proposedValue The proposed value.
|
| + * @param {Element} element The element for which the proposedValue is to be
|
| + * sanitized.
|
| + * @return {string} The sanitized value.
|
| + */
|
| + __gCrWeb.common.sanitizeValueForEmailInputType = function(
|
| + proposedValue, element) {
|
| + var valueWithLineBreakRemoved = proposedValue.replace(/(\r\n|\n\r)/gm, '');
|
| +
|
| + if (!element.multiple) {
|
| + return __gCrWeb.common.trim(proposedValue);
|
| + }
|
| + var addresses = valueWithLineBreakRemoved.split(',');
|
| + for (var i = 0; i < addresses.length; ++i) {
|
| + addresses[i] = __gCrWeb.common.trim(addresses[i]);
|
| + }
|
| + return addresses.join(',');
|
| + };
|
| +
|
| + /**
|
| + * Returns the sanitized value of a proposed value for a number input.
|
| + *
|
| + * The logic is based on
|
| + *
|
| + * String NumberInputType::sanitizeValue(const String& proposedValue)
|
| + * const
|
| + *
|
| + * in chromium/src/third_party/WebKit/Source/core/html/NumberInputType.cpp
|
| + *
|
| + * Note in this implementation method Number() is used in the place of method
|
| + * parseToDoubleForNumberType() called in NumberInputType.cpp.
|
| + *
|
| + * @param {string} proposedValue The proposed value.
|
| + * @return {string} The sanitized value.
|
| + */
|
| + __gCrWeb.common.sanitizeValueForNumberInputType = function(proposedValue) {
|
| + var sanitizedValue = Number(proposedValue);
|
| + if (isNaN(sanitizedValue)) {
|
| + return '';
|
| + }
|
| + return sanitizedValue;
|
| + };
|
| +
|
| + /**
|
| + * Trims any whitespace from the start and end of a string.
|
| + * Used in preference to String.prototype.trim as this can be overridden by
|
| + * sites.
|
| + *
|
| + * @param {string} str The string to be trimmed.
|
| + * @return {string} The string after trimming.
|
| + */
|
| + __gCrWeb.common.trim = function(str) {
|
| + return str.replace(/^\s+|\s+$/g, '');
|
| + };
|
| +
|
| + /**
|
| + * Returns the name that should be used for the specified |element| when
|
| + * storing Autofill data. Various attributes are used to attempt to identify
|
| + * the element, beginning with 'name' and 'id' attributes. Providing a
|
| + * uniquely reversible identifier for any element is a non-trivial problem;
|
| + * this solution attempts to satisfy the majority of cases.
|
| + *
|
| + * It aims to provide the logic in
|
| + * WebString nameForAutofill() const;
|
| + * in chromium/src/third_party/WebKit/Source/WebKit/chromium/public/
|
| + * WebFormControlElement.h
|
| + *
|
| + * @param {Element} element An element of which the name for Autofill will be
|
| + * returned.
|
| + * @return {string} the name for Autofill.
|
| + */
|
| + __gCrWeb.common.nameForAutofill = function(element) {
|
| + if (!element) {
|
| + return '';
|
| + }
|
| + var trimmedName = element.name;
|
| + if (trimmedName) {
|
| + trimmedName = __gCrWeb.common.trim(trimmedName);
|
| + if (trimmedName.length > 0) {
|
| + return trimmedName;
|
| + }
|
| + }
|
| + trimmedName = element.getAttribute('id');
|
| + if (trimmedName) {
|
| + return __gCrWeb.common.trim(trimmedName);
|
| + }
|
| + trimmedName = element.getAttribute('autocomplete');
|
| + if (trimmedName && trimmedName !== 'off') {
|
| + return __gCrWeb.common.trim(trimmedName);
|
| + }
|
| + trimmedName = element.getAttribute('placeholder');
|
| + if (trimmedName) {
|
| + return __gCrWeb.common.trim(trimmedName);
|
| + }
|
| + return '';
|
| + };
|
| +
|
| + /**
|
| + * Acquires the specified DOM |attribute| from the DOM |element| and returns
|
| + * its lower-case value, or null if not present.
|
| + * @param {Element} element A DOM element.
|
| + * @param {string} attribute An attribute name.
|
| + * @return {?string} Lowercase value of DOM element or null if not present.
|
| + */
|
| + __gCrWeb.common.getLowerCaseAttribute = function(element, attribute) {
|
| + if (!element) {
|
| + return null;
|
| + }
|
| + var value = element.getAttribute(attribute);
|
| + if (value) {
|
| + return value.toLowerCase();
|
| + }
|
| + return null;
|
| + };
|
| +
|
| + /**
|
| + * Converts a relative URL into an absolute URL.
|
| + * @param {Object} doc Document.
|
| + * @param {string} relativeURL Relative URL.
|
| + * @return {string} Absolute URL.
|
| + */
|
| + __gCrWeb.common.absoluteURL = function(doc, relativeURL) {
|
| + // In the case of data: URL-based pages, relativeURL === absoluteURL.
|
| + if (doc.location.protocol === 'data:') {
|
| + return doc.location.href;
|
| + }
|
| + var urlNormalizer = doc['__gCrWebURLNormalizer'];
|
| + if (!urlNormalizer) {
|
| + urlNormalizer = doc.createElement('a');
|
| + doc['__gCrWebURLNormalizer'] = urlNormalizer;
|
| + }
|
| +
|
| + // Use the magical quality of the <a> element. It automatically converts
|
| + // relative URLs into absolute ones.
|
| + urlNormalizer.href = relativeURL;
|
| + return urlNormalizer.href;
|
| + };
|
| +
|
| + /**
|
| + * Extracts the webpage URL from the given URL by removing the query
|
| + * and the reference (aka fragment) from the URL.
|
| + * @param {string} url Web page URL.
|
| + * @return {string} Web page URL with query and reference removed.
|
| + */
|
| + __gCrWeb.common.removeQueryAndReferenceFromURL = function(url) {
|
| + var queryIndex = url.indexOf('?');
|
| + if (queryIndex != -1) {
|
| + return url.substring(0, queryIndex);
|
| + }
|
| +
|
| + var hashIndex = url.indexOf('#');
|
| + if (hashIndex != -1) {
|
| + return url.substring(0, hashIndex);
|
| + }
|
| + return url;
|
| + };
|
| +
|
| + /**
|
| + * Returns the form's |name| attribute if non-empty; otherwise the form's |id|
|
| + * attribute, or the index of the form (with prefix) in document.forms.
|
| + *
|
| + * It is partially based on the logic in
|
| + * const string16 GetFormIdentifier(const blink::WebFormElement& form)
|
| + * in chromium/src/components/autofill/renderer/form_autofill_util.h.
|
| + *
|
| + * @param {Element} form An element for which the identifier is returned.
|
| + * @return {string} a string that represents the element's identifier.
|
| + */
|
| + __gCrWeb.common.getFormIdentifier = function(form) {
|
| + if (!form)
|
| + return '';
|
| + var name = form.getAttribute('name');
|
| + if (name && name.length != 0) {
|
| + return name;
|
| + }
|
| + name = form.getAttribute('id');
|
| + if (name) {
|
| + return name;
|
| + }
|
| + // A form name must be supplied, because the element will later need to be
|
| + // identified from the name. A last resort is to take the index number of
|
| + // the form in document.forms. ids are not supposed to begin with digits (by
|
| + // HTML 4 spec) so this is unlikely to match a true id.
|
| + for (var idx = 0; idx != document.forms.length; idx++) {
|
| + if (document.forms[idx] == form) {
|
| + return __gCrWeb.common.kNamelessFormIDPrefix + idx;
|
| + }
|
| + }
|
| + return '';
|
| + };
|
| +
|
| + /**
|
| + * Returns the form element from an ID obtained from getFormIdentifier.
|
| + *
|
| + * This works on a 'best effort' basis since DOM changes can always change the
|
| + * actual element that the ID refers to.
|
| + *
|
| + * @param {string} name An ID string obtained via getFormIdentifier.
|
| + * @return {Element} The original form element, if it can be determined.
|
| + */
|
| + __gCrWeb.common.getFormElementFromIdentifier = function(name) {
|
| + // First attempt is from the name / id supplied.
|
| + var form = document.forms.namedItem(name);
|
| + if (form) {
|
| + return form;
|
| + }
|
| + // Second attempt is from the prefixed index position of the form in
|
| + // document.forms.
|
| + if (name.indexOf(__gCrWeb.common.kNamelessFormIDPrefix) == 0) {
|
| + var nameAsInteger = 0 |
|
| + name.substring(__gCrWeb.common.kNamelessFormIDPrefix.length);
|
| + if (__gCrWeb.common.kNamelessFormIDPrefix + nameAsInteger == name &&
|
| + nameAsInteger < document.forms.length) {
|
| + return document.forms[nameAsInteger];
|
| + }
|
| + }
|
| + return null;
|
| + };
|
| +
|
| + /**
|
| + * Creates and dispatches an HTML event.
|
| + *
|
| + * @param {Element} element The element for which an event is created.
|
| + * @param {string} type The type of the event.
|
| + * @param {boolean} bubbles A boolean indicating whether the event should
|
| + * bubble up through the event chain or not.
|
| + * @param {boolean} cancelable A boolean indicating whether the event can be
|
| + * canceled.
|
| + */
|
| + __gCrWeb.common.createAndDispatchHTMLEvent = function(
|
| + element, type, bubbles, cancelable) {
|
| + var changeEvent = element.ownerDocument.createEvent('HTMLEvents');
|
| + changeEvent.initEvent(type, bubbles, cancelable);
|
| +
|
| + // A timer is used to avoid reentering JavaScript evaluation.
|
| + window.setTimeout(function() {
|
| + element.dispatchEvent(changeEvent);
|
| + }, 0);
|
| + };
|
| +} // End of anonymous object
|
|
|