Skip to content

Commit 3771e30

Browse files
committed
Update webdriver.atoms.inject.locators.* to support finding elements
in another window or frame. The search operation can no longer be piped through bot.inject.executeScript since it violates the contract of only referencing symbols defined in the context of the target frame.
1 parent 7a90f73 commit 3771e30

File tree

4 files changed

+201
-75
lines changed

4 files changed

+201
-75
lines changed

javascript/atoms/inject.js

+7-3
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,8 @@ bot.inject.recompileFunction_ = function(fn, theWindow) {
216216
* @param {!(Function|string)} fn Either the function to execute, or a string
217217
* defining the body of an anonymous function that should be executed. This
218218
* function should only contain references to symbols defined in the context
219-
* of the current window.
219+
* of the target window ({@code opt_window}). Any references to symbols
220+
* defined in this context will likely generate a ReferenceError.
220221
* @param {Array.<*>} args An array of wrapped script arguments, as defined by
221222
* the WebDriver wire protocol.
222223
* @param {boolean=} opt_stringify Whether the result should be returned as a
@@ -261,8 +262,11 @@ bot.inject.executeScript = function(fn, args, opt_stringify, opt_window) {
261262
* from an external source. It handles wrapping and unwrapping of input/output
262263
* values.
263264
*
264-
* @param {(function()|string)} fn Either the function to execute, or a string
265-
* defining the body of an anonymous function that should be executed.
265+
* @param {(!Function|string)} fn Either the function to execute, or a string
266+
* defining the body of an anonymous function that should be executed. This
267+
* function should only contain references to symbols defined in the context
268+
* of the target window ({@code opt_window}). Any references to symbols
269+
* defined in this context will likely generate a ReferenceError.
266270
* @param {Array.<*>} args An array of wrapped script arguments, as defined by
267271
* the WebDriver wire protocol.
268272
* @param {number} timeout The amount of time, in milliseconds, the script

javascript/webdriver/atoms/inject/execute_script.js

+13-10
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,14 @@ goog.require('bot.inject.cache');
2929
*
3030
* @param {!(string|Function)} fn The function to execute.
3131
* @param {Array.<*>} args Array of arguments to pass to fn.
32-
* @param {{bot.inject.WINDOW_KEY:string}=} opt_window The serialized window
33-
* object to be read from the cache.
32+
* @param {{WINDOW: string}=} opt_window The serialized window object to be
33+
* read from the cache.
3434
* @return {string} The response object, serialized and returned in string
3535
* format.
3636
*/
3737
webdriver.atoms.inject.executeScript = function(fn, args, opt_window) {
3838
return /**@type {string}*/(bot.inject.executeScript(fn, args, true,
39-
webdriver.atoms.inject.getWindow_(opt_window)));
39+
webdriver.atoms.inject.getWindow(opt_window)));
4040
};
4141

4242

@@ -48,26 +48,29 @@ webdriver.atoms.inject.executeScript = function(fn, args, opt_window) {
4848
* @param {function(string)|function(!bot.response.ResponseObject)} onDone
4949
* The function to call when the given {@code fn} invokes its callback,
5050
* or when an exception or timeout occurs. This will always be called.
51-
* @param {{bot.inject.WINDOW_KEY:string}=} opt_window The serialized window
51+
* @param {{WINDOW: string}=} opt_window The serialized window
5252
* object to be read from the cache.
5353
*/
5454
webdriver.atoms.inject.executeAsyncScript =
5555
function(fn, args, timeout, onDone, opt_window) {
5656
bot.inject.executeAsyncScript(
5757
fn, args, timeout, onDone, true,
58-
webdriver.atoms.inject.getWindow_(opt_window));
58+
webdriver.atoms.inject.getWindow(opt_window));
5959
};
6060

6161

6262
/**
63-
* Get the window to use.
63+
* Decodes a serialized {WINDOW: string} object using the current document's
64+
* cache.
6465
*
65-
* @param {{bot.inject.WINDOW_KEY:string}=} opt_window The serialized window
66-
* object to be read from the cache.
66+
* @param {{WINDOW: string}=} opt_window The serialized window object to be
67+
* read from the cache. If undefined, this function will trivially return
68+
* the current window.
6769
* @return {!Window} A reference to a window.
68-
* @private
70+
* @throws {bot.Error} If the serialized window cannot be found in the current
71+
* document's cache.
6972
*/
70-
webdriver.atoms.inject.getWindow_ = function(opt_window) {
73+
webdriver.atoms.inject.getWindow = function(opt_window) {
7174
var win;
7275
if (opt_window) {
7376
win = bot.inject.cache.getElement(opt_window[bot.inject.WINDOW_KEY]);

javascript/webdriver/atoms/inject/find_element.js

+71-22
Original file line numberDiff line numberDiff line change
@@ -20,41 +20,90 @@
2020
goog.provide('webdriver.atoms.inject.locators');
2121

2222
goog.require('bot.locators');
23+
goog.require('bot.inject');
2324
goog.require('webdriver.atoms.inject');
2425

26+
2527
/**
2628
* Finds an element by using the given lookup strategy.
2729
* @param {string} strategy The strategy to use to locate the element.
2830
* @param {string} using The locator to use.
29-
* @param {(Document|Element)=} opt_root The document or element to perform
30-
* the search under. If not specified, will use {@code document}
31-
* as the root.
32-
* @return {string} The result wrapped
33-
* in a JSON string as defined by the WebDriver wire protocol.
31+
* @param {?{ELEMENT: string}=} opt_root The WebElement reference for the
32+
* element to perform the search under. If not specified, will use
33+
* {@code document} for the target page.
34+
* @param {{WINDOW:string}=} opt_window The serialized window object for the
35+
* page to find the element in. The referenced window must exist in the
36+
* page executing this script's cache.
37+
* @return {string} A JSON serialized {@link bot.response.ResponseObject}.
3438
*/
35-
webdriver.atoms.inject.locators.findElement =
36-
function(strategy, using, opt_root) {
37-
var locator = {};
38-
locator[strategy] = using;
39-
return webdriver.atoms.inject.executeScript(bot.locators.findElement,
40-
[locator, opt_root]);
39+
webdriver.atoms.inject.locators.findElement = function(
40+
strategy, using, opt_root, opt_window) {
41+
return webdriver.atoms.inject.locators.performSearch_(
42+
strategy, using, bot.locators.findElement, opt_root, opt_window);
4143
};
4244

4345

4446
/**
4547
* Finds all elements by using the given lookup strategy.
4648
* @param {string} strategy The strategy to use to locate the element.
4749
* @param {string} using The locator to use.
48-
* @param {(Document|Element)=} opt_root The document or element to perform
49-
* the search under. If not specified, will use {@code document}
50-
* as the root.
51-
* @return {string} The result wrapped
52-
* in a JSON string as defined by the WebDriver wire protocol.
50+
* @param {?{ELEMENT: string}=} opt_root The WebElement reference for the
51+
* element to perform the search under. If not specified, will use
52+
* {@code document} for the target page.
53+
* @param {{WINDOW:string}=} opt_window The serialized window object for the
54+
* page to find the element in. The referenced window must exist in the
55+
* page executing this script's cache.
56+
* @return {string} A JSON serialized {@link bot.response.ResponseObject}.
5357
*/
54-
webdriver.atoms.inject.locators.findElements =
55-
function(strategy, using, opt_root) {
56-
var locator = {};
57-
locator[strategy] = using;
58-
return webdriver.atoms.inject.executeScript(bot.locators.findElements,
59-
[locator, opt_root]);
58+
webdriver.atoms.inject.locators.findElements = function(
59+
strategy, using, opt_root, opt_window) {
60+
return webdriver.atoms.inject.locators.performSearch_(
61+
strategy, using, bot.locators.findElements, opt_root, opt_window);
62+
};
63+
64+
65+
/**
66+
* Performs a search for one or more elements.
67+
* @param {string} strategy The strategy to use to locate the element.
68+
* @param {string} target The locator to use.
69+
* @param {(function(!Object, (Document|Element)=): Element|
70+
* function(!Object, (Document|Element)=): !goog.array.ArrayLike)}
71+
* searchFn The search function to invoke.
72+
* @param {?{ELEMENT: string}=} opt_root The WebElement reference for the
73+
* element to perform the search under. If not specified, will use
74+
* {@code document} for the target page.
75+
* @param {{WINDOW:string}=} opt_window The serialized window object for the
76+
* page to find the element in. The referenced window must exist in the
77+
* page executing this script's cache.
78+
* @return {string} A JSON serialized {@link bot.response.ResponseObject}.
79+
* @private
80+
*/
81+
webdriver.atoms.inject.locators.performSearch_ = function(
82+
strategy, target, searchFn, opt_root, opt_window) {
83+
var locator = {};
84+
locator[strategy] = target;
85+
86+
var response;
87+
try {
88+
// Step 1: find the window we are locating the element in.
89+
var targetWindow = webdriver.atoms.inject.getWindow(opt_window);
90+
91+
// Step 2: decode the root of our search.
92+
var root;
93+
if (opt_root) {
94+
root = /** @type {!Element} */ (bot.inject.cache.getElement(
95+
opt_root[bot.inject.ELEMENT_KEY], targetWindow.document));
96+
} else {
97+
root = targetWindow.document;
98+
}
99+
100+
// Step 3: perform the search.
101+
var found = searchFn(locator, root);
102+
103+
// Step 4: encode our response.
104+
response = bot.inject.wrapResponse(found);
105+
} catch (ex) {
106+
response = bot.inject.wrapError(ex);
107+
}
108+
return goog.json.serialize(response);
60109
};

0 commit comments

Comments
 (0)