diff options
Diffstat (limited to 'dom/system/PathUtils.h')
-rw-r--r-- | dom/system/PathUtils.h | 267 |
1 files changed, 267 insertions, 0 deletions
diff --git a/dom/system/PathUtils.h b/dom/system/PathUtils.h new file mode 100644 index 0000000000..cbfceef269 --- /dev/null +++ b/dom/system/PathUtils.h @@ -0,0 +1,267 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_PathUtils__ +#define mozilla_dom_PathUtils__ + +#include "mozilla/DataMutex.h" +#include "mozilla/EnumeratedArray.h" +#include "mozilla/Maybe.h" +#include "mozilla/MozPromise.h" +#include "mozilla/Mutex.h" +#include "mozilla/Result.h" +#include "mozilla/dom/PathUtilsBinding.h" +#include "mozilla/dom/Promise.h" +#include "nsAppDirectoryServiceDefs.h" +#include "nsDirectoryServiceDefs.h" +#include "nsString.h" +#include "nsTArray.h" + +namespace mozilla { +class ErrorResult; + +namespace dom { + +class PathUtils final { + public: + /** + * Initialize the given nsIFile with the given path. + * + * This is equivalent to calling nsIFile::InitWithPath() with the caveat that + * on Windows debug or during Windows CI tests, we will crash if the path + * contains a forward slash. + * + * @param aFile The file to initialize. + * @param aPath The path to initialize the file with. + * + * @return The result of calling nsIFile::InitWithPath. + */ + static nsresult InitFileWithPath(nsIFile* aFile, const nsAString& aPath); + + static void Filename(const GlobalObject&, const nsAString& aPath, + nsString& aResult, ErrorResult& aErr); + + static void Parent(const GlobalObject&, const nsAString& aPath, + const int32_t aDepth, nsString& aResult, + ErrorResult& aErr); + + static void Join(const GlobalObject&, const Sequence<nsString>& aComponents, + nsString& aResult, ErrorResult& aErr); + + /** + * Join a sequence of path components and return an nsIFile with the resulting + * path. + * + * @param aComponents A sequence of path components. The first component must + * be an absolute path. + * @param aErr The error result, if any. + * + * @return An nsIFile with the resulting path, if there were no errors. + * Otherwise, nullptr is returned. + */ + static already_AddRefed<nsIFile> Join(const Span<const nsString>& aComponents, + ErrorResult& aErr); + + static void JoinRelative(const GlobalObject&, const nsAString& aBasePath, + const nsAString& aRelativePath, nsString& aResult, + ErrorResult& aErr); + + static void ToExtendedWindowsPath(const GlobalObject&, const nsAString& aPath, + nsString& aResult, ErrorResult& aErr); + + static void Normalize(const GlobalObject&, const nsAString& aPath, + nsString& aResult, ErrorResult& aErr); + + static void Split(const GlobalObject&, const nsAString& aPath, + nsTArray<nsString>& aResult, ErrorResult& aErr); + + static void SplitRelative(const GlobalObject& aGlobal, const nsAString& aPath, + const SplitRelativeOptions& aOptions, + nsTArray<nsString>& aResult, ErrorResult& aErr); + + static void ToFileURI(const GlobalObject&, const nsAString& aPath, + nsCString& aResult, ErrorResult& aErr); + + static bool IsAbsolute(const GlobalObject&, const nsAString& aPath); + + static void GetProfileDirSync(const GlobalObject&, nsString& aResult, + ErrorResult& aErr); + static void GetLocalProfileDirSync(const GlobalObject&, nsString& aResult, + ErrorResult& aErr); + static void GetTempDirSync(const GlobalObject&, nsString& aResult, + ErrorResult& aErr); + static void GetOSTempDirSync(const GlobalObject&, nsString& aResult, + ErrorResult& aErr); + static void GetXulLibraryPathSync(const GlobalObject&, nsString& aResult, + ErrorResult& aErr); + + static already_AddRefed<Promise> GetProfileDirAsync( + const GlobalObject& aGlobal, ErrorResult& aErr); + static already_AddRefed<Promise> GetLocalProfileDirAsync( + const GlobalObject& aGlobal, ErrorResult& aErr); + static already_AddRefed<Promise> GetTempDirAsync(const GlobalObject& aGlobal, + ErrorResult& aErr); + static already_AddRefed<Promise> GetOSTempDirAsync( + const GlobalObject& aGlobal, ErrorResult& aErr); + static already_AddRefed<Promise> GetXulLibraryPathAsync( + const GlobalObject& aGlobal, ErrorResult& aErr); + + private: + class DirectoryCache; + friend class DirectoryCache; + + static StaticDataMutex<Maybe<DirectoryCache>> sDirCache; +}; + +/** + * A cache of commonly used directories + */ +class PathUtils::DirectoryCache final { + public: + /** + * A directory that can be requested via |GetDirectorySync| or + * |GetDirectoryAsync|. + */ + enum class Directory { + /** + * The user's profile directory. + */ + Profile, + /** + * The user's local profile directory. + */ + LocalProfile, + /** + * The temporary directory for the process. + */ + Temp, + /** + * The OS temporary directory. + */ + OSTemp, + /** + * The libxul path. + */ + XulLibrary, + /** + * The number of Directory entries. + */ + Count, + }; + + DirectoryCache(); + DirectoryCache(const DirectoryCache&) = delete; + DirectoryCache(DirectoryCache&&) = delete; + DirectoryCache& operator=(const DirectoryCache&) = delete; + DirectoryCache& operator=(DirectoryCache&&) = delete; + + /** + * Ensure the cache is instantiated and schedule its destructor to run at + * shutdown. + * + * If the cache is already instantiated, this is a no-op. + * + * @param aCache The cache to ensure is instantiated. + */ + static DirectoryCache& Ensure(Maybe<DirectoryCache>& aCache); + + void GetDirectorySync(nsString& aResult, ErrorResult& aErr, + const Directory aRequestedDir); + + /** + * Request the path of a specific directory. + * + * If the directory has not been requested before, this may require a trip to + * the main thread to retrieve its path. + * + * @param aGlobalObject The JavaScript global. + * @param aErr The error result. + * @param aRequestedDir The directory for which the path is to be retrieved. + * + * @return A promise that resolves to the path of the requested directory. + */ + already_AddRefed<Promise> GetDirectoryAsync(const GlobalObject& aGlobalObject, + ErrorResult& aErr, + const Directory aRequestedDir); + + private: + using PopulateDirectoriesPromise = MozPromise<Ok, nsresult, false>; + + /** + * Populate the directory cache entry for the requested directory. + * + * @param aRequestedDir The directory cache entry that was requested via + * |GetDirectory|. + * + * @return If the requested directory has not been populated, this returns a + * promise that resolves when the population is complete. + * + * If the requested directory has already been populated, it returns + * nullptr instead. + */ + already_AddRefed<PopulateDirectoriesPromise> PopulateDirectories( + const Directory aRequestedDir); + + /** + * Initialize the requested directory cache entry. + * + * If |Directory::Temp| is requested, all cache entries will be populated. + * Otherwise, only the profile and local profile cache entries will be + * populated. The profile and local profile cache entries have no additional + * overhead for populating them, but the temp directory requires creating a + * directory on the main thread if it has not already happened. + * + * Must be called on the main thread. + * + * @param aRequestedDir The requested directory. + * + * @return The result of initializing directories. + */ + nsresult PopulateDirectoriesImpl(const Directory aRequestedDir); + + /** + * Resolve the internal PopulateDirectoriesPromise corresponding to + * |aRequestedDir| with the given result. + * + * This will allow all pending queries for the requested directory to resolve + * or be rejected. + * + * @param aRv The return value from PopulateDirectoriesImpl. + * @param aRequestedDir The requested directory cache entry. This is used to + * determine which internal MozPromiseHolder we are + * resolving. + */ + void ResolvePopulateDirectoriesPromise(nsresult aRv, + const Directory aRequestedDir); + + /** + * Resolve the given JS promise with the path of the requested directory + * + * Can only be called once the cache entry for the requested directory is + * populated. + * + * @param aPromise The JS promise to resolve. + * @param aRequestedDir The requested directory cache entry. + */ + void ResolveWithDirectory(Promise* aPromise, const Directory aRequestedDir); + + template <typename T> + using DirectoryArray = EnumeratedArray<Directory, Directory::Count, T>; + + DirectoryArray<nsString> mDirectories; + DirectoryArray<MozPromiseHolder<PopulateDirectoriesPromise>> mPromises; + + static constexpr DirectoryArray<const char*> kDirectoryNames{ + NS_APP_USER_PROFILE_50_DIR, NS_APP_USER_PROFILE_LOCAL_50_DIR, + NS_APP_CONTENT_PROCESS_TEMP_DIR, NS_OS_TEMP_DIR, + NS_XPCOM_LIBRARY_FILE, + }; +}; + +} // namespace dom +} // namespace mozilla + +#endif |