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

Side by Side Diff: ios/web/web_state/js/resources/common.js

Issue 788253002: Upstream injected JavaScript for the iOS web layer (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: bug reference Created 6 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « ios/web/web_state/js/resources/base.js ('k') | ios/web/web_state/js/resources/message.js » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // This file provides common methods that can be shared by other JavaScripts.
6
7 /**
8 * Namespace for this file. It depends on |__gCrWeb| having already been
9 * injected. String 'common' is used in |__gCrWeb['common']| as it needs to be
10 * accessed in Objective-C code.
11 */
12 __gCrWeb['common'] = {};
13
14 /* Beginning of anonymous object. */
15 new function() {
16 // JSON safe object to protect against custom implementation of Object.toJSON
17 // in host pages.
18 __gCrWeb['common'].JSONSafeObject = function JSONSafeObject() {
19 };
20
21 /**
22 * Protect against custom implementation of Object.toJSON in host pages.
23 */
24 __gCrWeb['common'].JSONSafeObject.prototype.toJSON = null;
25
26 /**
27 * Retain the original JSON.stringify method where possible to reduce the
28 * impact of sites overriding it
29 */
30 __gCrWeb.common.JSONStringify = JSON.stringify;
31
32 /**
33 * Prefix used in references to form elements that have no 'id' or 'name'
34 */
35 __gCrWeb.common.kNamelessFormIDPrefix = 'gChrome~';
36
37 /**
38 * Tests an element's visiblity. This test is expensive so should be used
39 * sparingly.
40 * @param {Element} element A DOM element.
41 * @return {boolean} true if the |element| is currently part of the visible
42 * DOM.
43 */
44 __gCrWeb.common.isElementVisible = function(element) {
45 while (element !== document) {
46 var style = window.getComputedStyle(element);
47 if (style.display === 'none' || style.visibility === 'hidden') {
48 return false;
49 }
50 // Move up the tree and test again.
51 element = element.parentNode;
52 }
53 // Test reached the top of the DOM without finding a concealed ancestor.
54 return true;
55 };
56
57 /**
58 * Based on Element::isFormControlElement() (WebKit)
59 * @param {Element} element A DOM element.
60 * @return {boolean} true if the |element| is a form control element.
61 */
62 __gCrWeb.common.isFormControlElement = function(element) {
63 var tagName = element.tagName;
64 return (tagName === 'INPUT' ||
65 tagName === 'SELECT' ||
66 tagName === 'TEXTAREA');
67 };
68
69 /**
70 * Detects focusable elements.
71 * @param {Element} element A DOM element.
72 * @return {boolean} true if the |element| is focusable.
73 */
74 __gCrWeb.common.isFocusable = function(element) {
75 // When the disabled or hidden attributes are present, controls do not
76 // receive focus.
77 if (element.hasAttribute('disabled') || element.hasAttribute('hidden'))
78 return false;
79 return __gCrWeb.common.isFormControlElement(element);
80 };
81
82 /**
83 * Returns an array of control elements in a form.
84 *
85 * This method is based on the logic in method
86 * void WebFormElement::getFormControlElements(
87 * WebVector<WebFormControlElement>&) const
88 * in chromium/src/third_party/WebKit/Source/WebKit/chromium/src/
89 * WebFormElement.cpp.
90 *
91 * @param {Element} form A form element for which the control elements are
92 * returned.
93 * @return {Array.<Element>}
94 */
95 __gCrWeb.common.getFormControlElements = function(form) {
96 if (!form) {
97 return [];
98 }
99 var results = [];
100 // Get input and select elements from form.elements.
101 // TODO(chenyu): according to
102 // http://www.w3.org/TR/2011/WD-html5-20110525/forms.html, form.elements are
103 // the "listed elements whose form owner is the form element, with the
104 // exception of input elements whose type attribute is in the Image Button
105 // state, which must, for historical reasons, be excluded from this
106 // particular collection." In WebFormElement.cpp, this method is implemented
107 // by returning elements in form's associated elements that have tag 'INPUT'
108 // or 'SELECT'. Check if input Image Buttons are excluded in that
109 // implementation. Note for Autofill, as input Image Button is not
110 // considered as autofillable elements, there is no impact on Autofill
111 // feature.
112 var elements = form.elements;
113 for (var i = 0; i < elements.length; i++) {
114 if (__gCrWeb.common.isFormControlElement(elements[i])) {
115 results.push(elements[i]);
116 }
117 }
118 return results;
119 };
120
121 /**
122 * Returns true if an element can be autocompleted.
123 *
124 * This method aims to provide the same logic as method
125 * bool autoComplete() const
126 * in chromium/src/third_party/WebKit/Source/WebKit/chromium/src/
127 * WebFormElement.cpp.
128 *
129 * @param {Element} element An element to check if it can be autocompleted.
130 * @return {boolean} true if element can be autocompleted.
131 */
132 __gCrWeb.common.autoComplete = function(element) {
133 if (!element) {
134 return false;
135 }
136 if (__gCrWeb.common.getLowerCaseAttribute(
137 element, 'autocomplete') == 'off') {
138 return false;
139 }
140 if (__gCrWeb.common.getLowerCaseAttribute(
141 element.form, 'autocomplete') == 'off') {
142 return false;
143 }
144 return true;
145 };
146
147 /**
148 * Returns if an element is a text field.
149 * This returns true for all of textfield-looking types such as text,
150 * password, search, email, url, and number.
151 *
152 * This method aims to provide the same logic as method
153 * bool WebInputElement::isTextField() const
154 * in chromium/src/third_party/WebKit/Source/WebKit/chromium/src/
155 * WebInputElement.cpp, where this information is from
156 * bool HTMLInputElement::isTextField() const
157 * {
158 * return m_inputType->isTextField();
159 * }
160 * (chromium/src/third_party/WebKit/Source/WebCore/html/HTMLInputElement.cpp)
161 *
162 * The implementation here is based on the following:
163 *
164 * - Method bool InputType::isTextField() defaults to be false and it is
165 * override to return true only in HTMLInputElement's subclass
166 * TextFieldInputType (chromium/src/third_party/WebKit/Source/WebCore/html/
167 * TextFieldInputType.h).
168 *
169 * - The implementation here considers all the subclasses of
170 * TextFieldInputType: NumberInputType and BaseTextInputType, which has
171 * subclasses EmailInputType, PasswordInputType, SearchInputType,
172 * TelephoneInputType, TextInputType, URLInputType. (All these classes are
173 * defined in chromium/src/third_party/WebKit/Source/WebCore/html/)
174 *
175 * @param {Element} element An element to examine if it is a text field.
176 * @return {boolean} true if element has type=text.
177 */
178 __gCrWeb.common.isTextField = function(element) {
179 if (!element) {
180 return false;
181 }
182 if (element.type === 'hidden') {
183 return false;
184 }
185 return element.type === 'text' ||
186 element.type === 'email' ||
187 element.type === 'password' ||
188 element.type === 'search' ||
189 element.type === 'tel' ||
190 element.type === 'url' ||
191 element.type === 'number';
192 };
193
194 /**
195 * Sets the checked value of an input and dispatches an change event if
196 * |shouldSendChangeEvent|.
197 *
198 * This is a simplified version of the implementation of
199 *
200 * void setChecked(bool nowChecked, TextFieldEventBehavior eventBehavior)
201 *
202 * in chromium/src/third_party/WebKit/Source/WebKit/chromium/src/
203 * WebInputElement.cpp, which calls
204 * void HTMLInputElement::setChecked(
205 * bool nowChecked, TextFieldEventBehavior eventBehavior)
206 * in chromium/src/third_party/WebKit/Source/core/html/HTMLInputElement.cpp.
207 *
208 * @param {boolean} nowChecked The new checked value of the input element.
209 * @param {Element} input The input element of which the value is set.
210 * @param {boolean} shouldSendChangeEvent Whether a change event should be
211 * dispatched.
212 */
213 __gCrWeb.common.setInputElementChecked = function(
214 nowChecked, input, shouldSendChangeEvent) {
215 var checkedChanged = input.checked !== nowChecked;
216 input.checked = nowChecked;
217 if (checkedChanged) {
218 __gCrWeb.common.createAndDispatchHTMLEvent(input, 'change', true, false);
219 }
220 };
221
222 /**
223 * Sets the value of an input and dispatches an change event if
224 * |shouldSendChangeEvent|.
225 *
226 * It is based on the logic in
227 *
228 * void setValue(const WebString&, bool sendChangeEvent = false)
229 *
230 * in chromium/src/third_party/WebKit/Source/WebKit/chromium/src/
231 * WebInputElement.cpp, which calls
232 * void setValue(const String& value, TextFieldEventBehavior eventBehavior)
233 * in chromium/src/third_party/WebKit/Source/core/html/HTMLInputElement.cpp.
234 *
235 * @param {string} value The value the input element will be set.
236 * @param {Element} input The input element of which the value is set.
237 * @param {boolean} shouldSendChangeEvent Whether a change event should be
238 * dispatched.
239 */
240 __gCrWeb.common.setInputElementValue = function(
241 value, input, shouldSendChangeEvent) {
242 // In HTMLInputElement.cpp there is a check on canSetValue(value), which
243 // returns false only for file input. As file input is not relevant for
244 // autofill and this method is only used for autofill for now, there is no
245 // such check in this implementation.
246 var sanitizedValue = __gCrWeb.common.sanitizeValueForInputElement(
247 value, input);
248 var valueChanged = sanitizedValue !== input.value;
249 input.value = sanitizedValue;
250 if (valueChanged) {
251 __gCrWeb.common.createAndDispatchHTMLEvent(input, 'change', true, false);
252 }
253 };
254
255 /**
256 * Returns a sanitized value of proposedValue for a given input element type.
257 * The logic is based on
258 *
259 * String sanitizeValue(const String&) const
260 *
261 * in chromium/src/third_party/WebKit/Source/core/html/InputType.h
262 *
263 * @param {string} proposedValue The proposed value.
264 * @param {Element} element The element for which the proposedValue is to be
265 * sanitized.
266 * @return {string} The sanitized value.
267 */
268 __gCrWeb.common.sanitizeValueForInputElement = function(
269 proposedValue, element) {
270 if (!proposedValue) {
271 return null;
272 }
273
274 // Method HTMLInputElement::sanitizeValue() calls InputType::sanitizeValue()
275 // (chromium/src/third_party/WebKit/Source/core/html/InputType.cpp) for
276 // non-null proposedValue. InputType::sanitizeValue() returns the original
277 // proposedValue by default and it is overridden in classes
278 // BaseDateAndTimeInputType, ColorInputType, RangeInputType and
279 // TextFieldInputType (all are in
280 // chromium/src/third_party/WebKit/Source/core/html/). Currently only
281 // TextFieldInputType is relevant and sanitizeValue() for other types of
282 // input elements has not been implemented.
283 if (__gCrWeb.common.isTextField(element)) {
284 return __gCrWeb.common.sanitizeValueForTextFieldInputType(
285 proposedValue, element);
286 }
287 return proposedValue;
288 };
289
290 /**
291 * Returns a sanitized value for a text field.
292 *
293 * The logic is based on |String sanitizeValue(const String&)|
294 * in chromium/src/third_party/WebKit/Source/core/html/TextFieldInputType.h
295 * Note this method is overridden in EmailInputType and NumberInputType.
296 *
297 * @param {string} proposedValue The proposed value.
298 * @param {Element} element The element for which the proposedValue is to be
299 * sanitized.
300 * @return {string} The sanitized value.
301 */
302 __gCrWeb.common.sanitizeValueForTextFieldInputType = function(
303 proposedValue, element) {
304 var textFieldElementType = element.type;
305 if (textFieldElementType === 'email') {
306 return __gCrWeb.common.sanitizeValueForEmailInputType(
307 proposedValue, element);
308 } else if (textFieldElementType === 'number') {
309 return __gCrWeb.common.sanitizeValueForNumberInputType(proposedValue);
310 }
311 var valueWithLineBreakRemoved = proposedValue.replace(/(\r\n|\n|\r)/gm, '');
312 // TODO(chenyu): Should we also implement numCharactersInGraphemeClusters()
313 // in chromium/src/third_party/WebKit/Source/core/platform/text/
314 // TextBreakIterator.cpp and call it here when computing newLength?
315 // Different from the implementation in TextFieldInputType.h, where a limit
316 // on the text length is considered due to
317 // https://bugs.webkit.org/show_bug.cgi?id=14536, no such limit is
318 // considered here for now.
319 var newLength = valueWithLineBreakRemoved.length;
320 // This logic is from method String limitLength() in TextFieldInputType.h
321 for (var i = 0; i < newLength; ++i) {
322 var current = valueWithLineBreakRemoved[i];
323 if (current < ' ' && current != '\t') {
324 newLength = i;
325 break;
326 }
327 }
328 return valueWithLineBreakRemoved.substring(0, newLength);
329 };
330
331 /**
332 * Returns the sanitized value for an email input.
333 *
334 * The logic is based on
335 *
336 * String EmailInputType::sanitizeValue(const String& proposedValue) const
337 *
338 * in chromium/src/third_party/WebKit/Source/core/html/EmailInputType.cpp
339 *
340 * @param {string} proposedValue The proposed value.
341 * @param {Element} element The element for which the proposedValue is to be
342 * sanitized.
343 * @return {string} The sanitized value.
344 */
345 __gCrWeb.common.sanitizeValueForEmailInputType = function(
346 proposedValue, element) {
347 var valueWithLineBreakRemoved = proposedValue.replace(/(\r\n|\n\r)/gm, '');
348
349 if (!element.multiple) {
350 return __gCrWeb.common.trim(proposedValue);
351 }
352 var addresses = valueWithLineBreakRemoved.split(',');
353 for (var i = 0; i < addresses.length; ++i) {
354 addresses[i] = __gCrWeb.common.trim(addresses[i]);
355 }
356 return addresses.join(',');
357 };
358
359 /**
360 * Returns the sanitized value of a proposed value for a number input.
361 *
362 * The logic is based on
363 *
364 * String NumberInputType::sanitizeValue(const String& proposedValue)
365 * const
366 *
367 * in chromium/src/third_party/WebKit/Source/core/html/NumberInputType.cpp
368 *
369 * Note in this implementation method Number() is used in the place of method
370 * parseToDoubleForNumberType() called in NumberInputType.cpp.
371 *
372 * @param {string} proposedValue The proposed value.
373 * @return {string} The sanitized value.
374 */
375 __gCrWeb.common.sanitizeValueForNumberInputType = function(proposedValue) {
376 var sanitizedValue = Number(proposedValue);
377 if (isNaN(sanitizedValue)) {
378 return '';
379 }
380 return sanitizedValue;
381 };
382
383 /**
384 * Trims any whitespace from the start and end of a string.
385 * Used in preference to String.prototype.trim as this can be overridden by
386 * sites.
387 *
388 * @param {string} str The string to be trimmed.
389 * @return {string} The string after trimming.
390 */
391 __gCrWeb.common.trim = function(str) {
392 return str.replace(/^\s+|\s+$/g, '');
393 };
394
395 /**
396 * Returns the name that should be used for the specified |element| when
397 * storing Autofill data. Various attributes are used to attempt to identify
398 * the element, beginning with 'name' and 'id' attributes. Providing a
399 * uniquely reversible identifier for any element is a non-trivial problem;
400 * this solution attempts to satisfy the majority of cases.
401 *
402 * It aims to provide the logic in
403 * WebString nameForAutofill() const;
404 * in chromium/src/third_party/WebKit/Source/WebKit/chromium/public/
405 * WebFormControlElement.h
406 *
407 * @param {Element} element An element of which the name for Autofill will be
408 * returned.
409 * @return {string} the name for Autofill.
410 */
411 __gCrWeb.common.nameForAutofill = function(element) {
412 if (!element) {
413 return '';
414 }
415 var trimmedName = element.name;
416 if (trimmedName) {
417 trimmedName = __gCrWeb.common.trim(trimmedName);
418 if (trimmedName.length > 0) {
419 return trimmedName;
420 }
421 }
422 trimmedName = element.getAttribute('id');
423 if (trimmedName) {
424 return __gCrWeb.common.trim(trimmedName);
425 }
426 trimmedName = element.getAttribute('autocomplete');
427 if (trimmedName && trimmedName !== 'off') {
428 return __gCrWeb.common.trim(trimmedName);
429 }
430 trimmedName = element.getAttribute('placeholder');
431 if (trimmedName) {
432 return __gCrWeb.common.trim(trimmedName);
433 }
434 return '';
435 };
436
437 /**
438 * Acquires the specified DOM |attribute| from the DOM |element| and returns
439 * its lower-case value, or null if not present.
440 * @param {Element} element A DOM element.
441 * @param {string} attribute An attribute name.
442 * @return {?string} Lowercase value of DOM element or null if not present.
443 */
444 __gCrWeb.common.getLowerCaseAttribute = function(element, attribute) {
445 if (!element) {
446 return null;
447 }
448 var value = element.getAttribute(attribute);
449 if (value) {
450 return value.toLowerCase();
451 }
452 return null;
453 };
454
455 /**
456 * Converts a relative URL into an absolute URL.
457 * @param {Object} doc Document.
458 * @param {string} relativeURL Relative URL.
459 * @return {string} Absolute URL.
460 */
461 __gCrWeb.common.absoluteURL = function(doc, relativeURL) {
462 // In the case of data: URL-based pages, relativeURL === absoluteURL.
463 if (doc.location.protocol === 'data:') {
464 return doc.location.href;
465 }
466 var urlNormalizer = doc['__gCrWebURLNormalizer'];
467 if (!urlNormalizer) {
468 urlNormalizer = doc.createElement('a');
469 doc['__gCrWebURLNormalizer'] = urlNormalizer;
470 }
471
472 // Use the magical quality of the <a> element. It automatically converts
473 // relative URLs into absolute ones.
474 urlNormalizer.href = relativeURL;
475 return urlNormalizer.href;
476 };
477
478 /**
479 * Extracts the webpage URL from the given URL by removing the query
480 * and the reference (aka fragment) from the URL.
481 * @param {string} url Web page URL.
482 * @return {string} Web page URL with query and reference removed.
483 */
484 __gCrWeb.common.removeQueryAndReferenceFromURL = function(url) {
485 var queryIndex = url.indexOf('?');
486 if (queryIndex != -1) {
487 return url.substring(0, queryIndex);
488 }
489
490 var hashIndex = url.indexOf('#');
491 if (hashIndex != -1) {
492 return url.substring(0, hashIndex);
493 }
494 return url;
495 };
496
497 /**
498 * Returns the form's |name| attribute if non-empty; otherwise the form's |id|
499 * attribute, or the index of the form (with prefix) in document.forms.
500 *
501 * It is partially based on the logic in
502 * const string16 GetFormIdentifier(const blink::WebFormElement& form)
503 * in chromium/src/components/autofill/renderer/form_autofill_util.h.
504 *
505 * @param {Element} form An element for which the identifier is returned.
506 * @return {string} a string that represents the element's identifier.
507 */
508 __gCrWeb.common.getFormIdentifier = function(form) {
509 if (!form)
510 return '';
511 var name = form.getAttribute('name');
512 if (name && name.length != 0) {
513 return name;
514 }
515 name = form.getAttribute('id');
516 if (name) {
517 return name;
518 }
519 // A form name must be supplied, because the element will later need to be
520 // identified from the name. A last resort is to take the index number of
521 // the form in document.forms. ids are not supposed to begin with digits (by
522 // HTML 4 spec) so this is unlikely to match a true id.
523 for (var idx = 0; idx != document.forms.length; idx++) {
524 if (document.forms[idx] == form) {
525 return __gCrWeb.common.kNamelessFormIDPrefix + idx;
526 }
527 }
528 return '';
529 };
530
531 /**
532 * Returns the form element from an ID obtained from getFormIdentifier.
533 *
534 * This works on a 'best effort' basis since DOM changes can always change the
535 * actual element that the ID refers to.
536 *
537 * @param {string} name An ID string obtained via getFormIdentifier.
538 * @return {Element} The original form element, if it can be determined.
539 */
540 __gCrWeb.common.getFormElementFromIdentifier = function(name) {
541 // First attempt is from the name / id supplied.
542 var form = document.forms.namedItem(name);
543 if (form) {
544 return form;
545 }
546 // Second attempt is from the prefixed index position of the form in
547 // document.forms.
548 if (name.indexOf(__gCrWeb.common.kNamelessFormIDPrefix) == 0) {
549 var nameAsInteger = 0 |
550 name.substring(__gCrWeb.common.kNamelessFormIDPrefix.length);
551 if (__gCrWeb.common.kNamelessFormIDPrefix + nameAsInteger == name &&
552 nameAsInteger < document.forms.length) {
553 return document.forms[nameAsInteger];
554 }
555 }
556 return null;
557 };
558
559 /**
560 * Creates and dispatches an HTML event.
561 *
562 * @param {Element} element The element for which an event is created.
563 * @param {string} type The type of the event.
564 * @param {boolean} bubbles A boolean indicating whether the event should
565 * bubble up through the event chain or not.
566 * @param {boolean} cancelable A boolean indicating whether the event can be
567 * canceled.
568 */
569 __gCrWeb.common.createAndDispatchHTMLEvent = function(
570 element, type, bubbles, cancelable) {
571 var changeEvent = element.ownerDocument.createEvent('HTMLEvents');
572 changeEvent.initEvent(type, bubbles, cancelable);
573
574 // A timer is used to avoid reentering JavaScript evaluation.
575 window.setTimeout(function() {
576 element.dispatchEvent(changeEvent);
577 }, 0);
578 };
579 } // End of anonymous object
OLDNEW
« no previous file with comments | « ios/web/web_state/js/resources/base.js ('k') | ios/web/web_state/js/resources/message.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698