diff options
Diffstat (limited to '')
-rw-r--r-- | dom/fs/api/FileSystemManager.cpp | 2 | ||||
-rw-r--r-- | dom/fs/api/FileSystemWritableFileStream.cpp | 43 | ||||
-rw-r--r-- | dom/fs/child/FileSystemShutdownBlocker.cpp | 14 | ||||
-rw-r--r-- | dom/fs/include/fs/FileSystemShutdownBlocker.h | 2 | ||||
-rw-r--r-- | dom/fs/parent/datamodel/FileSystemDatabaseManagerVersion001.cpp | 2 | ||||
-rw-r--r-- | dom/fs/parent/datamodel/FileSystemDatabaseManagerVersion001.h | 2 | ||||
-rw-r--r-- | dom/fs/parent/datamodel/FileSystemDatabaseManagerVersion002.cpp | 3 | ||||
-rw-r--r-- | dom/fs/test/common/test_writableFileStream.js | 106 | ||||
-rw-r--r-- | dom/fs/test/crashtests/1874334-2.html | 8 | ||||
-rw-r--r-- | dom/fs/test/crashtests/1874334.html | 16 | ||||
-rw-r--r-- | dom/fs/test/crashtests/crashtests.list | 3 | ||||
-rw-r--r-- | dom/fs/test/crashtests/sw1874334-2.js | 12 |
12 files changed, 197 insertions, 16 deletions
diff --git a/dom/fs/api/FileSystemManager.cpp b/dom/fs/api/FileSystemManager.cpp index 099739f8a1..5bcc49c615 100644 --- a/dom/fs/api/FileSystemManager.cpp +++ b/dom/fs/api/FileSystemManager.cpp @@ -100,7 +100,7 @@ void FileSystemManager::BeginRequest( MOZ_ASSERT(mGlobal); // Check if we're allowed to use storage - if (mGlobal->GetStorageAccess() < StorageAccess::eSessionScoped) { + if (mGlobal->GetStorageAccess() <= StorageAccess::ePrivateBrowsing) { aFailure(NS_ERROR_DOM_SECURITY_ERR); return; } diff --git a/dom/fs/api/FileSystemWritableFileStream.cpp b/dom/fs/api/FileSystemWritableFileStream.cpp index ab133b2707..8e0abe3ee6 100644 --- a/dom/fs/api/FileSystemWritableFileStream.cpp +++ b/dom/fs/api/FileSystemWritableFileStream.cpp @@ -86,7 +86,7 @@ class WritableFileStreamUnderlyingSinkAlgorithms final FileSystemWritableFileStream& aStream) : mStream(&aStream) {} - already_AddRefed<Promise> WriteCallback( + already_AddRefed<Promise> WriteCallbackImpl( JSContext* aCx, JS::Handle<JS::Value> aChunk, WritableStreamDefaultController& aController, ErrorResult& aRv) override; @@ -180,8 +180,10 @@ class FileSystemWritableFileStream::CloseHandler { * @brief Transition from initial to open state. In initial state * */ - void Open() { + void Open(std::function<void()>&& aCallback) { MOZ_ASSERT(State::Initial == mState); + + mShutdownBlocker->SetCallback(std::move(aCallback)); mShutdownBlocker->Block(); mState = State::Open; @@ -193,6 +195,7 @@ class FileSystemWritableFileStream::CloseHandler { */ void Close() { mShutdownBlocker->Unblock(); + mShutdownBlocker = nullptr; mState = State::Closed; mClosePromiseHolder.ResolveIfExists(true, __func__); } @@ -329,7 +332,17 @@ FileSystemWritableFileStream::Create( autoClose.release(); stream->mWorkerRef = std::move(workerRef); - stream->mCloseHandler->Open(); + + // The close handler passes this callback to `FileSystemShutdownBlocker` + // which has separate handling for debug and release builds. Basically, + // there's no content process shutdown blocking in release builds, so the + // callback might be executed in debug builds only. + stream->mCloseHandler->Open([stream]() { + if (stream->IsOpen()) { + // We don't need the promise, we just begin the closing process. + Unused << stream->BeginAbort(); + } + }); // Step 9: Return stream. return stream; @@ -479,23 +492,27 @@ already_AddRefed<Promise> FileSystemWritableFileStream::Write( ArrayBufferViewOrArrayBufferOrBlobOrUTF8StringOrWriteParams data; if (!data.Init(aCx, aChunk)) { aError.StealExceptionFromJSContext(aCx); + // XXX(krosylight): The Streams spec does not provide a way to catch errors + // thrown from the write algorithm, and the File System spec does not try + // catching them either. This is unfortunate, as per the spec the type error + // from write() must immediately return a rejected promise without any file + // handle closure. For now we handle it here manually. See also: + // - https://github.com/whatwg/streams/issues/636 + // - https://github.com/whatwg/fs/issues/153 + if (IsOpen()) { + (void)BeginAbort(); + } return nullptr; } // Step 2. Let p be a new promise. - RefPtr<Promise> promise = Promise::Create(GetParentObject(), aError); - if (aError.Failed()) { - return nullptr; - } - - RefPtr<Promise> innerPromise = Promise::Create(GetParentObject(), aError); - if (aError.Failed()) { - return nullptr; - } + RefPtr<Promise> promise = Promise::CreateInfallible(GetParentObject()); RefPtr<Command> command = CreateCommand(); // Step 3.3. + // XXX: This should ideally be also handled by the streams but we don't + // currently have the hook. See https://github.com/whatwg/streams/issues/620. Write(data)->Then( GetCurrentSerialEventTarget(), __func__, [self = RefPtr{this}, command, @@ -961,7 +978,7 @@ NS_IMPL_CYCLE_COLLECTION_INHERITED(WritableFileStreamUnderlyingSinkAlgorithms, // Step 3 of // https://fs.spec.whatwg.org/#create-a-new-filesystemwritablefilestream already_AddRefed<Promise> -WritableFileStreamUnderlyingSinkAlgorithms::WriteCallback( +WritableFileStreamUnderlyingSinkAlgorithms::WriteCallbackImpl( JSContext* aCx, JS::Handle<JS::Value> aChunk, WritableStreamDefaultController& aController, ErrorResult& aRv) { return mStream->Write(aCx, aChunk, aRv); diff --git a/dom/fs/child/FileSystemShutdownBlocker.cpp b/dom/fs/child/FileSystemShutdownBlocker.cpp index bddbf18c6b..e6301e52ea 100644 --- a/dom/fs/child/FileSystemShutdownBlocker.cpp +++ b/dom/fs/child/FileSystemShutdownBlocker.cpp @@ -36,6 +36,8 @@ class FileSystemWritableBlocker : public FileSystemShutdownBlocker { public: FileSystemWritableBlocker() : mName(CreateBlockerName()) {} + void SetCallback(std::function<void()>&& aCallback) override; + NS_DECL_ISUPPORTS_INHERITED NS_DECL_NSIASYNCSHUTDOWNBLOCKER @@ -50,8 +52,13 @@ class FileSystemWritableBlocker : public FileSystemShutdownBlocker { private: const nsString mName; + std::function<void()> mCallback; }; +void FileSystemWritableBlocker::SetCallback(std::function<void()>&& aCallback) { + mCallback = std::move(aCallback); +} + NS_IMPL_ISUPPORTS_INHERITED(FileSystemWritableBlocker, FileSystemShutdownBlocker, nsIAsyncShutdownBlocker) @@ -112,11 +119,18 @@ FileSystemWritableBlocker::BlockShutdown( nsIAsyncShutdownClient* /* aBarrier */) { MOZ_ASSERT(NS_IsMainThread()); + if (mCallback) { + auto callback = std::move(mCallback); + callback(); + } + return NS_OK; } class FileSystemNullBlocker : public FileSystemShutdownBlocker { public: + void SetCallback(std::function<void()>&& aCallback) override {} + NS_IMETHODIMP Block() override { return NS_OK; } NS_IMETHODIMP Unblock() override { return NS_OK; } diff --git a/dom/fs/include/fs/FileSystemShutdownBlocker.h b/dom/fs/include/fs/FileSystemShutdownBlocker.h index f11d263f4e..4932e2889e 100644 --- a/dom/fs/include/fs/FileSystemShutdownBlocker.h +++ b/dom/fs/include/fs/FileSystemShutdownBlocker.h @@ -17,6 +17,8 @@ class FileSystemShutdownBlocker : public nsIAsyncShutdownBlocker { public: static already_AddRefed<FileSystemShutdownBlocker> CreateForWritable(); + virtual void SetCallback(std::function<void()>&& aCallback) = 0; + NS_DECL_ISUPPORTS NS_DECL_NSIASYNCSHUTDOWNBLOCKER diff --git a/dom/fs/parent/datamodel/FileSystemDatabaseManagerVersion001.cpp b/dom/fs/parent/datamodel/FileSystemDatabaseManagerVersion001.cpp index c65bf01508..a898f1afb6 100644 --- a/dom/fs/parent/datamodel/FileSystemDatabaseManagerVersion001.cpp +++ b/dom/fs/parent/datamodel/FileSystemDatabaseManagerVersion001.cpp @@ -1233,7 +1233,7 @@ void FileSystemDatabaseManagerVersion001::DecreaseCachedQuotaUsage( } nsresult FileSystemDatabaseManagerVersion001::UpdateCachedQuotaUsage( - const FileId& aFileId, Usage aOldUsage, Usage aNewUsage) { + const FileId& aFileId, Usage aOldUsage, Usage aNewUsage) const { quota::QuotaManager* quotaManager = quota::QuotaManager::Get(); MOZ_ASSERT(quotaManager); diff --git a/dom/fs/parent/datamodel/FileSystemDatabaseManagerVersion001.h b/dom/fs/parent/datamodel/FileSystemDatabaseManagerVersion001.h index 333c5af6c2..64ecfa5b01 100644 --- a/dom/fs/parent/datamodel/FileSystemDatabaseManagerVersion001.h +++ b/dom/fs/parent/datamodel/FileSystemDatabaseManagerVersion001.h @@ -130,7 +130,7 @@ class FileSystemDatabaseManagerVersion001 : public FileSystemDatabaseManager { void DecreaseCachedQuotaUsage(int64_t aDelta); nsresult UpdateCachedQuotaUsage(const FileId& aFileId, Usage aOldUsage, - Usage aNewUsage); + Usage aNewUsage) const; nsresult ClearDestinationIfNotLocked( const FileSystemConnection& aConnection, diff --git a/dom/fs/parent/datamodel/FileSystemDatabaseManagerVersion002.cpp b/dom/fs/parent/datamodel/FileSystemDatabaseManagerVersion002.cpp index 3543346ff0..a6c61b0e3e 100644 --- a/dom/fs/parent/datamodel/FileSystemDatabaseManagerVersion002.cpp +++ b/dom/fs/parent/datamodel/FileSystemDatabaseManagerVersion002.cpp @@ -463,6 +463,9 @@ nsresult FileSystemDatabaseManagerVersion002::GetFile( if (mainFileId) { QM_TRY_UNWRAP(aFile, mFileManager->CreateFileFrom(aFileId, mainFileId.value())); + int64_t fileSize = 0; + QM_TRY(QM_TO_RESULT(aFile->GetFileSize(&fileSize))); + UpdateCachedQuotaUsage(aFileId, 0, fileSize); } else { // LockShared/EnsureTemporaryFileId has provided a brand new fileId. QM_TRY_UNWRAP(aFile, mFileManager->GetOrCreateFile(aFileId)); diff --git a/dom/fs/test/common/test_writableFileStream.js b/dom/fs/test/common/test_writableFileStream.js index 016c53bf3b..5b6caa0d20 100644 --- a/dom/fs/test/common/test_writableFileStream.js +++ b/dom/fs/test/common/test_writableFileStream.js @@ -2,6 +2,7 @@ http://creativecommons.org/publicdomain/zero/1.0/ */ const allowCreate = { create: true }; +const denyCreate = { create: false }; exported_symbols.test0 = async function () { let root = await navigator.storage.getDirectory(); @@ -145,6 +146,111 @@ exported_symbols.bug1825018 = async function () { } }; +exported_symbols.usageTest = async function () { + const bufferSize = 1024; + const keepData = { keepExistingData: true }; + const fromEmpty = { keepExistingData: false }; + + let root = await navigator.storage.getDirectory(); + Assert.ok(root, "Can we access the root directory?"); + + const baseUsage = await Utils.getCachedOriginUsage(); + Assert.ok(true, "Usage " + baseUsage); + // Create a file. + { + const fileHandle = await root.getFileHandle("usagetest.txt", allowCreate); + Assert.ok(!!fileHandle, "Can we get file handle?"); + + const writable = await fileHandle.createWritable(fromEmpty); + Assert.ok(!!writable, "Can we create writable file stream?"); + + const buffer = new ArrayBuffer(bufferSize); + Assert.ok(!!buffer, "Can we create array buffer?"); + + const result = await writable.write(buffer); + Assert.equal(result, undefined, "Can we write entire buffer?"); + + await writable.close(); + } + + { + const fileUsage = await Utils.getCachedOriginUsage(); + Assert.ok(true, "Usage " + fileUsage); + Assert.ok(fileUsage >= baseUsage + bufferSize); + + const fileHandle = await root.getFileHandle("usagetest.txt", denyCreate); + Assert.ok(!!fileHandle, "Can we get file handle?"); + + { + const usageNow = await Utils.getCachedOriginUsage(); + Assert.equal(usageNow, fileUsage); + } + + const writableA = await fileHandle.createWritable(keepData); + Assert.ok(!!writableA, "Can we create writable file stream?"); + + { + const usageNow = await Utils.getCachedOriginUsage(); + Assert.ok(true, "Usage " + usageNow.usage); + Assert.equal(usageNow, fileUsage + bufferSize); + } + + const writableB = await fileHandle.createWritable(keepData); + Assert.ok(!!writableB, "Can we create writable file stream?"); + + { + const usageNow = await Utils.getCachedOriginUsage(); + Assert.equal(usageNow, fileUsage + 2 * bufferSize); + } + + const writableC = await fileHandle.createWritable(keepData); + Assert.ok(!!writableC, "Can we create writable file stream?"); + + { + const usageNow = await Utils.getCachedOriginUsage(); + Assert.equal(usageNow, fileUsage + 3 * bufferSize); + } + + const writableD = await fileHandle.createWritable(fromEmpty); + Assert.ok(!!writableD, "Can we create writable file stream?"); + + { + const usageNow = await Utils.getCachedOriginUsage(); + // We did not keep existing data for this writable + Assert.equal(usageNow, fileUsage + 3 * bufferSize); + } + + await writableA.abort(); + + { + const usageNow = await Utils.getCachedOriginUsage(); + Assert.equal(usageNow, fileUsage + 2 * bufferSize); + } + + await writableB.close(); + + { + const usageNow = await Utils.getCachedOriginUsage(); + Assert.equal(usageNow, fileUsage + bufferSize); + } + + await writableC.abort(); + + { + const usageNow = await Utils.getCachedOriginUsage(); + Assert.equal(usageNow, fileUsage); + } + + await writableD.close(); + + { + const usageNow = await Utils.getCachedOriginUsage(); + // Buffer was overwritten with nothing. + Assert.equal(usageNow, fileUsage - bufferSize); + } + } +}; + for (const [key, value] of Object.entries(exported_symbols)) { Object.defineProperty(value, "name", { value: key, diff --git a/dom/fs/test/crashtests/1874334-2.html b/dom/fs/test/crashtests/1874334-2.html new file mode 100644 index 0000000000..3d30d7496f --- /dev/null +++ b/dom/fs/test/crashtests/1874334-2.html @@ -0,0 +1,8 @@ +<script> +document.addEventListener("DOMContentLoaded", async () => { + await self.navigator.serviceWorker.register("sw1874334-2.js?318", { }) + await self.navigator.serviceWorker.register("sw1874334-2.js?363", { }) + await self.navigator.serviceWorker.register("sw1874334-2.js?87", { }) + await self.fetch("missing", {"headers": []}) +}) +</script> diff --git a/dom/fs/test/crashtests/1874334.html b/dom/fs/test/crashtests/1874334.html new file mode 100644 index 0000000000..590025a60f --- /dev/null +++ b/dom/fs/test/crashtests/1874334.html @@ -0,0 +1,16 @@ +<script> + +async function stuff() { + let arr = new ArrayBuffer(500000); + let blob = new Blob([arr]); + let file = new File([blob], "", { }); + + let dir = await self.navigator.storage.getDirectory(); + let handle = await dir.getFileHandle("514600c6-596b-4676-ab0c-3e6f1e86759f", {"create": true}); + let wfs = await handle.createWritable({"keepExistingData": true}); + + await file.stream().pipeTo(wfs); +}; + +document.addEventListener("DOMContentLoaded", stuff); +</script> diff --git a/dom/fs/test/crashtests/crashtests.list b/dom/fs/test/crashtests/crashtests.list index a083cb80b2..c5130c4232 100644 --- a/dom/fs/test/crashtests/crashtests.list +++ b/dom/fs/test/crashtests/crashtests.list @@ -1,6 +1,7 @@ # StorageManager isn't enabled on Android defaults skip-if(Android) pref(dom.fs.enabled,true) pref(dom.fs.writable_file_stream.enabled,true) +# Most of the issues here are reproducible only with --verify load 1798773.html load 1800470.html load 1809759.html @@ -8,3 +9,5 @@ load 1816710.html load 1841702.html HTTP load 1844619.html HTTP load 1858820.html +load 1874334.html +HTTP load 1874334-2.html diff --git a/dom/fs/test/crashtests/sw1874334-2.js b/dom/fs/test/crashtests/sw1874334-2.js new file mode 100644 index 0000000000..59d82ebaaa --- /dev/null +++ b/dom/fs/test/crashtests/sw1874334-2.js @@ -0,0 +1,12 @@ +(async () => { + let arr = new ArrayBuffer(43109) + let blob = new Blob([arr, arr, arr, arr, arr]) + let req = new Request("missing", {"headers": []}) + let dir = await self.navigator.storage.getDirectory() + let file = new File([blob, arr], "", { }) + let handle = await dir.getFileHandle("514600c6-596b-4676-ab0c-3e6f1e86759f", {"create": true}) + let wfs = await handle.createWritable({"keepExistingData": true}) + await handle.createWritable({"keepExistingData": true}) + try { await req.json(arr, {"headers": []}) } catch (e) {} + try { await file.stream().pipeTo(wfs, { }) } catch (e) {} +})() |