OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2010 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/webdriver/session_manager.h" |
| 6 |
| 7 #ifdef OS_POSIX |
| 8 #include <unistd.h> |
| 9 #endif |
| 10 #ifdef OS_WIN |
| 11 #include <windows.h> |
| 12 #include <shellapi.h> |
| 13 #endif |
| 14 |
| 15 #include <stdlib.h> |
| 16 #include <vector> |
| 17 |
| 18 #include "base/command_line.h" |
| 19 #include "base/logging.h" |
| 20 #include "base/process.h" |
| 21 #include "base/process_util.h" |
| 22 #include "base/string_util.h" |
| 23 #include "base/json/json_reader.h" |
| 24 #include "base/json/json_writer.h" |
| 25 |
| 26 #include "chrome/app/chrome_dll_resource.h" |
| 27 #include "chrome/common/chrome_constants.h" |
| 28 #include "chrome/common/chrome_switches.h" |
| 29 #include "chrome/test/webdriver/utility_functions.h" |
| 30 |
| 31 #include "third_party/webdriver/atoms.h" |
| 32 |
| 33 namespace webdriver { |
| 34 |
| 35 #ifdef OS_WIN |
| 36 namespace { |
| 37 std::string GetTempPath() { |
| 38 DWORD result = ::GetTempPath(0, L""); |
| 39 if (result == 0) |
| 40 LOG(ERROR) << "Could not get system temp path" << std::endl; |
| 41 |
| 42 std::vector<TCHAR> tempPath(result + 1); |
| 43 result = ::GetTempPath(static_cast<DWORD>(tempPath.size()), &tempPath[0]); |
| 44 if ((result == 0) || (result >= tempPath.size())) { |
| 45 LOG(ERROR) << "Could not get system temp path" << std::endl; |
| 46 NOTREACHED(); |
| 47 } |
| 48 return std::string(tempPath.begin(), |
| 49 tempPath.begin() + static_cast<std::size_t>(result)); |
| 50 } |
| 51 } // namespace |
| 52 #endif |
| 53 |
| 54 Session::Session(const std::string& id, AutomationProxy* proxy) |
| 55 : id_(id), proxy_(proxy), process_(base::kNullProcessHandle), |
| 56 window_num_(0), implicit_wait_(0), current_frame_xpath_(L"") { |
| 57 } |
| 58 |
| 59 bool Session::Init(base::ProcessHandle process_handle) { |
| 60 CHECK(process_.handle() == base::kNullProcessHandle) |
| 61 << "Session has already been initialized"; |
| 62 process_.set_handle(process_handle); |
| 63 |
| 64 if (!proxy_->WaitForInitialLoads()) { |
| 65 LOG(WARNING) << "Failed to wait for initial loads" << std::endl; |
| 66 return false; |
| 67 } |
| 68 |
| 69 if (!WaitForLaunch()) { |
| 70 return false; |
| 71 } |
| 72 |
| 73 return LoadProxies(); |
| 74 } |
| 75 |
| 76 scoped_refptr<WindowProxy> Session::GetWindow() { |
| 77 return ActivateTab() ? browser_->GetWindow() : NULL; |
| 78 } |
| 79 |
| 80 scoped_refptr<TabProxy> Session::ActivateTab() { |
| 81 int tab_index; |
| 82 if (!tab_->GetTabIndex(&tab_index) || |
| 83 !browser_->ActivateTab(tab_index)) { |
| 84 LOG(ERROR) << "Failed to session tab"; |
| 85 return NULL; |
| 86 } |
| 87 return tab_; |
| 88 } |
| 89 |
| 90 bool Session::WaitForLaunch() { |
| 91 AutomationLaunchResult result = proxy_->WaitForAppLaunch(); |
| 92 if (result == AUTOMATION_SUCCESS) { |
| 93 LOG(INFO) << "Automation setup is a success" << std::endl; |
| 94 } else if (result == AUTOMATION_TIMEOUT) { |
| 95 LOG(WARNING) << "Timeout, automation setup failed" << std::endl; |
| 96 return false; |
| 97 } else { |
| 98 LOG(WARNING) << "Failure in automation setup" << std::endl; |
| 99 return false; |
| 100 } |
| 101 |
| 102 LOG(INFO) << proxy_->channel_id() << std::endl; |
| 103 if (!proxy_->OpenNewBrowserWindow(Browser::TYPE_NORMAL, true)) { |
| 104 LOG(WARNING) << "Failed to open a new browser window" << std::endl; |
| 105 return false; |
| 106 } |
| 107 return true; |
| 108 } |
| 109 |
| 110 bool Session::LoadProxies() { |
| 111 scoped_refptr<BrowserProxy> browser = proxy_->GetBrowserWindow(0); |
| 112 if (!browser.get()) { |
| 113 LOG(WARNING) << "Failed to get browser window."; |
| 114 return false; |
| 115 } |
| 116 |
| 117 scoped_refptr<TabProxy> tab = browser->GetActiveTab(); |
| 118 if (!tab->NavigateToURL(GURL("about://config/"))) { |
| 119 LOG(WARNING) << "Could not navigate to about://config/" << std::endl; |
| 120 return false; |
| 121 } |
| 122 |
| 123 SetBrowserAndTab(0, browser, tab); |
| 124 return true; |
| 125 } |
| 126 |
| 127 void Session::SetBrowserAndTab(const int window_num, |
| 128 const scoped_refptr<BrowserProxy>& browser, |
| 129 const scoped_refptr<TabProxy>& tab) { |
| 130 window_num_ = window_num; |
| 131 browser_ = browser; |
| 132 tab_ = tab; |
| 133 current_frame_xpath_ = L""; |
| 134 |
| 135 int tab_num; |
| 136 LOG_IF(WARNING, !tab->GetTabIndex(&tab_num) || !browser->ActivateTab(tab_num)) |
| 137 << "Failed to activate tab"; |
| 138 } |
| 139 |
| 140 bool Session::CreateTemporaryProfileDirectory() { |
| 141 memset(tmp_profile_dir_, 0, sizeof tmp_profile_dir_); |
| 142 #ifdef OS_POSIX |
| 143 strncat(tmp_profile_dir_, "/tmp/webdriverXXXXXX", sizeof tmp_profile_dir_); |
| 144 if (mkdtemp(tmp_profile_dir_) == NULL) { |
| 145 LOG(ERROR) << "mkdtemp failed"; |
| 146 return false; |
| 147 } |
| 148 #elif OS_WIN |
| 149 DWORD ret; |
| 150 ProfileDir temp_dir; |
| 151 |
| 152 ret = GetTempPathA(sizeof temp_dir, temp_dir); |
| 153 if (ret == 0 || ret > sizeof temp_dir) { |
| 154 LOG(ERROR) << "Could not find the temp directory" << std::endl; |
| 155 return false; |
| 156 } |
| 157 |
| 158 ret = GetTempFileNameA(temp_dir, // directory for tmp files |
| 159 "webdriver", // temp file name prefix |
| 160 static_cast<int>(time(NULL)) % 65535 + 1, |
| 161 tmp_profile_dir_); // buffer for name |
| 162 |
| 163 if (ret ==0) { |
| 164 LOG(ERROR) << "Could not generate temp directory name" << std::endl; |
| 165 return false; |
| 166 } |
| 167 |
| 168 if (!CreateDirectoryA(tmp_profile_dir_, NULL)) { |
| 169 DWORD dw = GetLastError(); |
| 170 LOG(ERROR) << "Error code: " << dw << std::endl; |
| 171 return false; |
| 172 } |
| 173 #endif |
| 174 LOG(INFO) << "Using temporary profile directory: " << tmp_profile_dir_; |
| 175 return true; |
| 176 } |
| 177 |
| 178 #ifdef OS_WIN |
| 179 bool DeleteDirectory(const char* directory) { |
| 180 SHFILEOPSTRUCT fileop; |
| 181 size_t convertedChars; |
| 182 int len = strlen(directory); |
| 183 wchar_t *pszFrom = new wchar_t[len+2]; |
| 184 memset(&fileop, 0, sizeof fileop); |
| 185 memset(pszFrom, 0, sizeof pszFrom); |
| 186 |
| 187 mbstowcs_s(&convertedChars, pszFrom, len+2, directory, len); |
| 188 fileop.wFunc = FO_DELETE; // delete operation |
| 189 // source file name as double null terminated string |
| 190 fileop.pFrom = (LPCWSTR) pszFrom; |
| 191 fileop.fFlags = FOF_NOCONFIRMATION|FOF_SILENT; // do not prompt the user |
| 192 fileop.fAnyOperationsAborted = FALSE; |
| 193 |
| 194 int ret = SHFileOperation(&fileop); |
| 195 delete pszFrom; |
| 196 return (ret == 0); |
| 197 } |
| 198 #endif |
| 199 |
| 200 void Session::Terminate() { |
| 201 if (!proxy_->SetFilteredInet(false)) { |
| 202 LOG(ERROR) << "Error in closing down session" << std::endl; |
| 203 } |
| 204 |
| 205 #ifdef OS_WIN |
| 206 if (!DeleteDirectory(tmp_profile_dir_)) { |
| 207 LOG(ERROR) << "Could not clean up temp directory: " |
| 208 << tmp_profile_dir_ << std::endl; |
| 209 } |
| 210 #else |
| 211 if (rmdir(tmp_profile_dir_)) { |
| 212 LOG(ERROR) << "Could not clean up temp directory: " |
| 213 << tmp_profile_dir_ << std::endl; |
| 214 } |
| 215 #endif |
| 216 process_.Terminate(EXIT_SUCCESS); |
| 217 } |
| 218 |
| 219 ErrorCode Session::ExecuteScript(const std::wstring& script, |
| 220 const ListValue* const args, |
| 221 Value** value) { |
| 222 std::string args_as_json; |
| 223 base::JSONWriter::Write(static_cast<const Value* const>(args), |
| 224 /*pretty_print=*/false, |
| 225 &args_as_json); |
| 226 |
| 227 std::wstring jscript = L"window.domAutomationController.send("; |
| 228 jscript.append(L"(function(){") |
| 229 // Every injected script is fed through the executeScript atom. This atom |
| 230 // will catch any errors that are thrown and convert them to the |
| 231 // appropriate JSON structure. |
| 232 .append(build_atom(EXECUTE_SCRIPT, sizeof EXECUTE_SCRIPT)) |
| 233 .append(L"var result = executeScript(function(){") |
| 234 .append(script) |
| 235 .append(L"},") |
| 236 .append(ASCIIToWide(args_as_json)) |
| 237 .append(L");return JSON.stringify(result);})());"); |
| 238 |
| 239 // Should we also log the script that's being executed? It could be several KB |
| 240 // in size and will add lots of noise to the logs. |
| 241 LOG(INFO) << "Executing script in frame: " << current_frame_xpath_; |
| 242 |
| 243 std::wstring result; |
| 244 scoped_refptr<TabProxy> tab = ActivateTab(); |
| 245 if (!tab->ExecuteAndExtractString(current_frame_xpath_, jscript, &result)) { |
| 246 *value = Value::CreateStringValue( |
| 247 "Unknown internal script execution failure"); |
| 248 return kUnknownError; |
| 249 } |
| 250 |
| 251 LOG(INFO) << "...script result: " << result; |
| 252 std::string temp = WideToASCII(result); |
| 253 scoped_ptr<Value> r(base::JSONReader::ReadAndReturnError( |
| 254 temp, true, NULL, NULL)); |
| 255 if (!r.get()) { |
| 256 *value = Value::CreateStringValue( |
| 257 "Internal script execution error: failed to parse script result"); |
| 258 return kUnknownError; |
| 259 } |
| 260 |
| 261 if (r->GetType() != Value::TYPE_DICTIONARY) { |
| 262 std::ostringstream stream; |
| 263 stream << "Internal script execution error: script result must be a " |
| 264 << print_valuetype(Value::TYPE_DICTIONARY) |
| 265 << ", but was " |
| 266 << print_valuetype(r->GetType()) << ": " << result; |
| 267 *value = Value::CreateStringValue(stream.str()); |
| 268 return kUnknownError; |
| 269 } |
| 270 |
| 271 DictionaryValue* result_dict = static_cast<DictionaryValue*>(r.get()); |
| 272 |
| 273 Value* tmp; |
| 274 if (result_dict->Get("value", &tmp)) { |
| 275 // result_dict owns the returned value, so we need to make a copy. |
| 276 *value = tmp->DeepCopy(); |
| 277 } else { |
| 278 // "value" was not defined in the returned dictionary, set to null. |
| 279 *value = Value::CreateNullValue(); |
| 280 } |
| 281 |
| 282 int status; |
| 283 if (!result_dict->GetInteger("status", &status)) { |
| 284 NOTREACHED() << "...script did not return a status flag."; |
| 285 } |
| 286 return static_cast<ErrorCode>(status); |
| 287 } |
| 288 } // namespace webdriver |
OLD | NEW |