diff --git a/dev/Interop/StoragePickers/FileOpenPicker.cpp b/dev/Interop/StoragePickers/FileOpenPicker.cpp index 9412982353..a28628f90f 100644 --- a/dev/Interop/StoragePickers/FileOpenPicker.cpp +++ b/dev/Interop/StoragePickers/FileOpenPicker.cpp @@ -28,6 +28,7 @@ namespace winrt::Microsoft::Windows::Storage::Pickers::implementation } void FileOpenPicker::ViewMode(winrt::Microsoft::Windows::Storage::Pickers::PickerViewMode const& value) { + PickerCommon::ValidateViewMode(value); m_viewMode = value; } hstring FileOpenPicker::SettingsIdentifier() @@ -36,6 +37,7 @@ namespace winrt::Microsoft::Windows::Storage::Pickers::implementation } void FileOpenPicker::SettingsIdentifier(hstring const& value) { + PickerCommon::ValidateStringNoEmbeddedNulls(value); m_settingsIdentifier = value; } winrt::Microsoft::Windows::Storage::Pickers::PickerLocationId FileOpenPicker::SuggestedStartLocation() @@ -44,6 +46,7 @@ namespace winrt::Microsoft::Windows::Storage::Pickers::implementation } void FileOpenPicker::SuggestedStartLocation(winrt::Microsoft::Windows::Storage::Pickers::PickerLocationId const& value) { + PickerCommon::ValidateSuggestedStartLocation(value); m_suggestedStartLocation = value; } winrt::hstring FileOpenPicker::CommitButtonText() @@ -52,6 +55,7 @@ namespace winrt::Microsoft::Windows::Storage::Pickers::implementation } void FileOpenPicker::CommitButtonText(winrt::hstring const& value) { + PickerCommon::ValidateStringNoEmbeddedNulls(value); m_commitButtonText = value; } winrt::Windows::Foundation::Collections::IVector FileOpenPicker::FileTypeFilter() diff --git a/dev/Interop/StoragePickers/FileOpenPicker.h b/dev/Interop/StoragePickers/FileOpenPicker.h index 208025488d..76f509ff89 100644 --- a/dev/Interop/StoragePickers/FileOpenPicker.h +++ b/dev/Interop/StoragePickers/FileOpenPicker.h @@ -5,6 +5,8 @@ #include "Microsoft.Windows.Storage.Pickers.FileOpenPicker.g.h" #include "PickerCommon.h" #include "StoragePickersTelemetryHelper.h" +#include +#include "FileTypeFilterVector.h" namespace winrt::Microsoft::Windows::Storage::Pickers::implementation { @@ -35,7 +37,9 @@ namespace winrt::Microsoft::Windows::Storage::Pickers::implementation winrt::hstring m_settingsIdentifier{}; PickerLocationId m_suggestedStartLocation{ PickerLocationId::Unspecified }; winrt::hstring m_commitButtonText{}; - winrt::Windows::Foundation::Collections::IVector m_fileTypeFilter{ winrt::single_threaded_vector() }; + + winrt::Windows::Foundation::Collections::IVector m_fileTypeFilter{ make() }; + StoragePickersTelemetryHelper m_telemetryHelper{}; void CaptureParameters(PickerCommon::PickerParameters& parameters); diff --git a/dev/Interop/StoragePickers/FileSavePicker.cpp b/dev/Interop/StoragePickers/FileSavePicker.cpp index 5af869dd72..340ce9ad1c 100644 --- a/dev/Interop/StoragePickers/FileSavePicker.cpp +++ b/dev/Interop/StoragePickers/FileSavePicker.cpp @@ -33,6 +33,7 @@ namespace winrt::Microsoft::Windows::Storage::Pickers::implementation } void FileSavePicker::SettingsIdentifier(hstring const& value) { + PickerCommon::ValidateStringNoEmbeddedNulls(value); m_settingsIdentifier = value; } winrt::Microsoft::Windows::Storage::Pickers::PickerLocationId FileSavePicker::SuggestedStartLocation() @@ -41,6 +42,7 @@ namespace winrt::Microsoft::Windows::Storage::Pickers::implementation } void FileSavePicker::SuggestedStartLocation(winrt::Microsoft::Windows::Storage::Pickers::PickerLocationId const& value) { + PickerCommon::ValidateSuggestedStartLocation(value); m_suggestedStartLocation = value; } hstring FileSavePicker::CommitButtonText() @@ -49,6 +51,7 @@ namespace winrt::Microsoft::Windows::Storage::Pickers::implementation } void FileSavePicker::CommitButtonText(hstring const& value) { + PickerCommon::ValidateStringNoEmbeddedNulls(value); m_commitButtonText = value; } winrt::Windows::Foundation::Collections::IMap> FileSavePicker::FileTypeChoices() @@ -86,12 +89,13 @@ namespace winrt::Microsoft::Windows::Storage::Pickers::implementation { return m_suggestedFileName; } + void FileSavePicker::SuggestedFileName(hstring const& value) { + PickerCommon::ValidateSuggestedFileName(value); m_suggestedFileName = value; } - void FileSavePicker::CaptureParameters(PickerCommon::PickerParameters& parameters) { parameters.HWnd = winrt::Microsoft::UI::GetWindowFromWindowId(m_windowId); diff --git a/dev/Interop/StoragePickers/FileSavePicker.h b/dev/Interop/StoragePickers/FileSavePicker.h index 81f3d841c3..fa29c40177 100644 --- a/dev/Interop/StoragePickers/FileSavePicker.h +++ b/dev/Interop/StoragePickers/FileSavePicker.h @@ -5,6 +5,7 @@ #include "Microsoft.Windows.Storage.Pickers.FileSavePicker.g.h" #include "PickerCommon.h" #include "StoragePickersTelemetryHelper.h" +#include "FileTypeChoicesMap.h" namespace winrt::Microsoft::Windows::Storage::Pickers::implementation { @@ -41,7 +42,7 @@ namespace winrt::Microsoft::Windows::Storage::Pickers::implementation hstring m_settingsIdentifier{}; PickerLocationId m_suggestedStartLocation{ PickerLocationId::Unspecified }; hstring m_commitButtonText{}; - winrt::Windows::Foundation::Collections::IMap> m_fileTypeChoices{ winrt::single_threaded_map>() }; + winrt::Windows::Foundation::Collections::IMap> m_fileTypeChoices{ make() }; hstring m_defaultFileExtension{}; hstring m_suggestedSaveFilePath{}; hstring m_suggestedFileName{}; diff --git a/dev/Interop/StoragePickers/FileTypeChoicesMap.cpp b/dev/Interop/StoragePickers/FileTypeChoicesMap.cpp new file mode 100644 index 0000000000..3f04a4a373 --- /dev/null +++ b/dev/Interop/StoragePickers/FileTypeChoicesMap.cpp @@ -0,0 +1,65 @@ +// Copyright (c) Microsoft Corporation and Contributors. +// Licensed under the MIT License. + +#include "pch.h" +#include "FileTypeChoicesMap.h" +#include "FileTypeFilterVector.h" + +namespace winrt::Microsoft::Windows::Storage::Pickers::implementation +{ + FileTypeChoicesMap::FileTypeChoicesMap() + { + } + + bool FileTypeChoicesMap::Insert(hstring const& key, winrt::Windows::Foundation::Collections::IVector const& value) + { + // Create a new FileTypeFilterVector and copy all values from the input vector + auto validatingVector = make(); + + if (value) + { + // Each Append call will validate the extension + for (auto const& item : value) + { + validatingVector.as>().Append(item); + } + } + + return m_innerMap.Insert(key, validatingVector); + } + + winrt::Windows::Foundation::Collections::IVector FileTypeChoicesMap::Lookup(hstring const& key) const + { + return m_innerMap.Lookup(key); + } + + uint32_t FileTypeChoicesMap::Size() const + { + return m_innerMap.Size(); + } + + bool FileTypeChoicesMap::HasKey(hstring const& key) const + { + return m_innerMap.HasKey(key); + } + + winrt::Windows::Foundation::Collections::IMapView> FileTypeChoicesMap::GetView() const + { + return m_innerMap.GetView(); + } + + void FileTypeChoicesMap::Remove(hstring const& key) + { + m_innerMap.Remove(key); + } + + void FileTypeChoicesMap::Clear() + { + m_innerMap.Clear(); + } + + winrt::Windows::Foundation::Collections::IIterator>> FileTypeChoicesMap::First() const + { + return m_innerMap.First(); + } +} diff --git a/dev/Interop/StoragePickers/FileTypeChoicesMap.h b/dev/Interop/StoragePickers/FileTypeChoicesMap.h new file mode 100644 index 0000000000..0f1e2a1a87 --- /dev/null +++ b/dev/Interop/StoragePickers/FileTypeChoicesMap.h @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation and Contributors. +// Licensed under the MIT License. + +#pragma once +#include +#include "FileTypeFilterVector.h" + +namespace winrt::Microsoft::Windows::Storage::Pickers::implementation +{ + struct FileTypeChoicesMap : implements>, + winrt::Windows::Foundation::Collections::IIterable>>> + { + FileTypeChoicesMap(); + + // IMap> + winrt::Windows::Foundation::Collections::IVector Lookup(hstring const& key) const; + uint32_t Size() const; + bool HasKey(hstring const& key) const; + winrt::Windows::Foundation::Collections::IMapView> GetView() const; + bool Insert(hstring const& key, winrt::Windows::Foundation::Collections::IVector const& value); + void Remove(hstring const& key); + void Clear(); + + // IIterable>> + winrt::Windows::Foundation::Collections::IIterator>> First() const; + + private: + winrt::Windows::Foundation::Collections::IMap> m_innerMap{ + single_threaded_map>() }; + }; +} diff --git a/dev/Interop/StoragePickers/FileTypeFilterVector.cpp b/dev/Interop/StoragePickers/FileTypeFilterVector.cpp new file mode 100644 index 0000000000..5fe3d8bad5 --- /dev/null +++ b/dev/Interop/StoragePickers/FileTypeFilterVector.cpp @@ -0,0 +1,86 @@ +// Copyright (c) Microsoft Corporation and Contributors. +// Licensed under the MIT License. + +#include "pch.h" +#include "FileTypeFilterVector.h" +#include "PickerCommon.h" + +namespace winrt::Microsoft::Windows::Storage::Pickers::implementation +{ + FileTypeFilterVector::FileTypeFilterVector() + { + } + + void FileTypeFilterVector::SetAt(uint32_t index, hstring const& value) + { + PickerCommon::ValidateSingleFileTypeFilterElement(value); + m_innerVector.SetAt(index, value); + } + + void FileTypeFilterVector::InsertAt(uint32_t index, hstring const& value) + { + PickerCommon::ValidateSingleFileTypeFilterElement(value); + m_innerVector.InsertAt(index, value); + } + + void FileTypeFilterVector::Append(hstring const& value) + { + PickerCommon::ValidateSingleFileTypeFilterElement(value); + m_innerVector.Append(value); + } + + void FileTypeFilterVector::ReplaceAll(array_view items) + { + // Validate all items before replacing + for (auto const& item : items) + { + PickerCommon::ValidateSingleFileTypeFilterElement(item); + } + m_innerVector.ReplaceAll(items); + } + + hstring FileTypeFilterVector::GetAt(uint32_t index) const + { + return m_innerVector.GetAt(index); + } + + uint32_t FileTypeFilterVector::Size() const + { + return m_innerVector.Size(); + } + + winrt::Windows::Foundation::Collections::IVectorView FileTypeFilterVector::GetView() const + { + return m_innerVector.GetView(); + } + + bool FileTypeFilterVector::IndexOf(hstring const& value, uint32_t& index) const + { + return m_innerVector.IndexOf(value, index); + } + + void FileTypeFilterVector::RemoveAt(uint32_t index) + { + m_innerVector.RemoveAt(index); + } + + void FileTypeFilterVector::RemoveAtEnd() + { + m_innerVector.RemoveAtEnd(); + } + + void FileTypeFilterVector::Clear() + { + m_innerVector.Clear(); + } + + uint32_t FileTypeFilterVector::GetMany(uint32_t startIndex, array_view items) const + { + return m_innerVector.GetMany(startIndex, items); + } + + winrt::Windows::Foundation::Collections::IIterator FileTypeFilterVector::First() const + { + return m_innerVector.First(); + } +} diff --git a/dev/Interop/StoragePickers/FileTypeFilterVector.h b/dev/Interop/StoragePickers/FileTypeFilterVector.h new file mode 100644 index 0000000000..7118a29d66 --- /dev/null +++ b/dev/Interop/StoragePickers/FileTypeFilterVector.h @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation and Contributors. +// Licensed under the MIT License. + +#pragma once +#include + +namespace winrt::Microsoft::Windows::Storage::Pickers::implementation +{ + struct FileTypeFilterVector : implements, + winrt::Windows::Foundation::Collections::IIterable> + { + FileTypeFilterVector(); + + // IVector + hstring GetAt(uint32_t index) const; + uint32_t Size() const; + winrt::Windows::Foundation::Collections::IVectorView GetView() const; + bool IndexOf(hstring const& value, uint32_t& index) const; + void SetAt(uint32_t index, hstring const& value); + void InsertAt(uint32_t index, hstring const& value); + void RemoveAt(uint32_t index); + void Append(hstring const& value); + void RemoveAtEnd(); + void Clear(); + uint32_t GetMany(uint32_t startIndex, array_view items) const; + void ReplaceAll(array_view items); + + // IIterable + winrt::Windows::Foundation::Collections::IIterator First() const; + + private: + winrt::Windows::Foundation::Collections::IVector m_innerVector{ single_threaded_vector() }; + }; +} diff --git a/dev/Interop/StoragePickers/FolderPicker.cpp b/dev/Interop/StoragePickers/FolderPicker.cpp index 6d38c058e0..2c906e6a19 100644 --- a/dev/Interop/StoragePickers/FolderPicker.cpp +++ b/dev/Interop/StoragePickers/FolderPicker.cpp @@ -26,6 +26,7 @@ namespace winrt::Microsoft::Windows::Storage::Pickers::implementation } void FolderPicker::ViewMode(winrt::Microsoft::Windows::Storage::Pickers::PickerViewMode const& value) { + PickerCommon::ValidateViewMode(value); m_viewMode = value; } hstring FolderPicker::SettingsIdentifier() @@ -34,6 +35,7 @@ namespace winrt::Microsoft::Windows::Storage::Pickers::implementation } void FolderPicker::SettingsIdentifier(hstring const& value) { + PickerCommon::ValidateStringNoEmbeddedNulls(value); m_settingsIdentifier = value; } winrt::Microsoft::Windows::Storage::Pickers::PickerLocationId FolderPicker::SuggestedStartLocation() @@ -42,6 +44,7 @@ namespace winrt::Microsoft::Windows::Storage::Pickers::implementation } void FolderPicker::SuggestedStartLocation(winrt::Microsoft::Windows::Storage::Pickers::PickerLocationId const& value) { + PickerCommon::ValidateSuggestedStartLocation(value); m_suggestedStartLocation = value; } hstring FolderPicker::CommitButtonText() @@ -50,6 +53,7 @@ namespace winrt::Microsoft::Windows::Storage::Pickers::implementation } void FolderPicker::CommitButtonText(hstring const& value) { + PickerCommon::ValidateStringNoEmbeddedNulls(value); m_commitButtonText = value; } diff --git a/dev/Interop/StoragePickers/Microsoft.Windows.Storage.Pickers.idl b/dev/Interop/StoragePickers/Microsoft.Windows.Storage.Pickers.idl index 6cfee3d9d2..3712b6cbec 100644 --- a/dev/Interop/StoragePickers/Microsoft.Windows.Storage.Pickers.idl +++ b/dev/Interop/StoragePickers/Microsoft.Windows.Storage.Pickers.idl @@ -23,8 +23,7 @@ namespace Microsoft.Windows.Storage.Pickers ComputerFolder, Desktop, Downloads, - HomeGroup, - MusicLibrary, + MusicLibrary = 5, PicturesLibrary, VideosLibrary, Objects3D, diff --git a/dev/Interop/StoragePickers/PickerCommon.cpp b/dev/Interop/StoragePickers/PickerCommon.cpp index a4b3b31d53..e70388e295 100644 --- a/dev/Interop/StoragePickers/PickerCommon.cpp +++ b/dev/Interop/StoragePickers/PickerCommon.cpp @@ -4,6 +4,7 @@ #include "pch.h" #include "shellapi.h" #include "PickerCommon.h" +#include "PickerLocalization.h" #include #include "ShObjIdl.h" #include "shobjidl_core.h" @@ -56,9 +57,6 @@ namespace { case winrt::Microsoft::Windows::Storage::Pickers::PickerLocationId::Downloads: knownFolderId = FOLDERID_Downloads; break; - case winrt::Microsoft::Windows::Storage::Pickers::PickerLocationId::HomeGroup: - knownFolderId = FOLDERID_HomeGroup; - break; case winrt::Microsoft::Windows::Storage::Pickers::PickerLocationId::MusicLibrary: knownFolderId = FOLDERID_MusicLibrary; break; @@ -148,6 +146,94 @@ namespace PickerCommon { return { nullptr, L""}; } + void ValidateViewMode(winrt::Microsoft::Windows::Storage::Pickers::PickerViewMode const& value) + { + switch (value) + { + case winrt::Microsoft::Windows::Storage::Pickers::PickerViewMode::List: + case winrt::Microsoft::Windows::Storage::Pickers::PickerViewMode::Thumbnail: + return; + default: + throw winrt::hresult_invalid_argument( + PickerLocalization::GetStoragePickersLocalizationText(InvalidViewModeLocalizationKey)); + break; + } + } + + void ValidateSuggestedStartLocation(winrt::Microsoft::Windows::Storage::Pickers::PickerLocationId const& value) + { + switch (value) + { + case winrt::Microsoft::Windows::Storage::Pickers::PickerLocationId::DocumentsLibrary: + case winrt::Microsoft::Windows::Storage::Pickers::PickerLocationId::ComputerFolder: + case winrt::Microsoft::Windows::Storage::Pickers::PickerLocationId::Desktop: + case winrt::Microsoft::Windows::Storage::Pickers::PickerLocationId::Downloads: + case winrt::Microsoft::Windows::Storage::Pickers::PickerLocationId::MusicLibrary: + case winrt::Microsoft::Windows::Storage::Pickers::PickerLocationId::PicturesLibrary: + case winrt::Microsoft::Windows::Storage::Pickers::PickerLocationId::VideosLibrary: + case winrt::Microsoft::Windows::Storage::Pickers::PickerLocationId::Objects3D: + case winrt::Microsoft::Windows::Storage::Pickers::PickerLocationId::Unspecified: + return; + default: + throw winrt::hresult_invalid_argument( + PickerLocalization::GetStoragePickersLocalizationText(InvalidSuggestedStartLocationLocalizationKey)); + break; + } + } + + void ValidateStringNoEmbeddedNulls(winrt::hstring const& value) + { + if (value.empty()) + { + return; + } + + for (int i = 0; i < value.size(); i++) + { + if (value[i] == L'\0') + { + throw winrt::hresult_invalid_argument( + PickerLocalization::GetStoragePickersLocalizationText(StringNoEmbeddedNullsLocalizationKey)); + } + } + } + + void ValidateSingleFileTypeFilterElement(winrt::hstring const& filter) + { + if (filter == L"*") + { + return; // "*" is a valid filter, stands for "All Files" + } + + if (filter.empty() || (filter[0] != L'.')) + { + throw winrt::hresult_invalid_argument( + PickerLocalization::GetStoragePickersLocalizationText(ImproperFileExtensionLocalizationKey)); + } + + for (int i = 1; i < filter.size(); i++) + { + if (filter[i] == L'.' || filter[i] == L'*' || filter[i] == L'?') + { + throw winrt::hresult_invalid_argument( + PickerLocalization::GetStoragePickersLocalizationText(ImproperFileExtensionLocalizationKey)); + } + } + + ValidateStringNoEmbeddedNulls(filter); + } + + void ValidateSuggestedFileName(winrt::hstring const& suggestedFileName) + { + if (suggestedFileName.size() > MAX_PATH) + { + throw winrt::hresult_invalid_argument( + PickerLocalization::GetStoragePickersLocalizationText(MaxSaveFileLengthExceededLocalizationKey)); + } + + ValidateStringNoEmbeddedNulls(suggestedFileName); + } + winrt::hstring PickerParameters::FormatExtensionWithWildcard(winrt::hstring extension) { if (!extension.empty() && extension[0] == L'*') diff --git a/dev/Interop/StoragePickers/PickerCommon.h b/dev/Interop/StoragePickers/PickerCommon.h index 43fc633c5b..fc73331aa9 100644 --- a/dev/Interop/StoragePickers/PickerCommon.h +++ b/dev/Interop/StoragePickers/PickerCommon.h @@ -13,10 +13,22 @@ namespace PickerCommon { winrt::hstring GetPathFromShellItem(winrt::com_ptr shellItem); const winrt::hstring AllFilesLocalizationKey = L"Microsoft.WindowsAppRuntime/StoragePickers/All Files"; + const winrt::hstring InvalidViewModeLocalizationKey{ L"Microsoft.WindowsAppRuntime/StoragePickers/IDS_APIERROR_INVALIDVIEWMODEVALUE"}; + const winrt::hstring InvalidSuggestedStartLocationLocalizationKey{L"Microsoft.WindowsAppRuntime/StoragePickers/IDS_APIERROR_INVALIDSUGGESTEDSTARTLOCATIONVALUE"}; + const winrt::hstring ImproperFileExtensionLocalizationKey{L"Microsoft.WindowsAppRuntime/StoragePickers/IDS_APIERROR_IMPROPERFILEEXTENSION"}; + const winrt::hstring StringNoEmbeddedNullsLocalizationKey{L"Microsoft.WindowsAppRuntime/StoragePickers/IDS_APIERROR_STRINGSNOEMBEDDEDNULLS"}; + const winrt::hstring MaxSaveFileLengthExceededLocalizationKey{L"Microsoft.WindowsAppRuntime/StoragePickers/IDS_APIERROR_MAXSAVEFILELENGTHEXCEEDED"}; + bool IsHStringNullOrEmpty(winrt::hstring value); std::pair, std::wstring> ParseFolderItemAndFileName(winrt::hstring const& filePath); + void ValidateStringNoEmbeddedNulls(winrt::hstring const& value); + void ValidateViewMode(winrt::Microsoft::Windows::Storage::Pickers::PickerViewMode const& value); + void ValidateSuggestedStartLocation(winrt::Microsoft::Windows::Storage::Pickers::PickerLocationId const& value); + void ValidateSingleFileTypeFilterElement(winrt::hstring const& filter); + void ValidateSuggestedFileName(winrt::hstring const& suggestedFileName); + struct PickerParameters { HWND HWnd{}; winrt::hstring CommitButtonText; diff --git a/dev/Interop/StoragePickers/PickerLocalization.h b/dev/Interop/StoragePickers/PickerLocalization.h index a0cb453809..bc1ce265ee 100644 --- a/dev/Interop/StoragePickers/PickerLocalization.h +++ b/dev/Interop/StoragePickers/PickerLocalization.h @@ -2,7 +2,6 @@ // Licensed under the MIT License. #pragma once -#include "PickerCommon.h" namespace PickerLocalization { winrt::hstring GetStoragePickersLocalizationText(winrt::hstring key); diff --git a/dev/Interop/StoragePickers/StoragePickers.vcxitems b/dev/Interop/StoragePickers/StoragePickers.vcxitems index 8f138033db..f2c51153ae 100644 --- a/dev/Interop/StoragePickers/StoragePickers.vcxitems +++ b/dev/Interop/StoragePickers/StoragePickers.vcxitems @@ -18,6 +18,8 @@ + + @@ -26,6 +28,8 @@ + + diff --git a/specs/StoragePickers/Microsoft.Windows.Storage.Pickers.md b/specs/StoragePickers/Microsoft.Windows.Storage.Pickers.md index 211e1000f1..713216790f 100644 --- a/specs/StoragePickers/Microsoft.Windows.Storage.Pickers.md +++ b/specs/StoragePickers/Microsoft.Windows.Storage.Pickers.md @@ -53,7 +53,6 @@ namespace Microsoft.Windows.Storage.Pickers ComputerFolder = 1, Desktop = 2, Downloads = 3, - HomeGroup = 4, // Will be removed in future. MusicLibrary = 5, PicturesLibrary = 6, VideosLibrary = 7, diff --git a/specs/StoragePickers/PickerLocationId.md b/specs/StoragePickers/PickerLocationId.md index 79d51189cb..af4d6303a7 100644 --- a/specs/StoragePickers/PickerLocationId.md +++ b/specs/StoragePickers/PickerLocationId.md @@ -13,7 +13,6 @@ Namespace: [Microsoft.Windows.Storage.Pickers](./Microsoft.Windows.Storage.Picke | ComputerFolder | 1 | [This PC](#pickerlocationidcomputerfolder) | | Desktop | 2 | [The Desktop](#pickerlocationiddesktop) | | Downloads | 3 | [The Downloads folder](#pickerlocationiddownloads) | -| HomeGroup | 4 | [The HomeGroup folder will be removed in future, (avoid using this)](#pickerlocationidhomegroup) | | MusicLibrary | 5 | [The Music library](#pickerlocationidmusiclibrary) | | PicturesLibrary | 6 | [The Pictures library](#pickerlocationidpictureslibrary) | | VideosLibrary | 7 | [The Videos library](#pickerlocationidvideoslibrary). | @@ -29,7 +28,6 @@ enum PickerLocationId ComputerFolder = 1, Desktop = 2, Downloads = 3, - HomeGroup = 4, MusicLibrary = 5, PicturesLibrary = 6, VideosLibrary = 7, @@ -64,12 +62,6 @@ The `Downloads` value represents the user's Downloads folder. This is the default location where web browsers and other applications save downloaded files. It is often mapped to `%USERPROFILE%\Downloads`. -## PickerLocationId.HomeGroup -! Note that [HomeGroup has been removed from Windows 10 (version 1803) and later](https://support.microsoft.com/en-us/windows/homegroup-removed-from-windows-10-version-1803-07ca5db1-7bca-4d11-68a3-a31ff4a09979), -so it is not recommended to use this value. - -This enum value is retained now for backward compatibility with the [Windows.Storage.Pickers.PickerLocationId](https://learn.microsoft.com/en-us/uwp/api/windows.storage.pickers.pickerlocationid), -it might be removed in future. ## PickerLocationId.MusicLibrary The `MusicLibrary` value represents the user's Music library. diff --git a/test/StoragePickersTests/PickerCommonTests.cpp b/test/StoragePickersTests/PickerCommonTests.cpp index 7e746a5dec..99263b0648 100644 --- a/test/StoragePickersTests/PickerCommonTests.cpp +++ b/test/StoragePickersTests/PickerCommonTests.cpp @@ -24,6 +24,9 @@ namespace Test::PickerCommonTests { class PickerCommonTests { + private: + static const wchar_t s_rawStringWithNull[]; + static const winrt::hstring s_embeddedNullString; public: BEGIN_TEST_CLASS(PickerCommonTests) TEST_CLASS_PROPERTY(L"ThreadingModel", L"MTA") // MTA is required for ::Test::Bootstrap::SetupPackages() @@ -300,5 +303,480 @@ namespace Test::PickerCommonTests } } + + TEST_METHOD(VerifyValidateViewMode) + { + // Arrange. + std::vector> test_cases { + {0, true}, // PickerViewMode::List + {1, true}, // PickerViewMode::Thumbnail + {2, false}, // An invalid value out of range + {-1, false}, // Negative value + {100, false} // An very large value out of range + }; + winrt::Microsoft::UI::WindowId windowId{}; + winrt::Microsoft::Windows::Storage::Pickers::FileOpenPicker picker(windowId); + + // Act & Assert + for (const auto& test_case : test_cases) + { + int viewModeValue = std::get<0>(test_case); + bool expectValid = std::get<1>(test_case); + + if (expectValid) + { + picker.ViewMode((winrt::Microsoft::Windows::Storage::Pickers::PickerViewMode)viewModeValue); + VERIFY_ARE_EQUAL(picker.ViewMode(), (winrt::Microsoft::Windows::Storage::Pickers::PickerViewMode)viewModeValue); + } + else + { + try + { + picker.ViewMode((winrt::Microsoft::Windows::Storage::Pickers::PickerViewMode)viewModeValue); + VERIFY_FAIL(L"Expected exception for invalid view mode"); + } + catch (...) + { + // Expected exception for invalid view mode + } + } + } + } + + TEST_METHOD(VerifyValidateSuggestedStartLocation) + { + // Arrange. + std::vector> test_cases { + {0, true}, // DocumentsLibrary + {1, true}, // ComputerFolder + {2, true}, // Desktop + {3, true}, // Downloads + {4, false}, // HomeGroup is removed, should return false. + {5, true}, // MusicLibrary + {6, true}, // PicturesLibrary + {7, true}, // VideosLibrary + {8, true}, // Objects3D + {9, true}, // Unspecified + {10, false}, // An invalid value out of range + {-1, false}, // Negative value + {100, false}, // An very large value out of range + }; + winrt::Microsoft::UI::WindowId windowId{}; + winrt::Microsoft::Windows::Storage::Pickers::FileOpenPicker picker(windowId); + + // Act & Assert + for (const auto& test_case : test_cases) + { + int locationValue = std::get<0>(test_case); + bool expectValid = std::get<1>(test_case); + + if (expectValid) + { + picker.SuggestedStartLocation((winrt::Microsoft::Windows::Storage::Pickers::PickerLocationId)locationValue); + VERIFY_ARE_EQUAL(picker.SuggestedStartLocation(), (winrt::Microsoft::Windows::Storage::Pickers::PickerLocationId)locationValue); + } + else + { + try + { + picker.SuggestedStartLocation((winrt::Microsoft::Windows::Storage::Pickers::PickerLocationId)locationValue); + VERIFY_FAIL(L"Expected exception for invalid suggested start location"); + } + catch (...) + { + // Expected exception for invalid suggested start location + } + } + } + } + + TEST_METHOD(VerifyValidateSuggestedFileName) + { + // Arrange. + auto test_cases = std::vector>{ + {L"validFileName.txt", true}, // Valid file name + {s_embeddedNullString, false}, // embedded null + {L"", true}, // Allow Empty string + {L"validFileNameWithSpaces .txt", true},// Allow spaces + { + L"too_long_file_name_that_exceeds_the_maximum_length_of_a_file_name_usually_260_characters_too_long_file_name_that_exceeds_the_maximum_length_of_a_file_name_usually_260_characters_too_long_file_name_that_exceeds_the_maximum_length_of_a_file_name_usually_260_characters.txt", + false, // File name too long + }, + }; + + winrt::Microsoft::UI::WindowId windowId{}; + winrt::Microsoft::Windows::Storage::Pickers::FileSavePicker picker(windowId); + + // Act & Assert + for (const auto& test_case : test_cases) + { + winrt::hstring suggestedFileName = std::get<0>(test_case); + bool expectValid = std::get<1>(test_case); + if (expectValid) + { + picker.SuggestedFileName(suggestedFileName); + VERIFY_ARE_EQUAL(picker.SuggestedFileName(), suggestedFileName); + } + else + { + try + { + picker.SuggestedFileName(suggestedFileName); + + std::wstring errorMessage = L"Expected exception for invalid suggested file name: " + std::wstring(suggestedFileName); + VERIFY_FAIL(errorMessage.c_str()); + } + catch (...) + { + // Expected exception for invalid suggested file name + } + } + } + } + + std::vector> cbsi_text_test_cases{ + {L"Valid", true}, // Valid commit button text + {L"", true}, // Allow empty string + {L"Valid Text", true}, // Allow spaces + {s_embeddedNullString, false}, // Embedded null is invalid + }; + + TEST_METHOD(VerifyFolderPicker_ValidateCommitButtonText) + { + // Arrange. + winrt::Microsoft::UI::WindowId windowId{}; + winrt::Microsoft::Windows::Storage::Pickers::FolderPicker picker(windowId); + + // Act & Assert + for (const auto& test_case : cbsi_text_test_cases) + { + winrt::hstring commitButtonText = std::get<0>(test_case); + bool expectValid = std::get<1>(test_case); + + if (expectValid) + { + picker.CommitButtonText(commitButtonText); + VERIFY_ARE_EQUAL(picker.CommitButtonText(), commitButtonText); + } + else + { + try + { + picker.CommitButtonText(commitButtonText); + std::wstring errorMessage = L"Expected exception for invalid commit button text: " + std::wstring(commitButtonText); + VERIFY_FAIL(errorMessage.c_str()); + } + catch (...) + { + // Expected exception for invalid commit button text + } + } + } + } + + TEST_METHOD(VerifyFileOpenPicker_ValidateCommitButtonText) + { + // Arrange. + winrt::Microsoft::UI::WindowId windowId{}; + winrt::Microsoft::Windows::Storage::Pickers::FileOpenPicker picker(windowId); + + // Act & Assert + for (const auto& test_case : cbsi_text_test_cases) + { + winrt::hstring commitButtonText = std::get<0>(test_case); + bool expectValid = std::get<1>(test_case); + + if (expectValid) + { + picker.CommitButtonText(commitButtonText); + VERIFY_ARE_EQUAL(picker.CommitButtonText(), commitButtonText); + } + else + { + try + { + picker.CommitButtonText(commitButtonText); + std::wstring errorMessage = L"Expected exception for invalid commit button text: " + std::wstring(commitButtonText); + VERIFY_FAIL(errorMessage.c_str()); + } + catch (...) + { + // Expected exception for invalid commit button text + } + } + } + } + + TEST_METHOD(VerifyFileSavePicker_ValidateCommitButtonText) + { + // Arrange. + winrt::Microsoft::UI::WindowId windowId{}; + winrt::Microsoft::Windows::Storage::Pickers::FileSavePicker picker(windowId); + + // Act & Assert + for (const auto& test_case : cbsi_text_test_cases) + { + winrt::hstring commitButtonText = std::get<0>(test_case); + bool expectValid = std::get<1>(test_case); + + if (expectValid) + { + picker.CommitButtonText(commitButtonText); + VERIFY_ARE_EQUAL(picker.CommitButtonText(), commitButtonText); + } + else + { + try + { + picker.CommitButtonText(commitButtonText); + std::wstring errorMessage = L"Expected exception for invalid commit button text: " + std::wstring(commitButtonText); + VERIFY_FAIL(errorMessage.c_str()); + } + catch (...) + { + // Expected exception for invalid commit button text + } + } + } + } + + TEST_METHOD(VerifyFolderPicker_ValidateSettingsIdentifer) + { + // Arrange. + winrt::Microsoft::UI::WindowId windowId{}; + winrt::Microsoft::Windows::Storage::Pickers::FolderPicker picker(windowId); + + // Act & Assert + for (const auto& test_case : cbsi_text_test_cases) + { + winrt::hstring settingsIdentifier = std::get<0>(test_case); + bool expectValid = std::get<1>(test_case); + + if (expectValid) + { + picker.SettingsIdentifier(settingsIdentifier); + VERIFY_ARE_EQUAL(picker.SettingsIdentifier(), settingsIdentifier); + } + else + { + try + { + picker.SettingsIdentifier(settingsIdentifier); + std::wstring errorMessage = L"Expected exception for invalid settings identifier text: " + std::wstring(settingsIdentifier); + VERIFY_FAIL(errorMessage.c_str()); + } + catch (...) + { + // Expected exception for invalid settings identifier text + } + } + } + } + + TEST_METHOD(VerifyFileOpenPicker_ValidateSettingsIdentifier) + { + // Arrange. + winrt::Microsoft::UI::WindowId windowId{}; + winrt::Microsoft::Windows::Storage::Pickers::FileOpenPicker picker(windowId); + + // Act & Assert + for (const auto& test_case : cbsi_text_test_cases) + { + winrt::hstring settingsIdentifier = std::get<0>(test_case); + bool expectValid = std::get<1>(test_case); + + if (expectValid) + { + picker.SettingsIdentifier(settingsIdentifier); + VERIFY_ARE_EQUAL(picker.SettingsIdentifier(), settingsIdentifier); + } + else + { + try + { + picker.SettingsIdentifier(settingsIdentifier); + std::wstring errorMessage = L"Expected exception for invalid settings identifier text: " + std::wstring(settingsIdentifier); + VERIFY_FAIL(errorMessage.c_str()); + } + catch (...) + { + // Expected exception for invalid settings identifier text + } + } + } + } + + TEST_METHOD(VerifyFileSavePicker_ValidateSettingsIdentifier) + { + // Arrange. + winrt::Microsoft::UI::WindowId windowId{}; + winrt::Microsoft::Windows::Storage::Pickers::FileSavePicker picker(windowId); + + // Act & Assert + for (const auto& test_case : cbsi_text_test_cases) + { + winrt::hstring settingsIdentifier = std::get<0>(test_case); + bool expectValid = std::get<1>(test_case); + + if (expectValid) + { + picker.SettingsIdentifier(settingsIdentifier); + VERIFY_ARE_EQUAL(picker.SettingsIdentifier(), settingsIdentifier); + } + else + { + try + { + picker.SettingsIdentifier(settingsIdentifier); + std::wstring errorMessage = L"Expected exception for invalid settings identifier text: " + std::wstring(settingsIdentifier); + VERIFY_FAIL(errorMessage.c_str()); + } + catch (...) + { + // Expected exception for invalid settings identifier text + } + } + } + } + + std::vector> file_extension_validation_test_cases{ + {L".txt", true}, + {L"*", true}, // One asterisk is valid + {L"**", false}, // More than one asterisk is invalid + {L"*.docx", false}, // Filter (if not "*") must start with '.' + {L"txt", false}, // Must start with '.' or '*' + {L"", false}, // Must start with '.' or '*', empty string is invalid + {L".t*t", false}, // Should not contain other wildcards + {L".t.", false}, // Should not contain other wildcards + {L".?txt", false}, // Should not contain other wildcards + }; + + TEST_METHOD(VerifyValidateSingleFileTypeFilterElement) + { + for (const auto& test_case : file_extension_validation_test_cases) + { + winrt::hstring filter = std::get<0>(test_case); + bool expectValid = std::get<1>(test_case); + + if (expectValid) + { + PickerCommon::ValidateSingleFileTypeFilterElement(filter); + } + else + { + try + { + PickerCommon::ValidateSingleFileTypeFilterElement(filter); + + std::wstring errorMessage = L"Expected exception for invalid single file type filter element: " + std::wstring(filter); + VERIFY_FAIL(errorMessage.c_str()); + } + catch (...) + { + // Expected exception for invalid single file type filter element + } + } + } + } + + TEST_METHOD(VerifyValidateFileTypeFilter) + { + // Arrange. + winrt::Microsoft::UI::WindowId windowId{}; + winrt::Microsoft::Windows::Storage::Pickers::FileOpenPicker picker(windowId); + + // Act & Assert + for (const auto& test_case : file_extension_validation_test_cases) + { + auto& filter = std::get<0>(test_case); + bool expectValid = std::get<1>(test_case); + + if (expectValid) + { + picker.FileTypeFilter().Append(filter); + auto newFilters = picker.FileTypeFilter().GetView(); + VERIFY_ARE_EQUAL(newFilters.GetAt(newFilters.Size() - 1), filter); + } + else + { + try + { + picker.FileTypeFilter().Append(filter); + + std::wstring errorMessage = L"Expected exception for invalid file type filter: " + std::wstring(filter); + VERIFY_FAIL(errorMessage.c_str()); + } + catch (...) + { + // Expected exception for invalid file type filter + } + } + } + } + + TEST_METHOD(VerifyValidateFileTypeChoices) + { + // Arrange. + winrt::Microsoft::UI::WindowId windowId{}; + winrt::Microsoft::Windows::Storage::Pickers::FileSavePicker picker(windowId); + + // Act & Assert + for (const auto& test_case : file_extension_validation_test_cases) + { + auto& choice = std::get<0>(test_case); + bool expectValid = std::get<1>(test_case); + + if (expectValid) + { + // Create a vector with the extension + auto extensions = winrt::single_threaded_vector({ choice }); + picker.FileTypeChoices().Insert(L"TestChoice", extensions); + + // Verify that the choice was added + auto choices = picker.FileTypeChoices(); + VERIFY_IS_TRUE(choices.HasKey(L"TestChoice")); + auto newExtensions = choices.Lookup(L"TestChoice"); + VERIFY_ARE_EQUAL(newExtensions.GetAt(0), choice); + } + else + { + // Attempt to insert a key-value pair that has invalid choice + try + { + auto extensions = winrt::single_threaded_vector({ choice }); + picker.FileTypeChoices().Insert(L"InvalidChoice", extensions); + + std::wstring errorMessage = L"Expected exception when inserting an invalid file type choice: " + std::wstring(choice); + VERIFY_FAIL(errorMessage.c_str()); + } + catch (...) + { + // Expected exception for invalid file type choice + } + + // Attempt to append an invalid choice to the existing choices + picker.FileTypeChoices().Insert( + L"ValidChoice", + winrt::single_threaded_vector({ L".txt", L".doc" })); + + try + { + picker.FileTypeChoices().Lookup(L"ValidChoice").Append(choice); + + std::wstring errorMessage = L"Expected exception when appending an invalid file type choice: " + std::wstring(choice); + VERIFY_FAIL(errorMessage.c_str()); + } + catch (...) + { + // Expected exception for invalid file type filter + } + } + } + } + }; + + const wchar_t PickerCommonTests::s_rawStringWithNull[] = L"in\0valid.txt"; + const winrt::hstring PickerCommonTests::s_embeddedNullString{ std::wstring_view(s_rawStringWithNull, 12) }; + } diff --git a/test/StoragePickersTests/PickerLocalization.h b/test/StoragePickersTests/PickerLocalization.h new file mode 100644 index 0000000000..8db08c98e7 --- /dev/null +++ b/test/StoragePickersTests/PickerLocalization.h @@ -0,0 +1,10 @@ +// Copyright (c) Microsoft Corporation and Contributors. +// Licensed under the MIT License. + +// This is a mocked PickderLocalization head file for test purpose. +#pragma once + +namespace PickerLocalization +{ + winrt::hstring GetStoragePickersLocalizationText(winrt::hstring key); +} \ No newline at end of file diff --git a/test/StoragePickersTests/Pickerlocalization.cpp b/test/StoragePickersTests/Pickerlocalization.cpp new file mode 100644 index 0000000000..49e1989dbd --- /dev/null +++ b/test/StoragePickersTests/Pickerlocalization.cpp @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft Corporation and Contributors. +// Licensed under the MIT License. + +// This is a mocked PickerLocalization source file for test purpose. +#include "pch.h" +#include "PickerLocalization.h" + +namespace PickerLocalization +{ + winrt::hstring GetStoragePickersLocalizationText(winrt::hstring key) + { + return key; + } +} diff --git a/test/StoragePickersTests/StoragePickersTests.cpp b/test/StoragePickersTests/StoragePickersTests.cpp index ec17dbae3d..39d01368f8 100644 --- a/test/StoragePickersTests/StoragePickersTests.cpp +++ b/test/StoragePickersTests/StoragePickersTests.cpp @@ -203,5 +203,6 @@ namespace Test::StoragePickersTests picker.CommitButtonText(L"commit"); VERIFY_ARE_EQUAL(picker.CommitButtonText(), L"commit"); } + }; } diff --git a/test/StoragePickersTests/StoragePickersTests.vcxproj b/test/StoragePickersTests/StoragePickersTests.vcxproj index 04e6485e4b..1d1b2fff7a 100644 --- a/test/StoragePickersTests/StoragePickersTests.vcxproj +++ b/test/StoragePickersTests/StoragePickersTests.vcxproj @@ -109,6 +109,7 @@ Create + $(RepoRoot)\build\VersionInfo;%(AdditionalIncludeDirectories) @@ -116,6 +117,7 @@ + diff --git a/test/StoragePickersTests/StoragePickersTests.vcxproj.filters b/test/StoragePickersTests/StoragePickersTests.vcxproj.filters index 5c34c9542c..660a8cc362 100644 --- a/test/StoragePickersTests/StoragePickersTests.vcxproj.filters +++ b/test/StoragePickersTests/StoragePickersTests.vcxproj.filters @@ -33,6 +33,9 @@ Source Files + + Source Files + @@ -41,5 +44,8 @@ Source Files + + Source Files + \ No newline at end of file