diff --git a/java/client/test/org/openqa/selenium/environment/webserver/GeneratedJsTestServlet.java b/java/client/test/org/openqa/selenium/environment/webserver/GeneratedJsTestServlet.java index 9415fd3411c0e..3fee1445b20c2 100644 --- a/java/client/test/org/openqa/selenium/environment/webserver/GeneratedJsTestServlet.java +++ b/java/client/test/org/openqa/selenium/environment/webserver/GeneratedJsTestServlet.java @@ -49,7 +49,7 @@ protected void doGet(HttpServletRequest req, HttpServletResponse resp) + " var path = '../../.." + req.getPathInfo() + "';\n" + " goog.addDependency(path, ['" + symbol + "'],\n" + " goog.dependencies_.requires['../../.." + req.getPathInfo() + "'] || [],\n" - + " !!goog.dependencies_.pathIsModule[path]);\n" + + " !!goog.dependencies_.loadFlags[path]);\n" + " goog.require('" + symbol + "');\n" + " })()\n" + "").getBytes(Charsets.UTF_8); diff --git a/javascript/atoms/domcore.js b/javascript/atoms/domcore.js index ca85972452358..568d4b96d58a0 100644 --- a/javascript/atoms/domcore.js +++ b/javascript/atoms/domcore.js @@ -159,11 +159,16 @@ bot.dom.core.getProperty = function(element, propertyName) { * the given tag name. If the tag name is not provided, returns true if the node * is an element, regardless of the tag name.h * + * @template T * @param {Node} node The node to test. - * @param {string=} opt_tagName Tag name to test the node for. + * @param {(goog.dom.TagName|string)=} opt_tagName Tag name to test the node for. * @return {boolean} Whether the node is an element with the given tag name. */ bot.dom.core.isElement = function(node, opt_tagName) { + // because we call this with deprecated tags such as SHADOW + if (opt_tagName && (typeof opt_tagName !== 'string')) { + opt_tagName = opt_tagName.toString(); + } return !!node && node.nodeType == goog.dom.NodeType.ELEMENT && (!opt_tagName || node.tagName.toUpperCase() == opt_tagName); }; diff --git a/rake-tasks/crazy_fun/mappings/javascript.rb b/rake-tasks/crazy_fun/mappings/javascript.rb index fe3a4f2c30b4e..982d36314e7ae 100644 --- a/rake-tasks/crazy_fun/mappings/javascript.rb +++ b/rake-tasks/crazy_fun/mappings/javascript.rb @@ -221,7 +221,7 @@ def parse_deps(file) IO.read(file).each_line do |line| if data = @@ADD_DEP_REGEX.match(line) info = Info.new(File.expand_path(data[1], @closure_dir)) - info.is_module = data[4] == "true" + info.is_module = (data[4] != "false" and data[4] != "{}") @@DEPS_FILES[file].push(info) @@FILES[file] = info @@ -334,7 +334,7 @@ def resolve_deps(file, symbol, result_list, seen_list) "\\s*,\\s*", "\\[([^\\]]+)?\\]", # Required symbols "\\s*", - "(?:,\\s*(true|false))?", # Module flag. + "(?:,\\s*(true|false|(?:\\{[^\\}]*\\})))?", # Module flag. "\\s*\\)" ].each {|r| r.to_s}.join('') @@MODULE_REGEX = /^goog\.module\s*\(\s*['"]([^'"]+)['"]\s*\)/ @@ -570,7 +570,7 @@ def handle(fun, dir, args) mkdir_p File.dirname(output) flag_file = File.join(File.dirname(output), "closure_flags.txt") - File.open(flag_file, 'w') {|f| f.write(expanded_flags)} + File.open(flag_file, 'w') {|f| f.write(expanded_flags)} cmd = "java -cp third_party/closure/bin/compiler.jar com.google.javascript.jscomp.CommandLineRunner --flagfile " << flag_file sh cmd @@ -865,7 +865,7 @@ def handle(fun, dir, args) mkdir_p File.dirname(output) flag_file = File.join(File.dirname(output), "closure_flags.txt") - File.open(flag_file, 'w') {|f| f.write(expanded_flags)} + File.open(flag_file, 'w') {|f| f.write(expanded_flags)} cmd = "java -cp third_party/closure/bin/compiler.jar com.google.javascript.jscomp.CommandLineRunner " << "--flagfile " << flag_file diff --git a/third_party/closure/bin/README.md b/third_party/closure/bin/README.md index 080319ecfd798..1d8a8f13f12be 100644 --- a/third_party/closure/bin/README.md +++ b/third_party/closure/bin/README.md @@ -9,12 +9,13 @@ The [Closure Compiler](https://developers.google.com/closure/compiler/) is a too * [Download a specific version](https://github.com/google/closure-compiler/wiki/Binary-Downloads). Also available via: - [Maven](https://github.com/google/closure-compiler/wiki/Maven) - [NPM](https://www.npmjs.com/package/google-closure-compiler) + * [Use the JavaScript version](https://github.com/google/closure-compiler-js), with no Java dependency * See the [Google Developers Site](https://developers.google.com/closure/compiler/docs/gettingstarted_app) for documentation including instructions for running the compiler from the command line. ## Options for Getting Help -1. Post in the [Closure Compiler Discuss Group](https://groups.google.com/forum/#!forum/closure-compiler-discuss) -2. Ask a question on [Stack Overflow](http://stackoverflow.com/questions/tagged/google-closure-compiler) -3. Consult the [FAQ](https://github.com/google/closure-compiler/wiki/FAQ) +1. Post in the [Closure Compiler Discuss Group](https://groups.google.com/forum/#!forum/closure-compiler-discuss). +2. Ask a question on [Stack Overflow](http://stackoverflow.com/questions/tagged/google-closure-compiler). +3. Consult the [FAQ](https://github.com/google/closure-compiler/wiki/FAQ). ## Building it Yourself @@ -40,21 +41,29 @@ Note: The Closure Compiler requires [Java 7 or higher](http://www.java.com/). ``` -3. Run `mvn -DskipTests` (omit the `-DskipTests` if you want to run all the +3. On the command line, at the root of this project, run `mvn -DskipTests` (omit the `-DskipTests` if you want to run all the unit tests too). - This will produce a jar file called `target/closure-compiler-1.0-SNAPSHOT.jar`. + This will produce a jar file called `target/closure-compiler-1.0-SNAPSHOT.jar`. You can run this jar + as per the [Running section](#running) of this Readme. If you want to depend on the compiler via + Maven in another Java project, use the `com.google.javascript/closure-compiler-unshaded` artifact. + + Running `mvn -DskipTests -pl externs/pom.xml,pom-main.xml,pom-main-shaded.xml` + will skip building the GWT version of the compiler. This can speed up the build process significantly. ### Using [Eclipse](http://www.eclipse.org/) 1. Download and open the [Eclipse IDE](http://www.eclipse.org/). -2. Navigate to `File > New > Project ...` and create a Java Project. Give - the project a name. -3. Select `Create project from existing source` and choose the root of the - checked-out source tree as the existing directory. -3. Navigate to the `build.xml` file. You will see all the build rules in - the Outline pane. Run the `jar` rule to build the compiler in - `build/compiler.jar`. +2. On the command line, at the root of this project, run `mvn eclipse:eclipse -DdownloadSources=true` to download JARs and build Eclipse project configuration. +3. Navigate to `File > Import > Maven > Existing Maven Projects` and browse to closure-compiler inside of Eclipse. +4. Import both closure-compiler and the nested externs project. +5. Disregard the warnings about maven-antrun-plugin and build errors. +6. In Package Explorer, remove from the build path: + - `src/com/google/javascript/jscomp/debugger/DebuggerGwtMain.java` + - `src/com/google/javascript/jscomp/gwt/` +7. [Exclude the files](http://stackoverflow.com/questions/1187868/how-can-i-exclude-some-folders-from-my-eclipse-project) in the directory `src/com/google/debugging/sourcemap/super` from the project. +8. Build project in Eclipse (right click on the project `closure-compiler-parent` and select `Build Project`). +9. See *Using Maven* above to build the JAR. ## Running @@ -88,6 +97,14 @@ java -jar compiler.jar --help More detailed information about running the Closure Compiler is available in the [documentation](http://code.google.com/closure/compiler/docs/gettingstarted_app.html). + +### Run using Eclipse + +1. Open the class `src/com/google/javascript/jscomp/CommandLineRunner.java` or create your own extended version of the class. +2. Run the class in Eclipse. +3. See the instructions above on how to use the interactive mode - but beware of the [bug](http://stackoverflow.com/questions/4711098/passing-end-of-transmission-ctrl-d-character-in-eclipse-cdt-console) regarding passing "End of Transmission" in the Eclipse console. + + ## Compiling Multiple Scripts If you have multiple scripts, you should compile them all together with one @@ -147,7 +164,9 @@ will re-order the inputs automatically. signed for them. 2. To make sure your changes are of the type that will be accepted, ask about your patch on the [Closure Compiler Discuss Group](https://groups.google.com/forum/#!forum/closure-compiler-discuss) 3. Fork the repository. -4. Make your changes. +4. Make your changes. Check out our + [coding conventions](https://github.com/google/closure-compiler/wiki/Contributors#coding-conventions) + for details on making sure your code is in correct style. 5. Submit a pull request for your changes. A project developer will review your work and then merge your request into the project. ## Closure Compiler License @@ -210,11 +229,6 @@ system have been added. ### Args4j - - - - - @@ -222,7 +236,7 @@ system have been added. - + @@ -245,11 +259,6 @@ options/arguments in your CUI application. ### Guava Libraries
Code Pathlib/args4j.jar
URL https://args4j.dev.java.net/
Version2.0.262.33
- - - - - @@ -279,19 +288,14 @@ options/arguments in your CUI application. ### JSR 305
Code Pathlib/guava.jar
URL https://github.com/google/guava
- - - - - - + - + @@ -313,11 +317,6 @@ options/arguments in your CUI application. ### JUnit
Code Pathlib/jsr305.jar
URLhttp://code.google.com/p/jsr-305/https://github.com/findbugsproject/findbugs
Versionsvn revision 473.0.1
- - - - - @@ -325,7 +324,7 @@ options/arguments in your CUI application. - + @@ -347,11 +346,6 @@ options/arguments in your CUI application. ### Protocol Buffers
Code Pathlib/junit.jar
URL http://sourceforge.net/projects/junit/
Version4.114.12
- - - - - @@ -359,7 +353,7 @@ options/arguments in your CUI application. - + @@ -382,11 +376,6 @@ an encoding of structured data. ### Truth
Code Pathlib/protobuf-java.jar
URL https://github.com/google/protobuf
Version2.5.03.0.2
- - - - - @@ -394,7 +383,7 @@ an encoding of structured data. - + @@ -416,13 +405,6 @@ an encoding of structured data. ### Ant
Code Pathlib/truth.jar
URL https://github.com/google/truth
Version0.240.32
- - - - - @@ -430,7 +412,7 @@ an encoding of structured data. - + @@ -453,11 +435,6 @@ without make's wrinkles and with the full portability of pure java code. ### GSON
Code Path - lib/ant.jar, lib/ant-launcher.jar -
URL http://ant.apache.org/bindownload.cgi
Version1.8.11.9.7
- - - - - @@ -465,7 +442,7 @@ without make's wrinkles and with the full portability of pure java code. - + diff --git a/third_party/closure/bin/compiler.jar b/third_party/closure/bin/compiler.jar index 0d42389c08b71..d621604551a5e 100644 Binary files a/third_party/closure/bin/compiler.jar and b/third_party/closure/bin/compiler.jar differ diff --git a/third_party/closure/goog/a11y/aria/aria.js b/third_party/closure/goog/a11y/aria/aria.js index 78188ba4f63dd..0df45f98f21fc 100644 --- a/third_party/closure/goog/a11y/aria/aria.js +++ b/third_party/closure/goog/a11y/aria/aria.js @@ -54,14 +54,14 @@ goog.a11y.aria.ROLE_ATTRIBUTE_ = 'role'; * they don't contain content to be made accessible. * @private */ -goog.a11y.aria.TAGS_WITH_ASSUMED_ROLES_ = [ +goog.a11y.aria.TAGS_WITH_ASSUMED_ROLES_ = goog.object.createSet([ goog.dom.TagName.A, goog.dom.TagName.AREA, goog.dom.TagName.BUTTON, goog.dom.TagName.HEAD, goog.dom.TagName.INPUT, goog.dom.TagName.LINK, goog.dom.TagName.MENU, goog.dom.TagName.META, goog.dom.TagName.OPTGROUP, goog.dom.TagName.OPTION, goog.dom.TagName.PROGRESS, goog.dom.TagName.STYLE, goog.dom.TagName.SELECT, goog.dom.TagName.SOURCE, goog.dom.TagName.TEXTAREA, goog.dom.TagName.TITLE, goog.dom.TagName.TRACK -]; +]); /** @@ -70,7 +70,7 @@ goog.a11y.aria.TAGS_WITH_ASSUMED_ROLES_ = [ * to manage their active descendants or children. See * {@link http://www.w3.org/TR/wai-aria/states_and_properties * #aria-activedescendant} for more information. - * @private @const + * @private @const {!Array} */ goog.a11y.aria.CONTAINER_ROLES_ = [ goog.a11y.aria.Role.COMBOBOX, goog.a11y.aria.Role.GRID, @@ -113,7 +113,7 @@ goog.a11y.aria.setRole = function(element, roleName) { /** * Gets role of an element. * @param {!Element} element DOM element to get role of. - * @return {goog.a11y.aria.Role} ARIA Role name. + * @return {?goog.a11y.aria.Role} ARIA Role name. */ goog.a11y.aria.getRole = function(element) { var role = element.getAttribute(goog.a11y.aria.ROLE_ATTRIBUTE_); @@ -275,12 +275,11 @@ goog.a11y.aria.setLabel = function(element, label) { * semantics is well supported by most screen readers. * Only to be used internally by the ARIA library in goog.a11y.aria.*. * @param {!Element} element The element to assert an ARIA role set. - * @param {!goog.array.ArrayLike} allowedRoles The child roles of + * @param {!IArrayLike} allowedRoles The child roles of * the roles. */ goog.a11y.aria.assertRoleIsSetInternalUtil = function(element, allowedRoles) { - if (goog.array.contains( - goog.a11y.aria.TAGS_WITH_ASSUMED_ROLES_, element.tagName)) { + if (goog.a11y.aria.TAGS_WITH_ASSUMED_ROLES_[element.tagName]) { return; } var elementRole = /** @type {string}*/ (goog.a11y.aria.getRole(element)); @@ -359,7 +358,7 @@ goog.a11y.aria.getStateString = function(element, stateName) { * Only to be used internally by the ARIA library in goog.a11y.aria.*. * @param {!Element} element DOM node to get state from. * @param {!goog.a11y.aria.State} stateName State name. - * @return {!goog.array.ArrayLike} string Array + * @return {!IArrayLike} string Array * value of the state attribute. */ goog.a11y.aria.getStringArrayStateInternalUtil = function(element, stateName) { @@ -398,7 +397,7 @@ goog.a11y.aria.isContainerRole = function(element) { /** * Splits the input stringValue on whitespace. * @param {string} stringValue The value of the string to split. - * @return {!goog.array.ArrayLike} string Array + * @return {!IArrayLike} string Array * value as result of the split. * @private */ diff --git a/third_party/closure/goog/array/array.js b/third_party/closure/goog/array/array.js index 239af643f2691..0acfbd85fd056 100644 --- a/third_party/closure/goog/array/array.js +++ b/third_party/closure/goog/array/array.js @@ -20,7 +20,6 @@ goog.provide('goog.array'); -goog.provide('goog.array.ArrayLike'); goog.require('goog.asserts'); @@ -51,16 +50,10 @@ goog.define('goog.NATIVE_ARRAY_PROTOTYPES', goog.TRUSTED_SITE); goog.define('goog.array.ASSUME_NATIVE_FUNCTIONS', false); -/** - * @typedef {Array|NodeList|Arguments|{length: number}} - */ -goog.array.ArrayLike; - - /** * Returns the last element in an array without removing it. * Same as goog.array.last. - * @param {Array|goog.array.ArrayLike} array The array. + * @param {IArrayLike|string} array The array. * @return {T} Last item in array. * @template T */ @@ -72,7 +65,7 @@ goog.array.peek = function(array) { /** * Returns the last element in an array without removing it. * Same as goog.array.peek. - * @param {Array|goog.array.ArrayLike} array The array. + * @param {IArrayLike|string} array The array. * @return {T} Last item in array. * @template T */ @@ -91,7 +84,7 @@ goog.array.last = goog.array.peek; * * See {@link http://tinyurl.com/developer-mozilla-org-array-indexof} * - * @param {Array|goog.array.ArrayLike} arr The array to be searched. + * @param {IArrayLike|string} arr The array to be searched. * @param {T} obj The object for which we are searching. * @param {number=} opt_fromIndex The index at which to start the search. If * omitted the search starts at index 0. @@ -132,7 +125,7 @@ goog.array.indexOf = goog.NATIVE_ARRAY_PROTOTYPES && * * See {@link http://tinyurl.com/developer-mozilla-org-array-lastindexof} * - * @param {!Array|!goog.array.ArrayLike} arr The array to be searched. + * @param {!IArrayLike|string} arr The array to be searched. * @param {T} obj The object for which we are searching. * @param {?number=} opt_fromIndex The index at which to start the search. If * omitted the search starts at the end of the array. @@ -175,7 +168,7 @@ goog.array.lastIndexOf = goog.NATIVE_ARRAY_PROTOTYPES && * Calls a function for each element in an array. Skips holes in the array. * See {@link http://tinyurl.com/developer-mozilla-org-array-foreach} * - * @param {Array|goog.array.ArrayLike} arr Array or array like object over + * @param {IArrayLike|string} arr Array or array like object over * which to iterate. * @param {?function(this: S, T, number, ?): ?} f The function to call for every * element. This function takes 3 arguments (the element, the index and the @@ -205,7 +198,7 @@ goog.array.forEach = goog.NATIVE_ARRAY_PROTOTYPES && * Calls a function for each element in an array, starting from the last * element rather than the first. * - * @param {Array|goog.array.ArrayLike} arr Array or array + * @param {IArrayLike|string} arr Array or array * like object over which to iterate. * @param {?function(this: S, T, number, ?): ?} f The function to call for every * element. This function @@ -232,7 +225,7 @@ goog.array.forEachRight = function(arr, f, opt_obj) { * * See {@link http://tinyurl.com/developer-mozilla-org-array-filter} * - * @param {Array|goog.array.ArrayLike} arr Array or array + * @param {IArrayLike|string} arr Array or array * like object over which to iterate. * @param {?function(this:S, T, number, ?):boolean} f The function to call for * every element. This function @@ -275,7 +268,7 @@ goog.array.filter = goog.NATIVE_ARRAY_PROTOTYPES && * * See {@link http://tinyurl.com/developer-mozilla-org-array-map} * - * @param {Array|goog.array.ArrayLike} arr Array or array like object + * @param {IArrayLike|string} arr Array or array like object * over which to iterate. * @param {function(this:THIS, VALUE, number, ?): RESULT} f The function to call * for every element. This function takes 3 arguments (the element, @@ -315,7 +308,7 @@ goog.array.map = goog.NATIVE_ARRAY_PROTOTYPES && * goog.array.reduce(a, function(r, v, i, arr) {return r + v;}, 0); * returns 10 * - * @param {Array|goog.array.ArrayLike} arr Array or array + * @param {IArrayLike|string} arr Array or array * like object over which to iterate. * @param {function(this:S, R, T, number, ?) : R} f The function to call for * every element. This function @@ -358,7 +351,7 @@ goog.array.reduce = goog.NATIVE_ARRAY_PROTOTYPES && * goog.array.reduceRight(a, function(r, v, i, arr) {return r + v;}, ''); * returns 'cba' * - * @param {Array|goog.array.ArrayLike} arr Array or array + * @param {IArrayLike|string} arr Array or array * like object over which to iterate. * @param {?function(this:S, R, T, number, ?) : R} f The function to call for * every element. This function @@ -399,7 +392,7 @@ goog.array.reduceRight = goog.NATIVE_ARRAY_PROTOTYPES && * * See {@link http://tinyurl.com/developer-mozilla-org-array-some} * - * @param {Array|goog.array.ArrayLike} arr Array or array + * @param {IArrayLike|string} arr Array or array * like object over which to iterate. * @param {?function(this:S, T, number, ?) : boolean} f The function to call for * for every element. This function takes 3 arguments (the element, the @@ -435,7 +428,7 @@ goog.array.some = goog.NATIVE_ARRAY_PROTOTYPES && * * See {@link http://tinyurl.com/developer-mozilla-org-array-every} * - * @param {Array|goog.array.ArrayLike} arr Array or array + * @param {IArrayLike|string} arr Array or array * like object over which to iterate. * @param {?function(this:S, T, number, ?) : boolean} f The function to call for * for every element. This function takes 3 arguments (the element, the @@ -468,7 +461,7 @@ goog.array.every = goog.NATIVE_ARRAY_PROTOTYPES && * Counts the array elements that fulfill the predicate, i.e. for which the * callback function returns true. Skips holes in the array. * - * @param {!(Array|goog.array.ArrayLike)} arr Array or array like object + * @param {!IArrayLike|string} arr Array or array like object * over which to iterate. * @param {function(this: S, T, number, ?): boolean} f The function to call for * every element. Takes 3 arguments (the element, the index and the array). @@ -490,7 +483,7 @@ goog.array.count = function(arr, f, opt_obj) { /** * Search an array for the first element that satisfies a given condition and * return that element. - * @param {Array|goog.array.ArrayLike} arr Array or array + * @param {IArrayLike|string} arr Array or array * like object over which to iterate. * @param {?function(this:S, T, number, ?) : boolean} f The function to call * for every element. This function takes 3 arguments (the element, the @@ -509,7 +502,7 @@ goog.array.find = function(arr, f, opt_obj) { /** * Search an array for the first element that satisfies a given condition and * return its index. - * @param {Array|goog.array.ArrayLike} arr Array or array + * @param {IArrayLike|string} arr Array or array * like object over which to iterate. * @param {?function(this:S, T, number, ?) : boolean} f The function to call for * every element. This function @@ -535,7 +528,7 @@ goog.array.findIndex = function(arr, f, opt_obj) { /** * Search an array (in reverse order) for the last element that satisfies a * given condition and return that element. - * @param {Array|goog.array.ArrayLike} arr Array or array + * @param {IArrayLike|string} arr Array or array * like object over which to iterate. * @param {?function(this:S, T, number, ?) : boolean} f The function to call * for every element. This function @@ -555,7 +548,7 @@ goog.array.findRight = function(arr, f, opt_obj) { /** * Search an array (in reverse order) for the last element that satisfies a * given condition and return its index. - * @param {Array|goog.array.ArrayLike} arr Array or array + * @param {IArrayLike|string} arr Array or array * like object over which to iterate. * @param {?function(this:S, T, number, ?) : boolean} f The function to call * for every element. This function @@ -580,7 +573,7 @@ goog.array.findIndexRight = function(arr, f, opt_obj) { /** * Whether the array contains the given object. - * @param {goog.array.ArrayLike} arr The array to test for the presence of the + * @param {IArrayLike|string} arr The array to test for the presence of the * element. * @param {*} obj The object for which to test. * @return {boolean} true if obj is present. @@ -592,7 +585,7 @@ goog.array.contains = function(arr, obj) { /** * Whether the array is empty. - * @param {goog.array.ArrayLike} arr The array to test. + * @param {IArrayLike|string} arr The array to test. * @return {boolean} true if empty. */ goog.array.isEmpty = function(arr) { @@ -602,7 +595,7 @@ goog.array.isEmpty = function(arr) { /** * Clears the array. - * @param {goog.array.ArrayLike} arr Array or array like object to clear. + * @param {IArrayLike} arr Array or array like object to clear. */ goog.array.clear = function(arr) { // For non real arrays we don't have the magic length so we delete the @@ -631,7 +624,7 @@ goog.array.insert = function(arr, obj) { /** * Inserts an object at the given index of the array. - * @param {goog.array.ArrayLike} arr The array to modify. + * @param {IArrayLike} arr The array to modify. * @param {*} obj The object to insert. * @param {number=} opt_i The index at which to insert the object. If omitted, * treated as 0. A negative index is counted from the end of the array. @@ -643,8 +636,8 @@ goog.array.insertAt = function(arr, obj, opt_i) { /** * Inserts at the given index of the array, all elements of another array. - * @param {goog.array.ArrayLike} arr The array to modify. - * @param {goog.array.ArrayLike} elementsToAdd The array of elements to add. + * @param {IArrayLike} arr The array to modify. + * @param {IArrayLike} elementsToAdd The array of elements to add. * @param {number=} opt_i The index at which to insert the object. If omitted, * treated as 0. A negative index is counted from the end of the array. */ @@ -673,7 +666,7 @@ goog.array.insertBefore = function(arr, obj, opt_obj2) { /** * Removes the first occurrence of a particular value from an array. - * @param {Array|goog.array.ArrayLike} arr Array from which to remove + * @param {IArrayLike} arr Array from which to remove * value. * @param {T} obj Object to remove. * @return {boolean} True if an element was removed. @@ -689,9 +682,26 @@ goog.array.remove = function(arr, obj) { }; +/** + * Removes the last occurrence of a particular value from an array. + * @param {!IArrayLike} arr Array from which to remove value. + * @param {T} obj Object to remove. + * @return {boolean} True if an element was removed. + * @template T + */ +goog.array.removeLast = function(arr, obj) { + var i = goog.array.lastIndexOf(arr, obj); + if (i >= 0) { + goog.array.removeAt(arr, i); + return true; + } + return false; +}; + + /** * Removes from an array the element at index i - * @param {goog.array.ArrayLike} arr Array or array like object from which to + * @param {IArrayLike} arr Array or array like object from which to * remove value. * @param {number} i The index to remove. * @return {boolean} True if an element was removed. @@ -708,7 +718,7 @@ goog.array.removeAt = function(arr, i) { /** * Removes the first value that satisfies the given condition. - * @param {Array|goog.array.ArrayLike} arr Array or array + * @param {IArrayLike} arr Array or array * like object over which to iterate. * @param {?function(this:S, T, number, ?) : boolean} f The function to call * for every element. This function @@ -730,7 +740,7 @@ goog.array.removeIf = function(arr, f, opt_obj) { /** * Removes all values that satisfy the given condition. - * @param {Array|goog.array.ArrayLike} arr Array or array + * @param {IArrayLike} arr Array or array * like object over which to iterate. * @param {?function(this:S, T, number, ?) : boolean} f The function to call * for every element. This function @@ -781,7 +791,7 @@ goog.array.removeAllIf = function(arr, f, opt_obj) { * @return {!Array} The new resultant array. */ goog.array.concat = function(var_args) { - return Array.prototype.concat.apply(Array.prototype, arguments); + return Array.prototype.concat.apply([], arguments); }; @@ -792,13 +802,13 @@ goog.array.concat = function(var_args) { * @template T */ goog.array.join = function(var_args) { - return Array.prototype.concat.apply(Array.prototype, arguments); + return Array.prototype.concat.apply([], arguments); }; /** * Converts an object to an array. - * @param {Array|goog.array.ArrayLike} object The object to convert to an + * @param {IArrayLike|string} object The object to convert to an * array. * @return {!Array} The object converted into an array. If object has a * length property, every property indexed with a non-negative number @@ -825,7 +835,7 @@ goog.array.toArray = function(object) { /** * Does a shallow copy of an array. - * @param {Array|goog.array.ArrayLike} arr Array or array-like object to + * @param {IArrayLike|string} arr Array or array-like object to * clone. * @return {!Array} Clone of the input array. * @template T @@ -871,7 +881,7 @@ goog.array.extend = function(arr1, var_args) { * splice. This means that it might work on other objects similar to arrays, * such as the arguments object. * - * @param {Array|goog.array.ArrayLike} arr The array to modify. + * @param {IArrayLike} arr The array to modify. * @param {number|undefined} index The index at which to start changing the * array. If not defined, treated as 0. * @param {number} howMany How many elements to remove (0 means no removal. A @@ -894,7 +904,7 @@ goog.array.splice = function(arr, index, howMany, var_args) { * Array slice. This means that it might work on other objects similar to * arrays, such as the arguments object. * - * @param {Array|goog.array.ArrayLike} arr The array from + * @param {IArrayLike|string} arr The array from * which to copy a segment. * @param {number} start The index of the first element to copy. * @param {number=} opt_end The index after the last element to copy. @@ -931,7 +941,7 @@ goog.array.slice = function(arr, start, opt_end) { * Runtime: N, * Worstcase space: 2N (no dupes) * - * @param {Array|goog.array.ArrayLike} arr The array from which to remove + * @param {IArrayLike} arr The array from which to remove * duplicates. * @param {Array=} opt_rv An optional array in which to return the results, * instead of performing the removal inplace. If specified, the original @@ -977,7 +987,7 @@ goog.array.removeDuplicates = function(arr, opt_rv, opt_hashFn) { * * Runtime: O(log n) * - * @param {Array|goog.array.ArrayLike} arr The array to be searched. + * @param {IArrayLike} arr The array to be searched. * @param {TARGET} target The sought value. * @param {function(TARGET, VALUE): number=} opt_compareFn Optional comparison * function by which the array is ordered. Should take 2 arguments to @@ -1006,7 +1016,7 @@ goog.array.binarySearch = function(arr, target, opt_compareFn) { * * Runtime: O(log n) * - * @param {Array|goog.array.ArrayLike} arr The array to be searched. + * @param {IArrayLike} arr The array to be searched. * @param {function(this:THIS, VALUE, number, ?): number} evaluator * Evaluator function that receives 3 arguments (the element, the index and * the array). Should return a negative number, zero, or a positive number @@ -1040,7 +1050,7 @@ goog.array.binarySelect = function(arr, evaluator, opt_obj) { * * Runtime: O(log n) * - * @param {Array|goog.array.ArrayLike} arr The array to be searched. + * @param {IArrayLike} arr The array to be searched. * @param {function(?, ?, ?): number | function(?, ?): number} compareFn * Either an evaluator or a comparison function, as defined by binarySearch * and binarySelect above. @@ -1092,7 +1102,7 @@ goog.array.binarySearch_ = function( * goog.array.defaultCompare, which compares the elements using * the built in < and > operators. This will produce the expected behavior * for homogeneous arrays of String(s) and Number(s), unlike the native sort, - * but will give unpredictable results for heterogenous lists of strings and + * but will give unpredictable results for heterogeneous lists of strings and * numbers with different numbers of digits. * * This sort is not guaranteed to be stable. @@ -1132,16 +1142,17 @@ goog.array.sort = function(arr, opt_compareFn) { * @template T */ goog.array.stableSort = function(arr, opt_compareFn) { + var compArr = new Array(arr.length); for (var i = 0; i < arr.length; i++) { - arr[i] = {index: i, value: arr[i]}; + compArr[i] = {index: i, value: arr[i]}; } var valueCompareFn = opt_compareFn || goog.array.defaultCompare; function stableCompareFn(obj1, obj2) { return valueCompareFn(obj1.value, obj2.value) || obj1.index - obj2.index; } - goog.array.sort(arr, stableCompareFn); + goog.array.sort(compArr, stableCompareFn); for (var i = 0; i < arr.length; i++) { - arr[i] = arr[i].value; + arr[i] = compArr[i].value; } }; @@ -1217,8 +1228,8 @@ goog.array.isSorted = function(arr, opt_compareFn, opt_strict) { * have the same length and their corresponding elements are equal according to * the comparison function. * - * @param {goog.array.ArrayLike} arr1 The first array to compare. - * @param {goog.array.ArrayLike} arr2 The second array to compare. + * @param {IArrayLike} arr1 The first array to compare. + * @param {IArrayLike} arr2 The second array to compare. * @param {Function=} opt_equalsFn Optional comparison function. * Should take 2 arguments to compare, and return true if the arguments * are equal. Defaults to {@link goog.array.defaultCompareEquality} which @@ -1243,9 +1254,9 @@ goog.array.equals = function(arr1, arr2, opt_equalsFn) { /** * 3-way array compare function. - * @param {!Array|!goog.array.ArrayLike} arr1 The first array to + * @param {!IArrayLike} arr1 The first array to * compare. - * @param {!Array|!goog.array.ArrayLike} arr2 The second array to + * @param {!IArrayLike} arr2 The second array to * compare. * @param {function(VALUE, VALUE): number=} opt_compareFn Optional comparison * function by which the array is to be ordered. Should take 2 arguments to @@ -1314,7 +1325,7 @@ goog.array.defaultCompareEquality = function(a, b) { /** * Inserts a value into a sorted array. The array is not modified if the * value is already present. - * @param {Array|goog.array.ArrayLike} array The array to modify. + * @param {IArrayLike} array The array to modify. * @param {VALUE} value The object to insert. * @param {function(VALUE, VALUE): number=} opt_compareFn Optional comparison * function by which the array is ordered. Should take 2 arguments to @@ -1336,7 +1347,7 @@ goog.array.binaryInsert = function(array, value, opt_compareFn) { /** * Removes a value from a sorted array. - * @param {!Array|!goog.array.ArrayLike} array The array to modify. + * @param {!IArrayLike} array The array to modify. * @param {VALUE} value The object to remove. * @param {function(VALUE, VALUE): number=} opt_compareFn Optional comparison * function by which the array is ordered. Should take 2 arguments to @@ -1386,7 +1397,7 @@ goog.array.bucket = function(array, sorter, opt_obj) { /** * Creates a new object built from the provided array and the key-generation * function. - * @param {Array|goog.array.ArrayLike} arr Array or array like object over + * @param {IArrayLike} arr Array or array like object over * which to iterate whose elements will be the values in the new object. * @param {?function(this:S, T, number, ?) : string} keyFunc The function to * call for every element. This function takes 3 arguments (the element, the @@ -1537,7 +1548,7 @@ goog.array.rotate = function(array, n) { * of the items. Example use case: keeping a list of JavaScript objects * synchronized with the corresponding list of DOM elements after one of the * elements has been dragged to a new position. - * @param {!(Array|Arguments|{length:number})} arr The array to modify. + * @param {!IArrayLike} arr The array to modify. * @param {number} fromIndex Index of the item to move between 0 and * {@code arr.length - 1}. * @param {number} toIndex Target index between 0 and {@code arr.length - 1}. @@ -1563,7 +1574,7 @@ goog.array.moveItem = function(arr, fromIndex, toIndex) { * This is similar to the zip() function in Python. See {@link * http://docs.python.org/library/functions.html#zip} * - * @param {...!goog.array.ArrayLike} var_args Arrays to be combined. + * @param {...!IArrayLike} var_args Arrays to be combined. * @return {!Array>} A new array of arrays created from * provided arrays. */ @@ -1632,3 +1643,23 @@ goog.array.copyByIndex = function(arr, index_arr) { goog.array.forEach(index_arr, function(index) { result.push(arr[index]); }); return result; }; + + +/** + * Maps each element of the input array into zero or more elements of the output + * array. + * + * @param {!IArrayLike|string} arr Array or array like object + * over which to iterate. + * @param {function(this:THIS, VALUE, number, ?): !Array} f The function + * to call for every element. This function takes 3 arguments (the element, + * the index and the array) and should return an array. The result will be + * used to extend a new array. + * @param {THIS=} opt_obj The object to be used as the value of 'this' within f. + * @return {!Array} a new array with the concatenation of all arrays + * returned from f. + * @template THIS, VALUE, RESULT + */ +goog.array.concatMap = function(arr, f, opt_obj) { + return goog.array.concat.apply([], goog.array.map(arr, f, opt_obj)); +}; diff --git a/third_party/closure/goog/asserts/asserts.js b/third_party/closure/goog/asserts/asserts.js index 43aac1ab659a5..1f4b65305c143 100644 --- a/third_party/closure/goog/asserts/asserts.js +++ b/third_party/closure/goog/asserts/asserts.js @@ -164,7 +164,7 @@ goog.asserts.assert = function(condition, opt_message, var_args) { * switch(type) { * case FOO: doSomething(); break; * case BAR: doSomethingElse(); break; - * default: goog.assert.fail('Unrecognized type: ' + type); + * default: goog.asserts.fail('Unrecognized type: ' + type); * // We have only 2 types - "default:" section is unreachable code. * } * diff --git a/third_party/closure/goog/async/animationdelay.js b/third_party/closure/goog/async/animationdelay.js index 673a90a70827b..d9057d86ee1c2 100644 --- a/third_party/closure/goog/async/animationdelay.js +++ b/third_party/closure/goog/async/animationdelay.js @@ -46,11 +46,13 @@ goog.require('goog.functions'); * animations, see: * @see http://paulirish.com/2011/requestanimationframe-for-smart-animating/ * - * @param {function(number)} listener Function to call when the delay completes. - * Will be passed the timestamp when it's called, in unix ms. + * @param {function(this:THIS, number)} listener Function to call + * when the delay completes. Will be passed the timestamp when it's called, + * in unix ms. * @param {Window=} opt_window The window object to execute the delay in. * Defaults to the global object. - * @param {Object=} opt_handler The object scope to invoke the function in. + * @param {THIS=} opt_handler The object scope to invoke the function in. + * @template THIS * @constructor * @struct * @extends {goog.Disposable} @@ -74,13 +76,15 @@ goog.async.AnimationDelay = function(listener, opt_window, opt_handler) { /** * The function that will be invoked after a delay. - * @private {function(number)} + * @const + * @private */ this.listener_ = listener; /** * The object context to invoke the callback in. - * @private {Object|undefined} + * @const + * @private {(THIS|undefined)} */ this.handler_ = opt_handler; @@ -156,6 +160,16 @@ goog.async.AnimationDelay.prototype.start = function() { }; +/** + * Starts the delay timer if it's not already active. + */ +goog.async.AnimationDelay.prototype.startIfNotActive = function() { + if (!this.isActive()) { + this.start(); + } +}; + + /** * Stops the delay timer if it is active. No action is taken if the timer is not * in use. @@ -245,7 +259,7 @@ goog.async.AnimationDelay.prototype.getRaf_ = function() { /** - * @return {?function(number): number} The cancelAnimationFrame function, + * @return {?function(number): undefined} The cancelAnimationFrame function, * or null if not available on this browser. * @private */ diff --git a/third_party/closure/goog/async/debouncer.js b/third_party/closure/goog/async/debouncer.js index c1935566ebfcc..729be486f6e45 100644 --- a/third_party/closure/goog/async/debouncer.js +++ b/third_party/closure/goog/async/debouncer.js @@ -47,29 +47,26 @@ goog.async.Debouncer = function(listener, interval, opt_handler) { /** * Function to callback - * @private {function(this: T, ...?)} + * @const @private {function(this: T, ...?)} */ this.listener_ = opt_handler != null ? goog.bind(listener, opt_handler) : listener; /** * Interval for the debounce time - * @type {number} - * @private + * @const @private {number} */ this.interval_ = interval; /** * Cached callback function invoked after the debounce timeout completes - * @type {!Function} - * @private + * @const @private {!Function} */ this.callback_ = goog.bind(this.onTimer_, this); /** * Indicates that the action is pending and needs to be fired. - * @type {boolean} - * @private + * @private {boolean} */ this.shouldFire_ = false; @@ -77,21 +74,26 @@ goog.async.Debouncer = function(listener, interval, opt_handler) { * Indicates the count of nested pauses currently in effect on the debouncer. * When this count is not zero, fired actions will be postponed until the * debouncer is resumed enough times to drop the pause count to zero. - * @type {number} - * @private + * @private {number} */ this.pauseCount_ = 0; /** * Timer for scheduling the next callback - * @type {?number} - * @private + * @private {?number} */ this.timer_ = null; + /** + * When set this is a timestamp. On the onfire we want to reschedule the + * callback so it ends up at this time. + * @private {?number} + */ + this.refireAt_ = null; + /** * The last arguments passed into {@code fire}. - * @private {!Array|!IArrayLike} + * @private {!IArrayLike} */ this.args_ = []; }; @@ -107,8 +109,17 @@ goog.inherits(goog.async.Debouncer, goog.Disposable); * @param {...?} var_args Arguments to pass on to the debounced function. */ goog.async.Debouncer.prototype.fire = function(var_args) { - this.stop(); this.args_ = arguments; + // When this method is called, we need to prevent fire() calls from within the + // previous interval from calling the callback. The simplest way of doing this + // is to call this.stop() which calls clearTimeout, and then reschedule the + // timeout. However clearTimeout and setTimeout are expensive, so we just + // leave them untouched and when they do happen we potentially reschedule. + this.shouldFire_ = false; + if (this.timer_) { + this.refireAt_ = goog.now() + this.interval_; + return; + } this.timer_ = goog.Timer.callOnce(this.callback_, this.interval_); }; @@ -122,6 +133,7 @@ goog.async.Debouncer.prototype.stop = function() { goog.Timer.clear(this.timer_); this.timer_ = null; } + this.refireAt_ = null; this.shouldFire_ = false; this.args_ = []; }; @@ -166,6 +178,14 @@ goog.async.Debouncer.prototype.disposeInternal = function() { * @private */ goog.async.Debouncer.prototype.onTimer_ = function() { + // There is a newer call to fire() within the debounce interval. + // Reschedule the callback and return. + if (this.refireAt_) { + this.timer_ = + goog.Timer.callOnce(this.callback_, this.refireAt_ - goog.now()); + this.refireAt_ = null; + return; + } this.timer_ = null; if (!this.pauseCount_) { diff --git a/third_party/closure/goog/async/freelist.js b/third_party/closure/goog/async/freelist.js index b05c9ff377a09..c58ddf704bfb6 100644 --- a/third_party/closure/goog/async/freelist.js +++ b/third_party/closure/goog/async/freelist.js @@ -33,16 +33,16 @@ goog.async.FreeList = goog.defineClass(null, { * @param {number} limit */ constructor: function(create, reset, limit) { - /** @const {number} */ + /** @private @const {number} */ this.limit_ = limit; - /** @const {function()} */ + /** @private @const {function()} */ this.create_ = create; - /** @const {function(ITEM):void} */ + /** @private @const {function(ITEM):void} */ this.reset_ = reset; - /** @type {number} */ + /** @private {number} */ this.occupants_ = 0; - /** @type {ITEM} */ + /** @private {ITEM} */ this.head_ = null; }, diff --git a/third_party/closure/goog/async/nexttick.js b/third_party/closure/goog/async/nexttick.js index fc25b55d30a90..bd3fb0807344b 100644 --- a/third_party/closure/goog/async/nexttick.js +++ b/third_party/closure/goog/async/nexttick.js @@ -143,6 +143,7 @@ goog.async.nextTick.setImmediate_; goog.async.nextTick.getSetImmediateEmulator_ = function() { // Create a private message channel and use it to postMessage empty messages // to ourselves. + /** @type {!Function|undefined} */ var Channel = goog.global['MessageChannel']; // If MessageChannel is not available and we are in a browser, implement // an iframe based polyfill in browsers that have postMessage and @@ -157,7 +158,7 @@ goog.async.nextTick.getSetImmediateEmulator_ = function() { Channel = function() { // Make an empty, invisible iframe. var iframe = /** @type {!HTMLIFrameElement} */ ( - document.createElement(goog.dom.TagName.IFRAME)); + document.createElement(String(goog.dom.TagName.IFRAME))); iframe.style.display = 'none'; iframe.src = ''; document.documentElement.appendChild(iframe); @@ -193,7 +194,7 @@ goog.async.nextTick.getSetImmediateEmulator_ = function() { }; }; } - if (typeof Channel !== 'undefined' && (!goog.labs.userAgent.browser.isIE())) { + if (typeof Channel !== 'undefined' && !goog.labs.userAgent.browser.isIE()) { // Exclude all of IE due to // http://codeforhire.com/2013/09/21/setimmediate-and-messagechannel-broken-on-internet-explorer-10/ // which allows starving postMessage with a busy setTimeout loop. @@ -220,9 +221,10 @@ goog.async.nextTick.getSetImmediateEmulator_ = function() { // Implementation for IE6 to IE10: Script elements fire an asynchronous // onreadystatechange event when inserted into the DOM. if (typeof document !== 'undefined' && - 'onreadystatechange' in document.createElement(goog.dom.TagName.SCRIPT)) { + 'onreadystatechange' in + document.createElement(String(goog.dom.TagName.SCRIPT))) { return function(cb) { - var script = document.createElement(goog.dom.TagName.SCRIPT); + var script = document.createElement(String(goog.dom.TagName.SCRIPT)); script.onreadystatechange = function() { // Clean up and call the callback. script.onreadystatechange = null; @@ -237,7 +239,9 @@ goog.async.nextTick.getSetImmediateEmulator_ = function() { // Fall back to setTimeout with 0. In browsers this creates a delay of 5ms // or more. // NOTE(user): This fallback is used for IE11. - return function(cb) { goog.global.setTimeout(cb, 0); }; + return function(cb) { + goog.global.setTimeout(/** @type {function()} */ (cb), 0); + }; }; diff --git a/third_party/closure/goog/async/run.js b/third_party/closure/goog/async/run.js index 749c9b9cf0c16..832b6faf3e915 100644 --- a/third_party/closure/goog/async/run.js +++ b/third_party/closure/goog/async/run.js @@ -48,8 +48,11 @@ goog.async.run = function(callback, opt_context) { goog.async.run.initializeRunner_ = function() { // If native Promises are available in the browser, just schedule the callback // on a fulfilled promise, which is specified to be async, but as fast as - // possible. - if (goog.global.Promise && goog.global.Promise.resolve) { + // possible. Use goog.global.Promise instead of just Promise because the + // relevant externs may be missing, and don't alias it because this could + // confuse the compiler into thinking the polyfill is required when it should + // be treated as optional. + if (String(goog.global.Promise).indexOf('[native code]') != -1) { var promise = goog.global.Promise.resolve(undefined); goog.async.run.schedule_ = function() { promise.then(goog.async.run.processWorkQueue); diff --git a/third_party/closure/goog/async/throttle.js b/third_party/closure/goog/async/throttle.js index 041cc9ac8f952..504a34334d8be 100644 --- a/third_party/closure/goog/async/throttle.js +++ b/third_party/closure/goog/async/throttle.js @@ -69,7 +69,7 @@ goog.async.Throttle = function(listener, interval, opt_handler) { /** * The last arguments passed into {@code fire}. - * @private {!goog.array.ArrayLike} + * @private {!IArrayLike} */ this.args_ = []; }; diff --git a/third_party/closure/goog/async/workqueue.js b/third_party/closure/goog/async/workqueue.js index de66231520156..1ff6e9e5ea97a 100644 --- a/third_party/closure/goog/async/workqueue.js +++ b/third_party/closure/goog/async/workqueue.js @@ -46,7 +46,7 @@ goog.define('goog.async.WorkQueue.DEFAULT_MAX_UNUSED', 100); /** @const @private {goog.async.FreeList} */ goog.async.WorkQueue.freelist_ = new goog.async.FreeList( function() { return new goog.async.WorkItem(); }, - function(item) { item.reset() }, goog.async.WorkQueue.DEFAULT_MAX_UNUSED); + function(item) { item.reset(); }, goog.async.WorkQueue.DEFAULT_MAX_UNUSED); /** diff --git a/third_party/closure/goog/base.js b/third_party/closure/goog/base.js index 8cb1ac6847107..f1716c09b5d74 100644 --- a/third_party/closure/goog/base.js +++ b/third_party/closure/goog/base.js @@ -15,19 +15,19 @@ /** * @fileoverview Bootstrap for the Google JS Library (Closure). * - * In uncompiled mode base.js will write out Closure's deps file, unless the - * global CLOSURE_NO_DEPS is set to true. This allows projects to - * include their own deps file(s) from different locations. + * In uncompiled mode base.js will attempt to load Closure's deps file, unless + * the global CLOSURE_NO_DEPS is set to true. This allows projects + * to include their own deps file(s) from different locations. * - * @author arv@google.com (Erik Arvidsson) + * Avoid including base.js more than once. This is strictly discouraged and not + * supported. goog.require(...) won't work properly in that case. * * @provideGoog */ /** - * @define {boolean} Overridden to true by the compiler when - * --process_closure_primitives is specified. + * @define {boolean} Overridden to true by the compiler. */ var COMPILED = false; @@ -90,8 +90,6 @@ goog.global.CLOSURE_DEFINES; /** * Returns true if the specified value is not undefined. - * WARNING: Do not use this to test if an object has a property. Use the in - * operator instead. * * @param {?} val Variable to test. * @return {boolean} Whether variable is defined. @@ -102,6 +100,35 @@ goog.isDef = function(val) { return val !== void 0; }; +/** + * Returns true if the specified value is a string. + * @param {?} val Variable to test. + * @return {boolean} Whether variable is a string. + */ +goog.isString = function(val) { + return typeof val == 'string'; +}; + + +/** + * Returns true if the specified value is a boolean. + * @param {?} val Variable to test. + * @return {boolean} Whether variable is boolean. + */ +goog.isBoolean = function(val) { + return typeof val == 'boolean'; +}; + + +/** + * Returns true if the specified value is a number. + * @param {?} val Variable to test. + * @return {boolean} Whether variable is a number. + */ +goog.isNumber = function(val) { + return typeof val == 'number'; +}; + /** * Builds an object structure for the provided namespace path, ensuring that @@ -111,7 +138,7 @@ goog.isDef = function(val) { * @param {string} name name of the object that this file defines. * @param {*=} opt_object the object to expose at the end of the path. * @param {Object=} opt_objectToExportTo The object to add the path to; default - * is |goog.global|. + * is `goog.global`. * @private */ goog.exportPath_ = function(name, opt_object, opt_objectToExportTo) { @@ -125,17 +152,11 @@ goog.exportPath_ = function(name, opt_object, opt_objectToExportTo) { cur.execScript('var ' + parts[0]); } - // Certain browsers cannot parse code in the form for((a in b); c;); - // This pattern is produced by the JSCompiler when it collapses the - // statement above into the conditional loop below. To prevent this from - // happening, use a for-loop and reserve the init logic as below. - - // Parentheses added to eliminate strict JS warning in Firefox. for (var part; parts.length && (part = parts.shift());) { if (!parts.length && goog.isDef(opt_object)) { // last part and we have an object; use it cur[part] = opt_object; - } else if (cur[part]) { + } else if (cur[part] && cur[part] !== Object.prototype[part]) { cur = cur[part]; } else { cur = cur[part] = {}; @@ -158,11 +179,16 @@ goog.define = function(name, defaultValue) { var value = defaultValue; if (!COMPILED) { if (goog.global.CLOSURE_UNCOMPILED_DEFINES && + // Anti DOM-clobbering runtime check (b/37736576). + /** @type {?} */ (goog.global.CLOSURE_UNCOMPILED_DEFINES).nodeType === + undefined && Object.prototype.hasOwnProperty.call( goog.global.CLOSURE_UNCOMPILED_DEFINES, name)) { value = goog.global.CLOSURE_UNCOMPILED_DEFINES[name]; } else if ( goog.global.CLOSURE_DEFINES && + // Anti DOM-clobbering runtime check (b/37736576). + /** @type {?} */ (goog.global.CLOSURE_DEFINES).nodeType === undefined && Object.prototype.hasOwnProperty.call( goog.global.CLOSURE_DEFINES, name)) { value = goog.global.CLOSURE_DEFINES[name]; @@ -174,11 +200,12 @@ goog.define = function(name, defaultValue) { /** * @define {boolean} DEBUG is provided as a convenience so that debugging code - * that should not be included in a production js_binary can be easily stripped - * by specifying --define goog.DEBUG=false to the JSCompiler. For example, most - * toString() methods should be declared inside an "if (goog.DEBUG)" conditional - * because they are generally used for debugging purposes and it is difficult - * for the JSCompiler to statically determine whether they are used. + * that should not be included in a production. It can be easily stripped + * by specifying --define goog.DEBUG=false to the Closure Compiler aka + * JSCompiler. For example, most toString() methods should be declared inside an + * "if (goog.DEBUG)" conditional because they are generally used for debugging + * purposes and it is difficult for the JSCompiler to statically determine + * whether they are used. */ goog.define('goog.DEBUG', true); @@ -186,7 +213,7 @@ goog.define('goog.DEBUG', true); /** * @define {string} LOCALE defines the locale being used for compilation. It is * used to select locale specific data to be compiled in js binary. BUILD rule - * can specify this value by "--define goog.LOCALE=" as JSCompiler + * can specify this value by "--define goog.LOCALE=" as a compiler * option. * * Take into account that the locale code format is important. You should use @@ -200,7 +227,8 @@ goog.define('goog.DEBUG', true); * For language codes you should use values defined by ISO 693-1. See it here * http://www.w3.org/WAI/ER/IG/ert/iso639.htm. There is only one exception from * this rule: the Hebrew language. For legacy reasons the old code (iw) should - * be used instead of the new code (he), see http://wiki/Main/IIISynonyms. + * be used instead of the new code (he). + * */ goog.define('goog.LOCALE', 'en'); // default to en @@ -214,7 +242,7 @@ goog.define('goog.LOCALE', 'en'); // default to en * * If your JavaScript can be loaded by a third party site and you are wary about * relying on non-standard implementations, specify - * "--define goog.TRUSTED_SITE=false" to the JSCompiler. + * "--define goog.TRUSTED_SITE=false" to the compiler. */ goog.define('goog.TRUSTED_SITE', true); @@ -224,7 +252,7 @@ goog.define('goog.TRUSTED_SITE', true); * * This define can be used to trigger alternate implementations compatible with * running in EcmaScript Strict mode or warn about unavailable functionality. - * @see https://goo.gl/g5EoHI + * @see https://goo.gl/PudQ4y * */ goog.define('goog.STRICT_MODE_COMPATIBLE', false); @@ -267,6 +295,9 @@ goog.define('goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING', false); * "goog.package.part". */ goog.provide = function(name) { + if (goog.isInModuleLoader_()) { + throw Error('goog.provide can not be used within a goog.module.'); + } if (!COMPILED) { // Ensure that the same namespace isn't provided twice. // A goog.module/goog.provide maps a goog.require to a specific file @@ -342,6 +373,7 @@ goog.VALID_MODULE_RE_ = /^[a-zA-Z_$][a-zA-Z0-9._$]*$/; * * @param {string} name Namespace provided by this file in the form * "goog.package.part", is expected but not required. + * @return {void} */ goog.module = function(name) { if (!goog.isString(name) || !name || @@ -349,7 +381,13 @@ goog.module = function(name) { throw Error('Invalid module identifier'); } if (!goog.isInModuleLoader_()) { - throw Error('Module ' + name + ' has been loaded incorrectly.'); + throw Error( + 'Module ' + name + ' has been loaded incorrectly. Note, ' + + 'modules cannot be loaded as normal scripts. They require some kind of ' + + 'pre-processing step. You\'re likely trying to load a module via a ' + + 'script tag or as a part of a concatenated bundle without rewriting the ' + + 'module. For more info see: ' + + 'https://github.com/google/closure-library/wiki/goog.module:-an-ES6-module-like-alternative-to-goog.provide.'); } if (goog.moduleLoaderState_.moduleName) { throw Error('goog.module may only be called once per module.'); @@ -390,14 +428,14 @@ goog.module.get = function(name) { */ goog.module.getInternal_ = function(name) { if (!COMPILED) { - if (goog.isProvided_(name)) { - // goog.require only return a value with-in goog.module files. - return name in goog.loadedModules_ ? goog.loadedModules_[name] : - goog.getObjectByName(name); - } else { - return null; + if (name in goog.loadedModules_) { + return goog.loadedModules_[name]; + } else if (!goog.implicitNamespaces_[name]) { + var ns = goog.getObjectByName(name); + return ns != null ? ns : null; } } + return null; }; @@ -470,6 +508,9 @@ goog.setTestOnly = function(opt_message) { * into the JavaScript binary. If it is required elsewhere, it will be type * checked as normal. * + * Before using goog.forwardDeclare, please read the documentation at + * https://github.com/google/closure-compiler/wiki/Bad-Type-Annotation to + * understand the options and tradeoffs when working with forward declarations. * * @param {string} name The namespace to forward declare in the form of * "goog.package.part". @@ -581,7 +622,7 @@ goog.addDependency = function(relPath, provides, requires, opt_loadFlags) { } for (var i = 0; provide = provides[i]; i++) { deps.nameToPath[provide] = path; - deps.pathIsModule[path] = opt_loadFlags['module'] == 'goog'; + deps.loadFlags[path] = opt_loadFlags; } for (var j = 0; require = requires[j]; j++) { if (!(path in deps.requires)) { @@ -641,8 +682,7 @@ goog.logToConsole_ = function(msg) { /** * Implements a system for the dynamic resolution of dependencies that works in * parallel with the BUILD system. Note that all calls to goog.require will be - * stripped by the JSCompiler when the --process_closure_primitives option is - * used. + * stripped by the compiler. * @see goog.provide * @param {string} name Namespace to include (as was given in goog.provide()) in * the form "goog.package.part". @@ -659,23 +699,20 @@ goog.require = function(name) { if (goog.isProvided_(name)) { if (goog.isInModuleLoader_()) { return goog.module.getInternal_(name); - } else { - return null; } - } - - if (goog.ENABLE_DEBUG_LOADER) { + } else if (goog.ENABLE_DEBUG_LOADER) { var path = goog.getPathFromDeps_(name); if (path) { goog.writeScripts_(path); - return null; + } else { + var errorMessage = 'goog.require could not find: ' + name; + goog.logToConsole_(errorMessage); + + throw Error(errorMessage); } } - var errorMessage = 'goog.require could not find: ' + name; - goog.logToConsole_(errorMessage); - - throw Error(errorMessage); + return null; } }; @@ -695,7 +732,8 @@ goog.global.CLOSURE_BASE_PATH; /** - * Whether to write out Closure's deps file. By default, the deps are written. + * Whether to attempt to load Closure's deps file. By default, when uncompiled, + * deps files will attempt to be loaded. * @type {boolean|undefined} */ goog.global.CLOSURE_NO_DEPS; @@ -728,9 +766,6 @@ goog.nullFunction = function() {}; * Now if a subclass of Foo fails to override bar(), an error will be thrown * when bar() is invoked. * - * Note: This does not take the name of the function to override as an argument - * because that would make it more difficult to obfuscate our JavaScript code. - * * @type {!Function} * @throws {Error} when invoked to indicate the method should be overridden. */ @@ -746,6 +781,10 @@ goog.abstractMethod = function() { * method to. */ goog.addSingletonGetter = function(ctor) { + // instance_ is immediately set to prevent issues with sealed constructors + // such as are encountered when a constructor is returned as the export object + // of a goog.module in unoptimized code. + ctor.instance_ = undefined; ctor.getInstance = function() { if (ctor.instance_) { return ctor.instance_; @@ -801,13 +840,32 @@ goog.loadedModules_ = {}; goog.DEPENDENCIES_ENABLED = !COMPILED && goog.ENABLE_DEBUG_LOADER; +/** + * @define {string} How to decide whether to transpile. Valid values + * are 'always', 'never', and 'detect'. The default ('detect') is to + * use feature detection to determine which language levels need + * transpilation. + */ +// NOTE(user): we could expand this to accept a language level to bypass +// detection: e.g. goog.TRANSPILE == 'es5' would transpile ES6 files but +// would leave ES3 and ES5 files alone. +goog.define('goog.TRANSPILE', 'detect'); + + +/** + * @define {string} Path to the transpiler. Executing the script at this + * path (relative to base.js) should define a function $jscomp.transpile. + */ +goog.define('goog.TRANSPILER', 'transpile.js'); + + if (goog.DEPENDENCIES_ENABLED) { /** * This object is used to keep track of dependencies and other data that is * used for loading scripts. * @private * @type {{ - * pathIsModule: !Object, + * loadFlags: !Object>, * nameToPath: !Object, * requires: !Object>, * visited: !Object, @@ -816,7 +874,7 @@ if (goog.DEPENDENCIES_ENABLED) { * }} */ goog.dependencies_ = { - pathIsModule: {}, // 1 to 1 + loadFlags: {}, // 1 to 1 nameToPath: {}, // 1 to 1 @@ -848,7 +906,9 @@ if (goog.DEPENDENCIES_ENABLED) { * @private */ goog.findBasePath_ = function() { - if (goog.isDef(goog.global.CLOSURE_BASE_PATH)) { + if (goog.isDef(goog.global.CLOSURE_BASE_PATH) && + // Anti DOM-clobbering runtime check (b/37736576). + goog.isString(goog.global.CLOSURE_BASE_PATH)) { goog.basePath = goog.global.CLOSURE_BASE_PATH; return; } else if (!goog.inHtmlDocument_()) { @@ -856,7 +916,13 @@ if (goog.DEPENDENCIES_ENABLED) { } /** @type {Document} */ var doc = goog.global.document; - var scripts = doc.getElementsByTagName('SCRIPT'); + // If we have a currentScript available, use it exclusively. + var currentScript = doc.currentScript; + if (currentScript) { + var scripts = [currentScript]; + } else { + var scripts = doc.getElementsByTagName('SCRIPT'); + } // Search backwards since the current script is in almost all cases the one // that has base.js. for (var i = scripts.length - 1; i >= 0; --i) { @@ -888,24 +954,41 @@ if (goog.DEPENDENCIES_ENABLED) { }; - /** @const @private {boolean} */ + /** + * Whether the browser is IE9 or earlier, which needs special handling + * for deferred modules. + * @const @private {boolean} + */ goog.IS_OLD_IE_ = !!(!goog.global.atob && goog.global.document && goog.global.document.all); /** - * Given a URL initiate retrieval and execution of the module. + * Whether IE9 or earlier is waiting on a dependency. This ensures that + * deferred modules that have no non-deferred dependencies actually get + * loaded, since if we defer them and then never pull in a non-deferred + * script, then `goog.loadQueuedModules_` will never be called. Instead, + * if not waiting on anything we simply don't defer in the first place. + * @private {boolean} + */ + goog.oldIeWaiting_ = false; + + + /** + * Given a URL initiate retrieval and execution of a script that needs + * pre-processing. * @param {string} src Script source URL. + * @param {boolean} isModule Whether this is a goog.module. + * @param {boolean} needsTranspile Whether this source needs transpilation. * @private */ - goog.importModule_ = function(src) { + goog.importProcessedScript_ = function(src, isModule, needsTranspile) { // In an attempt to keep browsers from timing out loading scripts using // synchronous XHRs, put each load in its own script block. - var bootstrap = 'goog.retrieveAndExecModule_("' + src + '");'; + var bootstrap = 'goog.retrieveAndExec_("' + src + '", ' + isModule + ', ' + + needsTranspile + ');'; - if (goog.importScript_('', bootstrap)) { - goog.dependencies_.written[src] = true; - } + goog.importScript_('', bootstrap); }; @@ -975,6 +1058,7 @@ if (goog.DEPENDENCIES_ENABLED) { goog.maybeProcessDeferredPath_(path); } } + goog.oldIeWaiting_ = false; }; @@ -999,7 +1083,10 @@ if (goog.DEPENDENCIES_ENABLED) { */ goog.isDeferredModule_ = function(name) { var path = goog.getPathFromDeps_(name); - if (path && goog.dependencies_.pathIsModule[path]) { + var loadFlags = path && goog.dependencies_.loadFlags[path] || {}; + var languageLevel = loadFlags['lang'] || 'es3'; + if (path && (loadFlags['module'] == 'goog' || + goog.needsTranspile_(languageLevel))) { var abspath = goog.basePath + path; return (abspath) in goog.dependencies_.deferred; } @@ -1063,69 +1150,7 @@ if (goog.DEPENDENCIES_ENABLED) { // Because this executes synchronously, we don't need to do any additional // bookkeeping. When "goog.loadModule" the namespace will be marked as // having been provided which is sufficient. - goog.retrieveAndExecModule_(url); - }; - - - /** - * @param {function(?):?|string} moduleDef The module definition. - */ - goog.loadModule = function(moduleDef) { - // NOTE: we allow function definitions to be either in the from - // of a string to eval (which keeps the original source intact) or - // in a eval forbidden environment (CSP) we allow a function definition - // which in its body must call {@code goog.module}, and return the exports - // of the module. - var previousState = goog.moduleLoaderState_; - try { - goog.moduleLoaderState_ = { - moduleName: undefined, - declareLegacyNamespace: false - }; - var exports; - if (goog.isFunction(moduleDef)) { - exports = moduleDef.call(goog.global, {}); - } else if (goog.isString(moduleDef)) { - exports = goog.loadModuleFromSource_.call(goog.global, moduleDef); - } else { - throw Error('Invalid module definition'); - } - - var moduleName = goog.moduleLoaderState_.moduleName; - if (!goog.isString(moduleName) || !moduleName) { - throw Error('Invalid module name \"' + moduleName + '\"'); - } - - // Don't seal legacy namespaces as they may be uses as a parent of - // another namespace - if (goog.moduleLoaderState_.declareLegacyNamespace) { - goog.constructNamespace_(moduleName, exports); - } else if (goog.SEAL_MODULE_EXPORTS && Object.seal) { - Object.seal(exports); - } - - goog.loadedModules_[moduleName] = exports; - } finally { - goog.moduleLoaderState_ = previousState; - } - }; - - - /** - * @private @const {function(string):?} - * - * The new type inference warns because this function has no formal - * parameters, but its jsdoc says that it takes one argument. - * (The argument is used via arguments[0], but NTI does not detect this.) - * @suppress {newCheckTypes} - */ - goog.loadModuleFromSource_ = function() { - // NOTE: we avoid declaring parameters or local variables here to avoid - // masking globals or leaking values into the module definition. - 'use strict'; - var exports = {}; - eval(arguments[0]); - return exports; + goog.retrieveAndExec_(url, true, false); }; @@ -1208,18 +1233,17 @@ if (goog.DEPENDENCIES_ENABLED) { } } - var isOldIE = goog.IS_OLD_IE_; - if (opt_sourceText === undefined) { - if (!isOldIE) { + if (!goog.IS_OLD_IE_) { if (goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING) { goog.appendScriptSrcNode_(src); } else { goog.writeScriptSrcNode_(src); } } else { - var state = " onreadystatechange='goog.onScriptLoad_(this, " + - ++goog.lastNonModuleScriptIndex_ + ")' "; + goog.oldIeWaiting_ = true; + var state = ' onreadystatechange=\'goog.onScriptLoad_(this, ' + + ++goog.lastNonModuleScriptIndex_ + ')\' '; doc.write( ' + + +

Closure Performance Tests - byteArrayToString

+
+
+ + + + diff --git a/third_party/closure/goog/crypt/bytestring_perf.js b/third_party/closure/goog/crypt/bytestring_perf.js new file mode 100644 index 0000000000000..7f9fe7b08330e --- /dev/null +++ b/third_party/closure/goog/crypt/bytestring_perf.js @@ -0,0 +1,125 @@ +// Copyright 2014 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Performance test for different implementations of + * byteArrayToString. + */ + + +goog.provide('goog.crypt.byteArrayToStringPerf'); + +goog.require('goog.array'); +goog.require('goog.dom'); +goog.require('goog.testing.PerformanceTable'); + +goog.setTestOnly('goog.crypt.byteArrayToStringPerf'); + + +var table = new goog.testing.PerformanceTable(goog.dom.getElement('perfTable')); + + +var BYTES_LENGTH = Math.pow(2, 20); +var CHUNK_SIZE = 8192; + +function getBytes() { + var bytes = []; + for (var i = 0; i < BYTES_LENGTH; i++) { + bytes.push('A'.charCodeAt(0)); + } + return bytes; +} + +function copyAndSpliceByteArray(bytes) { + // Copy the passed byte array since we're going to destroy it. + var remainingBytes = goog.array.clone(bytes); + var strings = []; + + // Convert each chunk to a string. + while (remainingBytes.length) { + var chunk = goog.array.splice(remainingBytes, 0, CHUNK_SIZE); + strings.push(String.fromCharCode.apply(null, chunk)); + } + return strings.join(''); +} + +function sliceByteArrayConcat(bytes) { + var str = ''; + for (var i = 0; i < bytes.length; i += CHUNK_SIZE) { + var chunk = goog.array.slice(bytes, i, i + CHUNK_SIZE); + str += String.fromCharCode.apply(null, chunk); + } + return str; +} + + +function sliceByteArrayJoin(bytes) { + var strings = []; + for (var i = 0; i < bytes.length; i += CHUNK_SIZE) { + var chunk = goog.array.slice(bytes, i, i + CHUNK_SIZE); + strings.push(String.fromCharCode.apply(null, chunk)); + } + return strings.join(''); +} + +function mapByteArray(bytes) { + var strings = goog.array.map(bytes, String.fromCharCode); + return strings.join(''); +} + +function forLoopByteArrayConcat(bytes) { + var str = ''; + for (var i = 0; i < bytes.length; i++) { + str += String.fromCharCode(bytes[i]); + } + return str; +} + +function forLoopByteArrayJoin(bytes) { + var strs = []; + for (var i = 0; i < bytes.length; i++) { + strs.push(String.fromCharCode(bytes[i])); + } + return strs.join(''); +} + + +function run() { + var bytes = getBytes(); + table.run( + goog.partial(copyAndSpliceByteArray, getBytes()), + 'Copy array and splice out chunks.'); + + table.run( + goog.partial(sliceByteArrayConcat, getBytes()), + 'Slice out copies of the byte array, concatenating results'); + + table.run( + goog.partial(sliceByteArrayJoin, getBytes()), + 'Slice out copies of the byte array, joining results'); + + table.run( + goog.partial(forLoopByteArrayConcat, getBytes()), + 'Use for loop with concat.'); + + table.run( + goog.partial(forLoopByteArrayJoin, getBytes()), + 'Use for loop with join.'); + + // Purposefully commented out. This ends up being tremendously expensive. + // table.run(goog.partial(mapByteArray, getBytes()), + // 'Use goog.array.map and fromCharCode.'); +} + +run(); diff --git a/third_party/closure/goog/crypt/cbc.js b/third_party/closure/goog/crypt/cbc.js index f55108e856d36..b3d3f9ce233bc 100644 --- a/third_party/closure/goog/crypt/cbc.js +++ b/third_party/closure/goog/crypt/cbc.js @@ -25,6 +25,7 @@ goog.provide('goog.crypt.Cbc'); goog.require('goog.array'); goog.require('goog.asserts'); goog.require('goog.crypt'); +goog.require('goog.crypt.BlockCipher'); @@ -34,13 +35,11 @@ goog.require('goog.crypt'); * #Cipher-block_chaining_.28CBC.29 * * @param {!goog.crypt.BlockCipher} cipher The block cipher to use. - * @param {number=} opt_blockSize The block size of the cipher in bytes. - * Defaults to 16 bytes. * @constructor * @final * @struct */ -goog.crypt.Cbc = function(cipher, opt_blockSize) { +goog.crypt.Cbc = function(cipher) { /** * Block cipher. @@ -48,13 +47,6 @@ goog.crypt.Cbc = function(cipher, opt_blockSize) { * @private */ this.cipher_ = cipher; - - /** - * Block size in bytes. - * @type {number} - * @private - */ - this.blockSize_ = opt_blockSize || 16; }; @@ -70,11 +62,11 @@ goog.crypt.Cbc = function(cipher, opt_blockSize) { goog.crypt.Cbc.prototype.encrypt = function(plainText, initialVector) { goog.asserts.assert( - plainText.length % this.blockSize_ == 0, + plainText.length % this.cipher_.BLOCK_SIZE == 0, 'Data\'s length must be multiple of block size.'); goog.asserts.assert( - initialVector.length == this.blockSize_, + initialVector.length == this.cipher_.BLOCK_SIZE, 'Initial vector must be size of one block.'); // Implementation of @@ -85,10 +77,10 @@ goog.crypt.Cbc.prototype.encrypt = function(plainText, initialVector) { // Generate each block of the encrypted cypher text. for (var blockStartIndex = 0; blockStartIndex < plainText.length; - blockStartIndex += this.blockSize_) { + blockStartIndex += this.cipher_.BLOCK_SIZE) { // Takes one block from the input message. var plainTextBlock = goog.array.slice( - plainText, blockStartIndex, blockStartIndex + this.blockSize_); + plainText, blockStartIndex, blockStartIndex + this.cipher_.BLOCK_SIZE); var input = goog.crypt.xorByteArray(plainTextBlock, vector); var resultBlock = this.cipher_.encrypt(input); @@ -113,11 +105,11 @@ goog.crypt.Cbc.prototype.encrypt = function(plainText, initialVector) { goog.crypt.Cbc.prototype.decrypt = function(cipherText, initialVector) { goog.asserts.assert( - cipherText.length % this.blockSize_ == 0, + cipherText.length % this.cipher_.BLOCK_SIZE == 0, 'Data\'s length must be multiple of block size.'); goog.asserts.assert( - initialVector.length == this.blockSize_, + initialVector.length == this.cipher_.BLOCK_SIZE, 'Initial vector must be size of one block.'); // Implementation of @@ -131,7 +123,7 @@ goog.crypt.Cbc.prototype.decrypt = function(cipherText, initialVector) { while (blockStartIndex < cipherText.length) { // Takes one block. var cipherTextBlock = goog.array.slice( - cipherText, blockStartIndex, blockStartIndex + this.blockSize_); + cipherText, blockStartIndex, blockStartIndex + this.cipher_.BLOCK_SIZE); var resultBlock = this.cipher_.decrypt(cipherTextBlock); var plainTextBlock = goog.crypt.xorByteArray(vector, resultBlock); @@ -139,7 +131,7 @@ goog.crypt.Cbc.prototype.decrypt = function(cipherText, initialVector) { goog.array.extend(plainText, plainTextBlock); vector = cipherTextBlock; - blockStartIndex += this.blockSize_; + blockStartIndex += this.cipher_.BLOCK_SIZE; } return plainText; diff --git a/third_party/closure/goog/crypt/crypt_perf.html b/third_party/closure/goog/crypt/crypt_perf.html new file mode 100644 index 0000000000000..5f073759f5a04 --- /dev/null +++ b/third_party/closure/goog/crypt/crypt_perf.html @@ -0,0 +1,85 @@ + + + + + + + Closure Performance Tests - UTF8 encoding and decoding + + + + + +

Closure Performance Tests - UTF8 encoding and decoding

+

+ User-agent: + +

+
+
+ + + + diff --git a/third_party/closure/goog/crypt/ctr.js b/third_party/closure/goog/crypt/ctr.js new file mode 100644 index 0000000000000..d2f9a6b8d8de7 --- /dev/null +++ b/third_party/closure/goog/crypt/ctr.js @@ -0,0 +1,112 @@ +// Copyright 2016 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.crypt.Ctr'); + +goog.require('goog.array'); +goog.require('goog.asserts'); +goog.require('goog.crypt'); + +/** + * Implementation of Ctr mode for block ciphers. See + * http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation + * #Cipher-block_chaining_.28Ctr.29. for an overview, and + * http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf + * for the spec. + * + * @param {!goog.crypt.BlockCipher} cipher The block cipher to use. + * @constructor + * @final + * @struct + */ +goog.crypt.Ctr = function(cipher) { + + /** + * Block cipher. + * @type {!goog.crypt.BlockCipher} + * @private + */ + this.cipher_ = cipher; +}; + +/** + * Encrypts a message. + * + * @param {!Array|!Uint8Array} plainText Message to encrypt. An array of + * bytes. The length does not have to be a multiple of the blocksize. + * @param {!Array|!Uint8Array} initialVector Initial vector for the Ctr + * mode. An array of bytes with the same length as the block size, that + * should be not reused when using the same key. + * @return {!Array} Encrypted message. + */ +goog.crypt.Ctr.prototype.encrypt = function(plainText, initialVector) { + + goog.asserts.assert( + initialVector.length == this.cipher_.BLOCK_SIZE, + 'Initial vector must be size of one block.'); + + // Copy the IV, so it's not modified. + var counter = goog.array.clone(initialVector); + + var keyStreamBlock = []; + var encryptedArray = []; + var plainTextBlock = []; + + while (encryptedArray.length < plainText.length) { + keyStreamBlock = this.cipher_.encrypt(counter); + goog.crypt.Ctr.incrementBigEndianCounter_(counter); + + plainTextBlock = goog.array.slice( + plainText, encryptedArray.length, + encryptedArray.length + this.cipher_.BLOCK_SIZE); + goog.array.extend( + encryptedArray, + goog.crypt.xorByteArray( + plainTextBlock, + goog.array.slice(keyStreamBlock, 0, plainTextBlock.length))); + } + + return encryptedArray; +}; + + +/** + * Decrypts a message. In CTR, this is the same as encrypting. + * + * @param {!Array|!Uint8Array} cipherText Message to decrypt. The length + * does not have to be a multiple of the blocksize. + * @param {!Array|!Uint8Array} initialVector Initial vector for the Ctr + * mode. An array of bytes with the same length as the block size. + * @return {!Array} Decrypted message. + */ +goog.crypt.Ctr.prototype.decrypt = goog.crypt.Ctr.prototype.encrypt; + +/** + * Increments the big-endian integer represented in counter in-place. + * + * @param {!Array|!Uint8Array} counter The array of bytes to modify. + * @private + */ +goog.crypt.Ctr.incrementBigEndianCounter_ = function(counter) { + for (var i = counter.length - 1; i >= 0; i--) { + var currentByte = counter[i]; + currentByte = (currentByte + 1) & 0xFF; // Allow wrapping around. + counter[i] = currentByte; + if (currentByte != 0) { + // This iteration hasn't wrapped around, which means there is + // no carry to add to the next byte. + return; + } // else, repeat with next byte. + } +}; diff --git a/third_party/closure/goog/crypt/hash.js b/third_party/closure/goog/crypt/hash.js index 51209be61d51d..cef806aef9637 100644 --- a/third_party/closure/goog/crypt/hash.js +++ b/third_party/closure/goog/crypt/hash.js @@ -45,7 +45,7 @@ goog.crypt.Hash.prototype.reset = goog.abstractMethod; /** - * Adds a byte array (array with values in [0-255] range) or a string (might + * Adds a byte array (array with values in [0-255] range) or a string (must * only contain 8-bit, i.e., Latin1 characters) to the internal accumulator. * * Many hash functions operate on blocks of data and implement optimizations diff --git a/third_party/closure/goog/crypt/md5_perf.html b/third_party/closure/goog/crypt/md5_perf.html new file mode 100644 index 0000000000000..57aa8c88f8724 --- /dev/null +++ b/third_party/closure/goog/crypt/md5_perf.html @@ -0,0 +1,39 @@ + + + + + +Closure Performance Tests - goog.crypt.Md5 + + + + + +

Closure Performance Tests - goog.crypt.Md5

+

+User-agent: + +

+ + + diff --git a/third_party/closure/goog/crypt/sha1_perf.html b/third_party/closure/goog/crypt/sha1_perf.html new file mode 100644 index 0000000000000..b26c155340d71 --- /dev/null +++ b/third_party/closure/goog/crypt/sha1_perf.html @@ -0,0 +1,40 @@ + + + + + +Closure Performance Tests - goog.crypt.Sha1 + + + + + +

Closure Performance Tests - goog.crypt.Sha1

+

+User-agent: + +

+ + + + diff --git a/third_party/closure/goog/crypt/sha224_perf.html b/third_party/closure/goog/crypt/sha224_perf.html new file mode 100644 index 0000000000000..4c39e6440a56b --- /dev/null +++ b/third_party/closure/goog/crypt/sha224_perf.html @@ -0,0 +1,40 @@ + + + + + +Closure Performance Tests - goog.crypt.Sha224 + + + + + +

Closure Performance Tests - goog.crypt.Sha224

+

+User-agent: + +

+ + + + diff --git a/third_party/closure/goog/crypt/sha256_perf.html b/third_party/closure/goog/crypt/sha256_perf.html new file mode 100644 index 0000000000000..0b7922b3f232a --- /dev/null +++ b/third_party/closure/goog/crypt/sha256_perf.html @@ -0,0 +1,40 @@ + + + + + +Closure Performance Tests - goog.crypt.Sha256 + + + + + +

Closure Performance Tests - goog.crypt.Sha256

+

+User-agent: + +

+ + + + diff --git a/third_party/closure/goog/crypt/sha2_64bit.js b/third_party/closure/goog/crypt/sha2_64bit.js index cb87f74c23f20..e45a6a078c24f 100644 --- a/third_party/closure/goog/crypt/sha2_64bit.js +++ b/third_party/closure/goog/crypt/sha2_64bit.js @@ -58,9 +58,8 @@ goog.crypt.Sha2_64bit = function(numHashBlocks, initHashBlocks) { * {@code this.blocksize} bytes, we feed it into [@code computeChunk_}. * @private {!Uint8Array|!Array} */ - this.chunk_ = goog.isDef(goog.global.Uint8Array) ? - new Uint8Array(goog.crypt.Sha2_64bit.BLOCK_SIZE_) : - new Array(goog.crypt.Sha2_64bit.BLOCK_SIZE_); + this.chunk_ = goog.global['Uint8Array'] ? new Uint8Array(this.blockSize) : + new Array(this.blockSize); /** * Current number of bytes in {@code this.chunk_}. @@ -174,7 +173,7 @@ goog.crypt.Sha2_64bit.prototype.update = function(message, opt_length) { chunkBytes = 0; } } - } else if (goog.isArray(message)) { + } else if (goog.isArrayLike(message)) { for (var i = 0; i < length; i++) { var b = message[i]; // Hack: b|0 coerces b to an integer, so the last part confirms that diff --git a/third_party/closure/goog/crypt/sha512_perf.html b/third_party/closure/goog/crypt/sha512_perf.html new file mode 100644 index 0000000000000..e7cf5d21acc90 --- /dev/null +++ b/third_party/closure/goog/crypt/sha512_perf.html @@ -0,0 +1,41 @@ + + + + + +Closure Performance Tests - goog.crypt.Sha512 + + + + + +

Closure Performance Tests - goog.crypt.Sha512

+

+User-agent: + +

+ + + + diff --git a/third_party/closure/goog/cssom/cssom.js b/third_party/closure/goog/cssom/cssom.js index daee0bad17a00..1ff34e2561ee5 100644 --- a/third_party/closure/goog/cssom/cssom.js +++ b/third_party/closure/goog/cssom/cssom.js @@ -17,8 +17,6 @@ * References: * - W3C: http://dev.w3.org/csswg/cssom/ * - MSDN: http://msdn.microsoft.com/en-us/library/ms531209(VS.85).aspx. - * @supported in FF3, IE6, IE7, Safari 3.1.2, Chrome - * TODO(user): Fix in Opera. * TODO(user): Consider hacking page, media, etc.. to work. * This would be pretty challenging. IE returns the text for any rule * regardless of whether or not the media is correct or not. Firefox at @@ -142,7 +140,8 @@ goog.cssom.getAllCssStyleSheets = function( // http://dev.w3.org/csswg/cssom/#the-stylesheetlist for (var i = 0, n = styleSheet.length; i < n; i++) { goog.array.extend( - styleSheetsOutput, goog.cssom.getAllCssStyleSheets(styleSheet[i])); + styleSheetsOutput, goog.cssom.getAllCssStyleSheets( + /** @type {!CSSStyleSheet} */ (styleSheet[i]))); } } else { // We need to walk through rules in browsers which implement .cssRules @@ -216,8 +215,8 @@ goog.cssom.getCssTextFromCssRule = function(cssRule) { goog.cssom.getCssRuleIndexInParentStyleSheet = function( cssRule, opt_parentStyleSheet) { // Look for our special style.ruleIndex property from getAllCss. - if (cssRule.style && cssRule.style['-closure-rule-index']) { - return cssRule.style['-closure-rule-index']; + if (cssRule.style && /** @type {!Object} */ (cssRule.style)['-closure-rule-index']) { + return (/** @type {!Object} */ (cssRule.style))['-closure-rule-index']; } var parentStyleSheet = @@ -251,7 +250,8 @@ goog.cssom.getCssRuleIndexInParentStyleSheet = function( */ goog.cssom.getParentStyleSheet = function(cssRule) { return cssRule.parentStyleSheet || - cssRule.style && cssRule.style['-closure-parent-stylesheet']; + cssRule.style && + (/** @type {!Object} */ (cssRule.style))['-closure-parent-stylesheet']; }; @@ -354,11 +354,11 @@ goog.cssom.removeCssRule = function(cssStyleSheet, index) { * @return {!Element} The newly created STYLE element. */ goog.cssom.addCssText = function(cssText, opt_domHelper) { - var document = - opt_domHelper ? opt_domHelper.getDocument() : goog.dom.getDocument(); - var cssNode = document.createElement(goog.dom.TagName.STYLE); + var domHelper = opt_domHelper || goog.dom.getDomHelper(); + var document = domHelper.getDocument(); + var cssNode = domHelper.createElement(goog.dom.TagName.STYLE); cssNode.type = 'text/css'; - var head = document.getElementsByTagName(goog.dom.TagName.HEAD)[0]; + var head = domHelper.getElementsByTagName(goog.dom.TagName.HEAD)[0]; head.appendChild(cssNode); if (cssNode.styleSheet) { // IE. @@ -430,7 +430,8 @@ goog.cssom.getAllCss_ = function(styleSheet, isTextOutput) { // Unfortunately we have to use the style object to store these // pieces of info since the rule object is read-only. if (!cssRule.parentStyleSheet) { - cssRule.style['-closure-parent-stylesheet'] = styleSheet; + (/** @type {!Object} */ (cssRule.style))[ + '-closure-parent-stylesheet'] = styleSheet; } // This is a hack to help with possible removal of the rule later, @@ -438,7 +439,7 @@ goog.cssom.getAllCss_ = function(styleSheet, isTextOutput) { // onto the style object as a property. // Unfortunately we have to use the style object to store these // pieces of info since the rule object is read-only. - cssRule.style['-closure-rule-index'] = + (/** @type {!Object} */ (cssRule.style))['-closure-rule-index'] = isTextOutput ? undefined : ruleIndex; } cssOut.push(cssRule); diff --git a/third_party/closure/goog/cssom/iframe/style.js b/third_party/closure/goog/cssom/iframe/style.js index f10be20ccc016..660e182269b5b 100644 --- a/third_party/closure/goog/cssom/iframe/style.js +++ b/third_party/closure/goog/cssom/iframe/style.js @@ -785,7 +785,7 @@ goog.cssom.iframe.style.getElementContext = function( goog.cssom.iframe.style.isTransparentValue_( computedStyle['backgroundColor'])) { // opt_useAncestorBackgroundRules means that, if the original element - // has a transparent backgorund, background properties rules should be + // has a transparent background, background properties rules should be // added to explicitly make the body have the same background appearance // as in the original element, even if its positioned somewhere else // in the DOM. diff --git a/third_party/closure/goog/datasource/datamanager.js b/third_party/closure/goog/datasource/datamanager.js index 24c366874e800..89fb498bd25ec 100644 --- a/third_party/closure/goog/datasource/datamanager.js +++ b/third_party/closure/goog/datasource/datamanager.js @@ -476,7 +476,7 @@ goog.ds.DataManager.prototype.removeListenersByFunction_ = function( * @return {number} Number of listeners. */ goog.ds.DataManager.prototype.getListenerCount = function() { - var count = 0; + var /** number */ count = 0; goog.object.forEach(this.listenerMap_, function(matchingListeners) { count += goog.structs.getCount(matchingListeners); }); diff --git a/third_party/closure/goog/datasource/expr.js b/third_party/closure/goog/datasource/expr.js index 097f2a088c102..bff1e6304ce4f 100644 --- a/third_party/closure/goog/datasource/expr.js +++ b/third_party/closure/goog/datasource/expr.js @@ -190,6 +190,8 @@ goog.ds.Expr.prototype.getNext = function() { * @param {goog.ds.DataNode=} opt_ds Optional datasource to evaluate against. * If not provided, evaluates against DataManager global root. * @return {*} Value of the node, or null if doesn't exist. + * @suppress {missingRequire} Cannot depend on goog.ds.DataManager because + * it creates a circular dependency. */ goog.ds.Expr.prototype.getValue = function(opt_ds) { if (opt_ds == null) { @@ -262,6 +264,8 @@ goog.ds.Expr.prototype.getNode = function(opt_ds, opt_canCreate) { * @return {goog.ds.DataNode|goog.ds.DataNodeList} Matching node or nodes, * depending on value of opt_selectOne. * @private + * @suppress {missingRequire} Cannot depend on goog.ds.DataManager because + * it creates a circular dependency. */ goog.ds.Expr.prototype.getNodes_ = function( opt_ds, opt_selectOne, opt_canCreate) { diff --git a/third_party/closure/goog/datasource/jsxmlhttpdatasource.js b/third_party/closure/goog/datasource/jsxmlhttpdatasource.js index 11a56add11569..d540468d2cbf9 100644 --- a/third_party/closure/goog/datasource/jsxmlhttpdatasource.js +++ b/third_party/closure/goog/datasource/jsxmlhttpdatasource.js @@ -15,8 +15,7 @@ /** * @fileoverview * DataSource implementation that uses XMLHttpRequest as transport, with - * response as serialized JS object (not required to be JSON) that can - * be evaluated and set to a variable. + * response as valid JSON. * * Response can have unexecutable starting/ending text to prevent inclusion * using '); + } catch (e) { + // ignore + } + assertFalse(window.xssFiredInertDocument); +} + + +/** + * @suppress {accessControls} + */ +function testInertCustomElements() { + if (typeof HTMLTemplateElement != 'function' || !document.registerElement) { + return; // skip test + } + + var inertDoc = goog.html.sanitizer.CssSanitizer.createInertDocument_(); + var xFooConstructor = document.registerElement('x-foo'); + var xFooElem = + document.implementation.createHTMLDocument('').createElement('x-foo'); + assertTrue(xFooElem instanceof xFooConstructor); // sanity check + + var inertXFooElem = inertDoc.createElement('x-foo'); + assertFalse(inertXFooElem instanceof xFooConstructor); +} diff --git a/third_party/closure/goog/html/sanitizer/htmlsanitizer.js b/third_party/closure/goog/html/sanitizer/htmlsanitizer.js new file mode 100644 index 0000000000000..1aecb100ce6f1 --- /dev/null +++ b/third_party/closure/goog/html/sanitizer/htmlsanitizer.js @@ -0,0 +1,1483 @@ +// Copyright 2016 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +/** + * @fileoverview An HTML sanitizer that can satisfy a variety of security + * policies. + * + * This package provides html sanitizing functions. It does not enforce string + * to string conversion, instead returning a dom-like element when possible. + * + * Examples of usage of the static {@code goog.goog.html.sanitizer.sanitize}: + *
+ *   var safeHtml = goog.html.sanitizer.sanitize('';
+  var expected = '';
+  // TODO(pelizzi): use unblockTag once it's available
+  delete goog.html.sanitizer.TagBlacklist['TEMPLATE'];
+  var builder = new goog.html.sanitizer.HtmlSanitizer.Builder();
+  goog.html.sanitizer.unsafe.alsoAllowTags(just, builder, ['TEMPLATE']);
+  assertSanitizedHtml(input, expected, builder.build());
+  goog.html.sanitizer.TagBlacklist['TEMPLATE'] = true;
+}
+
+
+function testOnlyAllowEmptyAttrList() {
+  var input = '

b

' + + 'c'; + var expected = '

b

c'; + assertSanitizedHtml( + input, expected, new goog.html.sanitizer.HtmlSanitizer.Builder() + .onlyAllowAttributes([]) + .build()); +} + + +function testOnlyAllowUnWhitelistedAttr() { + assertThrows(function() { + new goog.html.sanitizer.HtmlSanitizer.Builder().onlyAllowAttributes( + ['alt', 'zzz']); + }); +} + + +function testOnlyAllowAttributeWildCard() { + var input = + '
yep
'; + var expected = '
yep
'; + assertSanitizedHtml( + input, expected, + new goog.html.sanitizer.HtmlSanitizer.Builder() + .onlyAllowAttributes([{tagName: '*', attributeName: 'alt'}]) + .build()); +} + + +function testOnlyAllowAttributeLabelForA() { + var input = 'fff'; + var expected = 'fff'; + assertSanitizedHtml( + input, expected, new goog.html.sanitizer.HtmlSanitizer.Builder() + .onlyAllowAttributes([{ + tagName: '*', + attributeName: 'label', + policy: function(value, hints) { + if (hints.tagName !== 'a') { + return null; + } + return value; + } + }]) + .build()); +} + + +function testOnlyAllowAttributePolicy() { + var input = 'yesno'; + var expected = 'yes'; + assertSanitizedHtml( + input, expected, new goog.html.sanitizer.HtmlSanitizer.Builder() + .onlyAllowAttributes([{ + tagName: '*', + attributeName: 'alt', + policy: function(value, hints) { + assertEquals(hints.attributeName, 'alt'); + return value === 'yes' ? value : null; + } + }]) + .build()); +} + + +function testOnlyAllowAttributePolicyPipe1() { + var input = 'b'; + var expected = 'b'; + assertSanitizedHtml( + input, expected, new goog.html.sanitizer.HtmlSanitizer.Builder() + .onlyAllowAttributes([{ + tagName: 'a', + attributeName: 'target', + policy: function(value, hints) { + assertEquals(hints.attributeName, 'target'); + return '_blank'; + } + }]) + .build()); +} + + +function testOnlyAllowAttributePolicyPipe2() { + var input = 'b'; + var expected = 'b'; + assertSanitizedHtml( + input, expected, new goog.html.sanitizer.HtmlSanitizer.Builder() + .onlyAllowAttributes([{ + tagName: 'a', + attributeName: 'target', + policy: function(value, hints) { + assertEquals(hints.attributeName, 'target'); + return 'nope'; + } + }]) + .build()); +} + + +function testOnlyAllowAttributeSpecificPolicyThrows() { + assertThrows(function() { + new goog.html.sanitizer.HtmlSanitizer.Builder().onlyAllowAttributes([ + {tagName: 'img', attributeName: 'src', policy: goog.functions.identity} + ]); + }); +} + + +function testOnlyAllowAttributeGenericPolicyThrows() { + assertThrows(function() { + new goog.html.sanitizer.HtmlSanitizer.Builder().onlyAllowAttributes([{ + tagName: '*', + attributeName: 'target', + policy: goog.functions.identity + }]); + }); +} + + +function testOnlyAllowAttributeRefineThrows() { + var builder = + new goog.html.sanitizer.HtmlSanitizer.Builder() + .onlyAllowAttributes( + ['aria-checked', {tagName: 'LINK', attributeName: 'HREF'}]) + .onlyAllowAttributes(['aria-checked']); + assertThrows(function() { + builder.onlyAllowAttributes(['alt']); + }); +} + + +function testUrlWithCredentials() { + if (isIE8() || isIE9()) { + return; + } + // IE has trouble getting and setting URL attributes with credentials. Both + // HTMLSanitizer and assertHtmlMatches are affected by the bug, hence the use + // of plain string matching. + var url = 'http://foo:bar@example.com'; + var input = '
' + + '
'; + var expectedIE = '
'; + var sanitizer = + new goog.html.sanitizer.HtmlSanitizer.Builder() + .withCustomNetworkRequestUrlPolicy(goog.html.SafeUrl.sanitize) + .allowCssStyles() + .build(); + if (goog.userAgent.EDGE_OR_IE) { + assertEquals( + expectedIE, goog.html.SafeHtml.unwrap(sanitizer.sanitize(input))); + } else { + assertSanitizedHtml(input, input, sanitizer); + } +} diff --git a/third_party/closure/goog/html/sanitizer/tagblacklist.js b/third_party/closure/goog/html/sanitizer/tagblacklist.js new file mode 100644 index 0000000000000..ef4956fb27a1b --- /dev/null +++ b/third_party/closure/goog/html/sanitizer/tagblacklist.js @@ -0,0 +1,54 @@ +// Copyright 2016 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +/** + * @fileoverview Contains the tag blacklist for use in the Html sanitizer. + */ + +goog.provide('goog.html.sanitizer.TagBlacklist'); + + +/** + * A list of tags which should be removed entirely from the DOM, rather than + * merely being made inert. Tag names must be in all caps. Note that even if + * TEMPLATE is removed from this blacklist (or even whitelisted) it will + * continue to be removed from the HTML as TEMPLATE is used interally to + * denote nodes which should not be added to the sanitized HTML. + * @const @dict {boolean} + */ +goog.html.sanitizer.TagBlacklist = { + 'APPLET': true, + 'AUDIO': true, + 'BASE': true, + 'BGSOUND': true, + 'EMBED': true, + // NOTE: can remove this for old browser behavior + 'IFRAME': true, + // Can result in network requests + 'ISINDEX': true, + // Unused and just unnecessarily increase attack surface + 'KEYGEN': true, + 'LAYER': true, + 'LINK': true, + 'META': true, + 'OBJECT': true, + 'SCRIPT': true, + // Can result in an XSS in FF + // https://bugzilla.mozilla.org/show_bug.cgi?id=1205631 + 'SVG': true, + 'STYLE': true, + 'TEMPLATE': true, + 'VIDEO': true +}; diff --git a/third_party/closure/goog/html/sanitizer/tagwhitelist.js b/third_party/closure/goog/html/sanitizer/tagwhitelist.js new file mode 100644 index 0000000000000..97079df7325b1 --- /dev/null +++ b/third_party/closure/goog/html/sanitizer/tagwhitelist.js @@ -0,0 +1,125 @@ +// Copyright 2016 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +/** + * @fileoverview Contains the tag whitelist for use in the Html sanitizer. + */ + +goog.provide('goog.html.sanitizer.TagWhitelist'); + + +/** + * A tag whitelist for allowed tags. Tag names must be in all caps. + * @const @dict {boolean} + */ +goog.html.sanitizer.TagWhitelist = { + 'A': true, // HTMLAnchorElement + 'ABBR': true, // HTMLElement + 'ACRONYM': true, // HTMLElement + 'ADDRESS': true, // HTMLElement + 'AREA': true, // HTMLAreaElement + 'ARTICLE': true, // HTMLElement + 'ASIDE': true, // HTMLElement + 'B': true, // HTMLElement + 'BDI': true, // HTMLElement + 'BDO': true, // HTMLElement + 'BIG': true, // HTMLElement + 'BLOCKQUOTE': true, // HTMLQuoteElement + 'BR': true, // HTMLBRElement + 'BUTTON': true, // HTMLButtonElement + 'CAPTION': true, // HTMLTableCaptionElement + 'CENTER': true, // HTMLElement + 'CITE': true, // HTMLElement + 'CODE': true, // HTMLElement + 'COL': true, // HTMLTableColElement + 'COLGROUP': true, // HTMLTableColElement + 'DATA': true, // HTMLElement + 'DATALIST': true, // HTMLDataListElement + 'DD': true, // HTMLElement + 'DEL': true, // HTMLModElement + 'DETAILS': true, // HTMLDetailsElement + 'DFN': true, // HTMLElement + 'DIALOG': true, // HTMLDialogElement + 'DIR': true, // HTMLDirectoryElement + 'DIV': true, // HTMLDivElement + 'DL': true, // HTMLDListElement + 'DT': true, // HTMLElement + 'EM': true, // HTMLElement + 'FIELDSET': true, // HTMLFieldSetElement + 'FIGCAPTION': true, // HTMLElement + 'FIGURE': true, // HTMLElement + 'FONT': true, // HTMLFontElement + 'FOOTER': true, // HTMLElement + // Disallowed by default via tagBlacklist unless allowed via the builder. + 'FORM': true, // HTMLFormElement + 'H1': true, // HTMLHeadingElement + 'H2': true, // HTMLHeadingElement + 'H3': true, // HTMLHeadingElement + 'H4': true, // HTMLHeadingElement + 'H5': true, // HTMLHeadingElement + 'H6': true, // HTMLHeadingElement + 'HEADER': true, // HTMLElement + 'HGROUP': true, // HTMLElement + 'HR': true, // HTMLHRElement + 'I': true, // HTMLElement + 'IMG': true, // HTMLImageElement + 'INPUT': true, // HTMLInputElement + 'INS': true, // HTMLModElement + 'KBD': true, // HTMLElement + 'LABEL': true, // HTMLLabelElement + 'LEGEND': true, // HTMLLegendElement + 'LI': true, // HTMLLIElement + 'MAIN': true, // HTMLElement + 'MAP': true, // HTMLMapElement + 'MARK': true, // HTMLElement + 'MENU': true, // HTMLMenuElement + 'METER': true, // HTMLMeterElement + 'NAV': true, // HTMLElement + 'NOSCRIPT': true, // HTMLElement + 'OL': true, // HTMLOListElement + 'OPTGROUP': true, // HTMLOptGroupElement + 'OPTION': true, // HTMLOptionElement + 'OUTPUT': true, // HTMLOutputElement + 'P': true, // HTMLParagraphElement + 'PRE': true, // HTMLPreElement + 'PROGRESS': true, // HTMLProgressElement + 'Q': true, // HTMLQuoteElement + 'S': true, // HTMLElement + 'SAMP': true, // HTMLElement + 'SECTION': true, // HTMLElement + 'SELECT': true, // HTMLSelectElement + 'SMALL': true, // HTMLElement + 'SOURCE': true, // HTMLSourceElement + 'SPAN': true, // HTMLSpanElement + 'STRIKE': true, // HTMLElement + 'STRONG': true, // HTMLElement + 'SUB': true, // HTMLElement + 'SUMMARY': true, // HTMLElement + 'SUP': true, // HTMLElement + 'TABLE': true, // HTMLTableElement + 'TBODY': true, // HTMLTableSectionElement + 'TD': true, // HTMLTableDataCellElement + 'TEXTAREA': true, // HTMLTextAreaElement + 'TFOOT': true, // HTMLTableSectionElement + 'TH': true, // HTMLTableHeaderCellElement + 'THEAD': true, // HTMLTableSectionElement + 'TIME': true, // HTMLTimeElement + 'TR': true, // HTMLTableRowElement + 'TT': true, // HTMLElement + 'U': true, // HTMLElement + 'UL': true, // HTMLUListElement + 'VAR': true, // HTMLElement + 'WBR': true // HTMLElement +}; diff --git a/third_party/closure/goog/html/sanitizer/unsafe.js b/third_party/closure/goog/html/sanitizer/unsafe.js new file mode 100644 index 0000000000000..9c512f8073107 --- /dev/null +++ b/third_party/closure/goog/html/sanitizer/unsafe.js @@ -0,0 +1,134 @@ +// Copyright 2016 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Potentially unsafe API for the HTML sanitizer. + * + * The HTML sanitizer enforces a default a safe policy, and also limits how the + * policy can be relaxed, so that developers cannot misconfigure it and + * introduce vulnerabilities. + * + * This file extends the HTML sanitizer's capabilities with potentially unsafe + * configuration options, such as the ability to extend the tag whitelist (e.g. + * to support web components). + * + * @supported IE 10+, Chrome 26+, Firefox 22+, Safari 7.1+, Opera 15+ + * @visibility {//closure/goog/html/sanitizer:approved_for_unsafe_config} + */ + +goog.provide('goog.html.sanitizer.unsafe'); + +goog.require('goog.asserts'); +goog.require('goog.html.sanitizer.HtmlSanitizer.Builder'); +goog.require('goog.string'); +goog.require('goog.string.Const'); + + +/** + * Extends the tag whitelist with the list of tags provided. + * + * IMPORTANT: Uses of this method must be carefully security-reviewed to ensure + * that the new tags do not introduce untrusted code execution or unsanctioned + * network activity. + * + * @param {!goog.string.Const} justification A constant string explaining why + * the addition of these tags to the whitelist is safe. May include a + * security review ticket number. + * @param {!goog.html.sanitizer.HtmlSanitizer.Builder} builder The builder + * whose tag whitelist should be extended. + * @param {!Array} tags A list of additional tags to allow through the + * sanitizer. Note that if the tag is also present in the blacklist, + * its addition to the whitelist has no effect. The tag names are + * case-insensitive. + * @return {!goog.html.sanitizer.HtmlSanitizer.Builder} + */ +goog.html.sanitizer.unsafe.alsoAllowTags = function( + justification, builder, tags) { + goog.asserts.assertString( + goog.string.Const.unwrap(justification), 'must provide justification'); + goog.asserts.assert( + !goog.string.isEmptyOrWhitespace(goog.string.Const.unwrap(justification)), + 'must provide non-empty justification'); + return builder.alsoAllowTagsPrivateDoNotAccessOrElse(tags); +}; + +/** + * Installs custom attribute policies for the attributes provided in the list. + * This can be used either on non-whitelisted attributes, effectively extending + * the attribute whitelist, or on attributes that are whitelisted and already + * have a policy, to override their policies. + * + * IMPORTANT: Uses of this method must be carefully security-reviewed to ensure + * that the new tags do not introduce untrusted code execution or unsanctioned + * network activity. + * + * @param {!goog.string.Const} justification A constant string explaining why + * the addition of these attributes to the whitelist is safe. May include a + * security review ticket number. + * @param {!goog.html.sanitizer.HtmlSanitizer.Builder} builder The builder + * whose attribute whitelist should be extended. + * @param {!Array<(string|!goog.html.sanitizer.HtmlSanitizerAttributePolicy)>} + * attrs A list of attributes whose policy should be overridden. Attributes + * can come in of two forms: + * - string: allow all values and just trim whitespaces for this attribute + * on all tags. + * - HtmlSanitizerAttributePolicy: allows specifying a policy for a + * particular tag. The tagName can be '*', which means all tags. If no + * policy is passed, the default is allow all values and just trim + * whitespaces. + * The tag and attribute names are case-insensitive. + * @return {!goog.html.sanitizer.HtmlSanitizer.Builder} + */ +goog.html.sanitizer.unsafe.alsoAllowAttributes = function( + justification, builder, attrs) { + goog.asserts.assertString( + goog.string.Const.unwrap(justification), 'must provide justification'); + goog.asserts.assert( + !goog.string.isEmptyOrWhitespace(goog.string.Const.unwrap(justification)), + 'must provide non-empty justification'); + return builder.alsoAllowAttributesPrivateDoNotAccessOrElse(attrs); +}; + + +/** + * Turns off sanitization of TEMPLATE tag descendants. The output is still + * safe to consume as a whole, but clients need to handle the contents of + * TEMPLATE nodes carefully, hence its definition in the unsafe package. + * + * Note that this only applies to descendants of unsanitized template tags, not + * to the tag itself, which must be manually added to the whitelist and removed + * from the blacklist. + * + * IMPORTANT: Uses of this method must be carefully security-reviewed to ensure + * that the new tags do not introduce untrusted code execution or unsanctioned + * network activity. + * + * @param {!goog.string.Const} justification A constant string explaining why + * the templates should not be sanitized, and why this is safe. May include + * a security review ticket number. + * @param {!goog.html.sanitizer.HtmlSanitizer.Builder} builder The builder + * whose template tag descendants should not be sanitized. + * @return {!goog.html.sanitizer.HtmlSanitizer.Builder} + * @throws {Error} Thrown if the browser does not support TEMPLATE tags. + * In this case, careful post-sanitization handling wouldn't matter. + */ +goog.html.sanitizer.unsafe.keepUnsanitizedTemplateContents = function( + justification, builder) { + goog.asserts.assertString( + goog.string.Const.unwrap(justification), 'must provide justification'); + goog.asserts.assert( + !goog.string.isEmptyOrWhitespace(goog.string.Const.unwrap(justification)), + 'must provide non-empty justification'); + return builder.keepUnsanitizedTemplateContentsPrivateDoNotAccessOrElse(); +}; diff --git a/third_party/closure/goog/html/sanitizer/unsafe_test.js b/third_party/closure/goog/html/sanitizer/unsafe_test.js new file mode 100644 index 0000000000000..f009b5591e4f7 --- /dev/null +++ b/third_party/closure/goog/html/sanitizer/unsafe_test.js @@ -0,0 +1,248 @@ +// Copyright 2016 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Unit Test for the unsafe API of the HTML Sanitizer. + */ + +goog.setTestOnly(); + +goog.require('goog.html.SafeHtml'); +goog.require('goog.html.sanitizer.HtmlSanitizer'); +goog.require('goog.html.sanitizer.TagBlacklist'); +goog.require('goog.html.sanitizer.unsafe'); + +goog.require('goog.string.Const'); +goog.require('goog.testing.dom'); +goog.require('goog.testing.jsunit'); +goog.require('goog.userAgent'); + +/** + * @return {boolean} Whether the browser is IE8 or below. + */ +function isIE8() { + return goog.userAgent.IE && !goog.userAgent.isVersionOrHigher(9); +} + + +/** + * @return {boolean} Whether the browser is IE9. + */ +function isIE9() { + return goog.userAgent.IE && !goog.userAgent.isVersionOrHigher(10) && !isIE8(); +} + + +var just = goog.string.Const.from('test'); + + +/** + * Sanitizes the original HTML and asserts that it is the same as the expected + * HTML. Supports adding tags and attributes through the unsafe API. + * @param {string} originalHtml + * @param {string} expectedHtml + * @param {?Array=} opt_tags + * @param {?Array<(string|!goog.html.sanitizer.HtmlSanitizerAttributePolicy)>=} + * opt_attrs + * @param {?goog.html.sanitizer.HtmlSanitizer.Builder=} opt_builder + */ +function assertSanitizedHtml( + originalHtml, expectedHtml, opt_tags, opt_attrs, opt_builder) { + var builder = opt_builder || new goog.html.sanitizer.HtmlSanitizer.Builder(); + if (opt_tags) + builder = goog.html.sanitizer.unsafe.alsoAllowTags(just, builder, opt_tags); + if (opt_attrs) + builder = goog.html.sanitizer.unsafe.alsoAllowAttributes( + just, builder, opt_attrs); + var sanitizer = builder.build(); + try { + var sanitized = sanitizer.sanitize(originalHtml); + if (isIE9()) { + assertEquals('', goog.html.SafeHtml.unwrap(sanitized)); + return; + } + goog.testing.dom.assertHtmlMatches( + expectedHtml, goog.html.SafeHtml.unwrap(sanitized), + true /* opt_strictAttributes */); + } catch (err) { + if (!isIE8()) { + throw err; + } + } +} + + +function testAllowEmptyTagList() { + var input = ''; + var expected = ''; + assertSanitizedHtml(input, expected, []); +} + + +function testAllowBlacklistedTag() { + var input = '
'; + var expected = '
'; + assertSanitizedHtml(input, expected, ['SCriPT']); +} + + +function testAllowUnknownTags() { + var input = 'aaa'; + var expected = 'aaa'; + assertSanitizedHtml(input, expected, ['HElLO', 'zZZ']); +} + + +function testAllowAlreadyWhiteListedTag() { + var input = '

'; + var expected = '

'; + assertSanitizedHtml(input, expected, ['p', 'ZZZ']); +} + + +function testAllowEmptyAttrList() { + var input = 'b'; + var expected = 'b'; + assertSanitizedHtml(input, expected, null, []); +} + + +function testAllowUnknownAttributeSimple() { + var input = ''; + var expected = ''; + assertSanitizedHtml(input, expected, null, ['Zzz']); +} + + +function testAllowUnknownAttributeWildCard() { + var input = '
'; + var expected = '
'; + assertSanitizedHtml( + input, expected, null, [{tagName: '*', attributeName: 'aB'}]); +} + + +function testAllowUnknownAttributeOnSpecificTag() { + var input = 'fff'; + var expected = 'fff'; + assertSanitizedHtml( + input, expected, null, [{tagName: 'a', attributeName: 'WwW'}]); +} + + +function testAllowUnknownAttributePolicy() { + var input = ''; + var expected = ''; + assertSanitizedHtml(input, expected, null, [{ + tagName: '*', + attributeName: 'aB', + policy: function(value, hints) { + assertEquals(hints.attributeName, 'ab'); + return value === 'yes' ? value : null; + } + }]); +} + + +function testAllowOverwriteAttrPolicy() { + var input = ''; + var expected = ''; + assertSanitizedHtml( + input, expected, null, [{ + tagName: 'a', + attributeName: 'href', + policy: function(value) { return value === 'yes' ? value : null; } + }]); +} + + +function testWhitelistAliasing() { + var builder = new goog.html.sanitizer.HtmlSanitizer.Builder(); + goog.html.sanitizer.unsafe.alsoAllowTags(just, builder, ['QqQ']); + goog.html.sanitizer.unsafe.alsoAllowAttributes(just, builder, ['QqQ']); + builder.build(); + assertUndefined(goog.html.sanitizer.TagWhitelist['QQQ']); + assertUndefined(goog.html.sanitizer.TagWhitelist['QqQ']); + assertUndefined(goog.html.sanitizer.TagWhitelist['qqq']); + assertUndefined(goog.html.sanitizer.AttributeWhitelist['* QQQ']); + assertUndefined(goog.html.sanitizer.AttributeWhitelist['* QqQ']); + assertUndefined(goog.html.sanitizer.AttributeWhitelist['* qqq']); +} + + +function testTemplateUnsanitized() { + if (!goog.html.sanitizer.HTML_SANITIZER_TEMPLATE_SUPPORTED) { + return; + } + var input = ''; + // TODO(pelizzi): use unblockTag once it's available + delete goog.html.sanitizer.TagBlacklist['TEMPLATE']; + var builder = new goog.html.sanitizer.HtmlSanitizer.Builder(); + goog.html.sanitizer.unsafe.keepUnsanitizedTemplateContents(just, builder); + assertSanitizedHtml(input, input, ['TEMPLATE'], null, builder); + goog.html.sanitizer.TagBlacklist['TEMPLATE'] = true; +} + + +function testTemplateSanitizedUnsanitizedXSS() { + if (!goog.html.sanitizer.HTML_SANITIZER_TEMPLATE_SUPPORTED) { + return; + } + var input = ''; + var expected = '

a

'; + delete goog.html.sanitizer.TagBlacklist['TEMPLATE']; + var builder = new goog.html.sanitizer.HtmlSanitizer.Builder(); + goog.html.sanitizer.unsafe.keepUnsanitizedTemplateContents(just, builder); + assertSanitizedHtml(input, expected, null, null, builder); + goog.html.sanitizer.TagBlacklist['TEMPLATE'] = true; +} + + +function testTemplateUnsanitizedThrowsIE() { + if (goog.html.sanitizer.HTML_SANITIZER_TEMPLATE_SUPPORTED) { + return; + } + var builder = new goog.html.sanitizer.HtmlSanitizer.Builder(); + assertThrows(function() { + goog.html.sanitizer.unsafe.keepUnsanitizedTemplateContents(just, builder); + }); +} + + +function testAllowRelaxExistingAttributePolicyWildcard() { + var input = ''; + // define a tag-specific one, takes precedence + assertSanitizedHtml( + input, input, null, + [{tagName: 'a', attributeName: 'href', policy: goog.functions.identity}]); + // overwrite the global one + assertSanitizedHtml( + input, input, null, + [{tagName: '*', attributeName: 'href', policy: goog.functions.identity}]); +} + + +function testAllowRelaxExistingAttributePolicySpecific() { + var input = ''; + var expected = ''; + // overwrite the global one, the specific one still has precedence + assertSanitizedHtml(input, expected, null, [ + {tagName: '*', attributeName: 'target', policy: goog.functions.identity} + ]); + // overwrite the tag-specific one, this one should take precedence + assertSanitizedHtml(input, input, null, [ + {tagName: 'a', attributeName: 'target', policy: goog.functions.identity} + ]); +} diff --git a/third_party/closure/goog/html/silverlight.js b/third_party/closure/goog/html/silverlight.js index a8377f004cf68..111abd244e7ea 100644 --- a/third_party/closure/goog/html/silverlight.js +++ b/third_party/closure/goog/html/silverlight.js @@ -52,11 +52,11 @@ goog.html.silverlight.FORBIDDEN_ATTRS_AND_PARAMS_ON_SILVERLIGHT_ = [ * - typemustmatch: "" (the empty string, meaning true for a boolean attribute) * * @param {!goog.html.TrustedResourceUrl} source The value of the source param. - * @param {!Object=} opt_params Mapping used to generate child + * @param {?Object=} opt_params Mapping used to generate child * param tags. Each tag has a name and value attribute, as defined in * mapping. Only names consisting of [a-zA-Z0-9-] are allowed. Value of * null or undefined causes the param tag to be omitted. - * @param {!Object=} opt_attributes + * @param {?Object=} opt_attributes * Mapping from other attribute names to their values. Only attribute names * consisting of [a-zA-Z0-9-] are allowed. Value of null or undefined causes * the attribute to be omitted. diff --git a/third_party/closure/goog/html/trustedresourceurl.js b/third_party/closure/goog/html/trustedresourceurl.js index 6181e24e94421..9d2265a3a6744 100644 --- a/third_party/closure/goog/html/trustedresourceurl.js +++ b/third_party/closure/goog/html/trustedresourceurl.js @@ -64,7 +64,7 @@ goog.html.TrustedResourceUrl = function() { /** * A type marker used to implement additional run-time type checking. * @see goog.html.TrustedResourceUrl#unwrap - * @const + * @const {!Object} * @private */ this.TRUSTED_RESOURCE_URL_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ = @@ -178,6 +178,114 @@ goog.html.TrustedResourceUrl.unwrap = function(trustedResourceUrl) { }; +/** + * Creates a TrustedResourceUrl from a format string and arguments. + * + * The arguments for interpolation into the format string map labels to values. + * Values of type `goog.string.Const` are interpolated without modifcation. + * Values of other types are cast to string and encoded with + * encodeURIComponent. + * + * `%{
... ". * * @param {!goog.html.SafeHtml} html for one row. - * @param {Document} doc object to hold the Element. + * @param {!goog.dom.DomHelper} dom DOM to hold the Element. * @return {Element} table row node created from the HTML. * @private */ -goog.ui.DrilldownRow.createRowNode_ = function(html, doc) { +goog.ui.DrilldownRow.createRowNode_ = function(html, dom) { // Note: this may be slow. var tableHtml = goog.html.SafeHtml.create(goog.dom.TagName.TABLE, {}, html); - var div = doc.createElement(goog.dom.TagName.DIV); + var div = dom.createElement(goog.dom.TagName.DIV); goog.dom.safe.setInnerHtml(div, tableHtml); return div.firstChild.rows[0]; }; diff --git a/third_party/closure/goog/ui/editor/defaulttoolbar.js b/third_party/closure/goog/ui/editor/defaulttoolbar.js index c24f51036fc85..6442dce8fca2c 100644 --- a/third_party/closure/goog/ui/editor/defaulttoolbar.js +++ b/third_party/closure/goog/ui/editor/defaulttoolbar.js @@ -555,7 +555,7 @@ goog.ui.editor.DefaultToolbar.fontSizeFactory_ = function( */ goog.ui.editor.DefaultToolbar.colorUpdateFromValue_ = function(button, color) { var value = color; - /** @preserveTry */ + try { if (goog.userAgent.IE) { // IE returns a number that, converted to hex, is a BGR color. diff --git a/third_party/closure/goog/ui/editor/linkdialog.js b/third_party/closure/goog/ui/editor/linkdialog.js index bb80daba80e0a..7d5147b0d72fc 100644 --- a/third_party/closure/goog/ui/editor/linkdialog.js +++ b/third_party/closure/goog/ui/editor/linkdialog.js @@ -37,6 +37,7 @@ goog.require('goog.events.Event'); goog.require('goog.events.EventHandler'); goog.require('goog.events.InputHandler'); goog.require('goog.html.SafeHtml'); +goog.require('goog.html.SafeHtmlFormatter'); goog.require('goog.string'); goog.require('goog.string.Unicode'); goog.require('goog.style'); @@ -511,10 +512,9 @@ goog.ui.editor.LinkDialog.prototype.buildTextToDisplayDiv_ = function() { }, [goog.ui.editor.messages.MSG_TEXT_TO_DISPLAY, goog.string.Unicode.NBSP]); goog.dom.safe.setInnerHtml(table.rows[0].cells[0], html); - this.textToDisplayInput_ = /** @type {!HTMLInputElement} */ ( - this.dom.createDom( - goog.dom.TagName.INPUT, - {id: goog.ui.editor.LinkDialog.Id_.TEXT_TO_DISPLAY})); + this.textToDisplayInput_ = this.dom.createDom( + goog.dom.TagName.INPUT, + {id: goog.ui.editor.LinkDialog.Id_.TEXT_TO_DISPLAY}); var textInput = this.textToDisplayInput_; // 98% prevents scroll bars in standards mode. // TODO(robbyw): Is this necessary for quirks mode? @@ -545,9 +545,8 @@ goog.ui.editor.LinkDialog.prototype.buildTextToDisplayDiv_ = function() { * @private */ goog.ui.editor.LinkDialog.prototype.buildOpenInNewWindowDiv_ = function() { - this.openInNewWindowCheckbox_ = /** @type {!HTMLInputElement} */ ( - this.dom.createDom( - goog.dom.TagName.INPUT, {'type': goog.dom.InputType.CHECKBOX})); + this.openInNewWindowCheckbox_ = this.dom.createDom( + goog.dom.TagName.INPUT, {'type': goog.dom.InputType.CHECKBOX}); return this.dom.createDom( goog.dom.TagName.DIV, null, this.dom.createDom( @@ -562,23 +561,27 @@ goog.ui.editor.LinkDialog.prototype.buildOpenInNewWindowDiv_ = function() { * @private */ goog.ui.editor.LinkDialog.prototype.buildRelNoFollowDiv_ = function() { + var formatter = new goog.html.SafeHtmlFormatter(); /** @desc Checkbox text for adding 'rel=nofollow' attribute to a link. */ var MSG_ADD_REL_NOFOLLOW_ATTR = goog.getMsg( "Add '{$relNoFollow}' attribute ({$linkStart}Learn more{$linkEnd})", { 'relNoFollow': 'rel=nofollow', - 'linkStart': '', - 'linkEnd': '' + 'linkStart': formatter.startTag('a', { + 'href': 'http://support.google.com/webmasters/bin/' + + 'answer.py?hl=en&answer=96569', + 'target': '_blank' + }), + 'linkEnd': formatter.endTag('a') }); - this.relNoFollowCheckbox_ = /** @type {!HTMLInputElement} */ ( - this.dom.createDom( - goog.dom.TagName.INPUT, {'type': goog.dom.InputType.CHECKBOX})); + this.relNoFollowCheckbox_ = this.dom.createDom( + goog.dom.TagName.INPUT, {'type': goog.dom.InputType.CHECKBOX}); return this.dom.createDom( goog.dom.TagName.DIV, null, this.dom.createDom( goog.dom.TagName.LABEL, null, this.relNoFollowCheckbox_, - goog.dom.htmlToDocumentFragment(MSG_ADD_REL_NOFOLLOW_ATTR))); + goog.dom.safeHtmlToNode( + formatter.format(MSG_ADD_REL_NOFOLLOW_ATTR)))); }; diff --git a/third_party/closure/goog/ui/editor/tabpane.js b/third_party/closure/goog/ui/editor/tabpane.js index f4032101156ef..168b83988ea34 100644 --- a/third_party/closure/goog/ui/editor/tabpane.js +++ b/third_party/closure/goog/ui/editor/tabpane.js @@ -22,6 +22,7 @@ goog.provide('goog.ui.editor.TabPane'); goog.require('goog.asserts'); +goog.require('goog.dom'); goog.require('goog.dom.InputType'); goog.require('goog.dom.TagName'); goog.require('goog.dom.classlist'); @@ -193,7 +194,7 @@ goog.ui.editor.TabPane.prototype.handleTabSelect_ = function(e) { if (this.selectedRadio_) { this.selectedRadio_.checked = false; } - this.selectedRadio_ = - tab.getElement().getElementsByTagName(goog.dom.TagName.INPUT)[0]; + this.selectedRadio_ = goog.dom.getElementsByTagName( + goog.dom.TagName.INPUT, tab.getElementStrict())[0]; this.selectedRadio_.checked = true; }; diff --git a/third_party/closure/goog/ui/editor/toolbarcontroller.js b/third_party/closure/goog/ui/editor/toolbarcontroller.js index bdb63743c75c7..313ab3e1de1c3 100644 --- a/third_party/closure/goog/ui/editor/toolbarcontroller.js +++ b/third_party/closure/goog/ui/editor/toolbarcontroller.js @@ -239,14 +239,14 @@ goog.ui.editor.ToolbarController.prototype.disposeInternal = function() { * @protected */ goog.ui.editor.ToolbarController.prototype.updateToolbar = function(e) { - if (!this.toolbar_.isEnabled() || + if (!this.toolbar_.isEnabled() || !this.field_.isSelectionEditable() || !this.dispatchEvent(goog.ui.Component.EventType.CHANGE)) { return; } var state; - /** @preserveTry */ + try { /** @type {Array} */ e.commands; // Added by dispatchEvent. diff --git a/third_party/closure/goog/ui/editor/toolbarfactory.js b/third_party/closure/goog/ui/editor/toolbarfactory.js index 09853a83a966c..1944e5f35ad6c 100644 --- a/third_party/closure/goog/ui/editor/toolbarfactory.js +++ b/third_party/closure/goog/ui/editor/toolbarfactory.js @@ -184,7 +184,7 @@ goog.ui.editor.ToolbarFactory.LEGACY_SIZE_TO_PX_MAP_ = * 'H4') * * @param {!goog.ui.Select} button "Format block" menu button. - * @param {!Array<{caption: string, command: goog.dom.TagName}>} formats Array + * @param {!Array<{caption: string, command: !goog.dom.TagName}>} formats Array * of format option descriptors. */ goog.ui.editor.ToolbarFactory.addFormatOptions = function(button, formats) { @@ -199,7 +199,7 @@ goog.ui.editor.ToolbarFactory.addFormatOptions = function(button, formats) { * Adds a menu item to the given "Format block" menu button. * @param {!goog.ui.Select} button "Format block" menu button. * @param {string} caption Caption to show in the menu. - * @param {goog.dom.TagName} tag Corresponding block format tag. + * @param {!goog.dom.TagName} tag Corresponding block format tag. */ goog.ui.editor.ToolbarFactory.addFormatOption = function(button, caption, tag) { // Construct the option, and add it to the button. @@ -207,7 +207,7 @@ goog.ui.editor.ToolbarFactory.addFormatOption = function(button, caption, tag) { var buttonDom = button.getDomHelper(); var option = new goog.ui.Option( buttonDom.createDom(goog.dom.TagName.DIV, null, caption), tag, buttonDom); - option.setId(tag); + option.setId(String(tag)); button.addItem(option); }; diff --git a/third_party/closure/goog/ui/emoji/progressiveemojipaletterenderer.js b/third_party/closure/goog/ui/emoji/progressiveemojipaletterenderer.js index bf1748c0a4615..c9e14560b6f1e 100644 --- a/third_party/closure/goog/ui/emoji/progressiveemojipaletterenderer.js +++ b/third_party/closure/goog/ui/emoji/progressiveemojipaletterenderer.js @@ -55,10 +55,8 @@ goog.ui.emoji.ProgressiveEmojiPaletteRenderer.prototype var y = spriteInfo.getYOffsetCssValue(); // Need this extra div for proper vertical centering. var inner = dom.createDom(goog.dom.TagName.IMG, {'src': displayUrl}); - var el = /** @type {!HTMLDivElement} */ ( - dom.createDom( - goog.dom.TagName.DIV, goog.getCssName('goog-palette-cell-extra'), - inner)); + var el = dom.createDom( + goog.dom.TagName.DIV, goog.getCssName('goog-palette-cell-extra'), inner); goog.style.setStyle(el, { 'width': width, 'height': height, diff --git a/third_party/closure/goog/ui/filterobservingmenuitemrenderer.js b/third_party/closure/goog/ui/filterobservingmenuitemrenderer.js index cc643d175a230..a906916ef1b94 100644 --- a/third_party/closure/goog/ui/filterobservingmenuitemrenderer.js +++ b/third_party/closure/goog/ui/filterobservingmenuitemrenderer.js @@ -30,6 +30,7 @@ goog.require('goog.ui.MenuItemRenderer'); /** * Default renderer for {@link goog.ui.FilterObservingMenuItem}s. Each item has * the following structure: + * *
...(content)...
* * @constructor diff --git a/third_party/closure/goog/ui/flatbuttonrenderer.js b/third_party/closure/goog/ui/flatbuttonrenderer.js index 484497781a883..cc2166040b5c1 100644 --- a/third_party/closure/goog/ui/flatbuttonrenderer.js +++ b/third_party/closure/goog/ui/flatbuttonrenderer.js @@ -13,7 +13,7 @@ // limitations under the License. /** - * @fileoverview Similiar functionality of {@link goog.ui.ButtonRenderer}, + * @fileoverview Similar functionality of {@link goog.ui.ButtonRenderer}, * but uses a
element instead of a
Code Pathlib/gson.jar
URL https://github.com/google/gson
Version2.2.42.7