summaryrefslogtreecommitdiffstats
path: root/dom/fs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--dom/fs/api/FileSystemManager.cpp2
-rw-r--r--dom/fs/api/FileSystemWritableFileStream.cpp43
-rw-r--r--dom/fs/child/FileSystemShutdownBlocker.cpp14
-rw-r--r--dom/fs/include/fs/FileSystemShutdownBlocker.h2
-rw-r--r--dom/fs/parent/datamodel/FileSystemDatabaseManagerVersion001.cpp2
-rw-r--r--dom/fs/parent/datamodel/FileSystemDatabaseManagerVersion001.h2
-rw-r--r--dom/fs/parent/datamodel/FileSystemDatabaseManagerVersion002.cpp3
-rw-r--r--dom/fs/test/common/test_writableFileStream.js106
-rw-r--r--dom/fs/test/crashtests/1874334-2.html8
-rw-r--r--dom/fs/test/crashtests/1874334.html16
-rw-r--r--dom/fs/test/crashtests/crashtests.list3
-rw-r--r--dom/fs/test/crashtests/sw1874334-2.js12
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) {}
+})()