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