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::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