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 17 matching lines...) Expand all Loading... | |
| 28 var ELEMENT_KEY = 'ELEMENT'; | 28 var ELEMENT_KEY = 'ELEMENT'; |
| 29 | 29 |
| 30 /** | 30 /** |
| 31 * A cache which maps IDs <-> cached objects for the purpose of identifying | 31 * A cache which maps IDs <-> cached objects for the purpose of identifying |
| 32 * a script object remotely. | 32 * a script object remotely. |
| 33 * @constructor | 33 * @constructor |
| 34 */ | 34 */ |
| 35 function Cache() { | 35 function Cache() { |
| 36 this.cache_ = {}; | 36 this.cache_ = {}; |
| 37 this.nextId_ = 1; | 37 this.nextId_ = 1; |
| 38 this.idPrefix_ = Math.random().toString(); | |
| 38 } | 39 } |
| 39 | 40 |
| 40 Cache.prototype = { | 41 Cache.prototype = { |
| 41 | 42 |
| 42 /** | 43 /** |
| 43 * Stores a given item in the cache and returns a unique ID. | 44 * Stores a given item in the cache and returns a unique ID. |
| 44 * | 45 * |
| 45 * @param {!Object} item The item to store in the cache. | 46 * @param {!Object} item The item to store in the cache. |
| 46 * @return {number} The ID for the cached item. | 47 * @return {number} The ID for the cached item. |
| 47 */ | 48 */ |
| 48 storeItem_: function(item) { | 49 storeItem: function(item) { |
| 49 for (var i in this.cache_) { | 50 for (var i in this.cache_) { |
| 50 if (item == this.cache_[i]) | 51 if (item == this.cache_[i]) |
| 51 return i; | 52 return i; |
| 52 } | 53 } |
| 53 var id = this.nextId_.toString(); | 54 var id = this.idPrefix_ + ':' + this.nextId_; |
| 54 this.cache_[id] = item; | 55 this.cache_[id] = item; |
| 55 this.nextId_++; | 56 this.nextId_++; |
| 56 return id; | 57 return id; |
| 57 }, | 58 }, |
| 58 | 59 |
| 59 /** | 60 /** |
| 60 * Retrieves the cached object for the given ID. | 61 * Retrieves the cached object for the given ID. |
| 61 * | 62 * |
| 62 * @param {number} id The ID for the cached item to retrieve. | 63 * @param {number} id The ID for the cached item to retrieve. |
| 63 * @return {!Object} The retrieved item. | 64 * @return {!Object} The retrieved item. |
| 64 */ | 65 */ |
| 65 retrieveItem_: function(id) { | 66 retrieveItem: function(id) { |
| 66 var item = this.cache_[id]; | 67 var item = this.cache_[id]; |
| 67 if (item) | 68 if (item) |
| 68 return item; | 69 return item; |
| 69 var error = new Error('not in cache'); | 70 var error = new Error('not in cache'); |
| 70 error.code = StatusCode.STALE_ELEMENT_REFERENCE; | 71 error.code = StatusCode.STALE_ELEMENT_REFERENCE; |
| 71 error.message = 'element is not attached to the page document'; | 72 error.message = 'element is not attached to the page document'; |
| 72 throw error; | 73 throw error; |
| 73 }, | 74 }, |
| 74 | 75 |
| 75 /** | 76 /** |
| 76 * Clears stale items from the cache. | 77 * Clears stale items from the cache. |
| 77 */ | 78 */ |
| 78 clearStale: function() { | 79 clearStale: function() { |
| 79 for (var id in this.cache_) { | 80 for (var id in this.cache_) { |
| 80 var node = this.cache_[id]; | 81 var node = this.cache_[id]; |
| 81 while (node) { | 82 while (node) { |
| 82 if (node == document) | 83 if (node == document) |
| 83 break; | 84 break; |
| 84 node = node.parentNode; | 85 node = node.parentNode; |
| 85 } | 86 } |
| 86 if (!node) | 87 if (!node) |
| 87 delete this.cache_[id]; | 88 delete this.cache_[id]; |
| 88 } | 89 } |
| 89 }, | |
| 90 | |
| 91 /** | |
| 92 * Wraps the given value to be transmitted remotely by converting | |
| 93 * appropriate objects to cached object IDs. | |
| 94 * | |
| 95 * @param {*} value The value to wrap. | |
| 96 * @return {*} The wrapped value. | |
| 97 */ | |
| 98 wrap: function(value) { | |
| 99 if (typeof(value) == 'object' && value != null) { | |
| 100 var nodeType = value['nodeType']; | |
| 101 if (nodeType == NodeType.ELEMENT || nodeType == NodeType.DOCUMENT) { | |
| 102 var wrapped = {}; | |
| 103 wrapped[ELEMENT_KEY] = this.storeItem_(value); | |
| 104 return wrapped; | |
| 105 } | |
| 106 | |
| 107 var obj = (typeof(value.length) == 'number') ? [] : {}; | |
| 108 for (var prop in value) | |
| 109 obj[prop] = this.wrap(value[prop]); | |
| 110 return obj; | |
| 111 } | |
| 112 return value; | |
| 113 }, | |
| 114 | |
| 115 /** | |
| 116 * Unwraps the given value by converting from object IDs to the cached | |
| 117 * objects. | |
| 118 * | |
| 119 * @param {*} value The value to unwrap. | |
| 120 * @return {*} The unwrapped value. | |
| 121 */ | |
| 122 unwrap: function(value) { | |
| 123 if (typeof(value) == 'object' && value != null) { | |
| 124 if (ELEMENT_KEY in value) | |
| 125 return this.retrieveItem_(value[ELEMENT_KEY]); | |
| 126 | |
| 127 var obj = (typeof(value.length) == 'number') ? [] : {}; | |
| 128 for (var prop in value) | |
| 129 obj[prop] = this.unwrap(value[prop]); | |
| 130 return obj; | |
| 131 } | |
| 132 return value; | |
| 133 } | 90 } |
| 134 }; | 91 }; |
| 135 | 92 |
| 136 /** | 93 /** |
| 137 * Returns the global object cache for the page. | 94 * Returns the global object cache for the page. |
| 95 * @param {Document=} opt_doc The document whose cache to retrieve. Defaults to | |
| 96 * the current document. | |
| 138 * @return {!Cache} The page's object cache. | 97 * @return {!Cache} The page's object cache. |
| 139 */ | 98 */ |
| 140 function getPageCache() { | 99 function getPageCache(opt_doc) { |
| 100 var doc = opt_doc || document; | |
| 141 // We use the same key as selenium's javascript/atoms/inject.js. | 101 // We use the same key as selenium's javascript/atoms/inject.js. |
| 142 var key = '$wdc_'; | 102 var key = '$wdc_'; |
| 143 if (!(key in document)) | 103 if (!(key in doc)) |
| 144 document[key] = new Cache(); | 104 doc[key] = new Cache(); |
| 145 return document[key]; | 105 return doc[key]; |
| 146 } | 106 } |
| 147 | 107 |
| 148 /** | 108 /** |
| 109 * Wraps the given value to be transmitted remotely by converting | |
| 110 * appropriate objects to cached object IDs. | |
| 111 * | |
| 112 * @param {*} value The value to wrap. | |
| 113 * @return {*} The wrapped value. | |
| 114 */ | |
| 115 function wrap(value) { | |
| 116 if (typeof(value) == 'object' && value != null) { | |
| 117 var nodeType = value['nodeType']; | |
| 118 if (nodeType == NodeType.ELEMENT || nodeType == NodeType.DOCUMENT) { | |
| 119 var wrapped = {}; | |
| 120 wrapped[ELEMENT_KEY] = getPageCache(value.ownerDocument).storeItem(value); | |
| 121 return wrapped; | |
| 122 } | |
| 123 | |
| 124 var obj = (typeof(value.length) == 'number') ? [] : {}; | |
| 125 for (var prop in value) | |
| 126 obj[prop] = wrap(value[prop]); | |
| 127 return obj; | |
| 128 } | |
| 129 return value; | |
| 130 } | |
| 131 | |
| 132 /** | |
| 133 * Unwraps the given value by converting from object IDs to the cached | |
| 134 * objects. | |
| 135 * | |
| 136 * @param {*} value The value to unwrap. | |
| 137 * @param {Cache} cache The cache to retrieve wrapped elements from. | |
| 138 * @return {*} The unwrapped value. | |
| 139 */ | |
| 140 function unwrap(value, cache) { | |
| 141 if (typeof(value) == 'object' && value != null) { | |
| 142 if (ELEMENT_KEY in value) | |
| 143 return cache.retrieveItem(value[ELEMENT_KEY]); | |
| 144 | |
| 145 var obj = (typeof(value.length) == 'number') ? [] : {}; | |
| 146 for (var prop in value) | |
| 147 obj[prop] = unwrap(value[prop], cache); | |
| 148 return obj; | |
| 149 } | |
| 150 return value; | |
| 151 } | |
| 152 | |
| 153 /** | |
| 149 * Calls a given function and returns its value. | 154 * Calls a given function and returns its value. |
| 150 * | 155 * |
| 151 * The inputs to and outputs of the function will be unwrapped and wrapped | 156 * The inputs to and outputs of the function will be unwrapped and wrapped |
| 152 * respectively, unless otherwise specified. This wrapping involves converting | 157 * respectively, unless otherwise specified. This wrapping involves converting |
| 153 * between cached object reference IDs and actual JS objects. The cache will | 158 * between cached object reference IDs and actual JS objects. The cache will |
| 154 * automatically be pruned each call to remove stale references. | 159 * automatically be pruned each call to remove stale references. |
| 155 * | 160 * |
| 156 * @param {function(...[*]) : *} func The function to invoke. | 161 * @param {function(...[*]) : *} func The function to invoke. |
| 157 * @param {!Array.<*>} args The array of arguments to supply to the function, | 162 * @param {!Array.<*>} args The array of arguments to supply to the function, |
| 158 * which will be unwrapped before invoking the function. | 163 * which will be unwrapped before invoking the function. |
| 159 * @param {boolean=} opt_unwrappedReturn Whether the function's return value | 164 * @param {boolean=} opt_unwrappedReturn Whether the function's return value |
| 160 * should be left unwrapped. | 165 * should be left unwrapped. |
| 161 * @return {*} An object containing a status and value property, where status | 166 * @return {*} An object containing a status and value property, where status |
| 162 * is a WebDriver status code and value is the wrapped value. If an | 167 * is a WebDriver status code and value is the wrapped value. If an |
| 163 * unwrapped return was specified, this will be the function's pure return | 168 * unwrapped return was specified, this will be the function's pure return |
| 164 * value. | 169 * value. |
| 165 */ | 170 */ |
| 166 function callFunction(func, args, opt_unwrappedReturn) { | 171 function callFunction(func, args, opt_unwrappedReturn) { |
| 167 var cache = getPageCache(); | 172 var cache = getPageCache(document); |
|
kkania
2013/04/17 21:04:28
remove document?
chrisgao (Use stgao instead)
2013/04/17 22:17:32
Done.
| |
| 168 cache.clearStale(); | 173 cache.clearStale(); |
| 169 | 174 |
| 170 if (opt_unwrappedReturn) | 175 if (opt_unwrappedReturn) |
| 171 return func.apply(null, cache.unwrap(args)); | 176 return func.apply(null, unwrap(args, cache)); |
| 172 | 177 |
| 173 var status = 0; | 178 var status = 0; |
| 174 try { | 179 try { |
| 175 var returnValue = cache.wrap(func.apply(null, cache.unwrap(args))); | 180 var returnValue = wrap(func.apply(null, unwrap(args, cache))); |
| 176 } catch (error) { | 181 } catch (error) { |
| 177 status = error.code || StatusCode.UNKNOWN_ERROR; | 182 status = error.code || StatusCode.UNKNOWN_ERROR; |
| 178 var returnValue = error.message; | 183 var returnValue = error.message; |
| 179 } | 184 } |
| 180 return { | 185 return { |
| 181 status: status, | 186 status: status, |
| 182 value: returnValue | 187 value: returnValue |
| 183 } | 188 } |
| 184 } | 189 } |
| OLD | NEW |