Bug 1759152 - Expose file system to workers. r=dom-storage-reviewers,jesup,janv

Depends on D149983

Differential Revision: https://phabricator.services.mozilla.com/D140862
This commit is contained in:
Jari Jalkanen 2022-07-15 09:40:40 +00:00
parent bbf960e82c
commit f1f7e8a209
12 changed files with 287 additions and 58 deletions

View File

@ -9,6 +9,7 @@
#include "nsNetCID.h"
#include "mozilla/dom/FileSystemTypes.h"
#include "mozilla/dom/quota/QuotaCommon.h"
#include "mozilla/ipc/Endpoint.h"
#include "mozilla/ipc/FileDescriptorUtils.h"
#include "mozilla/Maybe.h"
@ -65,14 +66,19 @@ mozilla::ipc::IPCResult BackgroundFileSystemParent::RecvGetRoot(
nsAutoCString origin =
quota::QuotaManager::GetOriginFromValidatedPrincipalInfo(mPrincipalInfo);
// This opens the quota manager, which has to be done on PBackground
auto res =
fs::data::FileSystemDataManager::CreateFileSystemDataManager(origin);
QM_TRY(OkIf(res.isErr()), IPC_OK(), [aResolver](const auto&) {
aResolver(fs::FileSystemGetRootResponse(NS_ERROR_UNEXPECTED));
});
auto sendBackError = [aResolver](const auto& aRv) {
aResolver(fs::FileSystemGetRootResponse(aRv));
};
fs::EntryId rootId = fs::data::GetRootHandle(origin);
// This opens the quota manager, which has to be done on PBackground
QM_TRY_UNWRAP(
fs::data::FileSystemDataManager * dataRaw,
fs::data::FileSystemDataManager::CreateFileSystemDataManager(origin),
IPC_OK(), sendBackError);
UniquePtr<fs::data::FileSystemDataManager> data(dataRaw);
UniquePtr<fs::data::FileSystemDataManager> data(res.unwrap());
nsCOMPtr<nsIThread> pbackground = NS_GetCurrentThread();
nsCOMPtr<nsIEventTarget> target =
@ -86,22 +92,19 @@ mozilla::ipc::IPCResult BackgroundFileSystemParent::RecvGetRoot(
// We'll have to thread-hop back to this thread to respond. We could
// just have the create be one-way, then send the actual request on the
// new channel, but that's an extra IPC instead.
InvokeAsync(
taskqueue, __func__,
[origin, parentEp = std::move(aParentEp), aResolver,
data = std::move(data), taskqueue, pbackground]() mutable {
RefPtr<OriginPrivateFileSystemParent> parent =
new OriginPrivateFileSystemParent(taskqueue);
if (!parentEp.Bind(parent)) {
auto response = fs::FileSystemGetRootResponse(NS_ERROR_FAILURE);
return RootPromise::CreateAndReject(response, __func__);
}
// Send response back to pbackground to send to child
auto response =
fs::FileSystemGetRootResponse(fs::data::GetRootHandle(origin));
return RootPromise::CreateAndResolve(response, __func__);
})
InvokeAsync(taskqueue, __func__,
[origin, parentEp = std::move(aParentEp), aResolver, rootId,
data = std::move(data), taskqueue, pbackground]() mutable {
RefPtr<OriginPrivateFileSystemParent> parent =
new OriginPrivateFileSystemParent(taskqueue);
if (!parentEp.Bind(parent)) {
auto response =
fs::FileSystemGetRootResponse(NS_ERROR_FAILURE);
return RootPromise::CreateAndReject(response, __func__);
}
// Send response back to pbackground to send to child
return RootPromise::CreateAndResolve(rootId, __func__);
})
->Then(GetCurrentSerialEventTarget(), __func__,
[aResolver](const RootPromise::ResolveOrRejectValue& aValue) {
if (aValue.IsReject()) {

View File

@ -3,19 +3,192 @@
* http://creativecommons.org/publicdomain/zero/1.0/
*/
async function test1() {
const { nsresult } = await require_module("dom/fs/test/common/nsresult.js");
exported_symbols.testGetDirectoryDoesNotThrow = async function() {
await navigator.storage.getDirectory();
Assert.ok(true, "Should not have thrown");
};
exported_symbols.testGetDirectoryKindIsDirectory = async function() {
const root = await navigator.storage.getDirectory();
Assert.equal(root.kind, "directory");
};
exported_symbols.testDirectoryHandleStringConversion = async function() {
const root = await navigator.storage.getDirectory();
Assert.equal(
"" + root,
"[object FileSystemDirectoryHandle]",
"Is directoryHandle convertible to string?"
);
};
exported_symbols.testNewDirectoryHandleFromPrototype = async function() {
const root = await navigator.storage.getDirectory();
try {
await navigator.storage.getDirectory();
Object.create(root.prototype);
Assert.ok(false, "Should have thrown");
} catch (e) {
} catch (ex) {
Assert.ok(true, "Should have thrown");
Assert.ok(
e.result === nsresult.NS_ERROR_NOT_IMPLEMENTED,
"Threw right result code"
Assert.ok(ex instanceof TypeError, "Threw the right error type");
}
};
exported_symbols.testDirectoryHandleSupportsKeysIterator = async function() {
const root = await navigator.storage.getDirectory();
const it = await root.keys();
Assert.ok(!!it, "Does root support keys iterator?");
};
exported_symbols.testKeysIteratorNextIsCallable = async function() {
const root = await navigator.storage.getDirectory();
const it = await root.keys();
Assert.ok(!!it, "Does root support keys iterator?");
try {
await it.next();
Assert.ok(false, "Should have thrown");
} catch (ex) {
Assert.ok(true, "Should have thrown");
Assert.equal(
ex.result,
Cr.NS_ERROR_NOT_IMPLEMENTED,
"Threw the right result code"
);
}
};
exported_symbols.testDirectoryHandleSupportsValuesIterator = async function() {
const root = await navigator.storage.getDirectory();
const it = await root.values();
Assert.ok(!!it, "Does root support values iterator?");
};
exported_symbols.testValuesIteratorNextIsCallable = async function() {
const root = await navigator.storage.getDirectory();
const it = await root.values();
Assert.ok(!!it, "Does root support values iterator?");
try {
await it.next();
Assert.ok(false, "Should have thrown");
} catch (ex) {
Assert.ok(true, "Should have thrown");
Assert.equal(
ex.result,
Cr.NS_ERROR_NOT_IMPLEMENTED,
"Threw the right result code"
);
}
};
exported_symbols.testDirectoryHandleSupportsEntriesIterator = async function() {
const root = await navigator.storage.getDirectory();
const it = await root.entries();
Assert.ok(!!it, "Does root support entries iterator?");
};
exported_symbols.testEntriesIteratorNextIsCallable = async function() {
const root = await navigator.storage.getDirectory();
const it = await root.entries();
Assert.ok(!!it, "Does root support entries iterator?");
try {
await it.next();
Assert.ok(false, "Should have thrown");
} catch (ex) {
Assert.ok(true, "Should have thrown");
Assert.equal(
ex.result,
Cr.NS_ERROR_NOT_IMPLEMENTED,
"Threw the right result code"
);
}
};
exported_symbols.testGetFileHandleIsCallable = async function() {
const root = await navigator.storage.getDirectory();
const allowCreate = { create: true };
try {
await root.getFileHandle("name", allowCreate);
Assert.ok(false, "Should have thrown");
} catch (ex) {
Assert.ok(true, "Should have thrown");
Assert.equal(
ex.result,
Cr.NS_ERROR_NOT_IMPLEMENTED,
"Threw the right result code"
);
}
};
exported_symbols.testGetDirectoryHandleIsCallable = async function() {
const root = await navigator.storage.getDirectory();
const allowCreate = { create: true };
try {
await root.getDirectoryHandle("name", allowCreate);
Assert.ok(false, "Should have thrown");
} catch (ex) {
Assert.ok(true, "Should have thrown");
Assert.equal(
ex.result,
Cr.NS_ERROR_NOT_IMPLEMENTED,
"Threw the right result code"
);
}
};
exported_symbols.testRemoveEntryIsCallable = async function() {
const root = await navigator.storage.getDirectory();
const removeOptions = { recursive: true };
try {
await root.removeEntry("root", removeOptions);
Assert.ok(false, "Should have thrown");
} catch (ex) {
Assert.ok(true, "Should have thrown");
Assert.equal(
ex.result,
Cr.NS_ERROR_NOT_IMPLEMENTED,
"Threw the right result code"
);
}
};
exported_symbols.testResolveIsCallable = async function() {
const root = await navigator.storage.getDirectory();
try {
await root.resolve(root);
Assert.ok(false, "Should have thrown");
} catch (ex) {
Assert.ok(true, "Should have thrown");
Assert.equal(
ex.result,
Cr.NS_ERROR_NOT_IMPLEMENTED,
"Threw the right result code"
);
}
};
for (const [key, value] of Object.entries(exported_symbols)) {
Object.defineProperty(value, "name", {
value: key,
writable: false,
});
}
exported_symbols.test1 = test1;

View File

@ -36,7 +36,7 @@ async function run_test_in_worker(script) {
// XXX It would be nice if we could call add_setup here (xpcshell-test and
// browser-test support it.
add_task(async function() {
add_task(async function setup() {
const { setStoragePrefs, clearStoragesForOrigin } = await import(
"/tests/dom/quota/test/modules/StorageUtils.js"
);

View File

@ -11,11 +11,19 @@
<script type="text/javascript" src="head.js"></script>
<script type="text/javascript">
add_task(async function test_1() {
const { test1 } = await require_module("dom/fs/test/common/test_basics.js");
// XXX This should be done in a task, but SimpleTest currently doesn't
// support adding new tasks during a task execution.
async function init() {
const testSet = "dom/fs/test/common/test_basics.js";
await test1();
});
const testCases = await require_module(testSet);
Object.values(testCases).forEach(testItem => {
add_task(testItem);
});
}
init();
</script>
</head>

View File

@ -3,8 +3,9 @@
* http://creativecommons.org/publicdomain/zero/1.0/
*/
add_task(async function test_1() {
const { test1 } = await require_module("dom/fs/test/common/test_basics.js");
await test1();
add_task(async function init() {
const testCases = await require_module("dom/fs/test/common/test_basics.js");
Object.values(testCases).forEach(async testItem => {
add_task(testItem);
});
});

View File

@ -3,8 +3,12 @@
* http://creativecommons.org/publicdomain/zero/1.0/
*/
add_task(async function test_1() {
const { test1 } = await require_module("dom/fs/test/common/test_basics.js");
add_task(async function init() {
const testSet = "dom/fs/test/common/test_basics.js";
await test1();
const testCases = await require_module(testSet);
Object.values(testCases).forEach(testItem => {
add_task(testItem);
});
});

View File

@ -10,6 +10,7 @@
#include <cstdlib>
#include <utility>
#include "ErrorList.h"
#include "fs/FileSystemRequestHandler.h"
#include "MainThreadUtils.h"
#include "js/CallArgs.h"
#include "js/TypeDecls.h"
@ -754,13 +755,16 @@ already_AddRefed<Promise> StorageManager::Estimate(ErrorResult& aRv) {
}
already_AddRefed<Promise> StorageManager::GetDirectory(ErrorResult& aRv) {
RefPtr<Promise> promise = Promise::Create(GetParentObject(), aRv);
if (aRv.Failed()) {
MOZ_ASSERT(mOwner);
RefPtr<Promise> promise = Promise::Create(mOwner, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
promise->MaybeReject(NS_ERROR_NOT_IMPLEMENTED);
MOZ_ASSERT(promise);
fs::FileSystemRequestHandler{}.GetRoot(promise);
return promise.forget();
}

View File

@ -103,4 +103,5 @@ FINAL_LIBRARY = "xul"
LOCAL_INCLUDES += [
"/caps",
"/dom/fs/include",
]

View File

@ -5,7 +5,6 @@
// Just a wrapper around SimpleTest related functions for now.
export const Assert = {
ok(value, message) {
ok(value, message);
},
ok,
equal: is,
};

View File

@ -20,6 +20,10 @@ export async function runTestInWorker(script) {
ok(data.value, data.message);
break;
case "is":
is(data.a, data.b, data.message);
break;
case "info":
info(data.message);
break;
@ -27,6 +31,11 @@ export async function runTestInWorker(script) {
case "finish":
resolve();
break;
case "failure":
ok(false, "Worker had a failure: " + data.message);
resolve();
break;
}
};

View File

@ -11,4 +11,12 @@ const Assert = {
message,
});
},
equal(a, b, message) {
postMessage({
op: "is",
a,
b,
message,
});
},
};

View File

@ -10,6 +10,7 @@ const Cr = {
function add_task(func) {
if (!add_task.tasks) {
add_task.tasks = [];
add_task.index = 0;
}
add_task.tasks.push(func);
@ -20,17 +21,35 @@ addEventListener("message", async function onMessage(event) {
postMessage({ op: "info", message });
}
function executeSoon(callback) {
const channel = new MessageChannel();
channel.port1.postMessage("");
channel.port2.onmessage = function() {
callback();
};
}
function runNextTest() {
if (add_task.index < add_task.tasks.length) {
const task = add_task.tasks[add_task.index++];
info("add_task | Entering test " + task.name);
task()
.then(function() {
executeSoon(runNextTest);
info("add_task | Leaving test " + task.name);
})
.catch(function(ex) {
postMessage({ op: "failure", message: "" + ex });
});
} else {
postMessage({ op: "finish" });
}
}
removeEventListener("message", onMessage);
const data = event.data;
importScripts(...data);
let task;
while ((task = add_task.tasks.shift())) {
info("add_task | Entering test " + task.name);
await task();
info("add_task | Leaving test " + task.name);
}
postMessage({ op: "finish" });
executeSoon(runNextTest);
});