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/viewer/metro_viewer_process_host.h"
6
7 #include <shlobj.h>
8
9 #include "base/command_line.h"
10 #include "base/file_util.h"
11 #include "base/files/file_path.h"
12 #include "base/memory/ref_counted.h"
13 #include "base/path_service.h"
14 #include "base/process/process.h"
15 #include "base/process/process_handle.h"
16 #include "base/strings/string16.h"
17 #include "base/synchronization/waitable_event.h"
18 #include "base/time/time.h"
19 #include "base/win/scoped_comptr.h"
20 #include "base/win/windows_version.h"
21 #include "ipc/ipc_channel_proxy.h"
22 #include "ipc/ipc_message.h"
23 #include "ipc/ipc_message_macros.h"
24 #include "ui/aura/remote_window_tree_host_win.h"
25 #include "ui/metro_viewer/metro_viewer_messages.h"
26 #include "win8/viewer/metro_viewer_constants.h"
27
28 namespace {
29
30 const int kViewerProcessConnectionTimeoutSecs = 60;
31
32 } // namespace
33
34 namespace win8 {
35
36 // static
37 MetroViewerProcessHost* MetroViewerProcessHost::instance_ = NULL;
38
InternalMessageFilter(MetroViewerProcessHost * owner)39 MetroViewerProcessHost::InternalMessageFilter::InternalMessageFilter(
40 MetroViewerProcessHost* owner)
41 : owner_(owner) {
42 }
43
OnChannelConnected(int32 peer_pid)44 void MetroViewerProcessHost::InternalMessageFilter::OnChannelConnected(
45 int32 peer_pid) {
46 owner_->NotifyChannelConnected();
47 }
48
MetroViewerProcessHost(base::SingleThreadTaskRunner * ipc_task_runner)49 MetroViewerProcessHost::MetroViewerProcessHost(
50 base::SingleThreadTaskRunner* ipc_task_runner) {
51 DCHECK(!instance_);
52 instance_ = this;
53
54 channel_ = IPC::ChannelProxy::Create(kMetroViewerIPCChannelName,
55 IPC::Channel::MODE_NAMED_SERVER,
56 this,
57 ipc_task_runner);
58 }
59
~MetroViewerProcessHost()60 MetroViewerProcessHost::~MetroViewerProcessHost() {
61 if (!channel_) {
62 instance_ = NULL;
63 return;
64 }
65
66 base::ProcessId viewer_process_id = GetViewerProcessId();
67 channel_->Close();
68 if (message_filter_) {
69 // Wait for the viewer process to go away.
70 if (viewer_process_id != base::kNullProcessId) {
71 base::ProcessHandle viewer_process = NULL;
72 base::OpenProcessHandleWithAccess(
73 viewer_process_id,
74 PROCESS_QUERY_INFORMATION | SYNCHRONIZE,
75 &viewer_process);
76 if (viewer_process) {
77 ::WaitForSingleObject(viewer_process, INFINITE);
78 ::CloseHandle(viewer_process);
79 }
80 }
81 channel_->RemoveFilter(message_filter_);
82 }
83 instance_ = NULL;
84 }
85
GetViewerProcessId()86 base::ProcessId MetroViewerProcessHost::GetViewerProcessId() {
87 if (channel_)
88 return channel_->GetPeerPID();
89 return base::kNullProcessId;
90 }
91
LaunchViewerAndWaitForConnection(const base::string16 & app_user_model_id)92 bool MetroViewerProcessHost::LaunchViewerAndWaitForConnection(
93 const base::string16& app_user_model_id) {
94 DCHECK_EQ(base::kNullProcessId, channel_->GetPeerPID());
95
96 channel_connected_event_.reset(new base::WaitableEvent(false, false));
97
98 message_filter_ = new InternalMessageFilter(this);
99 channel_->AddFilter(message_filter_);
100
101 if (base::win::GetVersion() >= base::win::VERSION_WIN8) {
102 base::win::ScopedComPtr<IApplicationActivationManager> activator;
103 HRESULT hr = activator.CreateInstance(CLSID_ApplicationActivationManager);
104 if (SUCCEEDED(hr)) {
105 DWORD pid = 0;
106 // Use the "connect" verb to
107 hr = activator->ActivateApplication(
108 app_user_model_id.c_str(), kMetroViewerConnectVerb, AO_NONE, &pid);
109 }
110
111 LOG_IF(ERROR, FAILED(hr)) << "Tried and failed to launch Metro Chrome. "
112 << "hr=" << std::hex << hr;
113 } else {
114 // For Windows 7 we need to launch the viewer ourselves.
115 base::FilePath chrome_path;
116 if (!PathService::Get(base::DIR_EXE, &chrome_path))
117 return false;
118 // TODO(cpu): launch with "-ServerName:DefaultBrowserServer"
119 // note that the viewer might try to launch chrome again.
120 CHECK(false);
121 }
122
123 // Having launched the viewer process, now we wait for it to connect.
124 bool success =
125 channel_connected_event_->TimedWait(base::TimeDelta::FromSeconds(
126 kViewerProcessConnectionTimeoutSecs));
127 channel_connected_event_.reset();
128 return success;
129 }
130
Send(IPC::Message * msg)131 bool MetroViewerProcessHost::Send(IPC::Message* msg) {
132 return channel_->Send(msg);
133 }
134
OnMessageReceived(const IPC::Message & message)135 bool MetroViewerProcessHost::OnMessageReceived(
136 const IPC::Message& message) {
137 DCHECK(CalledOnValidThread());
138 bool handled = true;
139 IPC_BEGIN_MESSAGE_MAP(MetroViewerProcessHost, message)
140 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_FileSaveAsDone,
141 OnFileSaveAsDone)
142 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_FileOpenDone,
143 OnFileOpenDone)
144 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_MultiFileOpenDone,
145 OnMultiFileOpenDone)
146 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_OpenURL, OnOpenURL)
147 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_SearchRequest, OnHandleSearchRequest)
148 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_SelectFolderDone,
149 OnSelectFolderDone)
150 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_SetTargetSurface, OnSetTargetSurface)
151 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_WindowSizeChanged,
152 OnWindowSizeChanged)
153 IPC_MESSAGE_UNHANDLED(handled = false)
154 IPC_END_MESSAGE_MAP()
155 return handled ? true :
156 aura::RemoteWindowTreeHostWin::Instance()->OnMessageReceived(message);
157 }
158
159 // static
HandleActivateDesktop(const base::FilePath & path,bool ash_exit)160 void MetroViewerProcessHost::HandleActivateDesktop(
161 const base::FilePath& path,
162 bool ash_exit) {
163 if (instance_) {
164 instance_->Send(
165 new MetroViewerHostMsg_ActivateDesktop(path, ash_exit));
166 }
167 }
168
169 // static
HandleMetroExit()170 void MetroViewerProcessHost::HandleMetroExit() {
171 if (instance_)
172 instance_->Send(new MetroViewerHostMsg_MetroExit());
173 }
174
175 // static
HandleOpenFile(const base::string16 & title,const base::FilePath & default_path,const base::string16 & filter,const OpenFileCompletion & on_success,const FileSelectionCanceled & on_failure)176 void MetroViewerProcessHost::HandleOpenFile(
177 const base::string16& title,
178 const base::FilePath& default_path,
179 const base::string16& filter,
180 const OpenFileCompletion& on_success,
181 const FileSelectionCanceled& on_failure) {
182 if (instance_) {
183 instance_->HandleOpenFileImpl(title, default_path, filter, on_success,
184 on_failure);
185 }
186 }
187
188 // static
HandleOpenMultipleFiles(const base::string16 & title,const base::FilePath & default_path,const base::string16 & filter,const OpenMultipleFilesCompletion & on_success,const FileSelectionCanceled & on_failure)189 void MetroViewerProcessHost::HandleOpenMultipleFiles(
190 const base::string16& title,
191 const base::FilePath& default_path,
192 const base::string16& filter,
193 const OpenMultipleFilesCompletion& on_success,
194 const FileSelectionCanceled& on_failure) {
195 if (instance_) {
196 instance_->HandleOpenMultipleFilesImpl(title, default_path, filter,
197 on_success, on_failure);
198 }
199 }
200
201 // static
HandleSaveFile(const base::string16 & title,const base::FilePath & default_path,const base::string16 & filter,int filter_index,const base::string16 & default_extension,const SaveFileCompletion & on_success,const FileSelectionCanceled & on_failure)202 void MetroViewerProcessHost::HandleSaveFile(
203 const base::string16& title,
204 const base::FilePath& default_path,
205 const base::string16& filter,
206 int filter_index,
207 const base::string16& default_extension,
208 const SaveFileCompletion& on_success,
209 const FileSelectionCanceled& on_failure) {
210 if (instance_) {
211 instance_->HandleSaveFileImpl(title, default_path, filter, filter_index,
212 default_extension, on_success, on_failure);
213 }
214 }
215
216 // static
HandleSelectFolder(const base::string16 & title,const SelectFolderCompletion & on_success,const FileSelectionCanceled & on_failure)217 void MetroViewerProcessHost::HandleSelectFolder(
218 const base::string16& title,
219 const SelectFolderCompletion& on_success,
220 const FileSelectionCanceled& on_failure) {
221 if (instance_)
222 instance_->HandleSelectFolderImpl(title, on_success, on_failure);
223 }
224
HandleOpenFileImpl(const base::string16 & title,const base::FilePath & default_path,const base::string16 & filter,const OpenFileCompletion & on_success,const FileSelectionCanceled & on_failure)225 void MetroViewerProcessHost::HandleOpenFileImpl(
226 const base::string16& title,
227 const base::FilePath& default_path,
228 const base::string16& filter,
229 const OpenFileCompletion& on_success,
230 const FileSelectionCanceled& on_failure) {
231 // Can only have one of these operations in flight.
232 DCHECK(file_open_completion_callback_.is_null());
233 DCHECK(failure_callback_.is_null());
234
235 file_open_completion_callback_ = on_success;
236 failure_callback_ = on_failure;
237
238 Send(new MetroViewerHostMsg_DisplayFileOpen(title, filter, default_path,
239 false));
240 }
241
HandleOpenMultipleFilesImpl(const base::string16 & title,const base::FilePath & default_path,const base::string16 & filter,const OpenMultipleFilesCompletion & on_success,const FileSelectionCanceled & on_failure)242 void MetroViewerProcessHost::HandleOpenMultipleFilesImpl(
243 const base::string16& title,
244 const base::FilePath& default_path,
245 const base::string16& filter,
246 const OpenMultipleFilesCompletion& on_success,
247 const FileSelectionCanceled& on_failure) {
248 // Can only have one of these operations in flight.
249 DCHECK(multi_file_open_completion_callback_.is_null());
250 DCHECK(failure_callback_.is_null());
251 multi_file_open_completion_callback_ = on_success;
252 failure_callback_ = on_failure;
253
254 Send(new MetroViewerHostMsg_DisplayFileOpen(title, filter, default_path,
255 true));
256 }
257
HandleSaveFileImpl(const base::string16 & title,const base::FilePath & default_path,const base::string16 & filter,int filter_index,const base::string16 & default_extension,const SaveFileCompletion & on_success,const FileSelectionCanceled & on_failure)258 void MetroViewerProcessHost::HandleSaveFileImpl(
259 const base::string16& title,
260 const base::FilePath& default_path,
261 const base::string16& filter,
262 int filter_index,
263 const base::string16& default_extension,
264 const SaveFileCompletion& on_success,
265 const FileSelectionCanceled& on_failure) {
266 MetroViewerHostMsg_SaveAsDialogParams params;
267 params.title = title;
268 params.default_extension = default_extension;
269 params.filter = filter;
270 params.filter_index = filter_index;
271 params.suggested_name = default_path;
272
273 // Can only have one of these operations in flight.
274 DCHECK(file_saveas_completion_callback_.is_null());
275 DCHECK(failure_callback_.is_null());
276 file_saveas_completion_callback_ = on_success;
277 failure_callback_ = on_failure;
278
279 Send(new MetroViewerHostMsg_DisplayFileSaveAs(params));
280 }
281
HandleSelectFolderImpl(const base::string16 & title,const SelectFolderCompletion & on_success,const FileSelectionCanceled & on_failure)282 void MetroViewerProcessHost::HandleSelectFolderImpl(
283 const base::string16& title,
284 const SelectFolderCompletion& on_success,
285 const FileSelectionCanceled& on_failure) {
286 // Can only have one of these operations in flight.
287 DCHECK(select_folder_completion_callback_.is_null());
288 DCHECK(failure_callback_.is_null());
289 select_folder_completion_callback_ = on_success;
290 failure_callback_ = on_failure;
291
292 Send(new MetroViewerHostMsg_DisplaySelectFolder(title));
293 }
294
NotifyChannelConnected()295 void MetroViewerProcessHost::NotifyChannelConnected() {
296 if (channel_connected_event_)
297 channel_connected_event_->Signal();
298 }
299
OnFileSaveAsDone(bool success,const base::FilePath & filename,int filter_index)300 void MetroViewerProcessHost::OnFileSaveAsDone(bool success,
301 const base::FilePath& filename,
302 int filter_index) {
303 if (success)
304 file_saveas_completion_callback_.Run(filename, filter_index, NULL);
305 else
306 failure_callback_.Run(NULL);
307 file_saveas_completion_callback_.Reset();
308 failure_callback_.Reset();
309 }
310
311
OnFileOpenDone(bool success,const base::FilePath & filename)312 void MetroViewerProcessHost::OnFileOpenDone(bool success,
313 const base::FilePath& filename) {
314 if (success)
315 file_open_completion_callback_.Run(base::FilePath(filename), 0, NULL);
316 else
317 failure_callback_.Run(NULL);
318 file_open_completion_callback_.Reset();
319 failure_callback_.Reset();
320 }
321
OnMultiFileOpenDone(bool success,const std::vector<base::FilePath> & files)322 void MetroViewerProcessHost::OnMultiFileOpenDone(
323 bool success,
324 const std::vector<base::FilePath>& files) {
325 if (success)
326 multi_file_open_completion_callback_.Run(files, NULL);
327 else
328 failure_callback_.Run(NULL);
329 multi_file_open_completion_callback_.Reset();
330 failure_callback_.Reset();
331 }
332
OnSelectFolderDone(bool success,const base::FilePath & folder)333 void MetroViewerProcessHost::OnSelectFolderDone(
334 bool success,
335 const base::FilePath& folder) {
336 if (success)
337 select_folder_completion_callback_.Run(base::FilePath(folder), 0, NULL);
338 else
339 failure_callback_.Run(NULL);
340 select_folder_completion_callback_.Reset();
341 failure_callback_.Reset();
342 }
343
344 } // namespace win8
345