Home | History | Annotate | Download | only in chrome_frame
      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