Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(116)

Side by Side Diff: chrome/browser/win/chrome_select_file_dialog_factory.cc

Issue 2122303002: Revive experiment to isolate shell operations. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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/win/chrome_select_file_dialog_factory.h" 5 #include "chrome/browser/win/chrome_select_file_dialog_factory.h"
6 6
7 #include <Windows.h> 7 #include <Windows.h>
8 #include <commdlg.h> 8 #include <commdlg.h>
9 9
10 #include <vector>
11
10 #include "base/bind.h" 12 #include "base/bind.h"
11 #include "base/bind_helpers.h" 13 #include "base/bind_helpers.h"
12 #include "base/callback.h" 14 #include "base/feature_list.h"
13 #include "base/location.h" 15 #include "base/strings/string_util.h"
14 #include "base/logging.h"
15 #include "base/macros.h"
16 #include "base/metrics/field_trial.h"
17 #include "base/strings/string16.h"
18 #include "base/synchronization/waitable_event.h" 16 #include "base/synchronization/waitable_event.h"
19 #include "chrome/common/chrome_utility_messages.h" 17 #include "chrome/common/shell_handler_win.mojom.h"
20 #include "chrome/grit/generated_resources.h" 18 #include "chrome/grit/generated_resources.h"
21 #include "content/public/browser/utility_process_host.h" 19 #include "content/public/browser/utility_process_mojo_client.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" 20 #include "ui/base/l10n/l10n_util.h"
25 #include "ui/base/win/open_file_name_win.h" 21 #include "ui/base/win/open_file_name_win.h"
26 #include "ui/shell_dialogs/select_file_dialog_win.h" 22 #include "ui/shell_dialogs/select_file_dialog_win.h"
27 23
28 namespace { 24 namespace {
29 25
30 bool ShouldIsolateShellOperations() { 26 constexpr base::Feature kIsolateShellOperations {
31 return base::FieldTrialList::FindFullName("IsolateShellOperations") == 27 "IsolateShellOperations", base::FEATURE_DISABLED_BY_DEFAULT
32 "Enabled"; 28 };
33 }
34 29
35 // Receives the GetOpenFileName result from the utility process. 30 // Implements GetOpenFileName() and GetSaveFileName() for
36 class GetOpenFileNameClient : public content::UtilityProcessHostClient { 31 // CreateWinSelectFileDialog by delegating to a utility process.
32 class OpenFileNameClient {
37 public: 33 public:
38 GetOpenFileNameClient(); 34 OpenFileNameClient();
39 35
40 // Blocks until the GetOpenFileName result is received (including failure to 36 // Invokes ::GetOpenFileName() and stores the result into |directory| and
41 // launch or a crash of the utility process). 37 // |filenames|. Returns false on failure.
42 void WaitForCompletion(); 38 bool BlockingGetOpenFileName(OPENFILENAME* ofn);
43 39
44 // Returns the selected directory. 40 // Invokes ::GetSaveFileName() and stores the result into |path| and
45 const base::FilePath& directory() const { return directory_; } 41 // |one_based_filter_index|. Returns false on failure.
46 42 bool BlockingGetSaveFileName(OPENFILENAME* ofn);
47 // Returns the list of selected filenames. Each should be interpreted as a
48 // child of directory().
49 const std::vector<base::FilePath>& filenames() const { return filenames_; }
50
51 // UtilityProcessHostClient implementation
52 void OnProcessCrashed(int exit_code) override;
53 void OnProcessLaunchFailed(int error_code) override;
54 bool OnMessageReceived(const IPC::Message& message) override;
55
56 protected:
57 ~GetOpenFileNameClient() override;
58 43
59 private: 44 private:
60 void OnResult(const base::FilePath& directory, 45 void StartClient();
61 const std::vector<base::FilePath>& filenames);
62 void OnFailure();
63 46
47 void InvokeGetOpenFileNameOnIOThread(OPENFILENAME* ofn);
48 void InvokeGetSaveFileNameOnIOThread(OPENFILENAME* ofn);
49
50 // Callbacks for Mojo invokation.
51 void OnGetOpenFileNameResult(const base::FilePath& directory,
sky 2016/07/08 15:28:20 nit: more often than not we use past test, e.g. On
Patrick Monette 2016/07/08 19:36:10 Done.
52 mojo::Array<base::FilePath> filenames);
53 void OnGetSaveFileNameResult(const base::FilePath& path,
54 uint32_t one_based_filter_index);
55
56 void OnConnectionError();
57
58 // Must only be accessed on the IO thread.
59 std::unique_ptr<content::UtilityProcessMojoClient<mojom::ShellHandler>>
60 client_;
61
62 // This is used to block until the result is received.
63 base::WaitableEvent result_received_event_;
64
65 // Result variables for GetOpenFileName.
64 base::FilePath directory_; 66 base::FilePath directory_;
65 std::vector<base::FilePath> filenames_; 67 std::vector<base::FilePath> filenames_;
66 base::WaitableEvent event_;
67 68
68 DISALLOW_COPY_AND_ASSIGN(GetOpenFileNameClient); 69 // Result variables for GetSaveFileName.
70 base::FilePath path_;
71 DWORD one_based_filter_index_;
sky 2016/07/08 15:28:20 Please initialize this.
Patrick Monette 2016/07/08 19:36:10 Done.
72
73 DISALLOW_COPY_AND_ASSIGN(OpenFileNameClient);
69 }; 74 };
70 75
71 GetOpenFileNameClient::GetOpenFileNameClient() 76 OpenFileNameClient::OpenFileNameClient()
72 : event_(base::WaitableEvent::ResetPolicy::MANUAL, 77 : result_received_event_(base::WaitableEvent::ResetPolicy::MANUAL,
73 base::WaitableEvent::InitialState::NOT_SIGNALED) {} 78 base::WaitableEvent::InitialState::NOT_SIGNALED) {}
74 79
75 void GetOpenFileNameClient::WaitForCompletion() { 80 bool OpenFileNameClient::BlockingGetOpenFileName(OPENFILENAME* ofn) {
76 event_.Wait(); 81 content::BrowserThread::PostTask(
77 } 82 content::BrowserThread::IO, FROM_HERE,
83 base::Bind(&OpenFileNameClient::InvokeGetOpenFileNameOnIOThread,
84 base::Unretained(this), ofn));
78 85
79 void GetOpenFileNameClient::OnProcessCrashed(int exit_code) { 86 result_received_event_.Wait();
80 event_.Signal();
81 }
82 87
83 void GetOpenFileNameClient::OnProcessLaunchFailed(int error_code) { 88 if (filenames_.empty())
84 event_.Signal();
85 }
86
87 bool GetOpenFileNameClient::OnMessageReceived(const IPC::Message& message) {
88 bool handled = true;
89 IPC_BEGIN_MESSAGE_MAP(GetOpenFileNameClient, message)
90 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_GetOpenFileName_Failed,
91 OnFailure)
92 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_GetOpenFileName_Result,
93 OnResult)
94 IPC_MESSAGE_UNHANDLED(handled = false)
95 IPC_END_MESSAGE_MAP()
96 return handled;
97 }
98
99 GetOpenFileNameClient::~GetOpenFileNameClient() {}
100
101 void GetOpenFileNameClient::OnResult(
102 const base::FilePath& directory,
103 const std::vector<base::FilePath>& filenames) {
104 directory_ = directory;
105 filenames_ = filenames;
106 event_.Signal();
107 }
108
109 void GetOpenFileNameClient::OnFailure() {
110 event_.Signal();
111 }
112
113 // Initiates IPC with a new utility process using |client|. Instructs the
114 // utility process to call GetOpenFileName with |ofn|. |current_task_runner|
115 // must be the currently executing task runner.
116 void DoInvokeGetOpenFileName(
117 OPENFILENAME* ofn,
118 scoped_refptr<GetOpenFileNameClient> client,
119 const scoped_refptr<base::SequencedTaskRunner>& current_task_runner) {
120 DCHECK(current_task_runner->RunsTasksOnCurrentThread());
121
122 base::WeakPtr<content::UtilityProcessHost> utility_process_host(
123 content::UtilityProcessHost::Create(client, current_task_runner)
124 ->AsWeakPtr());
125 utility_process_host->SetName(l10n_util::GetStringUTF16(
126 IDS_UTILITY_PROCESS_FILE_DIALOG_NAME));
127 utility_process_host->DisableSandbox();
128 utility_process_host->Send(new ChromeUtilityMsg_GetOpenFileName(
129 ofn->hwndOwner,
130 ofn->Flags & ~OFN_ENABLEHOOK, // We can't send a hook function over IPC.
131 ui::win::OpenFileName::GetFilters(ofn),
132 base::FilePath(ofn->lpstrInitialDir ? ofn->lpstrInitialDir
133 : base::string16()),
134 base::FilePath(ofn->lpstrFile)));
135 }
136
137 // Invokes GetOpenFileName in a utility process. Blocks until the result is
138 // received. Uses |blocking_task_runner| for IPC.
139 bool GetOpenFileNameInUtilityProcess(
140 const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner,
141 OPENFILENAME* ofn) {
142 scoped_refptr<GetOpenFileNameClient> client(new GetOpenFileNameClient);
143 blocking_task_runner->PostTask(
144 FROM_HERE,
145 base::Bind(&DoInvokeGetOpenFileName,
146 base::Unretained(ofn), client, blocking_task_runner));
147 client->WaitForCompletion();
148
149 if (client->filenames().empty())
150 return false; 89 return false;
151 90
152 ui::win::OpenFileName::SetResult( 91 ui::win::OpenFileName::SetResult(directory_, filenames_, ofn);
153 client->directory(), client->filenames(), ofn);
154 return true; 92 return true;
155 } 93 }
156 94
157 // Implements GetOpenFileName for CreateWinSelectFileDialog by delegating to a 95 bool OpenFileNameClient::BlockingGetSaveFileName(OPENFILENAME* ofn) {
158 // utility process. 96 content::BrowserThread::PostTask(
159 bool GetOpenFileNameImpl( 97 content::BrowserThread::IO, FROM_HERE,
160 const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner, 98 base::Bind(&OpenFileNameClient::InvokeGetSaveFileNameOnIOThread,
161 OPENFILENAME* ofn) { 99 base::Unretained(this), ofn));
162 if (ShouldIsolateShellOperations()) 100
163 return GetOpenFileNameInUtilityProcess(blocking_task_runner, ofn); 101 result_received_event_.Wait();
102
103 if (path_.empty())
104 return false;
105
106 base::wcslcpy(ofn->lpstrFile, path_.value().c_str(), ofn->nMaxFile);
107 ofn->nFilterIndex = one_based_filter_index_;
108 return true;
109 }
110
111 void OpenFileNameClient::StartClient() {
112 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
113 client_.reset(new content::UtilityProcessMojoClient<mojom::ShellHandler>(
114 l10n_util::GetStringUTF16(IDS_UTILITY_PROCESS_FILE_DIALOG_NAME)));
115
116 client_->set_disable_sandbox();
117 client_->set_error_callback(base::Bind(&OpenFileNameClient::OnConnectionError,
118 base::Unretained(this)));
119
120 client_->Start();
121 }
122
123 void OpenFileNameClient::InvokeGetOpenFileNameOnIOThread(OPENFILENAME* ofn) {
124 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
125 StartClient();
126 client_->service()->DoGetOpenFileName(
127 reinterpret_cast<uint64_t>(ofn->hwndOwner),
128 static_cast<uint32_t>(ofn->Flags), ui::win::OpenFileName::GetFilters(ofn),
129 ofn->lpstrInitialDir ? base::FilePath(ofn->lpstrInitialDir)
130 : base::FilePath(),
131 base::FilePath(ofn->lpstrFile),
132 base::Bind(&OpenFileNameClient::OnGetOpenFileNameResult,
133 base::Unretained(this)));
134 }
135
136 void OpenFileNameClient::InvokeGetSaveFileNameOnIOThread(OPENFILENAME* ofn) {
137 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
138 StartClient();
139 client_->service()->DoGetSaveFileName(
140 reinterpret_cast<uint64_t>(ofn->hwndOwner),
141 static_cast<uint32_t>(ofn->Flags), ui::win::OpenFileName::GetFilters(ofn),
142 ofn->nFilterIndex,
143 ofn->lpstrInitialDir ? base::FilePath(ofn->lpstrInitialDir)
144 : base::FilePath(),
145 base::FilePath(ofn->lpstrFile),
146 ofn->lpstrDefExt ? base::FilePath(ofn->lpstrDefExt) : base::FilePath(),
147 base::Bind(&OpenFileNameClient::OnGetSaveFileNameResult,
148 base::Unretained(this)));
149 }
150
151 void OpenFileNameClient::OnGetOpenFileNameResult(
152 const base::FilePath& directory,
153 mojo::Array<base::FilePath> filenames) {
154 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
155 client_.reset();
156 directory_ = directory;
157 filenames_ = filenames.storage();
158
159 result_received_event_.Signal();
160 }
161
162 void OpenFileNameClient::OnGetSaveFileNameResult(
163 const base::FilePath& path,
164 uint32_t one_based_filter_index) {
165 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
166 client_.reset();
167 path_ = path;
168 one_based_filter_index_ = one_based_filter_index;
169
170 result_received_event_.Signal();
171 }
172
173 void OpenFileNameClient::OnConnectionError() {
174 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
175 client_.reset();
176 result_received_event_.Signal();
177 }
178
179 bool GetOpenFileNameImpl(OPENFILENAME* ofn) {
180 if (base::FeatureList::IsEnabled(kIsolateShellOperations))
181 return OpenFileNameClient().BlockingGetOpenFileName(ofn);
164 182
165 return ::GetOpenFileName(ofn) == TRUE; 183 return ::GetOpenFileName(ofn) == TRUE;
166 } 184 }
167 185
168 class GetSaveFileNameClient : public content::UtilityProcessHostClient { 186 bool GetSaveFileNameImpl(OPENFILENAME* ofn) {
169 public: 187 if (base::FeatureList::IsEnabled(kIsolateShellOperations))
170 GetSaveFileNameClient(); 188 return OpenFileNameClient().BlockingGetSaveFileName(ofn);
171
172 // Blocks until the GetSaveFileName result is received (including failure to
173 // launch or a crash of the utility process).
174 void WaitForCompletion();
175
176 // Returns the selected path.
177 const base::FilePath& path() const { return path_; }
178
179 // Returns the index of the user-selected filter.
180 int one_based_filter_index() const { return one_based_filter_index_; }
181
182 // UtilityProcessHostClient implementation
183 void OnProcessCrashed(int exit_code) override;
184 void OnProcessLaunchFailed(int error_code) override;
185 bool OnMessageReceived(const IPC::Message& message) override;
186
187 protected:
188 ~GetSaveFileNameClient() override;
189
190 private:
191 void OnResult(const base::FilePath& path, int one_based_filter_index);
192 void OnFailure();
193
194 base::FilePath path_;
195 int one_based_filter_index_;
196 base::WaitableEvent event_;
197
198 DISALLOW_COPY_AND_ASSIGN(GetSaveFileNameClient);
199 };
200
201 GetSaveFileNameClient::GetSaveFileNameClient()
202 : one_based_filter_index_(0),
203 event_(base::WaitableEvent::ResetPolicy::MANUAL,
204 base::WaitableEvent::InitialState::NOT_SIGNALED) {}
205
206 void GetSaveFileNameClient::WaitForCompletion() {
207 event_.Wait();
208 }
209
210 void GetSaveFileNameClient::OnProcessCrashed(int exit_code) {
211 event_.Signal();
212 }
213
214 void GetSaveFileNameClient::OnProcessLaunchFailed(int error_code) {
215 event_.Signal();
216 }
217
218 bool GetSaveFileNameClient::OnMessageReceived(const IPC::Message& message) {
219 bool handled = true;
220 IPC_BEGIN_MESSAGE_MAP(GetSaveFileNameClient, message)
221 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_GetSaveFileName_Failed,
222 OnFailure)
223 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_GetSaveFileName_Result,
224 OnResult)
225 IPC_MESSAGE_UNHANDLED(handled = false)
226 IPC_END_MESSAGE_MAP()
227 return handled;
228 }
229
230 GetSaveFileNameClient::~GetSaveFileNameClient() {}
231
232 void GetSaveFileNameClient::OnResult(const base::FilePath& path,
233 int one_based_filter_index) {
234 path_ = path;
235 one_based_filter_index_ = one_based_filter_index;
236 event_.Signal();
237 }
238
239 void GetSaveFileNameClient::OnFailure() {
240 event_.Signal();
241 }
242
243 // Initiates IPC with a new utility process using |client|. Instructs the
244 // utility process to call GetSaveFileName with |ofn|. |current_task_runner|
245 // must be the currently executing task runner.
246 void DoInvokeGetSaveFileName(
247 OPENFILENAME* ofn,
248 scoped_refptr<GetSaveFileNameClient> client,
249 const scoped_refptr<base::SequencedTaskRunner>& current_task_runner) {
250 DCHECK(current_task_runner->RunsTasksOnCurrentThread());
251
252 base::WeakPtr<content::UtilityProcessHost> utility_process_host(
253 content::UtilityProcessHost::Create(client, current_task_runner)
254 ->AsWeakPtr());
255 utility_process_host->SetName(l10n_util::GetStringUTF16(
256 IDS_UTILITY_PROCESS_FILE_DIALOG_NAME));
257 utility_process_host->DisableSandbox();
258 ChromeUtilityMsg_GetSaveFileName_Params params;
259 params.owner = ofn->hwndOwner;
260 // We can't pass the hook function over IPC.
261 params.flags = ofn->Flags & ~OFN_ENABLEHOOK;
262 params.filters = ui::win::OpenFileName::GetFilters(ofn);
263 params.one_based_filter_index = ofn->nFilterIndex;
264 params.suggested_filename = base::FilePath(ofn->lpstrFile);
265 params.initial_directory = base::FilePath(
266 ofn->lpstrInitialDir ? ofn->lpstrInitialDir : base::string16());
267 params.default_extension =
268 ofn->lpstrDefExt ? base::string16(ofn->lpstrDefExt) : base::string16();
269
270 utility_process_host->Send(new ChromeUtilityMsg_GetSaveFileName(params));
271 }
272
273 // Invokes GetSaveFileName in a utility process. Blocks until the result is
274 // received. Uses |blocking_task_runner| for IPC.
275 bool GetSaveFileNameInUtilityProcess(
276 const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner,
277 OPENFILENAME* ofn) {
278 scoped_refptr<GetSaveFileNameClient> client(new GetSaveFileNameClient);
279 blocking_task_runner->PostTask(
280 FROM_HERE,
281 base::Bind(&DoInvokeGetSaveFileName,
282 base::Unretained(ofn), client, blocking_task_runner));
283 client->WaitForCompletion();
284
285 if (client->path().empty())
286 return false;
287
288 base::wcslcpy(ofn->lpstrFile, client->path().value().c_str(), ofn->nMaxFile);
289 ofn->nFilterIndex = client->one_based_filter_index();
290
291 return true;
292 }
293
294 // Implements GetSaveFileName for CreateWinSelectFileDialog by delegating to a
295 // utility process.
296 bool GetSaveFileNameImpl(
297 const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner,
298 OPENFILENAME* ofn) {
299 if (ShouldIsolateShellOperations())
300 return GetSaveFileNameInUtilityProcess(blocking_task_runner, ofn);
301 189
302 return ::GetSaveFileName(ofn) == TRUE; 190 return ::GetSaveFileName(ofn) == TRUE;
303 } 191 }
304 192
305 } // namespace 193 } // namespace
306 194
307 ChromeSelectFileDialogFactory::ChromeSelectFileDialogFactory( 195 ChromeSelectFileDialogFactory::ChromeSelectFileDialogFactory() = default;
308 const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner)
309 : blocking_task_runner_(blocking_task_runner) {
310 }
311
312 ChromeSelectFileDialogFactory::~ChromeSelectFileDialogFactory() {}
313 196
314 ui::SelectFileDialog* ChromeSelectFileDialogFactory::Create( 197 ui::SelectFileDialog* ChromeSelectFileDialogFactory::Create(
315 ui::SelectFileDialog::Listener* listener, 198 ui::SelectFileDialog::Listener* listener,
316 ui::SelectFilePolicy* policy) { 199 ui::SelectFilePolicy* policy) {
317 return ui::CreateWinSelectFileDialog( 200 return ui::CreateWinSelectFileDialog(listener, policy,
318 listener, 201 base::Bind(GetOpenFileNameImpl),
319 policy, 202 base::Bind(GetSaveFileNameImpl));
320 base::Bind(GetOpenFileNameImpl, blocking_task_runner_),
321 base::Bind(GetSaveFileNameImpl, blocking_task_runner_));
322 } 203 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698