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::win::ScopedHandle process_handle; 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 if (!base::WaitForExitCodeWithTimeout( 115 process_handle.Get(), &exit_code, 116 base::TimeDelta::FromMilliseconds(kDllRegistrationTimeoutMs))) { 117 LOG(ERROR) << "Timeout waiting to register or unregister DLL with " 118 "command: " << registration_command; 119 base::KillProcess(process_handle.Get(), 0, false); 120 NOTREACHED() << "Aborting test due to registration failure."; 121 } 122 } 123 if (exit_code != 0) { 124 if (registration_operation == REGISTER) { 125 LOG(ERROR) 126 << "DLL registration failed (exit code: 0x" << std::hex << exit_code 127 << ", command: " << registration_command 128 << "). Make sure you are running as Admin."; 129 ::ExitProcess(1); 130 } else { 131 LOG(WARNING) 132 << "DLL unregistration failed (exit code: 0x" << std::hex << exit_code 133 << ", command: " << registration_command << ")."; 134 } 135 } 136 } 137 138 // static 139 void ScopedChromeFrameRegistrar::RegisterAtPath( 140 const std::wstring& path, RegistrationType registration_type) { 141 DoRegistration(path, registration_type, REGISTER); 142 } 143 144 // static 145 void ScopedChromeFrameRegistrar::UnregisterAtPath( 146 const std::wstring& path, RegistrationType registration_type) { 147 DoRegistration(path, registration_type, UNREGISTER); 148 } 149 150 base::FilePath ScopedChromeFrameRegistrar::GetReferenceChromeFrameDllPath() { 151 base::FilePath reference_build_dir; 152 PathService::Get(chrome::DIR_APP, &reference_build_dir); 153 154 reference_build_dir = reference_build_dir.DirName(); 155 reference_build_dir = reference_build_dir.DirName(); 156 157 reference_build_dir = reference_build_dir.AppendASCII("chrome_frame"); 158 reference_build_dir = reference_build_dir.AppendASCII("tools"); 159 reference_build_dir = reference_build_dir.AppendASCII("test"); 160 reference_build_dir = reference_build_dir.AppendASCII("reference_build"); 161 reference_build_dir = reference_build_dir.AppendASCII("chrome"); 162 reference_build_dir = reference_build_dir.AppendASCII("servers"); 163 reference_build_dir = reference_build_dir.Append(kChromeFrameDllName); 164 return reference_build_dir; 165 } 166 167 // static 168 void ScopedChromeFrameRegistrar::RegisterAndExitProcessIfDirected() { 169 // This method is invoked before any Chromium helpers have been initialized. 170 // Take pains to use only Win32 and CRT functions. 171 int argc = 0; 172 const wchar_t* const* argv = ::CommandLineToArgvW(::GetCommandLine(), &argc); 173 if (argc < 2 || ::lstrcmp(argv[1], kCallRegistrationEntrypointSwitch) != 0) 174 return; 175 if (argc != 4) { 176 printf("Usage: %S %S <path to dll> <entrypoint>\n", argv[0], 177 kCallRegistrationEntrypointSwitch); 178 return; 179 } 180 181 // The only way to leave from here on down is ExitProcess. 182 const wchar_t* dll_path = argv[2]; 183 const wchar_t* wide_entrypoint = argv[3]; 184 char entrypoint[256]; 185 HRESULT exit_code = 0; 186 int entrypoint_len = lstrlen(wide_entrypoint); 187 if (entrypoint_len <= 0 || entrypoint_len >= arraysize(entrypoint)) { 188 exit_code = HRESULT_FROM_WIN32(ERROR_PROC_NOT_FOUND); 189 } else { 190 // Convert wide to narrow. Since the entrypoint must be a narrow string 191 // anyway, it is safe to truncate each character like this. 192 std::copy(wide_entrypoint, wide_entrypoint + entrypoint_len + 1, 193 &entrypoint[0]); 194 HMODULE dll_module = ::LoadLibrary(dll_path); 195 if (dll_module == NULL) { 196 exit_code = HRESULT_FROM_WIN32(::GetLastError()); 197 } else { 198 typedef HRESULT (STDAPICALLTYPE *RegisterFp)(); 199 RegisterFp register_func = 200 reinterpret_cast<RegisterFp>(::GetProcAddress(dll_module, 201 entrypoint)); 202 if (register_func == NULL) { 203 exit_code = HRESULT_FROM_WIN32(::GetLastError()); 204 } else { 205 exit_code = register_func(); 206 } 207 ::FreeLibrary(dll_module); 208 } 209 } 210 211 ::ExitProcess(exit_code); 212 } 213 214 // Non-statics 215 216 ScopedChromeFrameRegistrar::ScopedChromeFrameRegistrar( 217 const std::wstring& path, RegistrationType registration_type) 218 : registration_type_(registration_type) { 219 if (!register_chrome_path_provider_) { 220 // Register paths needed by the ScopedChromeFrameRegistrar. 221 chrome::RegisterPathProvider(); 222 register_chrome_path_provider_ = true; 223 } 224 original_dll_path_ = path; 225 RegisterChromeFrameAtPath(original_dll_path_); 226 } 227 228 ScopedChromeFrameRegistrar::ScopedChromeFrameRegistrar( 229 RegistrationType registration_type) 230 : registration_type_(registration_type) { 231 if (!register_chrome_path_provider_) { 232 // Register paths needed by the ScopedChromeFrameRegistrar. 233 chrome::RegisterPathProvider(); 234 register_chrome_path_provider_ = true; 235 } 236 original_dll_path_ = GetChromeFrameBuildPath().value(); 237 RegisterChromeFrameAtPath(original_dll_path_); 238 } 239 240 ScopedChromeFrameRegistrar::~ScopedChromeFrameRegistrar() { 241 if (base::FilePath(original_dll_path_) != 242 base::FilePath(new_chrome_frame_dll_path_)) { 243 RegisterChromeFrameAtPath(original_dll_path_); 244 } else if (registration_type_ == PER_USER) { 245 UnregisterAtPath(new_chrome_frame_dll_path_, registration_type_); 246 HWND chrome_frame_helper_window = 247 FindWindow(L"ChromeFrameHelperWindowClass", NULL); 248 if (IsWindow(chrome_frame_helper_window)) { 249 PostMessage(chrome_frame_helper_window, WM_CLOSE, 0, 0); 250 } else { 251 base::KillProcesses(L"chrome_frame_helper.exe", 0, NULL); 252 } 253 } 254 } 255 256 void ScopedChromeFrameRegistrar::RegisterChromeFrameAtPath( 257 const std::wstring& path) { 258 RegisterAtPath(path, registration_type_); 259 new_chrome_frame_dll_path_ = path; 260 } 261 262 void ScopedChromeFrameRegistrar::RegisterReferenceChromeFrameBuild() { 263 RegisterChromeFrameAtPath(GetReferenceChromeFrameDllPath().value()); 264 } 265 266 std::wstring ScopedChromeFrameRegistrar::GetChromeFrameDllPath() const { 267 return new_chrome_frame_dll_path_; 268 } 269 270 bool IsWorkstationLocked() { 271 bool is_locked = true; 272 HDESK input_desk = ::OpenInputDesktop(0, 0, GENERIC_READ); 273 if (input_desk) { 274 wchar_t name[256] = {0}; 275 DWORD needed = 0; 276 if (::GetUserObjectInformation(input_desk, 277 UOI_NAME, 278 name, 279 sizeof(name), 280 &needed)) { 281 is_locked = lstrcmpi(name, L"default") != 0; 282 } 283 ::CloseDesktop(input_desk); 284 } 285 return is_locked; 286 } 287