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/files/file_path.h" 11 #include "base/files/file_util.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 39 MetroViewerProcessHost::InternalMessageFilter::InternalMessageFilter( 40 MetroViewerProcessHost* owner) 41 : owner_(owner) { 42 } 43 44 void MetroViewerProcessHost::InternalMessageFilter::OnChannelConnected( 45 int32 peer_pid) { 46 owner_->NotifyChannelConnected(); 47 } 48 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 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 86 base::ProcessId MetroViewerProcessHost::GetViewerProcessId() { 87 if (channel_) 88 return channel_->GetPeerPID(); 89 return base::kNullProcessId; 90 } 91 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 131 bool MetroViewerProcessHost::Send(IPC::Message* msg) { 132 return channel_->Send(msg); 133 } 134 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 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 170 void MetroViewerProcessHost::HandleMetroExit() { 171 if (instance_) 172 instance_->Send(new MetroViewerHostMsg_MetroExit()); 173 } 174 175 // static 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 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 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 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 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 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 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 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 295 void MetroViewerProcessHost::NotifyChannelConnected() { 296 if (channel_connected_event_) 297 channel_connected_event_->Signal(); 298 } 299 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 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 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 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