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_frame/test_utils.h" 6 7 #include <atlbase.h> 8 #include <atlwin.h> 9 #include <shellapi.h> 10 11 #include <algorithm> 12 13 #include "base/command_line.h" 14 #include "base/file_util.h" 15 #include "base/files/file_path.h" 16 #include "base/logging.h" 17 #include "base/path_service.h" 18 #include "base/process/kill.h" 19 #include "base/process/launch.h" 20 #include "base/strings/stringprintf.h" 21 #include "base/strings/utf_string_conversions.h" 22 #include "base/win/scoped_handle.h" 23 #include "chrome/common/chrome_paths.h" 24 #include "chrome/common/chrome_switches.h" 25 #include "chrome_frame/test/chrome_frame_test_utils.h" 26 #include "testing/gtest/include/gtest/gtest.h" 27 28 const wchar_t kChromeFrameDllName[] = L"npchrome_frame.dll"; 29 const wchar_t kChromeLauncherExeName[] = L"chrome_launcher.exe"; 30 // How long to wait for DLLs to register or unregister themselves before killing 31 // the registrar. 32 const int64 kDllRegistrationTimeoutMs = 30 * 1000; 33 34 base::FilePath GetChromeFrameBuildPath() { 35 base::FilePath build_path; 36 PathService::Get(chrome::DIR_APP, &build_path); 37 38 base::FilePath dll_path = build_path.Append(kChromeFrameDllName); 39 40 if (!base::PathExists(dll_path)) { 41 // Well, dang.. try looking in the current directory. 42 dll_path = build_path.Append(kChromeFrameDllName); 43 } 44 45 if (!base::PathExists(dll_path)) { 46 // No luck, return something empty. 47 dll_path = base::FilePath(); 48 } 49 50 return dll_path; 51 } 52 53 const wchar_t ScopedChromeFrameRegistrar::kCallRegistrationEntrypointSwitch[] = 54 L"--call-registration-entrypoint"; 55 56 bool ScopedChromeFrameRegistrar::register_chrome_path_provider_ = false; 57 58 // static 59 void ScopedChromeFrameRegistrar::RegisterDefaults() { 60 if (!register_chrome_path_provider_) { 61 chrome::RegisterPathProvider(); 62 register_chrome_path_provider_ = true; 63 } 64 } 65 66 // Registers or unregisters the DLL at |path| by calling out to the current 67 // executable with --call-registration-entrypoint. Loading the DLL into the 68 // test process is problematic for component=shared_library builds since 69 // singletons in base.dll aren't designed to handle multiple initialization. 70 // Use of rundll32.exe is problematic since it does not return useful error 71 // information. 72 // static 73 void ScopedChromeFrameRegistrar::DoRegistration( 74 const string16& path, 75 RegistrationType registration_type, 76 RegistrationOperation registration_operation) { 77 static const char* const kEntrypoints[] = { 78 "DllRegisterServer", 79 "DllUnregisterServer", 80 "DllRegisterUserServer", 81 "DllUnregisterUserServer", 82 }; 83 84 DCHECK(!path.empty()); 85 DCHECK(registration_type == PER_USER || registration_type == SYSTEM_LEVEL); 86 DCHECK(registration_operation == REGISTER || 87 registration_operation == UNREGISTER); 88 89 int entrypoint_index = 0; 90 base::LaunchOptions launch_options; 91 base::ProcessHandle process_handle = INVALID_HANDLE_VALUE; 92 int exit_code = -1; 93 94 if (registration_type == PER_USER) 95 entrypoint_index += 2; 96 if (registration_operation == UNREGISTER) 97 entrypoint_index += 1; 98 string16 registration_command(ASCIIToUTF16("\"")); 99 registration_command += 100 CommandLine::ForCurrentProcess()->GetProgram().value(); 101 registration_command += ASCIIToUTF16("\" "); 102 registration_command += kCallRegistrationEntrypointSwitch; 103 registration_command += ASCIIToUTF16(" \""); 104 registration_command += path; 105 registration_command += ASCIIToUTF16("\" "); 106 registration_command += ASCIIToUTF16(kEntrypoints[entrypoint_index]); 107 launch_options.wait = true; 108 if (!base::LaunchProcess(registration_command, launch_options, 109 &process_handle)) { 110 PLOG(FATAL) 111 << "Failed to register or unregister DLL with command: " 112 << registration_command; 113 } else { 114 base::win::ScopedHandle rundll32(process_handle); 115 if (!base::WaitForExitCodeWithTimeout( 116 process_handle, &exit_code, 117 base::TimeDelta::FromMilliseconds(kDllRegistrationTimeoutMs))) { 118 LOG(ERROR) << "Timeout waiting to register or unregister DLL with " 119 "command: " << registration_command; 120 base::KillProcess(process_handle, 0, false); 121 NOTREACHED() << "Aborting test due to registration failure."; 122 } 123 } 124 if (exit_code != 0) { 125 if (registration_operation == REGISTER) { 126 LOG(ERROR) 127 << "DLL registration failed (exit code: 0x" << std::hex << exit_code 128 << ", command: " << registration_command 129 << "). Make sure you are running as Admin."; 130 ::ExitProcess(1); 131 } else { 132 LOG(WARNING) 133 << "DLL unregistration failed (exit code: 0x" << std::hex << exit_code 134 << ", command: " << registration_command << ")."; 135 } 136 } 137 } 138 139 // static 140 void ScopedChromeFrameRegistrar::RegisterAtPath( 141 const std::wstring& path, RegistrationType registration_type) { 142 DoRegistration(path, registration_type, REGISTER); 143 } 144 145 // static 146 void ScopedChromeFrameRegistrar::UnregisterAtPath( 147 const std::wstring& path, RegistrationType registration_type) { 148 DoRegistration(path, registration_type, UNREGISTER); 149 } 150 151 base::FilePath ScopedChromeFrameRegistrar::GetReferenceChromeFrameDllPath() { 152 base::FilePath reference_build_dir; 153 PathService::Get(chrome::DIR_APP, &reference_build_dir); 154 155 reference_build_dir = reference_build_dir.DirName(); 156 reference_build_dir = reference_build_dir.DirName(); 157 158 reference_build_dir = reference_build_dir.AppendASCII("chrome_frame"); 159 reference_build_dir = reference_build_dir.AppendASCII("tools"); 160 reference_build_dir = reference_build_dir.AppendASCII("test"); 161 reference_build_dir = reference_build_dir.AppendASCII("reference_build"); 162 reference_build_dir = reference_build_dir.AppendASCII("chrome"); 163 reference_build_dir = reference_build_dir.AppendASCII("servers"); 164 reference_build_dir = reference_build_dir.Append(kChromeFrameDllName); 165 return reference_build_dir; 166 } 167 168 // static 169 void ScopedChromeFrameRegistrar::RegisterAndExitProcessIfDirected() { 170 // This method is invoked before any Chromium helpers have been initialized. 171 // Take pains to use only Win32 and CRT functions. 172 int argc = 0; 173 const wchar_t* const* argv = ::CommandLineToArgvW(::GetCommandLine(), &argc); 174 if (argc < 2 || ::lstrcmp(argv[1], kCallRegistrationEntrypointSwitch) != 0) 175 return; 176 if (argc != 4) { 177 printf("Usage: %S %S <path to dll> <entrypoint>\n", argv[0], 178 kCallRegistrationEntrypointSwitch); 179 return; 180 } 181 182 // The only way to leave from here on down is ExitProcess. 183 const wchar_t* dll_path = argv[2]; 184 const wchar_t* wide_entrypoint = argv[3]; 185 char entrypoint[256]; 186 HRESULT exit_code = 0; 187 int entrypoint_len = lstrlen(wide_entrypoint); 188 if (entrypoint_len <= 0 || entrypoint_len >= arraysize(entrypoint)) { 189 exit_code = HRESULT_FROM_WIN32(ERROR_PROC_NOT_FOUND); 190 } else { 191 // Convert wide to narrow. Since the entrypoint must be a narrow string 192 // anyway, it is safe to truncate each character like this. 193 std::copy(wide_entrypoint, wide_entrypoint + entrypoint_len + 1, 194 &entrypoint[0]); 195 HMODULE dll_module = ::LoadLibrary(dll_path); 196 if (dll_module == NULL) { 197 exit_code = HRESULT_FROM_WIN32(::GetLastError()); 198 } else { 199 typedef HRESULT (STDAPICALLTYPE *RegisterFp)(); 200 RegisterFp register_func = 201 reinterpret_cast<RegisterFp>(::GetProcAddress(dll_module, 202 entrypoint)); 203 if (register_func == NULL) { 204 exit_code = HRESULT_FROM_WIN32(::GetLastError()); 205 } else { 206 exit_code = register_func(); 207 } 208 ::FreeLibrary(dll_module); 209 } 210 } 211 212 ::ExitProcess(exit_code); 213 } 214 215 // Non-statics 216 217 ScopedChromeFrameRegistrar::ScopedChromeFrameRegistrar( 218 const std::wstring& path, RegistrationType registration_type) 219 : registration_type_(registration_type) { 220 if (!register_chrome_path_provider_) { 221 // Register paths needed by the ScopedChromeFrameRegistrar. 222 chrome::RegisterPathProvider(); 223 register_chrome_path_provider_ = true; 224 } 225 original_dll_path_ = path; 226 RegisterChromeFrameAtPath(original_dll_path_); 227 } 228 229 ScopedChromeFrameRegistrar::ScopedChromeFrameRegistrar( 230 RegistrationType registration_type) 231 : registration_type_(registration_type) { 232 if (!register_chrome_path_provider_) { 233 // Register paths needed by the ScopedChromeFrameRegistrar. 234 chrome::RegisterPathProvider(); 235 register_chrome_path_provider_ = true; 236 } 237 original_dll_path_ = GetChromeFrameBuildPath().value(); 238 RegisterChromeFrameAtPath(original_dll_path_); 239 } 240 241 ScopedChromeFrameRegistrar::~ScopedChromeFrameRegistrar() { 242 if (base::FilePath(original_dll_path_) != 243 base::FilePath(new_chrome_frame_dll_path_)) { 244 RegisterChromeFrameAtPath(original_dll_path_); 245 } else if (registration_type_ == PER_USER) { 246 UnregisterAtPath(new_chrome_frame_dll_path_, registration_type_); 247 HWND chrome_frame_helper_window = 248 FindWindow(L"ChromeFrameHelperWindowClass", NULL); 249 if (IsWindow(chrome_frame_helper_window)) { 250 PostMessage(chrome_frame_helper_window, WM_CLOSE, 0, 0); 251 } else { 252 base::KillProcesses(L"chrome_frame_helper.exe", 0, NULL); 253 } 254 } 255 } 256 257 void ScopedChromeFrameRegistrar::RegisterChromeFrameAtPath( 258 const std::wstring& path) { 259 RegisterAtPath(path, registration_type_); 260 new_chrome_frame_dll_path_ = path; 261 } 262 263 void ScopedChromeFrameRegistrar::RegisterReferenceChromeFrameBuild() { 264 RegisterChromeFrameAtPath(GetReferenceChromeFrameDllPath().value()); 265 } 266 267 std::wstring ScopedChromeFrameRegistrar::GetChromeFrameDllPath() const { 268 return new_chrome_frame_dll_path_; 269 } 270 271 bool IsWorkstationLocked() { 272 bool is_locked = true; 273 HDESK input_desk = ::OpenInputDesktop(0, 0, GENERIC_READ); 274 if (input_desk) { 275 wchar_t name[256] = {0}; 276 DWORD needed = 0; 277 if (::GetUserObjectInformation(input_desk, 278 UOI_NAME, 279 name, 280 sizeof(name), 281 &needed)) { 282 is_locked = lstrcmpi(name, L"default") != 0; 283 } 284 ::CloseDesktop(input_desk); 285 } 286 return is_locked; 287 } 288