2020-07-03 01:30:03 +03:00
|
|
|
/* -*- 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_IOUtils__
|
|
|
|
#define mozilla_dom_IOUtils__
|
|
|
|
|
Bug 1680151 - Reduce copies in IOUtils::Read{,UTF8} r=nika,tcampbell
Previously, in both Read and ReadUTF8, we were doing copies where we did not
need to. Read allocated an nsTArray and passed that to JS, which performed a
copy of its contents to create a Uint8Array. ReadUTF8, on the other hand, would
take that nsTArray, convert it into ns nsString (1 copy), and then pass it to
JS for it to recreate the string (2 copies).
Now, we allocate our string and array buffers up front in JS' memory pools
directly and use the JS API to create the strings and arrays ourselves (instead
of relying on Promise::MaybeResolve() to do a copying conversion). Read now
performs 0 copies in the best case (if the file is not compressed) and ReadUTF8
also does 0 copies in the best case (if the file is not compressed and the
string is ASCII). In the worst case, Read performs a single extra allocation
(to decompress the file) and ReadUTF8 performs 2 (to decompress the file and to
convert a UTF-8 string to either a Latin1 string or a UTF-16 string).
Differential Revision: https://phabricator.services.mozilla.com/D99004
2021-01-15 07:28:08 +03:00
|
|
|
#include "js/Utility.h"
|
2020-07-03 01:32:03 +03:00
|
|
|
#include "mozilla/AlreadyAddRefed.h"
|
2020-08-28 18:57:44 +03:00
|
|
|
#include "mozilla/Attributes.h"
|
2020-07-30 17:03:03 +03:00
|
|
|
#include "mozilla/Buffer.h"
|
2020-07-03 01:32:03 +03:00
|
|
|
#include "mozilla/DataMutex.h"
|
2020-11-10 19:04:28 +03:00
|
|
|
#include "mozilla/MozPromise.h"
|
|
|
|
#include "mozilla/Result.h"
|
2021-03-23 07:26:50 +03:00
|
|
|
#include "mozilla/StaticPtr.h"
|
2020-07-03 01:32:03 +03:00
|
|
|
#include "mozilla/dom/BindingDeclarations.h"
|
|
|
|
#include "mozilla/dom/IOUtilsBinding.h"
|
|
|
|
#include "mozilla/dom/TypedArray.h"
|
|
|
|
#include "nsIAsyncShutdown.h"
|
|
|
|
#include "nsISerialEventTarget.h"
|
2020-07-30 17:02:50 +03:00
|
|
|
#include "nsPrintfCString.h"
|
2021-03-23 07:26:50 +03:00
|
|
|
#include "nsProxyRelease.h"
|
2020-07-30 17:02:50 +03:00
|
|
|
#include "nsString.h"
|
2020-11-10 19:04:28 +03:00
|
|
|
#include "nsStringFwd.h"
|
|
|
|
#include "nsTArray.h"
|
2020-09-11 01:43:30 +03:00
|
|
|
#include "prio.h"
|
2020-07-03 01:30:03 +03:00
|
|
|
|
2021-11-04 22:47:29 +03:00
|
|
|
class nsFileStream;
|
|
|
|
|
2020-07-03 01:30:03 +03:00
|
|
|
namespace mozilla {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Utility class to be used with |UniquePtr| to automatically close NSPR file
|
|
|
|
* descriptors when they go out of scope.
|
|
|
|
*
|
|
|
|
* Example:
|
|
|
|
*
|
|
|
|
* UniquePtr<PRFileDesc, PR_CloseDelete> fd = PR_Open(path, flags, mode);
|
|
|
|
*/
|
|
|
|
class PR_CloseDelete {
|
|
|
|
public:
|
|
|
|
constexpr PR_CloseDelete() = default;
|
|
|
|
PR_CloseDelete(const PR_CloseDelete& aOther) = default;
|
2020-07-03 01:30:12 +03:00
|
|
|
PR_CloseDelete(PR_CloseDelete&& aOther) = default;
|
|
|
|
PR_CloseDelete& operator=(const PR_CloseDelete& aOther) = default;
|
|
|
|
PR_CloseDelete& operator=(PR_CloseDelete&& aOther) = default;
|
2020-07-03 01:30:03 +03:00
|
|
|
|
|
|
|
void operator()(PRFileDesc* aPtr) const { PR_Close(aPtr); }
|
|
|
|
};
|
|
|
|
|
2020-07-03 01:32:03 +03:00
|
|
|
namespace dom {
|
|
|
|
|
2020-08-26 18:37:35 +03:00
|
|
|
/**
|
|
|
|
* Implementation for the Web IDL interface at dom/chrome-webidl/IOUtils.webidl.
|
|
|
|
* Methods of this class must only be called from the parent process.
|
|
|
|
*/
|
2020-07-03 01:32:03 +03:00
|
|
|
class IOUtils final {
|
|
|
|
public:
|
2020-07-30 17:03:03 +03:00
|
|
|
class IOError;
|
|
|
|
|
2020-07-03 01:32:03 +03:00
|
|
|
static already_AddRefed<Promise> Read(GlobalObject& aGlobal,
|
|
|
|
const nsAString& aPath,
|
2020-08-28 18:57:44 +03:00
|
|
|
const ReadOptions& aOptions);
|
2020-07-03 01:32:03 +03:00
|
|
|
|
2020-08-26 18:37:25 +03:00
|
|
|
static already_AddRefed<Promise> ReadUTF8(GlobalObject& aGlobal,
|
2020-08-28 18:57:44 +03:00
|
|
|
const nsAString& aPath,
|
|
|
|
const ReadUTF8Options& aOptions);
|
2020-08-26 18:37:25 +03:00
|
|
|
|
2021-01-18 22:52:32 +03:00
|
|
|
static already_AddRefed<Promise> ReadJSON(GlobalObject& aGlobal,
|
|
|
|
const nsAString& aPath,
|
|
|
|
const ReadUTF8Options& aOptions);
|
|
|
|
|
2020-12-09 08:48:46 +03:00
|
|
|
static already_AddRefed<Promise> Write(GlobalObject& aGlobal,
|
|
|
|
const nsAString& aPath,
|
|
|
|
const Uint8Array& aData,
|
|
|
|
const WriteOptions& aOptions);
|
2020-07-03 01:32:03 +03:00
|
|
|
|
2020-12-09 08:48:46 +03:00
|
|
|
static already_AddRefed<Promise> WriteUTF8(GlobalObject& aGlobal,
|
|
|
|
const nsAString& aPath,
|
2021-01-15 07:28:00 +03:00
|
|
|
const nsACString& aString,
|
2020-12-09 08:48:46 +03:00
|
|
|
const WriteOptions& aOptions);
|
2020-08-26 18:37:25 +03:00
|
|
|
|
2021-01-18 22:52:34 +03:00
|
|
|
static already_AddRefed<Promise> WriteJSON(GlobalObject& aGlobal,
|
|
|
|
const nsAString& aPath,
|
|
|
|
JS::Handle<JS::Value> aValue,
|
|
|
|
const WriteOptions& aOptions);
|
|
|
|
|
Bug 1650227: Implement IOUtils move method r=barret,Gijs
This patch introduces a move method to the IOUtils interface, which allows
for renaming/moving files or directories on disk. Source and destination
files may be specified either by an absolute path, or a relative path from
the current working directory.
This method has well-defined behaviour similar to the POSIX mv command
(except that this may create missing directories as necessary).
The behaviour is briefly summarized below:
1. If the source is a file that exists:
a. If the destination is a file that does not exist, the source is
renamed (and re-parented as a child of the destination parent
directory). The destination parent directory will be created if
necessary.
b. If the destination is a file that does exist, the destination is
replaced with the source (unless the noOverwrite option is true).
2. If the source is a directory that exists:
a. If the destination is a directory, then the source directory is
re-parented such that it becomes a child of the destination.
b. If the destination does not exist, then the source is renamed,
creating additional directories if needed.
c. If the destination is a file, then an error occurs.
3. If the source does not exist, an error occurs.
Differential Revision: https://phabricator.services.mozilla.com/D82202
2020-07-15 19:03:52 +03:00
|
|
|
static already_AddRefed<Promise> Move(GlobalObject& aGlobal,
|
|
|
|
const nsAString& aSourcePath,
|
|
|
|
const nsAString& aDestPath,
|
|
|
|
const MoveOptions& aOptions);
|
|
|
|
|
2020-07-18 03:31:57 +03:00
|
|
|
static already_AddRefed<Promise> Remove(GlobalObject& aGlobal,
|
|
|
|
const nsAString& aPath,
|
|
|
|
const RemoveOptions& aOptions);
|
|
|
|
|
2020-07-21 18:13:35 +03:00
|
|
|
static already_AddRefed<Promise> MakeDirectory(
|
|
|
|
GlobalObject& aGlobal, const nsAString& aPath,
|
|
|
|
const MakeDirectoryOptions& aOptions);
|
|
|
|
|
2020-07-23 21:15:30 +03:00
|
|
|
static already_AddRefed<Promise> Stat(GlobalObject& aGlobal,
|
|
|
|
const nsAString& aPath);
|
|
|
|
|
2020-08-10 19:00:32 +03:00
|
|
|
static already_AddRefed<Promise> Copy(GlobalObject& aGlobal,
|
|
|
|
const nsAString& aSourcePath,
|
|
|
|
const nsAString& aDestPath,
|
|
|
|
const CopyOptions& aOptions);
|
|
|
|
|
2021-11-25 05:19:20 +03:00
|
|
|
static already_AddRefed<Promise> SetModificationTime(
|
2020-08-26 18:31:57 +03:00
|
|
|
GlobalObject& aGlobal, const nsAString& aPath,
|
|
|
|
const Optional<int64_t>& aModification);
|
|
|
|
|
2021-11-25 13:45:55 +03:00
|
|
|
static already_AddRefed<Promise> GetChildren(
|
|
|
|
GlobalObject& aGlobal, const nsAString& aPath,
|
|
|
|
const GetChildrenOptions& aOptions);
|
2020-08-28 18:49:58 +03:00
|
|
|
|
2020-12-03 08:37:45 +03:00
|
|
|
static already_AddRefed<Promise> SetPermissions(GlobalObject& aGlobal,
|
|
|
|
const nsAString& aPath,
|
2021-01-30 03:50:47 +03:00
|
|
|
uint32_t aPermissions,
|
|
|
|
const bool aHonorUmask);
|
2020-12-03 08:37:45 +03:00
|
|
|
|
2020-12-03 08:38:03 +03:00
|
|
|
static already_AddRefed<Promise> Exists(GlobalObject& aGlobal,
|
|
|
|
const nsAString& aPath);
|
|
|
|
|
2022-01-18 08:04:52 +03:00
|
|
|
static already_AddRefed<Promise> CreateUniqueFile(
|
|
|
|
GlobalObject& aGlobal, const nsAString& aParent, const nsAString& aPrefix,
|
|
|
|
const uint32_t aPermissions);
|
|
|
|
static already_AddRefed<Promise> CreateUniqueDirectory(
|
|
|
|
GlobalObject& aGlobal, const nsAString& aParent, const nsAString& aPrefix,
|
|
|
|
const uint32_t aPermissions);
|
|
|
|
|
|
|
|
private:
|
|
|
|
/**
|
|
|
|
* A helper method for CreateUniqueFile and CreateUniqueDirectory.
|
|
|
|
*/
|
|
|
|
static already_AddRefed<Promise> CreateUnique(GlobalObject& aGlobal,
|
|
|
|
const nsAString& aParent,
|
|
|
|
const nsAString& aPrefix,
|
|
|
|
const uint32_t aFileType,
|
|
|
|
const uint32_t aPermissions);
|
|
|
|
|
|
|
|
public:
|
2021-12-17 20:21:22 +03:00
|
|
|
#if defined(XP_WIN)
|
|
|
|
static already_AddRefed<Promise> GetWindowsAttributes(GlobalObject& aGlobal,
|
|
|
|
const nsAString& aPath);
|
|
|
|
|
|
|
|
static already_AddRefed<Promise> SetWindowsAttributes(
|
|
|
|
GlobalObject& aGlobal, const nsAString& aPath,
|
|
|
|
const mozilla::dom::WindowsFileAttributes& aAttrs);
|
2021-12-25 03:30:11 +03:00
|
|
|
#elif defined(XP_MACOSX)
|
|
|
|
static already_AddRefed<Promise> HasMacXAttr(GlobalObject& aGlobal,
|
|
|
|
const nsAString& aPath,
|
|
|
|
const nsACString& aAttr);
|
|
|
|
static already_AddRefed<Promise> GetMacXAttr(GlobalObject& aGlobal,
|
|
|
|
const nsAString& aPath,
|
|
|
|
const nsACString& aAttr);
|
|
|
|
static already_AddRefed<Promise> SetMacXAttr(GlobalObject& aGlobal,
|
|
|
|
const nsAString& aPath,
|
|
|
|
const nsACString& aAttr,
|
|
|
|
const Uint8Array& aValue);
|
|
|
|
static already_AddRefed<Promise> DelMacXAttr(GlobalObject& aGlobal,
|
|
|
|
const nsAString& aPath,
|
|
|
|
const nsACString& aAttr);
|
2021-12-17 20:21:22 +03:00
|
|
|
#endif
|
|
|
|
|
2021-03-23 07:26:50 +03:00
|
|
|
static void GetProfileBeforeChange(GlobalObject& aGlobal,
|
|
|
|
JS::MutableHandle<JS::Value>,
|
|
|
|
ErrorResult& aRv);
|
|
|
|
|
2021-11-04 22:47:29 +03:00
|
|
|
static RefPtr<SyncReadFile> OpenFileForSyncReading(GlobalObject& aGlobal,
|
|
|
|
const nsAString& aPath,
|
|
|
|
ErrorResult& aRv);
|
|
|
|
|
Bug 1680151 - Reduce copies in IOUtils::Read{,UTF8} r=nika,tcampbell
Previously, in both Read and ReadUTF8, we were doing copies where we did not
need to. Read allocated an nsTArray and passed that to JS, which performed a
copy of its contents to create a Uint8Array. ReadUTF8, on the other hand, would
take that nsTArray, convert it into ns nsString (1 copy), and then pass it to
JS for it to recreate the string (2 copies).
Now, we allocate our string and array buffers up front in JS' memory pools
directly and use the JS API to create the strings and arrays ourselves (instead
of relying on Promise::MaybeResolve() to do a copying conversion). Read now
performs 0 copies in the best case (if the file is not compressed) and ReadUTF8
also does 0 copies in the best case (if the file is not compressed and the
string is ASCII). In the worst case, Read performs a single extra allocation
(to decompress the file) and ReadUTF8 performs 2 (to decompress the file and to
convert a UTF-8 string to either a Latin1 string or a UTF-16 string).
Differential Revision: https://phabricator.services.mozilla.com/D99004
2021-01-15 07:28:08 +03:00
|
|
|
class JsBuffer;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The kind of buffer to allocate.
|
|
|
|
*
|
|
|
|
* This controls what kind of JS object (a JSString or a Uint8Array) is
|
|
|
|
* returned by |ToJSValue()|.
|
|
|
|
*/
|
|
|
|
enum class BufferKind {
|
|
|
|
String,
|
|
|
|
Uint8Array,
|
|
|
|
};
|
|
|
|
|
2020-07-03 01:32:03 +03:00
|
|
|
private:
|
|
|
|
~IOUtils() = default;
|
|
|
|
|
2020-12-09 08:48:44 +03:00
|
|
|
template <typename T>
|
|
|
|
using IOPromise = MozPromise<T, IOError, true>;
|
|
|
|
|
2020-07-03 01:32:03 +03:00
|
|
|
friend class IOUtilsShutdownBlocker;
|
2020-07-23 21:15:30 +03:00
|
|
|
struct InternalFileInfo;
|
2020-12-09 08:48:46 +03:00
|
|
|
struct InternalWriteOpts;
|
2020-08-28 18:57:44 +03:00
|
|
|
class MozLZ4;
|
2021-03-23 07:26:50 +03:00
|
|
|
class EventQueue;
|
|
|
|
class State;
|
2020-07-30 17:02:50 +03:00
|
|
|
|
2021-12-16 07:47:19 +03:00
|
|
|
template <typename Fn>
|
|
|
|
static already_AddRefed<Promise> WithPromiseAndState(GlobalObject& aGlobal,
|
|
|
|
Fn aFn);
|
|
|
|
|
2021-03-23 07:26:50 +03:00
|
|
|
/**
|
|
|
|
* Dispatch a task on the event queue and resolve or reject the associated
|
|
|
|
* promise based on the result.
|
|
|
|
*
|
2022-02-01 06:23:44 +03:00
|
|
|
* NB: If the calling thread is a worker, this function takes care of keepting
|
|
|
|
* it alive until the |IOPromise| can complete.
|
|
|
|
*
|
2021-03-23 07:26:50 +03:00
|
|
|
* @param aPromise The promise corresponding to the task running on the event
|
|
|
|
* queue.
|
|
|
|
* @param aFunc The task to run.
|
|
|
|
*/
|
2021-01-18 22:52:29 +03:00
|
|
|
template <typename OkT, typename Fn>
|
2021-03-23 07:26:50 +03:00
|
|
|
static void DispatchAndResolve(EventQueue* aQueue, Promise* aPromise,
|
|
|
|
Fn aFunc);
|
2020-07-30 17:03:03 +03:00
|
|
|
|
2020-07-30 17:02:43 +03:00
|
|
|
/**
|
|
|
|
* Creates a new JS Promise.
|
|
|
|
*
|
|
|
|
* @return The new promise, or |nullptr| on failure.
|
|
|
|
*/
|
2020-07-03 01:32:03 +03:00
|
|
|
static already_AddRefed<Promise> CreateJSPromise(GlobalObject& aGlobal);
|
|
|
|
|
2020-07-30 17:02:43 +03:00
|
|
|
// Allow conversion of |InternalFileInfo| with |ToJSValue|.
|
2021-03-10 11:19:25 +03:00
|
|
|
friend bool ToJSValue(JSContext* aCx,
|
|
|
|
const InternalFileInfo& aInternalFileInfo,
|
|
|
|
JS::MutableHandle<JS::Value> aValue);
|
2020-07-30 17:02:43 +03:00
|
|
|
|
2020-07-30 17:02:34 +03:00
|
|
|
/**
|
|
|
|
* Attempts to read the entire file at |aPath| into a buffer.
|
|
|
|
*
|
2020-11-10 19:04:28 +03:00
|
|
|
* @param aFile The location of the file.
|
2021-04-29 02:27:42 +03:00
|
|
|
* @param aOffset The offset to start reading from.
|
2020-08-28 18:57:44 +03:00
|
|
|
* @param aMaxBytes If |Some|, then only read up this this number of bytes,
|
|
|
|
* otherwise attempt to read the whole file.
|
|
|
|
* @param aDecompress If true, decompress the bytes read from disk before
|
|
|
|
* returning the result to the caller.
|
Bug 1680151 - Reduce copies in IOUtils::Read{,UTF8} r=nika,tcampbell
Previously, in both Read and ReadUTF8, we were doing copies where we did not
need to. Read allocated an nsTArray and passed that to JS, which performed a
copy of its contents to create a Uint8Array. ReadUTF8, on the other hand, would
take that nsTArray, convert it into ns nsString (1 copy), and then pass it to
JS for it to recreate the string (2 copies).
Now, we allocate our string and array buffers up front in JS' memory pools
directly and use the JS API to create the strings and arrays ourselves (instead
of relying on Promise::MaybeResolve() to do a copying conversion). Read now
performs 0 copies in the best case (if the file is not compressed) and ReadUTF8
also does 0 copies in the best case (if the file is not compressed and the
string is ASCII). In the worst case, Read performs a single extra allocation
(to decompress the file) and ReadUTF8 performs 2 (to decompress the file and to
convert a UTF-8 string to either a Latin1 string or a UTF-16 string).
Differential Revision: https://phabricator.services.mozilla.com/D99004
2021-01-15 07:28:08 +03:00
|
|
|
* @param aBufferKind The kind of buffer to allocate.
|
2020-07-30 17:02:38 +03:00
|
|
|
*
|
Bug 1680151 - Reduce copies in IOUtils::Read{,UTF8} r=nika,tcampbell
Previously, in both Read and ReadUTF8, we were doing copies where we did not
need to. Read allocated an nsTArray and passed that to JS, which performed a
copy of its contents to create a Uint8Array. ReadUTF8, on the other hand, would
take that nsTArray, convert it into ns nsString (1 copy), and then pass it to
JS for it to recreate the string (2 copies).
Now, we allocate our string and array buffers up front in JS' memory pools
directly and use the JS API to create the strings and arrays ourselves (instead
of relying on Promise::MaybeResolve() to do a copying conversion). Read now
performs 0 copies in the best case (if the file is not compressed) and ReadUTF8
also does 0 copies in the best case (if the file is not compressed and the
string is ASCII). In the worst case, Read performs a single extra allocation
(to decompress the file) and ReadUTF8 performs 2 (to decompress the file and to
convert a UTF-8 string to either a Latin1 string or a UTF-16 string).
Differential Revision: https://phabricator.services.mozilla.com/D99004
2021-01-15 07:28:08 +03:00
|
|
|
* @return A buffer containing the entire (decompressed) file contents, or an
|
2020-08-28 18:57:44 +03:00
|
|
|
* error.
|
2020-07-30 17:02:34 +03:00
|
|
|
*/
|
Bug 1680151 - Reduce copies in IOUtils::Read{,UTF8} r=nika,tcampbell
Previously, in both Read and ReadUTF8, we were doing copies where we did not
need to. Read allocated an nsTArray and passed that to JS, which performed a
copy of its contents to create a Uint8Array. ReadUTF8, on the other hand, would
take that nsTArray, convert it into ns nsString (1 copy), and then pass it to
JS for it to recreate the string (2 copies).
Now, we allocate our string and array buffers up front in JS' memory pools
directly and use the JS API to create the strings and arrays ourselves (instead
of relying on Promise::MaybeResolve() to do a copying conversion). Read now
performs 0 copies in the best case (if the file is not compressed) and ReadUTF8
also does 0 copies in the best case (if the file is not compressed and the
string is ASCII). In the worst case, Read performs a single extra allocation
(to decompress the file) and ReadUTF8 performs 2 (to decompress the file and to
convert a UTF-8 string to either a Latin1 string or a UTF-16 string).
Differential Revision: https://phabricator.services.mozilla.com/D99004
2021-01-15 07:28:08 +03:00
|
|
|
static Result<JsBuffer, IOError> ReadSync(nsIFile* aFile,
|
2021-12-03 20:21:52 +03:00
|
|
|
const uint64_t aOffset,
|
2021-04-29 02:27:42 +03:00
|
|
|
const Maybe<uint32_t> aMaxBytes,
|
Bug 1680151 - Reduce copies in IOUtils::Read{,UTF8} r=nika,tcampbell
Previously, in both Read and ReadUTF8, we were doing copies where we did not
need to. Read allocated an nsTArray and passed that to JS, which performed a
copy of its contents to create a Uint8Array. ReadUTF8, on the other hand, would
take that nsTArray, convert it into ns nsString (1 copy), and then pass it to
JS for it to recreate the string (2 copies).
Now, we allocate our string and array buffers up front in JS' memory pools
directly and use the JS API to create the strings and arrays ourselves (instead
of relying on Promise::MaybeResolve() to do a copying conversion). Read now
performs 0 copies in the best case (if the file is not compressed) and ReadUTF8
also does 0 copies in the best case (if the file is not compressed and the
string is ASCII). In the worst case, Read performs a single extra allocation
(to decompress the file) and ReadUTF8 performs 2 (to decompress the file and to
convert a UTF-8 string to either a Latin1 string or a UTF-16 string).
Differential Revision: https://phabricator.services.mozilla.com/D99004
2021-01-15 07:28:08 +03:00
|
|
|
const bool aDecompress,
|
|
|
|
BufferKind aBufferKind);
|
2020-07-03 01:32:03 +03:00
|
|
|
|
Bug 1680151 - Reduce copies in IOUtils::Read{,UTF8} r=nika,tcampbell
Previously, in both Read and ReadUTF8, we were doing copies where we did not
need to. Read allocated an nsTArray and passed that to JS, which performed a
copy of its contents to create a Uint8Array. ReadUTF8, on the other hand, would
take that nsTArray, convert it into ns nsString (1 copy), and then pass it to
JS for it to recreate the string (2 copies).
Now, we allocate our string and array buffers up front in JS' memory pools
directly and use the JS API to create the strings and arrays ourselves (instead
of relying on Promise::MaybeResolve() to do a copying conversion). Read now
performs 0 copies in the best case (if the file is not compressed) and ReadUTF8
also does 0 copies in the best case (if the file is not compressed and the
string is ASCII). In the worst case, Read performs a single extra allocation
(to decompress the file) and ReadUTF8 performs 2 (to decompress the file and to
convert a UTF-8 string to either a Latin1 string or a UTF-16 string).
Differential Revision: https://phabricator.services.mozilla.com/D99004
2021-01-15 07:28:08 +03:00
|
|
|
/*
|
2020-08-26 18:37:25 +03:00
|
|
|
* Attempts to read the entire file at |aPath| as a UTF-8 string.
|
|
|
|
*
|
2020-11-10 19:04:28 +03:00
|
|
|
* @param aFile The location of the file.
|
2020-08-28 18:57:44 +03:00
|
|
|
* @param aDecompress If true, decompress the bytes read from disk before
|
|
|
|
* returning the result to the caller.
|
2020-08-26 18:37:25 +03:00
|
|
|
*
|
2020-08-28 18:57:44 +03:00
|
|
|
* @return The (decompressed) contents of the file re-encoded as a UTF-16
|
|
|
|
* string.
|
2020-08-26 18:37:25 +03:00
|
|
|
*/
|
Bug 1680151 - Reduce copies in IOUtils::Read{,UTF8} r=nika,tcampbell
Previously, in both Read and ReadUTF8, we were doing copies where we did not
need to. Read allocated an nsTArray and passed that to JS, which performed a
copy of its contents to create a Uint8Array. ReadUTF8, on the other hand, would
take that nsTArray, convert it into ns nsString (1 copy), and then pass it to
JS for it to recreate the string (2 copies).
Now, we allocate our string and array buffers up front in JS' memory pools
directly and use the JS API to create the strings and arrays ourselves (instead
of relying on Promise::MaybeResolve() to do a copying conversion). Read now
performs 0 copies in the best case (if the file is not compressed) and ReadUTF8
also does 0 copies in the best case (if the file is not compressed and the
string is ASCII). In the worst case, Read performs a single extra allocation
(to decompress the file) and ReadUTF8 performs 2 (to decompress the file and to
convert a UTF-8 string to either a Latin1 string or a UTF-16 string).
Differential Revision: https://phabricator.services.mozilla.com/D99004
2021-01-15 07:28:08 +03:00
|
|
|
static Result<JsBuffer, IOError> ReadUTF8Sync(nsIFile* aFile,
|
2020-08-28 18:57:44 +03:00
|
|
|
const bool aDecompress);
|
2020-08-26 18:37:25 +03:00
|
|
|
|
2020-07-30 17:02:36 +03:00
|
|
|
/**
|
2020-11-10 19:04:49 +03:00
|
|
|
* Attempt to write the entirety of |aByteArray| to the file at |aPath|.
|
2020-07-30 17:02:36 +03:00
|
|
|
* This may occur by writing to an intermediate destination and performing a
|
|
|
|
* move, depending on |aOptions|.
|
|
|
|
*
|
2020-11-10 19:04:49 +03:00
|
|
|
* @param aFile The location of the file.
|
2020-07-30 17:02:36 +03:00
|
|
|
* @param aByteArray The data to write to the file.
|
|
|
|
* @param aOptions Options to modify the way the write is completed.
|
2020-07-30 17:02:38 +03:00
|
|
|
*
|
|
|
|
* @return The number of bytes written to the file, or an error if the write
|
|
|
|
* failed or was incomplete.
|
2020-07-30 17:02:36 +03:00
|
|
|
*/
|
2020-12-09 08:48:46 +03:00
|
|
|
static Result<uint32_t, IOError> WriteSync(
|
2020-12-03 07:10:30 +03:00
|
|
|
nsIFile* aFile, const Span<const uint8_t>& aByteArray,
|
2020-12-09 08:48:46 +03:00
|
|
|
const InternalWriteOpts& aOptions);
|
2020-12-09 08:34:21 +03:00
|
|
|
|
2020-07-30 17:02:38 +03:00
|
|
|
/**
|
2020-11-10 04:58:47 +03:00
|
|
|
* Attempts to move the file located at |aSourceFile| to |aDestFile|.
|
2020-07-30 17:02:38 +03:00
|
|
|
*
|
2020-11-10 04:58:47 +03:00
|
|
|
* @param aSourceFile The location of the file to move.
|
|
|
|
* @param aDestFile The destination for the file.
|
2021-04-30 01:38:02 +03:00
|
|
|
* @param aNoOverWrite If true, abort with an error if a file already exists
|
|
|
|
* at |aDestFile|. Otherwise, the file will be overwritten by the move.
|
2020-07-30 17:02:38 +03:00
|
|
|
*
|
|
|
|
* @return Ok if the file was moved successfully, or an error.
|
|
|
|
*/
|
2020-12-03 07:10:30 +03:00
|
|
|
static Result<Ok, IOError> MoveSync(nsIFile* aSourceFile, nsIFile* aDestFile,
|
2020-08-10 19:00:32 +03:00
|
|
|
bool aNoOverwrite);
|
|
|
|
|
|
|
|
/**
|
2020-11-10 04:58:47 +03:00
|
|
|
* Attempts to copy the file at |aSourceFile| to |aDestFile|.
|
2020-08-10 19:00:32 +03:00
|
|
|
*
|
2020-11-10 04:58:47 +03:00
|
|
|
* @param aSourceFile The location of the file to copy.
|
|
|
|
* @param aDestFile The destination that the file will be copied to.
|
2020-08-10 19:00:32 +03:00
|
|
|
*
|
|
|
|
* @return Ok if the operation was successful, or an error.
|
|
|
|
*/
|
2020-12-03 07:10:30 +03:00
|
|
|
static Result<Ok, IOError> CopySync(nsIFile* aSourceFile, nsIFile* aDestFile,
|
2020-08-10 19:00:32 +03:00
|
|
|
bool aNoOverWrite, bool aRecursive);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Provides the implementation for |CopySync| and |MoveSync|.
|
|
|
|
*
|
2020-11-10 04:58:47 +03:00
|
|
|
* @param aMethod A pointer to one of |nsIFile::MoveTo| or |CopyTo|
|
2020-08-10 19:00:32 +03:00
|
|
|
* instance methods.
|
|
|
|
* @param aMethodName The name of the method to the performed. Either "move"
|
|
|
|
* or "copy".
|
|
|
|
* @param aSource The source file to be copied or moved.
|
|
|
|
* @param aDest The destination file.
|
|
|
|
* @param aNoOverwrite If true, allow overwriting |aDest| during the copy or
|
|
|
|
* move. Otherwise, abort with an error if the file would
|
|
|
|
* be overwritten.
|
|
|
|
*
|
|
|
|
* @return Ok if the operation was successful, or an error.
|
|
|
|
*/
|
|
|
|
template <typename CopyOrMoveFn>
|
|
|
|
static Result<Ok, IOError> CopyOrMoveSync(CopyOrMoveFn aMethod,
|
|
|
|
const char* aMethodName,
|
2020-11-10 04:58:47 +03:00
|
|
|
nsIFile* aSource, nsIFile* aDest,
|
2020-08-10 19:00:32 +03:00
|
|
|
bool aNoOverwrite);
|
Bug 1650227: Implement IOUtils move method r=barret,Gijs
This patch introduces a move method to the IOUtils interface, which allows
for renaming/moving files or directories on disk. Source and destination
files may be specified either by an absolute path, or a relative path from
the current working directory.
This method has well-defined behaviour similar to the POSIX mv command
(except that this may create missing directories as necessary).
The behaviour is briefly summarized below:
1. If the source is a file that exists:
a. If the destination is a file that does not exist, the source is
renamed (and re-parented as a child of the destination parent
directory). The destination parent directory will be created if
necessary.
b. If the destination is a file that does exist, the destination is
replaced with the source (unless the noOverwrite option is true).
2. If the source is a directory that exists:
a. If the destination is a directory, then the source directory is
re-parented such that it becomes a child of the destination.
b. If the destination does not exist, then the source is renamed,
creating additional directories if needed.
c. If the destination is a file, then an error occurs.
3. If the source does not exist, an error occurs.
Differential Revision: https://phabricator.services.mozilla.com/D82202
2020-07-15 19:03:52 +03:00
|
|
|
|
2020-07-30 17:02:38 +03:00
|
|
|
/**
|
2020-11-10 04:58:55 +03:00
|
|
|
* Attempts to remove the file located at |aFile|.
|
2020-07-30 17:02:38 +03:00
|
|
|
*
|
2020-11-10 04:58:55 +03:00
|
|
|
* @param aFile The location of the file.
|
2020-07-30 17:02:38 +03:00
|
|
|
* @param aIgnoreAbsent If true, suppress errors due to an absent target file.
|
|
|
|
* @param aRecursive If true, attempt to recursively remove descendant
|
|
|
|
* files. This option is safe to use even if the target
|
|
|
|
* is not a directory.
|
|
|
|
*
|
|
|
|
* @return Ok if the file was removed successfully, or an error.
|
|
|
|
*/
|
2020-12-03 07:10:30 +03:00
|
|
|
static Result<Ok, IOError> RemoveSync(nsIFile* aFile, bool aIgnoreAbsent,
|
|
|
|
bool aRecursive);
|
2020-07-18 03:31:57 +03:00
|
|
|
|
2020-07-21 18:13:35 +03:00
|
|
|
/**
|
2020-11-10 04:59:02 +03:00
|
|
|
* Attempts to create a new directory at |aFile|.
|
2020-07-21 18:13:35 +03:00
|
|
|
*
|
2020-11-10 04:59:02 +03:00
|
|
|
* @param aFile The location of the directory to create.
|
2020-07-21 18:13:35 +03:00
|
|
|
* @param aCreateAncestors If true, create missing ancestor directories as
|
|
|
|
* needed. Otherwise, report an error if the target
|
|
|
|
* has non-existing ancestor directories.
|
|
|
|
* @param aIgnoreExisting If true, suppress errors that occur if the target
|
|
|
|
* directory already exists. Otherwise, propagate the
|
|
|
|
* error if it occurs.
|
|
|
|
* @param aMode Optional file mode. Defaults to 0777 to allow the
|
|
|
|
* system umask to compute the best mode for the new
|
|
|
|
* directory.
|
2020-07-30 17:02:38 +03:00
|
|
|
*
|
|
|
|
* @return Ok if the directory was created successfully, or an error.
|
2020-07-21 18:13:35 +03:00
|
|
|
*/
|
2020-12-03 07:10:30 +03:00
|
|
|
static Result<Ok, IOError> MakeDirectorySync(nsIFile* aFile,
|
2020-11-10 04:59:02 +03:00
|
|
|
bool aCreateAncestors,
|
|
|
|
bool aIgnoreExisting,
|
|
|
|
int32_t aMode = 0777);
|
2020-07-21 18:13:35 +03:00
|
|
|
|
2020-07-30 17:02:38 +03:00
|
|
|
/**
|
2020-11-10 04:59:10 +03:00
|
|
|
* Attempts to stat a file at |aFile|.
|
2020-07-30 17:02:38 +03:00
|
|
|
*
|
2020-11-10 04:59:10 +03:00
|
|
|
* @param aFile The location of the file.
|
2020-07-30 17:02:38 +03:00
|
|
|
*
|
|
|
|
* @return An |InternalFileInfo| struct if successful, or an error.
|
|
|
|
*/
|
2020-12-03 07:10:30 +03:00
|
|
|
static Result<IOUtils::InternalFileInfo, IOError> StatSync(nsIFile* aFile);
|
2020-08-26 18:31:57 +03:00
|
|
|
|
|
|
|
/**
|
2020-11-10 04:59:17 +03:00
|
|
|
* Attempts to update the last modification time of the file at |aFile|.
|
2020-08-26 18:31:57 +03:00
|
|
|
*
|
2020-11-10 04:59:17 +03:00
|
|
|
* @param aFile The location of the file.
|
2020-08-26 18:31:57 +03:00
|
|
|
* @param aNewModTime Some value in milliseconds since Epoch. For the current
|
|
|
|
* system time, use |Nothing|.
|
|
|
|
*
|
|
|
|
* @return Timestamp of the file if the operation was successful, or an error.
|
|
|
|
*/
|
2021-11-25 05:19:20 +03:00
|
|
|
static Result<int64_t, IOError> SetModificationTimeSync(
|
|
|
|
nsIFile* aFile, const Maybe<int64_t>& aNewModTime);
|
2020-08-28 18:49:58 +03:00
|
|
|
|
|
|
|
/**
|
2020-11-10 04:59:30 +03:00
|
|
|
* Returns the immediate children of the directory at |aFile|, if any.
|
2020-08-28 18:49:58 +03:00
|
|
|
*
|
2020-11-10 04:59:30 +03:00
|
|
|
* @param aFile The location of the directory.
|
2020-08-28 18:49:58 +03:00
|
|
|
*
|
2020-11-10 04:59:30 +03:00
|
|
|
* @return An array of absolute paths identifying the children of |aFile|.
|
2020-08-28 18:49:58 +03:00
|
|
|
* If there are no children, an empty array. Otherwise, an error.
|
|
|
|
*/
|
2021-11-25 13:45:55 +03:00
|
|
|
static Result<nsTArray<nsString>, IOError> GetChildrenSync(
|
|
|
|
nsIFile* aFile, bool aIgnoreAbsent);
|
2020-12-03 08:37:45 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the permissions of the given file.
|
|
|
|
*
|
|
|
|
* Windows does not make a distinction between user, group, and other
|
|
|
|
* permissions like UNICES do. If a permission flag is set for any of user,
|
|
|
|
* group, or other has a permission, then all users will have that
|
|
|
|
* permission.
|
|
|
|
*
|
|
|
|
* @param aFile The location of the file.
|
|
|
|
* @param aPermissions The permissions to set, as a UNIX file mode.
|
|
|
|
*
|
|
|
|
* @return |Ok| if the permissions were successfully set, or an error.
|
|
|
|
*/
|
|
|
|
static Result<Ok, IOError> SetPermissionsSync(nsIFile* aFile,
|
|
|
|
const uint32_t aPermissions);
|
2020-12-03 08:38:03 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Return whether or not the file exists.
|
|
|
|
*
|
|
|
|
* @param aFile The location of the file.
|
|
|
|
*
|
|
|
|
* @return Whether or not the file exists.
|
|
|
|
*/
|
|
|
|
static Result<bool, IOError> ExistsSync(nsIFile* aFile);
|
2021-03-23 07:26:50 +03:00
|
|
|
|
2022-01-18 08:04:52 +03:00
|
|
|
/**
|
|
|
|
* Create a file or directory with a unique path.
|
|
|
|
*
|
|
|
|
* @param aFile The location of the file or directory (including prefix)
|
|
|
|
* @param aFileType One of |nsIFile::NORMAL_FILE_TYPE| or
|
|
|
|
* |nsIFile::DIRECTORY_TYPE|.
|
|
|
|
* @param aperms The permissions to create the file or directory with.
|
|
|
|
*
|
|
|
|
* @return A unique path.
|
|
|
|
*/
|
|
|
|
static Result<nsString, IOError> CreateUniqueSync(
|
|
|
|
nsIFile* aFile, const uint32_t aFileType, const uint32_t aPermissions);
|
|
|
|
|
2021-12-17 20:21:22 +03:00
|
|
|
#if defined(XP_WIN)
|
|
|
|
/**
|
|
|
|
* Return the Windows-specific attributes of the file.
|
|
|
|
*
|
|
|
|
* @param aFile The location of the file.
|
|
|
|
*
|
|
|
|
* @return The Windows-specific attributes of the file.
|
|
|
|
*/
|
|
|
|
static Result<uint32_t, IOError> GetWindowsAttributesSync(nsIFile* aFile);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the Windows-specific attributes of the file.
|
|
|
|
*
|
|
|
|
* @param aFile The location of the file.
|
|
|
|
* @param aAttrs The attributes to set on the file.
|
|
|
|
*
|
|
|
|
* @return |Ok| if the attributes were successfully set, or an error.
|
|
|
|
*/
|
|
|
|
static Result<Ok, IOError> SetWindowsAttributesSync(
|
|
|
|
nsIFile* aFile, const uint32_t aSetAttrs, const uint32_t aClearAttrs);
|
2021-12-25 03:30:11 +03:00
|
|
|
#elif defined(XP_MACOSX)
|
|
|
|
static Result<bool, IOError> HasMacXAttrSync(nsIFile* aFile,
|
|
|
|
const nsCString& aAttr);
|
|
|
|
static Result<nsTArray<uint8_t>, IOError> GetMacXAttrSync(
|
|
|
|
nsIFile* aFile, const nsCString& aAttr);
|
|
|
|
static Result<Ok, IOError> SetMacXAttrSync(nsIFile* aFile,
|
|
|
|
const nsCString& aAttr,
|
|
|
|
const nsTArray<uint8_t>& aValue);
|
|
|
|
static Result<Ok, IOError> DelMacXAttrSync(nsIFile* aFile,
|
|
|
|
const nsCString& aAttr);
|
2021-12-17 20:21:22 +03:00
|
|
|
#endif
|
|
|
|
|
2021-03-23 07:26:50 +03:00
|
|
|
enum class EventQueueStatus {
|
|
|
|
Uninitialized,
|
|
|
|
Initialized,
|
|
|
|
Shutdown,
|
|
|
|
};
|
|
|
|
|
|
|
|
enum class ShutdownBlockerStatus {
|
|
|
|
Uninitialized,
|
|
|
|
Initialized,
|
|
|
|
Failed,
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Internal IOUtils state.
|
|
|
|
*/
|
|
|
|
class State {
|
|
|
|
public:
|
|
|
|
StaticAutoPtr<EventQueue> mEventQueue;
|
|
|
|
EventQueueStatus mQueueStatus = EventQueueStatus::Uninitialized;
|
|
|
|
ShutdownBlockerStatus mBlockerStatus = ShutdownBlockerStatus::Uninitialized;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set up shutdown hooks to free our internals at shutdown.
|
|
|
|
*
|
|
|
|
* NB: Must be called on main thread.
|
|
|
|
*/
|
|
|
|
void SetShutdownHooks();
|
|
|
|
};
|
|
|
|
|
|
|
|
using StateMutex = StaticDataMutex<State>;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Lock the state mutex and return a handle. If shutdown has not yet
|
|
|
|
* finished, the internals will be constructed if necessary.
|
|
|
|
*
|
|
|
|
* @returns A handle to the internal state, which can be used to retrieve the
|
|
|
|
* event queue.
|
|
|
|
* If |Some| is returned, |mEventQueue| is guaranteed to be
|
|
|
|
* initialized. If shutdown has finished, |Nothing| is returned.
|
|
|
|
*/
|
|
|
|
static Maybe<StateMutex::AutoLock> GetState();
|
|
|
|
|
|
|
|
static StateMutex sState;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The IOUtils event queue.
|
|
|
|
*/
|
|
|
|
class IOUtils::EventQueue final {
|
|
|
|
friend void IOUtils::State::SetShutdownHooks();
|
|
|
|
|
|
|
|
public:
|
|
|
|
EventQueue();
|
|
|
|
|
|
|
|
EventQueue(const EventQueue&) = delete;
|
|
|
|
EventQueue(EventQueue&&) = delete;
|
|
|
|
EventQueue& operator=(const EventQueue&) = delete;
|
|
|
|
EventQueue& operator=(EventQueue&&) = delete;
|
|
|
|
|
2022-02-01 06:23:44 +03:00
|
|
|
/**
|
|
|
|
* Dispatch a task on the event queue.
|
|
|
|
*
|
|
|
|
* NB: If using this directly from |IOUtils| instead of
|
|
|
|
* |IOUtils::DispatchAndResolve| *and* the calling thread is a worker, you
|
|
|
|
* *must* take care to keep the worker thread alive until the |IOPromise|
|
|
|
|
* resolves or rejects. See the implementation of
|
|
|
|
* |IOUtils::DispatchAndResolve| or |IOUtils::GetWindowsAttributes| for an
|
|
|
|
* example.
|
|
|
|
*
|
|
|
|
* @param aFunc The task to dispatch on the event queue.
|
|
|
|
*
|
|
|
|
* @return A promise that resolves to the task's return value or rejects with
|
|
|
|
* an error.
|
|
|
|
*/
|
2021-03-23 07:26:50 +03:00
|
|
|
template <typename OkT, typename Fn>
|
|
|
|
RefPtr<IOPromise<OkT>> Dispatch(Fn aFunc);
|
|
|
|
|
|
|
|
Result<already_AddRefed<nsIAsyncShutdownClient>, nsresult>
|
|
|
|
GetProfileBeforeChangeClient();
|
|
|
|
|
|
|
|
Result<already_AddRefed<nsIAsyncShutdownBarrier>, nsresult>
|
|
|
|
GetProfileBeforeChangeBarrier();
|
|
|
|
|
|
|
|
private:
|
|
|
|
nsresult SetShutdownHooks();
|
|
|
|
|
|
|
|
nsCOMPtr<nsISerialEventTarget> mBackgroundEventTarget;
|
|
|
|
nsCOMPtr<nsIAsyncShutdownBarrier> mProfileBeforeChangeBarrier;
|
2021-02-22 21:25:06 +03:00
|
|
|
};
|
|
|
|
|
2020-07-30 17:02:50 +03:00
|
|
|
/**
|
|
|
|
* An error class used with the |Result| type returned by most private |IOUtils|
|
|
|
|
* methods.
|
|
|
|
*/
|
|
|
|
class IOUtils::IOError {
|
|
|
|
public:
|
|
|
|
MOZ_IMPLICIT IOError(nsresult aCode) : mCode(aCode), mMessage(Nothing()) {}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Replaces the message associated with this error.
|
|
|
|
*/
|
|
|
|
template <typename... Args>
|
|
|
|
IOError WithMessage(const char* const aMessage, Args... aArgs) {
|
|
|
|
mMessage.emplace(nsPrintfCString(aMessage, aArgs...));
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
IOError WithMessage(const char* const aMessage) {
|
|
|
|
mMessage.emplace(nsCString(aMessage));
|
|
|
|
return *this;
|
|
|
|
}
|
2020-11-10 05:00:01 +03:00
|
|
|
IOError WithMessage(const nsCString& aMessage) {
|
2020-07-30 17:02:50 +03:00
|
|
|
mMessage.emplace(aMessage);
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the |nsresult| associated with this error.
|
|
|
|
*/
|
|
|
|
nsresult Code() const { return mCode; }
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Maybe returns a message associated with this error.
|
|
|
|
*/
|
|
|
|
const Maybe<nsCString>& Message() const { return mMessage; }
|
2020-07-30 00:52:32 +03:00
|
|
|
|
2020-07-30 17:02:50 +03:00
|
|
|
private:
|
|
|
|
nsresult mCode;
|
|
|
|
Maybe<nsCString> mMessage;
|
2020-07-03 01:32:03 +03:00
|
|
|
};
|
|
|
|
|
2020-07-23 21:15:30 +03:00
|
|
|
/**
|
|
|
|
* This is an easier to work with representation of a |mozilla::dom::FileInfo|
|
|
|
|
* for private use in the IOUtils implementation.
|
|
|
|
*
|
|
|
|
* Because web IDL dictionaries are not easily copy/moveable, this class is
|
|
|
|
* used instead, until converted to the proper |mozilla::dom::FileInfo| before
|
|
|
|
* returning any results to JavaScript.
|
|
|
|
*/
|
|
|
|
struct IOUtils::InternalFileInfo {
|
|
|
|
nsString mPath;
|
2020-12-09 08:48:51 +03:00
|
|
|
FileType mType = FileType::Other;
|
|
|
|
uint64_t mSize = 0;
|
|
|
|
uint64_t mLastModified = 0;
|
2020-12-03 08:37:33 +03:00
|
|
|
Maybe<uint64_t> mCreationTime;
|
2020-12-09 08:48:51 +03:00
|
|
|
uint32_t mPermissions = 0;
|
2020-07-23 21:15:30 +03:00
|
|
|
};
|
|
|
|
|
2020-07-30 17:03:03 +03:00
|
|
|
/**
|
|
|
|
* This is an easier to work with representation of a
|
2020-12-09 08:48:46 +03:00
|
|
|
* |mozilla::dom::WriteOptions| for private use in the |IOUtils|
|
2020-07-30 17:03:03 +03:00
|
|
|
* implementation.
|
|
|
|
*
|
|
|
|
* Because web IDL dictionaries are not easily copy/moveable, this class is
|
|
|
|
* used instead.
|
|
|
|
*/
|
2020-12-09 08:48:46 +03:00
|
|
|
struct IOUtils::InternalWriteOpts {
|
2020-11-10 19:04:49 +03:00
|
|
|
RefPtr<nsIFile> mBackupFile;
|
|
|
|
RefPtr<nsIFile> mTmpFile;
|
2021-04-30 01:38:02 +03:00
|
|
|
WriteMode mMode;
|
2020-12-09 08:48:46 +03:00
|
|
|
bool mFlush = false;
|
|
|
|
bool mCompress = false;
|
2020-08-26 18:37:25 +03:00
|
|
|
|
2020-12-09 08:48:46 +03:00
|
|
|
static Result<InternalWriteOpts, IOUtils::IOError> FromBinding(
|
|
|
|
const WriteOptions& aOptions);
|
2020-07-30 17:03:03 +03:00
|
|
|
};
|
|
|
|
|
2020-08-28 18:57:44 +03:00
|
|
|
/**
|
|
|
|
* Re-implements the file compression and decompression utilities found
|
|
|
|
* in toolkit/components/lz4/lz4.js
|
|
|
|
*
|
|
|
|
* This implementation uses the non-standard data layout:
|
|
|
|
*
|
|
|
|
* - MAGIC_NUMBER (8 bytes)
|
|
|
|
* - content size (uint32_t, little endian)
|
|
|
|
* - content, as obtained from mozilla::Compression::LZ4::compress
|
|
|
|
*
|
|
|
|
* See bug 1209390 for more info.
|
|
|
|
*/
|
|
|
|
class IOUtils::MozLZ4 {
|
|
|
|
public:
|
|
|
|
static constexpr std::array<uint8_t, 8> MAGIC_NUMBER{
|
|
|
|
{'m', 'o', 'z', 'L', 'z', '4', '0', '\0'}};
|
|
|
|
|
|
|
|
static const uint32_t HEADER_SIZE = 8 + sizeof(uint32_t);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Compresses |aUncompressed| byte array, and returns a byte array with the
|
|
|
|
* correct format whose contents may be written to disk.
|
|
|
|
*/
|
|
|
|
static Result<nsTArray<uint8_t>, IOError> Compress(
|
|
|
|
Span<const uint8_t> aUncompressed);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Checks |aFileContents| for the correct file header, and returns the
|
|
|
|
* decompressed content.
|
|
|
|
*/
|
Bug 1680151 - Reduce copies in IOUtils::Read{,UTF8} r=nika,tcampbell
Previously, in both Read and ReadUTF8, we were doing copies where we did not
need to. Read allocated an nsTArray and passed that to JS, which performed a
copy of its contents to create a Uint8Array. ReadUTF8, on the other hand, would
take that nsTArray, convert it into ns nsString (1 copy), and then pass it to
JS for it to recreate the string (2 copies).
Now, we allocate our string and array buffers up front in JS' memory pools
directly and use the JS API to create the strings and arrays ourselves (instead
of relying on Promise::MaybeResolve() to do a copying conversion). Read now
performs 0 copies in the best case (if the file is not compressed) and ReadUTF8
also does 0 copies in the best case (if the file is not compressed and the
string is ASCII). In the worst case, Read performs a single extra allocation
(to decompress the file) and ReadUTF8 performs 2 (to decompress the file and to
convert a UTF-8 string to either a Latin1 string or a UTF-16 string).
Differential Revision: https://phabricator.services.mozilla.com/D99004
2021-01-15 07:28:08 +03:00
|
|
|
static Result<IOUtils::JsBuffer, IOError> Decompress(
|
|
|
|
Span<const uint8_t> aFileContents, IOUtils::BufferKind);
|
2020-08-28 18:57:44 +03:00
|
|
|
};
|
|
|
|
|
2021-03-23 07:26:50 +03:00
|
|
|
class IOUtilsShutdownBlocker : public nsIAsyncShutdownBlocker,
|
|
|
|
public nsIAsyncShutdownCompletionCallback {
|
2020-07-03 01:32:03 +03:00
|
|
|
public:
|
|
|
|
NS_DECL_THREADSAFE_ISUPPORTS
|
|
|
|
NS_DECL_NSIASYNCSHUTDOWNBLOCKER
|
2021-03-23 07:26:50 +03:00
|
|
|
NS_DECL_NSIASYNCSHUTDOWNCOMPLETIONCALLBACK
|
|
|
|
|
|
|
|
enum Phase {
|
|
|
|
ProfileBeforeChange,
|
|
|
|
XpcomWillShutdown,
|
|
|
|
};
|
|
|
|
|
|
|
|
explicit IOUtilsShutdownBlocker(Phase aPhase) : mPhase(aPhase) {}
|
2020-07-03 01:32:03 +03:00
|
|
|
|
|
|
|
private:
|
|
|
|
virtual ~IOUtilsShutdownBlocker() = default;
|
2021-03-23 07:26:50 +03:00
|
|
|
|
|
|
|
Phase mPhase;
|
|
|
|
RefPtr<nsIAsyncShutdownClient> mParentClient;
|
2020-07-03 01:32:03 +03:00
|
|
|
};
|
|
|
|
|
Bug 1680151 - Reduce copies in IOUtils::Read{,UTF8} r=nika,tcampbell
Previously, in both Read and ReadUTF8, we were doing copies where we did not
need to. Read allocated an nsTArray and passed that to JS, which performed a
copy of its contents to create a Uint8Array. ReadUTF8, on the other hand, would
take that nsTArray, convert it into ns nsString (1 copy), and then pass it to
JS for it to recreate the string (2 copies).
Now, we allocate our string and array buffers up front in JS' memory pools
directly and use the JS API to create the strings and arrays ourselves (instead
of relying on Promise::MaybeResolve() to do a copying conversion). Read now
performs 0 copies in the best case (if the file is not compressed) and ReadUTF8
also does 0 copies in the best case (if the file is not compressed and the
string is ASCII). In the worst case, Read performs a single extra allocation
(to decompress the file) and ReadUTF8 performs 2 (to decompress the file and to
convert a UTF-8 string to either a Latin1 string or a UTF-16 string).
Differential Revision: https://phabricator.services.mozilla.com/D99004
2021-01-15 07:28:08 +03:00
|
|
|
/**
|
|
|
|
* A buffer that is allocated inside one of JS heaps so that it can be converted
|
|
|
|
* to a JSString or Uint8Array object with at most one copy in the worst case.
|
|
|
|
*/
|
|
|
|
class IOUtils::JsBuffer final {
|
|
|
|
public:
|
|
|
|
/**
|
|
|
|
* Create a new buffer of the given kind with the requested capacity.
|
|
|
|
*
|
|
|
|
* @param aBufferKind The kind of buffer to create (either a string or an
|
|
|
|
* array).
|
|
|
|
* @param aCapacity The capacity of the buffer.
|
|
|
|
*
|
|
|
|
* @return Either a successfully created buffer or an error if it could not be
|
|
|
|
* allocated.
|
|
|
|
*/
|
|
|
|
static Result<JsBuffer, IOUtils::IOError> Create(
|
|
|
|
IOUtils::BufferKind aBufferKind, size_t aCapacity);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a new, empty buffer.
|
|
|
|
*
|
|
|
|
* This operation cannot fail.
|
|
|
|
*
|
|
|
|
* @param aBufferKind The kind of buffer to create (either a string or an
|
|
|
|
* array).
|
|
|
|
*
|
|
|
|
* @return An empty JsBuffer.
|
|
|
|
*/
|
|
|
|
static JsBuffer CreateEmpty(IOUtils::BufferKind aBufferKind);
|
|
|
|
|
|
|
|
JsBuffer(const JsBuffer&) = delete;
|
|
|
|
JsBuffer(JsBuffer&& aOther) noexcept;
|
|
|
|
JsBuffer& operator=(const JsBuffer&) = delete;
|
|
|
|
JsBuffer& operator=(JsBuffer&& aOther) noexcept;
|
|
|
|
|
|
|
|
size_t Length() { return mLength; }
|
|
|
|
char* Elements() { return mBuffer.get(); }
|
|
|
|
void SetLength(size_t aNewLength) {
|
|
|
|
MOZ_RELEASE_ASSERT(aNewLength <= mCapacity);
|
|
|
|
mLength = aNewLength;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return a span for writing to the buffer.
|
|
|
|
*
|
|
|
|
* |SetLength| should be called after the buffer has been written to.
|
|
|
|
*
|
|
|
|
* @returns A span for writing to. The size of the span is the entire
|
|
|
|
* allocated capacity.
|
|
|
|
*/
|
|
|
|
Span<char> BeginWriting() {
|
|
|
|
MOZ_RELEASE_ASSERT(mBuffer.get());
|
|
|
|
return Span(mBuffer.get(), mCapacity);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return a span for reading from.
|
|
|
|
*
|
|
|
|
* @returns A span for reading form. The size of the span is the set length
|
|
|
|
* of the buffer.
|
|
|
|
*/
|
|
|
|
Span<const char> BeginReading() const {
|
|
|
|
MOZ_RELEASE_ASSERT(mBuffer.get() || mLength == 0);
|
|
|
|
return Span(mBuffer.get(), mLength);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Consume the JsBuffer and convert it into a JSString.
|
|
|
|
*
|
|
|
|
* NOTE: This method asserts the buffer was allocated as a string buffer.
|
|
|
|
*
|
|
|
|
* @param aBuffer The buffer to convert to a string. After this call, the
|
|
|
|
* buffer will be invaldated and |IntoString| cannot be called
|
|
|
|
* again.
|
|
|
|
*
|
|
|
|
* @returns A JSString with the contents of |aBuffer|.
|
|
|
|
*/
|
|
|
|
static JSString* IntoString(JSContext* aCx, JsBuffer aBuffer);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Consume the JsBuffer and convert it into a Uint8Array.
|
|
|
|
*
|
|
|
|
* NOTE: This method asserts the buffer was allocated as an array buffer.
|
|
|
|
*
|
|
|
|
* @param aBuffer The buffer to convert to an array. After this call, the
|
|
|
|
* buffer will be invalidated and |IntoUint8Array| cannot be
|
|
|
|
* called again.
|
|
|
|
*
|
|
|
|
* @returns A JSBuffer
|
|
|
|
*/
|
|
|
|
static JSObject* IntoUint8Array(JSContext* aCx, JsBuffer aBuffer);
|
|
|
|
|
2021-03-10 11:19:25 +03:00
|
|
|
friend bool ToJSValue(JSContext* aCx, JsBuffer&& aBuffer,
|
|
|
|
JS::MutableHandle<JS::Value> aValue);
|
Bug 1680151 - Reduce copies in IOUtils::Read{,UTF8} r=nika,tcampbell
Previously, in both Read and ReadUTF8, we were doing copies where we did not
need to. Read allocated an nsTArray and passed that to JS, which performed a
copy of its contents to create a Uint8Array. ReadUTF8, on the other hand, would
take that nsTArray, convert it into ns nsString (1 copy), and then pass it to
JS for it to recreate the string (2 copies).
Now, we allocate our string and array buffers up front in JS' memory pools
directly and use the JS API to create the strings and arrays ourselves (instead
of relying on Promise::MaybeResolve() to do a copying conversion). Read now
performs 0 copies in the best case (if the file is not compressed) and ReadUTF8
also does 0 copies in the best case (if the file is not compressed and the
string is ASCII). In the worst case, Read performs a single extra allocation
(to decompress the file) and ReadUTF8 performs 2 (to decompress the file and to
convert a UTF-8 string to either a Latin1 string or a UTF-16 string).
Differential Revision: https://phabricator.services.mozilla.com/D99004
2021-01-15 07:28:08 +03:00
|
|
|
|
|
|
|
private:
|
|
|
|
IOUtils::BufferKind mBufferKind;
|
|
|
|
size_t mCapacity;
|
|
|
|
size_t mLength;
|
|
|
|
JS::UniqueChars mBuffer;
|
|
|
|
|
|
|
|
JsBuffer(BufferKind aBufferKind, size_t aCapacity);
|
|
|
|
};
|
|
|
|
|
2021-11-04 22:47:29 +03:00
|
|
|
class SyncReadFile : public nsISupports, public nsWrapperCache {
|
|
|
|
public:
|
|
|
|
SyncReadFile(nsISupports* aParent, RefPtr<nsFileStream>&& aStream,
|
|
|
|
int64_t aSize);
|
|
|
|
|
|
|
|
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
|
|
|
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(SyncReadFile)
|
|
|
|
|
|
|
|
nsISupports* GetParentObject() const { return mParent; }
|
|
|
|
|
|
|
|
virtual JSObject* WrapObject(JSContext* aCx,
|
|
|
|
JS::Handle<JSObject*> aGivenProto) override;
|
|
|
|
|
|
|
|
int64_t Size() const { return mSize; }
|
|
|
|
void ReadBytesInto(const Uint8Array&, const int64_t, ErrorResult& aRv);
|
|
|
|
void Close();
|
|
|
|
|
|
|
|
private:
|
|
|
|
virtual ~SyncReadFile();
|
|
|
|
|
|
|
|
nsCOMPtr<nsISupports> mParent;
|
|
|
|
RefPtr<nsFileStream> mStream;
|
|
|
|
int64_t mSize = 0;
|
|
|
|
};
|
|
|
|
|
2020-07-03 01:32:03 +03:00
|
|
|
} // namespace dom
|
2020-07-03 01:30:03 +03:00
|
|
|
} // namespace mozilla
|
|
|
|
|
|
|
|
#endif
|