1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef mozilla_dom_IOUtils__
8 #define mozilla_dom_IOUtils__
10 #include "js/Utility.h"
11 #include "mozilla/AlreadyAddRefed.h"
12 #include "mozilla/Attributes.h"
13 #include "mozilla/Buffer.h"
14 #include "mozilla/DataMutex.h"
15 #include "mozilla/MozPromise.h"
16 #include "mozilla/Result.h"
17 #include "mozilla/StaticPtr.h"
18 #include "mozilla/dom/BindingDeclarations.h"
19 #include "mozilla/dom/IOUtilsBinding.h"
20 #include "mozilla/dom/TypedArray.h"
21 #include "nsIAsyncShutdown.h"
23 #include "nsISerialEventTarget.h"
24 #include "nsPrintfCString.h"
25 #include "nsProxyRelease.h"
27 #include "nsStringFwd.h"
31 class nsFileRandomAccessStream
;
36 * Utility class to be used with |UniquePtr| to automatically close NSPR file
37 * descriptors when they go out of scope.
41 * UniquePtr<PRFileDesc, PR_CloseDelete> fd = PR_Open(path, flags, mode);
43 class PR_CloseDelete
{
45 constexpr PR_CloseDelete() = default;
46 PR_CloseDelete(const PR_CloseDelete
& aOther
) = default;
47 PR_CloseDelete(PR_CloseDelete
&& aOther
) = default;
48 PR_CloseDelete
& operator=(const PR_CloseDelete
& aOther
) = default;
49 PR_CloseDelete
& operator=(PR_CloseDelete
&& aOther
) = default;
51 void operator()(PRFileDesc
* aPtr
) const { PR_Close(aPtr
); }
57 * Implementation for the Web IDL interface at dom/chrome-webidl/IOUtils.webidl.
58 * Methods of this class must only be called from the parent process.
64 enum class ShutdownPhase
: uint8_t {
72 using PhaseArray
= EnumeratedArray
<IOUtils::ShutdownPhase
, T
,
73 size_t(IOUtils::ShutdownPhase::Count
)>;
75 static already_AddRefed
<Promise
> Read(GlobalObject
& aGlobal
,
76 const nsAString
& aPath
,
77 const ReadOptions
& aOptions
,
80 static already_AddRefed
<Promise
> ReadUTF8(GlobalObject
& aGlobal
,
81 const nsAString
& aPath
,
82 const ReadUTF8Options
& aOptions
,
85 static already_AddRefed
<Promise
> ReadJSON(GlobalObject
& aGlobal
,
86 const nsAString
& aPath
,
87 const ReadUTF8Options
& aOptions
,
90 static already_AddRefed
<Promise
> Write(GlobalObject
& aGlobal
,
91 const nsAString
& aPath
,
92 const Uint8Array
& aData
,
93 const WriteOptions
& aOptions
,
96 static already_AddRefed
<Promise
> WriteUTF8(GlobalObject
& aGlobal
,
97 const nsAString
& aPath
,
98 const nsACString
& aString
,
99 const WriteOptions
& aOptions
,
100 ErrorResult
& aError
);
102 static already_AddRefed
<Promise
> WriteJSON(GlobalObject
& aGlobal
,
103 const nsAString
& aPath
,
104 JS::Handle
<JS::Value
> aValue
,
105 const WriteOptions
& aOptions
,
106 ErrorResult
& aError
);
108 static already_AddRefed
<Promise
> Move(GlobalObject
& aGlobal
,
109 const nsAString
& aSourcePath
,
110 const nsAString
& aDestPath
,
111 const MoveOptions
& aOptions
,
112 ErrorResult
& aError
);
114 static already_AddRefed
<Promise
> Remove(GlobalObject
& aGlobal
,
115 const nsAString
& aPath
,
116 const RemoveOptions
& aOptions
,
117 ErrorResult
& aError
);
119 static already_AddRefed
<Promise
> MakeDirectory(
120 GlobalObject
& aGlobal
, const nsAString
& aPath
,
121 const MakeDirectoryOptions
& aOptions
, ErrorResult
& aError
);
123 static already_AddRefed
<Promise
> Stat(GlobalObject
& aGlobal
,
124 const nsAString
& aPath
,
125 ErrorResult
& aError
);
127 static already_AddRefed
<Promise
> Copy(GlobalObject
& aGlobal
,
128 const nsAString
& aSourcePath
,
129 const nsAString
& aDestPath
,
130 const CopyOptions
& aOptions
,
131 ErrorResult
& aError
);
133 static already_AddRefed
<Promise
> SetAccessTime(
134 GlobalObject
& aGlobal
, const nsAString
& aPath
,
135 const Optional
<int64_t>& aAccess
, ErrorResult
& aError
);
137 static already_AddRefed
<Promise
> SetModificationTime(
138 GlobalObject
& aGlobal
, const nsAString
& aPath
,
139 const Optional
<int64_t>& aModification
, ErrorResult
& aError
);
142 using SetTimeFn
= decltype(&nsIFile::SetLastAccessedTime
);
145 std::is_same_v
<SetTimeFn
, decltype(&nsIFile::SetLastModifiedTime
)>);
147 static already_AddRefed
<Promise
> SetTime(GlobalObject
& aGlobal
,
148 const nsAString
& aPath
,
149 const Optional
<int64_t>& aNewTime
,
150 SetTimeFn aSetTimeFn
,
151 const char* const aTimeKind
,
152 ErrorResult
& aError
);
155 static already_AddRefed
<Promise
> GetChildren(
156 GlobalObject
& aGlobal
, const nsAString
& aPath
,
157 const GetChildrenOptions
& aOptions
, ErrorResult
& aError
);
159 static already_AddRefed
<Promise
> SetPermissions(GlobalObject
& aGlobal
,
160 const nsAString
& aPath
,
161 uint32_t aPermissions
,
162 const bool aHonorUmask
,
163 ErrorResult
& aError
);
165 static already_AddRefed
<Promise
> Exists(GlobalObject
& aGlobal
,
166 const nsAString
& aPath
,
167 ErrorResult
& aError
);
169 static already_AddRefed
<Promise
> CreateUniqueFile(GlobalObject
& aGlobal
,
170 const nsAString
& aParent
,
171 const nsAString
& aPrefix
,
172 const uint32_t aPermissions
,
173 ErrorResult
& aError
);
174 static already_AddRefed
<Promise
> CreateUniqueDirectory(
175 GlobalObject
& aGlobal
, const nsAString
& aParent
, const nsAString
& aPrefix
,
176 const uint32_t aPermissions
, ErrorResult
& aError
);
180 * A helper method for CreateUniqueFile and CreateUniqueDirectory.
182 static already_AddRefed
<Promise
> CreateUnique(GlobalObject
& aGlobal
,
183 const nsAString
& aParent
,
184 const nsAString
& aPrefix
,
185 const uint32_t aFileType
,
186 const uint32_t aPermissions
,
187 ErrorResult
& aError
);
190 static already_AddRefed
<Promise
> ComputeHexDigest(
191 GlobalObject
& aGlobal
, const nsAString
& aPath
,
192 const HashAlgorithm aAlgorithm
, ErrorResult
& aError
);
195 static already_AddRefed
<Promise
> GetWindowsAttributes(GlobalObject
& aGlobal
,
196 const nsAString
& aPath
,
197 ErrorResult
& aError
);
199 static already_AddRefed
<Promise
> SetWindowsAttributes(
200 GlobalObject
& aGlobal
, const nsAString
& aPath
,
201 const mozilla::dom::WindowsFileAttributes
& aAttrs
, ErrorResult
& aError
);
202 #elif defined(XP_MACOSX)
203 static already_AddRefed
<Promise
> HasMacXAttr(GlobalObject
& aGlobal
,
204 const nsAString
& aPath
,
205 const nsACString
& aAttr
,
206 ErrorResult
& aError
);
207 static already_AddRefed
<Promise
> GetMacXAttr(GlobalObject
& aGlobal
,
208 const nsAString
& aPath
,
209 const nsACString
& aAttr
,
210 ErrorResult
& aError
);
211 static already_AddRefed
<Promise
> SetMacXAttr(GlobalObject
& aGlobal
,
212 const nsAString
& aPath
,
213 const nsACString
& aAttr
,
214 const Uint8Array
& aValue
,
215 ErrorResult
& aError
);
216 static already_AddRefed
<Promise
> DelMacXAttr(GlobalObject
& aGlobal
,
217 const nsAString
& aPath
,
218 const nsACString
& aAttr
,
219 ErrorResult
& aError
);
223 using UnixString
= OwningUTF8StringOrUint8Array
;
224 static uint32_t LaunchProcess(GlobalObject
& aGlobal
,
225 const Sequence
<UnixString
>& aArgv
,
226 const LaunchOptions
& aOptions
,
230 static already_AddRefed
<Promise
> GetFile(
231 GlobalObject
& aGlobal
, const Sequence
<nsString
>& aComponents
,
232 ErrorResult
& aError
);
234 static already_AddRefed
<Promise
> GetDirectory(
235 GlobalObject
& aGlobal
, const Sequence
<nsString
>& aComponents
,
236 ErrorResult
& aError
);
238 static void GetProfileBeforeChange(GlobalObject
& aGlobal
,
239 JS::MutableHandle
<JS::Value
>,
242 static void GetSendTelemetry(GlobalObject
& aGlobal
,
243 JS::MutableHandle
<JS::Value
>, ErrorResult
& aRv
);
245 static RefPtr
<SyncReadFile
> OpenFileForSyncReading(GlobalObject
& aGlobal
,
246 const nsAString
& aPath
,
252 * The kind of buffer to allocate.
254 * This controls what kind of JS object (a JSString or a Uint8Array) is
255 * returned by |ToJSValue()|.
257 enum class BufferKind
{
263 ~IOUtils() = default;
265 template <typename T
>
266 using IOPromise
= MozPromise
<T
, IOError
, true>;
268 friend class IOUtilsShutdownBlocker
;
269 struct InternalFileInfo
;
270 struct InternalWriteOpts
;
275 template <typename Fn
>
276 static already_AddRefed
<Promise
> WithPromiseAndState(GlobalObject
& aGlobal
,
281 * Dispatch a task on the event queue and resolve or reject the associated
282 * promise based on the result.
284 * NB: If the calling thread is a worker, this function takes care of keepting
285 * it alive until the |IOPromise| can complete.
287 * @param aPromise The promise corresponding to the task running on the event
289 * @param aFunc The task to run.
291 template <typename OkT
, typename Fn
>
292 static void DispatchAndResolve(EventQueue
* aQueue
, Promise
* aPromise
,
296 * Creates a new JS Promise.
298 * @return The new promise, or |nullptr| on failure.
300 static already_AddRefed
<Promise
> CreateJSPromise(GlobalObject
& aGlobal
,
301 ErrorResult
& aError
);
303 // Allow conversion of |InternalFileInfo| with |ToJSValue|.
304 friend bool ToJSValue(JSContext
* aCx
,
305 const InternalFileInfo
& aInternalFileInfo
,
306 JS::MutableHandle
<JS::Value
> aValue
);
309 * Attempts to read the entire file at |aPath| into a buffer.
311 * @param aFile The location of the file.
312 * @param aOffset The offset to start reading from.
313 * @param aMaxBytes If |Some|, then only read up this this number of bytes,
314 * otherwise attempt to read the whole file.
315 * @param aDecompress If true, decompress the bytes read from disk before
316 * returning the result to the caller.
317 * @param aBufferKind The kind of buffer to allocate.
319 * @return A buffer containing the entire (decompressed) file contents, or an
322 static Result
<JsBuffer
, IOError
> ReadSync(nsIFile
* aFile
,
323 const uint64_t aOffset
,
324 const Maybe
<uint32_t> aMaxBytes
,
325 const bool aDecompress
,
326 BufferKind aBufferKind
);
329 * Attempts to read the entire file at |aPath| as a UTF-8 string.
331 * @param aFile The location of the file.
332 * @param aDecompress If true, decompress the bytes read from disk before
333 * returning the result to the caller.
335 * @return The (decompressed) contents of the file re-encoded as a UTF-16
338 static Result
<JsBuffer
, IOError
> ReadUTF8Sync(nsIFile
* aFile
,
339 const bool aDecompress
);
342 * Attempt to write the entirety of |aByteArray| to the file at |aPath|.
343 * This may occur by writing to an intermediate destination and performing a
344 * move, depending on |aOptions|.
346 * @param aFile The location of the file.
347 * @param aByteArray The data to write to the file.
348 * @param aOptions Options to modify the way the write is completed.
350 * @return The number of bytes written to the file, or an error if the write
351 * failed or was incomplete.
353 static Result
<uint32_t, IOError
> WriteSync(
354 nsIFile
* aFile
, const Span
<const uint8_t>& aByteArray
,
355 const InternalWriteOpts
& aOptions
);
358 * Attempts to move the file located at |aSourceFile| to |aDestFile|.
360 * @param aSourceFile The location of the file to move.
361 * @param aDestFile The destination for the file.
362 * @param aNoOverWrite If true, abort with an error if a file already exists
363 * at |aDestFile|. Otherwise, the file will be overwritten by the move.
365 * @return Ok if the file was moved successfully, or an error.
367 static Result
<Ok
, IOError
> MoveSync(nsIFile
* aSourceFile
, nsIFile
* aDestFile
,
371 * Attempts to copy the file at |aSourceFile| to |aDestFile|.
373 * @param aSourceFile The location of the file to copy.
374 * @param aDestFile The destination that the file will be copied to.
376 * @return Ok if the operation was successful, or an error.
378 static Result
<Ok
, IOError
> CopySync(nsIFile
* aSourceFile
, nsIFile
* aDestFile
,
379 bool aNoOverWrite
, bool aRecursive
);
382 * Provides the implementation for |CopySync| and |MoveSync|.
384 * @param aMethod A pointer to one of |nsIFile::MoveTo| or |CopyTo|
386 * @param aMethodName The name of the method to the performed. Either "move"
388 * @param aSource The source file to be copied or moved.
389 * @param aDest The destination file.
390 * @param aNoOverwrite If true, allow overwriting |aDest| during the copy or
391 * move. Otherwise, abort with an error if the file would
394 * @return Ok if the operation was successful, or an error.
396 template <typename CopyOrMoveFn
>
397 static Result
<Ok
, IOError
> CopyOrMoveSync(CopyOrMoveFn aMethod
,
398 const char* aMethodName
,
399 nsIFile
* aSource
, nsIFile
* aDest
,
403 * Attempts to remove the file located at |aFile|.
405 * @param aFile The location of the file.
406 * @param aIgnoreAbsent If true, suppress errors due to an absent target
408 * @param aRecursive If true, attempt to recursively remove descendant
409 * files. This option is safe to use even if the target
410 * is not a directory.
411 * @param aRetryReadonly Retry a delete that failed with a NotAllowedError by
412 * first removing the readonly attribute. Only has an
415 * @return Ok if the file was removed successfully, or an error.
417 static Result
<Ok
, IOError
> RemoveSync(nsIFile
* aFile
, bool aIgnoreAbsent
,
418 bool aRecursive
, bool aRetryReadonly
);
421 * Attempts to create a new directory at |aFile|.
423 * @param aFile The location of the directory to create.
424 * @param aCreateAncestors If true, create missing ancestor directories as
425 * needed. Otherwise, report an error if the target
426 * has non-existing ancestor directories.
427 * @param aIgnoreExisting If true, suppress errors that occur if the target
428 * directory already exists. Otherwise, propagate the
429 * error if it occurs.
430 * @param aMode Optional file mode. Defaults to 0777 to allow the
431 * system umask to compute the best mode for the new
434 * @return Ok if the directory was created successfully, or an error.
436 static Result
<Ok
, IOError
> MakeDirectorySync(nsIFile
* aFile
,
437 bool aCreateAncestors
,
438 bool aIgnoreExisting
,
439 int32_t aMode
= 0777);
442 * Attempts to stat a file at |aFile|.
444 * @param aFile The location of the file.
446 * @return An |InternalFileInfo| struct if successful, or an error.
448 static Result
<IOUtils::InternalFileInfo
, IOError
> StatSync(nsIFile
* aFile
);
451 * Attempts to update the last access or modification time of the file at
454 * @param aFile The location of the file.
455 * @param SetTimeFn A member function pointer to either
456 * nsIFile::SetLastAccessedTime or
457 * nsIFile::SetLastModifiedTime.
458 * @param aNewTime Some value in milliseconds since Epoch.
460 * @return Timestamp of the file if the operation was successful, or an error.
462 static Result
<int64_t, IOError
> SetTimeSync(nsIFile
* aFile
,
463 SetTimeFn aSetTimeFn
,
467 * Returns the immediate children of the directory at |aFile|, if any.
469 * @param aFile The location of the directory.
471 * @return An array of absolute paths identifying the children of |aFile|.
472 * If there are no children, an empty array. Otherwise, an error.
474 static Result
<nsTArray
<nsString
>, IOError
> GetChildrenSync(
475 nsIFile
* aFile
, bool aIgnoreAbsent
);
478 * Set the permissions of the given file.
480 * Windows does not make a distinction between user, group, and other
481 * permissions like UNICES do. If a permission flag is set for any of user,
482 * group, or other has a permission, then all users will have that
485 * @param aFile The location of the file.
486 * @param aPermissions The permissions to set, as a UNIX file mode.
488 * @return |Ok| if the permissions were successfully set, or an error.
490 static Result
<Ok
, IOError
> SetPermissionsSync(nsIFile
* aFile
,
491 const uint32_t aPermissions
);
494 * Return whether or not the file exists.
496 * @param aFile The location of the file.
498 * @return Whether or not the file exists.
500 static Result
<bool, IOError
> ExistsSync(nsIFile
* aFile
);
503 * Create a file or directory with a unique path.
505 * @param aFile The location of the file or directory (including prefix)
506 * @param aFileType One of |nsIFile::NORMAL_FILE_TYPE| or
507 * |nsIFile::DIRECTORY_TYPE|.
508 * @param aperms The permissions to create the file or directory with.
510 * @return A unique path.
512 static Result
<nsString
, IOError
> CreateUniqueSync(
513 nsIFile
* aFile
, const uint32_t aFileType
, const uint32_t aPermissions
);
516 * Compute the hash of a file.
518 * @param aFile The file to hash.
519 * @param aAlgorithm The hashing algorithm to use.
521 * @return The hash of the file, as a hex digest.
523 static Result
<nsCString
, IOError
> ComputeHexDigestSync(
524 nsIFile
* aFile
, const HashAlgorithm aAlgorithm
);
528 * Return the Windows-specific attributes of the file.
530 * @param aFile The location of the file.
532 * @return The Windows-specific attributes of the file.
534 static Result
<uint32_t, IOError
> GetWindowsAttributesSync(nsIFile
* aFile
);
537 * Set the Windows-specific attributes of the file.
539 * @param aFile The location of the file.
540 * @param aAttrs The attributes to set on the file.
542 * @return |Ok| if the attributes were successfully set, or an error.
544 static Result
<Ok
, IOError
> SetWindowsAttributesSync(
545 nsIFile
* aFile
, const uint32_t aSetAttrs
, const uint32_t aClearAttrs
);
546 #elif defined(XP_MACOSX)
547 static Result
<bool, IOError
> HasMacXAttrSync(nsIFile
* aFile
,
548 const nsCString
& aAttr
);
549 static Result
<nsTArray
<uint8_t>, IOError
> GetMacXAttrSync(
550 nsIFile
* aFile
, const nsCString
& aAttr
);
551 static Result
<Ok
, IOError
> SetMacXAttrSync(nsIFile
* aFile
,
552 const nsCString
& aAttr
,
553 const nsTArray
<uint8_t>& aValue
);
554 static Result
<Ok
, IOError
> DelMacXAttrSync(nsIFile
* aFile
,
555 const nsCString
& aAttr
);
558 static void GetShutdownClient(GlobalObject
& aGlobal
,
559 JS::MutableHandle
<JS::Value
> aClient
,
560 ErrorResult
& aRv
, const ShutdownPhase aPhase
);
562 enum class EventQueueStatus
{
568 enum class ShutdownBlockerStatus
{
575 * Internal IOUtils state.
579 StaticAutoPtr
<EventQueue
> mEventQueue
;
580 EventQueueStatus mQueueStatus
= EventQueueStatus::Uninitialized
;
581 ShutdownBlockerStatus mBlockerStatus
= ShutdownBlockerStatus::Uninitialized
;
584 * Set up shutdown hooks to free our internals at shutdown.
586 * NB: Must be called on main thread.
588 void SetShutdownHooks();
591 using StateMutex
= StaticDataMutex
<State
>;
594 * Lock the state mutex and return a handle. If shutdown has not yet
595 * finished, the internals will be constructed if necessary.
597 * @returns A handle to the internal state, which can be used to retrieve the
599 * If |Some| is returned, |mEventQueue| is guaranteed to be
600 * initialized. If shutdown has finished, |Nothing| is returned.
602 static Maybe
<StateMutex::AutoLock
> GetState();
604 static StateMutex sState
;
608 * The IOUtils event queue.
610 class IOUtils::EventQueue final
{
611 friend void IOUtils::State::SetShutdownHooks();
616 EventQueue(const EventQueue
&) = delete;
617 EventQueue(EventQueue
&&) = delete;
618 EventQueue
& operator=(const EventQueue
&) = delete;
619 EventQueue
& operator=(EventQueue
&&) = delete;
622 * Dispatch a task on the event queue.
624 * NB: If using this directly from |IOUtils| instead of
625 * |IOUtils::DispatchAndResolve| *and* the calling thread is a worker, you
626 * *must* take care to keep the worker thread alive until the |IOPromise|
627 * resolves or rejects. See the implementation of
628 * |IOUtils::DispatchAndResolve| or |IOUtils::GetWindowsAttributes| for an
631 * @param aFunc The task to dispatch on the event queue.
633 * @return A promise that resolves to the task's return value or rejects with
636 template <typename OkT
, typename Fn
>
637 RefPtr
<IOPromise
<OkT
>> Dispatch(Fn aFunc
);
639 Result
<already_AddRefed
<nsIAsyncShutdownBarrier
>, nsresult
>
640 GetShutdownBarrier(const ShutdownPhase aPhase
);
641 Result
<already_AddRefed
<nsIAsyncShutdownClient
>, nsresult
> GetShutdownClient(
642 const ShutdownPhase aPhase
);
645 nsresult
SetShutdownHooks();
647 nsCOMPtr
<nsISerialEventTarget
> mBackgroundEventTarget
;
648 IOUtils::PhaseArray
<nsCOMPtr
<nsIAsyncShutdownBarrier
>> mBarriers
;
652 * An error class used with the |Result| type returned by most private |IOUtils|
655 class IOUtils::IOError
{
657 IOError(nsresult aCode
, const nsCString
& aMsg
)
658 : mCode(aCode
), mMessage(aMsg
) {}
660 IOError(nsresult aCode
, const char* const aFmt
, ...) MOZ_FORMAT_PRINTF(3, 4)
664 mMessage
.AppendVprintf(aFmt
, ap
);
668 static IOError
WithCause(const IOError
& aCause
, const nsCString
& aMsg
) {
669 IOError
e(aCause
.mCode
, aMsg
);
670 e
.mMessage
.AppendPrintf(": %s", aCause
.mMessage
.get());
674 static IOError
WithCause(const IOError
& aCause
, const char* const aFmt
, ...)
675 MOZ_FORMAT_PRINTF(2, 3) {
679 IOError
e(aCause
.mCode
, EmptyCString());
680 e
.mMessage
.AppendVprintf(aFmt
, ap
);
681 e
.mMessage
.AppendPrintf(": %s", aCause
.mMessage
.get());
688 * Returns the |nsresult| associated with this error.
690 nsresult
Code() const { return mCode
; }
693 * Returns the message associated with this error.
695 const nsCString
& Message() const { return mMessage
; }
703 * This is an easier to work with representation of a |mozilla::dom::FileInfo|
704 * for private use in the IOUtils implementation.
706 * Because web IDL dictionaries are not easily copy/moveable, this class is
707 * used instead, until converted to the proper |mozilla::dom::FileInfo| before
708 * returning any results to JavaScript.
710 struct IOUtils::InternalFileInfo
{
712 FileType mType
= FileType::Other
;
714 Maybe
<PRTime
> mCreationTime
; // In ms since epoch.
715 PRTime mLastAccessed
= 0; // In ms since epoch.
716 PRTime mLastModified
= 0; // In ms since epoch.
717 uint32_t mPermissions
= 0;
721 * This is an easier to work with representation of a
722 * |mozilla::dom::WriteOptions| for private use in the |IOUtils|
725 * Because web IDL dictionaries are not easily copy/moveable, this class is
728 struct IOUtils::InternalWriteOpts
{
729 RefPtr
<nsIFile
> mBackupFile
;
730 RefPtr
<nsIFile
> mTmpFile
;
733 bool mCompress
= false;
735 static Result
<InternalWriteOpts
, IOUtils::IOError
> FromBinding(
736 const WriteOptions
& aOptions
);
740 * Re-implements the file compression and decompression utilities found
741 * in toolkit/components/lz4/lz4.js
743 * This implementation uses the non-standard data layout:
745 * - MAGIC_NUMBER (8 bytes)
746 * - content size (uint32_t, little endian)
747 * - content, as obtained from mozilla::Compression::LZ4::compress
749 * See bug 1209390 for more info.
751 class IOUtils::MozLZ4
{
753 static constexpr std::array
<uint8_t, 8> MAGIC_NUMBER
{
754 {'m', 'o', 'z', 'L', 'z', '4', '0', '\0'}};
756 static const uint32_t HEADER_SIZE
= 8 + sizeof(uint32_t);
759 * Compresses |aUncompressed| byte array, and returns a byte array with the
760 * correct format whose contents may be written to disk.
762 static Result
<nsTArray
<uint8_t>, IOError
> Compress(
763 Span
<const uint8_t> aUncompressed
);
766 * Checks |aFileContents| for the correct file header, and returns the
767 * decompressed content.
769 static Result
<IOUtils::JsBuffer
, IOError
> Decompress(
770 Span
<const uint8_t> aFileContents
, IOUtils::BufferKind
);
773 class IOUtilsShutdownBlocker
: public nsIAsyncShutdownBlocker
,
774 public nsIAsyncShutdownCompletionCallback
{
776 NS_DECL_THREADSAFE_ISUPPORTS
777 NS_DECL_NSIASYNCSHUTDOWNBLOCKER
778 NS_DECL_NSIASYNCSHUTDOWNCOMPLETIONCALLBACK
780 explicit IOUtilsShutdownBlocker(const IOUtils::ShutdownPhase aPhase
)
784 virtual ~IOUtilsShutdownBlocker() = default;
787 * Called on the main thread after the event queue has been flushed.
791 static constexpr IOUtils::PhaseArray
<const char16_t
*> PHASE_NAMES
{
792 u
"profile-before-change",
793 u
"profile-before-change-telemetry",
794 u
"xpcom-will-shutdown",
797 // The last shutdown phase before we should shut down the event loop.
798 static constexpr auto LAST_IO_PHASE
= IOUtils::ShutdownPhase::SendTelemetry
;
800 IOUtils::ShutdownPhase mPhase
;
801 nsCOMPtr
<nsIAsyncShutdownClient
> mParentClient
;
805 * A buffer that is allocated inside one of JS heaps so that it can be converted
806 * to a JSString or Uint8Array object with at most one copy in the worst case.
808 class IOUtils::JsBuffer final
{
811 * Create a new buffer of the given kind with the requested capacity.
813 * @param aBufferKind The kind of buffer to create (either a string or an
815 * @param aCapacity The capacity of the buffer.
817 * @return Either a successfully created buffer or an error if it could not be
820 static Result
<JsBuffer
, IOUtils::IOError
> Create(
821 IOUtils::BufferKind aBufferKind
, size_t aCapacity
);
824 * Create a new, empty buffer.
826 * This operation cannot fail.
828 * @param aBufferKind The kind of buffer to create (either a string or an
831 * @return An empty JsBuffer.
833 static JsBuffer
CreateEmpty(IOUtils::BufferKind aBufferKind
);
835 JsBuffer(const JsBuffer
&) = delete;
836 JsBuffer(JsBuffer
&& aOther
) noexcept
;
837 JsBuffer
& operator=(const JsBuffer
&) = delete;
838 JsBuffer
& operator=(JsBuffer
&& aOther
) noexcept
;
840 size_t Length() { return mLength
; }
841 char* Elements() { return mBuffer
.get(); }
842 void SetLength(size_t aNewLength
) {
843 MOZ_RELEASE_ASSERT(aNewLength
<= mCapacity
);
844 mLength
= aNewLength
;
848 * Return a span for writing to the buffer.
850 * |SetLength| should be called after the buffer has been written to.
852 * @returns A span for writing to. The size of the span is the entire
853 * allocated capacity.
855 Span
<char> BeginWriting() {
856 MOZ_RELEASE_ASSERT(mBuffer
.get());
857 return Span(mBuffer
.get(), mCapacity
);
861 * Return a span for reading from.
863 * @returns A span for reading form. The size of the span is the set length
866 Span
<const char> BeginReading() const {
867 MOZ_RELEASE_ASSERT(mBuffer
.get() || mLength
== 0);
868 return Span(mBuffer
.get(), mLength
);
872 * Consume the JsBuffer and convert it into a JSString.
874 * NOTE: This method asserts the buffer was allocated as a string buffer.
876 * @param aBuffer The buffer to convert to a string. After this call, the
877 * buffer will be invaldated and |IntoString| cannot be called
880 * @returns A JSString with the contents of |aBuffer|.
882 static JSString
* IntoString(JSContext
* aCx
, JsBuffer aBuffer
);
885 * Consume the JsBuffer and convert it into a Uint8Array.
887 * NOTE: This method asserts the buffer was allocated as an array buffer.
889 * @param aBuffer The buffer to convert to an array. After this call, the
890 * buffer will be invalidated and |IntoUint8Array| cannot be
893 * @returns A JSBuffer
895 static JSObject
* IntoUint8Array(JSContext
* aCx
, JsBuffer aBuffer
);
897 friend bool ToJSValue(JSContext
* aCx
, JsBuffer
&& aBuffer
,
898 JS::MutableHandle
<JS::Value
> aValue
);
901 IOUtils::BufferKind mBufferKind
;
904 JS::UniqueChars mBuffer
;
906 JsBuffer(BufferKind aBufferKind
, size_t aCapacity
);
909 class SyncReadFile
: public nsISupports
, public nsWrapperCache
{
911 SyncReadFile(nsISupports
* aParent
, RefPtr
<nsFileRandomAccessStream
>&& aStream
,
914 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
915 NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(SyncReadFile
)
917 nsISupports
* GetParentObject() const { return mParent
; }
919 virtual JSObject
* WrapObject(JSContext
* aCx
,
920 JS::Handle
<JSObject
*> aGivenProto
) override
;
922 int64_t Size() const { return mSize
; }
923 void ReadBytesInto(const Uint8Array
&, const int64_t, ErrorResult
& aRv
);
927 virtual ~SyncReadFile();
929 nsCOMPtr
<nsISupports
> mParent
;
930 RefPtr
<nsFileRandomAccessStream
> mStream
;
935 } // namespace mozilla