Chromium Code Reviews| Index: chrome/test/chromedriver/js/call_function.js |
| diff --git a/chrome/test/chromedriver/js/call_function.js b/chrome/test/chromedriver/js/call_function.js |
| index a5c9b1f985ac39c7b82ac16f57305c2ec75cabc7..6dee10578534cd64d5db4425d75241e6605d61ec 100644 |
| --- a/chrome/test/chromedriver/js/call_function.js |
| +++ b/chrome/test/chromedriver/js/call_function.js |
| @@ -28,6 +28,13 @@ var NodeType = { |
| var ELEMENT_KEY = 'ELEMENT'; |
| /** |
| + * True if using W3C Element references. |
| + * @const |
| + * @type {boolean} |
| + */ |
| +var w3cEnabled = false; |
| + |
| +/** |
| * True if shadow dom is enabled. |
| * @const |
| * @type {boolean} |
| @@ -35,6 +42,95 @@ var ELEMENT_KEY = 'ELEMENT'; |
| var SHADOW_DOM_ENABLED = typeof ShadowRoot === 'function'; |
| /** |
| + * Generates a unique ID to identify an element. |
| + * @void |
| + * @return {string} Randomly generated ID. |
| + */ |
| +function generateUUID() { |
| + var array = new Uint8Array(16); |
| + window.crypto.getRandomValues(array); |
| + array[6] = 0x40 | (array[6] & 0x0f); |
| + array[8] = 0x80 | (array[8] & 0x3f); |
| + |
| + var UUID = ""; |
| + for (var i = 0; i < 16; i++) { |
| + var temp = array[i].toString(16); |
| + if (temp.length < 2) |
| + temp = "0" + temp; |
| + UUID += temp; |
| + if (i == 3 || i == 5 || i == 7 || i == 9) |
| + UUID += "-"; |
| + } |
| + return UUID; |
| +}; |
| + |
| +/** |
| + * A cache which maps IDs <-> cached objects for the purpose of identifying |
| + * a script object remotely. Uses UUIDs for identification. |
| + * @constructor |
| + */ |
| +function CacheWithUUID() { |
| + this.cache_ = {}; |
| +} |
| + |
| +CacheWithUUID.prototype = { |
|
stgao
2016/08/31 18:26:16
It seems that CacheWithUUID and Cache could share
samuong
2016/11/23 22:48:45
I think this makes sense for a follow-up CL, as yo
|
| + /** |
| + * Stores a given item in the cache and returns a unique UUID. |
| + * |
| + * @param {!Object} item The item to store in the cache. |
| + * @return {number} The UUID for the cached item. |
| + */ |
| + storeItem: function(item) { |
| + for (var i in this.cache_) { |
| + if (item == this.cache_[i]) |
| + return i; |
| + } |
| + var id = generateUUID(); |
| + this.cache_[id] = item; |
| + return id; |
| + }, |
| + |
| + /** |
| + * Retrieves the cached object for the given ID. |
| + * |
| + * @param {number} id The ID for the cached item to retrieve. |
| + * @return {!Object} The retrieved item. |
| + */ |
| + retrieveItem: function(id) { |
| + var item = this.cache_[id]; |
| + if (item) |
| + return item; |
| + var error = new Error('not in cache'); |
| + error.code = StatusCode.STALE_ELEMENT_REFERENCE; |
| + error.message = 'element is not attached to the page document'; |
| + throw error; |
| + }, |
| + |
| + /** |
| + * Clears stale items from the cache. |
| + */ |
| + clearStale: function() { |
| + for (var id in this.cache_) { |
| + var node = this.cache_[id]; |
| + if (!this.isNodeReachable_(node)) |
| + delete this.cache_[id]; |
| + } |
| + }, |
| + |
| + /** |
| + * @private |
| + * @param {!Node} node The node to check. |
| + * @return {boolean} If the nodes is reachable. |
| + */ |
| + isNodeReachable_: function(node) { |
| + var nodeRoot = getNodeRootThroughAnyShadows(node); |
| + return (nodeRoot == document); |
| + } |
| + |
| + |
| +}; |
| + |
| +/** |
| * A cache which maps IDs <-> cached objects for the purpose of identifying |
| * a script object remotely. |
| * @constructor |
| @@ -133,12 +229,19 @@ function getNodeRootThroughAnyShadows(node) { |
| * the current document. |
| * @return {!Cache} The page's object cache. |
| */ |
| -function getPageCache(opt_doc) { |
| +function getPageCache(opt_doc, opt_w3c) { |
| var doc = opt_doc || document; |
| + var w3c = opt_w3c || false; |
| var key = '$cdc_asdjflasutopfhvcZLmcfl_'; |
| - if (!(key in doc)) |
| - doc[key] = new Cache(); |
| - return doc[key]; |
| + if (w3c) { |
| + if (!(key in doc)) |
| + doc[key] = new CacheWithUUID(); |
| + return doc[key]; |
| + } else { |
| + if (!(key in doc)) |
| + doc[key] = new Cache(); |
| + return doc[key]; |
| + } |
| } |
| /** |
| @@ -163,7 +266,7 @@ function wrap(value) { |
| || (SHADOW_DOM_ENABLED && value instanceof ShadowRoot)) { |
| var wrapped = {}; |
| var root = getNodeRootThroughAnyShadows(value); |
| - wrapped[ELEMENT_KEY] = getPageCache(root).storeItem(value); |
| + wrapped[ELEMENT_KEY] = getPageCache(root, w3cEnabled).storeItem(value); |
| return wrapped; |
| } |
| @@ -209,6 +312,7 @@ function unwrap(value, cache) { |
| * @param {function(...[*]) : *} func The function to invoke. |
| * @param {!Array<*>} args The array of arguments to supply to the function, |
| * which will be unwrapped before invoking the function. |
| + * @param {boolean} w3c Whether to return a W3C compliant element reference. |
| * @param {boolean=} opt_unwrappedReturn Whether the function's return value |
| * should be left unwrapped. |
| * @return {*} An object containing a status and value property, where status |
| @@ -216,15 +320,20 @@ function unwrap(value, cache) { |
| * unwrapped return was specified, this will be the function's pure return |
| * value. |
| */ |
| -function callFunction(shadowHostIds, func, args, opt_unwrappedReturn) { |
| - var cache = getPageCache(); |
| +function callFunction(shadowHostIds, func, args, w3c, opt_unwrappedReturn) { |
| + if (w3c) { |
| + w3cEnabled = true; |
| + ELEMENT_KEY = 'element-6066-11e4-a52e-4f735466cecf'; |
| + |
| + } |
| + var cache = getPageCache(null, w3cEnabled); |
| cache.clearStale(); |
| if (shadowHostIds && SHADOW_DOM_ENABLED) { |
| for (var i = 0; i < shadowHostIds.length; i++) { |
| var host = cache.retrieveItem(shadowHostIds[i]); |
| // TODO(zachconrad): Use the olderShadowRoot API when available to check |
| // all of the shadow roots. |
| - cache = getPageCache(host.webkitShadowRoot); |
| + cache = getPageCache(host.webkitShadowRoot, w3cEnabled); |
| cache.clearStale(); |
| } |
| } |