widget/windows/winrt/nsMetroFilePicker.cpp

branch
TOR_BUG_9701
changeset 10
ac0c01689b40
equal deleted inserted replaced
-1:000000000000 0:d0a62b6b829c
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 #include "nsMetroFilePicker.h"
7 #include "nsComponentManagerUtils.h"
8 #include "nsNetUtil.h"
9 #include "nsAutoPtr.h"
10 #include "MetroUtils.h"
11
12 #include <windows.ui.viewmanagement.h>
13 #include <windows.storage.search.h>
14
15 using namespace ABI::Windows::Foundation;
16 using namespace ABI::Windows::Foundation::Collections;
17 using namespace ABI::Windows::Storage;
18 using namespace ABI::Windows::Storage::Pickers;
19 using namespace ABI::Windows::Storage::Streams;
20 using namespace ABI::Windows::UI;
21 using namespace ABI::Windows::UI::ViewManagement;
22 using namespace Microsoft::WRL;
23 using namespace Microsoft::WRL::Wrappers;
24 using namespace mozilla::widget::winrt;
25
26 ///////////////////////////////////////////////////////////////////////////////
27 // nsIFilePicker
28
29 nsMetroFilePicker::nsMetroFilePicker()
30 {
31 }
32
33 nsMetroFilePicker::~nsMetroFilePicker()
34 {
35 }
36
37 NS_IMPL_ISUPPORTS(nsMetroFilePicker, nsIFilePicker)
38
39 NS_IMETHODIMP
40 nsMetroFilePicker::Init(nsIDOMWindow *parent, const nsAString& title, int16_t mode)
41 {
42 mMode = mode;
43 HRESULT hr;
44 switch(mMode) {
45 case nsIFilePicker::modeOpen:
46 case nsIFilePicker::modeOpenMultiple:
47 hr = ActivateGenericInstance(RuntimeClass_Windows_Storage_Pickers_FileOpenPicker, mFileOpenPicker);
48 AssertRetHRESULT(hr, NS_ERROR_UNEXPECTED);
49 return NS_OK;
50 case nsIFilePicker::modeSave:
51 hr = ActivateGenericInstance(RuntimeClass_Windows_Storage_Pickers_FileSavePicker, mFileSavePicker);
52 AssertRetHRESULT(hr, NS_ERROR_UNEXPECTED);
53 return NS_OK;
54 default:
55 return NS_ERROR_NOT_IMPLEMENTED;
56 }
57 }
58
59 NS_IMETHODIMP
60 nsMetroFilePicker::Show(int16_t *aReturnVal)
61 {
62 // Metro file picker only offers an async variant which calls back to the
63 // UI thread, which is the main thread. We therefore can't call it
64 // synchronously from the main thread.
65 return NS_ERROR_NOT_IMPLEMENTED;
66 }
67
68 HRESULT nsMetroFilePicker::OnPickSingleFile(IAsyncOperation<StorageFile*>* aFile,
69 AsyncStatus aStatus)
70 {
71 if (aStatus != ABI::Windows::Foundation::AsyncStatus::Completed) {
72 if (mCallback)
73 mCallback->Done(nsIFilePicker::returnCancel);
74 return S_OK;
75 }
76
77 HRESULT hr;
78 ComPtr<IStorageFile> file;
79 hr = aFile->GetResults(file.GetAddressOf());
80 // When the user cancels hr == S_OK and file is nullptr
81 if (FAILED(hr) || !file) {
82 if (mCallback)
83 mCallback->Done(nsIFilePicker::returnCancel);
84 return S_OK;
85 }
86 ComPtr<IStorageItem> storageItem;
87 hr = file.As(&storageItem);
88 if (FAILED(hr)) {
89 if (mCallback)
90 mCallback->Done(nsIFilePicker::returnCancel);
91 return S_OK;
92 }
93
94 HSTRING path;
95 if (FAILED(storageItem->get_Path(&path))) {
96 if (mCallback)
97 mCallback->Done(nsIFilePicker::returnCancel);
98 return S_OK;
99 }
100 WindowsDuplicateString(path, mFilePath.GetAddressOf());
101 WindowsDeleteString(path);
102
103 if (mCallback) {
104 mCallback->Done(nsIFilePicker::returnOK);
105 }
106 return S_OK;
107 }
108
109 HRESULT nsMetroFilePicker::OnPickMultipleFiles(IAsyncOperation<IVectorView<StorageFile*>*>* aFileList,
110 AsyncStatus aStatus)
111 {
112 if (aStatus != ABI::Windows::Foundation::AsyncStatus::Completed) {
113 if (mCallback)
114 mCallback->Done(nsIFilePicker::returnCancel);
115 return S_OK;
116 }
117
118 HRESULT hr;
119 ComPtr<IVectorView<StorageFile*>> view;
120 hr = aFileList->GetResults(view.GetAddressOf());
121 if (FAILED(hr)) {
122 if (mCallback)
123 mCallback->Done(nsIFilePicker::returnCancel);
124 return S_OK;
125 }
126
127 unsigned int length;
128 view->get_Size(&length);
129 for (unsigned int idx = 0; idx < length; idx++) {
130 ComPtr<IStorageFile> file;
131 hr = view->GetAt(idx, file.GetAddressOf());
132 if (FAILED(hr)) {
133 continue;
134 }
135
136 ComPtr<IStorageItem> storageItem;
137 hr = file.As(&storageItem);
138 if (FAILED(hr)) {
139 continue;
140 }
141
142 HSTRING path;
143 if (SUCCEEDED(storageItem->get_Path(&path))) {
144 nsCOMPtr<nsILocalFile> file =
145 do_CreateInstance("@mozilla.org/file/local;1");
146 unsigned int tmp;
147 if (NS_SUCCEEDED(file->InitWithPath(
148 nsAutoString(WindowsGetStringRawBuffer(path, &tmp))))) {
149 mFiles.AppendObject(file);
150 }
151 }
152 WindowsDeleteString(path);
153 }
154
155 if (mCallback) {
156 mCallback->Done(nsIFilePicker::returnOK);
157 }
158 return S_OK;
159 }
160
161 NS_IMETHODIMP
162 nsMetroFilePicker::Open(nsIFilePickerShownCallback *aCallback)
163 {
164 HRESULT hr;
165 // Capture a reference to the callback which we'll also pass into the
166 // closure to ensure it's not freed.
167 mCallback = aCallback;
168
169 // The filepicker cannot open when in snapped view, try to unsnapp
170 // before showing the filepicker.
171 ApplicationViewState viewState;
172 MetroUtils::GetViewState(viewState);
173 if (viewState == ApplicationViewState::ApplicationViewState_Snapped) {
174 bool unsnapped = SUCCEEDED(MetroUtils::TryUnsnap());
175 NS_ENSURE_TRUE(unsnapped, NS_ERROR_FAILURE);
176 }
177
178 switch(mMode) {
179 case nsIFilePicker::modeOpen: {
180 NS_ENSURE_ARG_POINTER(mFileOpenPicker);
181
182 // Initiate the file picker operation
183 ComPtr<IAsyncOperation<StorageFile*>> asyncOperation;
184 hr = mFileOpenPicker->PickSingleFileAsync(asyncOperation.GetAddressOf());
185 AssertRetHRESULT(hr, NS_ERROR_FAILURE);
186
187 // Subscribe to the completed event
188 ComPtr<IAsyncOperationCompletedHandler<StorageFile*>>
189 completedHandler(Callback<IAsyncOperationCompletedHandler<StorageFile*>>(
190 this, &nsMetroFilePicker::OnPickSingleFile));
191 hr = asyncOperation->put_Completed(completedHandler.Get());
192 AssertRetHRESULT(hr, NS_ERROR_UNEXPECTED);
193 break;
194 }
195
196 case nsIFilePicker::modeOpenMultiple: {
197 NS_ENSURE_ARG_POINTER(mFileOpenPicker);
198
199 typedef IVectorView<StorageFile*> StorageTemplate;
200 typedef IAsyncOperation<StorageTemplate*> AsyncCallbackTemplate;
201 typedef IAsyncOperationCompletedHandler<StorageTemplate*> HandlerTemplate;
202
203 // Initiate the file picker operation
204 ComPtr<AsyncCallbackTemplate> asyncOperation;
205 hr = mFileOpenPicker->PickMultipleFilesAsync(asyncOperation.GetAddressOf());
206 AssertRetHRESULT(hr, NS_ERROR_FAILURE);
207
208 // Subscribe to the completed event
209 ComPtr<HandlerTemplate> completedHandler(Callback<HandlerTemplate>(
210 this, &nsMetroFilePicker::OnPickMultipleFiles));
211 hr = asyncOperation->put_Completed(completedHandler.Get());
212 AssertRetHRESULT(hr, NS_ERROR_UNEXPECTED);
213 break;
214 }
215
216 case nsIFilePicker::modeSave: {
217 NS_ENSURE_ARG_POINTER(mFileSavePicker);
218
219 // Set the default file name
220 mFileSavePicker->put_SuggestedFileName(HStringReference(mDefaultFilename.BeginReading()).Get());
221
222 // Set the default file extension
223 if (mDefaultExtension.Length() > 0) {
224 nsAutoString defaultFileExtension(mDefaultExtension);
225
226 // Touch up the extansion format platform hands us.
227 if (defaultFileExtension[0] == L'*') {
228 defaultFileExtension.Cut(0, 1);
229 } else if (defaultFileExtension[0] != L'.') {
230 defaultFileExtension.Insert(L".", 0);
231 }
232
233 // Sometimes the default file extension is not passed in correctly,
234 // so we purposfully ignore failures here.
235 HString ext;
236 ext.Set(defaultFileExtension.BeginReading());
237 hr = mFileSavePicker->put_DefaultFileExtension(ext.Get());
238 NS_ASSERTION(SUCCEEDED(hr), "put_DefaultFileExtension failed, bad format for extension?");
239
240 // Due to a bug in the WinRT file picker, the first file extension in the
241 // list is always used when saving a file. So we explicitly make sure the
242 // default extension is the first one in the list here.
243 if (mFirstTitle.Get()) {
244 ComPtr<IMap<HSTRING, IVector<HSTRING>*>> map;
245 mFileSavePicker->get_FileTypeChoices(map.GetAddressOf());
246 if (map) {
247 boolean found = false;
248 unsigned int index;
249 map->HasKey(mFirstTitle.Get(), &found);
250 if (found) {
251 ComPtr<IVector<HSTRING>> list;
252 if (SUCCEEDED(map->Lookup(mFirstTitle.Get(), list.GetAddressOf()))) {
253 HString ext;
254 ext.Set(defaultFileExtension.get());
255 found = false;
256 list->IndexOf(HStringReference(defaultFileExtension.get()).Get(), &index, &found);
257 if (found) {
258 list->RemoveAt(index);
259 list->InsertAt(0, HStringReference(defaultFileExtension.get()).Get());
260 }
261 }
262 }
263 }
264 }
265 }
266
267 // Dispatch the async show operation
268 ComPtr<IAsyncOperation<StorageFile*>> asyncOperation;
269 hr = mFileSavePicker->PickSaveFileAsync(asyncOperation.GetAddressOf());
270 AssertRetHRESULT(hr, NS_ERROR_FAILURE);
271
272 // Subscribe to the completed event
273 ComPtr<IAsyncOperationCompletedHandler<StorageFile*>>
274 completedHandler(Callback<IAsyncOperationCompletedHandler<StorageFile*>>(
275 this, &nsMetroFilePicker::OnPickSingleFile));
276 hr = asyncOperation->put_Completed(completedHandler.Get());
277 AssertRetHRESULT(hr, NS_ERROR_UNEXPECTED);
278 break;
279 }
280
281 case modeGetFolder:
282 return NS_ERROR_NOT_IMPLEMENTED;
283
284 default:
285 return NS_ERROR_NOT_IMPLEMENTED;
286 }
287 return NS_OK;
288 }
289
290 NS_IMETHODIMP
291 nsMetroFilePicker::GetFile(nsIFile **aFile)
292 {
293 NS_ENSURE_ARG_POINTER(aFile);
294 *aFile = nullptr;
295
296 if (WindowsIsStringEmpty(mFilePath.Get()))
297 return NS_OK;
298
299 nsCOMPtr<nsILocalFile> file(do_CreateInstance("@mozilla.org/file/local;1"));
300 NS_ENSURE_TRUE(file, NS_ERROR_FAILURE);
301 unsigned int length;
302 file->InitWithPath(nsAutoString(mFilePath.GetRawBuffer(&length)));
303 NS_ADDREF(*aFile = file);
304 return NS_OK;
305 }
306
307 NS_IMETHODIMP
308 nsMetroFilePicker::GetFileURL(nsIURI **aFileURL)
309 {
310 *aFileURL = nullptr;
311 nsCOMPtr<nsIFile> file;
312 nsresult rv = GetFile(getter_AddRefs(file));
313 if (!file)
314 return rv;
315
316 return NS_NewFileURI(aFileURL, file);
317 }
318
319 NS_IMETHODIMP
320 nsMetroFilePicker::GetFiles(nsISimpleEnumerator **aFiles)
321 {
322 NS_ENSURE_ARG_POINTER(aFiles);
323 return NS_NewArrayEnumerator(aFiles, mFiles);
324 }
325
326 // Set the filter index
327 NS_IMETHODIMP
328 nsMetroFilePicker::GetFilterIndex(int32_t *aFilterIndex)
329 {
330 // No associated concept with a Metro file picker
331 return NS_ERROR_NOT_IMPLEMENTED;
332 }
333
334 NS_IMETHODIMP
335 nsMetroFilePicker::SetFilterIndex(int32_t aFilterIndex)
336 {
337 // No associated concept with a Metro file picker
338 return NS_ERROR_NOT_IMPLEMENTED;
339 }
340
341 // AFACT, it's up to use to supply the implementation of a vector list.
342 class MozHStringVector : public RuntimeClass<IVector<HSTRING>> {
343 InspectableClass(L"MozHStringVector", TrustLevel::BaseTrust)
344 ~MozHStringVector() {
345 Clear();
346 }
347
348 // See IVector_impl in windows.foundation.collections.h
349 public:
350 STDMETHOD(GetAt)(unsigned aIndex, HSTRING* aString) {
351 if (aIndex >= mList.Length()) {
352 return E_INVALIDARG;
353 }
354 return WindowsDuplicateString(mList[aIndex], aString);
355 }
356
357 STDMETHOD(get_Size)(unsigned int* aLength) {
358 if (!aLength) {
359 return E_INVALIDARG;
360 }
361 *aLength = mList.Length();
362 return S_OK;
363 }
364
365 STDMETHOD(Append)(HSTRING aString) {
366 HSTRING str;
367 if (FAILED(WindowsDuplicateString(aString, &str))) {
368 return E_INVALIDARG;
369 }
370 mList.AppendElement(str);
371 return S_OK;
372 }
373
374 STDMETHOD(Clear)() {
375 int length = mList.Length();
376 for (int idx = 0; idx < length; idx++)
377 WindowsDeleteString(mList[idx]);
378 mList.Clear();
379 return S_OK;
380 }
381
382 // interfaces picker code doesn't seem to need
383 STDMETHOD(GetView)(IVectorView<HSTRING> **aView) { return E_NOTIMPL; }
384 STDMETHOD(IndexOf)(HSTRING aValue, unsigned *aIndex, boolean *found) { return E_NOTIMPL; }
385 STDMETHOD(SetAt)(unsigned aIndex, HSTRING aString) { return E_NOTIMPL; }
386 STDMETHOD(InsertAt)(unsigned aIndex, HSTRING aString) { return E_NOTIMPL; }
387 STDMETHOD(RemoveAt)(unsigned aIndex) { return E_NOTIMPL; }
388 STDMETHOD(RemoveAtEnd)() { return E_NOTIMPL; }
389
390 private:
391 nsTArray<HSTRING> mList;
392 };
393
394 nsresult
395 nsMetroFilePicker::ParseFiltersIntoVector(ComPtr<IVector<HSTRING>>& aVector,
396 const nsAString& aFilter,
397 bool aAllowAll)
398 {
399 const char16_t *beg = aFilter.BeginReading();
400 const char16_t *end = aFilter.EndReading();
401 for (const char16_t *cur = beg, *fileTypeStart = beg; cur <= end; ++cur) {
402 // Start of a a filetype, example: *.png
403 if (cur == end || char16_t(' ') == *cur) {
404 int32_t startPos = fileTypeStart - beg;
405 int32_t endPos = cur - fileTypeStart - (cur == end ? 0 : 1);
406 const nsAString& fileType = Substring(aFilter,
407 startPos,
408 endPos);
409 // There is no way to say show all files in Metro save file picker, so
410 // just use .data if * or *.* is specified.
411 if (fileType.IsEmpty() ||
412 fileType.Equals(L"*") ||
413 fileType.Equals(L"*.*")) {
414 HString str;
415 if (aAllowAll) {
416 str.Set(L"*");
417 aVector->Append(str.Get());
418 } else {
419 str.Set(L".data");
420 aVector->Append(str.Get());
421 }
422 } else {
423 nsAutoString filter(fileType);
424 if (filter[0] == L'*') {
425 filter.Cut(0, 1);
426 } else if (filter[0] != L'.') {
427 filter.Insert(L".", 0);
428 }
429 HString str;
430 str.Set(filter.BeginReading());
431 aVector->Append(str.Get());
432 }
433
434 fileTypeStart = cur + 1;
435 }
436 }
437 return NS_OK;
438 }
439
440 NS_IMETHODIMP
441 nsMetroFilePicker::AppendFilter(const nsAString& aTitle,
442 const nsAString& aFilter)
443 {
444 HRESULT hr;
445 switch(mMode) {
446 case nsIFilePicker::modeOpen:
447 case nsIFilePicker::modeOpenMultiple: {
448 NS_ENSURE_ARG_POINTER(mFileOpenPicker);
449 ComPtr<IVector<HSTRING>> list;
450 mFileOpenPicker->get_FileTypeFilter(list.GetAddressOf());
451 nsresult rv = ParseFiltersIntoVector(list, aFilter, true);
452 NS_ENSURE_SUCCESS(rv, rv);
453 }
454
455 case nsIFilePicker::modeSave: {
456 NS_ENSURE_ARG_POINTER(mFileSavePicker);
457
458 ComPtr<IMap<HSTRING,IVector<HSTRING>*>> map;
459 hr = mFileSavePicker->get_FileTypeChoices(map.GetAddressOf());
460 AssertRetHRESULT(hr, NS_ERROR_FAILURE);
461
462 HString key;
463 key.Set(aTitle.BeginReading());
464
465 ComPtr<IVector<HSTRING>> saveTypes;
466 saveTypes = Make<MozHStringVector>();
467 nsresult rv = ParseFiltersIntoVector(saveTypes, aFilter, false);
468 NS_ENSURE_SUCCESS(rv, rv);
469
470 if (WindowsIsStringEmpty(mFirstTitle.Get())) {
471 mFirstTitle.Set(key.Get());
472 }
473
474 boolean replaced;
475 map->Insert(key.Get(), saveTypes.Get(), &replaced);
476 }
477 break;
478
479 default:
480 return NS_ERROR_FAILURE;
481 }
482 return NS_OK;
483 }
484

mercurial