OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/chrome_select_file_dialog_factory_win.h" | 5 #include "chrome/browser/chrome_select_file_dialog_factory_win.h" |
6 | 6 |
7 #include <Windows.h> | 7 #include <Windows.h> |
8 #include <commdlg.h> | 8 #include <commdlg.h> |
9 | 9 |
10 #include "base/bind.h" | 10 #include "base/bind.h" |
11 #include "base/bind_helpers.h" | 11 #include "base/bind_helpers.h" |
12 #include "base/callback.h" | 12 #include "base/callback.h" |
13 #include "base/location.h" | 13 #include "base/location.h" |
14 #include "base/logging.h" | 14 #include "base/logging.h" |
15 #include "base/metrics/field_trial.h" | 15 #include "base/metrics/field_trial.h" |
16 #include "base/strings/string16.h" | 16 #include "base/strings/string16.h" |
17 #include "base/synchronization/waitable_event.h" | 17 #include "base/synchronization/waitable_event.h" |
18 #include "base/win/metro.h" | 18 #include "base/win/metro.h" |
19 #include "chrome/common/chrome_utility_messages.h" | 19 #include "chrome/common/chrome_utility_messages.h" |
20 #include "content/public/browser/utility_process_host.h" | 20 #include "content/public/browser/utility_process_host.h" |
21 #include "content/public/browser/utility_process_host_client.h" | 21 #include "content/public/browser/utility_process_host_client.h" |
22 #include "ipc/ipc_message_macros.h" | 22 #include "ipc/ipc_message_macros.h" |
23 #include "ui/base/win/open_file_name_win.h" | 23 #include "ui/base/win/open_file_name_win.h" |
24 #include "ui/shell_dialogs/select_file_dialog_win.h" | 24 #include "ui/shell_dialogs/select_file_dialog_win.h" |
25 | 25 |
26 namespace { | 26 namespace { |
27 | 27 |
| 28 bool CallMetroOPENFILENAMEMethod(const char* method_name, OPENFILENAME* ofn) { |
| 29 typedef BOOL (*MetroOPENFILENAMEMethod)(OPENFILENAME*); |
| 30 MetroOPENFILENAMEMethod metro_method = NULL; |
| 31 HMODULE metro_module = base::win::GetMetroModule(); |
| 32 |
| 33 if (metro_module != NULL) { |
| 34 metro_method = reinterpret_cast<MetroOPENFILENAMEMethod>( |
| 35 ::GetProcAddress(metro_module, method_name)); |
| 36 } |
| 37 |
| 38 if (metro_method != NULL) |
| 39 return metro_method(ofn) == TRUE; |
| 40 |
| 41 NOTREACHED(); |
| 42 |
| 43 return false; |
| 44 } |
| 45 |
| 46 bool ShouldIsolateShellOperations() { |
| 47 return base::FieldTrialList::FindFullName("IsolateShellOperations") == |
| 48 "Enabled"; |
| 49 } |
| 50 |
28 // Receives the GetOpenFileName result from the utility process. | 51 // Receives the GetOpenFileName result from the utility process. |
29 class GetOpenFileNameClient : public content::UtilityProcessHostClient { | 52 class GetOpenFileNameClient : public content::UtilityProcessHostClient { |
30 public: | 53 public: |
31 GetOpenFileNameClient(); | 54 GetOpenFileNameClient(); |
32 | 55 |
33 // Blocks until the GetOpenFileName result is received (including failure to | 56 // Blocks until the GetOpenFileName result is received (including failure to |
34 // launch or a crash of the utility process). | 57 // launch or a crash of the utility process). |
35 void WaitForCompletion(); | 58 void WaitForCompletion(); |
36 | 59 |
37 // Returns the selected directory. | 60 // Returns the selected directory. |
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
110 scoped_refptr<GetOpenFileNameClient> client, | 133 scoped_refptr<GetOpenFileNameClient> client, |
111 const scoped_refptr<base::SequencedTaskRunner>& current_task_runner) { | 134 const scoped_refptr<base::SequencedTaskRunner>& current_task_runner) { |
112 DCHECK(current_task_runner->RunsTasksOnCurrentThread()); | 135 DCHECK(current_task_runner->RunsTasksOnCurrentThread()); |
113 | 136 |
114 base::WeakPtr<content::UtilityProcessHost> utility_process_host( | 137 base::WeakPtr<content::UtilityProcessHost> utility_process_host( |
115 content::UtilityProcessHost::Create(client, current_task_runner) | 138 content::UtilityProcessHost::Create(client, current_task_runner) |
116 ->AsWeakPtr()); | 139 ->AsWeakPtr()); |
117 utility_process_host->DisableSandbox(); | 140 utility_process_host->DisableSandbox(); |
118 utility_process_host->Send(new ChromeUtilityMsg_GetOpenFileName( | 141 utility_process_host->Send(new ChromeUtilityMsg_GetOpenFileName( |
119 ofn->hwndOwner, | 142 ofn->hwndOwner, |
120 ofn->Flags, | 143 ofn->Flags & ~OFN_ENABLEHOOK, // We can't send a hook function over IPC. |
121 ui::win::OpenFileName::GetFilters(ofn), | 144 ui::win::OpenFileName::GetFilters(ofn), |
122 base::FilePath(ofn->lpstrInitialDir ? ofn->lpstrInitialDir | 145 base::FilePath(ofn->lpstrInitialDir ? ofn->lpstrInitialDir |
123 : base::string16()), | 146 : base::string16()), |
124 base::FilePath(ofn->lpstrFile))); | 147 base::FilePath(ofn->lpstrFile))); |
125 } | 148 } |
126 | 149 |
127 // Invokes GetOpenFileName in a utility process. Blocks until the result is | 150 // Invokes GetOpenFileName in a utility process. Blocks until the result is |
128 // received. Uses |blocking_task_runner| for IPC. | 151 // received. Uses |blocking_task_runner| for IPC. |
129 bool GetOpenFileNameInUtilityProcess( | 152 bool GetOpenFileNameInUtilityProcess( |
130 const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner, | 153 const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner, |
(...skipping 11 matching lines...) Expand all Loading... |
142 ui::win::OpenFileName::SetResult( | 165 ui::win::OpenFileName::SetResult( |
143 client->directory(), client->filenames(), ofn); | 166 client->directory(), client->filenames(), ofn); |
144 return true; | 167 return true; |
145 } | 168 } |
146 | 169 |
147 // Implements GetOpenFileName for CreateWinSelectFileDialog by delegating either | 170 // Implements GetOpenFileName for CreateWinSelectFileDialog by delegating either |
148 // to Metro or a utility process. | 171 // to Metro or a utility process. |
149 bool GetOpenFileNameImpl( | 172 bool GetOpenFileNameImpl( |
150 const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner, | 173 const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner, |
151 OPENFILENAME* ofn) { | 174 OPENFILENAME* ofn) { |
152 HMODULE metro_module = base::win::GetMetroModule(); | 175 if (base::win::IsMetroProcess()) |
153 if (metro_module != NULL) { | 176 return CallMetroOPENFILENAMEMethod("MetroGetOpenFileName", ofn); |
154 typedef BOOL (*MetroGetOpenFileName)(OPENFILENAME*); | |
155 MetroGetOpenFileName metro_get_open_file_name = | |
156 reinterpret_cast<MetroGetOpenFileName>( | |
157 ::GetProcAddress(metro_module, "MetroGetOpenFileName")); | |
158 if (metro_get_open_file_name == NULL) { | |
159 NOTREACHED(); | |
160 return false; | |
161 } | |
162 | 177 |
163 return metro_get_open_file_name(ofn) == TRUE; | 178 if (ShouldIsolateShellOperations()) |
164 } | |
165 | |
166 if (base::FieldTrialList::FindFullName("IsolateShellOperations") == | |
167 "Enabled") { | |
168 return GetOpenFileNameInUtilityProcess(blocking_task_runner, ofn); | 179 return GetOpenFileNameInUtilityProcess(blocking_task_runner, ofn); |
169 } | |
170 | 180 |
171 return ::GetOpenFileName(ofn) == TRUE; | 181 return ::GetOpenFileName(ofn) == TRUE; |
172 } | 182 } |
173 | 183 |
| 184 class GetSaveFileNameClient : public content::UtilityProcessHostClient { |
| 185 public: |
| 186 GetSaveFileNameClient(); |
| 187 |
| 188 // Blocks until the GetSaveFileName result is received (including failure to |
| 189 // launch or a crash of the utility process). |
| 190 void WaitForCompletion(); |
| 191 |
| 192 // Returns the selected path. |
| 193 const base::FilePath& path() const { return path_; } |
| 194 |
| 195 // Returns the index of the user-selected filter. |
| 196 int one_based_filter_index() const { return one_based_filter_index_; } |
| 197 |
| 198 // UtilityProcessHostClient implementation |
| 199 virtual void OnProcessCrashed(int exit_code) OVERRIDE; |
| 200 virtual void OnProcessLaunchFailed() OVERRIDE; |
| 201 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; |
| 202 |
| 203 protected: |
| 204 virtual ~GetSaveFileNameClient(); |
| 205 |
| 206 private: |
| 207 void OnResult(const base::FilePath& path, int one_based_filter_index); |
| 208 void OnFailure(); |
| 209 |
| 210 base::FilePath path_; |
| 211 int one_based_filter_index_; |
| 212 base::WaitableEvent event_; |
| 213 |
| 214 DISALLOW_COPY_AND_ASSIGN(GetSaveFileNameClient); |
| 215 }; |
| 216 |
| 217 GetSaveFileNameClient::GetSaveFileNameClient() |
| 218 : event_(true, false), one_based_filter_index_(0) { |
| 219 } |
| 220 |
| 221 void GetSaveFileNameClient::WaitForCompletion() { |
| 222 event_.Wait(); |
| 223 } |
| 224 |
| 225 void GetSaveFileNameClient::OnProcessCrashed(int exit_code) { |
| 226 event_.Signal(); |
| 227 } |
| 228 |
| 229 void GetSaveFileNameClient::OnProcessLaunchFailed() { |
| 230 event_.Signal(); |
| 231 } |
| 232 |
| 233 bool GetSaveFileNameClient::OnMessageReceived(const IPC::Message& message) { |
| 234 bool handled = true; |
| 235 IPC_BEGIN_MESSAGE_MAP(GetSaveFileNameClient, message) |
| 236 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_GetSaveFileName_Failed, |
| 237 OnFailure) |
| 238 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_GetSaveFileName_Result, |
| 239 OnResult) |
| 240 IPC_MESSAGE_UNHANDLED(handled = false) |
| 241 IPC_END_MESSAGE_MAP() |
| 242 return handled; |
| 243 } |
| 244 |
| 245 GetSaveFileNameClient::~GetSaveFileNameClient() {} |
| 246 |
| 247 void GetSaveFileNameClient::OnResult(const base::FilePath& path, |
| 248 int one_based_filter_index) { |
| 249 path_ = path; |
| 250 one_based_filter_index_ = one_based_filter_index; |
| 251 event_.Signal(); |
| 252 } |
| 253 |
| 254 void GetSaveFileNameClient::OnFailure() { |
| 255 event_.Signal(); |
| 256 } |
| 257 |
| 258 // Initiates IPC with a new utility process using |client|. Instructs the |
| 259 // utility process to call GetSaveFileName with |ofn|. |current_task_runner| |
| 260 // must be the currently executing task runner. |
| 261 void DoInvokeGetSaveFileName( |
| 262 OPENFILENAME* ofn, |
| 263 scoped_refptr<GetSaveFileNameClient> client, |
| 264 const scoped_refptr<base::SequencedTaskRunner>& current_task_runner) { |
| 265 DCHECK(current_task_runner->RunsTasksOnCurrentThread()); |
| 266 |
| 267 base::WeakPtr<content::UtilityProcessHost> utility_process_host( |
| 268 content::UtilityProcessHost::Create(client, current_task_runner) |
| 269 ->AsWeakPtr()); |
| 270 utility_process_host->DisableSandbox(); |
| 271 ChromeUtilityMsg_GetSaveFileName_Params params; |
| 272 params.owner = ofn->hwndOwner; |
| 273 // We can't pass the hook function over IPC. |
| 274 params.flags = ofn->Flags & ~OFN_ENABLEHOOK; |
| 275 params.filters = ui::win::OpenFileName::GetFilters(ofn); |
| 276 params.one_based_filter_index = ofn->nFilterIndex; |
| 277 params.suggested_filename = base::FilePath(ofn->lpstrFile); |
| 278 params.initial_directory = base::FilePath( |
| 279 ofn->lpstrInitialDir ? ofn->lpstrInitialDir : base::string16()); |
| 280 params.default_extension = |
| 281 ofn->lpstrDefExt ? base::string16(ofn->lpstrDefExt) : base::string16(); |
| 282 |
| 283 utility_process_host->Send(new ChromeUtilityMsg_GetSaveFileName(params)); |
| 284 } |
| 285 |
| 286 // Invokes GetSaveFileName in a utility process. Blocks until the result is |
| 287 // received. Uses |blocking_task_runner| for IPC. |
| 288 bool GetSaveFileNameInUtilityProcess( |
| 289 const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner, |
| 290 OPENFILENAME* ofn) { |
| 291 scoped_refptr<GetSaveFileNameClient> client(new GetSaveFileNameClient); |
| 292 blocking_task_runner->PostTask( |
| 293 FROM_HERE, |
| 294 base::Bind(&DoInvokeGetSaveFileName, |
| 295 base::Unretained(ofn), client, blocking_task_runner)); |
| 296 client->WaitForCompletion(); |
| 297 |
| 298 if (client->path().empty()) |
| 299 return false; |
| 300 |
| 301 base::wcslcpy(ofn->lpstrFile, client->path().value().c_str(), ofn->nMaxFile); |
| 302 ofn->nFilterIndex = client->one_based_filter_index(); |
| 303 |
| 304 return true; |
| 305 } |
| 306 |
| 307 // Implements GetSaveFileName for CreateWinSelectFileDialog by delegating either |
| 308 // to Metro or a utility process. |
| 309 bool GetSaveFileNameImpl( |
| 310 const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner, |
| 311 OPENFILENAME* ofn) { |
| 312 if (base::win::IsMetroProcess()) |
| 313 return CallMetroOPENFILENAMEMethod("MetroGetSaveFileName", ofn); |
| 314 |
| 315 if (ShouldIsolateShellOperations()) |
| 316 return GetSaveFileNameInUtilityProcess(blocking_task_runner, ofn); |
| 317 |
| 318 return ::GetSaveFileName(ofn) == TRUE; |
| 319 } |
| 320 |
174 } // namespace | 321 } // namespace |
175 | 322 |
176 ChromeSelectFileDialogFactory::ChromeSelectFileDialogFactory( | 323 ChromeSelectFileDialogFactory::ChromeSelectFileDialogFactory( |
177 const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner) | 324 const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner) |
178 : blocking_task_runner_(blocking_task_runner) { | 325 : blocking_task_runner_(blocking_task_runner) { |
179 } | 326 } |
180 | 327 |
181 ChromeSelectFileDialogFactory::~ChromeSelectFileDialogFactory() {} | 328 ChromeSelectFileDialogFactory::~ChromeSelectFileDialogFactory() {} |
182 | 329 |
183 ui::SelectFileDialog* ChromeSelectFileDialogFactory::Create( | 330 ui::SelectFileDialog* ChromeSelectFileDialogFactory::Create( |
184 ui::SelectFileDialog::Listener* listener, | 331 ui::SelectFileDialog::Listener* listener, |
185 ui::SelectFilePolicy* policy) { | 332 ui::SelectFilePolicy* policy) { |
186 return ui::CreateWinSelectFileDialog( | 333 return ui::CreateWinSelectFileDialog( |
187 listener, | 334 listener, |
188 policy, | 335 policy, |
189 base::Bind(GetOpenFileNameImpl, blocking_task_runner_)); | 336 base::Bind(GetOpenFileNameImpl, blocking_task_runner_), |
| 337 base::Bind(GetSaveFileNameImpl, blocking_task_runner_)); |
190 } | 338 } |
OLD | NEW |