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

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: Nits 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
« no previous file with comments | « chrome/browser/win/chrome_select_file_dialog_factory.h ('k') | chrome/chrome_common.gypi » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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};
dcheng 2016/07/11 09:00:46 Per https://sites.google.com/a/chromium.org/dev/de
Patrick Monette 2016/07/11 22:11:37 Doesn't work as base::Feature doesn't have a const
32 "Enabled";
33 }
34 28
35 // Receives the GetOpenFileName result from the utility process. 29 // Implements GetOpenFileName() and GetSaveFileName() for
36 class GetOpenFileNameClient : public content::UtilityProcessHostClient { 30 // CreateWinSelectFileDialog by delegating to a utility process.
31 class OpenFileNameClient {
37 public: 32 public:
38 GetOpenFileNameClient(); 33 OpenFileNameClient();
39 34
40 // Blocks until the GetOpenFileName result is received (including failure to 35 // Invokes ::GetOpenFileName() and stores the result into |directory| and
41 // launch or a crash of the utility process). 36 // |filenames|. Returns false on failure.
42 void WaitForCompletion(); 37 bool BlockingGetOpenFileName(OPENFILENAME* ofn);
43 38
44 // Returns the selected directory. 39 // Invokes ::GetSaveFileName() and stores the result into |path| and
45 const base::FilePath& directory() const { return directory_; } 40 // |one_based_filter_index|. Returns false on failure.
46 41 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 42
59 private: 43 private:
60 void OnResult(const base::FilePath& directory, 44 void StartClient();
61 const std::vector<base::FilePath>& filenames);
62 void OnFailure();
63 45
46 void InvokeGetOpenFileNameOnIOThread(OPENFILENAME* ofn);
47 void InvokeGetSaveFileNameOnIOThread(OPENFILENAME* ofn);
48
49 // Callbacks for Mojo invokation.
50 void OnDidGetOpenFileNames(const base::FilePath& directory,
51 mojo::Array<base::FilePath> filenames);
52 void OnDidGetSaveFileName(const base::FilePath& path,
53 uint32_t one_based_filter_index);
54
55 void OnConnectionError();
56
57 // Must only be accessed on the IO thread.
58 std::unique_ptr<content::UtilityProcessMojoClient<mojom::ShellHandler>>
59 client_;
60
61 // This is used to block until the result is received.
62 base::WaitableEvent result_received_event_;
63
64 // Result variables for GetOpenFileName.
64 base::FilePath directory_; 65 base::FilePath directory_;
65 std::vector<base::FilePath> filenames_; 66 std::vector<base::FilePath> filenames_;
66 base::WaitableEvent event_;
67 67
68 DISALLOW_COPY_AND_ASSIGN(GetOpenFileNameClient); 68 // Result variables for GetSaveFileName.
69 base::FilePath path_;
70 DWORD one_based_filter_index_ = 0;
71
72 DISALLOW_COPY_AND_ASSIGN(OpenFileNameClient);
69 }; 73 };
70 74
71 GetOpenFileNameClient::GetOpenFileNameClient() 75 OpenFileNameClient::OpenFileNameClient()
72 : event_(base::WaitableEvent::ResetPolicy::MANUAL, 76 : result_received_event_(base::WaitableEvent::ResetPolicy::MANUAL,
73 base::WaitableEvent::InitialState::NOT_SIGNALED) {} 77 base::WaitableEvent::InitialState::NOT_SIGNALED) {}
74 78
75 void GetOpenFileNameClient::WaitForCompletion() { 79 bool OpenFileNameClient::BlockingGetOpenFileName(OPENFILENAME* ofn) {
76 event_.Wait(); 80 content::BrowserThread::PostTask(
77 } 81 content::BrowserThread::IO, FROM_HERE,
82 base::Bind(&OpenFileNameClient::InvokeGetOpenFileNameOnIOThread,
83 base::Unretained(this), ofn));
78 84
79 void GetOpenFileNameClient::OnProcessCrashed(int exit_code) { 85 result_received_event_.Wait();
80 event_.Signal();
81 }
82 86
83 void GetOpenFileNameClient::OnProcessLaunchFailed(int error_code) { 87 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; 88 return false;
151 89
152 ui::win::OpenFileName::SetResult( 90 ui::win::OpenFileName::SetResult(directory_, filenames_, ofn);
153 client->directory(), client->filenames(), ofn);
154 return true; 91 return true;
155 } 92 }
156 93
157 // Implements GetOpenFileName for CreateWinSelectFileDialog by delegating to a 94 bool OpenFileNameClient::BlockingGetSaveFileName(OPENFILENAME* ofn) {
158 // utility process. 95 content::BrowserThread::PostTask(
159 bool GetOpenFileNameImpl( 96 content::BrowserThread::IO, FROM_HERE,
160 const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner, 97 base::Bind(&OpenFileNameClient::InvokeGetSaveFileNameOnIOThread,
161 OPENFILENAME* ofn) { 98 base::Unretained(this), ofn));
162 if (ShouldIsolateShellOperations()) 99
163 return GetOpenFileNameInUtilityProcess(blocking_task_runner, ofn); 100 result_received_event_.Wait();
101
102 if (path_.empty())
103 return false;
104
105 base::wcslcpy(ofn->lpstrFile, path_.value().c_str(), ofn->nMaxFile);
106 ofn->nFilterIndex = one_based_filter_index_;
107 return true;
108 }
109
110 void OpenFileNameClient::StartClient() {
111 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
112 client_.reset(new content::UtilityProcessMojoClient<mojom::ShellHandler>(
113 l10n_util::GetStringUTF16(IDS_UTILITY_PROCESS_FILE_DIALOG_NAME)));
114
115 client_->set_disable_sandbox();
116 client_->set_error_callback(base::Bind(&OpenFileNameClient::OnConnectionError,
117 base::Unretained(this)));
118
119 client_->Start();
120 }
121
122 void OpenFileNameClient::InvokeGetOpenFileNameOnIOThread(OPENFILENAME* ofn) {
123 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
124 StartClient();
125 client_->service()->DoGetOpenFileName(
126 reinterpret_cast<uint64_t>(ofn->hwndOwner),
127 static_cast<uint32_t>(ofn->Flags), ui::win::OpenFileName::GetFilters(ofn),
128 ofn->lpstrInitialDir ? base::FilePath(ofn->lpstrInitialDir)
129 : base::FilePath(),
130 base::FilePath(ofn->lpstrFile),
131 base::Bind(&OpenFileNameClient::OnDidGetOpenFileNames,
132 base::Unretained(this)));
133 }
134
135 void OpenFileNameClient::InvokeGetSaveFileNameOnIOThread(OPENFILENAME* ofn) {
136 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
137 StartClient();
138 client_->service()->DoGetSaveFileName(
139 reinterpret_cast<uint64_t>(ofn->hwndOwner),
140 static_cast<uint32_t>(ofn->Flags), ui::win::OpenFileName::GetFilters(ofn),
141 ofn->nFilterIndex,
142 ofn->lpstrInitialDir ? base::FilePath(ofn->lpstrInitialDir)
143 : base::FilePath(),
144 base::FilePath(ofn->lpstrFile),
145 ofn->lpstrDefExt ? base::FilePath(ofn->lpstrDefExt) : base::FilePath(),
146 base::Bind(&OpenFileNameClient::OnDidGetSaveFileName,
147 base::Unretained(this)));
148 }
149
150 void OpenFileNameClient::OnDidGetOpenFileNames(
151 const base::FilePath& directory,
152 mojo::Array<base::FilePath> filenames) {
153 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
154 client_.reset();
155 directory_ = directory;
156 filenames_ = filenames.storage();
157
158 result_received_event_.Signal();
159 }
160
161 void OpenFileNameClient::OnDidGetSaveFileName(const base::FilePath& path,
162 uint32_t one_based_filter_index) {
163 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
164 client_.reset();
165 path_ = path;
166 one_based_filter_index_ = one_based_filter_index;
167
168 result_received_event_.Signal();
169 }
170
171 void OpenFileNameClient::OnConnectionError() {
172 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
173 client_.reset();
174 result_received_event_.Signal();
175 }
176
177 bool GetOpenFileNameImpl(OPENFILENAME* ofn) {
178 if (base::FeatureList::IsEnabled(kIsolateShellOperations))
179 return OpenFileNameClient().BlockingGetOpenFileName(ofn);
164 180
165 return ::GetOpenFileName(ofn) == TRUE; 181 return ::GetOpenFileName(ofn) == TRUE;
166 } 182 }
167 183
168 class GetSaveFileNameClient : public content::UtilityProcessHostClient { 184 bool GetSaveFileNameImpl(OPENFILENAME* ofn) {
169 public: 185 if (base::FeatureList::IsEnabled(kIsolateShellOperations))
170 GetSaveFileNameClient(); 186 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 187
302 return ::GetSaveFileName(ofn) == TRUE; 188 return ::GetSaveFileName(ofn) == TRUE;
303 } 189 }
304 190
305 } // namespace 191 } // namespace
306 192
307 ChromeSelectFileDialogFactory::ChromeSelectFileDialogFactory( 193 ChromeSelectFileDialogFactory::ChromeSelectFileDialogFactory() = default;
308 const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner)
309 : blocking_task_runner_(blocking_task_runner) {
310 }
311 194
312 ChromeSelectFileDialogFactory::~ChromeSelectFileDialogFactory() {} 195 ChromeSelectFileDialogFactory::~ChromeSelectFileDialogFactory() = default;
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
« no previous file with comments | « chrome/browser/win/chrome_select_file_dialog_factory.h ('k') | chrome/chrome_common.gypi » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698