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