Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 /** | 5 /** |
| 6 * Enum for WebDriver status codes. | 6 * Enum for WebDriver status codes. |
| 7 * @enum {number} | 7 * @enum {number} |
| 8 */ | 8 */ |
| 9 var StatusCode = { | 9 var StatusCode = { |
| 10 STALE_ELEMENT_REFERENCE: 10, | 10 STALE_ELEMENT_REFERENCE: 10, |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 21 }; | 21 }; |
| 22 | 22 |
| 23 /** | 23 /** |
| 24 * Dictionary key to use for holding an element ID. | 24 * Dictionary key to use for holding an element ID. |
| 25 * @const | 25 * @const |
| 26 * @type {string} | 26 * @type {string} |
| 27 */ | 27 */ |
| 28 var ELEMENT_KEY = 'ELEMENT'; | 28 var ELEMENT_KEY = 'ELEMENT'; |
| 29 | 29 |
| 30 /** | 30 /** |
| 31 * True if using W3C Element references. | |
| 32 * @const | |
| 33 * @type {boolean} | |
| 34 */ | |
| 35 var w3cEnabled = false; | |
|
samuong
2016/08/11 20:45:14
if you're going to have this here, is there a need
| |
| 36 | |
| 37 /** | |
| 31 * True if shadow dom is enabled. | 38 * True if shadow dom is enabled. |
| 32 * @const | 39 * @const |
| 33 * @type {boolean} | 40 * @type {boolean} |
| 34 */ | 41 */ |
| 35 var SHADOW_DOM_ENABLED = typeof ShadowRoot === 'function'; | 42 var SHADOW_DOM_ENABLED = typeof ShadowRoot === 'function'; |
| 36 | 43 |
| 37 /** | 44 /** |
| 45 * Generates a unique ID to identify an element. | |
| 46 * @void | |
| 47 * @return {string} Randomly generated ID. | |
| 48 */ | |
| 49 function generateUUID() { | |
| 50 var UUID = ""; | |
| 51 for (var i = 0; i < 16; i++) { | |
| 52 if (i == 6 || i == 12 || i == 14 || i == 15) { | |
| 53 UUID += "00"; | |
| 54 } else if (i == 7 || i == 13) { | |
| 55 UUID += "01"; | |
|
samuong
2016/08/11 20:45:14
If I'm reading https://tools.ietf.org/html/rfc4122
| |
| 56 } else { | |
| 57 var rand = Math.round(Math.random() * 225).toString(16); | |
|
samuong
2016/08/11 20:45:14
Let's use window.crypto.getRandomValues() instead
| |
| 58 UUID += rand; | |
| 59 } | |
| 60 if (i == 3 || i == 5 || i == 7 || i == 9) { | |
| 61 UUID += "-"; | |
| 62 } | |
| 63 } | |
| 64 return UUID; | |
| 65 }; | |
| 66 | |
| 67 /** | |
| 68 * A cache which maps IDs <-> cached objects for the purpose of identifying | |
| 69 * a script object remotely. Uses UUIDs for identification. | |
| 70 * @constructor | |
| 71 */ | |
| 72 function CacheWithUUID() { | |
| 73 this.cache_ = {}; | |
| 74 } | |
| 75 | |
| 76 CacheWithUUID.prototype = { | |
| 77 /** | |
| 78 * Stores a given item in the cache and returns a unique UUID. | |
| 79 * | |
| 80 * @param {!Object} item The item to store in the cache. | |
| 81 * @return {number} The UUID for the cached item. | |
| 82 */ | |
| 83 storeItem: function(item) { | |
| 84 for (var i in this.cache_) { | |
| 85 if (item == this.cache_[i]) | |
| 86 return i; | |
| 87 } | |
| 88 var id = generateUUID(); | |
| 89 this.cache_[id] = item; | |
| 90 return id; | |
| 91 }, | |
| 92 | |
| 93 /** | |
| 94 * Retrieves the cached object for the given ID. | |
| 95 * | |
| 96 * @param {number} id The ID for the cached item to retrieve. | |
| 97 * @return {!Object} The retrieved item. | |
| 98 */ | |
| 99 retrieveItem: function(id) { | |
| 100 var item = this.cache_[id]; | |
| 101 if (item) | |
| 102 return item; | |
| 103 var error = new Error('not in cache'); | |
| 104 error.code = StatusCode.STALE_ELEMENT_REFERENCE; | |
| 105 error.message = 'element is not attached to the page document'; | |
| 106 throw error; | |
| 107 }, | |
| 108 | |
| 109 /** | |
| 110 * Clears stale items from the cache. | |
| 111 */ | |
| 112 clearStale: function() { | |
| 113 for (var id in this.cache_) { | |
| 114 var node = this.cache_[id]; | |
| 115 if (!this.isNodeReachable_(node)) | |
| 116 delete this.cache_[id]; | |
| 117 } | |
| 118 }, | |
| 119 | |
| 120 /** | |
| 121 * @private | |
| 122 * @param {!Node} node The node to check. | |
| 123 * @return {boolean} If the nodes is reachable. | |
| 124 */ | |
| 125 isNodeReachable_: function(node) { | |
| 126 var nodeRoot = getNodeRootThroughAnyShadows(node); | |
| 127 return (nodeRoot == document); | |
| 128 } | |
| 129 | |
| 130 | |
| 131 }; | |
| 132 | |
| 133 /** | |
| 38 * A cache which maps IDs <-> cached objects for the purpose of identifying | 134 * A cache which maps IDs <-> cached objects for the purpose of identifying |
| 39 * a script object remotely. | 135 * a script object remotely. |
| 40 * @constructor | 136 * @constructor |
| 41 */ | 137 */ |
| 42 function Cache() { | 138 function Cache() { |
| 43 this.cache_ = {}; | 139 this.cache_ = {}; |
| 44 this.nextId_ = 1; | 140 this.nextId_ = 1; |
| 45 this.idPrefix_ = Math.random().toString(); | 141 this.idPrefix_ = Math.random().toString(); |
| 46 } | 142 } |
| 47 | 143 |
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 121 */ | 217 */ |
| 122 function getNodeRootThroughAnyShadows(node) { | 218 function getNodeRootThroughAnyShadows(node) { |
| 123 var root = getNodeRoot(node); | 219 var root = getNodeRoot(node); |
| 124 while (SHADOW_DOM_ENABLED && root instanceof ShadowRoot) { | 220 while (SHADOW_DOM_ENABLED && root instanceof ShadowRoot) { |
| 125 root = getNodeRoot(root.host); | 221 root = getNodeRoot(root.host); |
| 126 } | 222 } |
| 127 return root; | 223 return root; |
| 128 } | 224 } |
| 129 | 225 |
| 130 /** | 226 /** |
| 131 * Returns the global object cache for the page. | 227 * Returns the global object cache for the page. |
|
samuong
2016/08/11 20:45:14
don't forget to add a comment here for the w3c arg
| |
| 132 * @param {Document=} opt_doc The document whose cache to retrieve. Defaults to | 228 * @param {Document=} opt_doc The document whose cache to retrieve. Defaults to |
| 133 * the current document. | 229 * the current document. |
| 134 * @return {!Cache} The page's object cache. | 230 * @return {!Cache} The page's object cache. |
| 135 */ | 231 */ |
| 136 function getPageCache(opt_doc) { | 232 function getPageCache(opt_doc, opt_w3c) { |
| 137 var doc = opt_doc || document; | 233 var doc = opt_doc || document; |
| 234 var w3c = opt_w3c || false; | |
|
samuong
2016/08/11 20:45:14
is there ever a case where we don't pass a value f
roisinmcl
2016/08/13 01:47:26
Done.
| |
| 138 var key = '$cdc_asdjflasutopfhvcZLmcfl_'; | 235 var key = '$cdc_asdjflasutopfhvcZLmcfl_'; |
| 139 if (!(key in doc)) | 236 if (w3c) { |
| 140 doc[key] = new Cache(); | 237 if (!(key in doc)) |
| 141 return doc[key]; | 238 doc[key] = new CacheWithUUID(); |
| 239 return doc[key]; | |
| 240 } else { | |
| 241 if (!(key in doc)) | |
| 242 doc[key] = new Cache(); | |
| 243 return doc[key]; | |
| 244 } | |
|
samuong
2016/08/11 20:45:14
if you make the "if (w3c)" check the inner conditi
roisinmcl
2016/08/13 01:47:26
Done.
| |
| 142 } | 245 } |
| 143 | 246 |
| 144 /** | 247 /** |
| 145 * Wraps the given value to be transmitted remotely by converting | 248 * Wraps the given value to be transmitted remotely by converting |
| 146 * appropriate objects to cached object IDs. | 249 * appropriate objects to cached object IDs. |
| 147 * | 250 * |
| 148 * @param {*} value The value to wrap. | 251 * @param {*} value The value to wrap. |
| 149 * @return {*} The wrapped value. | 252 * @return {*} The wrapped value. |
| 150 */ | 253 */ |
| 151 function wrap(value) { | 254 function wrap(value) { |
| 152 // As of crrev.com/1316933002, typeof() for some elements will return | 255 // As of crrev.com/1316933002, typeof() for some elements will return |
| 153 // 'function', not 'object'. So we need to check for both non-null objects, as | 256 // 'function', not 'object'. So we need to check for both non-null objects, as |
| 154 // well Elements that also happen to be callable functions (e.g. <embed> and | 257 // well Elements that also happen to be callable functions (e.g. <embed> and |
| 155 // <object> elements). Note that we can not use |value instanceof Object| here | 258 // <object> elements). Note that we can not use |value instanceof Object| here |
| 156 // since this does not work with frames/iframes, for example | 259 // since this does not work with frames/iframes, for example |
| 157 // frames[0].document.body instanceof Object == false even though | 260 // frames[0].document.body instanceof Object == false even though |
| 158 // typeof(frames[0].document.body) == 'object'. | 261 // typeof(frames[0].document.body) == 'object'. |
| 159 if ((typeof(value) == 'object' && value != null) || | 262 if ((typeof(value) == 'object' && value != null) || |
| 160 (typeof(value) == 'function' && value instanceof Element)) { | 263 (typeof(value) == 'function' && value instanceof Element)) { |
| 161 var nodeType = value['nodeType']; | 264 var nodeType = value['nodeType']; |
| 162 if (nodeType == NodeType.ELEMENT || nodeType == NodeType.DOCUMENT | 265 if (nodeType == NodeType.ELEMENT || nodeType == NodeType.DOCUMENT |
| 163 || (SHADOW_DOM_ENABLED && value instanceof ShadowRoot)) { | 266 || (SHADOW_DOM_ENABLED && value instanceof ShadowRoot)) { |
| 164 var wrapped = {}; | 267 var wrapped = {}; |
| 165 var root = getNodeRootThroughAnyShadows(value); | 268 var root = getNodeRootThroughAnyShadows(value); |
| 166 wrapped[ELEMENT_KEY] = getPageCache(root).storeItem(value); | 269 wrapped[ELEMENT_KEY] = getPageCache(root, w3cEnabled).storeItem(value); |
| 167 return wrapped; | 270 return wrapped; |
| 168 } | 271 } |
| 169 | 272 |
| 170 var obj = (typeof(value.length) == 'number') ? [] : {}; | 273 var obj = (typeof(value.length) == 'number') ? [] : {}; |
| 171 for (var prop in value) | 274 for (var prop in value) |
| 172 obj[prop] = wrap(value[prop]); | 275 obj[prop] = wrap(value[prop]); |
| 173 return obj; | 276 return obj; |
| 174 } | 277 } |
| 175 return value; | 278 return value; |
| 176 } | 279 } |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 209 * @param {function(...[*]) : *} func The function to invoke. | 312 * @param {function(...[*]) : *} func The function to invoke. |
| 210 * @param {!Array<*>} args The array of arguments to supply to the function, | 313 * @param {!Array<*>} args The array of arguments to supply to the function, |
| 211 * which will be unwrapped before invoking the function. | 314 * which will be unwrapped before invoking the function. |
| 212 * @param {boolean=} opt_unwrappedReturn Whether the function's return value | 315 * @param {boolean=} opt_unwrappedReturn Whether the function's return value |
| 213 * should be left unwrapped. | 316 * should be left unwrapped. |
| 214 * @return {*} An object containing a status and value property, where status | 317 * @return {*} An object containing a status and value property, where status |
| 215 * is a WebDriver status code and value is the wrapped value. If an | 318 * is a WebDriver status code and value is the wrapped value. If an |
| 216 * unwrapped return was specified, this will be the function's pure return | 319 * unwrapped return was specified, this will be the function's pure return |
| 217 * value. | 320 * value. |
| 218 */ | 321 */ |
| 219 function callFunction(shadowHostIds, func, args, opt_unwrappedReturn) { | 322 function callFunction(shadowHostIds, func, args, w3c, opt_unwrappedReturn) { |
| 220 var cache = getPageCache(); | 323 if (w3c) { |
| 324 w3cEnabled = true; | |
| 325 ELEMENT_KEY = 'element-6066-11e4-a52e-4f735466cecf'; | |
| 326 | |
| 327 } | |
| 328 var cache = getPageCache(null, w3cEnabled); | |
| 221 cache.clearStale(); | 329 cache.clearStale(); |
| 222 if (shadowHostIds && SHADOW_DOM_ENABLED) { | 330 if (shadowHostIds && SHADOW_DOM_ENABLED) { |
| 223 for (var i = 0; i < shadowHostIds.length; i++) { | 331 for (var i = 0; i < shadowHostIds.length; i++) { |
| 224 var host = cache.retrieveItem(shadowHostIds[i]); | 332 var host = cache.retrieveItem(shadowHostIds[i]); |
| 225 // TODO(zachconrad): Use the olderShadowRoot API when available to check | 333 // TODO(zachconrad): Use the olderShadowRoot API when available to check |
| 226 // all of the shadow roots. | 334 // all of the shadow roots. |
| 227 cache = getPageCache(host.webkitShadowRoot); | 335 cache = getPageCache(host.webkitShadowRoot, w3cEnabled); |
| 228 cache.clearStale(); | 336 cache.clearStale(); |
| 229 } | 337 } |
| 230 } | 338 } |
| 231 | 339 |
| 232 if (opt_unwrappedReturn) | 340 if (opt_unwrappedReturn) |
| 233 return func.apply(null, unwrap(args, cache)); | 341 return func.apply(null, unwrap(args, cache)); |
| 234 | 342 |
| 235 var status = 0; | 343 var status = 0; |
| 236 try { | 344 try { |
| 237 var returnValue = wrap(func.apply(null, unwrap(args, cache))); | 345 var returnValue = wrap(func.apply(null, unwrap(args, cache))); |
| 238 } catch (error) { | 346 } catch (error) { |
| 239 status = error.code || StatusCode.UNKNOWN_ERROR; | 347 status = error.code || StatusCode.UNKNOWN_ERROR; |
| 240 var returnValue = error.message; | 348 var returnValue = error.message; |
| 241 } | 349 } |
| 242 return { | 350 return { |
| 243 status: status, | 351 status: status, |
| 244 value: returnValue | 352 value: returnValue |
| 245 } | 353 } |
| 246 } | 354 } |
| OLD | NEW |