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