Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / chrome_select_file_dialog_factory_win.cc
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.
4
5 #include "chrome/browser/chrome_select_file_dialog_factory_win.h"
6
7 #include <Windows.h>
8 #include <commdlg.h>
9
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"
25
26 namespace {
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
51 // Receives the GetOpenFileName result from the utility process.
52 class GetOpenFileNameClient : public content::UtilityProcessHostClient {
53  public:
54   GetOpenFileNameClient();
55
56   // Blocks until the GetOpenFileName result is received (including failure to
57   // launch or a crash of the utility process).
58   void WaitForCompletion();
59
60   // Returns the selected directory.
61   const base::FilePath& directory() const { return directory_; }
62
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_; }
66
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;
71
72  protected:
73   virtual ~GetOpenFileNameClient();
74
75  private:
76   void OnResult(const base::FilePath& directory,
77                 const std::vector<base::FilePath>& filenames);
78   void OnFailure();
79
80   base::FilePath directory_;
81   std::vector<base::FilePath> filenames_;
82   base::WaitableEvent event_;
83
84   DISALLOW_COPY_AND_ASSIGN(GetOpenFileNameClient);
85 };
86
87 GetOpenFileNameClient::GetOpenFileNameClient() : event_(true, false) {
88 }
89
90 void GetOpenFileNameClient::WaitForCompletion() {
91   event_.Wait();
92 }
93
94 void GetOpenFileNameClient::OnProcessCrashed(int exit_code) {
95   event_.Signal();
96 }
97
98 void GetOpenFileNameClient::OnProcessLaunchFailed() {
99   event_.Signal();
100 }
101
102 bool GetOpenFileNameClient::OnMessageReceived(const IPC::Message& message) {
103   bool handled = true;
104   IPC_BEGIN_MESSAGE_MAP(GetOpenFileNameClient, message)
105     IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_GetOpenFileName_Failed,
106                         OnFailure)
107     IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_GetOpenFileName_Result,
108                         OnResult)
109     IPC_MESSAGE_UNHANDLED(handled = false)
110   IPC_END_MESSAGE_MAP()
111   return handled;
112 }
113
114 GetOpenFileNameClient::~GetOpenFileNameClient() {}
115
116 void GetOpenFileNameClient::OnResult(
117     const base::FilePath& directory,
118     const std::vector<base::FilePath>& filenames) {
119   directory_ = directory;
120   filenames_ = filenames;
121   event_.Signal();
122 }
123
124 void GetOpenFileNameClient::OnFailure() {
125   event_.Signal();
126 }
127
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(
132     OPENFILENAME* ofn,
133     scoped_refptr<GetOpenFileNameClient> client,
134     const scoped_refptr<base::SequencedTaskRunner>& current_task_runner) {
135   DCHECK(current_task_runner->RunsTasksOnCurrentThread());
136
137   base::WeakPtr<content::UtilityProcessHost> utility_process_host(
138       content::UtilityProcessHost::Create(client, current_task_runner)
139       ->AsWeakPtr());
140   utility_process_host->DisableSandbox();
141   utility_process_host->Send(new ChromeUtilityMsg_GetOpenFileName(
142       ofn->hwndOwner,
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
146                                           : base::string16()),
147       base::FilePath(ofn->lpstrFile)));
148 }
149
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,
154     OPENFILENAME* ofn) {
155   scoped_refptr<GetOpenFileNameClient> client(new GetOpenFileNameClient);
156   blocking_task_runner->PostTask(
157       FROM_HERE,
158       base::Bind(&DoInvokeGetOpenFileName,
159                  base::Unretained(ofn), client, blocking_task_runner));
160   client->WaitForCompletion();
161
162   if (!client->filenames().size())
163     return false;
164
165   ui::win::OpenFileName::SetResult(
166       client->directory(), client->filenames(), ofn);
167   return true;
168 }
169
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,
174     OPENFILENAME* ofn) {
175   if (base::win::IsMetroProcess())
176     return CallMetroOPENFILENAMEMethod("MetroGetOpenFileName", ofn);
177
178   if (ShouldIsolateShellOperations())
179     return GetOpenFileNameInUtilityProcess(blocking_task_runner, ofn);
180
181   return ::GetOpenFileName(ofn) == TRUE;
182 }
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
321 }  // namespace
322
323 ChromeSelectFileDialogFactory::ChromeSelectFileDialogFactory(
324     const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner)
325     : blocking_task_runner_(blocking_task_runner) {
326 }
327
328 ChromeSelectFileDialogFactory::~ChromeSelectFileDialogFactory() {}
329
330 ui::SelectFileDialog* ChromeSelectFileDialogFactory::Create(
331     ui::SelectFileDialog::Listener* listener,
332     ui::SelectFilePolicy* policy) {
333   return ui::CreateWinSelectFileDialog(
334       listener,
335       policy,
336       base::Bind(GetOpenFileNameImpl, blocking_task_runner_),
337       base::Bind(GetSaveFileNameImpl, blocking_task_runner_));
338 }