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

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

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

Powered by Google App Engine
This is Rietveld 408576698