1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/test/chromedriver/commands.h"
11 #include "base/bind.h"
12 #include "base/bind_helpers.h"
13 #include "base/logging.h"
14 #include "base/memory/linked_ptr.h"
15 #include "base/message_loop/message_loop.h"
16 #include "base/message_loop/message_loop_proxy.h"
17 #include "base/run_loop.h"
18 #include "base/strings/stringprintf.h"
19 #include "base/sys_info.h"
20 #include "base/values.h"
21 #include "chrome/test/chromedriver/capabilities.h"
22 #include "chrome/test/chromedriver/chrome/chrome.h"
23 #include "chrome/test/chromedriver/chrome/status.h"
24 #include "chrome/test/chromedriver/logging.h"
25 #include "chrome/test/chromedriver/session.h"
26 #include "chrome/test/chromedriver/session_thread_map.h"
27 #include "chrome/test/chromedriver/util.h"
29 void ExecuteGetStatus(
30 const base::DictionaryValue
& params
,
31 const std::string
& session_id
,
32 const CommandCallback
& callback
) {
33 base::DictionaryValue build
;
34 build
.SetString("version", "alpha");
36 base::DictionaryValue os
;
37 os
.SetString("name", base::SysInfo::OperatingSystemName());
38 os
.SetString("version", base::SysInfo::OperatingSystemVersion());
39 os
.SetString("arch", base::SysInfo::OperatingSystemArchitecture());
41 base::DictionaryValue info
;
42 info
.Set("build", build
.DeepCopy());
43 info
.Set("os", os
.DeepCopy());
45 Status(kOk
), scoped_ptr
<base::Value
>(info
.DeepCopy()), std::string());
48 void ExecuteCreateSession(
49 SessionThreadMap
* session_thread_map
,
50 const Command
& init_session_cmd
,
51 const base::DictionaryValue
& params
,
52 const std::string
& session_id
,
53 const CommandCallback
& callback
) {
54 std::string new_id
= session_id
;
56 new_id
= GenerateId();
57 scoped_ptr
<Session
> session(new Session(new_id
));
58 scoped_ptr
<base::Thread
> thread(new base::Thread(new_id
.c_str()));
59 if (!thread
->Start()) {
61 Status(kUnknownError
, "failed to start a thread for the new session"),
62 scoped_ptr
<base::Value
>(),
67 thread
->message_loop()->PostTask(
68 FROM_HERE
, base::Bind(&SetThreadLocalSession
, base::Passed(&session
)));
70 ->insert(std::make_pair(new_id
, make_linked_ptr(thread
.release())));
71 init_session_cmd
.Run(params
, new_id
, callback
);
76 void OnSessionQuit(const base::WeakPtr
<size_t>& quit_remaining_count
,
77 const base::Closure
& all_quit_func
,
79 scoped_ptr
<base::Value
> value
,
80 const std::string
& session_id
) {
81 // |quit_remaining_count| may no longer be valid if a timeout occurred.
82 if (!quit_remaining_count
)
85 (*quit_remaining_count
)--;
86 if (!*quit_remaining_count
)
93 const Command
& quit_command
,
94 SessionThreadMap
* session_thread_map
,
95 const base::DictionaryValue
& params
,
96 const std::string
& session_id
,
97 const CommandCallback
& callback
) {
98 size_t quit_remaining_count
= session_thread_map
->size();
99 base::WeakPtrFactory
<size_t> weak_ptr_factory(&quit_remaining_count
);
100 if (!quit_remaining_count
) {
101 callback
.Run(Status(kOk
), scoped_ptr
<base::Value
>(), session_id
);
104 base::RunLoop run_loop
;
105 for (SessionThreadMap::const_iterator iter
= session_thread_map
->begin();
106 iter
!= session_thread_map
->end();
108 quit_command
.Run(params
,
110 base::Bind(&OnSessionQuit
,
111 weak_ptr_factory
.GetWeakPtr(),
112 run_loop
.QuitClosure()));
114 base::MessageLoop::current()->PostDelayedTask(
115 FROM_HERE
, run_loop
.QuitClosure(), base::TimeDelta::FromSeconds(10));
116 // Uses a nested run loop to block this thread until all the quit
117 // commands have executed, or the timeout expires.
118 base::MessageLoop::current()->SetNestableTasksAllowed(true);
120 callback
.Run(Status(kOk
), scoped_ptr
<base::Value
>(), session_id
);
125 void TerminateSessionThreadOnCommandThread(SessionThreadMap
* session_thread_map
,
126 const std::string
& session_id
) {
127 session_thread_map
->erase(session_id
);
130 void ExecuteSessionCommandOnSessionThread(
131 const char* command_name
,
132 const SessionCommand
& command
,
133 bool return_ok_without_session
,
134 scoped_ptr
<base::DictionaryValue
> params
,
135 scoped_refptr
<base::SingleThreadTaskRunner
> cmd_task_runner
,
136 const CommandCallback
& callback_on_cmd
,
137 const base::Closure
& terminate_on_cmd
) {
138 Session
* session
= GetThreadLocalSession();
140 cmd_task_runner
->PostTask(
142 base::Bind(callback_on_cmd
,
143 Status(return_ok_without_session
? kOk
: kNoSuchSession
),
144 base::Passed(scoped_ptr
<base::Value
>()),
150 VLOG(0) << "COMMAND " << command_name
<< " "
151 << FormatValueForDisplay(*params
);
153 scoped_ptr
<base::Value
> value
;
154 Status status
= command
.Run(session
, *params
, &value
);
156 if (status
.IsError() && session
->chrome
) {
157 if (!session
->quit
&& session
->chrome
->HasCrashedWebView()) {
158 session
->quit
= true;
159 std::string
message("session deleted because of page crash");
160 if (!session
->detach
) {
161 Status quit_status
= session
->chrome
->Quit();
162 if (quit_status
.IsError())
163 message
+= ", but failed to kill browser:" + quit_status
.message();
165 status
= Status(kUnknownError
, message
, status
);
166 } else if (status
.code() == kDisconnected
) {
167 // Some commands, like clicking a button or link which closes the window,
168 // may result in a kDisconnected error code.
169 std::list
<std::string
> web_view_ids
;
170 Status status_tmp
= session
->chrome
->GetWebViewIds(&web_view_ids
);
171 if (status_tmp
.IsError() && status_tmp
.code() != kChromeNotReachable
) {
173 "failed to check if window was closed: " + status_tmp
.message());
174 } else if (std::find(web_view_ids
.begin(),
176 session
->window
) == web_view_ids
.end()) {
177 status
= Status(kOk
);
180 if (status
.IsError()) {
182 "Session info: chrome=" + session
->chrome
->GetVersion());
188 if (status
.IsError()) {
189 result
= status
.message();
191 result
= FormatValueForDisplay(*value
);
193 VLOG(0) << "RESPONSE " << command_name
194 << (result
.length() ? " " + result
: "");
197 if (status
.IsOk() && session
->auto_reporting_enabled
) {
198 std::string message
= session
->GetFirstBrowserError();
199 if (!message
.empty())
200 status
= Status(kUnknownError
, message
);
203 cmd_task_runner
->PostTask(
205 base::Bind(callback_on_cmd
, status
, base::Passed(&value
), session
->id
));
208 SetThreadLocalSession(scoped_ptr
<Session
>());
210 cmd_task_runner
->PostTask(FROM_HERE
, terminate_on_cmd
);
216 void ExecuteSessionCommand(
217 SessionThreadMap
* session_thread_map
,
218 const char* command_name
,
219 const SessionCommand
& command
,
220 bool return_ok_without_session
,
221 const base::DictionaryValue
& params
,
222 const std::string
& session_id
,
223 const CommandCallback
& callback
) {
224 SessionThreadMap::iterator iter
= session_thread_map
->find(session_id
);
225 if (iter
== session_thread_map
->end()) {
226 Status
status(return_ok_without_session
? kOk
: kNoSuchSession
);
227 callback
.Run(status
, scoped_ptr
<base::Value
>(), session_id
);
229 iter
->second
->message_loop()
230 ->PostTask(FROM_HERE
,
231 base::Bind(&ExecuteSessionCommandOnSessionThread
,
234 return_ok_without_session
,
235 base::Passed(make_scoped_ptr(params
.DeepCopy())),
236 base::MessageLoopProxy::current(),
238 base::Bind(&TerminateSessionThreadOnCommandThread
,
246 void CreateSessionOnSessionThreadForTesting(const std::string
& id
) {
247 SetThreadLocalSession(make_scoped_ptr(new Session(id
)));
250 } // namespace internal