Roll src/third_party/WebKit eb1578b:b559582 (svn 193425:193437)
[chromium-blink-merge.git] / chrome / browser / chrome_select_file_dialog_factory_win.cc
blob40d3baa4b60570cd347d5ed250ce53ca1ebd48f7
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"
7 #include <Windows.h>
8 #include <commdlg.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 "chrome/grit/generated_resources.h"
21 #include "content/public/browser/utility_process_host.h"
22 #include "content/public/browser/utility_process_host_client.h"
23 #include "ipc/ipc_message_macros.h"
24 #include "ui/base/l10n/l10n_util.h"
25 #include "ui/base/win/open_file_name_win.h"
26 #include "ui/shell_dialogs/select_file_dialog_win.h"
28 namespace {
30 bool CallMetroOPENFILENAMEMethod(const char* method_name, OPENFILENAME* ofn) {
31 typedef BOOL (*MetroOPENFILENAMEMethod)(OPENFILENAME*);
32 MetroOPENFILENAMEMethod metro_method = NULL;
33 HMODULE metro_module = base::win::GetMetroModule();
35 if (metro_module != NULL) {
36 metro_method = reinterpret_cast<MetroOPENFILENAMEMethod>(
37 ::GetProcAddress(metro_module, method_name));
40 if (metro_method != NULL)
41 return metro_method(ofn) == TRUE;
43 NOTREACHED();
45 return false;
48 bool ShouldIsolateShellOperations() {
49 return base::FieldTrialList::FindFullName("IsolateShellOperations") ==
50 "Enabled";
53 // Receives the GetOpenFileName result from the utility process.
54 class GetOpenFileNameClient : public content::UtilityProcessHostClient {
55 public:
56 GetOpenFileNameClient();
58 // Blocks until the GetOpenFileName result is received (including failure to
59 // launch or a crash of the utility process).
60 void WaitForCompletion();
62 // Returns the selected directory.
63 const base::FilePath& directory() const { return directory_; }
65 // Returns the list of selected filenames. Each should be interpreted as a
66 // child of directory().
67 const std::vector<base::FilePath>& filenames() const { return filenames_; }
69 // UtilityProcessHostClient implementation
70 void OnProcessCrashed(int exit_code) override;
71 void OnProcessLaunchFailed() override;
72 bool OnMessageReceived(const IPC::Message& message) override;
74 protected:
75 virtual ~GetOpenFileNameClient();
77 private:
78 void OnResult(const base::FilePath& directory,
79 const std::vector<base::FilePath>& filenames);
80 void OnFailure();
82 base::FilePath directory_;
83 std::vector<base::FilePath> filenames_;
84 base::WaitableEvent event_;
86 DISALLOW_COPY_AND_ASSIGN(GetOpenFileNameClient);
89 GetOpenFileNameClient::GetOpenFileNameClient() : event_(true, false) {
92 void GetOpenFileNameClient::WaitForCompletion() {
93 event_.Wait();
96 void GetOpenFileNameClient::OnProcessCrashed(int exit_code) {
97 event_.Signal();
100 void GetOpenFileNameClient::OnProcessLaunchFailed() {
101 event_.Signal();
104 bool GetOpenFileNameClient::OnMessageReceived(const IPC::Message& message) {
105 bool handled = true;
106 IPC_BEGIN_MESSAGE_MAP(GetOpenFileNameClient, message)
107 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_GetOpenFileName_Failed,
108 OnFailure)
109 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_GetOpenFileName_Result,
110 OnResult)
111 IPC_MESSAGE_UNHANDLED(handled = false)
112 IPC_END_MESSAGE_MAP()
113 return handled;
116 GetOpenFileNameClient::~GetOpenFileNameClient() {}
118 void GetOpenFileNameClient::OnResult(
119 const base::FilePath& directory,
120 const std::vector<base::FilePath>& filenames) {
121 directory_ = directory;
122 filenames_ = filenames;
123 event_.Signal();
126 void GetOpenFileNameClient::OnFailure() {
127 event_.Signal();
130 // Initiates IPC with a new utility process using |client|. Instructs the
131 // utility process to call GetOpenFileName with |ofn|. |current_task_runner|
132 // must be the currently executing task runner.
133 void DoInvokeGetOpenFileName(
134 OPENFILENAME* ofn,
135 scoped_refptr<GetOpenFileNameClient> client,
136 const scoped_refptr<base::SequencedTaskRunner>& current_task_runner) {
137 DCHECK(current_task_runner->RunsTasksOnCurrentThread());
139 base::WeakPtr<content::UtilityProcessHost> utility_process_host(
140 content::UtilityProcessHost::Create(client, current_task_runner)
141 ->AsWeakPtr());
142 utility_process_host->SetName(l10n_util::GetStringUTF16(
143 IDS_UTILITY_PROCESS_FILE_DIALOG_NAME));
144 utility_process_host->DisableSandbox();
145 utility_process_host->Send(new ChromeUtilityMsg_GetOpenFileName(
146 ofn->hwndOwner,
147 ofn->Flags & ~OFN_ENABLEHOOK, // We can't send a hook function over IPC.
148 ui::win::OpenFileName::GetFilters(ofn),
149 base::FilePath(ofn->lpstrInitialDir ? ofn->lpstrInitialDir
150 : base::string16()),
151 base::FilePath(ofn->lpstrFile)));
154 // Invokes GetOpenFileName in a utility process. Blocks until the result is
155 // received. Uses |blocking_task_runner| for IPC.
156 bool GetOpenFileNameInUtilityProcess(
157 const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner,
158 OPENFILENAME* ofn) {
159 scoped_refptr<GetOpenFileNameClient> client(new GetOpenFileNameClient);
160 blocking_task_runner->PostTask(
161 FROM_HERE,
162 base::Bind(&DoInvokeGetOpenFileName,
163 base::Unretained(ofn), client, blocking_task_runner));
164 client->WaitForCompletion();
166 if (!client->filenames().size())
167 return false;
169 ui::win::OpenFileName::SetResult(
170 client->directory(), client->filenames(), ofn);
171 return true;
174 // Implements GetOpenFileName for CreateWinSelectFileDialog by delegating either
175 // to Metro or a utility process.
176 bool GetOpenFileNameImpl(
177 const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner,
178 OPENFILENAME* ofn) {
179 if (base::win::IsMetroProcess())
180 return CallMetroOPENFILENAMEMethod("MetroGetOpenFileName", ofn);
182 if (ShouldIsolateShellOperations())
183 return GetOpenFileNameInUtilityProcess(blocking_task_runner, ofn);
185 return ::GetOpenFileName(ofn) == TRUE;
188 class GetSaveFileNameClient : public content::UtilityProcessHostClient {
189 public:
190 GetSaveFileNameClient();
192 // Blocks until the GetSaveFileName result is received (including failure to
193 // launch or a crash of the utility process).
194 void WaitForCompletion();
196 // Returns the selected path.
197 const base::FilePath& path() const { return path_; }
199 // Returns the index of the user-selected filter.
200 int one_based_filter_index() const { return one_based_filter_index_; }
202 // UtilityProcessHostClient implementation
203 void OnProcessCrashed(int exit_code) override;
204 void OnProcessLaunchFailed() override;
205 bool OnMessageReceived(const IPC::Message& message) override;
207 protected:
208 virtual ~GetSaveFileNameClient();
210 private:
211 void OnResult(const base::FilePath& path, int one_based_filter_index);
212 void OnFailure();
214 base::FilePath path_;
215 int one_based_filter_index_;
216 base::WaitableEvent event_;
218 DISALLOW_COPY_AND_ASSIGN(GetSaveFileNameClient);
221 GetSaveFileNameClient::GetSaveFileNameClient()
222 : event_(true, false), one_based_filter_index_(0) {
225 void GetSaveFileNameClient::WaitForCompletion() {
226 event_.Wait();
229 void GetSaveFileNameClient::OnProcessCrashed(int exit_code) {
230 event_.Signal();
233 void GetSaveFileNameClient::OnProcessLaunchFailed() {
234 event_.Signal();
237 bool GetSaveFileNameClient::OnMessageReceived(const IPC::Message& message) {
238 bool handled = true;
239 IPC_BEGIN_MESSAGE_MAP(GetSaveFileNameClient, message)
240 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_GetSaveFileName_Failed,
241 OnFailure)
242 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_GetSaveFileName_Result,
243 OnResult)
244 IPC_MESSAGE_UNHANDLED(handled = false)
245 IPC_END_MESSAGE_MAP()
246 return handled;
249 GetSaveFileNameClient::~GetSaveFileNameClient() {}
251 void GetSaveFileNameClient::OnResult(const base::FilePath& path,
252 int one_based_filter_index) {
253 path_ = path;
254 one_based_filter_index_ = one_based_filter_index;
255 event_.Signal();
258 void GetSaveFileNameClient::OnFailure() {
259 event_.Signal();
262 // Initiates IPC with a new utility process using |client|. Instructs the
263 // utility process to call GetSaveFileName with |ofn|. |current_task_runner|
264 // must be the currently executing task runner.
265 void DoInvokeGetSaveFileName(
266 OPENFILENAME* ofn,
267 scoped_refptr<GetSaveFileNameClient> client,
268 const scoped_refptr<base::SequencedTaskRunner>& current_task_runner) {
269 DCHECK(current_task_runner->RunsTasksOnCurrentThread());
271 base::WeakPtr<content::UtilityProcessHost> utility_process_host(
272 content::UtilityProcessHost::Create(client, current_task_runner)
273 ->AsWeakPtr());
274 utility_process_host->SetName(l10n_util::GetStringUTF16(
275 IDS_UTILITY_PROCESS_FILE_DIALOG_NAME));
276 utility_process_host->DisableSandbox();
277 ChromeUtilityMsg_GetSaveFileName_Params params;
278 params.owner = ofn->hwndOwner;
279 // We can't pass the hook function over IPC.
280 params.flags = ofn->Flags & ~OFN_ENABLEHOOK;
281 params.filters = ui::win::OpenFileName::GetFilters(ofn);
282 params.one_based_filter_index = ofn->nFilterIndex;
283 params.suggested_filename = base::FilePath(ofn->lpstrFile);
284 params.initial_directory = base::FilePath(
285 ofn->lpstrInitialDir ? ofn->lpstrInitialDir : base::string16());
286 params.default_extension =
287 ofn->lpstrDefExt ? base::string16(ofn->lpstrDefExt) : base::string16();
289 utility_process_host->Send(new ChromeUtilityMsg_GetSaveFileName(params));
292 // Invokes GetSaveFileName in a utility process. Blocks until the result is
293 // received. Uses |blocking_task_runner| for IPC.
294 bool GetSaveFileNameInUtilityProcess(
295 const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner,
296 OPENFILENAME* ofn) {
297 scoped_refptr<GetSaveFileNameClient> client(new GetSaveFileNameClient);
298 blocking_task_runner->PostTask(
299 FROM_HERE,
300 base::Bind(&DoInvokeGetSaveFileName,
301 base::Unretained(ofn), client, blocking_task_runner));
302 client->WaitForCompletion();
304 if (client->path().empty())
305 return false;
307 base::wcslcpy(ofn->lpstrFile, client->path().value().c_str(), ofn->nMaxFile);
308 ofn->nFilterIndex = client->one_based_filter_index();
310 return true;
313 // Implements GetSaveFileName for CreateWinSelectFileDialog by delegating either
314 // to Metro or a utility process.
315 bool GetSaveFileNameImpl(
316 const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner,
317 OPENFILENAME* ofn) {
318 if (base::win::IsMetroProcess())
319 return CallMetroOPENFILENAMEMethod("MetroGetSaveFileName", ofn);
321 if (ShouldIsolateShellOperations())
322 return GetSaveFileNameInUtilityProcess(blocking_task_runner, ofn);
324 return ::GetSaveFileName(ofn) == TRUE;
327 } // namespace
329 ChromeSelectFileDialogFactory::ChromeSelectFileDialogFactory(
330 const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner)
331 : blocking_task_runner_(blocking_task_runner) {
334 ChromeSelectFileDialogFactory::~ChromeSelectFileDialogFactory() {}
336 ui::SelectFileDialog* ChromeSelectFileDialogFactory::Create(
337 ui::SelectFileDialog::Listener* listener,
338 ui::SelectFilePolicy* policy) {
339 return ui::CreateWinSelectFileDialog(
340 listener,
341 policy,
342 base::Bind(GetOpenFileNameImpl, blocking_task_runner_),
343 base::Bind(GetSaveFileNameImpl, blocking_task_runner_));