diff --git a/dom/events/DataTransfer.cpp b/dom/events/DataTransfer.cpp index 76c75197afd09..2ad0effce8ed2 100644 --- a/dom/events/DataTransfer.cpp +++ b/dom/events/DataTransfer.cpp @@ -910,7 +910,9 @@ DataTransfer::GetFilesAndDirectories(ErrorResult& aRv) nsDependentSubstring dirname = Substring(path, 0, leafSeparatorIndex); nsDependentSubstring basename = Substring(path, leafSeparatorIndex); fs = MakeOrReuseFileSystem(dirname, fs, window); - filesAndDirsSeq[i].SetAsDirectory() = new Directory(fs, basename); + RefPtr directory = new Directory(fs, basename); + directory->SetContentFilters(NS_LITERAL_STRING("filter-out-sensitive")); + filesAndDirsSeq[i].SetAsDirectory() = directory; } else { filesAndDirsSeq[i].SetAsFile() = mFiles->Item(i); } diff --git a/dom/filesystem/Directory.cpp b/dom/filesystem/Directory.cpp index b09b534956b25..e0ee304c0d0c3 100644 --- a/dom/filesystem/Directory.cpp +++ b/dom/filesystem/Directory.cpp @@ -242,7 +242,7 @@ Directory::GetFilesAndDirectories() nsString realPath; ErrorResult rv; RefPtr task = - new GetDirectoryListingTask(mFileSystem, mPath, rv); + new GetDirectoryListingTask(mFileSystem, mPath, mFilters, rv); if (NS_WARN_IF(rv.Failed())) { return nullptr; } @@ -251,6 +251,12 @@ Directory::GetFilesAndDirectories() return task->GetPromise(); } +void +Directory::SetContentFilters(const nsAString& aFilters) +{ + mFilters = aFilters; +} + FileSystemBase* Directory::GetFileSystem() const { diff --git a/dom/filesystem/Directory.h b/dom/filesystem/Directory.h index 3683875a1c918..6617e00f35646 100644 --- a/dom/filesystem/Directory.h +++ b/dom/filesystem/Directory.h @@ -87,6 +87,31 @@ class Directory final // =========== End WebIDL bindings.============ + /** + * Sets a semi-colon separated list of filters to filter-in or filter-out + * certain types of files when the contents of this directory are requested + * via a GetFilesAndDirectories() call. + * + * Currently supported keywords: + * + * * filter-out-sensitive + * This keyword filters out files or directories that we don't wish to + * make available to Web content because we are concerned that there is + * a risk that users may unwittingly give Web content access to them + * and suffer undesirable consequences. The details of what is + * filtered out can be found in GetDirectoryListingTask::Work. + * + * In future, we will likely support filtering based on filename extensions + * (for example, aFilters could be "*.jpg; *.jpeg; *.gif"), but that isn't + * supported yet. Once supported, files that don't match a specified + * extension (if any are specified) would be filtered out. This + * functionality would allow us to apply the 'accept' attribute from + * to the results of a directory + * picker operation. + */ + void + SetContentFilters(const nsAString& aFilters); + FileSystemBase* GetFileSystem() const; private: @@ -108,6 +133,7 @@ class Directory final RefPtr mFileSystem; nsString mPath; + nsString mFilters; }; } // namespace dom diff --git a/dom/filesystem/GetDirectoryListingTask.cpp b/dom/filesystem/GetDirectoryListingTask.cpp index bbd133c18aaf2..04c3fd5b28a5e 100644 --- a/dom/filesystem/GetDirectoryListingTask.cpp +++ b/dom/filesystem/GetDirectoryListingTask.cpp @@ -6,6 +6,7 @@ #include "GetDirectoryListingTask.h" +#include "HTMLSplitOnSpacesTokenizer.h" #include "js/Value.h" #include "mozilla/dom/Directory.h" #include "mozilla/dom/File.h" @@ -22,9 +23,11 @@ namespace dom { GetDirectoryListingTask::GetDirectoryListingTask(FileSystemBase* aFileSystem, const nsAString& aTargetPath, + const nsAString& aFilters, ErrorResult& aRv) : FileSystemTaskBase(aFileSystem) , mTargetRealPath(aTargetPath) + , mFilters(aFilters) { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); MOZ_ASSERT(aFileSystem); @@ -40,12 +43,13 @@ GetDirectoryListingTask::GetDirectoryListingTask(FileSystemBase* aFileSystem, const FileSystemGetDirectoryListingParams& aParam, FileSystemRequestParent* aParent) : FileSystemTaskBase(aFileSystem, aParam, aParent) + , mTargetRealPath(aParam.realPath()) + , mFilters(aParam.filters()) { MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!"); MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); MOZ_ASSERT(aFileSystem); - mTargetRealPath = aParam.realPath(); } GetDirectoryListingTask::~GetDirectoryListingTask() @@ -65,7 +69,8 @@ FileSystemParams GetDirectoryListingTask::GetRequestParams(const nsString& aFileSystem) const { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); - return FileSystemGetDirectoryListingParams(aFileSystem, mTargetRealPath); + return FileSystemGetDirectoryListingParams(aFileSystem, mTargetRealPath, + mFilters); } FileSystemResponseValue @@ -155,6 +160,20 @@ GetDirectoryListingTask::Work() return rv; } + bool filterOutSensitive = false; + { + HTMLSplitOnSpacesTokenizer tokenizer(mFilters, ';'); + nsAutoString token; + while (tokenizer.hasMoreTokens()) { + token = tokenizer.nextToken(); + if (token.EqualsLiteral("filter-out-sensitive")) { + filterOutSensitive = true; + } else { + MOZ_CRASH("Unrecognized filter"); + } + } + } + for (;;) { bool hasMore = false; if (NS_WARN_IF(NS_FAILED(entries->HasMoreElements(&hasMore))) || !hasMore) { @@ -179,6 +198,21 @@ GetDirectoryListingTask::Work() !(isFile || isDir)) { continue; } + + if (filterOutSensitive) { + bool isHidden; + if (NS_WARN_IF(NS_FAILED(currFile->IsHidden(&isHidden))) || isHidden) { + continue; + } + nsAutoString leafName; + if (NS_WARN_IF(NS_FAILED(currFile->GetLeafName(leafName)))) { + continue; + } + if (leafName[0] == char16_t('.')) { + continue; + } + } + BlobImplFile* impl = new BlobImplFile(currFile); impl->LookupAndCacheIsDirectory(); mTargetBlobImpls.AppendElement(impl); @@ -226,7 +260,10 @@ GetDirectoryListingTask::HandlerCallback() MOZ_ASSERT(exist); } #endif - listing[i].SetAsDirectory() = new Directory(mFileSystem, path); + RefPtr directory = new Directory(mFileSystem, path); + // Propogate mFilter onto sub-Directory object: + directory->SetContentFilters(mFilters); + listing[i].SetAsDirectory() = directory; } else { listing[i].SetAsFile() = File::Create(mFileSystem->GetWindow(), mTargetBlobImpls[i]); } diff --git a/dom/filesystem/GetDirectoryListingTask.h b/dom/filesystem/GetDirectoryListingTask.h index 9e97bbdce7141..4a6dc7b045ab5 100644 --- a/dom/filesystem/GetDirectoryListingTask.h +++ b/dom/filesystem/GetDirectoryListingTask.h @@ -23,6 +23,7 @@ class GetDirectoryListingTask final // If aDirectoryOnly is set, we should ensure that the target is a directory. GetDirectoryListingTask(FileSystemBase* aFileSystem, const nsAString& aTargetPath, + const nsAString& aFilters, ErrorResult& aRv); GetDirectoryListingTask(FileSystemBase* aFileSystem, const FileSystemGetDirectoryListingParams& aParam, @@ -55,6 +56,7 @@ class GetDirectoryListingTask final private: RefPtr mPromise; nsString mTargetRealPath; + nsString mFilters; // We cannot store File or Directory objects bacause this object is created // on a different thread and File and Directory are not thread-safe. diff --git a/dom/html/HTMLInputElement.cpp b/dom/html/HTMLInputElement.cpp index 6205eed03cef3..048a64fb28170 100644 --- a/dom/html/HTMLInputElement.cpp +++ b/dom/html/HTMLInputElement.cpp @@ -4934,8 +4934,14 @@ HTMLInputElement::GetFilesAndDirectories(ErrorResult& aRv) fs = MakeOrReuseFileSystem(dirname, fs, window); nsAutoString dompath(NS_LITERAL_STRING(FILESYSTEM_DOM_PATH_SEPARATOR)); dompath.Append(Substring(path, leafSeparatorIndex + 1)); - filesAndDirsSeq[i].SetAsDirectory() = new Directory(fs, dompath); + RefPtr directory = new Directory(fs, dompath); + // In future we could refactor SetFilePickerFiltersFromAccept to return a + // semicolon separated list of file extensions and include that in the + // filter string passed here. + directory->SetContentFilters(NS_LITERAL_STRING("filter-out-sensitive")); + filesAndDirsSeq[i].SetAsDirectory() = directory; } else { + // This file was directly selected by the user, so don't filter it. filesAndDirsSeq[i].SetAsFile() = filesAndDirs[i]; } } diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index fbbce29936f98..c6ccb0120e381 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -276,6 +276,16 @@ struct FileSystemGetDirectoryListingParams { nsString filesystem; nsString realPath; + // 'filters' could be an array rather than a semicolon separated string + // (we'd then use InfallibleTArray internally), but that is + // wasteful. E10s requires us to pass the filters over as a string anyway, + // so avoiding using an array avoids serialization on the side passing the + // filters. Since an nsString can share its buffer when copied, + // using that instead of InfallibleTArray makes copying the filters + // around in any given process a bit more efficient too, since copying a + // single nsString is cheaper than copying InfallibleTArray member data and + // each nsString that it contains. + nsString filters; }; struct FileSystemGetFileOrDirectoryParams