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. 4 5 #include "chrome/test/chromedriver/commands.h" 6 7 #include <algorithm> 8 #include <list> 9 #include <utility> 10 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" 28 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"); 35 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()); 40 41 base::DictionaryValue info; 42 info.Set("build", build.DeepCopy()); 43 info.Set("os", os.DeepCopy()); 44 callback.Run( 45 Status(kOk), scoped_ptr<base::Value>(info.DeepCopy()), std::string()); 46 } 47 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; 55 if (new_id.empty()) 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()) { 60 callback.Run( 61 Status(kUnknownError, "failed to start a thread for the new session"), 62 scoped_ptr<base::Value>(), 63 std::string()); 64 return; 65 } 66 67 thread->message_loop()->PostTask( 68 FROM_HERE, base::Bind(&SetThreadLocalSession, base::Passed(&session))); 69 session_thread_map 70 ->insert(std::make_pair(new_id, make_linked_ptr(thread.release()))); 71 init_session_cmd.Run(params, new_id, callback); 72 } 73 74 namespace { 75 76 void OnSessionQuit(const base::WeakPtr<size_t>& quit_remaining_count, 77 const base::Closure& all_quit_func, 78 const Status& status, 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) 83 return; 84 85 (*quit_remaining_count)--; 86 if (!*quit_remaining_count) 87 all_quit_func.Run(); 88 } 89 90 } // namespace 91 92 void ExecuteQuitAll( 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); 102 return; 103 } 104 base::RunLoop run_loop; 105 for (SessionThreadMap::const_iterator iter = session_thread_map->begin(); 106 iter != session_thread_map->end(); 107 ++iter) { 108 quit_command.Run(params, 109 iter->first, 110 base::Bind(&OnSessionQuit, 111 weak_ptr_factory.GetWeakPtr(), 112 run_loop.QuitClosure())); 113 } 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); 119 run_loop.Run(); 120 callback.Run(Status(kOk), scoped_ptr<base::Value>(), session_id); 121 } 122 123 namespace { 124 125 void TerminateSessionThreadOnCommandThread(SessionThreadMap* session_thread_map, 126 const std::string& session_id) { 127 session_thread_map->erase(session_id); 128 } 129 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(); 139 if (!session) { 140 cmd_task_runner->PostTask( 141 FROM_HERE, 142 base::Bind(callback_on_cmd, 143 Status(return_ok_without_session ? kOk : kNoSuchSession), 144 base::Passed(scoped_ptr<base::Value>()), 145 std::string())); 146 return; 147 } 148 149 if (IsVLogOn(0)) { 150 VLOG(0) << "COMMAND " << command_name << " " 151 << FormatValueForDisplay(*params); 152 } 153 scoped_ptr<base::Value> value; 154 Status status = command.Run(session, *params, &value); 155 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(); 164 } 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) { 172 status.AddDetails( 173 "failed to check if window was closed: " + status_tmp.message()); 174 } else if (std::find(web_view_ids.begin(), 175 web_view_ids.end(), 176 session->window) == web_view_ids.end()) { 177 status = Status(kOk); 178 } 179 } 180 if (status.IsError()) { 181 status.AddDetails( 182 "Session info: chrome=" + session->chrome->GetVersion()); 183 } 184 } 185 186 if (IsVLogOn(0)) { 187 std::string result; 188 if (status.IsError()) { 189 result = status.message(); 190 } else if (value) { 191 result = FormatValueForDisplay(*value); 192 } 193 VLOG(0) << "RESPONSE " << command_name 194 << (result.length() ? " " + result : ""); 195 } 196 197 cmd_task_runner->PostTask( 198 FROM_HERE, 199 base::Bind(callback_on_cmd, status, base::Passed(&value), session->id)); 200 201 if (session->quit) { 202 SetThreadLocalSession(scoped_ptr<Session>()); 203 delete session; 204 cmd_task_runner->PostTask(FROM_HERE, terminate_on_cmd); 205 } 206 } 207 208 } // namespace 209 210 void ExecuteSessionCommand( 211 SessionThreadMap* session_thread_map, 212 const char* command_name, 213 const SessionCommand& command, 214 bool return_ok_without_session, 215 const base::DictionaryValue& params, 216 const std::string& session_id, 217 const CommandCallback& callback) { 218 SessionThreadMap::iterator iter = session_thread_map->find(session_id); 219 if (iter == session_thread_map->end()) { 220 Status status(return_ok_without_session ? kOk : kNoSuchSession); 221 callback.Run(status, scoped_ptr<base::Value>(), session_id); 222 } else { 223 iter->second->message_loop() 224 ->PostTask(FROM_HERE, 225 base::Bind(&ExecuteSessionCommandOnSessionThread, 226 command_name, 227 command, 228 return_ok_without_session, 229 base::Passed(make_scoped_ptr(params.DeepCopy())), 230 base::MessageLoopProxy::current(), 231 callback, 232 base::Bind(&TerminateSessionThreadOnCommandThread, 233 session_thread_map, 234 session_id))); 235 } 236 } 237 238 namespace internal { 239 240 void CreateSessionOnSessionThreadForTesting(const std::string& id) { 241 SetThreadLocalSession(make_scoped_ptr(new Session(id))); 242 } 243 244 } // namespace internal 245