summaryrefslogtreecommitdiffstats
path: root/dom/system/PathUtils.h
blob: ff01ddfc1ed9b75ac28fda7c75c294b3c2477aad (plain)
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
/* -*- 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 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> 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 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<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_OS_TEMP_DIR,
      NS_XPCOM_LIBRARY_FILE,
  };
};

}  // namespace dom
}  // namespace mozilla

#endif