OLD | NEW |
(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 |
OLD | NEW |