Roll src/third_party/WebKit bf18a82:a9cee16 (svn 185297:185304)
[chromium-blink-merge.git] / chrome / test / chromedriver / commands.cc
blob5af165d1a41ca7b8235087ea4e0110f31d2e1cae
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"
7 #include <algorithm>
8 #include <list>
9 #include <utility>
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/browser_info.h"
23 #include "chrome/test/chromedriver/chrome/chrome.h"
24 #include "chrome/test/chromedriver/chrome/status.h"
25 #include "chrome/test/chromedriver/logging.h"
26 #include "chrome/test/chromedriver/session.h"
27 #include "chrome/test/chromedriver/session_thread_map.h"
28 #include "chrome/test/chromedriver/util.h"
30 void ExecuteGetStatus(
31 const base::DictionaryValue& params,
32 const std::string& session_id,
33 const CommandCallback& callback) {
34 base::DictionaryValue build;
35 build.SetString("version", "alpha");
37 base::DictionaryValue os;
38 os.SetString("name", base::SysInfo::OperatingSystemName());
39 os.SetString("version", base::SysInfo::OperatingSystemVersion());
40 os.SetString("arch", base::SysInfo::OperatingSystemArchitecture());
42 base::DictionaryValue info;
43 info.Set("build", build.DeepCopy());
44 info.Set("os", os.DeepCopy());
45 callback.Run(
46 Status(kOk), scoped_ptr<base::Value>(info.DeepCopy()), std::string());
49 void ExecuteCreateSession(
50 SessionThreadMap* session_thread_map,
51 const Command& init_session_cmd,
52 const base::DictionaryValue& params,
53 const std::string& session_id,
54 const CommandCallback& callback) {
55 std::string new_id = session_id;
56 if (new_id.empty())
57 new_id = GenerateId();
58 scoped_ptr<Session> session(new Session(new_id));
59 scoped_ptr<base::Thread> thread(new base::Thread(new_id));
60 if (!thread->Start()) {
61 callback.Run(
62 Status(kUnknownError, "failed to start a thread for the new session"),
63 scoped_ptr<base::Value>(),
64 std::string());
65 return;
68 thread->message_loop()->PostTask(
69 FROM_HERE, base::Bind(&SetThreadLocalSession, base::Passed(&session)));
70 session_thread_map
71 ->insert(std::make_pair(new_id, make_linked_ptr(thread.release())));
72 init_session_cmd.Run(params, new_id, callback);
75 namespace {
77 void OnSessionQuit(const base::WeakPtr<size_t>& quit_remaining_count,
78 const base::Closure& all_quit_func,
79 const Status& status,
80 scoped_ptr<base::Value> value,
81 const std::string& session_id) {
82 // |quit_remaining_count| may no longer be valid if a timeout occurred.
83 if (!quit_remaining_count)
84 return;
86 (*quit_remaining_count)--;
87 if (!*quit_remaining_count)
88 all_quit_func.Run();
91 } // namespace
93 void ExecuteQuitAll(
94 const Command& quit_command,
95 SessionThreadMap* session_thread_map,
96 const base::DictionaryValue& params,
97 const std::string& session_id,
98 const CommandCallback& callback) {
99 size_t quit_remaining_count = session_thread_map->size();
100 base::WeakPtrFactory<size_t> weak_ptr_factory(&quit_remaining_count);
101 if (!quit_remaining_count) {
102 callback.Run(Status(kOk), scoped_ptr<base::Value>(), session_id);
103 return;
105 base::RunLoop run_loop;
106 for (SessionThreadMap::const_iterator iter = session_thread_map->begin();
107 iter != session_thread_map->end();
108 ++iter) {
109 quit_command.Run(params,
110 iter->first,
111 base::Bind(&OnSessionQuit,
112 weak_ptr_factory.GetWeakPtr(),
113 run_loop.QuitClosure()));
115 base::MessageLoop::current()->PostDelayedTask(
116 FROM_HERE, run_loop.QuitClosure(), base::TimeDelta::FromSeconds(10));
117 // Uses a nested run loop to block this thread until all the quit
118 // commands have executed, or the timeout expires.
119 base::MessageLoop::current()->SetNestableTasksAllowed(true);
120 run_loop.Run();
121 callback.Run(Status(kOk), scoped_ptr<base::Value>(), session_id);
124 namespace {
126 void TerminateSessionThreadOnCommandThread(SessionThreadMap* session_thread_map,
127 const std::string& session_id) {
128 session_thread_map->erase(session_id);
131 void ExecuteSessionCommandOnSessionThread(
132 const char* command_name,
133 const SessionCommand& command,
134 bool return_ok_without_session,
135 scoped_ptr<base::DictionaryValue> params,
136 scoped_refptr<base::SingleThreadTaskRunner> cmd_task_runner,
137 const CommandCallback& callback_on_cmd,
138 const base::Closure& terminate_on_cmd) {
139 Session* session = GetThreadLocalSession();
140 if (!session) {
141 cmd_task_runner->PostTask(
142 FROM_HERE,
143 base::Bind(callback_on_cmd,
144 Status(return_ok_without_session ? kOk : kNoSuchSession),
145 base::Passed(scoped_ptr<base::Value>()),
146 std::string()));
147 return;
150 if (IsVLogOn(0)) {
151 VLOG(0) << "COMMAND " << command_name << " "
152 << FormatValueForDisplay(*params);
155 // Notify |session|'s |CommandListener|s of the command.
156 // Will mark |session| for deletion if an error is encountered.
157 Status status = NotifyCommandListenersBeforeCommand(session, command_name);
159 // Only run the command if we were able to notify all listeners successfully.
160 // Otherwise, pass error to callback, delete |session|, and do not continue.
161 scoped_ptr<base::Value> value;
162 if (status.IsError()) {
163 LOG(ERROR) << status.message();
164 } else {
165 status = command.Run(session, *params, &value);
167 if (status.IsError() && session->chrome) {
168 if (!session->quit && session->chrome->HasCrashedWebView()) {
169 session->quit = true;
170 std::string message("session deleted because of page crash");
171 if (!session->detach) {
172 Status quit_status = session->chrome->Quit();
173 if (quit_status.IsError())
174 message += ", but failed to kill browser:" + quit_status.message();
176 status = Status(kUnknownError, message, status);
177 } else if (status.code() == kDisconnected) {
178 // Some commands, like clicking a button or link which closes the
179 // window, may result in a kDisconnected error code.
180 std::list<std::string> web_view_ids;
181 Status status_tmp = session->chrome->GetWebViewIds(&web_view_ids);
182 if (status_tmp.IsError() && status_tmp.code() != kChromeNotReachable) {
183 status.AddDetails(
184 "failed to check if window was closed: " + status_tmp.message());
185 } else if (std::find(web_view_ids.begin(),
186 web_view_ids.end(),
187 session->window) == web_view_ids.end()) {
188 status = Status(kOk);
191 if (status.IsError()) {
192 const BrowserInfo* browser_info = session->chrome->GetBrowserInfo();
193 status.AddDetails("Session info: " + browser_info->browser_name + "=" +
194 browser_info->browser_version);
198 if (IsVLogOn(0)) {
199 std::string result;
200 if (status.IsError()) {
201 result = status.message();
202 } else if (value) {
203 result = FormatValueForDisplay(*value);
205 VLOG(0) << "RESPONSE " << command_name
206 << (result.length() ? " " + result : "");
209 if (status.IsOk() && session->auto_reporting_enabled) {
210 std::string message = session->GetFirstBrowserError();
211 if (!message.empty())
212 status = Status(kUnknownError, message);
216 cmd_task_runner->PostTask(
217 FROM_HERE,
218 base::Bind(callback_on_cmd, status, base::Passed(&value), session->id));
220 if (session->quit) {
221 SetThreadLocalSession(scoped_ptr<Session>());
222 delete session;
223 cmd_task_runner->PostTask(FROM_HERE, terminate_on_cmd);
227 } // namespace
229 void ExecuteSessionCommand(
230 SessionThreadMap* session_thread_map,
231 const char* command_name,
232 const SessionCommand& command,
233 bool return_ok_without_session,
234 const base::DictionaryValue& params,
235 const std::string& session_id,
236 const CommandCallback& callback) {
237 SessionThreadMap::iterator iter = session_thread_map->find(session_id);
238 if (iter == session_thread_map->end()) {
239 Status status(return_ok_without_session ? kOk : kNoSuchSession);
240 callback.Run(status, scoped_ptr<base::Value>(), session_id);
241 } else {
242 iter->second->message_loop()
243 ->PostTask(FROM_HERE,
244 base::Bind(&ExecuteSessionCommandOnSessionThread,
245 command_name,
246 command,
247 return_ok_without_session,
248 base::Passed(make_scoped_ptr(params.DeepCopy())),
249 base::MessageLoopProxy::current(),
250 callback,
251 base::Bind(&TerminateSessionThreadOnCommandThread,
252 session_thread_map,
253 session_id)));
257 namespace internal {
259 void CreateSessionOnSessionThreadForTesting(const std::string& id) {
260 SetThreadLocalSession(make_scoped_ptr(new Session(id)));
263 } // namespace internal