Skip to content
Snippets Groups Projects
Commit 5218239a authored by Barret Rennie's avatar Barret Rennie
Browse files

Bug 1676942 - Add IOUtils::WriteJSON r=nika,tcampbell

parent ff56b503
No related branches found
No related tags found
No related merge requests found
......@@ -80,6 +80,18 @@ namespace IOUtils {
* otherwise rejects with a DOMException.
*/
Promise<unsigned long long> writeUTF8(DOMString path, UTF8String string, optional WriteOptions options = {});
/**
* Attempts to serialize |value| into a JSON string and encode it as into a
* UTF-8 string, then safely write the result to a file at |path|. Works
* exactly like |write|.
*
* @param path An absolute file path
* @param value The value to be serialized.
*
* @return Resolves with the number of bytes successfully written to the file,
* otherwise rejects with a DOMException.
*/
Promise<unsigned long long> writeJSON(DOMString path, any value, optional WriteOptions options = {});
/**
* Moves the file from |sourcePath| to |destPath|, creating necessary parents.
* If |destPath| is a directory, then the source file will be moved into the
......
......@@ -358,6 +358,59 @@ already_AddRefed<Promise> IOUtils::WriteUTF8(GlobalObject& aGlobal,
return promise.forget();
}
static bool AppendJsonAsUtf8(const char16_t* aData, uint32_t aLen, void* aStr) {
nsCString* str = static_cast<nsCString*>(aStr);
return AppendUTF16toUTF8(Span<const char16_t>(aData, aLen), *str, fallible);
}
/* static */
already_AddRefed<Promise> IOUtils::WriteJSON(GlobalObject& aGlobal,
const nsAString& aPath,
JS::Handle<JS::Value> aValue,
const WriteOptions& aOptions) {
MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
RefPtr<Promise> promise = CreateJSPromise(aGlobal);
if (!promise) {
return nullptr;
}
REJECT_IF_SHUTTING_DOWN(promise);
nsCOMPtr<nsIFile> file = new nsLocalFile();
REJECT_IF_INIT_PATH_FAILED(file, aPath, promise);
auto opts = InternalWriteOpts::FromBinding(aOptions);
if (opts.isErr()) {
RejectJSPromise(promise, opts.unwrapErr());
return promise.forget();
}
JSContext* cx = aGlobal.Context();
JS::Rooted<JS::Value> rootedValue(cx, aValue);
nsCString utf8Str;
if (!JS_Stringify(cx, &rootedValue, nullptr, JS::NullHandleValue,
AppendJsonAsUtf8, &utf8Str)) {
JS::Rooted<JS::Value> exn(cx, JS::UndefinedValue());
if (JS_GetPendingException(cx, &exn)) {
JS_ClearPendingException(cx);
promise->MaybeReject(exn);
} else {
RejectJSPromise(promise,
IOError(NS_ERROR_DOM_UNKNOWN_ERR)
.WithMessage("Could not serialize object to JSON"));
}
return promise.forget();
}
RunOnBackgroundThreadAndResolve<uint32_t>(
promise, [file = std::move(file), utf8Str = std::move(utf8Str),
opts = opts.unwrap()]() {
return WriteSync(file, AsBytes(Span(utf8Str)), opts);
});
return promise.forget();
}
/* static */
already_AddRefed<Promise> IOUtils::Move(GlobalObject& aGlobal,
const nsAString& aSourcePath,
......
......@@ -78,6 +78,11 @@ class IOUtils final {
const nsACString& aString,
const WriteOptions& aOptions);
static already_AddRefed<Promise> WriteJSON(GlobalObject& aGlobal,
const nsAString& aPath,
JS::Handle<JS::Value> aValue,
const WriteOptions& aOptions);
static already_AddRefed<Promise> Move(GlobalObject& aGlobal,
const nsAString& aSourcePath,
const nsAString& aDestPath,
......
......@@ -93,6 +93,54 @@
await cleanup(filename, invalidFilename);
});
add_task(async function write_json() {
const tmpDir = await PathUtils.getTempDir();
const filename = PathUtils.join(tmpDir, "test_ioutils_write_json.tmp");
info("Testing IOUtils.writeJSON() with an object...");
await IOUtils.writeJSON(filename, OBJECT);
const readObject = await IOUtils.readJSON(filename);
const readObjectStr = await IOUtils.readUTF8(filename);
ok(ObjectUtils.deepEqual(OBJECT, readObject), "JSON objects should round-trip");
ok(
readObjectStr === JSON.stringify(OBJECT),
"IOUtils.writeJSON() eqvuialent to JSON.stringify() for an object"
);
info("Testing IOUtils.writeJSON() with an array...");
await IOUtils.writeJSON(filename, ARRAY);
const readArray = await IOUtils.readJSON(filename);
const readArrayStr = await IOUtils.readUTF8(filename);
ok(ObjectUtils.deepEqual(ARRAY, readArray), "JSON arrays should round-trip");
ok(
readArrayStr === JSON.stringify(ARRAY),
"IOUtils.writeJSON() equivalent to JSON.stringify() for an array"
);
info("Testing IOUtils.writeJSON() with primitives...");
for (const primitive of PRIMITIVES) {
await IOUtils.writeJSON(filename, primitive);
const readPrimitive = await IOUtils.readJSON(filename);
const readPrimitiveStr = await IOUtils.readUTF8(filename);
ok(
primitive === readPrimitive,
`${primitive} === ${readPrimitive} -- IOUtils.writeJSON() should round trip primitive`
);
ok(
readPrimitiveStr === JSON.stringify(primitive),
`${readPrimitiveStr} === ${JSON.stringify(primitive)} -- IOUtils.writeJSON() equivalent to JSON.stringify for primitive`
);
}
info("Testing IOUtils.writeJSON() with unserializable objects...");
await Assert.rejects(
() => IOUtils.writeJSON(window),
/TypeError: cyclic object value/,
"IOUtils.writeJSON() cannot write cyclic objects"
);
await cleanup(filename);
});
</script>
</head>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment