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 |