Enable new bookmark apps everywhere but Mac.
[chromium-blink-merge.git] / win8 / delegate_execute / delegate_execute.cc
bloba41fb5bcc8ff8f886ceaa6847682cf671bae0315
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.
5 #include <atlbase.h>
6 #include <atlcom.h>
7 #include <atlctl.h>
8 #include <initguid.h>
9 #include <shellapi.h>
11 #include "base/at_exit.h"
12 #include "base/command_line.h"
13 #include "base/files/file_util.h"
14 #include "base/memory/scoped_ptr.h"
15 #include "base/process/kill.h"
16 #include "base/strings/string16.h"
17 #include "base/win/scoped_com_initializer.h"
18 #include "base/win/scoped_comptr.h"
19 #include "base/win/scoped_handle.h"
20 #include "base/win/windows_version.h"
21 #include "breakpad/src/client/windows/handler/exception_handler.h"
22 #include "chrome/common/chrome_switches.h"
23 #include "chrome/installer/util/browser_distribution.h"
24 #include "win8/delegate_execute/command_execute_impl.h"
25 #include "win8/delegate_execute/crash_server_init.h"
26 #include "win8/delegate_execute/delegate_execute_operation.h"
27 #include "win8/delegate_execute/resource.h"
29 using namespace ATL;
31 // Usually classes derived from CAtlExeModuleT, or other types of ATL
32 // COM module classes statically define their CLSID at compile time through
33 // the use of various macros, and ATL internals takes care of creating the
34 // class objects and registering them. However, we need to register the same
35 // object with different CLSIDs depending on a runtime setting, so we handle
36 // that logic here, before the main ATL message loop runs.
37 class DelegateExecuteModule
38 : public ATL::CAtlExeModuleT< DelegateExecuteModule > {
39 public :
40 typedef ATL::CAtlExeModuleT<DelegateExecuteModule> ParentClass;
41 typedef CComObject<CommandExecuteImpl> ImplType;
43 DelegateExecuteModule()
44 : registration_token_(0) {
47 HRESULT PreMessageLoop(int nShowCmd) {
48 HRESULT hr = S_OK;
49 base::string16 clsid_string;
50 GUID clsid;
51 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
52 if (!dist->GetCommandExecuteImplClsid(&clsid_string))
53 return E_FAIL;
54 hr = ::CLSIDFromString(clsid_string.c_str(), &clsid);
55 if (FAILED(hr))
56 return hr;
58 // We use the same class creation logic as ATL itself. See
59 // _ATL_OBJMAP_ENTRY::RegisterClassObject() in atlbase.h
60 hr = ImplType::_ClassFactoryCreatorClass::CreateInstance(
61 ImplType::_CreatorClass::CreateInstance, IID_IUnknown,
62 instance_.ReceiveVoid());
63 if (FAILED(hr))
64 return hr;
65 hr = ::CoRegisterClassObject(clsid, instance_.get(), CLSCTX_LOCAL_SERVER,
66 REGCLS_MULTIPLEUSE | REGCLS_SUSPENDED,
67 &registration_token_);
68 if (FAILED(hr))
69 return hr;
71 return ParentClass::PreMessageLoop(nShowCmd);
74 HRESULT PostMessageLoop() {
75 if (registration_token_ != 0) {
76 ::CoRevokeClassObject(registration_token_);
77 registration_token_ = 0;
80 instance_.Release();
82 return ParentClass::PostMessageLoop();
85 private:
86 base::win::ScopedComPtr<IUnknown> instance_;
87 DWORD registration_token_;
90 DelegateExecuteModule _AtlModule;
92 using delegate_execute::DelegateExecuteOperation;
93 using base::win::ScopedHandle;
95 int RelaunchChrome(const DelegateExecuteOperation& operation) {
96 AtlTrace("Relaunching [%ls] with flags [%ls]\n",
97 operation.mutex().c_str(), operation.relaunch_flags().c_str());
98 ScopedHandle mutex(OpenMutexW(SYNCHRONIZE, FALSE, operation.mutex().c_str()));
99 if (mutex.IsValid()) {
100 const int kWaitSeconds = 5;
101 DWORD result = ::WaitForSingleObject(mutex.Get(), kWaitSeconds * 1000);
102 if (result == WAIT_ABANDONED) {
103 // This is the normal case. Chrome exits and windows marks the mutex as
104 // abandoned.
105 } else if (result == WAIT_OBJECT_0) {
106 // This is unexpected. Check if somebody is not closing the mutex on
107 // RelaunchChromehelper, the mutex should not be closed.
108 AtlTrace("Unexpected release of the relaunch mutex!!\n");
109 } else if (result == WAIT_TIMEOUT) {
110 // This could mean that Chrome is hung. Proceed to exterminate.
111 base::Process process = operation.GetParent();
112 AtlTrace("%ds timeout. Killing Chrome %d\n", kWaitSeconds, process.Pid());
113 process.Terminate(0, false);
114 } else {
115 AtlTrace("Failed to wait for relaunch mutex, result is 0x%x\n", result);
117 } else {
118 // It is possible that chrome exits so fast that the mutex is not there.
119 AtlTrace("No relaunch mutex found\n");
122 // On Windows 8+ to launch Chrome we rely on Windows to use the
123 // IExecuteCommand interface exposed by delegate_execute to launch Chrome
124 // into Windows 8 metro mode or desktop.
125 // On Windows 7 we don't use delegate_execute and instead use plain vanilla
126 // ShellExecute to launch Chrome into ASH or desktop.
127 if (base::win::GetVersion() >= base::win::VERSION_WIN8) {
128 base::win::ScopedCOMInitializer com_initializer;
130 base::string16 relaunch_flags(operation.relaunch_flags());
131 SHELLEXECUTEINFO sei = { sizeof(sei) };
132 sei.fMask = SEE_MASK_FLAG_LOG_USAGE;
133 sei.nShow = SW_SHOWNORMAL;
134 sei.lpFile = operation.shortcut().value().c_str();
135 sei.lpParameters = relaunch_flags.c_str();
137 AtlTrace(L"Relaunching Chrome via shortcut [%ls]\n", sei.lpFile);
139 if (!::ShellExecuteExW(&sei)) {
140 int error = HRESULT_FROM_WIN32(::GetLastError());
141 AtlTrace("ShellExecute returned 0x%08X\n", error);
142 return error;
144 } else {
145 base::FilePath chrome_exe_path;
146 bool found_exe = CommandExecuteImpl::FindChromeExe(&chrome_exe_path);
147 DCHECK(found_exe);
148 if (found_exe) {
149 bool launch_ash = base::CommandLine::ForCurrentProcess()->HasSwitch(
150 switches::kForceImmersive);
151 if (launch_ash) {
152 AtlTrace(L"Relaunching Chrome into Windows ASH on Windows 7\n");
153 } else {
154 AtlTrace(L"Relaunching Chrome into Desktop From ASH on Windows 7\n");
156 SHELLEXECUTEINFO sei = { sizeof(sei) };
157 sei.fMask = SEE_MASK_FLAG_LOG_USAGE;
158 sei.nShow = SW_SHOWNORMAL;
159 // No point in using the shortcut if we are launching into ASH as any
160 // additonal command line switches specified in the shortcut will be
161 // ignored. This is because we don't have a good way to send the command
162 // line switches from the viewer to the browser process.
163 sei.lpFile = (launch_ash || operation.shortcut().empty()) ?
164 chrome_exe_path.value().c_str() :
165 operation.shortcut().value().c_str();
166 sei.lpParameters =
167 launch_ash ? L"-ServerName:DefaultBrowserServer" : NULL;
168 if (!::ShellExecuteExW(&sei)) {
169 int error = HRESULT_FROM_WIN32(::GetLastError());
170 AtlTrace("ShellExecute returned 0x%08X\n", error);
171 return error;
175 return S_OK;
178 extern "C" int WINAPI _tWinMain(HINSTANCE , HINSTANCE, LPTSTR, int nShowCmd) {
179 scoped_ptr<google_breakpad::ExceptionHandler> breakpad =
180 delegate_execute::InitializeCrashReporting();
182 base::AtExitManager exit_manager;
183 AtlTrace("delegate_execute enter\n");
185 base::CommandLine::Init(0, NULL);
186 HRESULT ret_code = E_UNEXPECTED;
187 DelegateExecuteOperation operation;
188 if (operation.Init(base::CommandLine::ForCurrentProcess())) {
189 switch (operation.operation_type()) {
190 case DelegateExecuteOperation::DELEGATE_EXECUTE:
191 ret_code = _AtlModule.WinMain(nShowCmd);
192 break;
193 case DelegateExecuteOperation::RELAUNCH_CHROME:
194 ret_code = RelaunchChrome(operation);
195 break;
196 default:
197 NOTREACHED();
200 AtlTrace("delegate_execute exit, code = %d\n", ret_code);
201 return ret_code;