OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 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/chromedriver/commands.h" | 5 #include "chrome/test/chromedriver/commands.h" |
6 | 6 |
7 #include <list> | 7 #include <list> |
| 8 #include <utility> |
8 | 9 |
| 10 #include "base/bind.h" |
| 11 #include "base/bind_helpers.h" |
| 12 #include "base/lazy_instance.h" |
9 #include "base/logging.h" // For CHECK macros. | 13 #include "base/logging.h" // For CHECK macros. |
| 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" |
10 #include "base/strings/stringprintf.h" | 18 #include "base/strings/stringprintf.h" |
11 #include "base/sys_info.h" | 19 #include "base/sys_info.h" |
| 20 #include "base/threading/thread_local.h" |
12 #include "base/values.h" | 21 #include "base/values.h" |
13 #include "chrome/test/chromedriver/capabilities.h" | 22 #include "chrome/test/chromedriver/capabilities.h" |
14 #include "chrome/test/chromedriver/chrome/chrome.h" | 23 #include "chrome/test/chromedriver/chrome/chrome.h" |
15 #include "chrome/test/chromedriver/chrome/chrome_android_impl.h" | 24 #include "chrome/test/chromedriver/chrome/chrome_android_impl.h" |
16 #include "chrome/test/chromedriver/chrome/chrome_desktop_impl.h" | 25 #include "chrome/test/chromedriver/chrome/chrome_desktop_impl.h" |
17 #include "chrome/test/chromedriver/chrome/device_manager.h" | 26 #include "chrome/test/chromedriver/chrome/device_manager.h" |
18 #include "chrome/test/chromedriver/chrome/devtools_event_listener.h" | 27 #include "chrome/test/chromedriver/chrome/devtools_event_listener.h" |
19 #include "chrome/test/chromedriver/chrome/status.h" | 28 #include "chrome/test/chromedriver/chrome/status.h" |
20 #include "chrome/test/chromedriver/chrome/version.h" | 29 #include "chrome/test/chromedriver/chrome/version.h" |
21 #include "chrome/test/chromedriver/chrome/web_view.h" | 30 #include "chrome/test/chromedriver/chrome/web_view.h" |
22 #include "chrome/test/chromedriver/chrome_launcher.h" | 31 #include "chrome/test/chromedriver/chrome_launcher.h" |
23 #include "chrome/test/chromedriver/logging.h" | 32 #include "chrome/test/chromedriver/logging.h" |
24 #include "chrome/test/chromedriver/net/net_util.h" | 33 #include "chrome/test/chromedriver/net/net_util.h" |
25 #include "chrome/test/chromedriver/net/url_request_context_getter.h" | 34 #include "chrome/test/chromedriver/net/url_request_context_getter.h" |
26 #include "chrome/test/chromedriver/session.h" | 35 #include "chrome/test/chromedriver/session.h" |
27 #include "chrome/test/chromedriver/session_map.h" | 36 #include "chrome/test/chromedriver/session_thread_map.h" |
28 #include "chrome/test/chromedriver/util.h" | 37 #include "chrome/test/chromedriver/util.h" |
29 | 38 |
30 Status ExecuteGetStatus( | 39 void ExecuteGetStatus( |
31 const base::DictionaryValue& params, | 40 const base::DictionaryValue& params, |
32 const std::string& session_id, | 41 const std::string& session_id, |
33 scoped_ptr<base::Value>* out_value, | 42 const CommandCallback& callback) { |
34 std::string* out_session_id) { | |
35 base::DictionaryValue build; | 43 base::DictionaryValue build; |
36 build.SetString("version", "alpha"); | 44 build.SetString("version", "alpha"); |
37 | 45 |
38 base::DictionaryValue os; | 46 base::DictionaryValue os; |
39 os.SetString("name", base::SysInfo::OperatingSystemName()); | 47 os.SetString("name", base::SysInfo::OperatingSystemName()); |
40 os.SetString("version", base::SysInfo::OperatingSystemVersion()); | 48 os.SetString("version", base::SysInfo::OperatingSystemVersion()); |
41 os.SetString("arch", base::SysInfo::OperatingSystemArchitecture()); | 49 os.SetString("arch", base::SysInfo::OperatingSystemArchitecture()); |
42 | 50 |
43 base::DictionaryValue info; | 51 base::DictionaryValue info; |
44 info.Set("build", build.DeepCopy()); | 52 info.Set("build", build.DeepCopy()); |
45 info.Set("os", os.DeepCopy()); | 53 info.Set("os", os.DeepCopy()); |
46 out_value->reset(info.DeepCopy()); | 54 callback.Run( |
47 return Status(kOk); | 55 Status(kOk), scoped_ptr<base::Value>(info.DeepCopy()), std::string()); |
48 } | 56 } |
49 | 57 |
50 NewSessionParams::NewSessionParams( | 58 NewSessionParams::NewSessionParams( |
51 Log* log, | 59 Log* log, |
52 SessionMap* session_map, | 60 SessionThreadMap* session_thread_map, |
53 scoped_refptr<URLRequestContextGetter> context_getter, | 61 scoped_refptr<URLRequestContextGetter> context_getter, |
54 const SyncWebSocketFactory& socket_factory, | 62 const SyncWebSocketFactory& socket_factory, |
55 DeviceManager* device_manager) | 63 DeviceManager* device_manager) |
56 : log(log), | 64 : log(log), |
57 session_map(session_map), | 65 session_thread_map(session_thread_map), |
58 context_getter(context_getter), | 66 context_getter(context_getter), |
59 socket_factory(socket_factory), | 67 socket_factory(socket_factory), |
60 device_manager(device_manager) {} | 68 device_manager(device_manager) {} |
61 | 69 |
62 NewSessionParams::~NewSessionParams() {} | 70 NewSessionParams::~NewSessionParams() {} |
63 | 71 |
64 Status ExecuteNewSession( | 72 namespace { |
| 73 |
| 74 base::LazyInstance<base::ThreadLocalPointer<Session> > |
| 75 lazy_tls_session = LAZY_INSTANCE_INITIALIZER; |
| 76 |
| 77 Status CreateSessionOnSessionThreadHelper( |
65 const NewSessionParams& bound_params, | 78 const NewSessionParams& bound_params, |
66 const base::DictionaryValue& params, | 79 const base::DictionaryValue& params, |
67 const std::string& session_id, | 80 const std::string& session_id, |
68 scoped_ptr<base::Value>* out_value, | 81 scoped_ptr<base::Value>* out_value) { |
69 std::string* out_session_id) { | |
70 int port; | 82 int port; |
71 if (!FindOpenPort(&port)) | 83 if (!FindOpenPort(&port)) |
72 return Status(kUnknownError, "failed to find an open port for Chrome"); | 84 return Status(kUnknownError, "failed to find an open port for Chrome"); |
73 | 85 |
74 const base::DictionaryValue* desired_caps; | 86 const base::DictionaryValue* desired_caps; |
75 if (!params.GetDictionary("desiredCapabilities", &desired_caps)) | 87 if (!params.GetDictionary("desiredCapabilities", &desired_caps)) |
76 return Status(kUnknownError, "cannot find dict 'desiredCapabilities'"); | 88 return Status(kUnknownError, "cannot find dict 'desiredCapabilities'"); |
77 | 89 |
78 Capabilities capabilities; | 90 Capabilities capabilities; |
79 Status status = capabilities.Parse(*desired_caps); | 91 Status status = capabilities.Parse(*desired_caps); |
(...skipping 21 matching lines...) Expand all Loading... |
101 return status; | 113 return status; |
102 | 114 |
103 std::list<std::string> web_view_ids; | 115 std::list<std::string> web_view_ids; |
104 status = chrome->GetWebViewIds(&web_view_ids); | 116 status = chrome->GetWebViewIds(&web_view_ids); |
105 if (status.IsError() || web_view_ids.empty()) { | 117 if (status.IsError() || web_view_ids.empty()) { |
106 chrome->Quit(); | 118 chrome->Quit(); |
107 return status.IsError() ? status : | 119 return status.IsError() ? status : |
108 Status(kUnknownError, "unable to discover open window in chrome"); | 120 Status(kUnknownError, "unable to discover open window in chrome"); |
109 } | 121 } |
110 | 122 |
| 123 scoped_ptr<Session> session(new Session(session_id, chrome.Pass())); |
| 124 session->devtools_logs.swap(devtools_logs); |
| 125 session->window = web_view_ids.front(); |
| 126 session->detach = capabilities.detach; |
| 127 out_value->reset(session->capabilities->DeepCopy()); |
| 128 lazy_tls_session.Pointer()->Set(session.release()); |
| 129 return Status(kOk); |
| 130 } |
| 131 |
| 132 void CreateSessionOnSessionThread( |
| 133 const scoped_refptr<base::SingleThreadTaskRunner>& cmd_task_runner, |
| 134 const NewSessionParams& bound_params, |
| 135 scoped_ptr<base::DictionaryValue> params, |
| 136 const std::string& session_id, |
| 137 const CommandCallback& callback_on_cmd) { |
| 138 scoped_ptr<base::Value> value; |
| 139 Status status = CreateSessionOnSessionThreadHelper( |
| 140 bound_params, *params, session_id, &value); |
| 141 cmd_task_runner->PostTask( |
| 142 FROM_HERE, |
| 143 base::Bind(callback_on_cmd, status, base::Passed(&value), session_id)); |
| 144 } |
| 145 |
| 146 } // namespace |
| 147 |
| 148 void ExecuteNewSession( |
| 149 const NewSessionParams& bound_params, |
| 150 const base::DictionaryValue& params, |
| 151 const std::string& session_id, |
| 152 const CommandCallback& callback) { |
111 std::string new_id = session_id; | 153 std::string new_id = session_id; |
112 if (new_id.empty()) | 154 if (new_id.empty()) |
113 new_id = GenerateId(); | 155 new_id = GenerateId(); |
114 scoped_ptr<Session> session(new Session(new_id, chrome.Pass())); | 156 scoped_ptr<base::Thread> thread(new base::Thread(new_id.c_str())); |
115 session->devtools_logs.swap(devtools_logs); | 157 if (!thread->Start()) { |
116 if (!session->thread.Start()) { | 158 callback.Run( |
117 chrome->Quit(); | 159 Status(kUnknownError, "failed to start a thread for the new session"), |
118 return Status(kUnknownError, | 160 scoped_ptr<base::Value>(), |
119 "failed to start a thread for the new session"); | 161 std::string()); |
| 162 return; |
120 } | 163 } |
121 session->window = web_view_ids.front(); | |
122 session->detach = capabilities.detach; | |
123 out_value->reset(session->capabilities->DeepCopy()); | |
124 *out_session_id = new_id; | |
125 | 164 |
126 scoped_refptr<SessionAccessor> accessor( | 165 thread->message_loop() |
127 new SessionAccessorImpl(session.Pass())); | 166 ->PostTask(FROM_HERE, |
128 bound_params.session_map->Set(new_id, accessor); | 167 base::Bind(&CreateSessionOnSessionThread, |
129 | 168 base::MessageLoopProxy::current(), |
130 return Status(kOk); | 169 bound_params, |
| 170 base::Passed(make_scoped_ptr(params.DeepCopy())), |
| 171 new_id, |
| 172 callback)); |
| 173 bound_params.session_thread_map |
| 174 ->insert(std::make_pair(new_id, make_linked_ptr(thread.release()))); |
131 } | 175 } |
132 | 176 |
133 Status ExecuteQuit( | 177 namespace { |
134 bool allow_detach, | 178 |
135 SessionMap* session_map, | 179 void OnSessionQuit(const base::WeakPtr<size_t>& quit_remaining_count, |
| 180 const base::Closure& all_quit_func, |
| 181 const Status& status, |
| 182 scoped_ptr<base::Value> value, |
| 183 const std::string& session_id) { |
| 184 // |quit_remaining_count| may no longer be valid if a timeout occurred. |
| 185 if (!quit_remaining_count) |
| 186 return; |
| 187 |
| 188 (*quit_remaining_count)--; |
| 189 if (!*quit_remaining_count) |
| 190 all_quit_func.Run(); |
| 191 } |
| 192 |
| 193 } // namespace |
| 194 |
| 195 void ExecuteQuitAll( |
| 196 const Command& quit_command, |
| 197 SessionThreadMap* session_thread_map, |
136 const base::DictionaryValue& params, | 198 const base::DictionaryValue& params, |
137 const std::string& session_id, | 199 const std::string& session_id, |
138 scoped_ptr<base::Value>* out_value, | 200 const CommandCallback& callback) { |
139 std::string* out_session_id) { | 201 size_t quit_remaining_count = session_thread_map->size(); |
140 *out_session_id = session_id; | 202 base::WeakPtrFactory<size_t> weak_ptr_factory(&quit_remaining_count); |
141 scoped_refptr<SessionAccessor> session_accessor; | 203 if (!quit_remaining_count) { |
142 if (!session_map->Get(session_id, &session_accessor)) | 204 callback.Run(Status(kOk), scoped_ptr<base::Value>(), session_id); |
143 return Status(kOk); | 205 return; |
144 scoped_ptr<base::AutoLock> session_lock; | 206 } |
145 Session* session = session_accessor->Access(&session_lock); | 207 base::RunLoop run_loop; |
146 if (!session) | 208 for (SessionThreadMap::const_iterator iter = session_thread_map->begin(); |
147 return Status(kOk); | 209 iter != session_thread_map->end(); |
148 CHECK(session_map->Remove(session->id)); | 210 ++iter) { |
149 if (allow_detach && session->detach) { | 211 quit_command.Run(params, |
150 session_accessor->DeleteSession(); | 212 iter->first, |
151 return Status(kOk); | 213 base::Bind(&OnSessionQuit, |
152 } else { | 214 weak_ptr_factory.GetWeakPtr(), |
153 Status status = session->chrome->Quit(); | 215 run_loop.QuitClosure())); |
154 session_accessor->DeleteSession(); | 216 } |
155 return status; | 217 base::MessageLoop::current()->PostDelayedTask( |
| 218 FROM_HERE, run_loop.QuitClosure(), base::TimeDelta::FromSeconds(10)); |
| 219 // Uses a nested run loop to block this thread until all the quit |
| 220 // commands have executed, or the timeout expires. |
| 221 base::MessageLoop::current()->SetNestableTasksAllowed(true); |
| 222 run_loop.Run(); |
| 223 callback.Run(Status(kOk), scoped_ptr<base::Value>(), session_id); |
| 224 } |
| 225 |
| 226 namespace { |
| 227 |
| 228 void TerminateSessionThreadOnCommandThread(SessionThreadMap* session_thread_map, |
| 229 const std::string& session_id) { |
| 230 session_thread_map->erase(session_id); |
| 231 } |
| 232 |
| 233 void ExecuteSessionCommandOnSessionThread( |
| 234 const SessionCommand& command, |
| 235 bool return_ok_without_session, |
| 236 scoped_ptr<base::DictionaryValue> params, |
| 237 scoped_refptr<base::SingleThreadTaskRunner> cmd_task_runner, |
| 238 const CommandCallback& callback_on_cmd, |
| 239 const base::Closure& terminate_on_cmd) { |
| 240 Session* session = lazy_tls_session.Pointer()->Get(); |
| 241 if (!session) { |
| 242 cmd_task_runner->PostTask( |
| 243 FROM_HERE, |
| 244 base::Bind(callback_on_cmd, |
| 245 Status(return_ok_without_session ? kOk : kNoSuchSession), |
| 246 base::Passed(scoped_ptr<base::Value>()), |
| 247 std::string())); |
| 248 return; |
| 249 } |
| 250 |
| 251 scoped_ptr<base::Value> value; |
| 252 Status status = command.Run(session, *params, &value); |
| 253 if (status.IsError() && session->chrome) |
| 254 status.AddDetails("Session info: chrome=" + session->chrome->GetVersion()); |
| 255 |
| 256 cmd_task_runner->PostTask( |
| 257 FROM_HERE, |
| 258 base::Bind(callback_on_cmd, status, base::Passed(&value), session->id)); |
| 259 |
| 260 if (session->quit) { |
| 261 lazy_tls_session.Pointer()->Set(NULL); |
| 262 delete session; |
| 263 cmd_task_runner->PostTask(FROM_HERE, terminate_on_cmd); |
156 } | 264 } |
157 } | 265 } |
158 | 266 |
159 Status ExecuteQuitAll( | 267 } // namespace |
160 Command quit_command, | 268 |
161 SessionMap* session_map, | 269 void ExecuteSessionCommand( |
| 270 SessionThreadMap* session_thread_map, |
| 271 const SessionCommand& command, |
| 272 bool return_ok_without_session, |
162 const base::DictionaryValue& params, | 273 const base::DictionaryValue& params, |
163 const std::string& session_id, | 274 const std::string& session_id, |
164 scoped_ptr<base::Value>* out_value, | 275 const CommandCallback& callback) { |
165 std::string* out_session_id) { | 276 SessionThreadMap::iterator iter = session_thread_map->find(session_id); |
166 std::vector<std::string> session_ids; | 277 if (iter == session_thread_map->end()) { |
167 session_map->GetKeys(&session_ids); | 278 Status status(return_ok_without_session ? kOk : kNoSuchSession); |
168 for (size_t i = 0; i < session_ids.size(); ++i) { | 279 callback.Run(status, scoped_ptr<base::Value>(), session_id); |
169 scoped_ptr<base::Value> unused_value; | 280 } else { |
170 std::string unused_session_id; | 281 iter->second->message_loop() |
171 quit_command.Run(params, session_ids[i], &unused_value, &unused_session_id); | 282 ->PostTask(FROM_HERE, |
| 283 base::Bind(&ExecuteSessionCommandOnSessionThread, |
| 284 command, |
| 285 return_ok_without_session, |
| 286 base::Passed(make_scoped_ptr(params.DeepCopy())), |
| 287 base::MessageLoopProxy::current(), |
| 288 callback, |
| 289 base::Bind(&TerminateSessionThreadOnCommandThread, |
| 290 session_thread_map, |
| 291 session_id))); |
172 } | 292 } |
173 return Status(kOk); | |
174 } | 293 } |
| 294 |
| 295 namespace internal { |
| 296 |
| 297 void CreateSessionOnSessionThreadForTesting(const std::string& id) { |
| 298 lazy_tls_session.Pointer()->Set(new Session(id)); |
| 299 } |
| 300 |
| 301 } // namespace internal |
OLD | NEW |