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
3 // found in the LICENSE file.
5 #include "chrome/browser/chrome_select_file_dialog_factory_win.h"
10 #include "base/bind.h"
11 #include "base/bind_helpers.h"
12 #include "base/callback.h"
13 #include "base/location.h"
14 #include "base/logging.h"
15 #include "base/metrics/field_trial.h"
16 #include "base/strings/string16.h"
17 #include "base/synchronization/waitable_event.h"
18 #include "base/win/metro.h"
19 #include "chrome/common/chrome_utility_messages.h"
20 #include "content/public/browser/utility_process_host.h"
21 #include "content/public/browser/utility_process_host_client.h"
22 #include "ipc/ipc_message_macros.h"
23 #include "ui/base/win/open_file_name_win.h"
24 #include "ui/shell_dialogs/select_file_dialog_win.h"
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();
33 if (metro_module
!= NULL
) {
34 metro_method
= reinterpret_cast<MetroOPENFILENAMEMethod
>(
35 ::GetProcAddress(metro_module
, method_name
));
38 if (metro_method
!= NULL
)
39 return metro_method(ofn
) == TRUE
;
46 bool ShouldIsolateShellOperations() {
47 return base::FieldTrialList::FindFullName("IsolateShellOperations") ==
51 // Receives the GetOpenFileName result from the utility process.
52 class GetOpenFileNameClient
: public content::UtilityProcessHostClient
{
54 GetOpenFileNameClient();
56 // Blocks until the GetOpenFileName result is received (including failure to
57 // launch or a crash of the utility process).
58 void WaitForCompletion();
60 // Returns the selected directory.
61 const base::FilePath
& directory() const { return directory_
; }
63 // Returns the list of selected filenames. Each should be interpreted as a
64 // child of directory().
65 const std::vector
<base::FilePath
>& filenames() const { return filenames_
; }
67 // UtilityProcessHostClient implementation
68 virtual void OnProcessCrashed(int exit_code
) OVERRIDE
;
69 virtual void OnProcessLaunchFailed() OVERRIDE
;
70 virtual bool OnMessageReceived(const IPC::Message
& message
) OVERRIDE
;
73 virtual ~GetOpenFileNameClient();
76 void OnResult(const base::FilePath
& directory
,
77 const std::vector
<base::FilePath
>& filenames
);
80 base::FilePath directory_
;
81 std::vector
<base::FilePath
> filenames_
;
82 base::WaitableEvent event_
;
84 DISALLOW_COPY_AND_ASSIGN(GetOpenFileNameClient
);
87 GetOpenFileNameClient::GetOpenFileNameClient() : event_(true, false) {
90 void GetOpenFileNameClient::WaitForCompletion() {
94 void GetOpenFileNameClient::OnProcessCrashed(int exit_code
) {
98 void GetOpenFileNameClient::OnProcessLaunchFailed() {
102 bool GetOpenFileNameClient::OnMessageReceived(const IPC::Message
& message
) {
104 IPC_BEGIN_MESSAGE_MAP(GetOpenFileNameClient
, message
)
105 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_GetOpenFileName_Failed
,
107 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_GetOpenFileName_Result
,
109 IPC_MESSAGE_UNHANDLED(handled
= false)
110 IPC_END_MESSAGE_MAP()
114 GetOpenFileNameClient::~GetOpenFileNameClient() {}
116 void GetOpenFileNameClient::OnResult(
117 const base::FilePath
& directory
,
118 const std::vector
<base::FilePath
>& filenames
) {
119 directory_
= directory
;
120 filenames_
= filenames
;
124 void GetOpenFileNameClient::OnFailure() {
128 // Initiates IPC with a new utility process using |client|. Instructs the
129 // utility process to call GetOpenFileName with |ofn|. |current_task_runner|
130 // must be the currently executing task runner.
131 void DoInvokeGetOpenFileName(
133 scoped_refptr
<GetOpenFileNameClient
> client
,
134 const scoped_refptr
<base::SequencedTaskRunner
>& current_task_runner
) {
135 DCHECK(current_task_runner
->RunsTasksOnCurrentThread());
137 base::WeakPtr
<content::UtilityProcessHost
> utility_process_host(
138 content::UtilityProcessHost::Create(client
, current_task_runner
)
140 utility_process_host
->DisableSandbox();
141 utility_process_host
->Send(new ChromeUtilityMsg_GetOpenFileName(
143 ofn
->Flags
& ~OFN_ENABLEHOOK
, // We can't send a hook function over IPC.
144 ui::win::OpenFileName::GetFilters(ofn
),
145 base::FilePath(ofn
->lpstrInitialDir
? ofn
->lpstrInitialDir
147 base::FilePath(ofn
->lpstrFile
)));
150 // Invokes GetOpenFileName in a utility process. Blocks until the result is
151 // received. Uses |blocking_task_runner| for IPC.
152 bool GetOpenFileNameInUtilityProcess(
153 const scoped_refptr
<base::SequencedTaskRunner
>& blocking_task_runner
,
155 scoped_refptr
<GetOpenFileNameClient
> client(new GetOpenFileNameClient
);
156 blocking_task_runner
->PostTask(
158 base::Bind(&DoInvokeGetOpenFileName
,
159 base::Unretained(ofn
), client
, blocking_task_runner
));
160 client
->WaitForCompletion();
162 if (!client
->filenames().size())
165 ui::win::OpenFileName::SetResult(
166 client
->directory(), client
->filenames(), ofn
);
170 // Implements GetOpenFileName for CreateWinSelectFileDialog by delegating either
171 // to Metro or a utility process.
172 bool GetOpenFileNameImpl(
173 const scoped_refptr
<base::SequencedTaskRunner
>& blocking_task_runner
,
175 if (base::win::IsMetroProcess())
176 return CallMetroOPENFILENAMEMethod("MetroGetOpenFileName", ofn
);
178 if (ShouldIsolateShellOperations())
179 return GetOpenFileNameInUtilityProcess(blocking_task_runner
, ofn
);
181 return ::GetOpenFileName(ofn
) == TRUE
;
184 class GetSaveFileNameClient
: public content::UtilityProcessHostClient
{
186 GetSaveFileNameClient();
188 // Blocks until the GetSaveFileName result is received (including failure to
189 // launch or a crash of the utility process).
190 void WaitForCompletion();
192 // Returns the selected path.
193 const base::FilePath
& path() const { return path_
; }
195 // Returns the index of the user-selected filter.
196 int one_based_filter_index() const { return one_based_filter_index_
; }
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
;
204 virtual ~GetSaveFileNameClient();
207 void OnResult(const base::FilePath
& path
, int one_based_filter_index
);
210 base::FilePath path_
;
211 int one_based_filter_index_
;
212 base::WaitableEvent event_
;
214 DISALLOW_COPY_AND_ASSIGN(GetSaveFileNameClient
);
217 GetSaveFileNameClient::GetSaveFileNameClient()
218 : event_(true, false), one_based_filter_index_(0) {
221 void GetSaveFileNameClient::WaitForCompletion() {
225 void GetSaveFileNameClient::OnProcessCrashed(int exit_code
) {
229 void GetSaveFileNameClient::OnProcessLaunchFailed() {
233 bool GetSaveFileNameClient::OnMessageReceived(const IPC::Message
& message
) {
235 IPC_BEGIN_MESSAGE_MAP(GetSaveFileNameClient
, message
)
236 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_GetSaveFileName_Failed
,
238 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_GetSaveFileName_Result
,
240 IPC_MESSAGE_UNHANDLED(handled
= false)
241 IPC_END_MESSAGE_MAP()
245 GetSaveFileNameClient::~GetSaveFileNameClient() {}
247 void GetSaveFileNameClient::OnResult(const base::FilePath
& path
,
248 int one_based_filter_index
) {
250 one_based_filter_index_
= one_based_filter_index
;
254 void GetSaveFileNameClient::OnFailure() {
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(
263 scoped_refptr
<GetSaveFileNameClient
> client
,
264 const scoped_refptr
<base::SequencedTaskRunner
>& current_task_runner
) {
265 DCHECK(current_task_runner
->RunsTasksOnCurrentThread());
267 base::WeakPtr
<content::UtilityProcessHost
> utility_process_host(
268 content::UtilityProcessHost::Create(client
, current_task_runner
)
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();
283 utility_process_host
->Send(new ChromeUtilityMsg_GetSaveFileName(params
));
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
,
291 scoped_refptr
<GetSaveFileNameClient
> client(new GetSaveFileNameClient
);
292 blocking_task_runner
->PostTask(
294 base::Bind(&DoInvokeGetSaveFileName
,
295 base::Unretained(ofn
), client
, blocking_task_runner
));
296 client
->WaitForCompletion();
298 if (client
->path().empty())
301 base::wcslcpy(ofn
->lpstrFile
, client
->path().value().c_str(), ofn
->nMaxFile
);
302 ofn
->nFilterIndex
= client
->one_based_filter_index();
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
,
312 if (base::win::IsMetroProcess())
313 return CallMetroOPENFILENAMEMethod("MetroGetSaveFileName", ofn
);
315 if (ShouldIsolateShellOperations())
316 return GetSaveFileNameInUtilityProcess(blocking_task_runner
, ofn
);
318 return ::GetSaveFileName(ofn
) == TRUE
;
323 ChromeSelectFileDialogFactory::ChromeSelectFileDialogFactory(
324 const scoped_refptr
<base::SequencedTaskRunner
>& blocking_task_runner
)
325 : blocking_task_runner_(blocking_task_runner
) {
328 ChromeSelectFileDialogFactory::~ChromeSelectFileDialogFactory() {}
330 ui::SelectFileDialog
* ChromeSelectFileDialogFactory::Create(
331 ui::SelectFileDialog::Listener
* listener
,
332 ui::SelectFilePolicy
* policy
) {
333 return ui::CreateWinSelectFileDialog(
336 base::Bind(GetOpenFileNameImpl
, blocking_task_runner_
),
337 base::Bind(GetSaveFileNameImpl
, blocking_task_runner_
));