Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(124)

Side by Side Diff: chrome/test/webdriver/session.cc

Issue 7522024: Refactor chromedriver's script execution to reduce amount of custom Value parsing. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: rebase Created 9 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « chrome/test/webdriver/session.h ('k') | chrome/test/webdriver/utility_functions.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 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 #include "chrome/test/webdriver/session.h" 5 #include "chrome/test/webdriver/session.h"
6 6
7 #include <sstream> 7 #include <sstream>
8 #include <vector> 8 #include <vector>
9 9
10 #include "base/command_line.h" 10 #include "base/command_line.h"
(...skipping 14 matching lines...) Expand all
25 #include "base/synchronization/waitable_event.h" 25 #include "base/synchronization/waitable_event.h"
26 #include "base/test/test_timeouts.h" 26 #include "base/test/test_timeouts.h"
27 #include "base/threading/platform_thread.h" 27 #include "base/threading/platform_thread.h"
28 #include "base/time.h" 28 #include "base/time.h"
29 #include "base/utf_string_conversions.h" 29 #include "base/utf_string_conversions.h"
30 #include "base/values.h" 30 #include "base/values.h"
31 #include "chrome/app/chrome_command_ids.h" 31 #include "chrome/app/chrome_command_ids.h"
32 #include "chrome/common/chrome_constants.h" 32 #include "chrome/common/chrome_constants.h"
33 #include "chrome/common/chrome_switches.h" 33 #include "chrome/common/chrome_switches.h"
34 #include "chrome/test/automation/automation_json_requests.h" 34 #include "chrome/test/automation/automation_json_requests.h"
35 #include "chrome/test/automation/value_conversion_util.h"
36 #include "chrome/test/webdriver/session_manager.h"
35 #include "chrome/test/webdriver/webdriver_error.h" 37 #include "chrome/test/webdriver/webdriver_error.h"
36 #include "chrome/test/webdriver/session_manager.h"
37 #include "chrome/test/webdriver/utility_functions.h"
38 #include "chrome/test/webdriver/webdriver_key_converter.h" 38 #include "chrome/test/webdriver/webdriver_key_converter.h"
39 #include "chrome/test/webdriver/webdriver_util.h"
39 #include "third_party/webdriver/atoms.h" 40 #include "third_party/webdriver/atoms.h"
40 #include "ui/gfx/point.h"
41 #include "ui/gfx/rect.h"
42 #include "ui/gfx/size.h"
43 41
44 namespace webdriver { 42 namespace webdriver {
45 43
46 FrameId::FrameId(int window_id, const FramePath& frame_path) 44 FrameId::FrameId(int window_id, const FramePath& frame_path)
47 : window_id(window_id), 45 : window_id(window_id),
48 frame_path(frame_path) { 46 frame_path(frame_path) {
49 } 47 }
50 48
51 FrameId& FrameId::operator=(const FrameId& other) { 49 FrameId& FrameId::operator=(const FrameId& other) {
52 window_id = other.window_id; 50 window_id = other.window_id;
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after
124 &args_as_json); 122 &args_as_json);
125 123
126 // Every injected script is fed through the executeScript atom. This atom 124 // Every injected script is fed through the executeScript atom. This atom
127 // will catch any errors that are thrown and convert them to the 125 // will catch any errors that are thrown and convert them to the
128 // appropriate JSON structure. 126 // appropriate JSON structure.
129 std::string jscript = base::StringPrintf( 127 std::string jscript = base::StringPrintf(
130 "window.domAutomationController.send((%s).apply(null," 128 "window.domAutomationController.send((%s).apply(null,"
131 "[function(){%s\n},%s,true]));", 129 "[function(){%s\n},%s,true]));",
132 atoms::EXECUTE_SCRIPT, script.c_str(), args_as_json.c_str()); 130 atoms::EXECUTE_SCRIPT, script.c_str(), args_as_json.c_str());
133 131
134 return ExecuteScriptAndParseResponse(frame_id, jscript, value); 132 return ExecuteScriptAndParseValue(frame_id, jscript, value);
135 } 133 }
136 134
137 Error* Session::ExecuteScript(const std::string& script, 135 Error* Session::ExecuteScript(const std::string& script,
138 const ListValue* const args, 136 const ListValue* const args,
139 Value** value) { 137 Value** value) {
140 return ExecuteScript(current_target_, script, args, value); 138 return ExecuteScript(current_target_, script, args, value);
141 } 139 }
142 140
141 Error* Session::ExecuteScriptAndParse(const FrameId& frame_id,
142 const std::string& anonymous_func_script,
143 const std::string& script_name,
144 const ListValue* args,
145 const ValueParser* parser) {
146 scoped_ptr<const ListValue> scoped_args(args);
147 scoped_ptr<const ValueParser> scoped_parser(parser);
148 std::string called_script = base::StringPrintf(
149 "return (%s).apply(null, arguments);", anonymous_func_script.c_str());
150 Value* unscoped_value = NULL;
151 Error* error = ExecuteScript(frame_id, called_script, args, &unscoped_value);
152 if (error) {
153 error->AddDetails(script_name + " execution failed");
154 return error;
155 }
156
157 scoped_ptr<Value> value(unscoped_value);
158 std::string error_msg;
159 if (!parser->Parse(value.get())) {
160 error_msg = base::StringPrintf("%s returned invalid value: %s",
161 script_name.c_str(), JsonStringify(value.get()).c_str());
162 return new Error(kUnknownError, error_msg);
163 }
164 return NULL;
165 }
166
143 Error* Session::ExecuteAsyncScript(const FrameId& frame_id, 167 Error* Session::ExecuteAsyncScript(const FrameId& frame_id,
144 const std::string& script, 168 const std::string& script,
145 const ListValue* const args, 169 const ListValue* const args,
146 Value** value) { 170 Value** value) {
147 std::string args_as_json; 171 std::string args_as_json;
148 base::JSONWriter::Write(static_cast<const Value* const>(args), 172 base::JSONWriter::Write(static_cast<const Value* const>(args),
149 /*pretty_print=*/false, 173 /*pretty_print=*/false,
150 &args_as_json); 174 &args_as_json);
151 175
152 int timeout_ms = async_script_timeout(); 176 int timeout_ms = async_script_timeout();
153 177
154 // Every injected script is fed through the executeScript atom. This atom 178 // Every injected script is fed through the executeScript atom. This atom
155 // will catch any errors that are thrown and convert them to the 179 // will catch any errors that are thrown and convert them to the
156 // appropriate JSON structure. 180 // appropriate JSON structure.
157 std::string jscript = base::StringPrintf( 181 std::string jscript = base::StringPrintf(
158 "(%s).apply(null, [function(){%s},%s,%d,%s,true]);", 182 "(%s).apply(null, [function(){%s},%s,%d,%s,true]);",
159 atoms::EXECUTE_ASYNC_SCRIPT, 183 atoms::EXECUTE_ASYNC_SCRIPT,
160 script.c_str(), 184 script.c_str(),
161 args_as_json.c_str(), 185 args_as_json.c_str(),
162 timeout_ms, 186 timeout_ms,
163 "function(result) {window.domAutomationController.send(result);}"); 187 "function(result) {window.domAutomationController.send(result);}");
164 188
165 return ExecuteScriptAndParseResponse(frame_id, jscript, value); 189 return ExecuteScriptAndParseValue(frame_id, jscript, value);
166 } 190 }
167 191
168 Error* Session::SendKeys(const WebElementId& element, const string16& keys) { 192 Error* Session::SendKeys(const WebElementId& element, const string16& keys) {
169 bool is_displayed = false; 193 bool is_displayed = false;
170 Error* error = IsElementDisplayed( 194 Error* error = IsElementDisplayed(
171 current_target_, element, true /* ignore_opacity */, &is_displayed); 195 current_target_, element, true /* ignore_opacity */, &is_displayed);
172 if (error) 196 if (error)
173 return error; 197 return error;
174 if (!is_displayed) 198 if (!is_displayed)
175 return new Error(kElementNotVisible); 199 return new Error(kElementNotVisible);
176 200
177 bool is_enabled = false; 201 bool is_enabled = false;
178 error = IsElementEnabled(current_target_, element, &is_enabled); 202 error = IsElementEnabled(current_target_, element, &is_enabled);
179 if (error) 203 if (error)
180 return error; 204 return error;
181 if (!is_enabled) 205 if (!is_enabled)
182 return new Error(kInvalidElementState); 206 return new Error(kInvalidElementState);
183 207
184 ListValue args;
185 args.Append(element.ToValue());
186 // Focus the target element in order to send keys to it. 208 // Focus the target element in order to send keys to it.
187 // First, the currently active element is blurred, if it is different from 209 // First, the currently active element is blurred, if it is different from
188 // the target element. We do not want to blur an element unnecessarily, 210 // the target element. We do not want to blur an element unnecessarily,
189 // because this may cause us to lose the current cursor position in the 211 // because this may cause us to lose the current cursor position in the
190 // element. 212 // element.
191 // Secondly, we focus the target element. 213 // Secondly, we focus the target element.
192 // Thirdly, if the target element is newly focused and is a text input, we 214 // Thirdly, if the target element is newly focused and is a text input, we
193 // set the cursor position at the end. 215 // set the cursor position at the end.
194 // Fourthly, we check if the new active element is the target element. If not, 216 // Fourthly, we check if the new active element is the target element. If not,
195 // we throw an error. 217 // we throw an error.
196 // Additional notes: 218 // Additional notes:
197 // - |document.activeElement| is the currently focused element, or body if 219 // - |document.activeElement| is the currently focused element, or body if
198 // no element is focused 220 // no element is focused
199 // - Even if |document.hasFocus()| returns true and the active element is 221 // - Even if |document.hasFocus()| returns true and the active element is
200 // the body, sometimes we still need to focus the body element for send 222 // the body, sometimes we still need to focus the body element for send
201 // keys to work. Not sure why 223 // keys to work. Not sure why
202 // - You cannot focus a descendant of a content editable node 224 // - You cannot focus a descendant of a content editable node
203 // TODO(jleyba): Update this to use the correct atom. 225 // TODO(jleyba): Update this to use the correct atom.
204 const char* kFocusScript = 226 const char* kFocusScript =
205 "var elem = arguments[0];" 227 "function(elem) {"
206 "var doc = elem.ownerDocument || elem;" 228 " var doc = elem.ownerDocument || elem;"
207 "var prevActiveElem = doc.activeElement;" 229 " var prevActiveElem = doc.activeElement;"
208 "if (elem != prevActiveElem && prevActiveElem)" 230 " if (elem != prevActiveElem && prevActiveElem)"
209 " prevActiveElem.blur();" 231 " prevActiveElem.blur();"
210 "elem.focus();" 232 " elem.focus();"
211 "if (elem != prevActiveElem && elem.value && elem.value.length &&" 233 " if (elem != prevActiveElem && elem.value && elem.value.length &&"
212 " elem.setSelectionRange) {" 234 " elem.setSelectionRange) {"
213 " elem.setSelectionRange(elem.value.length, elem.value.length);" 235 " elem.setSelectionRange(elem.value.length, elem.value.length);"
214 "}" 236 " }"
215 "if (elem != doc.activeElement)" 237 " if (elem != doc.activeElement)"
216 " throw new Error('Failed to send keys because cannot focus element.');"; 238 " throw new Error('Failed to send keys because cannot focus element');"
217 Value* unscoped_result = NULL; 239 "}";
218 error = ExecuteScript(kFocusScript, &args, &unscoped_result); 240 error = ExecuteScriptAndParse(current_target_,
241 kFocusScript,
242 "focusElement",
243 CreateListValueFrom(element),
244 CreateDirectValueParser(kSkipParsing));
219 if (error) 245 if (error)
220 return error; 246 return error;
221 247
222 error = NULL;
223 RunSessionTask(NewRunnableMethod( 248 RunSessionTask(NewRunnableMethod(
224 this, 249 this,
225 &Session::SendKeysOnSessionThread, 250 &Session::SendKeysOnSessionThread,
226 keys, 251 keys,
227 &error)); 252 &error));
228 return error; 253 return error;
229 } 254 }
230 255
231 Error* Session::DragAndDropFilePaths( 256 Error* Session::DragAndDropFilePaths(
232 const gfx::Point& location, 257 const Point& location,
233 const std::vector<FilePath::StringType>& paths) { 258 const std::vector<FilePath::StringType>& paths) {
234 Error* error = NULL; 259 Error* error = NULL;
235 RunSessionTask(NewRunnableMethod( 260 RunSessionTask(NewRunnableMethod(
236 automation_.get(), 261 automation_.get(),
237 &Automation::DragAndDropFilePaths, 262 &Automation::DragAndDropFilePaths,
238 current_target_.window_id, 263 current_target_.window_id,
239 location, 264 location,
240 paths, 265 paths,
241 &error)); 266 &error));
242 return error; 267 return error;
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
286 Error* error = NULL; 311 Error* error = NULL;
287 RunSessionTask(NewRunnableMethod( 312 RunSessionTask(NewRunnableMethod(
288 automation_.get(), 313 automation_.get(),
289 &Automation::Reload, 314 &Automation::Reload,
290 current_target_.window_id, 315 current_target_.window_id,
291 &error)); 316 &error));
292 return error; 317 return error;
293 } 318 }
294 319
295 Error* Session::GetURL(std::string* url) { 320 Error* Session::GetURL(std::string* url) {
296 ListValue no_args; 321 return ExecuteScriptAndParse(current_target_,
297 Value* unscoped_value = NULL; 322 "function() { return document.URL }",
298 Error* error = ExecuteScript(current_target_, 323 "getUrl",
299 "return document.URL;", 324 new ListValue(),
300 &no_args, 325 CreateDirectValueParser(url));
301 &unscoped_value);
302 scoped_ptr<Value> value(unscoped_value);
303 if (error)
304 return error;
305 if (!value->GetAsString(url))
306 return new Error(kUnknownError, "GetURL Script returned non-string: " +
307 JsonStringify(value.get()));
308 return NULL;
309 } 326 }
310 327
311 Error* Session::GetTitle(std::string* tab_title) { 328 Error* Session::GetTitle(std::string* tab_title) {
312 std::string script = 329 const char* kGetTitleScript =
313 "if (document.title)" 330 "function() {"
314 " return document.title;" 331 " if (document.title)"
315 "else" 332 " return document.title;"
316 " return document.URL;"; 333 " else"
317 334 " return document.URL;"
318 ListValue no_args; 335 "}";
319 Value* unscoped_value = NULL; 336 return ExecuteScriptAndParse(current_target_,
320 Error* error = ExecuteScript(current_target_, 337 kGetTitleScript,
321 script, 338 "getTitle",
322 &no_args, 339 new ListValue(),
323 &unscoped_value); 340 CreateDirectValueParser(tab_title));
324 scoped_ptr<Value> value(unscoped_value);
325 if (error)
326 return error;
327 if (!value->GetAsString(tab_title))
328 return new Error(kUnknownError, "GetTitle script returned non-string: " +
329 JsonStringify(value.get()));
330 return NULL;
331 } 341 }
332 342
333 Error* Session::MouseMoveAndClick(const gfx::Point& location, 343 Error* Session::MouseMoveAndClick(const Point& location,
334 automation::MouseButton button) { 344 automation::MouseButton button) {
335 Error* error = NULL; 345 Error* error = NULL;
336 RunSessionTask(NewRunnableMethod( 346 RunSessionTask(NewRunnableMethod(
337 automation_.get(), 347 automation_.get(),
338 &Automation::MouseClick, 348 &Automation::MouseClick,
339 current_target_.window_id, 349 current_target_.window_id,
340 location, 350 location,
341 button, 351 button,
342 &error)); 352 &error));
343 if (!error) 353 if (!error)
344 mouse_position_ = location; 354 mouse_position_ = location;
345 return error; 355 return error;
346 } 356 }
347 357
348 Error* Session::MouseMove(const gfx::Point& location) { 358 Error* Session::MouseMove(const Point& location) {
349 Error* error = NULL; 359 Error* error = NULL;
350 RunSessionTask(NewRunnableMethod( 360 RunSessionTask(NewRunnableMethod(
351 automation_.get(), 361 automation_.get(),
352 &Automation::MouseMove, 362 &Automation::MouseMove,
353 current_target_.window_id, 363 current_target_.window_id,
354 location, 364 location,
355 &error)); 365 &error));
356 if (!error) 366 if (!error)
357 mouse_position_ = location; 367 mouse_position_ = location;
358 return error; 368 return error;
359 } 369 }
360 370
361 Error* Session::MouseDrag(const gfx::Point& start, 371 Error* Session::MouseDrag(const Point& start,
362 const gfx::Point& end) { 372 const Point& end) {
363 Error* error = NULL; 373 Error* error = NULL;
364 RunSessionTask(NewRunnableMethod( 374 RunSessionTask(NewRunnableMethod(
365 automation_.get(), 375 automation_.get(),
366 &Automation::MouseDrag, 376 &Automation::MouseDrag,
367 current_target_.window_id, 377 current_target_.window_id,
368 start, 378 start,
369 end, 379 end,
370 &error)); 380 &error));
371 if (!error) 381 if (!error)
372 mouse_position_ = end; 382 mouse_position_ = end;
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after
473 switch_to_id = name_no; 483 switch_to_id = name_no;
474 } 484 }
475 485
476 if (!switch_to_id) { 486 if (!switch_to_id) {
477 std::vector<int> window_ids; 487 std::vector<int> window_ids;
478 Error* error = GetWindowIds(&window_ids); 488 Error* error = GetWindowIds(&window_ids);
479 if (error) 489 if (error)
480 return error; 490 return error;
481 // See if any of the window names match |name|. 491 // See if any of the window names match |name|.
482 for (size_t i = 0; i < window_ids.size(); ++i) { 492 for (size_t i = 0; i < window_ids.size(); ++i) {
483 ListValue empty_list;
484 Value* unscoped_name_value = NULL;
485 std::string window_name; 493 std::string window_name;
486 Error* error = ExecuteScript(FrameId(window_ids[i], FramePath()), 494 Error* error = ExecuteScriptAndParse(
487 "return window.name;", 495 FrameId(window_ids[i], FramePath()),
488 &empty_list, 496 "function() { return window.name; }",
489 &unscoped_name_value); 497 "getWindowName",
498 new ListValue(),
499 CreateDirectValueParser(&window_name));
490 if (error) 500 if (error)
491 return error; 501 return error;
492 scoped_ptr<Value> name_value(unscoped_name_value); 502 if (name == window_name) {
493 if (name_value->GetAsString(&window_name) &&
494 name == window_name) {
495 switch_to_id = window_ids[i]; 503 switch_to_id = window_ids[i];
496 break; 504 break;
497 } 505 }
498 } 506 }
499 } 507 }
500 508
501 if (!switch_to_id) 509 if (!switch_to_id)
502 return new Error(kNoSuchWindow); 510 return new Error(kNoSuchWindow);
503 frame_elements_.clear(); 511 frame_elements_.clear();
504 current_target_ = FrameId(switch_to_id, FramePath()); 512 current_target_ = FrameId(switch_to_id, FramePath());
505 return NULL; 513 return NULL;
506 } 514 }
507 515
508 Error* Session::SwitchToFrameWithNameOrId(const std::string& name_or_id) { 516 Error* Session::SwitchToFrameWithNameOrId(const std::string& name_or_id) {
509 std::string script = 517 std::string script =
510 "var arg = arguments[0];" 518 "function(arg) {"
511 "var xpath = '(/html/body//iframe|/html/frameset/frame)';" 519 " var xpath = '(/html/body//iframe|/html/frameset/frame)';"
512 "var sub = function(s) { return s.replace(/\\$/g, arg); };" 520 " var sub = function(s) { return s.replace(/\\$/g, arg); };"
513 "xpath += sub('[@name=\"$\" or @id=\"$\"]');" 521 " xpath += sub('[@name=\"$\" or @id=\"$\"]');"
514 "var frame = document.evaluate(xpath, document, null, " 522 " var frame = document.evaluate(xpath, document, null, "
515 " XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;" 523 " XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;"
516 "if (!frame) { return null; }" 524 " if (!frame) { return null; }"
517 "xpath = frame.tagName == 'IFRAME' ? '/html/body//iframe'" 525 " xpath = frame.tagName == 'IFRAME' ? '/html/body//iframe'"
518 " : '/html/frameset/frame';" 526 " : '/html/frameset/frame';"
519 "frame_xpath = xpath + " 527 " frame_xpath = xpath + "
520 " sub('[@' + (frame.id == arg ? 'id' : 'name') + '=\"$\"]');" 528 " sub('[@' + (frame.id == arg ? 'id' : 'name') + '=\"$\"]');"
521 "return [frame, frame_xpath];"; 529 " return [frame, frame_xpath];"
522 ListValue args; 530 "}";
523 args.Append(new StringValue(name_or_id)); 531 return SwitchToFrameWithJavaScriptLocatedFrame(
524 return SwitchToFrameWithJavaScriptLocatedFrame(script, &args); 532 script, CreateListValueFrom(name_or_id));
525 } 533 }
526 534
527 Error* Session::SwitchToFrameWithIndex(int index) { 535 Error* Session::SwitchToFrameWithIndex(int index) {
528 // We cannot simply index into window.frames because we need to know the 536 // We cannot simply index into window.frames because we need to know the
529 // tagName of the frameElement. If child frame N is from another domain, then 537 // tagName of the frameElement. If child frame N is from another domain, then
530 // the following will run afoul of the same origin policy: 538 // the following will run afoul of the same origin policy:
531 // window.frames[N].frameElement; 539 // window.frames[N].frameElement;
532 // Instead of indexing window.frames, we use a an XPath expression to index 540 // Instead of indexing window.frames, we use a an XPath expression to index
533 // into the list of all IFRAME and FRAME elements on the page - if we find 541 // into the list of all IFRAME and FRAME elements on the page - if we find
534 // something, then that XPath expression can be used as the new frame's XPath. 542 // something, then that XPath expression can be used as the new frame's XPath.
535 std::string script = 543 std::string script =
536 "var index = '[' + (arguments[0] + 1) + ']';" 544 "function(index) {"
537 "var xpath = '(/html/body//iframe|/html/frameset/frame)' + " 545 " var xpathIndex = '[' + (index + 1) + ']';"
538 " index;" 546 " var xpath = '(/html/body//iframe|/html/frameset/frame)' + "
539 "console.info('searching for frame by xpath: ' + xpath);" 547 " xpathIndex;"
540 "var frame = document.evaluate(xpath, document, null, " 548 " var frame = document.evaluate(xpath, document, null, "
541 "XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;" 549 " XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;"
542 "console.info(frame == null ? 'found nothing' : frame);" 550 " if (!frame) { return null; }"
543 "if (!frame) { return null; }" 551 " frame_xpath = ((frame.tagName == 'IFRAME' ? "
544 "frame_xpath = ((frame.tagName == 'IFRAME' ? " 552 " '(/html/body//iframe)' : '/html/frameset/frame') + xpathIndex);"
545 " '(/html/body//iframe)' : '/html/frameset/frame') + index);" 553 " return [frame, frame_xpath];"
546 "return [frame, frame_xpath];"; 554 "}";
547 ListValue args; 555 return SwitchToFrameWithJavaScriptLocatedFrame(
548 args.Append(Value::CreateIntegerValue(index)); 556 script, CreateListValueFrom(index));
549 return SwitchToFrameWithJavaScriptLocatedFrame(script, &args);
550 } 557 }
551 558
552 Error* Session::SwitchToFrameWithElement(const WebElementId& element) { 559 Error* Session::SwitchToFrameWithElement(const WebElementId& element) {
553 // TODO(jleyba): Extract this, and the other frame switch methods to an atom. 560 // TODO(jleyba): Extract this, and the other frame switch methods to an atom.
554 std::string script = 561 std::string script =
555 "var element = arguments[0];" 562 "function(elem) {"
556 "console.info('Attempting to switch to ' + element);" 563 " if (elem.nodeType != 1 || !/^i?frame$/i.test(elem.tagName)) {"
557 "if (element.nodeType != 1 || !/^i?frame$/i.test(element.tagName)) {" 564 " console.error('Element is not a frame');"
558 " console.info('Element is not a frame: ' + element + " 565 " return null;"
559 "' {nodeType:' + element.nodeType + ',tagName:' + element.tagName + '}');" 566 " }"
567 " for (var i = 0; i < window.frames.length; i++) {"
568 " if (elem.contentWindow == window.frames[i]) {"
569 " return [elem, '(//iframe|//frame)[' + (i + 1) + ']'];"
570 " }"
571 " }"
572 " console.info('Frame is not connected to this DOM tree');"
560 " return null;" 573 " return null;"
561 "}" 574 "}";
562 "for (var i = 0; i < window.frames.length; i++) {" 575 return SwitchToFrameWithJavaScriptLocatedFrame(
563 " if (element.contentWindow == window.frames[i]) {" 576 script, CreateListValueFrom(element));
564 " return [element, '(//iframe|//frame)[' + (i + 1) + ']'];"
565 " }"
566 "}"
567 "console.info('Frame is not connected to this DOM tree');"
568 "return null;";
569
570 ListValue args;
571 args.Append(element.ToValue());
572 return SwitchToFrameWithJavaScriptLocatedFrame(script, &args);
573 } 577 }
574 578
575 void Session::SwitchToTopFrame() { 579 void Session::SwitchToTopFrame() {
576 frame_elements_.clear(); 580 frame_elements_.clear();
577 current_target_.frame_path = FramePath(); 581 current_target_.frame_path = FramePath();
578 } 582 }
579 583
580 Error* Session::SwitchToTopFrameIfCurrentFrameInvalid() { 584 Error* Session::SwitchToTopFrameIfCurrentFrameInvalid() {
581 std::vector<std::string> components; 585 std::vector<std::string> components;
582 current_target_.frame_path.GetComponents(&components); 586 current_target_.frame_path.GetComponents(&components);
583 if (frame_elements_.size() != components.size()) { 587 if (frame_elements_.size() != components.size()) {
584 return new Error(kUnknownError, 588 return new Error(kUnknownError,
585 "Frame element vector out of sync with frame path"); 589 "Frame element vector out of sync with frame path");
586 } 590 }
587 FramePath frame_path; 591 FramePath frame_path;
588 // Start from the root path and check that each frame element that makes 592 // Start from the root path and check that each frame element that makes
589 // up the current frame target is valid by executing an empty script. 593 // up the current frame target is valid by executing an empty script.
590 // This code should not execute script in any frame before making sure the 594 // This code should not execute script in any frame before making sure the
591 // frame element is valid, otherwise the automation hangs until a timeout. 595 // frame element is valid, otherwise the automation hangs until a timeout.
592 for (size_t i = 0; i < frame_elements_.size(); ++i) { 596 for (size_t i = 0; i < frame_elements_.size(); ++i) {
593 FrameId frame_id(current_target_.window_id, frame_path); 597 FrameId frame_id(current_target_.window_id, frame_path);
594 ListValue args; 598 scoped_ptr<Error> error(ExecuteScriptAndParse(
595 args.Append(frame_elements_[i].ToValue()); 599 frame_id,
596 Value* unscoped_value = NULL; 600 "function(){ }",
597 scoped_ptr<Error> error(ExecuteScript( 601 "emptyScript",
598 frame_id, "", &args, &unscoped_value)); 602 CreateListValueFrom(frame_elements_[i]),
599 603 CreateDirectValueParser(kSkipParsing)));
600 scoped_ptr<Value> value(unscoped_value);
601 if (error.get() && error->code() == kStaleElementReference) { 604 if (error.get() && error->code() == kStaleElementReference) {
602 SwitchToTopFrame(); 605 SwitchToTopFrame();
603 } else if (error.get()) { 606 } else if (error.get()) {
604 return error.release(); 607 return error.release();
605 } 608 }
606 frame_path = frame_path.Append(components[i]); 609 frame_path = frame_path.Append(components[i]);
607 } 610 }
608 return NULL; 611 return NULL;
609 } 612 }
610 613
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after
723 const WebElementId& root_element, 726 const WebElementId& root_element,
724 const std::string& locator, 727 const std::string& locator,
725 const std::string& query, 728 const std::string& query,
726 std::vector<WebElementId>* elements) { 729 std::vector<WebElementId>* elements) {
727 return FindElementsHelper( 730 return FindElementsHelper(
728 frame_id, root_element, locator, query, false, elements); 731 frame_id, root_element, locator, query, false, elements);
729 } 732 }
730 733
731 Error* Session::GetElementLocationInView( 734 Error* Session::GetElementLocationInView(
732 const WebElementId& element, 735 const WebElementId& element,
733 gfx::Point* location) { 736 Point* location) {
734 gfx::Size size; 737 Size size;
735 Error* error = GetElementSize(current_target_, element, &size); 738 Error* error = GetElementSize(current_target_, element, &size);
736 if (error) 739 if (error)
737 return error; 740 return error;
738 return GetElementRegionInView( 741 return GetElementRegionInView(
739 element, gfx::Rect(gfx::Point(0, 0), size), 742 element, Rect(Point(0, 0), size),
740 false /* center */, false /* verify_clickable_at_middle */, location); 743 false /* center */, false /* verify_clickable_at_middle */, location);
741 } 744 }
742 745
743 Error* Session::GetElementRegionInView( 746 Error* Session::GetElementRegionInView(
744 const WebElementId& element, 747 const WebElementId& element,
745 const gfx::Rect& region, 748 const Rect& region,
746 bool center, 749 bool center,
747 bool verify_clickable_at_middle, 750 bool verify_clickable_at_middle,
748 gfx::Point* location) { 751 Point* location) {
749 CHECK(element.is_valid()); 752 CHECK(element.is_valid());
750 753
751 gfx::Point region_offset = region.origin(); 754 Point region_offset = region.origin();
752 gfx::Size region_size = region.size(); 755 Size region_size = region.size();
753 Error* error = GetElementRegionInViewHelper( 756 Error* error = GetElementRegionInViewHelper(
754 current_target_, element, region, center, verify_clickable_at_middle, 757 current_target_, element, region, center, verify_clickable_at_middle,
755 &region_offset); 758 &region_offset);
756 if (error) 759 if (error)
757 return error; 760 return error;
758 761
759 for (FramePath frame_path = current_target_.frame_path; 762 for (FramePath frame_path = current_target_.frame_path;
760 frame_path.IsSubframe(); 763 frame_path.IsSubframe();
761 frame_path = frame_path.Parent()) { 764 frame_path = frame_path.Parent()) {
762 // Find the frame element for the current frame path. 765 // Find the frame element for the current frame path.
(...skipping 12 matching lines...) Expand all
775 } 778 }
776 // Modify |region_offset| by the frame's border. 779 // Modify |region_offset| by the frame's border.
777 int border_left, border_top; 780 int border_left, border_top;
778 error = GetElementBorder( 781 error = GetElementBorder(
779 frame_id, frame_element, &border_left, &border_top); 782 frame_id, frame_element, &border_left, &border_top);
780 if (error) 783 if (error)
781 return error; 784 return error;
782 region_offset.Offset(border_left, border_top); 785 region_offset.Offset(border_left, border_top);
783 786
784 error = GetElementRegionInViewHelper( 787 error = GetElementRegionInViewHelper(
785 frame_id, frame_element, gfx::Rect(region_offset, region_size), 788 frame_id, frame_element, Rect(region_offset, region_size),
786 center, verify_clickable_at_middle, &region_offset); 789 center, verify_clickable_at_middle, &region_offset);
787 if (error) 790 if (error)
788 return error; 791 return error;
789 } 792 }
790 *location = region_offset; 793 *location = region_offset;
791 return NULL; 794 return NULL;
792 } 795 }
793 796
794 Error* Session::GetElementSize(const FrameId& frame_id, 797 Error* Session::GetElementSize(const FrameId& frame_id,
795 const WebElementId& element, 798 const WebElementId& element,
796 gfx::Size* size) { 799 Size* size) {
797 std::string script = base::StringPrintf( 800 return ExecuteScriptAndParse(frame_id,
798 "return (%s).apply(null, arguments);", atoms::GET_SIZE); 801 atoms::GET_SIZE,
799 ListValue args; 802 "getSize",
800 args.Append(element.ToValue()); 803 CreateListValueFrom(element),
801 804 CreateDirectValueParser(size));
802 Value* unscoped_result = NULL;
803 Error* error = ExecuteScript(frame_id, script, &args, &unscoped_result);
804 scoped_ptr<Value> result(unscoped_result);
805 if (error)
806 return error;
807 if (!result->IsType(Value::TYPE_DICTIONARY)) {
808 return new Error(kUnknownError, "GetSize atom returned non-dict type: " +
809 JsonStringify(result.get()));
810 }
811 DictionaryValue* dict = static_cast<DictionaryValue*>(result.get());
812 int width, height;
813 if (!dict->GetInteger("width", &width) ||
814 !dict->GetInteger("height", &height)) {
815 return new Error(kUnknownError, "GetSize atom returned invalid dict: " +
816 JsonStringify(dict));
817 }
818 *size = gfx::Size(width, height);
819 return NULL;
820 } 805 }
821 806
822 Error* Session::GetElementFirstClientRect(const FrameId& frame_id, 807 Error* Session::GetElementFirstClientRect(const FrameId& frame_id,
823 const WebElementId& element, 808 const WebElementId& element,
824 gfx::Rect* rect) { 809 Rect* rect) {
825 std::string script = base::StringPrintf( 810 return ExecuteScriptAndParse(frame_id,
826 "return (%s).apply(null, arguments);", atoms::GET_FIRST_CLIENT_RECT); 811 atoms::GET_FIRST_CLIENT_RECT,
827 ListValue args; 812 "getFirstClientRect",
828 args.Append(element.ToValue()); 813 CreateListValueFrom(element),
829 814 CreateDirectValueParser(rect));
830 Value* unscoped_result = NULL;
831 Error* error = ExecuteScript(frame_id, script, &args, &unscoped_result);
832 scoped_ptr<Value> result(unscoped_result);
833 if (error)
834 return error;
835 if (!result->IsType(Value::TYPE_DICTIONARY)) {
836 return new Error(
837 kUnknownError,
838 "GetFirstClientRect atom returned non-dict type: " +
839 JsonStringify(result.get()));
840 }
841 DictionaryValue* dict = static_cast<DictionaryValue*>(result.get());
842 // TODO(kkania): Convert the atom to return integers.
843 double left, top, width, height;
844 if (!dict->GetDouble("left", &left) ||
845 !dict->GetDouble("top", &top) ||
846 !dict->GetDouble("width", &width) ||
847 !dict->GetDouble("height", &height)) {
848 return new Error(
849 kUnknownError,
850 "GetFirstClientRect atom returned invalid dict: " +
851 JsonStringify(dict));
852 }
853 *rect = gfx::Rect(static_cast<int>(left), static_cast<int>(top),
854 static_cast<int>(width), static_cast<int>(height));
855 return NULL;
856 } 815 }
857 816
858 Error* Session::GetElementEffectiveStyle( 817 Error* Session::GetElementEffectiveStyle(
859 const FrameId& frame_id, 818 const FrameId& frame_id,
860 const WebElementId& element, 819 const WebElementId& element,
861 const std::string& prop, 820 const std::string& prop,
862 std::string* value) { 821 std::string* value) {
863 std::string script = base::StringPrintf( 822 return ExecuteScriptAndParse(frame_id,
864 "return (%s).apply(null, arguments);", atoms::GET_EFFECTIVE_STYLE); 823 atoms::GET_EFFECTIVE_STYLE,
865 ListValue args; 824 "getEffectiveStyle",
866 args.Append(element.ToValue()); 825 CreateListValueFrom(element, prop),
867 args.Append(Value::CreateStringValue(prop)); 826 CreateDirectValueParser(value));
868 Value* unscoped_result = NULL;
869 Error* error = ExecuteScript(
870 frame_id, script, &args, &unscoped_result);
871 scoped_ptr<Value> result(unscoped_result);
872 if (error) {
873 error->AddDetails(base::StringPrintf(
874 "GetEffectiveStyle atom failed for property (%s)", prop.c_str()));
875 return error;
876 }
877
878 if (!result->GetAsString(value)) {
879 std::string context = base::StringPrintf(
880 "GetEffectiveStyle atom returned non-string for property (%s): %s",
881 prop.c_str(), JsonStringify(result.get()).c_str());
882 return new Error(kUnknownError, context);
883 }
884 return NULL;
885 } 827 }
886 828
887 Error* Session::GetElementBorder(const FrameId& frame_id, 829 Error* Session::GetElementBorder(const FrameId& frame_id,
888 const WebElementId& element, 830 const WebElementId& element,
889 int* border_left, 831 int* border_left,
890 int* border_top) { 832 int* border_top) {
891 std::string border_left_str, border_top_str; 833 std::string border_left_str, border_top_str;
892 Error* error = GetElementEffectiveStyle( 834 Error* error = GetElementEffectiveStyle(
893 frame_id, element, "border-left-width", &border_left_str); 835 frame_id, element, "border-left-width", &border_left_str);
894 if (error) 836 if (error)
895 return error; 837 return error;
896 error = GetElementEffectiveStyle( 838 error = GetElementEffectiveStyle(
897 frame_id, element, "border-top-width", &border_top_str); 839 frame_id, element, "border-top-width", &border_top_str);
898 if (error) 840 if (error)
899 return error; 841 return error;
900 842
901 base::StringToInt(border_left_str, border_left); 843 base::StringToInt(border_left_str, border_left);
902 base::StringToInt(border_top_str, border_top); 844 base::StringToInt(border_top_str, border_top);
903 return NULL; 845 return NULL;
904 } 846 }
905 847
906 Error* Session::IsElementDisplayed(const FrameId& frame_id, 848 Error* Session::IsElementDisplayed(const FrameId& frame_id,
907 const WebElementId& element, 849 const WebElementId& element,
908 bool ignore_opacity, 850 bool ignore_opacity,
909 bool* is_displayed) { 851 bool* is_displayed) {
910 std::string script = base::StringPrintf( 852 return ExecuteScriptAndParse(frame_id,
911 "return (%s).apply(null, arguments);", atoms::IS_DISPLAYED); 853 atoms::IS_DISPLAYED,
912 ListValue args; 854 "isDisplayed",
913 args.Append(element.ToValue()); 855 CreateListValueFrom(element, ignore_opacity),
914 args.Append(Value::CreateBooleanValue(ignore_opacity)); 856 CreateDirectValueParser(is_displayed));
915
916 Value* unscoped_result = NULL;
917 Error* error = ExecuteScript(frame_id, script, &args, &unscoped_result);
918 scoped_ptr<Value> result(unscoped_result);
919 if (error)
920 return error;
921 if (!result->GetAsBoolean(is_displayed))
922 return new Error(kUnknownError, "IsDisplayed atom returned non-boolean: " +
923 JsonStringify(result.get()));
924 return NULL;
925 } 857 }
926 858
927 Error* Session::IsElementEnabled(const FrameId& frame_id, 859 Error* Session::IsElementEnabled(const FrameId& frame_id,
928 const WebElementId& element, 860 const WebElementId& element,
929 bool* is_enabled) { 861 bool* is_enabled) {
930 std::string script = base::StringPrintf( 862 return ExecuteScriptAndParse(frame_id,
931 "return (%s).apply(null, arguments);", atoms::IS_ENABLED); 863 atoms::IS_ENABLED,
932 ListValue args; 864 "isEnabled",
933 args.Append(element.ToValue()); 865 CreateListValueFrom(element),
934 866 CreateDirectValueParser(is_enabled));
935 Value* unscoped_result = NULL;
936 Error* error = ExecuteScript(frame_id, script, &args, &unscoped_result);
937 scoped_ptr<Value> result(unscoped_result);
938 if (error)
939 return error;
940 if (!result->GetAsBoolean(is_enabled))
941 return new Error(kUnknownError, "IsEnabled atom returned non-boolean: " +
942 JsonStringify(result.get()));
943 return NULL;
944 } 867 }
945 868
946 Error* Session::IsOptionElementSelected(const FrameId& frame_id, 869 Error* Session::IsOptionElementSelected(const FrameId& frame_id,
947 const WebElementId& element, 870 const WebElementId& element,
948 bool* is_selected) { 871 bool* is_selected) {
949 ListValue args; 872 return ExecuteScriptAndParse(
950 args.Append(element.ToValue()); 873 frame_id,
951 874 atoms::IS_SELECTED,
952 std::string script = base::StringPrintf( 875 "isSelected",
953 "return (%s).apply(null, arguments);", atoms::IS_SELECTED); 876 CreateListValueFrom(element),
954 877 CreateDirectValueParser(is_selected));
955 Value* result = NULL;
956 Error* error = ExecuteScript(frame_id, script, &args, &result);
957 if (error)
958 return error;
959 scoped_ptr<Value> scoped_result(result);
960 if (!result->GetAsBoolean(is_selected)) {
961 return new Error(kUnknownError, "isSelected atom returned non-boolean: " +
962 JsonStringify(result));
963 }
964 return NULL;
965 } 878 }
966 879
967 Error* Session::SetOptionElementSelected(const FrameId& frame_id, 880 Error* Session::SetOptionElementSelected(const FrameId& frame_id,
968 const WebElementId& element, 881 const WebElementId& element,
969 bool selected) { 882 bool selected) {
970 ListValue args; 883 return ExecuteScriptAndParse(frame_id,
971 args.Append(element.ToValue()); 884 atoms::SET_SELECTED,
972 args.Append(Value::CreateBooleanValue(selected)); 885 "setSelected",
973 886 CreateListValueFrom(element, selected),
974 std::string script = base::StringPrintf( 887 CreateDirectValueParser(kSkipParsing));
975 "return (%s).apply(null, arguments);", atoms::SET_SELECTED);
976
977 Value* unscoped_result = NULL;
978 Error* error = ExecuteScript(frame_id, script, &args, &unscoped_result);
979 scoped_ptr<Value> result(unscoped_result);
980 return error;
981 } 888 }
982 889
983 Error* Session::ToggleOptionElement(const FrameId& frame_id, 890 Error* Session::ToggleOptionElement(const FrameId& frame_id,
984 const WebElementId& element) { 891 const WebElementId& element) {
985 bool is_selected; 892 bool is_selected;
986 Error* error = IsOptionElementSelected(frame_id, element, &is_selected); 893 Error* error = IsOptionElementSelected(frame_id, element, &is_selected);
987 if (error) 894 if (error)
988 return error; 895 return error;
989 896
990 return SetOptionElementSelected(frame_id, element, !is_selected); 897 return SetOptionElementSelected(frame_id, element, !is_selected);
991 } 898 }
992 899
993 Error* Session::GetElementTagName(const FrameId& frame_id, 900 Error* Session::GetElementTagName(const FrameId& frame_id,
994 const WebElementId& element, 901 const WebElementId& element,
995 std::string* tag_name) { 902 std::string* tag_name) {
996 ListValue args; 903 return ExecuteScriptAndParse(
997 args.Append(element.ToValue()); 904 frame_id,
998 905 "function(elem) { return elem.tagName.toLowerCase() }",
999 std::string script = "return arguments[0].tagName.toLocaleLowerCase();"; 906 "getElementTagName",
1000 907 CreateListValueFrom(element),
1001 Value* unscoped_result = NULL; 908 CreateDirectValueParser(tag_name));
1002 Error* error = ExecuteScript(frame_id, script, &args, &unscoped_result);
1003 scoped_ptr<Value> result(unscoped_result);
1004 if (error)
1005 return error;
1006 if (!result->GetAsString(tag_name))
1007 return new Error(kUnknownError, "TagName script returned non-string: " +
1008 JsonStringify(result.get()));
1009 return NULL;
1010 } 909 }
1011 910
1012 Error* Session::GetClickableLocation(const WebElementId& element, 911 Error* Session::GetClickableLocation(const WebElementId& element,
1013 gfx::Point* location) { 912 Point* location) {
1014 bool is_displayed = false; 913 bool is_displayed = false;
1015 Error* error = IsElementDisplayed( 914 Error* error = IsElementDisplayed(
1016 current_target_, element, true /* ignore_opacity */, &is_displayed); 915 current_target_, element, true /* ignore_opacity */, &is_displayed);
1017 if (error) 916 if (error)
1018 return error; 917 return error;
1019 if (!is_displayed) 918 if (!is_displayed)
1020 return new Error(kElementNotVisible, "Element must be displayed to click"); 919 return new Error(kElementNotVisible, "Element must be displayed to click");
1021 920
1022 gfx::Rect rect; 921 Rect rect;
1023 error = GetElementFirstClientRect(current_target_, element, &rect); 922 error = GetElementFirstClientRect(current_target_, element, &rect);
1024 if (error) 923 if (error)
1025 return error; 924 return error;
1026 925
1027 error = GetElementRegionInView( 926 error = GetElementRegionInView(
1028 element, rect, true /* center */, true /* verify_clickable_at_middle */, 927 element, rect, true /* center */, true /* verify_clickable_at_middle */,
1029 location); 928 location);
1030 if (error) 929 if (error)
1031 return error; 930 return error;
1032 location->Offset(rect.width() / 2, rect.height() / 2); 931 location->Offset(rect.width() / 2, rect.height() / 2);
1033 return NULL; 932 return NULL;
1034 } 933 }
1035 934
1036 Error* Session::GetAttribute(const WebElementId& element, 935 Error* Session::GetAttribute(const WebElementId& element,
1037 const std::string& key, Value** value) { 936 const std::string& key,
1038 std::string script = base::StringPrintf( 937 Value** value) {
1039 "return (%s).apply(null, arguments);", atoms::GET_ATTRIBUTE); 938 return ExecuteScriptAndParse(current_target_,
1040 939 atoms::GET_ATTRIBUTE,
1041 ListValue args; 940 "getAttribute",
1042 args.Append(element.ToValue()); 941 CreateListValueFrom(element, key),
1043 args.Append(Value::CreateStringValue(key)); 942 CreateDirectValueParser(value));
1044
1045 Error* error = ExecuteScript(script, &args, value);
1046 if (error) {
1047 return error;
1048 }
1049
1050 return NULL;
1051 } 943 }
1052 944
1053 Error* Session::WaitForAllTabsToStopLoading() { 945 Error* Session::WaitForAllTabsToStopLoading() {
1054 if (!automation_.get()) 946 if (!automation_.get())
1055 return NULL; 947 return NULL;
1056 Error* error = NULL; 948 Error* error = NULL;
1057 RunSessionTask(NewRunnableMethod( 949 RunSessionTask(NewRunnableMethod(
1058 automation_.get(), 950 automation_.get(),
1059 &Automation::WaitForAllTabsToStopLoading, 951 &Automation::WaitForAllTabsToStopLoading,
1060 &error)); 952 &error));
(...skipping 27 matching lines...) Expand all
1088 } 980 }
1089 981
1090 void Session::set_implicit_wait(int timeout_ms) { 982 void Session::set_implicit_wait(int timeout_ms) {
1091 implicit_wait_ = timeout_ms; 983 implicit_wait_ = timeout_ms;
1092 } 984 }
1093 985
1094 int Session::implicit_wait() const { 986 int Session::implicit_wait() const {
1095 return implicit_wait_; 987 return implicit_wait_;
1096 } 988 }
1097 989
1098 const gfx::Point& Session::get_mouse_position() const { 990 const Point& Session::get_mouse_position() const {
1099 return mouse_position_; 991 return mouse_position_;
1100 } 992 }
1101 993
1102 const Session::Options& Session::options() const { 994 const Session::Options& Session::options() const {
1103 return options_; 995 return options_;
1104 } 996 }
1105 997
1106 void Session::RunSessionTask(Task* task) { 998 void Session::RunSessionTask(Task* task) {
1107 base::WaitableEvent done_event(false, false); 999 base::WaitableEvent done_event(false, false);
1108 thread_.message_loop_proxy()->PostTask(FROM_HERE, NewRunnableMethod( 1000 thread_.message_loop_proxy()->PostTask(FROM_HERE, NewRunnableMethod(
(...skipping 29 matching lines...) Expand all
1138 } 1030 }
1139 current_target_ = FrameId(tab_ids[0], FramePath()); 1031 current_target_ = FrameId(tab_ids[0], FramePath());
1140 } 1032 }
1141 1033
1142 void Session::TerminateOnSessionThread() { 1034 void Session::TerminateOnSessionThread() {
1143 if (automation_.get()) 1035 if (automation_.get())
1144 automation_->Terminate(); 1036 automation_->Terminate();
1145 automation_.reset(); 1037 automation_.reset();
1146 } 1038 }
1147 1039
1148 Error* Session::ExecuteScriptAndParseResponse(const FrameId& frame_id, 1040 Error* Session::ExecuteScriptAndParseValue(const FrameId& frame_id,
1149 const std::string& script, 1041 const std::string& script,
1150 Value** script_result) { 1042 Value** script_result) {
1151 std::string response_json; 1043 std::string response_json;
1152 Error* error = NULL; 1044 Error* error = NULL;
1153 RunSessionTask(NewRunnableMethod( 1045 RunSessionTask(NewRunnableMethod(
1154 automation_.get(), 1046 automation_.get(),
1155 &Automation::ExecuteScript, 1047 &Automation::ExecuteScript,
1156 frame_id.window_id, 1048 frame_id.window_id,
1157 frame_id.frame_path, 1049 frame_id.frame_path,
1158 script, 1050 script,
1159 &response_json, 1051 &response_json,
1160 &error)); 1052 &error));
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after
1229 key_events[i].modified_text.c_str(), 1121 key_events[i].modified_text.c_str(),
1230 key_events[i].modifiers); 1122 key_events[i].modifiers);
1231 (*error)->AddDetails(details); 1123 (*error)->AddDetails(details);
1232 return; 1124 return;
1233 } 1125 }
1234 } 1126 }
1235 } 1127 }
1236 1128
1237 Error* Session::SwitchToFrameWithJavaScriptLocatedFrame( 1129 Error* Session::SwitchToFrameWithJavaScriptLocatedFrame(
1238 const std::string& script, ListValue* args) { 1130 const std::string& script, ListValue* args) {
1239 Value* unscoped_result = NULL; 1131 class SwitchFrameValueParser : public ValueParser {
1240 Error* error = ExecuteScript(script, args, &unscoped_result); 1132 public:
1241 scoped_ptr<Value> result(unscoped_result); 1133 SwitchFrameValueParser(
1134 bool* found_frame, WebElementId* frame, std::string* xpath)
1135 : found_frame_(found_frame), frame_(frame), xpath_(xpath) { }
1136
1137 virtual ~SwitchFrameValueParser() { }
1138
1139 virtual bool Parse(base::Value* value) const OVERRIDE {
1140 if (value->IsType(Value::TYPE_NULL)) {
1141 *found_frame_ = false;
1142 return true;
1143 }
1144 ListValue* list;
1145 if (!value->GetAsList(&list))
1146 return false;
1147 *found_frame_ = true;
1148 return SetFromListValue(list, frame_, xpath_);
1149 }
1150
1151 private:
1152 bool* found_frame_;
1153 WebElementId* frame_;
1154 std::string* xpath_;
1155 };
1156
1157 bool found_frame;
1158 WebElementId new_frame_element;
1159 std::string xpath;
1160 Error* error = ExecuteScriptAndParse(
1161 current_target_, script, "switchFrame", args,
1162 new SwitchFrameValueParser(&found_frame, &new_frame_element, &xpath));
1242 if (error) 1163 if (error)
1243 return error; 1164 return error;
1244 1165
1245 ListValue* frame_and_xpath_list; 1166 if (!found_frame)
1246 if (!result->GetAsList(&frame_and_xpath_list))
1247 return new Error(kNoSuchFrame); 1167 return new Error(kNoSuchFrame);
1248 DictionaryValue* element_dict;
1249 std::string xpath;
1250 if (!frame_and_xpath_list->GetDictionary(0, &element_dict) ||
1251 !frame_and_xpath_list->GetString(1, &xpath)) {
1252 return new Error(
1253 kUnknownError,
1254 "Frame finding script did not return correct type: " +
1255 JsonStringify(frame_and_xpath_list));
1256 }
1257 WebElementId new_frame_element(element_dict);
1258 if (!new_frame_element.is_valid()) {
1259 return new Error(
1260 kUnknownError,
1261 "Frame finding script did not return a frame element: " +
1262 JsonStringify(element_dict));
1263 }
1264 1168
1265 frame_elements_.push_back(new_frame_element); 1169 frame_elements_.push_back(new_frame_element);
1266 current_target_.frame_path = current_target_.frame_path.Append(xpath); 1170 current_target_.frame_path = current_target_.frame_path.Append(xpath);
1267 return NULL; 1171 return NULL;
1268 } 1172 }
1269 1173
1270 Error* Session::FindElementsHelper(const FrameId& frame_id, 1174 Error* Session::FindElementsHelper(const FrameId& frame_id,
1271 const WebElementId& root_element, 1175 const WebElementId& root_element,
1272 const std::string& locator, 1176 const std::string& locator,
1273 const std::string& query, 1177 const std::string& query,
1274 bool find_one, 1178 bool find_one,
1275 std::vector<WebElementId>* elements) { 1179 std::vector<WebElementId>* elements) {
1276 CHECK(root_element.is_valid()); 1180 CHECK(root_element.is_valid());
1181 base::Time start_time = base::Time::Now();
1182 while (true) {
1183 std::vector<WebElementId> temp_elements;
1184 Error* error = ExecuteFindElementScriptAndParse(
1185 frame_id, root_element, locator, query, find_one, &temp_elements);
1186 if (error)
1187 return error;
1277 1188
1278 std::string jscript; 1189 if (temp_elements.size() > 0u) {
1279 if (find_one) { 1190 elements->swap(temp_elements);
1280 // TODO(jleyba): Write a Chrome-specific find element atom that will 1191 break;
1281 // correctly throw an error if the element cannot be found. 1192 }
1282 jscript = base::StringPrintf(
1283 "var result = (%s).apply(null, arguments);"
1284 "if (!result) {"
1285 "var e = new Error('Unable to locate element');"
1286 "e.code = %d;"
1287 "throw e;"
1288 "} else { return result; }",
1289 atoms::FIND_ELEMENT, kNoSuchElement);
1290 } else {
1291 jscript = base::StringPrintf("return (%s).apply(null, arguments);",
1292 atoms::FIND_ELEMENTS);
1293 }
1294 ListValue jscript_args;
1295 DictionaryValue* locator_dict = new DictionaryValue();
1296 locator_dict->SetString(locator, query);
1297 jscript_args.Append(locator_dict);
1298 jscript_args.Append(root_element.ToValue());
1299 1193
1300 // The element search needs to loop until at least one element is found or the 1194 if ((base::Time::Now() - start_time).InMilliseconds() > implicit_wait_) {
1301 // session's implicit wait timeout expires, whichever occurs first. 1195 if (find_one)
1302 base::Time start_time = base::Time::Now(); 1196 return new Error(kNoSuchElement);
1303 1197 break;
1304 scoped_ptr<Value> value;
1305 scoped_ptr<Error> error;
1306 bool done = false;
1307 while (!done) {
1308 Value* unscoped_value = NULL;
1309 error.reset(ExecuteScript(
1310 frame_id, jscript, &jscript_args, &unscoped_value));
1311 value.reset(unscoped_value);
1312 if (!error.get()) {
1313 // If searching for many elements, make sure we found at least one before
1314 // stopping.
1315 done = find_one ||
1316 (value->GetType() == Value::TYPE_LIST &&
1317 static_cast<ListValue*>(value.get())->GetSize() > 0);
1318 } else if (error->code() != kNoSuchElement) {
1319 return error.release();
1320 } 1198 }
1321 int64 elapsed_time = (base::Time::Now() - start_time).InMilliseconds(); 1199 base::PlatformThread::Sleep(50);
1322 done = done || elapsed_time > implicit_wait_;
1323 if (!done)
1324 base::PlatformThread::Sleep(50); // Prevent a busy loop.
1325 }
1326
1327 if (error.get())
1328 return error.release();
1329
1330 // Parse the results.
1331 const std::string kInvalidElementDictionaryMessage =
1332 "Find element script returned invalid element dictionary: " +
1333 JsonStringify(value.get());
1334 if (value->IsType(Value::TYPE_LIST)) {
1335 ListValue* element_list = static_cast<ListValue*>(value.get());
1336 for (size_t i = 0; i < element_list->GetSize(); ++i) {
1337 DictionaryValue* element_dict = NULL;
1338 if (!element_list->GetDictionary(i, &element_dict)) {
1339 return new Error(
1340 kUnknownError,
1341 "Find element script returned non-dictionary: " +
1342 JsonStringify(element_list));
1343 }
1344
1345 WebElementId element(element_dict);
1346 if (!element.is_valid()) {
1347 return new Error(kUnknownError, kInvalidElementDictionaryMessage);
1348 }
1349 elements->push_back(element);
1350 }
1351 } else if (value->IsType(Value::TYPE_DICTIONARY)) {
1352 DictionaryValue* element_dict =
1353 static_cast<DictionaryValue*>(value.get());
1354 WebElementId element(element_dict);
1355 if (!element.is_valid()) {
1356 return new Error(kUnknownError, kInvalidElementDictionaryMessage);
1357 }
1358 elements->push_back(element);
1359 } else {
1360 return new Error(
1361 kUnknownError,
1362 "Find element script returned unsupported type: " +
1363 JsonStringify(value.get()));
1364 } 1200 }
1365 return NULL; 1201 return NULL;
1366 } 1202 }
1367 1203
1204 Error* Session::ExecuteFindElementScriptAndParse(
1205 const FrameId& frame_id,
1206 const WebElementId& root_element,
1207 const std::string& locator,
1208 const std::string& query,
1209 bool find_one,
1210 std::vector<WebElementId>* elements) {
1211 CHECK(root_element.is_valid());
1212
1213 class FindElementsParser : public ValueParser {
1214 public:
1215 explicit FindElementsParser(std::vector<WebElementId>* elements)
1216 : elements_(elements) { }
1217
1218 virtual ~FindElementsParser() { }
1219
1220 virtual bool Parse(base::Value* value) const OVERRIDE {
1221 if (!value->IsType(Value::TYPE_LIST))
1222 return false;
1223 ListValue* list = static_cast<ListValue*>(value);
1224 for (size_t i = 0; i < list->GetSize(); ++i) {
1225 WebElementId element;
1226 Value* element_value = NULL;
1227 if (!list->Get(i, &element_value))
1228 return false;
1229 if (!SetFromValue(element_value, &element))
1230 return false;
1231 elements_->push_back(element);
1232 }
1233 return true;
1234 }
1235 private:
1236 std::vector<WebElementId>* elements_;
1237 };
1238
1239 class FindElementParser : public ValueParser {
1240 public:
1241 explicit FindElementParser(std::vector<WebElementId>* elements)
1242 : elements_(elements) { }
1243
1244 virtual ~FindElementParser() { }
1245
1246 virtual bool Parse(base::Value* value) const OVERRIDE {
1247 if (value->IsType(Value::TYPE_NULL))
1248 return true;
1249 WebElementId element;
1250 bool set = SetFromValue(value, &element);
1251 if (set)
1252 elements_->push_back(element);
1253 return set;
1254 }
1255 private:
1256 std::vector<WebElementId>* elements_;
1257 };
1258
1259 DictionaryValue locator_dict;
1260 locator_dict.SetString(locator, query);
1261 std::vector<WebElementId> temp_elements;
1262 Error* error = NULL;
1263 if (find_one) {
1264 error = ExecuteScriptAndParse(
1265 frame_id,
1266 atoms::FIND_ELEMENT,
1267 "findElement",
1268 CreateListValueFrom(&locator_dict, root_element),
1269 new FindElementParser(&temp_elements));
1270 } else {
1271 error = ExecuteScriptAndParse(
1272 frame_id,
1273 atoms::FIND_ELEMENTS,
1274 "findElements",
1275 CreateListValueFrom(&locator_dict, root_element),
1276 new FindElementsParser(&temp_elements));
1277 }
1278 if (!error)
1279 elements->swap(temp_elements);
1280 return error;
1281 }
1282
1368 Error* Session::VerifyElementIsClickable( 1283 Error* Session::VerifyElementIsClickable(
1369 const FrameId& frame_id, 1284 const FrameId& frame_id,
1370 const WebElementId& element, 1285 const WebElementId& element,
1371 const gfx::Point& location) { 1286 const Point& location) {
1372 std::string jscript = base::StringPrintf( 1287 class IsElementClickableParser : public ValueParser {
1373 "return (%s).apply(null, arguments);", atoms::IS_ELEMENT_CLICKABLE); 1288 public:
1374 ListValue jscript_args; 1289 IsElementClickableParser(bool* clickable, std::string* message)
1375 jscript_args.Append(element.ToValue()); 1290 : clickable_(clickable), message_(message) { }
1376 DictionaryValue* location_dict = new DictionaryValue(); 1291
1377 location_dict->SetInteger("x", location.x()); 1292 virtual ~IsElementClickableParser() { }
1378 location_dict->SetInteger("y", location.y()); 1293
1379 jscript_args.Append(location_dict); 1294 virtual bool Parse(base::Value* value) const OVERRIDE {
1380 Value* unscoped_value = NULL; 1295 if (!value->IsType(Value::TYPE_DICTIONARY))
1381 Error* error = ExecuteScript(frame_id, jscript, &jscript_args, 1296 return false;
1382 &unscoped_value); 1297 DictionaryValue* dict = static_cast<DictionaryValue*>(value);
1298 dict->GetString("message", message_);
1299 return dict->GetBoolean("clickable", clickable_);
1300 }
1301
1302 private:
1303 bool* clickable_;
1304 std::string* message_;
1305 };
1306
1307 bool clickable;
1308 std::string message;
1309 Error* error = ExecuteScriptAndParse(
1310 frame_id,
1311 atoms::IS_ELEMENT_CLICKABLE,
1312 "isElementClickable",
1313 CreateListValueFrom(element, location),
1314 new IsElementClickableParser(&clickable, &message));
1383 if (error) 1315 if (error)
1384 return error; 1316 return error;
1385 1317
1386 scoped_ptr<Value> value(unscoped_value);
1387 if (!value->IsType(Value::TYPE_DICTIONARY)) {
1388 return new Error(
1389 kUnknownError,
1390 "isElementClickable atom returned non-dictionary type: " +
1391 JsonStringify(value.get()));
1392 }
1393 DictionaryValue* dict = static_cast<DictionaryValue*>(value.get());
1394 bool clickable = false;
1395 if (!dict->GetBoolean("clickable", &clickable)) {
1396 return new Error(
1397 kUnknownError,
1398 "isElementClickable atom returned bad invalid dictionary: " +
1399 JsonStringify(dict));
1400 }
1401 std::string message;
1402 dict->GetString("message", &message);
1403 if (!clickable) { 1318 if (!clickable) {
1404 if (message.empty()) 1319 if (message.empty())
1405 message = "element is not clickable"; 1320 message = "element is not clickable";
1406 return new Error(kUnknownError, message); 1321 return new Error(kUnknownError, message);
1407 } 1322 }
1408 if (message.length()) { 1323 if (message.length()) {
1409 LOG(WARNING) << message; 1324 LOG(WARNING) << message;
1410 } 1325 }
1411 return NULL; 1326 return NULL;
1412 } 1327 }
1413 1328
1414 Error* Session::GetElementRegionInViewHelper( 1329 Error* Session::GetElementRegionInViewHelper(
1415 const FrameId& frame_id, 1330 const FrameId& frame_id,
1416 const WebElementId& element, 1331 const WebElementId& element,
1417 const gfx::Rect& region, 1332 const Rect& region,
1418 bool center, 1333 bool center,
1419 bool verify_clickable_at_middle, 1334 bool verify_clickable_at_middle,
1420 gfx::Point* location) { 1335 Point* location) {
1421 std::string jscript = base::StringPrintf( 1336 Point temp_location;
1422 "return (%s).apply(null, arguments);", atoms::GET_LOCATION_IN_VIEW); 1337 Error* error = ExecuteScriptAndParse(
1423 ListValue jscript_args; 1338 frame_id,
1424 jscript_args.Append(element.ToValue()); 1339 atoms::GET_LOCATION_IN_VIEW,
1425 jscript_args.Append(Value::CreateBooleanValue(center)); 1340 "getLocationInView",
1426 DictionaryValue* elem_offset_dict = new DictionaryValue(); 1341 CreateListValueFrom(element, center, region),
1427 elem_offset_dict->SetInteger("left", region.x()); 1342 CreateDirectValueParser(&temp_location));
1428 elem_offset_dict->SetInteger("top", region.y());
1429 elem_offset_dict->SetInteger("width", region.width());
1430 elem_offset_dict->SetInteger("height", region.height());
1431 jscript_args.Append(elem_offset_dict);
1432 Value* unscoped_value = NULL;
1433 Error* error = ExecuteScript(frame_id, jscript, &jscript_args,
1434 &unscoped_value);
1435 scoped_ptr<Value> value(unscoped_value);
1436 if (error)
1437 return error;
1438
1439 if (!value->IsType(Value::TYPE_DICTIONARY)) {
1440 return new Error(
1441 kUnknownError,
1442 "Location atom returned non-dictionary type: " +
1443 JsonStringify(value.get()));
1444 }
1445 DictionaryValue* loc_dict = static_cast<DictionaryValue*>(value.get());
1446 int x = 0, y = 0;
1447 if (!loc_dict->GetInteger("x", &x) ||
1448 !loc_dict->GetInteger("y", &y)) {
1449 return new Error(
1450 kUnknownError,
1451 "Location atom returned bad coordinate dictionary: " +
1452 JsonStringify(loc_dict));
1453 }
1454 gfx::Point temp_location = gfx::Point(x, y);
1455 1343
1456 if (verify_clickable_at_middle) { 1344 if (verify_clickable_at_middle) {
1457 gfx::Point middle_point = temp_location; 1345 Point middle_point = temp_location;
1458 middle_point.Offset(region.width() / 2, region.height() / 2); 1346 middle_point.Offset(region.width() / 2, region.height() / 2);
1459 error = VerifyElementIsClickable(frame_id, element, middle_point); 1347 error = VerifyElementIsClickable(frame_id, element, middle_point);
1460 if (error) 1348 if (error)
1461 return error; 1349 return error;
1462 } 1350 }
1463 *location = temp_location; 1351 *location = temp_location;
1464 return NULL; 1352 return NULL;
1465 } 1353 }
1466 1354
1467 Error* Session::GetScreenShot(std::string* png) { 1355 Error* Session::GetScreenShot(std::string* png) {
(...skipping 12 matching lines...) Expand all
1480 path, 1368 path,
1481 &error)); 1369 &error));
1482 if (error) 1370 if (error)
1483 return error; 1371 return error;
1484 if (!file_util::ReadFileToString(path, png)) 1372 if (!file_util::ReadFileToString(path, png))
1485 return new Error(kUnknownError, "Could not read screenshot file"); 1373 return new Error(kUnknownError, "Could not read screenshot file");
1486 return NULL; 1374 return NULL;
1487 } 1375 }
1488 1376
1489 Error* Session::GetBrowserConnectionState(bool* online) { 1377 Error* Session::GetBrowserConnectionState(bool* online) {
1490 std::string jscript = base::StringPrintf( 1378 return ExecuteScriptAndParse(
1491 "return (%s).apply(null, arguments);", atoms::IS_ONLINE); 1379 current_target_,
1492 base::ListValue no_args; 1380 atoms::IS_ONLINE,
1493 Value* unscoped_value = NULL; 1381 "isOnline",
1494 Error* error = ExecuteScript(jscript, 1382 new ListValue(),
1495 &no_args, 1383 CreateDirectValueParser(online));
1496 &unscoped_value);
1497 scoped_ptr<base::Value> value(unscoped_value);
1498 if (error)
1499 return error;
1500 if (!value->GetAsBoolean(online))
1501 return new Error(kUnknownError,
1502 "IS_ONLINE script returned non-boolean: " +
1503 JsonStringify(value.get()));
1504 return NULL;
1505 } 1384 }
1506 1385
1507 Error* Session::GetAppCacheStatus(int* status) { 1386 Error* Session::GetAppCacheStatus(int* status) {
1508 std::string jscript = base::StringPrintf( 1387 return ExecuteScriptAndParse(
1509 "return (%s).apply(null, arguments);", atoms::GET_APPCACHE_STATUS); 1388 current_target_,
1510 base::ListValue no_args; 1389 atoms::GET_APPCACHE_STATUS,
1511 Value* unscoped_value = NULL; 1390 "getAppcacheStatus",
1512 Error* error = ExecuteScript(jscript, 1391 new ListValue(),
1513 &no_args, 1392 CreateDirectValueParser(status));
1514 &unscoped_value);
1515 scoped_ptr<base::Value> value(unscoped_value);
1516 if (error)
1517 return error;
1518 if (!value->GetAsInteger(status))
1519 return new Error(kUnknownError,
1520 "GET_APPCACHE_STATUS script returned non-integer: " +
1521 JsonStringify(value.get()));
1522 return NULL;
1523 } 1393 }
1524 1394
1525 } // namespace webdriver 1395 } // namespace webdriver
OLDNEW
« no previous file with comments | « chrome/test/webdriver/session.h ('k') | chrome/test/webdriver/utility_functions.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698