mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 564621, bug 582077 - JSON.parse shouldn't allow {"a" : "b",} or [1,]. But, because Firefox's bookmarks "JSON" generation has historically generated invalid JSON (it no longer does, see bug 505656), preserve a "legacy" mode of parsing that can be used to load bookmarks.json files (at least until we no longer support migration from Firefox <4 profiles :-) ). r=sayrer
This commit is contained in:
parent
0e03a3a79b
commit
04a95a7188
@ -1 +1 @@
|
||||
{"title":"","id":1,"dateAdded":1233157910552624,"lastModified":1233157955206833,"type":"text/x-moz-place-container","root":"placesRoot","children":[{"title":"Bookmarks Menu","id":2,"parent":1,"dateAdded":1233157910552624,"lastModified":1233157993171424,"type":"text/x-moz-place-container","root":"bookmarksMenuFolder","children":[{"title":"examplejson","id":27,"parent":2,"dateAdded":1233157972101126,"lastModified":1233157984999673,"type":"text/x-moz-place","uri":"http://example.com/"}]},{"index":1,"title":"Bookmarks Toolbar","id":3,"parent":1,"dateAdded":1233157910552624,"lastModified":1233157972101126,"annos":[{"name":"bookmarkProperties/description","flags":0,"expires":4,"mimeType":null,"type":3,"value":"Add bookmarks to this folder to see them displayed on the Bookmarks Toolbar"}],"type":"text/x-moz-place-container","root":"toolbarFolder","children":[{"title":"examplejson","id":26,"parent":3,"dateAdded":1233157972101126,"lastModified":1233157984999673,"type":"text/x-moz-place","uri":"http://example.com/"}]},{"index":2,"title":"Tags","id":4,"parent":1,"dateAdded":1233157910552624,"lastModified":1233157910582667,"type":"text/x-moz-place-container","root":"tagsFolder","children":[]},{"index":3,"title":"Unsorted Bookmarks","id":5,"parent":1,"dateAdded":1233157910552624,"lastModified":1233157911033315,"type":"text/x-moz-place-container","root":"unfiledBookmarksFolder","children":[]}]}
|
||||
{"title":"","id":1,"dateAdded":1233157910552624,"lastModified":1233157955206833,"type":"text/x-moz-place-container","root":"placesRoot","children":[{"title":"Bookmarks Menu","id":2,"parent":1,"dateAdded":1233157910552624,"lastModified":1233157993171424,"type":"text/x-moz-place-container","root":"bookmarksMenuFolder","children":[{"title":"examplejson","id":27,"parent":2,"dateAdded":1233157972101126,"lastModified":1233157984999673,"type":"text/x-moz-place","uri":"http://example.com/"}]},{"index":1,"title":"Bookmarks Toolbar","id":3,"parent":1,"dateAdded":1233157910552624,"lastModified":1233157972101126,"annos":[{"name":"bookmarkProperties/description","flags":0,"expires":4,"mimeType":null,"type":3,"value":"Add bookmarks to this folder to see them displayed on the Bookmarks Toolbar"}],"type":"text/x-moz-place-container","root":"toolbarFolder","children":[{"title":"examplejson","id":26,"parent":3,"dateAdded":1233157972101126,"lastModified":1233157984999673,"type":"text/x-moz-place","uri":"http://example.com/"}]},{"index":2,"title":"Tags","id":4,"parent":1,"dateAdded":1233157910552624,"lastModified":1233157910582667,"type":"text/x-moz-place-container","root":"tagsFolder","children":[]},{"index":3,"title":"Unsorted Bookmarks","id":5,"parent":1,"dateAdded":1233157910552624,"lastModified":1233157911033315,"type":"text/x-moz-place-container","root":"unfiledBookmarksFolder","children":[]},]}
|
||||
|
@ -52,7 +52,7 @@ interface nsIScriptGlobalObject;
|
||||
/**
|
||||
* Encode and decode JSON text.
|
||||
*/
|
||||
[scriptable, uuid(6fcf09ee-87d0-42ec-a72a-8d60114e974f)]
|
||||
[scriptable, uuid(a4d68b4e-0c0b-4c7c-b540-ef2f9834171f)]
|
||||
interface nsIJSON : nsISupports
|
||||
{
|
||||
AString encode(/* in JSObject value */);
|
||||
@ -71,4 +71,25 @@ interface nsIJSON : nsISupports
|
||||
|
||||
// Make sure you GCroot the result of this function before using it.
|
||||
[noscript] jsval decodeToJSVal(in AString str, in JSContext cx);
|
||||
|
||||
|
||||
/*
|
||||
* Decode a JSON string, but also accept some strings in non-JSON format, as
|
||||
* the decoding methods here did previously before tightening.
|
||||
*
|
||||
* This method is provided only as a temporary transition path for users of
|
||||
* the old code who depended on the ability to decode leniently; new users
|
||||
* should use the non-legacy decoding methods.
|
||||
*
|
||||
* @param str the string to parse
|
||||
*/
|
||||
void /* JSObject */ legacyDecode(in AString str);
|
||||
|
||||
/* Identical to legacyDecode, but decode the contents of stream. */
|
||||
void /* JSObject */ legacyDecodeFromStream(in nsIInputStream stream,
|
||||
in long contentLength);
|
||||
|
||||
/* Identical to legacyDecode, but decode into a jsval. */
|
||||
// Make sure you GCroot the result of this function before using it.
|
||||
[noscript] jsval legacyDecodeToJSVal(in AString str, in JSContext cx);
|
||||
};
|
||||
|
@ -417,7 +417,7 @@ nsJSON::DecodeToJSVal(const nsAString &str, JSContext *cx, jsval *result)
|
||||
// Since we've called JS_BeginJSONParse, we have to call JS_FinishJSONParse,
|
||||
// even if JS_ConsumeJSONText fails. But if either fails, we'll report an
|
||||
// error.
|
||||
ok = ok && JS_FinishJSONParse(cx, parser, JSVAL_NULL);
|
||||
ok &= JS_FinishJSONParse(cx, parser, JSVAL_NULL);
|
||||
|
||||
if (!ok) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
@ -429,7 +429,8 @@ nsJSON::DecodeToJSVal(const nsAString &str, JSContext *cx, jsval *result)
|
||||
nsresult
|
||||
nsJSON::DecodeInternal(nsIInputStream *aStream,
|
||||
PRInt32 aContentLength,
|
||||
PRBool aNeedsConverter)
|
||||
PRBool aNeedsConverter,
|
||||
DecodingMode mode /* = STRICT */)
|
||||
{
|
||||
nsresult rv;
|
||||
nsIXPConnect *xpc = nsContentUtils::XPConnect();
|
||||
@ -464,7 +465,7 @@ nsJSON::DecodeInternal(nsIInputStream *aStream,
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
nsRefPtr<nsJSONListener>
|
||||
jsonListener(new nsJSONListener(cx, retvalPtr, aNeedsConverter));
|
||||
jsonListener(new nsJSONListener(cx, retvalPtr, aNeedsConverter, mode));
|
||||
|
||||
if (!jsonListener)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
@ -514,6 +515,52 @@ nsJSON::DecodeInternal(nsIInputStream *aStream,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsJSON::LegacyDecode(const nsAString& json)
|
||||
{
|
||||
const PRUnichar *data;
|
||||
PRUint32 len = NS_StringGetData(json, &data);
|
||||
nsCOMPtr<nsIInputStream> stream;
|
||||
nsresult rv = NS_NewByteInputStream(getter_AddRefs(stream),
|
||||
(const char*) data,
|
||||
len * sizeof(PRUnichar),
|
||||
NS_ASSIGNMENT_DEPEND);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return DecodeInternal(stream, len, PR_FALSE, LEGACY);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsJSON::LegacyDecodeFromStream(nsIInputStream *aStream, PRInt32 aContentLength)
|
||||
{
|
||||
return DecodeInternal(aStream, aContentLength, PR_TRUE, LEGACY);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsJSON::LegacyDecodeToJSVal(const nsAString &str, JSContext *cx, jsval *result)
|
||||
{
|
||||
JSAutoRequest ar(cx);
|
||||
|
||||
JSONParser *parser = JS_BeginJSONParse(cx, result);
|
||||
NS_ENSURE_TRUE(parser, NS_ERROR_UNEXPECTED);
|
||||
|
||||
JSBool ok = js_ConsumeJSONText(cx, parser,
|
||||
(jschar*)PromiseFlatString(str).get(),
|
||||
(uint32)str.Length(),
|
||||
LEGACY);
|
||||
|
||||
// Since we've called JS_BeginJSONParse, we have to call JS_FinishJSONParse,
|
||||
// even if js_ConsumeJSONText fails. But if either fails, we'll report an
|
||||
// error.
|
||||
ok &= JS_FinishJSONParse(cx, parser, JSVAL_NULL);
|
||||
|
||||
if (!ok) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
NS_NewJSON(nsISupports* aOuter, REFNSIID aIID, void** aResult)
|
||||
{
|
||||
@ -528,11 +575,13 @@ NS_NewJSON(nsISupports* aOuter, REFNSIID aIID, void** aResult)
|
||||
}
|
||||
|
||||
nsJSONListener::nsJSONListener(JSContext *cx, jsval *rootVal,
|
||||
PRBool needsConverter)
|
||||
PRBool needsConverter,
|
||||
DecodingMode mode /* = STRICT */)
|
||||
: mNeedsConverter(needsConverter),
|
||||
mJSONParser(nsnull),
|
||||
mCx(cx),
|
||||
mRootVal(rootVal)
|
||||
mRootVal(rootVal),
|
||||
mDecodingMode(mode)
|
||||
{
|
||||
}
|
||||
|
||||
@ -706,7 +755,8 @@ nsJSONListener::Consume(const PRUnichar* aBuffer, PRUint32 aByteLength)
|
||||
if (!mJSONParser)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
if (!JS_ConsumeJSONText(mCx, mJSONParser, (jschar*) aBuffer, aByteLength)) {
|
||||
if (!js_ConsumeJSONText(mCx, mJSONParser, (jschar*) aBuffer, aByteLength,
|
||||
mDecodingMode)) {
|
||||
Cleanup();
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
@ -40,6 +40,7 @@
|
||||
#define nsJSON_h__
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "json.h"
|
||||
#include "nsIJSON.h"
|
||||
#include "nsString.h"
|
||||
#include "nsCOMPtr.h"
|
||||
@ -86,9 +87,11 @@ public:
|
||||
|
||||
protected:
|
||||
nsresult EncodeInternal(nsJSONWriter *writer);
|
||||
|
||||
nsresult DecodeInternal(nsIInputStream *aStream,
|
||||
PRInt32 aContentLength,
|
||||
PRBool aNeedsConverter);
|
||||
PRBool aNeedsConverter,
|
||||
DecodingMode mode = STRICT);
|
||||
nsCOMPtr<nsIURI> mURI;
|
||||
};
|
||||
|
||||
@ -98,7 +101,8 @@ NS_NewJSON(nsISupports* aOuter, REFNSIID aIID, void** aResult);
|
||||
class nsJSONListener : public nsIStreamListener
|
||||
{
|
||||
public:
|
||||
nsJSONListener(JSContext *cx, jsval *rootVal, PRBool needsConverter);
|
||||
nsJSONListener(JSContext *cx, jsval *rootVal, PRBool needsConverter,
|
||||
DecodingMode mode);
|
||||
virtual ~nsJSONListener();
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
@ -112,6 +116,7 @@ protected:
|
||||
jsval *mRootVal;
|
||||
nsCOMPtr<nsIUnicodeDecoder> mDecoder;
|
||||
nsCString mSniffBuffer;
|
||||
DecodingMode mDecodingMode;
|
||||
nsresult ProcessBytes(const char* aBuffer, PRUint32 aByteLength);
|
||||
nsresult ConsumeConverted(const char* aBuffer, PRUint32 aByteLength);
|
||||
nsresult Consume(const PRUnichar *data, PRUint32 len);
|
||||
|
104
js/src/json.cpp
104
js/src/json.cpp
@ -988,37 +988,30 @@ HandleData(JSContext *cx, JSONParser *jp, JSONDataType type)
|
||||
}
|
||||
|
||||
JSBool
|
||||
js_ConsumeJSONText(JSContext *cx, JSONParser *jp, const jschar *data, uint32 len)
|
||||
js_ConsumeJSONText(JSContext *cx, JSONParser *jp, const jschar *data, uint32 len,
|
||||
DecodingMode decodingMode)
|
||||
{
|
||||
uint32 i;
|
||||
CHECK_REQUEST(cx);
|
||||
|
||||
if (*jp->statep == JSON_PARSE_STATE_INIT) {
|
||||
PushState(cx, jp, JSON_PARSE_STATE_VALUE);
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
for (uint32 i = 0; i < len; i++) {
|
||||
jschar c = data[i];
|
||||
switch (*jp->statep) {
|
||||
case JSON_PARSE_STATE_VALUE:
|
||||
case JSON_PARSE_STATE_ARRAY_INITIAL_VALUE:
|
||||
if (c == ']') {
|
||||
// empty array
|
||||
if (!PopState(cx, jp))
|
||||
return JS_FALSE;
|
||||
|
||||
if (*jp->statep != JSON_PARSE_STATE_ARRAY)
|
||||
return JSONParseError(jp, cx);
|
||||
|
||||
JS_ASSERT(*jp->statep == JSON_PARSE_STATE_ARRAY_AFTER_ELEMENT);
|
||||
if (!CloseArray(cx, jp) || !PopState(cx, jp))
|
||||
return JS_FALSE;
|
||||
|
||||
break;
|
||||
}
|
||||
// fall through if non-empty array or whitespace
|
||||
|
||||
if (c == '}') {
|
||||
// we should only find these in OBJECT_KEY state
|
||||
return JSONParseError(jp, cx);
|
||||
}
|
||||
|
||||
case JSON_PARSE_STATE_VALUE:
|
||||
if (c == '"') {
|
||||
*jp->statep = JSON_PARSE_STATE_STRING;
|
||||
break;
|
||||
@ -1038,56 +1031,77 @@ js_ConsumeJSONText(JSContext *cx, JSONParser *jp, const jschar *data, uint32 len
|
||||
break;
|
||||
}
|
||||
|
||||
// fall through in case the value is an object or array
|
||||
case JSON_PARSE_STATE_OBJECT_VALUE:
|
||||
if (c == '{') {
|
||||
*jp->statep = JSON_PARSE_STATE_OBJECT;
|
||||
if (!OpenObject(cx, jp) || !PushState(cx, jp, JSON_PARSE_STATE_OBJECT_PAIR))
|
||||
*jp->statep = JSON_PARSE_STATE_OBJECT_AFTER_PAIR;
|
||||
if (!OpenObject(cx, jp) || !PushState(cx, jp, JSON_PARSE_STATE_OBJECT_INITIAL_PAIR))
|
||||
return JS_FALSE;
|
||||
} else if (c == '[') {
|
||||
*jp->statep = JSON_PARSE_STATE_ARRAY;
|
||||
if (!OpenArray(cx, jp) || !PushState(cx, jp, JSON_PARSE_STATE_VALUE))
|
||||
*jp->statep = JSON_PARSE_STATE_ARRAY_AFTER_ELEMENT;
|
||||
if (!OpenArray(cx, jp) || !PushState(cx, jp, JSON_PARSE_STATE_ARRAY_INITIAL_VALUE))
|
||||
return JS_FALSE;
|
||||
} else if (!JS_ISXMLSPACE(c)) {
|
||||
return JSONParseError(jp, cx);
|
||||
}
|
||||
break;
|
||||
|
||||
case JSON_PARSE_STATE_OBJECT:
|
||||
if (c == '}') {
|
||||
if (!CloseObject(cx, jp) || !PopState(cx, jp))
|
||||
} else if (JS_ISXMLSPACE(c)) {
|
||||
// nothing to do
|
||||
} else if (decodingMode == LEGACY && c == ']') {
|
||||
if (!PopState(cx, jp))
|
||||
return JS_FALSE;
|
||||
} else if (c == ',') {
|
||||
if (!PushState(cx, jp, JSON_PARSE_STATE_OBJECT_PAIR))
|
||||
return JS_FALSE;
|
||||
} else if (c == ']' || !JS_ISXMLSPACE(c)) {
|
||||
return JSONParseError(jp, cx);
|
||||
}
|
||||
break;
|
||||
|
||||
case JSON_PARSE_STATE_ARRAY:
|
||||
if (c == ']') {
|
||||
JS_ASSERT(*jp->statep == JSON_PARSE_STATE_ARRAY_AFTER_ELEMENT);
|
||||
if (!CloseArray(cx, jp) || !PopState(cx, jp))
|
||||
return JS_FALSE;
|
||||
} else if (c == ',') {
|
||||
} else {
|
||||
return JSONParseError(jp, cx);
|
||||
}
|
||||
break;
|
||||
|
||||
case JSON_PARSE_STATE_ARRAY_AFTER_ELEMENT:
|
||||
if (c == ',') {
|
||||
if (!PushState(cx, jp, JSON_PARSE_STATE_VALUE))
|
||||
return JS_FALSE;
|
||||
} else if (c == ']') {
|
||||
if (!CloseArray(cx, jp) || !PopState(cx, jp))
|
||||
return JS_FALSE;
|
||||
} else if (!JS_ISXMLSPACE(c)) {
|
||||
return JSONParseError(jp, cx);
|
||||
}
|
||||
break;
|
||||
|
||||
case JSON_PARSE_STATE_OBJECT_AFTER_PAIR:
|
||||
if (c == ',') {
|
||||
if (!PushState(cx, jp, JSON_PARSE_STATE_OBJECT_PAIR))
|
||||
return JS_FALSE;
|
||||
} else if (c == '}') {
|
||||
if (!CloseObject(cx, jp) || !PopState(cx, jp))
|
||||
return JS_FALSE;
|
||||
} else if (!JS_ISXMLSPACE(c)) {
|
||||
return JSONParseError(jp, cx);
|
||||
}
|
||||
break;
|
||||
|
||||
case JSON_PARSE_STATE_OBJECT_INITIAL_PAIR:
|
||||
if (c == '}') {
|
||||
if (!PopState(cx, jp))
|
||||
return JS_FALSE;
|
||||
JS_ASSERT(*jp->statep == JSON_PARSE_STATE_OBJECT_AFTER_PAIR);
|
||||
if (!CloseObject(cx, jp) || !PopState(cx, jp))
|
||||
return JS_FALSE;
|
||||
break;
|
||||
}
|
||||
// fall through if non-empty object or whitespace
|
||||
|
||||
case JSON_PARSE_STATE_OBJECT_PAIR:
|
||||
if (c == '"') {
|
||||
// we want to be waiting for a : when the string has been read
|
||||
*jp->statep = JSON_PARSE_STATE_OBJECT_IN_PAIR;
|
||||
if (!PushState(cx, jp, JSON_PARSE_STATE_STRING))
|
||||
return JS_FALSE;
|
||||
} else if (c == '}') {
|
||||
// pop off the object pair state and the object state
|
||||
if (!CloseObject(cx, jp) || !PopState(cx, jp) || !PopState(cx, jp))
|
||||
} else if (JS_ISXMLSPACE(c)) {
|
||||
// nothing to do
|
||||
} else if (decodingMode == LEGACY && c == '}') {
|
||||
if (!PopState(cx, jp))
|
||||
return JS_FALSE;
|
||||
} else if (c == ']' || !JS_ISXMLSPACE(c)) {
|
||||
JS_ASSERT(*jp->statep == JSON_PARSE_STATE_OBJECT_AFTER_PAIR);
|
||||
if (!CloseObject(cx, jp) || !PopState(cx, jp))
|
||||
return JS_FALSE;
|
||||
} else {
|
||||
return JSONParseError(jp, cx);
|
||||
}
|
||||
break;
|
||||
@ -1114,7 +1128,7 @@ js_ConsumeJSONText(JSContext *cx, JSONParser *jp, const jschar *data, uint32 len
|
||||
return JS_FALSE;
|
||||
} else if (c == '\\') {
|
||||
*jp->statep = JSON_PARSE_STATE_STRING_ESCAPE;
|
||||
} else if (c < 31) {
|
||||
} else if (c <= 0x1F) {
|
||||
// The JSON lexical grammer does not allow a JSONStringCharacter to be
|
||||
// any of the Unicode characters U+0000 thru U+001F (control characters).
|
||||
return JSONParseError(jp, cx);
|
||||
|
@ -40,7 +40,10 @@
|
||||
/*
|
||||
* JS JSON functions.
|
||||
*/
|
||||
#include "jsscan.h"
|
||||
#include "jsprvtd.h"
|
||||
#include "jspubtd.h"
|
||||
#include "jsvalue.h"
|
||||
#include "jsvector.h"
|
||||
|
||||
#define JSON_MAX_DEPTH 2048
|
||||
#define JSON_PARSER_BUFSIZE 1024
|
||||
@ -64,23 +67,26 @@ enum JSONParserState {
|
||||
/* JSON fully processed, expecting only trailing whitespace. */
|
||||
JSON_PARSE_STATE_FINISHED,
|
||||
|
||||
/* Unused: to be removed in bug 564621. */
|
||||
JSON_PARSE_STATE_OBJECT_VALUE,
|
||||
|
||||
/* Start of JSON value. */
|
||||
JSON_PARSE_STATE_VALUE,
|
||||
|
||||
/* In object, at start of pair, at comma, or at closing brace. */
|
||||
JSON_PARSE_STATE_OBJECT,
|
||||
/* Start of first key/value pair in object, or at }. */
|
||||
JSON_PARSE_STATE_OBJECT_INITIAL_PAIR,
|
||||
|
||||
/* At start of pair within object, or at closing brace. */
|
||||
/* Start of subsequent key/value pair in object, after delimiting comma. */
|
||||
JSON_PARSE_STATE_OBJECT_PAIR,
|
||||
|
||||
/* At : in key/value pair in object. */
|
||||
JSON_PARSE_STATE_OBJECT_IN_PAIR,
|
||||
|
||||
/* In array, at start of element, at comma, or at closing bracket. */
|
||||
JSON_PARSE_STATE_ARRAY,
|
||||
/* Immediately after key/value pair in object: at , or }. */
|
||||
JSON_PARSE_STATE_OBJECT_AFTER_PAIR,
|
||||
|
||||
/* Start of first element of array or at ]. */
|
||||
JSON_PARSE_STATE_ARRAY_INITIAL_VALUE,
|
||||
|
||||
/* Immediately after element in array: at , or ]. */
|
||||
JSON_PARSE_STATE_ARRAY_AFTER_ELEMENT,
|
||||
|
||||
|
||||
/* The following states allow no leading whitespace. */
|
||||
@ -113,8 +119,18 @@ struct JSONParser;
|
||||
extern JSONParser *
|
||||
js_BeginJSONParse(JSContext *cx, js::Value *rootVal, bool suppressErrors = false);
|
||||
|
||||
extern JSBool
|
||||
js_ConsumeJSONText(JSContext *cx, JSONParser *jp, const jschar *data, uint32 len);
|
||||
/*
|
||||
* The type of JSON decoding to perform. Strict decoding is to-the-spec;
|
||||
* legacy decoding accepts a few non-JSON syntaxes historically accepted by the
|
||||
* implementation. (Full description of these deviations is deliberately
|
||||
* omitted.) New users should use strict decoding rather than legacy decoding,
|
||||
* as legacy decoding might be removed at a future time.
|
||||
*/
|
||||
enum DecodingMode { STRICT, LEGACY };
|
||||
|
||||
extern JS_FRIEND_API(JSBool)
|
||||
js_ConsumeJSONText(JSContext *cx, JSONParser *jp, const jschar *data, uint32 len,
|
||||
DecodingMode decodingMode = STRICT);
|
||||
|
||||
extern bool
|
||||
js_FinishJSONParse(JSContext *cx, JSONParser *jp, const js::Value &reviver);
|
||||
|
@ -1,3 +1,5 @@
|
||||
url-prefix ../../jsreftest.html?test=ecma_5/JSON/
|
||||
script cyclic-stringify.js
|
||||
script small-codepoints.js
|
||||
script trailing-comma.js
|
||||
script stringify-gap.js
|
||||
|
@ -0,0 +1,29 @@
|
||||
gTestsubsuite='JSON';
|
||||
|
||||
function testJSON(str, expectSyntaxError)
|
||||
{
|
||||
try
|
||||
{
|
||||
JSON.parse(str);
|
||||
reportCompare(false, expectSyntaxError,
|
||||
"string <" + str + "> " +
|
||||
"should" + (expectSyntaxError ? "n't" : "") + " " +
|
||||
"have parsed as JSON");
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
if (!(e instanceof SyntaxError))
|
||||
{
|
||||
reportCompare(true, false,
|
||||
"parsing string <" + str + "> threw a non-SyntaxError " +
|
||||
"exception: " + e);
|
||||
}
|
||||
else
|
||||
{
|
||||
reportCompare(true, expectSyntaxError,
|
||||
"string <" + str + "> " +
|
||||
"should" + (expectSyntaxError ? "n't" : "") + " " +
|
||||
"have parsed as JSON, exception: " + e);
|
||||
}
|
||||
}
|
||||
}
|
16
js/src/tests/ecma_5/JSON/small-codepoints.js
Normal file
16
js/src/tests/ecma_5/JSON/small-codepoints.js
Normal file
@ -0,0 +1,16 @@
|
||||
// Any copyright is dedicated to the Public Domain.
|
||||
// http://creativecommons.org/licenses/publicdomain/
|
||||
|
||||
var gTestfile = 'small-codepoints.js';
|
||||
//-----------------------------------------------------------------------------
|
||||
var BUGNUMBER = 554079;
|
||||
var summary = 'JSON.parse should reject U+0000 through U+001F';
|
||||
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
/**************
|
||||
* BEGIN TEST *
|
||||
**************/
|
||||
|
||||
for (var i = 0; i <= 0x1F; i++)
|
||||
testJSON('["a' + String.fromCharCode(i) + 'c"]', true);
|
32
js/src/tests/ecma_5/JSON/trailing-comma.js
Normal file
32
js/src/tests/ecma_5/JSON/trailing-comma.js
Normal file
@ -0,0 +1,32 @@
|
||||
// Any copyright is dedicated to the Public Domain.
|
||||
// http://creativecommons.org/licenses/publicdomain/
|
||||
|
||||
var gTestfile = 'trailing-comma.js';
|
||||
//-----------------------------------------------------------------------------
|
||||
var BUGNUMBER = 564621;
|
||||
var summary = 'JSON.parse should reject {"a" : "b",} or [1,]';
|
||||
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
/**************
|
||||
* BEGIN TEST *
|
||||
**************/
|
||||
|
||||
testJSON('[]', false);
|
||||
testJSON('[1]', false);
|
||||
testJSON('["a"]', false);
|
||||
testJSON('{}', false);
|
||||
testJSON('{"a":1}', false);
|
||||
testJSON('{"a":"b"}', false);
|
||||
testJSON('{"a":true}', false);
|
||||
testJSON('[{}]', false);
|
||||
|
||||
testJSON('[1,]', true);
|
||||
testJSON('["a",]', true);
|
||||
testJSON('{,}', true);
|
||||
testJSON('{"a":1,}', true);
|
||||
testJSON('{"a":"b",}', true);
|
||||
testJSON('{"a":true,}', true);
|
||||
testJSON('[{,}]', true);
|
||||
testJSON('[[1,]]', true);
|
||||
testJSON('[{"a":"b",}]', true);
|
@ -722,8 +722,13 @@ var PlacesUtils = {
|
||||
case this.TYPE_X_MOZ_PLACE:
|
||||
case this.TYPE_X_MOZ_PLACE_SEPARATOR:
|
||||
case this.TYPE_X_MOZ_PLACE_CONTAINER:
|
||||
var JSON = Cc["@mozilla.org/dom/json;1"].createInstance(Ci.nsIJSON);
|
||||
nodes = JSON.decode("[" + blob + "]");
|
||||
var json = Cc["@mozilla.org/dom/json;1"].createInstance(Ci.nsIJSON);
|
||||
// Old profiles (pre-Firefox 4) may contain bookmarks.json files with
|
||||
// trailing commas, which we once accepted but no longer do -- except
|
||||
// when decoded using the legacy decoder. This can be reverted to
|
||||
// json.decode (better yet, to the ECMA-standard JSON.parse) when we no
|
||||
// longer support upgrades from pre-Firefox 4 profiles.
|
||||
nodes = json.legacyDecode("[" + blob + "]");
|
||||
break;
|
||||
case this.TYPE_X_MOZ_URL:
|
||||
var parts = blob.split("\n");
|
||||
|
Loading…
Reference in New Issue
Block a user