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

Side by Side Diff: win8/metro_driver/file_picker.cc

Issue 1586843002: Remove remote tree host and some related input and metro_driver code (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@metro-mode-3
Patch Set: remove ash_unittests from being run Created 4 years, 11 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 | « win8/metro_driver/file_picker.h ('k') | win8/metro_driver/file_picker_ash.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2012 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 "stdafx.h"
6 #include "win8/metro_driver/file_picker.h"
7
8 #include <stddef.h>
9 #include <stdint.h>
10 #include <windows.storage.pickers.h>
11
12 #include "base/bind.h"
13 #include "base/files/file_path.h"
14 #include "base/logging.h"
15 #include "base/macros.h"
16 #include "base/message_loop/message_loop.h"
17 #include "base/strings/string_util.h"
18 #include "base/synchronization/waitable_event.h"
19 #include "base/win/metro.h"
20 #include "base/win/scoped_comptr.h"
21 #include "win8/metro_driver/chrome_app_view.h"
22 #include "win8/metro_driver/winrt_utils.h"
23
24 namespace {
25
26 namespace winstorage = ABI::Windows::Storage;
27 typedef winfoundtn::Collections::IVector<HSTRING> StringVectorItf;
28
29 // TODO(siggi): Complete this implementation and move it to a common place.
30 class StringVectorImpl : public mswr::RuntimeClass<StringVectorItf> {
31 public:
32 ~StringVectorImpl() {
33 std::for_each(strings_.begin(), strings_.end(), ::WindowsDeleteString);
34 }
35
36 HRESULT RuntimeClassInitialize(const std::vector<base::string16>& list) {
37 for (size_t i = 0; i < list.size(); ++i)
38 strings_.push_back(MakeHString(list[i]));
39
40 return S_OK;
41 }
42
43 // IVector<HSTRING> implementation.
44 STDMETHOD(GetAt)(unsigned index, HSTRING* item) {
45 if (index >= strings_.size())
46 return E_INVALIDARG;
47
48 return ::WindowsDuplicateString(strings_[index], item);
49 }
50 STDMETHOD(get_Size)(unsigned *size) {
51 if (strings_.size() > UINT_MAX)
52 return E_UNEXPECTED;
53 *size = static_cast<unsigned>(strings_.size());
54 return S_OK;
55 }
56 STDMETHOD(GetView)(winfoundtn::Collections::IVectorView<HSTRING> **view) {
57 return E_NOTIMPL;
58 }
59 STDMETHOD(IndexOf)(HSTRING value, unsigned *index, boolean *found) {
60 return E_NOTIMPL;
61 }
62
63 // write methods
64 STDMETHOD(SetAt)(unsigned index, HSTRING item) {
65 return E_NOTIMPL;
66 }
67 STDMETHOD(InsertAt)(unsigned index, HSTRING item) {
68 return E_NOTIMPL;
69 }
70 STDMETHOD(RemoveAt)(unsigned index) {
71 return E_NOTIMPL;
72 }
73 STDMETHOD(Append)(HSTRING item) {
74 return E_NOTIMPL;
75 }
76 STDMETHOD(RemoveAtEnd)() {
77 return E_NOTIMPL;
78 }
79 STDMETHOD(Clear)() {
80 return E_NOTIMPL;
81 }
82
83 private:
84 std::vector<HSTRING> strings_;
85 };
86
87 class FilePickerSessionBase {
88 public:
89 // Creates a file picker for open_file_name.
90 explicit FilePickerSessionBase(OPENFILENAME* open_file_name);
91
92 // Runs the picker, returns true on success.
93 bool Run();
94
95 protected:
96 // Creates, configures and starts a file picker.
97 // If the HRESULT returned is a failure code the file picker has not started,
98 // so no callbacks should be expected.
99 virtual HRESULT StartFilePicker() = 0;
100
101 // The parameters to our picker.
102 OPENFILENAME* open_file_name_;
103 // The event Run waits on.
104 base::WaitableEvent event_;
105 // True iff a file picker has successfully finished.
106 bool success_;
107
108 private:
109 // Initiate a file picker, must be called on the metro dispatcher's thread.
110 void DoFilePicker();
111
112 DISALLOW_COPY_AND_ASSIGN(FilePickerSessionBase);
113 };
114
115 class OpenFilePickerSession : public FilePickerSessionBase {
116 public:
117 explicit OpenFilePickerSession(OPENFILENAME* open_file_name);
118
119 private:
120 HRESULT StartFilePicker() override;
121
122 typedef winfoundtn::IAsyncOperation<winstorage::StorageFile*>
123 SingleFileAsyncOp;
124 typedef winfoundtn::Collections::IVectorView<
125 winstorage::StorageFile*> StorageFileVectorCollection;
126 typedef winfoundtn::IAsyncOperation<StorageFileVectorCollection*>
127 MultiFileAsyncOp;
128
129 // Called asynchronously when a single file picker is done.
130 HRESULT SinglePickerDone(SingleFileAsyncOp* async, AsyncStatus status);
131
132 // Called asynchronously when a multi file picker is done.
133 HRESULT MultiPickerDone(MultiFileAsyncOp* async, AsyncStatus status);
134
135 // Composes a multi-file result string suitable for returning to a
136 // from a storage file collection.
137 static HRESULT ComposeMultiFileResult(StorageFileVectorCollection* files,
138 base::string16* result);
139 private:
140 DISALLOW_COPY_AND_ASSIGN(OpenFilePickerSession);
141 };
142
143 class SaveFilePickerSession : public FilePickerSessionBase {
144 public:
145 explicit SaveFilePickerSession(OPENFILENAME* open_file_name);
146
147 private:
148 HRESULT StartFilePicker() override;
149
150 typedef winfoundtn::IAsyncOperation<winstorage::StorageFile*>
151 SaveFileAsyncOp;
152
153 // Called asynchronously when the save file picker is done.
154 HRESULT FilePickerDone(SaveFileAsyncOp* async, AsyncStatus status);
155 };
156
157 FilePickerSessionBase::FilePickerSessionBase(OPENFILENAME* open_file_name)
158 : open_file_name_(open_file_name),
159 event_(true, false),
160 success_(false) {
161 }
162
163 bool FilePickerSessionBase::Run() {
164 DCHECK(globals.appview_msg_loop != NULL);
165
166 // Post the picker request over to the metro thread.
167 bool posted = globals.appview_msg_loop->PostTask(FROM_HERE,
168 base::Bind(&FilePickerSessionBase::DoFilePicker, base::Unretained(this)));
169 if (!posted)
170 return false;
171
172 // Wait for the file picker to complete.
173 event_.Wait();
174
175 return success_;
176 }
177
178 void FilePickerSessionBase::DoFilePicker() {
179 // The file picker will fail if spawned from a snapped application,
180 // so let's attempt to unsnap first if we're in that state.
181 HRESULT hr = ChromeAppView::Unsnap();
182 if (FAILED(hr)) {
183 LOG(ERROR) << "Failed to unsnap for file picker, error 0x" << hr;
184 }
185
186 if (SUCCEEDED(hr))
187 hr = StartFilePicker();
188
189 if (FAILED(hr)) {
190 LOG(ERROR) << "Failed to start file picker, error 0x"
191 << std::hex << hr;
192
193 event_.Signal();
194 }
195 }
196
197 OpenFilePickerSession::OpenFilePickerSession(OPENFILENAME* open_file_name)
198 : FilePickerSessionBase(open_file_name) {
199 }
200
201 HRESULT OpenFilePickerSession::SinglePickerDone(SingleFileAsyncOp* async,
202 AsyncStatus status) {
203 if (status == Completed) {
204 mswr::ComPtr<winstorage::IStorageFile> file;
205 HRESULT hr = async->GetResults(file.GetAddressOf());
206
207 if (file) {
208 mswr::ComPtr<winstorage::IStorageItem> storage_item;
209 if (SUCCEEDED(hr))
210 hr = file.As(&storage_item);
211
212 mswrw::HString file_path;
213 if (SUCCEEDED(hr))
214 hr = storage_item->get_Path(file_path.GetAddressOf());
215
216 if (SUCCEEDED(hr)) {
217 UINT32 path_len = 0;
218 const wchar_t* path_str =
219 ::WindowsGetStringRawBuffer(file_path.Get(), &path_len);
220
221 // If the selected file name is longer than the supplied buffer,
222 // we return false as per GetOpenFileName documentation.
223 if (path_len < open_file_name_->nMaxFile) {
224 base::wcslcpy(open_file_name_->lpstrFile,
225 path_str,
226 open_file_name_->nMaxFile);
227 success_ = true;
228 }
229 }
230 } else {
231 LOG(ERROR) << "NULL IStorageItem";
232 }
233 } else {
234 LOG(ERROR) << "Unexpected async status " << static_cast<int>(status);
235 }
236
237 event_.Signal();
238
239 return S_OK;
240 }
241
242 HRESULT OpenFilePickerSession::MultiPickerDone(MultiFileAsyncOp* async,
243 AsyncStatus status) {
244 if (status == Completed) {
245 mswr::ComPtr<StorageFileVectorCollection> files;
246 HRESULT hr = async->GetResults(files.GetAddressOf());
247
248 if (files) {
249 base::string16 result;
250 if (SUCCEEDED(hr))
251 hr = ComposeMultiFileResult(files.Get(), &result);
252
253 if (SUCCEEDED(hr)) {
254 if (result.size() + 1 < open_file_name_->nMaxFile) {
255 // Because the result has embedded nulls, we must memcpy.
256 memcpy(open_file_name_->lpstrFile,
257 result.c_str(),
258 (result.size() + 1) * sizeof(result[0]));
259 success_ = true;
260 }
261 }
262 } else {
263 LOG(ERROR) << "NULL StorageFileVectorCollection";
264 }
265 } else {
266 LOG(ERROR) << "Unexpected async status " << static_cast<int>(status);
267 }
268
269 event_.Signal();
270
271 return S_OK;
272 }
273
274 HRESULT OpenFilePickerSession::StartFilePicker() {
275 DCHECK(globals.appview_msg_loop->BelongsToCurrentThread());
276 DCHECK(open_file_name_ != NULL);
277
278 mswrw::HStringReference class_name(
279 RuntimeClass_Windows_Storage_Pickers_FileOpenPicker);
280
281 // Create the file picker.
282 mswr::ComPtr<winstorage::Pickers::IFileOpenPicker> picker;
283 HRESULT hr = ::Windows::Foundation::ActivateInstance(
284 class_name.Get(), picker.GetAddressOf());
285 CheckHR(hr);
286
287 // Set the file type filter
288 mswr::ComPtr<winfoundtn::Collections::IVector<HSTRING>> filter;
289 hr = picker->get_FileTypeFilter(filter.GetAddressOf());
290 if (FAILED(hr))
291 return hr;
292
293 if (open_file_name_->lpstrFilter == NULL) {
294 hr = filter->Append(mswrw::HStringReference(L"*").Get());
295 if (FAILED(hr))
296 return hr;
297 } else {
298 // The filter is a concatenation of zero terminated string pairs,
299 // where each pair is {description, extension}. The concatenation ends
300 // with a zero length string - e.g. a double zero terminator.
301 const wchar_t* walk = open_file_name_->lpstrFilter;
302 while (*walk != L'\0') {
303 // Walk past the description.
304 walk += wcslen(walk) + 1;
305
306 // We should have an extension, but bail on malformed filters.
307 if (*walk == L'\0')
308 break;
309
310 // There can be a single extension, or a list of semicolon-separated ones.
311 std::vector<base::string16> extensions_win32_style = base::SplitString(
312 walk, L";", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
313
314 // Metro wants suffixes only, not patterns.
315 mswrw::HString extension;
316 for (size_t i = 0; i < extensions_win32_style.size(); ++i) {
317 if (extensions_win32_style[i] == L"*.*") {
318 // The wildcard filter is "*" for Metro. The string "*.*" produces
319 // an "invalid parameter" error.
320 hr = extension.Set(L"*");
321 } else {
322 // Metro wants suffixes only, not patterns.
323 base::string16 ext =
324 base::FilePath(extensions_win32_style[i]).Extension();
325 if ((ext.size() < 2) ||
326 (ext.find_first_of(L"*?") != base::string16::npos)) {
327 continue;
328 }
329 hr = extension.Set(ext.c_str());
330 }
331 if (SUCCEEDED(hr))
332 hr = filter->Append(extension.Get());
333 if (FAILED(hr))
334 return hr;
335 }
336
337 // Walk past the extension.
338 walk += wcslen(walk) + 1;
339 }
340 }
341
342 // Spin up a single or multi picker as appropriate.
343 if (open_file_name_->Flags & OFN_ALLOWMULTISELECT) {
344 mswr::ComPtr<MultiFileAsyncOp> completion;
345 hr = picker->PickMultipleFilesAsync(&completion);
346 if (FAILED(hr))
347 return hr;
348
349 // Create the callback method.
350 typedef winfoundtn::IAsyncOperationCompletedHandler<
351 StorageFileVectorCollection*> HandlerDoneType;
352 mswr::ComPtr<HandlerDoneType> handler(mswr::Callback<HandlerDoneType>(
353 this, &OpenFilePickerSession::MultiPickerDone));
354 DCHECK(handler.Get() != NULL);
355 hr = completion->put_Completed(handler.Get());
356
357 return hr;
358 } else {
359 mswr::ComPtr<SingleFileAsyncOp> completion;
360 hr = picker->PickSingleFileAsync(&completion);
361 if (FAILED(hr))
362 return hr;
363
364 // Create the callback method.
365 typedef winfoundtn::IAsyncOperationCompletedHandler<
366 winstorage::StorageFile*> HandlerDoneType;
367 mswr::ComPtr<HandlerDoneType> handler(mswr::Callback<HandlerDoneType>(
368 this, &OpenFilePickerSession::SinglePickerDone));
369 DCHECK(handler.Get() != NULL);
370 hr = completion->put_Completed(handler.Get());
371
372 return hr;
373 }
374 }
375
376 HRESULT OpenFilePickerSession::ComposeMultiFileResult(
377 StorageFileVectorCollection* files, base::string16* result) {
378 DCHECK(files != NULL);
379 DCHECK(result != NULL);
380
381 // Empty the output string.
382 result->clear();
383
384 unsigned int num_files = 0;
385 HRESULT hr = files->get_Size(&num_files);
386 if (FAILED(hr))
387 return hr;
388
389 // Make sure we return an error on an empty collection.
390 if (num_files == 0) {
391 DLOG(ERROR) << "Empty collection on input.";
392 return E_UNEXPECTED;
393 }
394
395 // This stores the base path that should be the parent of all the files.
396 base::FilePath base_path;
397
398 // Iterate through the collection and append the file paths to the result.
399 for (unsigned int i = 0; i < num_files; ++i) {
400 mswr::ComPtr<winstorage::IStorageFile> file;
401 hr = files->GetAt(i, file.GetAddressOf());
402 if (FAILED(hr))
403 return hr;
404
405 mswr::ComPtr<winstorage::IStorageItem> storage_item;
406 hr = file.As(&storage_item);
407 if (FAILED(hr))
408 return hr;
409
410 mswrw::HString file_path_str;
411 hr = storage_item->get_Path(file_path_str.GetAddressOf());
412 if (FAILED(hr))
413 return hr;
414
415 base::FilePath file_path(MakeStdWString(file_path_str.Get()));
416 if (base_path.empty()) {
417 DCHECK(result->empty());
418 base_path = file_path.DirName();
419
420 // Append the path, including the terminating zero.
421 // We do this only for the first file.
422 result->append(base_path.value().c_str(), base_path.value().size() + 1);
423 }
424 DCHECK(!result->empty());
425 DCHECK(!base_path.empty());
426 DCHECK(base_path == file_path.DirName());
427
428 // Append the base name, including the terminating zero.
429 base::FilePath base_name = file_path.BaseName();
430 result->append(base_name.value().c_str(), base_name.value().size() + 1);
431 }
432
433 DCHECK(!result->empty());
434
435 return S_OK;
436 }
437
438 SaveFilePickerSession::SaveFilePickerSession(OPENFILENAME* open_file_name)
439 : FilePickerSessionBase(open_file_name) {
440 }
441
442 HRESULT SaveFilePickerSession::StartFilePicker() {
443 DCHECK(globals.appview_msg_loop->BelongsToCurrentThread());
444 DCHECK(open_file_name_ != NULL);
445
446 mswrw::HStringReference class_name(
447 RuntimeClass_Windows_Storage_Pickers_FileSavePicker);
448
449 // Create the file picker.
450 mswr::ComPtr<winstorage::Pickers::IFileSavePicker> picker;
451 HRESULT hr = ::Windows::Foundation::ActivateInstance(
452 class_name.Get(), picker.GetAddressOf());
453 CheckHR(hr);
454
455 typedef winfoundtn::Collections::IMap<HSTRING, StringVectorItf*>
456 StringVectorMap;
457 mswr::ComPtr<StringVectorMap> choices;
458 hr = picker->get_FileTypeChoices(choices.GetAddressOf());
459 if (FAILED(hr))
460 return hr;
461
462 if (open_file_name_->lpstrFilter) {
463 // The filter is a concatenation of zero terminated string pairs,
464 // where each pair is {description, extension list}. The concatenation ends
465 // with a zero length string - e.g. a double zero terminator.
466 const wchar_t* walk = open_file_name_->lpstrFilter;
467 while (*walk != L'\0') {
468 mswrw::HString description;
469 hr = description.Set(walk);
470 if (FAILED(hr))
471 return hr;
472
473 // Walk past the description.
474 walk += wcslen(walk) + 1;
475
476 // We should have an extension, but bail on malformed filters.
477 if (*walk == L'\0')
478 break;
479
480 // There can be a single extension, or a list of semicolon-separated ones.
481 std::vector<base::string16> extensions_win32_style = base::SplitString(
482 walk, L";", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
483
484 // Metro wants suffixes only, not patterns. Also, metro does not support
485 // the all files ("*") pattern in the save picker.
486 std::vector<base::string16> extensions;
487 for (size_t i = 0; i < extensions_win32_style.size(); ++i) {
488 base::string16 ext =
489 base::FilePath(extensions_win32_style[i]).Extension();
490 if ((ext.size() < 2) ||
491 (ext.find_first_of(L"*?") != base::string16::npos))
492 continue;
493 extensions.push_back(ext);
494 }
495
496 if (!extensions.empty()) {
497 // Convert to a Metro collection class.
498 mswr::ComPtr<StringVectorItf> list;
499 hr = mswr::MakeAndInitialize<StringVectorImpl>(
500 list.GetAddressOf(), extensions);
501 if (FAILED(hr))
502 return hr;
503
504 // Finally set the filter.
505 boolean replaced = FALSE;
506 hr = choices->Insert(description.Get(), list.Get(), &replaced);
507 if (FAILED(hr))
508 return hr;
509 DCHECK_EQ(FALSE, replaced);
510 }
511
512 // Walk past the extension(s).
513 walk += wcslen(walk) + 1;
514 }
515 }
516
517 // The save picker requires at least one choice. Callers are strongly advised
518 // to provide sensible choices. If none were given, fallback to .dat.
519 uint32_t num_choices = 0;
520 hr = choices->get_Size(&num_choices);
521 if (FAILED(hr))
522 return hr;
523
524 if (num_choices == 0) {
525 mswrw::HString description;
526 // TODO(grt): Get a properly translated string. This can't be done from
527 // within metro_driver. Consider preprocessing the filter list in Chrome
528 // land to ensure it has this entry if all others are patterns. In that
529 // case, this whole block of code can be removed.
530 hr = description.Set(L"Data File");
531 if (FAILED(hr))
532 return hr;
533
534 mswr::ComPtr<StringVectorItf> list;
535 hr = mswr::MakeAndInitialize<StringVectorImpl>(
536 list.GetAddressOf(), std::vector<base::string16>(1, L".dat"));
537 if (FAILED(hr))
538 return hr;
539
540 boolean replaced = FALSE;
541 hr = choices->Insert(description.Get(), list.Get(), &replaced);
542 if (FAILED(hr))
543 return hr;
544 DCHECK_EQ(FALSE, replaced);
545 }
546
547 if (open_file_name_->lpstrFile != NULL) {
548 hr = picker->put_SuggestedFileName(
549 mswrw::HStringReference(
550 const_cast<const wchar_t*>(open_file_name_->lpstrFile)).Get());
551 if (FAILED(hr))
552 return hr;
553 }
554
555 mswr::ComPtr<SaveFileAsyncOp> completion;
556 hr = picker->PickSaveFileAsync(&completion);
557 if (FAILED(hr))
558 return hr;
559
560 // Create the callback method.
561 typedef winfoundtn::IAsyncOperationCompletedHandler<
562 winstorage::StorageFile*> HandlerDoneType;
563 mswr::ComPtr<HandlerDoneType> handler(mswr::Callback<HandlerDoneType>(
564 this, &SaveFilePickerSession::FilePickerDone));
565 DCHECK(handler.Get() != NULL);
566 hr = completion->put_Completed(handler.Get());
567
568 return hr;
569 }
570
571 HRESULT SaveFilePickerSession::FilePickerDone(SaveFileAsyncOp* async,
572 AsyncStatus status) {
573 if (status == Completed) {
574 mswr::ComPtr<winstorage::IStorageFile> file;
575 HRESULT hr = async->GetResults(file.GetAddressOf());
576
577 if (file) {
578 mswr::ComPtr<winstorage::IStorageItem> storage_item;
579 if (SUCCEEDED(hr))
580 hr = file.As(&storage_item);
581
582 mswrw::HString file_path;
583 if (SUCCEEDED(hr))
584 hr = storage_item->get_Path(file_path.GetAddressOf());
585
586 if (SUCCEEDED(hr)) {
587 base::string16 path_str = MakeStdWString(file_path.Get());
588
589 // If the selected file name is longer than the supplied buffer,
590 // we return false as per GetOpenFileName documentation.
591 if (path_str.size() < open_file_name_->nMaxFile) {
592 base::wcslcpy(open_file_name_->lpstrFile,
593 path_str.c_str(),
594 open_file_name_->nMaxFile);
595 success_ = true;
596 }
597 }
598 } else {
599 LOG(ERROR) << "NULL IStorageItem";
600 }
601 } else {
602 LOG(ERROR) << "Unexpected async status " << static_cast<int>(status);
603 }
604
605 event_.Signal();
606
607 return S_OK;
608 }
609
610 } // namespace
611
612 BOOL MetroGetOpenFileName(OPENFILENAME* open_file_name) {
613 OpenFilePickerSession session(open_file_name);
614
615 return session.Run();
616 }
617
618 BOOL MetroGetSaveFileName(OPENFILENAME* open_file_name) {
619 SaveFilePickerSession session(open_file_name);
620
621 return session.Run();
622 }
OLDNEW
« no previous file with comments | « win8/metro_driver/file_picker.h ('k') | win8/metro_driver/file_picker_ash.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698