[MacViews] Show comboboxes with a native NSMenu
[chromium-blink-merge.git] / chrome / test / chromedriver / commands.cc
blob667d5e99672bc387f7c6b3d20826c3c19974233d
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/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());
47 callback.Run(
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;
58 if (new_id.empty())
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()) {
63 callback.Run(
64 Status(kUnknownError, "failed to start a thread for the new session"),
65 scoped_ptr<base::Value>(),
66 std::string());
67 return;
70 thread->task_runner()->PostTask(
71 FROM_HERE, base::Bind(&SetThreadLocalSession, base::Passed(&session)));
72 session_thread_map
73 ->insert(std::make_pair(new_id, make_linked_ptr(thread.release())));
74 init_session_cmd.Run(params, new_id, callback);
77 namespace {
79 void OnGetSession(const base::WeakPtr<size_t>& session_remaining_count,
80 const base::Closure& all_get_session_func,
81 base::ListValue* session_list,
82 const Status& status,
83 scoped_ptr<base::Value> value,
84 const std::string& session_id) {
86 if (!session_remaining_count)
87 return;
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();
101 } // namespace
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);
115 return;
118 base::RunLoop run_loop;
120 for (SessionThreadMap::const_iterator iter = session_thread_map->begin();
121 iter != session_thread_map->end();
122 ++iter) {
123 session_capabilities_command.Run(params,
124 iter->first,
125 base::Bind(
126 &OnGetSession,
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);
134 run_loop.Run();
136 callback.Run(Status(kOk), session_list.Pass(), session_id);
139 namespace {
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)
148 return;
150 (*quit_remaining_count)--;
151 if (!*quit_remaining_count)
152 all_quit_func.Run();
155 } // namespace
157 void ExecuteQuitAll(
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);
167 return;
169 base::RunLoop run_loop;
170 for (SessionThreadMap::const_iterator iter = session_thread_map->begin();
171 iter != session_thread_map->end();
172 ++iter) {
173 quit_command.Run(params,
174 iter->first,
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);
184 run_loop.Run();
185 callback.Run(Status(kOk), scoped_ptr<base::Value>(), session_id);
188 namespace {
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();
204 if (!session) {
205 cmd_task_runner->PostTask(
206 FROM_HERE,
207 base::Bind(callback_on_cmd,
208 Status(return_ok_without_session ? kOk : kNoSuchSession),
209 base::Passed(scoped_ptr<base::Value>()),
210 std::string()));
211 return;
214 if (IsVLogOn(0)) {
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();
228 } else {
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) {
247 status.AddDetails(
248 "failed to check if window was closed: " + status_tmp.message());
249 } else if (std::find(web_view_ids.begin(),
250 web_view_ids.end(),
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);
262 if (IsVLogOn(0)) {
263 std::string result;
264 if (status.IsError()) {
265 result = status.message();
266 } else if (value) {
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(
281 FROM_HERE,
282 base::Bind(callback_on_cmd, status, base::Passed(&value), session->id));
284 if (session->quit) {
285 SetThreadLocalSession(scoped_ptr<Session>());
286 delete session;
287 cmd_task_runner->PostTask(FROM_HERE, terminate_on_cmd);
291 } // namespace
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);
305 } else {
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)));
316 namespace internal {
318 void CreateSessionOnSessionThreadForTesting(const std::string& id) {
319 SetThreadLocalSession(make_scoped_ptr(new Session(id)));
322 } // namespace internal