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/location.h"
14 #include "base/logging.h"
15 #include "base/memory/linked_ptr.h"
16 #include "base/message_loop/message_loop.h"
17 #include "base/run_loop.h"
18 #include "base/single_thread_task_runner.h"
19 #include "base/strings/stringprintf.h"
20 #include "base/sys_info.h"
21 #include "base/thread_task_runner_handle.h"
22 #include "base/values.h"
23 #include "chrome/test/chromedriver/capabilities.h"
24 #include "chrome/test/chromedriver/chrome/browser_info.h"
25 #include "chrome/test/chromedriver/chrome/chrome.h"
26 #include "chrome/test/chromedriver/chrome/status.h"
27 #include "chrome/test/chromedriver/logging.h"
28 #include "chrome/test/chromedriver/session.h"
29 #include "chrome/test/chromedriver/session_thread_map.h"
30 #include "chrome/test/chromedriver/util.h"
32 void ExecuteGetStatus(
33 const base::DictionaryValue
& params
,
34 const std::string
& session_id
,
35 const CommandCallback
& callback
) {
36 base::DictionaryValue build
;
37 build
.SetString("version", "alpha");
39 base::DictionaryValue os
;
40 os
.SetString("name", base::SysInfo::OperatingSystemName());
41 os
.SetString("version", base::SysInfo::OperatingSystemVersion());
42 os
.SetString("arch", base::SysInfo::OperatingSystemArchitecture());
44 base::DictionaryValue info
;
45 info
.Set("build", build
.DeepCopy());
46 info
.Set("os", os
.DeepCopy());
48 Status(kOk
), scoped_ptr
<base::Value
>(info
.DeepCopy()), std::string());
51 void ExecuteCreateSession(
52 SessionThreadMap
* session_thread_map
,
53 const Command
& init_session_cmd
,
54 const base::DictionaryValue
& params
,
55 const std::string
& session_id
,
56 const CommandCallback
& callback
) {
57 std::string new_id
= session_id
;
59 new_id
= GenerateId();
60 scoped_ptr
<Session
> session(new Session(new_id
));
61 scoped_ptr
<base::Thread
> thread(new base::Thread(new_id
));
62 if (!thread
->Start()) {
64 Status(kUnknownError
, "failed to start a thread for the new session"),
65 scoped_ptr
<base::Value
>(),
70 thread
->task_runner()->PostTask(
71 FROM_HERE
, base::Bind(&SetThreadLocalSession
, base::Passed(&session
)));
73 ->insert(std::make_pair(new_id
, make_linked_ptr(thread
.release())));
74 init_session_cmd
.Run(params
, new_id
, callback
);
79 void OnGetSession(const base::WeakPtr
<size_t>& session_remaining_count
,
80 const base::Closure
& all_get_session_func
,
81 base::ListValue
* session_list
,
83 scoped_ptr
<base::Value
> value
,
84 const std::string
& session_id
) {
86 if (!session_remaining_count
)
89 (*session_remaining_count
)--;
91 scoped_ptr
<base::DictionaryValue
> session(new base::DictionaryValue());
92 session
->Set("sessionId", new base::StringValue(session_id
));
93 session
->Set("capabilities", value
->DeepCopy());
94 session_list
->Append(session
.release());
96 if (!*session_remaining_count
) {
97 all_get_session_func
.Run();
103 void ExecuteGetSessions(const Command
& session_capabilities_command
,
104 SessionThreadMap
* session_thread_map
,
105 const base::DictionaryValue
& params
,
106 const std::string
& session_id
,
107 const CommandCallback
& callback
) {
109 size_t get_remaining_count
= session_thread_map
->size();
110 base::WeakPtrFactory
<size_t> weak_ptr_factory(&get_remaining_count
);
111 scoped_ptr
<base::ListValue
> session_list(new base::ListValue());
113 if (!get_remaining_count
) {
114 callback
.Run(Status(kOk
), session_list
.Pass(), session_id
);
118 base::RunLoop run_loop
;
120 for (SessionThreadMap::const_iterator iter
= session_thread_map
->begin();
121 iter
!= session_thread_map
->end();
123 session_capabilities_command
.Run(params
,
127 weak_ptr_factory
.GetWeakPtr(),
128 run_loop
.QuitClosure(),
129 session_list
.get()));
131 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
132 FROM_HERE
, run_loop
.QuitClosure(), base::TimeDelta::FromSeconds(10));
133 base::MessageLoop::current()->SetNestableTasksAllowed(true);
136 callback
.Run(Status(kOk
), session_list
.Pass(), session_id
);
141 void OnSessionQuit(const base::WeakPtr
<size_t>& quit_remaining_count
,
142 const base::Closure
& all_quit_func
,
143 const Status
& status
,
144 scoped_ptr
<base::Value
> value
,
145 const std::string
& session_id
) {
146 // |quit_remaining_count| may no longer be valid if a timeout occurred.
147 if (!quit_remaining_count
)
150 (*quit_remaining_count
)--;
151 if (!*quit_remaining_count
)
158 const Command
& quit_command
,
159 SessionThreadMap
* session_thread_map
,
160 const base::DictionaryValue
& params
,
161 const std::string
& session_id
,
162 const CommandCallback
& callback
) {
163 size_t quit_remaining_count
= session_thread_map
->size();
164 base::WeakPtrFactory
<size_t> weak_ptr_factory(&quit_remaining_count
);
165 if (!quit_remaining_count
) {
166 callback
.Run(Status(kOk
), scoped_ptr
<base::Value
>(), session_id
);
169 base::RunLoop run_loop
;
170 for (SessionThreadMap::const_iterator iter
= session_thread_map
->begin();
171 iter
!= session_thread_map
->end();
173 quit_command
.Run(params
,
175 base::Bind(&OnSessionQuit
,
176 weak_ptr_factory
.GetWeakPtr(),
177 run_loop
.QuitClosure()));
179 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
180 FROM_HERE
, run_loop
.QuitClosure(), base::TimeDelta::FromSeconds(10));
181 // Uses a nested run loop to block this thread until all the quit
182 // commands have executed, or the timeout expires.
183 base::MessageLoop::current()->SetNestableTasksAllowed(true);
185 callback
.Run(Status(kOk
), scoped_ptr
<base::Value
>(), session_id
);
190 void TerminateSessionThreadOnCommandThread(SessionThreadMap
* session_thread_map
,
191 const std::string
& session_id
) {
192 session_thread_map
->erase(session_id
);
195 void ExecuteSessionCommandOnSessionThread(
196 const char* command_name
,
197 const SessionCommand
& command
,
198 bool return_ok_without_session
,
199 scoped_ptr
<base::DictionaryValue
> params
,
200 scoped_refptr
<base::SingleThreadTaskRunner
> cmd_task_runner
,
201 const CommandCallback
& callback_on_cmd
,
202 const base::Closure
& terminate_on_cmd
) {
203 Session
* session
= GetThreadLocalSession();
205 cmd_task_runner
->PostTask(
207 base::Bind(callback_on_cmd
,
208 Status(return_ok_without_session
? kOk
: kNoSuchSession
),
209 base::Passed(scoped_ptr
<base::Value
>()),
215 VLOG(0) << "COMMAND " << command_name
<< " "
216 << FormatValueForDisplay(*params
);
219 // Notify |session|'s |CommandListener|s of the command.
220 // Will mark |session| for deletion if an error is encountered.
221 Status status
= NotifyCommandListenersBeforeCommand(session
, command_name
);
223 // Only run the command if we were able to notify all listeners successfully.
224 // Otherwise, pass error to callback, delete |session|, and do not continue.
225 scoped_ptr
<base::Value
> value
;
226 if (status
.IsError()) {
227 LOG(ERROR
) << status
.message();
229 status
= command
.Run(session
, *params
, &value
);
231 if (status
.IsError() && session
->chrome
) {
232 if (!session
->quit
&& session
->chrome
->HasCrashedWebView()) {
233 session
->quit
= true;
234 std::string
message("session deleted because of page crash");
235 if (!session
->detach
) {
236 Status quit_status
= session
->chrome
->Quit();
237 if (quit_status
.IsError())
238 message
+= ", but failed to kill browser:" + quit_status
.message();
240 status
= Status(kUnknownError
, message
, status
);
241 } else if (status
.code() == kDisconnected
) {
242 // Some commands, like clicking a button or link which closes the
243 // window, may result in a kDisconnected error code.
244 std::list
<std::string
> web_view_ids
;
245 Status status_tmp
= session
->chrome
->GetWebViewIds(&web_view_ids
);
246 if (status_tmp
.IsError() && status_tmp
.code() != kChromeNotReachable
) {
248 "failed to check if window was closed: " + status_tmp
.message());
249 } else if (std::find(web_view_ids
.begin(),
251 session
->window
) == web_view_ids
.end()) {
252 status
= Status(kOk
);
255 if (status
.IsError()) {
256 const BrowserInfo
* browser_info
= session
->chrome
->GetBrowserInfo();
257 status
.AddDetails("Session info: " + browser_info
->browser_name
+ "=" +
258 browser_info
->browser_version
);
264 if (status
.IsError()) {
265 result
= status
.message();
267 result
= FormatValueForDisplay(*value
);
269 VLOG(0) << "RESPONSE " << command_name
270 << (result
.length() ? " " + result
: "");
273 if (status
.IsOk() && session
->auto_reporting_enabled
) {
274 std::string message
= session
->GetFirstBrowserError();
275 if (!message
.empty())
276 status
= Status(kUnknownError
, message
);
280 cmd_task_runner
->PostTask(
282 base::Bind(callback_on_cmd
, status
, base::Passed(&value
), session
->id
));
285 SetThreadLocalSession(scoped_ptr
<Session
>());
287 cmd_task_runner
->PostTask(FROM_HERE
, terminate_on_cmd
);
293 void ExecuteSessionCommand(
294 SessionThreadMap
* session_thread_map
,
295 const char* command_name
,
296 const SessionCommand
& command
,
297 bool return_ok_without_session
,
298 const base::DictionaryValue
& params
,
299 const std::string
& session_id
,
300 const CommandCallback
& callback
) {
301 SessionThreadMap::iterator iter
= session_thread_map
->find(session_id
);
302 if (iter
== session_thread_map
->end()) {
303 Status
status(return_ok_without_session
? kOk
: kNoSuchSession
);
304 callback
.Run(status
, scoped_ptr
<base::Value
>(), session_id
);
306 iter
->second
->task_runner()->PostTask(
307 FROM_HERE
, base::Bind(&ExecuteSessionCommandOnSessionThread
,
308 command_name
, command
, return_ok_without_session
,
309 base::Passed(make_scoped_ptr(params
.DeepCopy())),
310 base::ThreadTaskRunnerHandle::Get(), callback
,
311 base::Bind(&TerminateSessionThreadOnCommandThread
,
312 session_thread_map
, session_id
)));
318 void CreateSessionOnSessionThreadForTesting(const std::string
& id
) {
319 SetThreadLocalSession(make_scoped_ptr(new Session(id
)));
322 } // namespace internal