1 // Copyright (c) 2013 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 "win8/test/metro_registration_helper.h" 6 7 #include <shlobj.h> 8 9 #include <vector> 10 11 #include "base/command_line.h" 12 #include "base/file_util.h" 13 #include "base/files/file_path.h" 14 #include "base/logging.h" 15 #include "base/path_service.h" 16 #include "base/process/kill.h" 17 #include "base/process/launch.h" 18 #include "base/process/process.h" 19 #include "base/strings/string16.h" 20 #include "base/win/scoped_co_mem.h" 21 #include "base/win/scoped_comptr.h" 22 #include "base/win/scoped_handle.h" 23 #include "win8/test/open_with_dialog_controller.h" 24 #include "win8/test/test_registrar_constants.h" 25 26 namespace { 27 28 const int kRegistrationTimeoutSeconds = 30; 29 30 // Copied from util_constants.cc to avoid taking a dependency on installer_util. 31 const wchar_t kChromeExe[] = L"chrome.exe"; 32 const wchar_t kRegistrar[] = L"test_registrar.exe"; 33 34 // Registers chrome.exe as a potential Win8 default browser. It will then show 35 // up in the default browser selection dialog as kDefaultTestExeName. Intended 36 // to be used by a test binary in the build output directory and assumes the 37 // presence of test_registrar.exe, a viewer process, and all needed DLLs in the 38 // same directory as the calling module. 39 bool RegisterTestDefaultBrowser() { 40 base::FilePath dir; 41 if (!PathService::Get(base::DIR_EXE, &dir)) 42 return false; 43 44 base::FilePath chrome_exe(dir.Append(kChromeExe)); 45 base::FilePath registrar(dir.Append(kRegistrar)); 46 47 if (!base::PathExists(chrome_exe) || !base::PathExists(registrar)) { 48 LOG(ERROR) << "Could not locate " << kChromeExe << " or " << kRegistrar; 49 return false; 50 } 51 52 // Perform the registration by invoking test_registrar.exe. 53 CommandLine register_command(registrar); 54 register_command.AppendArg("/RegServer"); 55 56 base::win::ScopedHandle register_handle; 57 if (base::LaunchProcess(register_command.GetCommandLineString(), 58 base::LaunchOptions(), 59 ®ister_handle)) { 60 int ret = 0; 61 if (base::WaitForExitCodeWithTimeout( 62 register_handle.Get(), &ret, 63 base::TimeDelta::FromSeconds(kRegistrationTimeoutSeconds))) { 64 if (ret == 0) { 65 return true; 66 } else { 67 LOG(ERROR) << "Win8 registration using " 68 << register_command.GetCommandLineString() 69 << " failed with error code " << ret; 70 } 71 } else { 72 LOG(ERROR) << "Win8 registration using " 73 << register_command.GetCommandLineString() << " timed out."; 74 } 75 } 76 77 PLOG(ERROR) << "Failed to launch Win8 registration utility using " 78 << register_command.GetCommandLineString(); 79 return false; 80 } 81 82 // Returns true if the test viewer's progid is the default handler for 83 // |protocol|. 84 bool IsTestDefaultForProtocol(const wchar_t* protocol) { 85 base::win::ScopedComPtr<IApplicationAssociationRegistration> registration; 86 HRESULT hr = registration.CreateInstance( 87 CLSID_ApplicationAssociationRegistration, NULL, CLSCTX_INPROC); 88 if (FAILED(hr)) { 89 LOG(ERROR) << std::hex << hr; 90 return false; 91 } 92 93 base::win::ScopedCoMem<wchar_t> current_app; 94 hr = registration->QueryCurrentDefault(protocol, AT_URLPROTOCOL, 95 AL_EFFECTIVE, ¤t_app); 96 if (FAILED(hr)) { 97 LOG(ERROR) << std::hex << hr; 98 return false; 99 } 100 101 return !base::string16(win8::test::kDefaultTestProgId).compare(current_app); 102 } 103 104 } // namespace 105 106 namespace win8 { 107 108 bool MakeTestDefaultBrowserSynchronously() { 109 static const wchar_t kDefaultBrowserProtocol[] = L"http"; 110 111 if (!RegisterTestDefaultBrowser()) 112 return false; 113 114 // Make sure the registration changes have been acknowledged by the shell 115 // before querying for the current default. 116 SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST | SHCNF_FLUSH, NULL, NULL); 117 118 // OpenWithDialogController will fail if the Test Runner is already default 119 // since it will not show up verbatim in the dialog (e.g., in EN-US, it will 120 // be prefixed by "Keep using "). 121 if (IsTestDefaultForProtocol(kDefaultBrowserProtocol)) 122 return true; 123 124 std::vector<base::string16> choices; 125 OpenWithDialogController controller; 126 HRESULT hr = controller.RunSynchronously( 127 NULL, kDefaultBrowserProtocol, win8::test::kDefaultTestExeName, &choices); 128 LOG_IF(ERROR, FAILED(hr)) << std::hex << hr; 129 DCHECK(IsTestDefaultForProtocol(kDefaultBrowserProtocol)); 130 return SUCCEEDED(hr); 131 } 132 133 } // namespace win8 134