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

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

Issue 6507015: Implement the target locator commands for ChromeDriver. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: address Pawel's concerns Created 9 years, 10 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
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_manager.h" 5 #include "chrome/test/webdriver/session_manager.h"
6 6
7 #include <vector> 7 #include <vector>
8 8
9 #include "base/command_line.h" 9 #include "base/command_line.h"
10 #include "base/file_util.h" 10 #include "base/file_util.h"
11 #include "base/logging.h" 11 #include "base/logging.h"
12 #include "base/message_loop_proxy.h" 12 #include "base/message_loop_proxy.h"
13 #include "base/process.h" 13 #include "base/process.h"
14 #include "base/process_util.h" 14 #include "base/process_util.h"
15 #include "base/scoped_ptr.h"
16 #include "base/string_number_conversions.h"
15 #include "base/string_util.h" 17 #include "base/string_util.h"
16 #include "base/json/json_reader.h" 18 #include "base/json/json_reader.h"
17 #include "base/json/json_writer.h" 19 #include "base/json/json_writer.h"
18 #include "base/test/test_timeouts.h" 20 #include "base/test/test_timeouts.h"
19 #include "base/utf_string_conversions.h" 21 #include "base/utf_string_conversions.h"
20 #include "chrome/app/chrome_command_ids.h" 22 #include "chrome/app/chrome_command_ids.h"
21 #include "chrome/common/chrome_constants.h" 23 #include "chrome/common/chrome_constants.h"
22 #include "chrome/common/chrome_switches.h" 24 #include "chrome/common/chrome_switches.h"
23 #include "chrome/test/test_launcher_utils.h" 25 #include "chrome/test/test_launcher_utils.h"
26 #include "chrome/test/webdriver/session_manager.h"
24 #include "chrome/test/webdriver/utility_functions.h" 27 #include "chrome/test/webdriver/utility_functions.h"
25 #include "chrome/test/webdriver/webdriver_key_converter.h" 28 #include "chrome/test/webdriver/webdriver_key_converter.h"
26 #include "third_party/webdriver/atoms.h" 29 #include "third_party/webdriver/atoms.h"
27 30
28 namespace webdriver { 31 namespace webdriver {
29 32
30 Session::Session(const std::string& id) 33 Session::Session()
31 : thread_(id.c_str()), 34 : id_(GenerateRandomID()),
32 id_(id), 35 thread_(id_.c_str()),
33 window_num_(0),
34 implicit_wait_(0), 36 implicit_wait_(0),
35 current_frame_xpath_("") { 37 current_frame_xpath_(""),
38 current_window_id_(0) {
39 SessionManager::GetInstance()->Add(this);
36 } 40 }
37 41
38 Session::~Session() {} 42 Session::~Session() {
43 SessionManager::GetInstance()->Remove(id_);
44 }
39 45
40 bool Session::Init() { 46 bool Session::Init() {
41 if (!thread_.Start()) { 47 bool success = false;
48 if (thread_.Start()) {
49 RunSessionTask(NewRunnableMethod(
50 this,
51 &Session::InitOnSessionThread,
52 &success));
53 } else {
42 LOG(ERROR) << "Cannot start session thread"; 54 LOG(ERROR) << "Cannot start session thread";
43 return false;
44 } 55 }
45 56 if (!success)
46 bool success = false; 57 delete this;
Jason Leyba 2011/02/14 21:00:23 This looks really suspicious.
47 RunSessionTask(NewRunnableMethod(
48 this,
49 &Session::InitOnSessionThread,
50 &success));
51 return success; 58 return success;
52 } 59 }
53 60
54 void Session::Terminate() { 61 void Session::Terminate() {
55 RunSessionTask(NewRunnableMethod( 62 RunSessionTask(NewRunnableMethod(
56 this, 63 this,
57 &Session::TerminateOnSessionThread)); 64 &Session::TerminateOnSessionThread));
65 delete this;
Jason Leyba 2011/02/14 21:00:23 Same as above. This looks really suspicious.
58 } 66 }
59 67
60 ErrorCode Session::ExecuteScript(const std::string& script, 68 ErrorCode Session::ExecuteScript(int window_id,
69 const std::string& frame_xpath,
70 const std::string& script,
61 const ListValue* const args, 71 const ListValue* const args,
62 Value** value) { 72 Value** value) {
63 std::string args_as_json; 73 std::string args_as_json;
64 base::JSONWriter::Write(static_cast<const Value* const>(args), 74 base::JSONWriter::Write(static_cast<const Value* const>(args),
65 /*pretty_print=*/false, 75 /*pretty_print=*/false,
66 &args_as_json); 76 &args_as_json);
67 77
68 std::string jscript = "window.domAutomationController.send((function(){" + 78 std::string jscript = "window.domAutomationController.send((function(){" +
69 // Every injected script is fed through the executeScript atom. This atom 79 // Every injected script is fed through the executeScript atom. This atom
70 // will catch any errors that are thrown and convert them to the 80 // will catch any errors that are thrown and convert them to the
71 // appropriate JSON structure. 81 // appropriate JSON structure.
72 build_atom(EXECUTE_SCRIPT, sizeof EXECUTE_SCRIPT) + 82 build_atom(EXECUTE_SCRIPT, sizeof EXECUTE_SCRIPT) +
73 "var result = executeScript(function(){" + script + "}," + 83 "var result = executeScript(function(){" + script + "}," +
74 args_as_json + ");return JSON.stringify(result);})());"; 84 args_as_json + ");return JSON.stringify(result);})());";
75 85
76 // Should we also log the script that's being executed? It could be several KB 86 // Should we also log the script that's being executed? It could be several KB
77 // in size and will add lots of noise to the logs. 87 // in size and will add lots of noise to the logs.
78 VLOG(1) << "Executing script in frame: " << current_frame_xpath_; 88 VLOG(1) << "Executing script in frame: " << current_frame_xpath_;
79 89
80 std::string result; 90 std::string result;
81 bool success; 91 bool success = false;
82 RunSessionTask(NewRunnableMethod( 92 RunSessionTask(NewRunnableMethod(
83 automation_.get(), 93 automation_.get(),
84 &Automation::ExecuteScript, 94 &Automation::ExecuteScript,
85 current_frame_xpath_, 95 window_id,
96 frame_xpath,
86 jscript, 97 jscript,
87 &result, 98 &result,
88 &success)); 99 &success));
89 if (!success) { 100 if (!success) {
90 *value = Value::CreateStringValue( 101 *value = Value::CreateStringValue(
91 "Unknown internal script execution failure"); 102 "Unknown internal script execution failure");
92 return kUnknownError; 103 return kUnknownError;
93 } 104 }
94 105
95 VLOG(1) << "...script result: " << result; 106 VLOG(1) << "...script result: " << result;
(...skipping 25 matching lines...) Expand all
121 *value = Value::CreateNullValue(); 132 *value = Value::CreateNullValue();
122 } 133 }
123 134
124 int status; 135 int status;
125 if (!result_dict->GetInteger("status", &status)) { 136 if (!result_dict->GetInteger("status", &status)) {
126 NOTREACHED() << "...script did not return a status flag."; 137 NOTREACHED() << "...script did not return a status flag.";
127 } 138 }
128 return static_cast<ErrorCode>(status); 139 return static_cast<ErrorCode>(status);
129 } 140 }
130 141
142 ErrorCode Session::ExecuteScript(const std::string& script,
143 const ListValue* const args,
144 Value** value) {
145 return ExecuteScript(
146 current_window_id_, current_frame_xpath_, script, args, value);
147 }
148
131 ErrorCode Session::SendKeys(DictionaryValue* element, const string16& keys) { 149 ErrorCode Session::SendKeys(DictionaryValue* element, const string16& keys) {
132 ListValue args; 150 ListValue args;
133 args.Append(element); 151 args.Append(element);
134 // TODO(jleyba): Update this to use the correct atom. 152 // TODO(jleyba): Update this to use the correct atom.
135 std::string script = "document.activeElement.blur();arguments[0].focus();"; 153 std::string script = "document.activeElement.blur();arguments[0].focus();";
136 Value* unscoped_result = NULL; 154 Value* unscoped_result = NULL;
137 ErrorCode code = ExecuteScript(script, &args, &unscoped_result); 155 ErrorCode code = ExecuteScript(script, &args, &unscoped_result);
138 scoped_ptr<Value> result(unscoped_result); 156 scoped_ptr<Value> result(unscoped_result);
139 if (code != kSuccess) 157 if (code != kSuccess)
140 return code; 158 return code;
141 159
142 bool success = false; 160 bool success = false;
143 RunSessionTask(NewRunnableMethod( 161 RunSessionTask(NewRunnableMethod(
144 this, 162 this,
145 &Session::SendKeysOnSessionThread, 163 &Session::SendKeysOnSessionThread,
146 keys, 164 keys,
147 &success)); 165 &success));
148 if (!success) 166 if (!success)
149 return kUnknownError; 167 return kUnknownError;
150 return kSuccess; 168 return kSuccess;
151 } 169 }
152 170
153 bool Session::NavigateToURL(const std::string& url) { 171 bool Session::NavigateToURL(const std::string& url) {
154 bool success = false; 172 bool success = false;
155 RunSessionTask(NewRunnableMethod( 173 RunSessionTask(NewRunnableMethod(
156 automation_.get(), 174 automation_.get(),
157 &Automation::NavigateToURL, 175 &Automation::NavigateToURL,
176 current_window_id_,
158 url, 177 url,
159 &success)); 178 &success));
160 return success; 179 return success;
161 } 180 }
162 181
163 bool Session::GoForward() { 182 bool Session::GoForward() {
164 bool success = false; 183 bool success = false;
165 RunSessionTask(NewRunnableMethod( 184 RunSessionTask(NewRunnableMethod(
166 automation_.get(), 185 automation_.get(),
167 &Automation::GoForward, 186 &Automation::GoForward,
187 current_window_id_,
168 &success)); 188 &success));
169 return success; 189 return success;
170 } 190 }
171 191
172 bool Session::GoBack() { 192 bool Session::GoBack() {
173 bool success = false; 193 bool success = false;
174 RunSessionTask(NewRunnableMethod( 194 RunSessionTask(NewRunnableMethod(
175 automation_.get(), 195 automation_.get(),
176 &Automation::GoBack, 196 &Automation::GoBack,
197 current_window_id_,
177 &success)); 198 &success));
178 return success; 199 return success;
179 } 200 }
180 201
181 bool Session::Reload() { 202 bool Session::Reload() {
182 bool success = false; 203 bool success = false;
183 RunSessionTask(NewRunnableMethod( 204 RunSessionTask(NewRunnableMethod(
184 automation_.get(), 205 automation_.get(),
185 &Automation::Reload, 206 &Automation::Reload,
207 current_window_id_,
186 &success)); 208 &success));
187 return success; 209 return success;
188 } 210 }
189 211
190 bool Session::GetURL(std::string* url) { 212 bool Session::GetURL(std::string* url) {
191 bool success = false; 213 bool success = false;
192 RunSessionTask(NewRunnableMethod( 214 RunSessionTask(NewRunnableMethod(
193 automation_.get(), 215 automation_.get(),
194 &Automation::GetURL, 216 &Automation::GetURL,
217 current_window_id_,
195 url, 218 url,
196 &success)); 219 &success));
197 return success; 220 return success;
198 } 221 }
199 222
200 bool Session::GetTabTitle(std::string* tab_title) { 223 bool Session::GetTabTitle(std::string* tab_title) {
201 bool success = false; 224 bool success = false;
202 RunSessionTask(NewRunnableMethod( 225 RunSessionTask(NewRunnableMethod(
203 automation_.get(), 226 automation_.get(),
204 &Automation::GetTabTitle, 227 &Automation::GetTabTitle,
228 current_window_id_,
205 tab_title, 229 tab_title,
206 &success)); 230 &success));
207 return success; 231 return success;
208 } 232 }
209 233
234 bool Session::GetWindowIds(std::vector<int>* window_ids) {
235 bool success = false;
236 RunSessionTask(NewRunnableMethod(
237 automation_.get(),
238 &Automation::GetTabIds,
239 window_ids,
240 &success));
241 return success;
242 }
243
244 ErrorCode Session::SwitchToWindow(const std::string& name) {
245 int switch_to_id = 0;
246 int name_no = 0;
247 if (base::StringToInt(name, &name_no)) {
248 bool does_exist = false;
249 RunSessionTask(NewRunnableMethod(
250 automation_.get(),
251 &Automation::DoesTabExist,
252 name_no,
253 &does_exist));
254 if (does_exist)
255 switch_to_id = name_no;
256 }
257
258 if (!switch_to_id) {
259 std::vector<int> window_ids;
260 GetWindowIds(&window_ids);
261 // See if any of the window names match |name|.
262 for (size_t i = 0; i < window_ids.size(); ++i) {
263 ListValue empty_list;
264 Value* unscoped_name_value;
265 std::string window_name;
266 ErrorCode code = ExecuteScript(window_ids[i],
267 "",
268 "return window.name;",
269 &empty_list,
270 &unscoped_name_value);
271 scoped_ptr<Value> name_value(unscoped_name_value);
272 if (code == kSuccess &&
273 name_value->GetAsString(&window_name) &&
274 name == window_name) {
275 switch_to_id = window_ids[i];
276 break;
277 }
278 }
279 }
280
281 if (!switch_to_id)
282 return kNoSuchWindow;
283 current_window_id_ = switch_to_id;
284 current_frame_xpath_ = "";
285 return kSuccess;
286 }
287
288 ErrorCode Session::SwitchToFrameWithNameOrId(const std::string& name_or_id) {
289 std::string script =
290 "var arg = arguments[0];"
291 "var xpath = '(/html/body//iframe|/html/frameset/frame)';"
292 "var sub = function(s) { return s.replace(/\\$/g, arg); };"
293 "xpath += sub('[@name=\"$\" or @id=\"$\"]');"
294 "var frame = document.evaluate(xpath, document, null, "
295 " XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;"
296 "if (!frame) { return null; }"
297 "xpath = frame.tagName == 'IFRAME' ? '/html/body//iframe'"
298 " : '/html/frameset/frame';"
299 "return xpath + sub('[@' + (frame.id == arg ? 'id' : 'name')"
300 " + '=\"$\"]');";
301 ListValue args;
302 args.Append(new StringValue(name_or_id));
303 return SwitchToFrameWithJavaScriptLocatedFrame(script, &args);
304 }
305
306 ErrorCode Session::SwitchToFrameWithIndex(int index) {
307 // We cannot simply index into window.frames because we need to know the
308 // tagName of the frameElement. If child frame N is from another domain, then
309 // the following will run afoul of the same origin policy:
310 // window.frames[N].frameElement;
311 // Instead of indexing window.frames, we use a an XPath expression to index
312 // into the list of all IFRAME and FRAME elements on the page - if we find
313 // something, then that XPath expression can be used as the new frame's XPath.
314 std::string script =
315 "var index = '[' + (arguments[0] + 1) + ']';"
316 "var xpath = '(/html/body//iframe|/html/frameset/frame)' + "
317 " index;"
318 "console.info('searching for frame by xpath: ' + xpath);"
319 "var frame = document.evaluate(xpath, document, null, "
320 "XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;"
321 "console.info(frame == null ? 'found nothing' : frame);"
322 "return frame == null ? null : ((frame.tagName == 'IFRAME' ? "
323 " '/html/body//iframe' : '/html/frameset/frame') + index);";
324 ListValue args;
325 args.Append(Value::CreateIntegerValue(index));
326 return SwitchToFrameWithJavaScriptLocatedFrame(script, &args);
327 }
328
329 bool Session::CloseWindow() {
330 bool success = false;
331
332 std::vector<int> window_ids;
333 if (!GetWindowIds(&window_ids) || window_ids.empty()) {
334 LOG(ERROR) << "Could not get window ids";
335 return false;
336 }
337 RunSessionTask(NewRunnableMethod(
338 automation_.get(),
339 &Automation::CloseTab,
340 current_window_id_,
341 &success));
342 if (success) {
343 // The automation connection will have closed if we just closed the last
344 // window, so terminate the session.
345 if (window_ids.size() == 1u)
346 Terminate();
347 }
348 return success;
349 }
350
210 void Session::RunSessionTask(Task* task) { 351 void Session::RunSessionTask(Task* task) {
211 base::WaitableEvent done_event(false, false); 352 base::WaitableEvent done_event(false, false);
212 thread_.message_loop_proxy()->PostTask(FROM_HERE, NewRunnableMethod( 353 thread_.message_loop_proxy()->PostTask(FROM_HERE, NewRunnableMethod(
213 this, 354 this,
214 &Session::RunSessionTaskOnSessionThread, 355 &Session::RunSessionTaskOnSessionThread,
215 task, 356 task,
216 &done_event)); 357 &done_event));
217 done_event.Wait(); 358 done_event.Wait();
218 } 359 }
219 360
220 void Session::RunSessionTaskOnSessionThread(Task* task, 361 void Session::RunSessionTaskOnSessionThread(Task* task,
221 base::WaitableEvent* done_event) { 362 base::WaitableEvent* done_event) {
222 task->Run(); 363 task->Run();
223 delete task; 364 delete task;
224 done_event->Signal(); 365 done_event->Signal();
225 } 366 }
226 367
227 void Session::InitOnSessionThread(bool* success) { 368 void Session::InitOnSessionThread(bool* success) {
228 automation_.reset(new Automation()); 369 automation_.reset(new Automation());
229 automation_->Init(success); 370 automation_->Init(success);
371 if (!*success)
372 return;
373
374 std::vector<int> tab_ids;
375 automation_->GetTabIds(&tab_ids, success);
376 if (!*success) {
377 LOG(ERROR) << "Could not get tab ids";
378 return;
379 }
380 if (tab_ids.empty()) {
381 LOG(ERROR) << "No tab ids after initialization";
382 *success = false;
383 } else {
384 current_window_id_ = tab_ids[0];
385 }
230 } 386 }
231 387
232 void Session::TerminateOnSessionThread() { 388 void Session::TerminateOnSessionThread() {
233 automation_->Terminate(); 389 if (automation_.get())
390 automation_->Terminate();
234 automation_.reset(); 391 automation_.reset();
235 } 392 }
236 393
237 void Session::SendKeysOnSessionThread(const string16& keys, 394 void Session::SendKeysOnSessionThread(const string16& keys,
238 bool* success) { 395 bool* success) {
239 *success = true; 396 *success = true;
240 std::vector<WebKeyEvent> key_events; 397 std::vector<WebKeyEvent> key_events;
241 ConvertKeysToWebKeyEvents(keys, &key_events); 398 ConvertKeysToWebKeyEvents(keys, &key_events);
242 for (size_t i = 0; i < key_events.size(); ++i) { 399 for (size_t i = 0; i < key_events.size(); ++i) {
243 bool key_success = false; 400 bool key_success = false;
244 automation_->SendWebKeyEvent(key_events[i], &key_success); 401 automation_->SendWebKeyEvent(
402 current_window_id_, key_events[i], &key_success);
245 if (!key_success) { 403 if (!key_success) {
246 LOG(ERROR) << "Failed to send key event. Event details:\n" 404 LOG(ERROR) << "Failed to send key event. Event details:\n"
247 << "Type: " << key_events[i].type << "\n" 405 << "Type: " << key_events[i].type << "\n"
248 << "KeyCode: " << key_events[i].key_code << "\n" 406 << "KeyCode: " << key_events[i].key_code << "\n"
249 << "UnmodifiedText: " << key_events[i].unmodified_text << "\n" 407 << "UnmodifiedText: " << key_events[i].unmodified_text << "\n"
250 << "ModifiedText: " << key_events[i].modified_text << "\n" 408 << "ModifiedText: " << key_events[i].modified_text << "\n"
251 << "Modifiers: " << key_events[i].modifiers << "\n"; 409 << "Modifiers: " << key_events[i].modifiers << "\n";
252 *success = false; 410 *success = false;
253 } 411 }
254 } 412 }
255 } 413 }
256 414
415 ErrorCode Session::SwitchToFrameWithJavaScriptLocatedFrame(
416 const std::string& script,
417 ListValue* args) {
418 Value* unscoped_result = NULL;
419 ErrorCode code = ExecuteScript(script, args, &unscoped_result);
420 scoped_ptr<Value> result(unscoped_result);
421 if (code != kSuccess)
422 return code;
423 std::string xpath;
424 if (result->GetAsString(&xpath)) {
425 if (current_frame_xpath_.length())
426 current_frame_xpath_ += "\n";
427 current_frame_xpath_ += xpath;
428 return kSuccess;
429 }
430 return kNoSuchFrame;
431 }
432
257 } // namespace webdriver 433 } // namespace webdriver
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698