| OLD | NEW |
| 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 Loading... |
| 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 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 108 &args_as_json); | 106 &args_as_json); |
| 109 | 107 |
| 110 // Every injected script is fed through the executeScript atom. This atom | 108 // Every injected script is fed through the executeScript atom. This atom |
| 111 // will catch any errors that are thrown and convert them to the | 109 // will catch any errors that are thrown and convert them to the |
| 112 // appropriate JSON structure. | 110 // appropriate JSON structure. |
| 113 std::string jscript = base::StringPrintf( | 111 std::string jscript = base::StringPrintf( |
| 114 "window.domAutomationController.send((%s).apply(null," | 112 "window.domAutomationController.send((%s).apply(null," |
| 115 "[function(){%s\n},%s,true]));", | 113 "[function(){%s\n},%s,true]));", |
| 116 atoms::EXECUTE_SCRIPT, script.c_str(), args_as_json.c_str()); | 114 atoms::EXECUTE_SCRIPT, script.c_str(), args_as_json.c_str()); |
| 117 | 115 |
| 118 return ExecuteScriptAndParseResponse(frame_id, jscript, value); | 116 return ExecuteScriptAndParseValue(frame_id, jscript, value); |
| 119 } | 117 } |
| 120 | 118 |
| 121 Error* Session::ExecuteScript(const std::string& script, | 119 Error* Session::ExecuteScript(const std::string& script, |
| 122 const ListValue* const args, | 120 const ListValue* const args, |
| 123 Value** value) { | 121 Value** value) { |
| 124 return ExecuteScript(current_target_, script, args, value); | 122 return ExecuteScript(current_target_, script, args, value); |
| 125 } | 123 } |
| 126 | 124 |
| 125 Error* Session::ExecuteScriptAndParse(const FrameId& frame_id, |
| 126 const std::string& anonymous_func_script, |
| 127 const std::string& script_name, |
| 128 const ListValue* args, |
| 129 const ValueParser* parser) { |
| 130 scoped_ptr<const ListValue> scoped_args(args); |
| 131 scoped_ptr<const ValueParser> scoped_parser(parser); |
| 132 std::string called_script = base::StringPrintf( |
| 133 "return (%s).apply(null, arguments);", anonymous_func_script.c_str()); |
| 134 Value* unscoped_value = NULL; |
| 135 Error* error = ExecuteScript(frame_id, called_script, args, &unscoped_value); |
| 136 if (error) { |
| 137 error->AddDetails(script_name + " execution failed"); |
| 138 return error; |
| 139 } |
| 140 |
| 141 scoped_ptr<Value> value(unscoped_value); |
| 142 std::string error_msg; |
| 143 if (!parser->Parse(value.get())) { |
| 144 error_msg = base::StringPrintf("%s returned invalid value: %s", |
| 145 script_name.c_str(), JsonStringify(value.get()).c_str()); |
| 146 return new Error(kUnknownError, error_msg); |
| 147 } |
| 148 return NULL; |
| 149 } |
| 150 |
| 127 Error* Session::ExecuteAsyncScript(const FrameId& frame_id, | 151 Error* Session::ExecuteAsyncScript(const FrameId& frame_id, |
| 128 const std::string& script, | 152 const std::string& script, |
| 129 const ListValue* const args, | 153 const ListValue* const args, |
| 130 Value** value) { | 154 Value** value) { |
| 131 std::string args_as_json; | 155 std::string args_as_json; |
| 132 base::JSONWriter::Write(static_cast<const Value* const>(args), | 156 base::JSONWriter::Write(static_cast<const Value* const>(args), |
| 133 /*pretty_print=*/false, | 157 /*pretty_print=*/false, |
| 134 &args_as_json); | 158 &args_as_json); |
| 135 | 159 |
| 136 int timeout_ms = async_script_timeout(); | 160 int timeout_ms = async_script_timeout(); |
| 137 | 161 |
| 138 // Every injected script is fed through the executeScript atom. This atom | 162 // Every injected script is fed through the executeScript atom. This atom |
| 139 // will catch any errors that are thrown and convert them to the | 163 // will catch any errors that are thrown and convert them to the |
| 140 // appropriate JSON structure. | 164 // appropriate JSON structure. |
| 141 std::string jscript = base::StringPrintf( | 165 std::string jscript = base::StringPrintf( |
| 142 "(%s).apply(null, [function(){%s},%s,%d,%s,true]);", | 166 "(%s).apply(null, [function(){%s},%s,%d,%s,true]);", |
| 143 atoms::EXECUTE_ASYNC_SCRIPT, | 167 atoms::EXECUTE_ASYNC_SCRIPT, |
| 144 script.c_str(), | 168 script.c_str(), |
| 145 args_as_json.c_str(), | 169 args_as_json.c_str(), |
| 146 timeout_ms, | 170 timeout_ms, |
| 147 "function(result) {window.domAutomationController.send(result);}"); | 171 "function(result) {window.domAutomationController.send(result);}"); |
| 148 | 172 |
| 149 return ExecuteScriptAndParseResponse(frame_id, jscript, value); | 173 return ExecuteScriptAndParseValue(frame_id, jscript, value); |
| 150 } | 174 } |
| 151 | 175 |
| 152 Error* Session::SendKeys(const WebElementId& element, const string16& keys) { | 176 Error* Session::SendKeys(const WebElementId& element, const string16& keys) { |
| 153 bool is_displayed = false; | 177 bool is_displayed = false; |
| 154 Error* error = IsElementDisplayed( | 178 Error* error = IsElementDisplayed( |
| 155 current_target_, element, true /* ignore_opacity */, &is_displayed); | 179 current_target_, element, true /* ignore_opacity */, &is_displayed); |
| 156 if (error) | 180 if (error) |
| 157 return error; | 181 return error; |
| 158 if (!is_displayed) | 182 if (!is_displayed) |
| 159 return new Error(kElementNotVisible); | 183 return new Error(kElementNotVisible); |
| 160 | 184 |
| 161 bool is_enabled = false; | 185 bool is_enabled = false; |
| 162 error = IsElementEnabled(current_target_, element, &is_enabled); | 186 error = IsElementEnabled(current_target_, element, &is_enabled); |
| 163 if (error) | 187 if (error) |
| 164 return error; | 188 return error; |
| 165 if (!is_enabled) | 189 if (!is_enabled) |
| 166 return new Error(kInvalidElementState); | 190 return new Error(kInvalidElementState); |
| 167 | 191 |
| 168 ListValue args; | |
| 169 args.Append(element.ToValue()); | |
| 170 // Focus the target element in order to send keys to it. | 192 // Focus the target element in order to send keys to it. |
| 171 // First, the currently active element is blurred, if it is different from | 193 // First, the currently active element is blurred, if it is different from |
| 172 // the target element. We do not want to blur an element unnecessarily, | 194 // the target element. We do not want to blur an element unnecessarily, |
| 173 // because this may cause us to lose the current cursor position in the | 195 // because this may cause us to lose the current cursor position in the |
| 174 // element. | 196 // element. |
| 175 // Secondly, we focus the target element. | 197 // Secondly, we focus the target element. |
| 176 // Thirdly, if the target element is newly focused and is a text input, we | 198 // Thirdly, if the target element is newly focused and is a text input, we |
| 177 // set the cursor position at the end. | 199 // set the cursor position at the end. |
| 178 // Fourthly, we check if the new active element is the target element. If not, | 200 // Fourthly, we check if the new active element is the target element. If not, |
| 179 // we throw an error. | 201 // we throw an error. |
| 180 // Additional notes: | 202 // Additional notes: |
| 181 // - |document.activeElement| is the currently focused element, or body if | 203 // - |document.activeElement| is the currently focused element, or body if |
| 182 // no element is focused | 204 // no element is focused |
| 183 // - Even if |document.hasFocus()| returns true and the active element is | 205 // - Even if |document.hasFocus()| returns true and the active element is |
| 184 // the body, sometimes we still need to focus the body element for send | 206 // the body, sometimes we still need to focus the body element for send |
| 185 // keys to work. Not sure why | 207 // keys to work. Not sure why |
| 186 // - You cannot focus a descendant of a content editable node | 208 // - You cannot focus a descendant of a content editable node |
| 187 // TODO(jleyba): Update this to use the correct atom. | 209 // TODO(jleyba): Update this to use the correct atom. |
| 188 const char* kFocusScript = | 210 const char* kFocusScript = |
| 189 "var elem = arguments[0];" | 211 "function(elem) {" |
| 190 "var doc = elem.ownerDocument || elem;" | 212 " var doc = elem.ownerDocument || elem;" |
| 191 "var prevActiveElem = doc.activeElement;" | 213 " var prevActiveElem = doc.activeElement;" |
| 192 "if (elem != prevActiveElem && prevActiveElem)" | 214 " if (elem != prevActiveElem && prevActiveElem)" |
| 193 " prevActiveElem.blur();" | 215 " prevActiveElem.blur();" |
| 194 "elem.focus();" | 216 " elem.focus();" |
| 195 "if (elem != prevActiveElem && elem.value && elem.value.length &&" | 217 " if (elem != prevActiveElem && elem.value && elem.value.length &&" |
| 196 " elem.setSelectionRange) {" | 218 " elem.setSelectionRange) {" |
| 197 " elem.setSelectionRange(elem.value.length, elem.value.length);" | 219 " elem.setSelectionRange(elem.value.length, elem.value.length);" |
| 198 "}" | 220 " }" |
| 199 "if (elem != doc.activeElement)" | 221 " if (elem != doc.activeElement)" |
| 200 " throw new Error('Failed to send keys because cannot focus element.');"; | 222 " throw new Error('Failed to send keys because cannot focus element');" |
| 201 Value* unscoped_result = NULL; | 223 "}"; |
| 202 error = ExecuteScript(kFocusScript, &args, &unscoped_result); | 224 error = ExecuteScriptAndParse(current_target_, |
| 225 kFocusScript, |
| 226 "focusElement", |
| 227 CreateListValueFrom(element), |
| 228 CreateDirectValueParser(kSkipParsing)); |
| 203 if (error) | 229 if (error) |
| 204 return error; | 230 return error; |
| 205 | 231 |
| 206 error = NULL; | |
| 207 RunSessionTask(NewRunnableMethod( | 232 RunSessionTask(NewRunnableMethod( |
| 208 this, | 233 this, |
| 209 &Session::SendKeysOnSessionThread, | 234 &Session::SendKeysOnSessionThread, |
| 210 keys, | 235 keys, |
| 211 &error)); | 236 &error)); |
| 212 return error; | 237 return error; |
| 213 } | 238 } |
| 214 | 239 |
| 215 Error* Session::DragAndDropFilePaths( | 240 Error* Session::DragAndDropFilePaths( |
| 216 const gfx::Point& location, | 241 const Point& location, |
| 217 const std::vector<FilePath::StringType>& paths) { | 242 const std::vector<FilePath::StringType>& paths) { |
| 218 Error* error = NULL; | 243 Error* error = NULL; |
| 219 RunSessionTask(NewRunnableMethod( | 244 RunSessionTask(NewRunnableMethod( |
| 220 automation_.get(), | 245 automation_.get(), |
| 221 &Automation::DragAndDropFilePaths, | 246 &Automation::DragAndDropFilePaths, |
| 222 current_target_.window_id, | 247 current_target_.window_id, |
| 223 location, | 248 location, |
| 224 paths, | 249 paths, |
| 225 &error)); | 250 &error)); |
| 226 return error; | 251 return error; |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 261 Error* error = NULL; | 286 Error* error = NULL; |
| 262 RunSessionTask(NewRunnableMethod( | 287 RunSessionTask(NewRunnableMethod( |
| 263 automation_.get(), | 288 automation_.get(), |
| 264 &Automation::Reload, | 289 &Automation::Reload, |
| 265 current_target_.window_id, | 290 current_target_.window_id, |
| 266 &error)); | 291 &error)); |
| 267 return error; | 292 return error; |
| 268 } | 293 } |
| 269 | 294 |
| 270 Error* Session::GetURL(std::string* url) { | 295 Error* Session::GetURL(std::string* url) { |
| 271 ListValue no_args; | 296 return ExecuteScriptAndParse(current_target_, |
| 272 Value* unscoped_value = NULL; | 297 "function() { return document.URL }", |
| 273 Error* error = ExecuteScript(current_target_, | 298 "getUrl", |
| 274 "return document.URL;", | 299 new ListValue(), |
| 275 &no_args, | 300 CreateDirectValueParser(url)); |
| 276 &unscoped_value); | |
| 277 scoped_ptr<Value> value(unscoped_value); | |
| 278 if (error) | |
| 279 return error; | |
| 280 if (!value->GetAsString(url)) | |
| 281 return new Error(kUnknownError, "GetURL Script returned non-string: " + | |
| 282 JsonStringify(value.get())); | |
| 283 return NULL; | |
| 284 } | 301 } |
| 285 | 302 |
| 286 Error* Session::GetTitle(std::string* tab_title) { | 303 Error* Session::GetTitle(std::string* tab_title) { |
| 287 std::string script = | 304 const char* kGetTitleScript = |
| 288 "if (document.title)" | 305 "function() {" |
| 289 " return document.title;" | 306 " if (document.title)" |
| 290 "else" | 307 " return document.title;" |
| 291 " return document.URL;"; | 308 " else" |
| 292 | 309 " return document.URL;" |
| 293 ListValue no_args; | 310 "}"; |
| 294 Value* unscoped_value = NULL; | 311 return ExecuteScriptAndParse(current_target_, |
| 295 Error* error = ExecuteScript(current_target_, | 312 kGetTitleScript, |
| 296 script, | 313 "getTitle", |
| 297 &no_args, | 314 new ListValue(), |
| 298 &unscoped_value); | 315 CreateDirectValueParser(tab_title)); |
| 299 scoped_ptr<Value> value(unscoped_value); | |
| 300 if (error) | |
| 301 return error; | |
| 302 if (!value->GetAsString(tab_title)) | |
| 303 return new Error(kUnknownError, "GetTitle script returned non-string: " + | |
| 304 JsonStringify(value.get())); | |
| 305 return NULL; | |
| 306 } | 316 } |
| 307 | 317 |
| 308 Error* Session::MouseMoveAndClick(const gfx::Point& location, | 318 Error* Session::MouseMoveAndClick(const Point& location, |
| 309 automation::MouseButton button) { | 319 automation::MouseButton button) { |
| 310 Error* error = NULL; | 320 Error* error = NULL; |
| 311 RunSessionTask(NewRunnableMethod( | 321 RunSessionTask(NewRunnableMethod( |
| 312 automation_.get(), | 322 automation_.get(), |
| 313 &Automation::MouseClick, | 323 &Automation::MouseClick, |
| 314 current_target_.window_id, | 324 current_target_.window_id, |
| 315 location, | 325 location, |
| 316 button, | 326 button, |
| 317 &error)); | 327 &error)); |
| 318 if (!error) | 328 if (!error) |
| 319 mouse_position_ = location; | 329 mouse_position_ = location; |
| 320 return error; | 330 return error; |
| 321 } | 331 } |
| 322 | 332 |
| 323 Error* Session::MouseMove(const gfx::Point& location) { | 333 Error* Session::MouseMove(const Point& location) { |
| 324 Error* error = NULL; | 334 Error* error = NULL; |
| 325 RunSessionTask(NewRunnableMethod( | 335 RunSessionTask(NewRunnableMethod( |
| 326 automation_.get(), | 336 automation_.get(), |
| 327 &Automation::MouseMove, | 337 &Automation::MouseMove, |
| 328 current_target_.window_id, | 338 current_target_.window_id, |
| 329 location, | 339 location, |
| 330 &error)); | 340 &error)); |
| 331 if (!error) | 341 if (!error) |
| 332 mouse_position_ = location; | 342 mouse_position_ = location; |
| 333 return error; | 343 return error; |
| 334 } | 344 } |
| 335 | 345 |
| 336 Error* Session::MouseDrag(const gfx::Point& start, | 346 Error* Session::MouseDrag(const Point& start, |
| 337 const gfx::Point& end) { | 347 const Point& end) { |
| 338 Error* error = NULL; | 348 Error* error = NULL; |
| 339 RunSessionTask(NewRunnableMethod( | 349 RunSessionTask(NewRunnableMethod( |
| 340 automation_.get(), | 350 automation_.get(), |
| 341 &Automation::MouseDrag, | 351 &Automation::MouseDrag, |
| 342 current_target_.window_id, | 352 current_target_.window_id, |
| 343 start, | 353 start, |
| 344 end, | 354 end, |
| 345 &error)); | 355 &error)); |
| 346 if (!error) | 356 if (!error) |
| 347 mouse_position_ = end; | 357 mouse_position_ = end; |
| (...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 448 switch_to_id = name_no; | 458 switch_to_id = name_no; |
| 449 } | 459 } |
| 450 | 460 |
| 451 if (!switch_to_id) { | 461 if (!switch_to_id) { |
| 452 std::vector<int> window_ids; | 462 std::vector<int> window_ids; |
| 453 Error* error = GetWindowIds(&window_ids); | 463 Error* error = GetWindowIds(&window_ids); |
| 454 if (error) | 464 if (error) |
| 455 return error; | 465 return error; |
| 456 // See if any of the window names match |name|. | 466 // See if any of the window names match |name|. |
| 457 for (size_t i = 0; i < window_ids.size(); ++i) { | 467 for (size_t i = 0; i < window_ids.size(); ++i) { |
| 458 ListValue empty_list; | |
| 459 Value* unscoped_name_value = NULL; | |
| 460 std::string window_name; | 468 std::string window_name; |
| 461 Error* error = ExecuteScript(FrameId(window_ids[i], FramePath()), | 469 Error* error = ExecuteScriptAndParse( |
| 462 "return window.name;", | 470 FrameId(window_ids[i], FramePath()), |
| 463 &empty_list, | 471 "function() { return window.name; }", |
| 464 &unscoped_name_value); | 472 "getWindowName", |
| 473 new ListValue(), |
| 474 CreateDirectValueParser(&window_name)); |
| 465 if (error) | 475 if (error) |
| 466 return error; | 476 return error; |
| 467 scoped_ptr<Value> name_value(unscoped_name_value); | 477 if (name == window_name) { |
| 468 if (name_value->GetAsString(&window_name) && | |
| 469 name == window_name) { | |
| 470 switch_to_id = window_ids[i]; | 478 switch_to_id = window_ids[i]; |
| 471 break; | 479 break; |
| 472 } | 480 } |
| 473 } | 481 } |
| 474 } | 482 } |
| 475 | 483 |
| 476 if (!switch_to_id) | 484 if (!switch_to_id) |
| 477 return new Error(kNoSuchWindow); | 485 return new Error(kNoSuchWindow); |
| 478 frame_elements_.clear(); | 486 frame_elements_.clear(); |
| 479 current_target_ = FrameId(switch_to_id, FramePath()); | 487 current_target_ = FrameId(switch_to_id, FramePath()); |
| 480 return NULL; | 488 return NULL; |
| 481 } | 489 } |
| 482 | 490 |
| 483 Error* Session::SwitchToFrameWithNameOrId(const std::string& name_or_id) { | 491 Error* Session::SwitchToFrameWithNameOrId(const std::string& name_or_id) { |
| 484 std::string script = | 492 std::string script = |
| 485 "var arg = arguments[0];" | 493 "function(arg) {" |
| 486 "var xpath = '(/html/body//iframe|/html/frameset/frame)';" | 494 " var xpath = '(/html/body//iframe|/html/frameset/frame)';" |
| 487 "var sub = function(s) { return s.replace(/\\$/g, arg); };" | 495 " var sub = function(s) { return s.replace(/\\$/g, arg); };" |
| 488 "xpath += sub('[@name=\"$\" or @id=\"$\"]');" | 496 " xpath += sub('[@name=\"$\" or @id=\"$\"]');" |
| 489 "var frame = document.evaluate(xpath, document, null, " | 497 " var frame = document.evaluate(xpath, document, null, " |
| 490 " XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;" | 498 " XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;" |
| 491 "if (!frame) { return null; }" | 499 " if (!frame) { return null; }" |
| 492 "xpath = frame.tagName == 'IFRAME' ? '/html/body//iframe'" | 500 " xpath = frame.tagName == 'IFRAME' ? '/html/body//iframe'" |
| 493 " : '/html/frameset/frame';" | 501 " : '/html/frameset/frame';" |
| 494 "frame_xpath = xpath + " | 502 " frame_xpath = xpath + " |
| 495 " sub('[@' + (frame.id == arg ? 'id' : 'name') + '=\"$\"]');" | 503 " sub('[@' + (frame.id == arg ? 'id' : 'name') + '=\"$\"]');" |
| 496 "return [frame, frame_xpath];"; | 504 " return [frame, frame_xpath];" |
| 497 ListValue args; | 505 "}"; |
| 498 args.Append(new StringValue(name_or_id)); | 506 return SwitchToFrameWithJavaScriptLocatedFrame( |
| 499 return SwitchToFrameWithJavaScriptLocatedFrame(script, &args); | 507 script, CreateListValueFrom(name_or_id)); |
| 500 } | 508 } |
| 501 | 509 |
| 502 Error* Session::SwitchToFrameWithIndex(int index) { | 510 Error* Session::SwitchToFrameWithIndex(int index) { |
| 503 // We cannot simply index into window.frames because we need to know the | 511 // We cannot simply index into window.frames because we need to know the |
| 504 // tagName of the frameElement. If child frame N is from another domain, then | 512 // tagName of the frameElement. If child frame N is from another domain, then |
| 505 // the following will run afoul of the same origin policy: | 513 // the following will run afoul of the same origin policy: |
| 506 // window.frames[N].frameElement; | 514 // window.frames[N].frameElement; |
| 507 // Instead of indexing window.frames, we use a an XPath expression to index | 515 // Instead of indexing window.frames, we use a an XPath expression to index |
| 508 // into the list of all IFRAME and FRAME elements on the page - if we find | 516 // into the list of all IFRAME and FRAME elements on the page - if we find |
| 509 // something, then that XPath expression can be used as the new frame's XPath. | 517 // something, then that XPath expression can be used as the new frame's XPath. |
| 510 std::string script = | 518 std::string script = |
| 511 "var index = '[' + (arguments[0] + 1) + ']';" | 519 "function(index) {" |
| 512 "var xpath = '(/html/body//iframe|/html/frameset/frame)' + " | 520 " var xpathIndex = '[' + (index + 1) + ']';" |
| 513 " index;" | 521 " var xpath = '(/html/body//iframe|/html/frameset/frame)' + " |
| 514 "console.info('searching for frame by xpath: ' + xpath);" | 522 " xpathIndex;" |
| 515 "var frame = document.evaluate(xpath, document, null, " | 523 " var frame = document.evaluate(xpath, document, null, " |
| 516 "XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;" | 524 " XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;" |
| 517 "console.info(frame == null ? 'found nothing' : frame);" | 525 " if (!frame) { return null; }" |
| 518 "if (!frame) { return null; }" | 526 " frame_xpath = ((frame.tagName == 'IFRAME' ? " |
| 519 "frame_xpath = ((frame.tagName == 'IFRAME' ? " | 527 " '(/html/body//iframe)' : '/html/frameset/frame') + xpathIndex);" |
| 520 " '(/html/body//iframe)' : '/html/frameset/frame') + index);" | 528 " return [frame, frame_xpath];" |
| 521 "return [frame, frame_xpath];"; | 529 "}"; |
| 522 ListValue args; | 530 return SwitchToFrameWithJavaScriptLocatedFrame( |
| 523 args.Append(Value::CreateIntegerValue(index)); | 531 script, CreateListValueFrom(index)); |
| 524 return SwitchToFrameWithJavaScriptLocatedFrame(script, &args); | |
| 525 } | 532 } |
| 526 | 533 |
| 527 Error* Session::SwitchToFrameWithElement(const WebElementId& element) { | 534 Error* Session::SwitchToFrameWithElement(const WebElementId& element) { |
| 528 // TODO(jleyba): Extract this, and the other frame switch methods to an atom. | 535 // TODO(jleyba): Extract this, and the other frame switch methods to an atom. |
| 529 std::string script = | 536 std::string script = |
| 530 "var element = arguments[0];" | 537 "function(elem) {" |
| 531 "console.info('Attempting to switch to ' + element);" | 538 " if (elem.nodeType != 1 || !/^i?frame$/i.test(elem.tagName)) {" |
| 532 "if (element.nodeType != 1 || !/^i?frame$/i.test(element.tagName)) {" | 539 " console.error('Element is not a frame');" |
| 533 " console.info('Element is not a frame: ' + element + " | 540 " return null;" |
| 534 "' {nodeType:' + element.nodeType + ',tagName:' + element.tagName + '}');" | 541 " }" |
| 542 " for (var i = 0; i < window.frames.length; i++) {" |
| 543 " if (elem.contentWindow == window.frames[i]) {" |
| 544 " return [elem, '(//iframe|//frame)[' + (i + 1) + ']'];" |
| 545 " }" |
| 546 " }" |
| 547 " console.info('Frame is not connected to this DOM tree');" |
| 535 " return null;" | 548 " return null;" |
| 536 "}" | 549 "}"; |
| 537 "for (var i = 0; i < window.frames.length; i++) {" | 550 return SwitchToFrameWithJavaScriptLocatedFrame( |
| 538 " if (element.contentWindow == window.frames[i]) {" | 551 script, CreateListValueFrom(element)); |
| 539 " return [element, '(//iframe|//frame)[' + (i + 1) + ']'];" | |
| 540 " }" | |
| 541 "}" | |
| 542 "console.info('Frame is not connected to this DOM tree');" | |
| 543 "return null;"; | |
| 544 | |
| 545 ListValue args; | |
| 546 args.Append(element.ToValue()); | |
| 547 return SwitchToFrameWithJavaScriptLocatedFrame(script, &args); | |
| 548 } | 552 } |
| 549 | 553 |
| 550 void Session::SwitchToTopFrame() { | 554 void Session::SwitchToTopFrame() { |
| 551 frame_elements_.clear(); | 555 frame_elements_.clear(); |
| 552 current_target_.frame_path = FramePath(); | 556 current_target_.frame_path = FramePath(); |
| 553 } | 557 } |
| 554 | 558 |
| 555 Error* Session::SwitchToTopFrameIfCurrentFrameInvalid() { | 559 Error* Session::SwitchToTopFrameIfCurrentFrameInvalid() { |
| 556 std::vector<std::string> components; | 560 std::vector<std::string> components; |
| 557 current_target_.frame_path.GetComponents(&components); | 561 current_target_.frame_path.GetComponents(&components); |
| 558 if (frame_elements_.size() != components.size()) { | 562 if (frame_elements_.size() != components.size()) { |
| 559 return new Error(kUnknownError, | 563 return new Error(kUnknownError, |
| 560 "Frame element vector out of sync with frame path"); | 564 "Frame element vector out of sync with frame path"); |
| 561 } | 565 } |
| 562 FramePath frame_path; | 566 FramePath frame_path; |
| 563 // Start from the root path and check that each frame element that makes | 567 // Start from the root path and check that each frame element that makes |
| 564 // up the current frame target is valid by executing an empty script. | 568 // up the current frame target is valid by executing an empty script. |
| 565 // This code should not execute script in any frame before making sure the | 569 // This code should not execute script in any frame before making sure the |
| 566 // frame element is valid, otherwise the automation hangs until a timeout. | 570 // frame element is valid, otherwise the automation hangs until a timeout. |
| 567 for (size_t i = 0; i < frame_elements_.size(); ++i) { | 571 for (size_t i = 0; i < frame_elements_.size(); ++i) { |
| 568 FrameId frame_id(current_target_.window_id, frame_path); | 572 FrameId frame_id(current_target_.window_id, frame_path); |
| 569 ListValue args; | 573 scoped_ptr<Error> error(ExecuteScriptAndParse( |
| 570 args.Append(frame_elements_[i].ToValue()); | 574 frame_id, |
| 571 Value* unscoped_value = NULL; | 575 "function(){ }", |
| 572 scoped_ptr<Error> error(ExecuteScript( | 576 "emptyScript", |
| 573 frame_id, "", &args, &unscoped_value)); | 577 CreateListValueFrom(frame_elements_[i]), |
| 574 | 578 CreateDirectValueParser(kSkipParsing))); |
| 575 scoped_ptr<Value> value(unscoped_value); | |
| 576 if (error.get() && error->code() == kStaleElementReference) { | 579 if (error.get() && error->code() == kStaleElementReference) { |
| 577 SwitchToTopFrame(); | 580 SwitchToTopFrame(); |
| 578 } else if (error.get()) { | 581 } else if (error.get()) { |
| 579 return error.release(); | 582 return error.release(); |
| 580 } | 583 } |
| 581 frame_path = frame_path.Append(components[i]); | 584 frame_path = frame_path.Append(components[i]); |
| 582 } | 585 } |
| 583 return NULL; | 586 return NULL; |
| 584 } | 587 } |
| 585 | 588 |
| (...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 698 const WebElementId& root_element, | 701 const WebElementId& root_element, |
| 699 const std::string& locator, | 702 const std::string& locator, |
| 700 const std::string& query, | 703 const std::string& query, |
| 701 std::vector<WebElementId>* elements) { | 704 std::vector<WebElementId>* elements) { |
| 702 return FindElementsHelper( | 705 return FindElementsHelper( |
| 703 frame_id, root_element, locator, query, false, elements); | 706 frame_id, root_element, locator, query, false, elements); |
| 704 } | 707 } |
| 705 | 708 |
| 706 Error* Session::GetElementLocationInView( | 709 Error* Session::GetElementLocationInView( |
| 707 const WebElementId& element, | 710 const WebElementId& element, |
| 708 gfx::Point* location) { | 711 Point* location) { |
| 709 gfx::Size size; | 712 Size size; |
| 710 Error* error = GetElementSize(current_target_, element, &size); | 713 Error* error = GetElementSize(current_target_, element, &size); |
| 711 if (error) | 714 if (error) |
| 712 return error; | 715 return error; |
| 713 return GetElementRegionInView( | 716 return GetElementRegionInView( |
| 714 element, gfx::Rect(gfx::Point(0, 0), size), | 717 element, Rect(Point(0, 0), size), false /* center */, location); |
| 715 false /* center */, location); | |
| 716 } | 718 } |
| 717 | 719 |
| 718 Error* Session::GetElementRegionInView( | 720 Error* Session::GetElementRegionInView( |
| 719 const WebElementId& element, | 721 const WebElementId& element, |
| 720 const gfx::Rect& region, | 722 const Rect& region, |
| 721 bool center, | 723 bool center, |
| 722 gfx::Point* location) { | 724 Point* location) { |
| 723 CHECK(element.is_valid()); | 725 CHECK(element.is_valid()); |
| 724 | 726 |
| 725 gfx::Point region_offset = region.origin(); | 727 Point region_offset = region.origin(); |
| 726 gfx::Size region_size = region.size(); | 728 Size region_size = region.size(); |
| 727 Error* error = GetElementRegionInViewHelper( | 729 Error* error = GetElementRegionInViewHelper( |
| 728 current_target_, element, region, center, ®ion_offset); | 730 current_target_, element, region, center, ®ion_offset); |
| 729 if (error) | 731 if (error) |
| 730 return error; | 732 return error; |
| 731 | 733 |
| 732 for (FramePath frame_path = current_target_.frame_path; | 734 for (FramePath frame_path = current_target_.frame_path; |
| 733 frame_path.IsSubframe(); | 735 frame_path.IsSubframe(); |
| 734 frame_path = frame_path.Parent()) { | 736 frame_path = frame_path.Parent()) { |
| 735 // Find the frame element for the current frame path. | 737 // Find the frame element for the current frame path. |
| 736 FrameId frame_id(current_target_.window_id, frame_path.Parent()); | 738 FrameId frame_id(current_target_.window_id, frame_path.Parent()); |
| (...skipping 11 matching lines...) Expand all Loading... |
| 748 } | 750 } |
| 749 // Modify |region_offset| by the frame's border. | 751 // Modify |region_offset| by the frame's border. |
| 750 int border_left, border_top; | 752 int border_left, border_top; |
| 751 error = GetElementBorder( | 753 error = GetElementBorder( |
| 752 frame_id, frame_element, &border_left, &border_top); | 754 frame_id, frame_element, &border_left, &border_top); |
| 753 if (error) | 755 if (error) |
| 754 return error; | 756 return error; |
| 755 region_offset.Offset(border_left, border_top); | 757 region_offset.Offset(border_left, border_top); |
| 756 | 758 |
| 757 error = GetElementRegionInViewHelper( | 759 error = GetElementRegionInViewHelper( |
| 758 frame_id, frame_element, gfx::Rect(region_offset, region_size), | 760 frame_id, frame_element, Rect(region_offset, region_size), |
| 759 center, ®ion_offset); | 761 center, ®ion_offset); |
| 760 if (error) | 762 if (error) |
| 761 return error; | 763 return error; |
| 762 } | 764 } |
| 763 *location = region_offset; | 765 *location = region_offset; |
| 764 return NULL; | 766 return NULL; |
| 765 } | 767 } |
| 766 | 768 |
| 767 Error* Session::GetElementSize(const FrameId& frame_id, | 769 Error* Session::GetElementSize(const FrameId& frame_id, |
| 768 const WebElementId& element, | 770 const WebElementId& element, |
| 769 gfx::Size* size) { | 771 Size* size) { |
| 770 std::string script = base::StringPrintf( | 772 return ExecuteScriptAndParse(frame_id, |
| 771 "return (%s).apply(null, arguments);", atoms::GET_SIZE); | 773 atoms::GET_SIZE, |
| 772 ListValue args; | 774 "getSize", |
| 773 args.Append(element.ToValue()); | 775 CreateListValueFrom(element), |
| 774 | 776 CreateDirectValueParser(size)); |
| 775 Value* unscoped_result = NULL; | |
| 776 Error* error = ExecuteScript(frame_id, script, &args, &unscoped_result); | |
| 777 scoped_ptr<Value> result(unscoped_result); | |
| 778 if (error) | |
| 779 return error; | |
| 780 if (!result->IsType(Value::TYPE_DICTIONARY)) { | |
| 781 return new Error(kUnknownError, "GetSize atom returned non-dict type: " + | |
| 782 JsonStringify(result.get())); | |
| 783 } | |
| 784 DictionaryValue* dict = static_cast<DictionaryValue*>(result.get()); | |
| 785 int width, height; | |
| 786 if (!dict->GetInteger("width", &width) || | |
| 787 !dict->GetInteger("height", &height)) { | |
| 788 return new Error(kUnknownError, "GetSize atom returned invalid dict: " + | |
| 789 JsonStringify(dict)); | |
| 790 } | |
| 791 *size = gfx::Size(width, height); | |
| 792 return NULL; | |
| 793 } | 777 } |
| 794 | 778 |
| 795 Error* Session::GetElementFirstClientRect(const FrameId& frame_id, | 779 Error* Session::GetElementFirstClientRect(const FrameId& frame_id, |
| 796 const WebElementId& element, | 780 const WebElementId& element, |
| 797 gfx::Rect* rect) { | 781 Rect* rect) { |
| 798 std::string script = base::StringPrintf( | 782 return ExecuteScriptAndParse(frame_id, |
| 799 "return (%s).apply(null, arguments);", atoms::GET_FIRST_CLIENT_RECT); | 783 atoms::GET_FIRST_CLIENT_RECT, |
| 800 ListValue args; | 784 "getFirstClientRect", |
| 801 args.Append(element.ToValue()); | 785 CreateListValueFrom(element), |
| 802 | 786 CreateDirectValueParser(rect)); |
| 803 Value* unscoped_result = NULL; | |
| 804 Error* error = ExecuteScript(frame_id, script, &args, &unscoped_result); | |
| 805 scoped_ptr<Value> result(unscoped_result); | |
| 806 if (error) | |
| 807 return error; | |
| 808 if (!result->IsType(Value::TYPE_DICTIONARY)) { | |
| 809 return new Error( | |
| 810 kUnknownError, | |
| 811 "GetFirstClientRect atom returned non-dict type: " + | |
| 812 JsonStringify(result.get())); | |
| 813 } | |
| 814 DictionaryValue* dict = static_cast<DictionaryValue*>(result.get()); | |
| 815 // TODO(kkania): Convert the atom to return integers. | |
| 816 double left, top, width, height; | |
| 817 if (!dict->GetDouble("left", &left) || | |
| 818 !dict->GetDouble("top", &top) || | |
| 819 !dict->GetDouble("width", &width) || | |
| 820 !dict->GetDouble("height", &height)) { | |
| 821 return new Error( | |
| 822 kUnknownError, | |
| 823 "GetFirstClientRect atom returned invalid dict: " + | |
| 824 JsonStringify(dict)); | |
| 825 } | |
| 826 *rect = gfx::Rect(static_cast<int>(left), static_cast<int>(top), | |
| 827 static_cast<int>(width), static_cast<int>(height)); | |
| 828 return NULL; | |
| 829 } | 787 } |
| 830 | 788 |
| 831 Error* Session::GetElementEffectiveStyle( | 789 Error* Session::GetElementEffectiveStyle( |
| 832 const FrameId& frame_id, | 790 const FrameId& frame_id, |
| 833 const WebElementId& element, | 791 const WebElementId& element, |
| 834 const std::string& prop, | 792 const std::string& prop, |
| 835 std::string* value) { | 793 std::string* value) { |
| 836 std::string script = base::StringPrintf( | 794 return ExecuteScriptAndParse(frame_id, |
| 837 "return (%s).apply(null, arguments);", atoms::GET_EFFECTIVE_STYLE); | 795 atoms::GET_EFFECTIVE_STYLE, |
| 838 ListValue args; | 796 "getEffectiveStyle", |
| 839 args.Append(element.ToValue()); | 797 CreateListValueFrom(element, prop), |
| 840 args.Append(Value::CreateStringValue(prop)); | 798 CreateDirectValueParser(value)); |
| 841 Value* unscoped_result = NULL; | |
| 842 Error* error = ExecuteScript( | |
| 843 frame_id, script, &args, &unscoped_result); | |
| 844 scoped_ptr<Value> result(unscoped_result); | |
| 845 if (error) { | |
| 846 error->AddDetails(base::StringPrintf( | |
| 847 "GetEffectiveStyle atom failed for property (%s)", prop.c_str())); | |
| 848 return error; | |
| 849 } | |
| 850 | |
| 851 if (!result->GetAsString(value)) { | |
| 852 std::string context = base::StringPrintf( | |
| 853 "GetEffectiveStyle atom returned non-string for property (%s): %s", | |
| 854 prop.c_str(), JsonStringify(result.get()).c_str()); | |
| 855 return new Error(kUnknownError, context); | |
| 856 } | |
| 857 return NULL; | |
| 858 } | 799 } |
| 859 | 800 |
| 860 Error* Session::GetElementBorder(const FrameId& frame_id, | 801 Error* Session::GetElementBorder(const FrameId& frame_id, |
| 861 const WebElementId& element, | 802 const WebElementId& element, |
| 862 int* border_left, | 803 int* border_left, |
| 863 int* border_top) { | 804 int* border_top) { |
| 864 std::string border_left_str, border_top_str; | 805 std::string border_left_str, border_top_str; |
| 865 Error* error = GetElementEffectiveStyle( | 806 Error* error = GetElementEffectiveStyle( |
| 866 frame_id, element, "border-left-width", &border_left_str); | 807 frame_id, element, "border-left-width", &border_left_str); |
| 867 if (error) | 808 if (error) |
| 868 return error; | 809 return error; |
| 869 error = GetElementEffectiveStyle( | 810 error = GetElementEffectiveStyle( |
| 870 frame_id, element, "border-top-width", &border_top_str); | 811 frame_id, element, "border-top-width", &border_top_str); |
| 871 if (error) | 812 if (error) |
| 872 return error; | 813 return error; |
| 873 | 814 |
| 874 base::StringToInt(border_left_str, border_left); | 815 base::StringToInt(border_left_str, border_left); |
| 875 base::StringToInt(border_top_str, border_top); | 816 base::StringToInt(border_top_str, border_top); |
| 876 return NULL; | 817 return NULL; |
| 877 } | 818 } |
| 878 | 819 |
| 879 Error* Session::IsElementDisplayed(const FrameId& frame_id, | 820 Error* Session::IsElementDisplayed(const FrameId& frame_id, |
| 880 const WebElementId& element, | 821 const WebElementId& element, |
| 881 bool ignore_opacity, | 822 bool ignore_opacity, |
| 882 bool* is_displayed) { | 823 bool* is_displayed) { |
| 883 std::string script = base::StringPrintf( | 824 return ExecuteScriptAndParse(frame_id, |
| 884 "return (%s).apply(null, arguments);", atoms::IS_DISPLAYED); | 825 atoms::IS_DISPLAYED, |
| 885 ListValue args; | 826 "isDisplayed", |
| 886 args.Append(element.ToValue()); | 827 CreateListValueFrom(element, ignore_opacity), |
| 887 args.Append(Value::CreateBooleanValue(ignore_opacity)); | 828 CreateDirectValueParser(is_displayed)); |
| 888 | |
| 889 Value* unscoped_result = NULL; | |
| 890 Error* error = ExecuteScript(frame_id, script, &args, &unscoped_result); | |
| 891 scoped_ptr<Value> result(unscoped_result); | |
| 892 if (error) | |
| 893 return error; | |
| 894 if (!result->GetAsBoolean(is_displayed)) | |
| 895 return new Error(kUnknownError, "IsDisplayed atom returned non-boolean: " + | |
| 896 JsonStringify(result.get())); | |
| 897 return NULL; | |
| 898 } | 829 } |
| 899 | 830 |
| 900 Error* Session::IsElementEnabled(const FrameId& frame_id, | 831 Error* Session::IsElementEnabled(const FrameId& frame_id, |
| 901 const WebElementId& element, | 832 const WebElementId& element, |
| 902 bool* is_enabled) { | 833 bool* is_enabled) { |
| 903 std::string script = base::StringPrintf( | 834 return ExecuteScriptAndParse(frame_id, |
| 904 "return (%s).apply(null, arguments);", atoms::IS_ENABLED); | 835 atoms::IS_ENABLED, |
| 905 ListValue args; | 836 "isEnabled", |
| 906 args.Append(element.ToValue()); | 837 CreateListValueFrom(element), |
| 907 | 838 CreateDirectValueParser(is_enabled)); |
| 908 Value* unscoped_result = NULL; | |
| 909 Error* error = ExecuteScript(frame_id, script, &args, &unscoped_result); | |
| 910 scoped_ptr<Value> result(unscoped_result); | |
| 911 if (error) | |
| 912 return error; | |
| 913 if (!result->GetAsBoolean(is_enabled)) | |
| 914 return new Error(kUnknownError, "IsEnabled atom returned non-boolean: " + | |
| 915 JsonStringify(result.get())); | |
| 916 return NULL; | |
| 917 } | 839 } |
| 918 | 840 |
| 919 Error* Session::SelectOptionElement(const FrameId& frame_id, | 841 Error* Session::SelectOptionElement(const FrameId& frame_id, |
| 920 const WebElementId& element) { | 842 const WebElementId& element) { |
| 921 ListValue args; | 843 return ExecuteScriptAndParse(frame_id, |
| 922 args.Append(element.ToValue()); | 844 atoms::SET_SELECTED, |
| 923 args.Append(Value::CreateBooleanValue(true)); | 845 "setSelected", |
| 924 | 846 CreateListValueFrom(element, true), |
| 925 std::string script = base::StringPrintf( | 847 CreateDirectValueParser(kSkipParsing)); |
| 926 "return (%s).apply(null, arguments);", atoms::SET_SELECTED); | |
| 927 | |
| 928 Value* unscoped_result = NULL; | |
| 929 Error* error = ExecuteScript(frame_id, script, &args, &unscoped_result); | |
| 930 scoped_ptr<Value> result(unscoped_result); | |
| 931 return error; | |
| 932 } | 848 } |
| 933 | 849 |
| 934 Error* Session::GetElementTagName(const FrameId& frame_id, | 850 Error* Session::GetElementTagName(const FrameId& frame_id, |
| 935 const WebElementId& element, | 851 const WebElementId& element, |
| 936 std::string* tag_name) { | 852 std::string* tag_name) { |
| 937 ListValue args; | 853 return ExecuteScriptAndParse( |
| 938 args.Append(element.ToValue()); | 854 frame_id, |
| 939 | 855 "function(elem) { return elem.tagName.toLowerCase() }", |
| 940 std::string script = "return arguments[0].tagName.toLocaleLowerCase();"; | 856 "getElementTagName", |
| 941 | 857 CreateListValueFrom(element), |
| 942 Value* unscoped_result = NULL; | 858 CreateDirectValueParser(tag_name)); |
| 943 Error* error = ExecuteScript(frame_id, script, &args, &unscoped_result); | |
| 944 scoped_ptr<Value> result(unscoped_result); | |
| 945 if (error) | |
| 946 return error; | |
| 947 if (!result->GetAsString(tag_name)) | |
| 948 return new Error(kUnknownError, "TagName script returned non-string: " + | |
| 949 JsonStringify(result.get())); | |
| 950 return NULL; | |
| 951 } | 859 } |
| 952 | 860 |
| 953 Error* Session::GetClickableLocation(const WebElementId& element, | 861 Error* Session::GetClickableLocation(const WebElementId& element, |
| 954 gfx::Point* location) { | 862 Point* location) { |
| 955 bool is_displayed = false; | 863 bool is_displayed = false; |
| 956 Error* error = IsElementDisplayed( | 864 Error* error = IsElementDisplayed( |
| 957 current_target_, element, true /* ignore_opacity */, &is_displayed); | 865 current_target_, element, true /* ignore_opacity */, &is_displayed); |
| 958 if (error) | 866 if (error) |
| 959 return error; | 867 return error; |
| 960 if (!is_displayed) | 868 if (!is_displayed) |
| 961 return new Error(kElementNotVisible, "Element must be displayed to click"); | 869 return new Error(kElementNotVisible, "Element must be displayed to click"); |
| 962 | 870 |
| 963 gfx::Rect rect; | 871 Rect rect; |
| 964 error = GetElementFirstClientRect(current_target_, element, &rect); | 872 error = GetElementFirstClientRect(current_target_, element, &rect); |
| 965 if (error) | 873 if (error) |
| 966 return error; | 874 return error; |
| 967 | 875 |
| 968 error = GetElementRegionInView(element, rect, true /* center */, location); | 876 error = GetElementRegionInView( |
| 877 element, rect, true /* center */, location); |
| 969 if (error) | 878 if (error) |
| 970 return error; | 879 return error; |
| 971 location->Offset(rect.width() / 2, rect.height() / 2); | 880 location->Offset(rect.width() / 2, rect.height() / 2); |
| 972 return NULL; | 881 return NULL; |
| 973 } | 882 } |
| 974 | 883 |
| 975 Error* Session::GetAttribute(const WebElementId& element, | 884 Error* Session::GetAttribute(const WebElementId& element, |
| 976 const std::string& key, Value** value) { | 885 const std::string& key, |
| 977 std::string script = base::StringPrintf( | 886 Value** value) { |
| 978 "return (%s).apply(null, arguments);", atoms::GET_ATTRIBUTE); | 887 return ExecuteScriptAndParse(current_target_, |
| 979 | 888 atoms::GET_ATTRIBUTE, |
| 980 ListValue args; | 889 "getAttribute", |
| 981 args.Append(element.ToValue()); | 890 CreateListValueFrom(element, key), |
| 982 args.Append(Value::CreateStringValue(key)); | 891 CreateDirectValueParser(value)); |
| 983 | |
| 984 Error* error = ExecuteScript(script, &args, value); | |
| 985 if (error) { | |
| 986 return error; | |
| 987 } | |
| 988 | |
| 989 return NULL; | |
| 990 } | 892 } |
| 991 | 893 |
| 992 Error* Session::WaitForAllTabsToStopLoading() { | 894 Error* Session::WaitForAllTabsToStopLoading() { |
| 993 if (!automation_.get()) | 895 if (!automation_.get()) |
| 994 return NULL; | 896 return NULL; |
| 995 Error* error = NULL; | 897 Error* error = NULL; |
| 996 RunSessionTask(NewRunnableMethod( | 898 RunSessionTask(NewRunnableMethod( |
| 997 automation_.get(), | 899 automation_.get(), |
| 998 &Automation::WaitForAllTabsToStopLoading, | 900 &Automation::WaitForAllTabsToStopLoading, |
| 999 &error)); | 901 &error)); |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1033 } | 935 } |
| 1034 | 936 |
| 1035 void Session::set_use_native_events(bool use_native_events) { | 937 void Session::set_use_native_events(bool use_native_events) { |
| 1036 use_native_events_ = use_native_events; | 938 use_native_events_ = use_native_events; |
| 1037 } | 939 } |
| 1038 | 940 |
| 1039 bool Session::use_native_events() const { | 941 bool Session::use_native_events() const { |
| 1040 return use_native_events_; | 942 return use_native_events_; |
| 1041 } | 943 } |
| 1042 | 944 |
| 1043 const gfx::Point& Session::get_mouse_position() const { | 945 const Point& Session::get_mouse_position() const { |
| 1044 return mouse_position_; | 946 return mouse_position_; |
| 1045 } | 947 } |
| 1046 | 948 |
| 1047 void Session::RunSessionTask(Task* task) { | 949 void Session::RunSessionTask(Task* task) { |
| 1048 base::WaitableEvent done_event(false, false); | 950 base::WaitableEvent done_event(false, false); |
| 1049 thread_.message_loop_proxy()->PostTask(FROM_HERE, NewRunnableMethod( | 951 thread_.message_loop_proxy()->PostTask(FROM_HERE, NewRunnableMethod( |
| 1050 this, | 952 this, |
| 1051 &Session::RunSessionTaskOnSessionThread, | 953 &Session::RunSessionTaskOnSessionThread, |
| 1052 task, | 954 task, |
| 1053 &done_event)); | 955 &done_event)); |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1086 } | 988 } |
| 1087 current_target_ = FrameId(tab_ids[0], FramePath()); | 989 current_target_ = FrameId(tab_ids[0], FramePath()); |
| 1088 } | 990 } |
| 1089 | 991 |
| 1090 void Session::TerminateOnSessionThread() { | 992 void Session::TerminateOnSessionThread() { |
| 1091 if (automation_.get()) | 993 if (automation_.get()) |
| 1092 automation_->Terminate(); | 994 automation_->Terminate(); |
| 1093 automation_.reset(); | 995 automation_.reset(); |
| 1094 } | 996 } |
| 1095 | 997 |
| 1096 Error* Session::ExecuteScriptAndParseResponse(const FrameId& frame_id, | 998 Error* Session::ExecuteScriptAndParseValue(const FrameId& frame_id, |
| 1097 const std::string& script, | 999 const std::string& script, |
| 1098 Value** script_result) { | 1000 Value** script_result) { |
| 1099 std::string response_json; | 1001 std::string response_json; |
| 1100 Error* error = NULL; | 1002 Error* error = NULL; |
| 1101 RunSessionTask(NewRunnableMethod( | 1003 RunSessionTask(NewRunnableMethod( |
| 1102 automation_.get(), | 1004 automation_.get(), |
| 1103 &Automation::ExecuteScript, | 1005 &Automation::ExecuteScript, |
| 1104 frame_id.window_id, | 1006 frame_id.window_id, |
| 1105 frame_id.frame_path, | 1007 frame_id.frame_path, |
| 1106 script, | 1008 script, |
| 1107 &response_json, | 1009 &response_json, |
| 1108 &error)); | 1010 &error)); |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1177 key_events[i].modified_text.c_str(), | 1079 key_events[i].modified_text.c_str(), |
| 1178 key_events[i].modifiers); | 1080 key_events[i].modifiers); |
| 1179 (*error)->AddDetails(details); | 1081 (*error)->AddDetails(details); |
| 1180 return; | 1082 return; |
| 1181 } | 1083 } |
| 1182 } | 1084 } |
| 1183 } | 1085 } |
| 1184 | 1086 |
| 1185 Error* Session::SwitchToFrameWithJavaScriptLocatedFrame( | 1087 Error* Session::SwitchToFrameWithJavaScriptLocatedFrame( |
| 1186 const std::string& script, ListValue* args) { | 1088 const std::string& script, ListValue* args) { |
| 1187 Value* unscoped_result = NULL; | 1089 class SwitchFrameValueParser : public ValueParser { |
| 1188 Error* error = ExecuteScript(script, args, &unscoped_result); | 1090 public: |
| 1189 scoped_ptr<Value> result(unscoped_result); | 1091 SwitchFrameValueParser( |
| 1092 bool* found_frame, WebElementId* frame, std::string* xpath) |
| 1093 : found_frame_(found_frame), frame_(frame), xpath_(xpath) { } |
| 1094 |
| 1095 virtual ~SwitchFrameValueParser() { } |
| 1096 |
| 1097 virtual bool Parse(base::Value* value) const OVERRIDE { |
| 1098 if (value->IsType(Value::TYPE_NULL)) { |
| 1099 *found_frame_ = false; |
| 1100 return true; |
| 1101 } |
| 1102 ListValue* list; |
| 1103 if (!value->GetAsList(&list)) |
| 1104 return false; |
| 1105 *found_frame_ = true; |
| 1106 return SetFromListValue(list, frame_, xpath_); |
| 1107 } |
| 1108 |
| 1109 private: |
| 1110 bool* found_frame_; |
| 1111 WebElementId* frame_; |
| 1112 std::string* xpath_; |
| 1113 }; |
| 1114 |
| 1115 bool found_frame; |
| 1116 WebElementId new_frame_element; |
| 1117 std::string xpath; |
| 1118 Error* error = ExecuteScriptAndParse( |
| 1119 current_target_, script, "switchFrame", args, |
| 1120 new SwitchFrameValueParser(&found_frame, &new_frame_element, &xpath)); |
| 1190 if (error) | 1121 if (error) |
| 1191 return error; | 1122 return error; |
| 1192 | 1123 |
| 1193 ListValue* frame_and_xpath_list; | 1124 if (!found_frame) |
| 1194 if (!result->GetAsList(&frame_and_xpath_list)) | |
| 1195 return new Error(kNoSuchFrame); | 1125 return new Error(kNoSuchFrame); |
| 1196 DictionaryValue* element_dict; | |
| 1197 std::string xpath; | |
| 1198 if (!frame_and_xpath_list->GetDictionary(0, &element_dict) || | |
| 1199 !frame_and_xpath_list->GetString(1, &xpath)) { | |
| 1200 return new Error( | |
| 1201 kUnknownError, | |
| 1202 "Frame finding script did not return correct type: " + | |
| 1203 JsonStringify(frame_and_xpath_list)); | |
| 1204 } | |
| 1205 WebElementId new_frame_element(element_dict); | |
| 1206 if (!new_frame_element.is_valid()) { | |
| 1207 return new Error( | |
| 1208 kUnknownError, | |
| 1209 "Frame finding script did not return a frame element: " + | |
| 1210 JsonStringify(element_dict)); | |
| 1211 } | |
| 1212 | 1126 |
| 1213 frame_elements_.push_back(new_frame_element); | 1127 frame_elements_.push_back(new_frame_element); |
| 1214 current_target_.frame_path = current_target_.frame_path.Append(xpath); | 1128 current_target_.frame_path = current_target_.frame_path.Append(xpath); |
| 1215 return NULL; | 1129 return NULL; |
| 1216 } | 1130 } |
| 1217 | 1131 |
| 1218 Error* Session::FindElementsHelper(const FrameId& frame_id, | 1132 Error* Session::FindElementsHelper(const FrameId& frame_id, |
| 1219 const WebElementId& root_element, | 1133 const WebElementId& root_element, |
| 1220 const std::string& locator, | 1134 const std::string& locator, |
| 1221 const std::string& query, | 1135 const std::string& query, |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1270 done = done || elapsed_time > implicit_wait_; | 1184 done = done || elapsed_time > implicit_wait_; |
| 1271 if (!done) | 1185 if (!done) |
| 1272 base::PlatformThread::Sleep(50); // Prevent a busy loop. | 1186 base::PlatformThread::Sleep(50); // Prevent a busy loop. |
| 1273 } | 1187 } |
| 1274 | 1188 |
| 1275 if (error.get()) | 1189 if (error.get()) |
| 1276 return error.release(); | 1190 return error.release(); |
| 1277 | 1191 |
| 1278 // Parse the results. | 1192 // Parse the results. |
| 1279 const std::string kInvalidElementDictionaryMessage = | 1193 const std::string kInvalidElementDictionaryMessage = |
| 1280 "Find element script returned invalid element dictionary: " + | 1194 "findElement script returned invalid element dictionary: " + |
| 1281 JsonStringify(value.get()); | 1195 JsonStringify(value.get()); |
| 1282 if (value->IsType(Value::TYPE_LIST)) { | 1196 if (find_one) { |
| 1283 ListValue* element_list = static_cast<ListValue*>(value.get()); | 1197 WebElementId element; |
| 1198 if (!SetFromValue(value.get(), &element)) |
| 1199 return new Error(kUnknownError, kInvalidElementDictionaryMessage); |
| 1200 elements->push_back(element); |
| 1201 } else { |
| 1202 ListValue* element_list; |
| 1203 if (!value->GetAsList(&element_list)) { |
| 1204 return new Error( |
| 1205 kUnknownError, |
| 1206 "findElements script returned unsupported type: " + |
| 1207 JsonStringify(value.get())); |
| 1208 } |
| 1284 for (size_t i = 0; i < element_list->GetSize(); ++i) { | 1209 for (size_t i = 0; i < element_list->GetSize(); ++i) { |
| 1285 DictionaryValue* element_dict = NULL; | 1210 WebElementId element; |
| 1286 if (!element_list->GetDictionary(i, &element_dict)) { | 1211 if (!SetFromValue(value.get(), &element)) |
| 1287 return new Error( | |
| 1288 kUnknownError, | |
| 1289 "Find element script returned non-dictionary: " + | |
| 1290 JsonStringify(element_list)); | |
| 1291 } | |
| 1292 | |
| 1293 WebElementId element(element_dict); | |
| 1294 if (!element.is_valid()) { | |
| 1295 return new Error(kUnknownError, kInvalidElementDictionaryMessage); | 1212 return new Error(kUnknownError, kInvalidElementDictionaryMessage); |
| 1296 } | |
| 1297 elements->push_back(element); | 1213 elements->push_back(element); |
| 1298 } | 1214 } |
| 1299 } else if (value->IsType(Value::TYPE_DICTIONARY)) { | |
| 1300 DictionaryValue* element_dict = | |
| 1301 static_cast<DictionaryValue*>(value.get()); | |
| 1302 WebElementId element(element_dict); | |
| 1303 if (!element.is_valid()) { | |
| 1304 return new Error(kUnknownError, kInvalidElementDictionaryMessage); | |
| 1305 } | |
| 1306 elements->push_back(element); | |
| 1307 } else { | |
| 1308 return new Error( | |
| 1309 kUnknownError, | |
| 1310 "Find element script returned unsupported type: " + | |
| 1311 JsonStringify(value.get())); | |
| 1312 } | 1215 } |
| 1313 return NULL; | 1216 return NULL; |
| 1314 } | 1217 } |
| 1315 | 1218 |
| 1316 Error* Session::GetElementRegionInViewHelper( | 1219 Error* Session::GetElementRegionInViewHelper( |
| 1317 const FrameId& frame_id, | 1220 const FrameId& frame_id, |
| 1318 const WebElementId& element, | 1221 const WebElementId& element, |
| 1319 const gfx::Rect& region, | 1222 const Rect& region, |
| 1320 bool center, | 1223 bool center, |
| 1321 gfx::Point* location) { | 1224 Point* location) { |
| 1322 std::string jscript = base::StringPrintf( | 1225 return ExecuteScriptAndParse(frame_id, |
| 1323 "return (%s).apply(null, arguments);", atoms::GET_LOCATION_IN_VIEW); | 1226 atoms::GET_LOCATION_IN_VIEW, |
| 1324 ListValue jscript_args; | 1227 "getLocationInView", |
| 1325 jscript_args.Append(element.ToValue()); | 1228 CreateListValueFrom(element, center, region), |
| 1326 jscript_args.Append(Value::CreateBooleanValue(center)); | 1229 CreateDirectValueParser(location)); |
| 1327 DictionaryValue* elem_offset_dict = new DictionaryValue(); | |
| 1328 elem_offset_dict->SetInteger("left", region.x()); | |
| 1329 elem_offset_dict->SetInteger("top", region.y()); | |
| 1330 elem_offset_dict->SetInteger("width", region.width()); | |
| 1331 elem_offset_dict->SetInteger("height", region.height()); | |
| 1332 jscript_args.Append(elem_offset_dict); | |
| 1333 Value* unscoped_value = NULL; | |
| 1334 Error* error = ExecuteScript(frame_id, jscript, &jscript_args, | |
| 1335 &unscoped_value); | |
| 1336 scoped_ptr<Value> value(unscoped_value); | |
| 1337 if (error) | |
| 1338 return error; | |
| 1339 if (!value->IsType(Value::TYPE_DICTIONARY)) { | |
| 1340 return new Error( | |
| 1341 kUnknownError, | |
| 1342 "Location atom returned non-dictionary type: " + | |
| 1343 JsonStringify(value.get())); | |
| 1344 } | |
| 1345 DictionaryValue* loc_dict = static_cast<DictionaryValue*>(value.get()); | |
| 1346 int x = 0, y = 0; | |
| 1347 if (!loc_dict->GetInteger("x", &x) || | |
| 1348 !loc_dict->GetInteger("y", &y)) { | |
| 1349 return new Error( | |
| 1350 kUnknownError, | |
| 1351 "Location atom returned bad coordinate dictionary: " + | |
| 1352 JsonStringify(loc_dict)); | |
| 1353 } | |
| 1354 *location = gfx::Point(x, y); | |
| 1355 return NULL; | |
| 1356 } | 1230 } |
| 1357 | 1231 |
| 1358 Error* Session::GetScreenShot(std::string* png) { | 1232 Error* Session::GetScreenShot(std::string* png) { |
| 1359 Error* error = NULL; | 1233 Error* error = NULL; |
| 1360 ScopedTempDir screenshots_dir; | 1234 ScopedTempDir screenshots_dir; |
| 1361 if (!screenshots_dir.CreateUniqueTempDir()) { | 1235 if (!screenshots_dir.CreateUniqueTempDir()) { |
| 1362 return new Error(kUnknownError, | 1236 return new Error(kUnknownError, |
| 1363 "Could not create temp directory for screenshot"); | 1237 "Could not create temp directory for screenshot"); |
| 1364 } | 1238 } |
| 1365 | 1239 |
| 1366 FilePath path = screenshots_dir.path().AppendASCII("screen"); | 1240 FilePath path = screenshots_dir.path().AppendASCII("screen"); |
| 1367 RunSessionTask(NewRunnableMethod( | 1241 RunSessionTask(NewRunnableMethod( |
| 1368 automation_.get(), | 1242 automation_.get(), |
| 1369 &Automation::CaptureEntirePageAsPNG, | 1243 &Automation::CaptureEntirePageAsPNG, |
| 1370 current_target_.window_id, | 1244 current_target_.window_id, |
| 1371 path, | 1245 path, |
| 1372 &error)); | 1246 &error)); |
| 1373 if (error) | 1247 if (error) |
| 1374 return error; | 1248 return error; |
| 1375 if (!file_util::ReadFileToString(path, png)) | 1249 if (!file_util::ReadFileToString(path, png)) |
| 1376 return new Error(kUnknownError, "Could not read screenshot file"); | 1250 return new Error(kUnknownError, "Could not read screenshot file"); |
| 1377 return NULL; | 1251 return NULL; |
| 1378 } | 1252 } |
| 1379 | 1253 |
| 1380 } // namespace webdriver | 1254 } // namespace webdriver |
| OLD | NEW |