widget/windows/winrt/nsMetroFilePicker.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     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/. */
     6 #include "nsMetroFilePicker.h"
     7 #include "nsComponentManagerUtils.h"
     8 #include "nsNetUtil.h"
     9 #include "nsAutoPtr.h"
    10 #include "MetroUtils.h"
    12 #include <windows.ui.viewmanagement.h>
    13 #include <windows.storage.search.h>
    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;
    26 ///////////////////////////////////////////////////////////////////////////////
    27 // nsIFilePicker
    29 nsMetroFilePicker::nsMetroFilePicker()
    30 {
    31 }
    33 nsMetroFilePicker::~nsMetroFilePicker()
    34 {
    35 }
    37 NS_IMPL_ISUPPORTS(nsMetroFilePicker, nsIFilePicker)
    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 }
    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 }
    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   }
    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   }
    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);
   103   if (mCallback) {
   104     mCallback->Done(nsIFilePicker::returnOK);
   105   }
   106   return S_OK;
   107 }
   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   }
   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   }
   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     }
   136     ComPtr<IStorageItem> storageItem;
   137     hr = file.As(&storageItem);
   138     if (FAILED(hr)) {
   139       continue;
   140     }
   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   }
   155   if (mCallback) {
   156     mCallback->Done(nsIFilePicker::returnOK);
   157   }
   158   return S_OK;
   159 }
   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;
   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   }
   178   switch(mMode) {
   179   case nsIFilePicker::modeOpen: {
   180     NS_ENSURE_ARG_POINTER(mFileOpenPicker);
   182     // Initiate the file picker operation
   183     ComPtr<IAsyncOperation<StorageFile*>> asyncOperation;
   184     hr = mFileOpenPicker->PickSingleFileAsync(asyncOperation.GetAddressOf());
   185     AssertRetHRESULT(hr, NS_ERROR_FAILURE);
   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   }
   196   case nsIFilePicker::modeOpenMultiple: {
   197     NS_ENSURE_ARG_POINTER(mFileOpenPicker);
   199     typedef IVectorView<StorageFile*> StorageTemplate;
   200     typedef IAsyncOperation<StorageTemplate*> AsyncCallbackTemplate;
   201     typedef IAsyncOperationCompletedHandler<StorageTemplate*> HandlerTemplate;
   203     // Initiate the file picker operation
   204     ComPtr<AsyncCallbackTemplate> asyncOperation;
   205     hr = mFileOpenPicker->PickMultipleFilesAsync(asyncOperation.GetAddressOf());
   206     AssertRetHRESULT(hr, NS_ERROR_FAILURE);
   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   }
   216   case nsIFilePicker::modeSave: {
   217     NS_ENSURE_ARG_POINTER(mFileSavePicker);
   219     // Set the default file name
   220     mFileSavePicker->put_SuggestedFileName(HStringReference(mDefaultFilename.BeginReading()).Get());
   222     // Set the default file extension
   223     if (mDefaultExtension.Length() > 0) {
   224       nsAutoString defaultFileExtension(mDefaultExtension);
   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       }
   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?");
   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     }
   267     // Dispatch the async show operation
   268     ComPtr<IAsyncOperation<StorageFile*>> asyncOperation;
   269     hr = mFileSavePicker->PickSaveFileAsync(asyncOperation.GetAddressOf());
   270     AssertRetHRESULT(hr, NS_ERROR_FAILURE);
   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   }
   281   case modeGetFolder:
   282     return NS_ERROR_NOT_IMPLEMENTED;
   284   default:
   285     return NS_ERROR_NOT_IMPLEMENTED;
   286   }
   287   return NS_OK;
   288 }
   290 NS_IMETHODIMP
   291 nsMetroFilePicker::GetFile(nsIFile **aFile)
   292 {
   293   NS_ENSURE_ARG_POINTER(aFile);
   294   *aFile = nullptr;
   296   if (WindowsIsStringEmpty(mFilePath.Get()))
   297     return NS_OK;
   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 }
   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;
   316   return NS_NewFileURI(aFileURL, file);
   317 }
   319 NS_IMETHODIMP
   320 nsMetroFilePicker::GetFiles(nsISimpleEnumerator **aFiles)
   321 {
   322   NS_ENSURE_ARG_POINTER(aFiles);
   323   return NS_NewArrayEnumerator(aFiles, mFiles);
   324 }
   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 }
   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 }
   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   }
   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   }
   357   STDMETHOD(get_Size)(unsigned int* aLength) {
   358     if (!aLength) {
   359       return E_INVALIDARG;
   360     }
   361     *aLength = mList.Length();
   362     return S_OK;
   363   }
   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   }
   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   }
   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; }
   390 private:
   391   nsTArray<HSTRING> mList;
   392 };
   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       }
   434       fileTypeStart = cur + 1;
   435     }
   436   }
   437   return NS_OK;
   438 }
   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   }
   455   case nsIFilePicker::modeSave: {
   456     NS_ENSURE_ARG_POINTER(mFileSavePicker);
   458     ComPtr<IMap<HSTRING,IVector<HSTRING>*>> map;
   459     hr = mFileSavePicker->get_FileTypeChoices(map.GetAddressOf());
   460     AssertRetHRESULT(hr, NS_ERROR_FAILURE);
   462     HString key;
   463     key.Set(aTitle.BeginReading());
   465     ComPtr<IVector<HSTRING>> saveTypes;
   466     saveTypes = Make<MozHStringVector>();
   467     nsresult rv = ParseFiltersIntoVector(saveTypes, aFilter, false);
   468     NS_ENSURE_SUCCESS(rv, rv);
   470     if (WindowsIsStringEmpty(mFirstTitle.Get())) {
   471       mFirstTitle.Set(key.Get());
   472     }
   474     boolean replaced;
   475     map->Insert(key.Get(), saveTypes.Get(), &replaced);
   476   }
   477   break;
   479   default:
   480     return NS_ERROR_FAILURE;
   481   }
   482   return NS_OK;
   483 }

mercurial