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/files/file_path.h"
13#include "base/files/file_util.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
26namespace {
27
28const int kRegistrationTimeoutSeconds = 30;
29
30// Copied from util_constants.cc to avoid taking a dependency on installer_util.
31const wchar_t kChromeExe[] = L"chrome.exe";
32const 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.
39bool 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                          &register_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|.
84bool 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, &current_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
106namespace win8 {
107
108bool 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