зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1676942 - Add IOUtils::ReadJSON r=nika,tcampbell
Differential Revision: https://phabricator.services.mozilla.com/D99154
This commit is contained in:
Родитель
1b41576f43
Коммит
7e2b57614d
|
@ -42,6 +42,15 @@ namespace IOUtils {
|
|||
* rejects with a DOMException.
|
||||
*/
|
||||
Promise<UTF8String> readUTF8(DOMString path, optional ReadUTF8Options opts = {});
|
||||
/**
|
||||
* Read the UTF-8 text file located at |path| and return the contents
|
||||
* parsed as JSON into a JS value.
|
||||
*
|
||||
* @param path An absolute path.
|
||||
*
|
||||
* @return Resolves with the contents of the file parsed as JSON.
|
||||
*/
|
||||
Promise<any> readJSON(DOMString path, optional ReadUTF8Options opts = {});
|
||||
/**
|
||||
* Attempts to safely write |data| to a file at |path|.
|
||||
*
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
#include "ErrorList.h"
|
||||
#include "js/ArrayBuffer.h"
|
||||
#include "js/JSON.h"
|
||||
#include "js/Utility.h"
|
||||
#include "js/experimental/TypedData.h"
|
||||
#include "jsfriendapi.h"
|
||||
|
@ -23,10 +24,10 @@
|
|||
#include "mozilla/Span.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "mozilla/TextUtils.h"
|
||||
#include "mozilla/dom/IOUtilsBinding.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "mozilla/Utf8.h"
|
||||
#include "mozilla/dom/IOUtilsBinding.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsError.h"
|
||||
#include "nsFileStreams.h"
|
||||
|
@ -236,6 +237,65 @@ already_AddRefed<Promise> IOUtils::ReadUTF8(GlobalObject& aGlobal,
|
|||
return promise.forget();
|
||||
}
|
||||
|
||||
/* static */
|
||||
already_AddRefed<Promise> IOUtils::ReadJSON(GlobalObject& aGlobal,
|
||||
const nsAString& aPath,
|
||||
const ReadUTF8Options& aOptions) {
|
||||
MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
|
||||
RefPtr<Promise> promise = CreateJSPromise(aGlobal);
|
||||
if (!promise) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIFile> file = new nsLocalFile();
|
||||
REJECT_IF_INIT_PATH_FAILED(file, aPath, promise);
|
||||
|
||||
RunOnBackgroundThread<JsBuffer>([file, decompress = aOptions.mDecompress]() {
|
||||
return ReadUTF8Sync(file, decompress);
|
||||
})
|
||||
->Then(
|
||||
GetCurrentSerialEventTarget(), __func__,
|
||||
[promise, file](JsBuffer&& aBuffer) {
|
||||
AutoJSAPI jsapi;
|
||||
if (NS_WARN_IF(!jsapi.Init(promise->GetGlobalObject()))) {
|
||||
promise->MaybeRejectWithUnknownError(
|
||||
"Could not initialize JS API");
|
||||
return;
|
||||
}
|
||||
JSContext* cx = jsapi.cx();
|
||||
|
||||
JS::Rooted<JSString*> jsonStr(
|
||||
cx, IOUtils::JsBuffer::IntoString(cx, std::move(aBuffer)));
|
||||
if (!jsonStr) {
|
||||
RejectJSPromise(promise, IOError(NS_ERROR_OUT_OF_MEMORY));
|
||||
return;
|
||||
}
|
||||
|
||||
JS::Rooted<JS::Value> val(cx);
|
||||
if (!JS_ParseJSON(cx, jsonStr, &val)) {
|
||||
JS::Rooted<JS::Value> exn(cx);
|
||||
if (JS_GetPendingException(cx, &exn)) {
|
||||
JS_ClearPendingException(cx);
|
||||
promise->MaybeReject(exn);
|
||||
} else {
|
||||
RejectJSPromise(
|
||||
promise,
|
||||
IOError(NS_ERROR_DOM_UNKNOWN_ERR)
|
||||
.WithMessage("ParseJSON threw an uncatchable exception "
|
||||
"while parsing file(%s)",
|
||||
file->HumanReadablePath().get()));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
promise->MaybeResolve(val);
|
||||
},
|
||||
[promise](const IOError& aErr) { RejectJSPromise(promise, aErr); });
|
||||
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
/* static */
|
||||
already_AddRefed<Promise> IOUtils::Write(GlobalObject& aGlobal,
|
||||
const nsAString& aPath,
|
||||
|
|
|
@ -64,6 +64,10 @@ class IOUtils final {
|
|||
const nsAString& aPath,
|
||||
const ReadUTF8Options& aOptions);
|
||||
|
||||
static already_AddRefed<Promise> ReadJSON(GlobalObject& aGlobal,
|
||||
const nsAString& aPath,
|
||||
const ReadUTF8Options& aOptions);
|
||||
|
||||
static already_AddRefed<Promise> Write(GlobalObject& aGlobal,
|
||||
const nsAString& aPath,
|
||||
const Uint8Array& aData,
|
||||
|
|
|
@ -8,6 +8,7 @@ support-files =
|
|||
[test_ioutils_dir_iteration.html]
|
||||
[test_ioutils_mkdir.html]
|
||||
[test_ioutils_read_write.html]
|
||||
[test_ioutils_read_write_json.html]
|
||||
[test_ioutils_read_write_utf8.html]
|
||||
[test_ioutils_remove.html]
|
||||
[test_ioutils_stat_touch.html]
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test the IOUtils file I/O API</title>
|
||||
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
<script src="file_ioutils_test_fixtures.js"></script>
|
||||
<script>
|
||||
"use strict";
|
||||
|
||||
const { Assert } = ChromeUtils.import("resource://testing-common/Assert.jsm");
|
||||
const { ObjectUtils } = ChromeUtils.import(
|
||||
"resource://gre/modules/ObjectUtils.jsm"
|
||||
);
|
||||
|
||||
const OBJECT = {
|
||||
"foo": [
|
||||
"bar",
|
||||
123,
|
||||
456.789,
|
||||
true,
|
||||
false,
|
||||
null,
|
||||
],
|
||||
"bar": {
|
||||
"baz": {},
|
||||
},
|
||||
};
|
||||
|
||||
const ARRAY = [1, 2.3, true, false, null, { "foo": "bar" }];
|
||||
|
||||
const PRIMITIVES = [123, true, false, "hello, world", null];
|
||||
|
||||
add_task(async function read_json() {
|
||||
const tmpDir = await PathUtils.getTempDir();
|
||||
const filename = PathUtils.join(tmpDir, "test_ioutils_read_json.tmp");
|
||||
|
||||
info("Testing IOUtils.readJSON() with a serialized object...");
|
||||
await IOUtils.writeUTF8(filename, JSON.stringify(OBJECT));
|
||||
const readObject = await IOUtils.readJSON(filename);
|
||||
const parsedObject = JSON.parse(await IOUtils.readUTF8(filename));
|
||||
ok(ObjectUtils.deepEqual(OBJECT, readObject), "JSON objects should round-trip");
|
||||
ok(
|
||||
ObjectUtils.deepEqual(parsedObject, readObject),
|
||||
"IOUtils.readJSON() equivalent to JSON.parse() for objects"
|
||||
);
|
||||
|
||||
info("Testing IOUtils.readJSON() with a serialized array...");
|
||||
await IOUtils.writeUTF8(filename, JSON.stringify(ARRAY));
|
||||
const readArray = await IOUtils.readJSON(filename);
|
||||
const parsedArray = JSON.parse(await IOUtils.readUTF8(filename));
|
||||
ok(ObjectUtils.deepEqual(ARRAY, readArray), "JSON arrays should round-trip");
|
||||
ok(
|
||||
ObjectUtils.deepEqual(parsedArray, readArray),
|
||||
"IOUtils.readJSON() equivalent to JSON.parse(IOUtils.readUTF8()) for arrays"
|
||||
);
|
||||
|
||||
info("Testing IOUtils.readJSON() with serialized primitives...");
|
||||
for (const primitive of PRIMITIVES) {
|
||||
await IOUtils.writeUTF8(filename, JSON.stringify(primitive));
|
||||
const readPrimitive = await IOUtils.readJSON(filename);
|
||||
const parsedPrimitive = JSON.parse(await IOUtils.readUTF8(filename));
|
||||
ok(primitive === readPrimitive, `JSON primitive ${primitive} should round trip`);
|
||||
ok(
|
||||
readPrimitive === parsedPrimitive,
|
||||
`${readPrimitive} === ${parsedPrimitive} -- IOUtils.readJSON() equivalent to JSON.parse() for primitive`
|
||||
);
|
||||
}
|
||||
|
||||
info("Testing IOUtils.readJSON() with a file that does not exist...");
|
||||
const notExistsFilename = PathUtils.join(tmpDir, "test_ioutils_read_json_not_exists.tmp");
|
||||
ok(!await IOUtils.exists(notExistsFilename), `${notExistsFilename} should not exist`);
|
||||
await Assert.rejects(
|
||||
IOUtils.readJSON(notExistsFilename),
|
||||
/NotFoundError: Could not open the file at/,
|
||||
"IOUtils::readJSON rejects when file does not exist"
|
||||
);
|
||||
|
||||
info("Testing IOUtils.readJSON() with a file that does not contain JSON");
|
||||
const invalidFilename = PathUtils.join(tmpDir, "test_ioutils_read_json_invalid.tmp");
|
||||
await IOUtils.writeUTF8(invalidFilename, ":)");
|
||||
|
||||
await Assert.rejects(
|
||||
IOUtils.readJSON(invalidFilename),
|
||||
/SyntaxError: JSON\.parse/,
|
||||
"IOUTils::readJSON rejects when the file contains invalid JSON"
|
||||
);
|
||||
|
||||
await cleanup(filename, invalidFilename);
|
||||
});
|
||||
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test"></pre>
|
||||
</body>
|
||||
|
||||
</html>
|
Загрузка…
Ссылка в новой задаче