1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
|
/* -*- 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;
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 dom::GlobalObject&, const nsAString& aPath,
nsString& aResult, ErrorResult& aErr);
static void Parent(const dom::GlobalObject&, const nsAString& aPath,
const int32_t aDepth, nsString& aResult,
ErrorResult& aErr);
static void Join(const dom::GlobalObject&,
const dom::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 dom::GlobalObject&, const nsAString& aBasePath,
const nsAString& aRelativePath, nsString& aResult,
ErrorResult& aErr);
static void ToExtendedWindowsPath(const dom::GlobalObject&,
const nsAString& aPath, nsString& aResult,
ErrorResult& aErr);
static void Normalize(const dom::GlobalObject&, const nsAString& aPath,
nsString& aResult, ErrorResult& aErr);
static void Split(const dom::GlobalObject&, const nsAString& aPath,
nsTArray<nsString>& aResult, ErrorResult& aErr);
static void SplitRelative(const dom::GlobalObject& aGlobal,
const nsAString& aPath,
const dom::SplitRelativeOptions& aOptions,
nsTArray<nsString>& aResult, ErrorResult& aErr);
static void ToFileURI(const dom::GlobalObject&, const nsAString& aPath,
nsCString& aResult, ErrorResult& aErr);
static bool IsAbsolute(const dom::GlobalObject&, const nsAString& aPath);
static void GetProfileDirSync(const dom::GlobalObject&, nsString& aResult,
ErrorResult& aErr);
static void GetLocalProfileDirSync(const dom::GlobalObject&,
nsString& aResult, ErrorResult& aErr);
static void GetTempDirSync(const dom::GlobalObject&, nsString& aResult,
ErrorResult& aErr);
static void GetXulLibraryPathSync(const dom::GlobalObject&, nsString& aResult,
ErrorResult& aErr);
static already_AddRefed<dom::Promise> GetProfileDirAsync(
const dom::GlobalObject& aGlobal, ErrorResult& aErr);
static already_AddRefed<dom::Promise> GetLocalProfileDirAsync(
const dom::GlobalObject& aGlobal, ErrorResult& aErr);
static already_AddRefed<dom::Promise> GetTempDirAsync(
const dom::GlobalObject& aGlobal, ErrorResult& aErr);
static already_AddRefed<dom::Promise> GetXulLibraryPathAsync(
const dom::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 OS temporary directory.
*/
Temp,
/**
* 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<dom::Promise> GetDirectoryAsync(
const dom::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(dom::Promise* aPromise,
const Directory aRequestedDir);
template <typename T>
using DirectoryArray =
EnumeratedArray<Directory, T, size_t(Directory::Count)>;
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_OS_TEMP_DIR,
NS_XPCOM_LIBRARY_FILE,
};
};
} // namespace mozilla
#endif
|