зеркало из https://github.com/mozilla/pjs.git
Bug 386789: create JSON utilities module and use it for search suggest/sessionstore/Places, patch by Simon Bünzli <zeniko@gmail.com>, r=sspitzer, sr+a=brendan
This commit is contained in:
Родитель
138c534b51
Коммит
141ee630e6
|
@ -46,6 +46,8 @@ var Ci = Components.interfaces;
|
|||
var Cc = Components.classes;
|
||||
var Cr = Components.results;
|
||||
|
||||
Components.utils.import("resource://gre/modules/JSON.jsm");
|
||||
|
||||
const LOAD_IN_SIDEBAR_ANNO = "bookmarkProperties/loadInSidebar";
|
||||
const DESCRIPTION_ANNO = "bookmarkProperties/description";
|
||||
const POST_DATA_ANNO = "URIProperties/POSTData";
|
||||
|
@ -543,7 +545,7 @@ var PlacesUtils = {
|
|||
}
|
||||
return node;
|
||||
}
|
||||
return this.toJSONString(gatherDataPlace(convertNode(aNode)));
|
||||
return JSON.toString(gatherDataPlace(convertNode(aNode)));
|
||||
|
||||
case this.TYPE_X_MOZ_URL:
|
||||
function gatherDataUrl(bNode) {
|
||||
|
@ -756,7 +758,7 @@ var PlacesUtils = {
|
|||
case this.TYPE_X_MOZ_PLACE:
|
||||
case this.TYPE_X_MOZ_PLACE_SEPARATOR:
|
||||
case this.TYPE_X_MOZ_PLACE_CONTAINER:
|
||||
nodes = this.parseJSON("[" + blob + "]");
|
||||
nodes = JSON.fromString("[" + blob + "]");
|
||||
break;
|
||||
case this.TYPE_X_MOZ_URL:
|
||||
var parts = blob.split("\n");
|
||||
|
@ -1516,113 +1518,6 @@ var PlacesUtils = {
|
|||
}
|
||||
}
|
||||
return -1;
|
||||
},
|
||||
|
||||
/**
|
||||
* Converts a JavaScript object into a JSON string
|
||||
* (see http://www.json.org/ for the full grammar).
|
||||
*
|
||||
* The inverse operation consists of eval("(" + JSON_string + ")");
|
||||
* and should be provably safe.
|
||||
*
|
||||
* @param aJSObject is the object to be converted
|
||||
* @return the object's JSON representation
|
||||
*/
|
||||
toJSONString: function PU_toJSONString(aJSObject) {
|
||||
// these characters have a special escape notation
|
||||
const charMap = { "\b": "\\b", "\t": "\\t", "\n": "\\n", "\f": "\\f",
|
||||
"\r": "\\r", '"': '\\"', "\\": "\\\\" };
|
||||
// we use a single string builder for efficiency reasons
|
||||
var parts = [];
|
||||
|
||||
// this recursive function walks through all objects and appends their
|
||||
// JSON representation to the string builder
|
||||
function jsonIfy(aObj) {
|
||||
if (typeof aObj == "boolean") {
|
||||
parts.push(aObj ? "true" : "false");
|
||||
}
|
||||
else if (typeof aObj == "number" && isFinite(aObj)) {
|
||||
// there is no representation for infinite numbers or for NaN!
|
||||
parts.push(aObj.toString());
|
||||
}
|
||||
else if (typeof aObj == "string") {
|
||||
aObj = aObj.replace(/[\\"\x00-\x1F\u0080-\uFFFF]/g, function($0) {
|
||||
// use the special escape notation if one exists, otherwise
|
||||
// produce a general unicode escape sequence
|
||||
return charMap[$0] ||
|
||||
"\\u" + ("0000" + $0.charCodeAt(0).toString(16)).slice(-4);
|
||||
});
|
||||
parts.push('"' + aObj + '"');
|
||||
}
|
||||
else if (aObj == null) {
|
||||
parts.push("null");
|
||||
}
|
||||
else if (aObj instanceof Array) {
|
||||
parts.push("[");
|
||||
for (var i = 0; i < aObj.length; i++) {
|
||||
jsonIfy(aObj[i]);
|
||||
parts.push(",");
|
||||
}
|
||||
if (parts[parts.length - 1] == ",")
|
||||
parts.pop(); // drop the trailing colon
|
||||
parts.push("]");
|
||||
}
|
||||
else if (typeof aObj == "object") {
|
||||
parts.push("{");
|
||||
for (var key in aObj) {
|
||||
jsonIfy(key.toString());
|
||||
parts.push(":");
|
||||
jsonIfy(aObj[key]);
|
||||
parts.push(",");
|
||||
}
|
||||
if (parts[parts.length - 1] == ",")
|
||||
parts.pop(); // drop the trailing colon
|
||||
parts.push("}");
|
||||
}
|
||||
else {
|
||||
throw new Error("No JSON representation for this object!");
|
||||
}
|
||||
} // end of jsonIfy definition
|
||||
jsonIfy(aJSObject);
|
||||
|
||||
var newJSONString = parts.join(" ");
|
||||
// sanity check - so that API consumers can just eval this string
|
||||
if (/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(
|
||||
newJSONString.replace(/"(\\.|[^"\\])*"/g, "")
|
||||
))
|
||||
throw new Error("JSON conversion failed unexpectedly!");
|
||||
|
||||
return newJSONString;
|
||||
},
|
||||
|
||||
/**
|
||||
* Converts a JSON string into a JavaScript object
|
||||
* (see http://www.json.org/ for the full grammar).
|
||||
*
|
||||
* @param jsonText is the object to be converted
|
||||
* @return a JS Object
|
||||
*/
|
||||
parseJSON: function parseJSON(jsonText) {
|
||||
var m = {
|
||||
'\b': '\\b',
|
||||
'\t': '\\t',
|
||||
'\n': '\\n',
|
||||
'\f': '\\f',
|
||||
'\r': '\\r',
|
||||
'"' : '\\"',
|
||||
'\\': '\\\\'
|
||||
};
|
||||
|
||||
var EVAL_SANDBOX = new Components.utils.Sandbox("about:blank");
|
||||
|
||||
if (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/.test(jsonText.
|
||||
replace(/\\./g, '@').
|
||||
replace(/"[^"\\\n\r]*"/g, ''))) {
|
||||
var j = Components.utils.evalInSandbox(jsonText, EVAL_SANDBOX);
|
||||
return j;
|
||||
}
|
||||
else
|
||||
throw new SyntaxError('parseJSON');
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -55,6 +55,7 @@ const HTTP_BAD_GATEWAY = 502;
|
|||
const HTTP_SERVICE_UNAVAILABLE = 503;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/JSON.jsm");
|
||||
|
||||
/**
|
||||
* SuggestAutoCompleteResult contains the results returned by the Suggest
|
||||
|
@ -522,21 +523,7 @@ SuggestAutoComplete.prototype = {
|
|||
|
||||
this._clearServerErrors();
|
||||
|
||||
// This is a modified version of Crockford's JSON sanitizer, obtained
|
||||
// from http://www.json.org/js.html.
|
||||
// This should use built-in functions once bug 340987 is fixed.
|
||||
const JSON_STRING = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
|
||||
var sandbox = new Cu.Sandbox(this._suggestURI.prePath);
|
||||
function parseJSON(aString) {
|
||||
try {
|
||||
if (JSON_STRING.test(aString))
|
||||
return Cu.evalInSandbox("(" + aString + ")", sandbox);
|
||||
} catch (e) {}
|
||||
|
||||
return [];
|
||||
};
|
||||
|
||||
var serverResults = parseJSON(responseText);
|
||||
var serverResults = JSON.fromString(responseText);
|
||||
var searchString = serverResults[0] || "";
|
||||
var results = serverResults[1] || [];
|
||||
|
||||
|
|
|
@ -52,6 +52,7 @@
|
|||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cr = Components.results;
|
||||
const Cu = Components.utils;
|
||||
|
||||
const CID = Components.ID("{5280606b-2510-4fe0-97ef-9b5a22eafe6b}");
|
||||
const CONTRACT_ID = "@mozilla.org/browser/sessionstore;1";
|
||||
|
@ -100,7 +101,10 @@ const CAPABILITIES = [
|
|||
];
|
||||
|
||||
// sandbox to evaluate JavaScript code from non-trustable sources
|
||||
var EVAL_SANDBOX = new Components.utils.Sandbox("about:blank");
|
||||
var EVAL_SANDBOX = new Cu.Sandbox("about:blank");
|
||||
|
||||
// module for JSON conversion (needed for the nsISessionStore API)
|
||||
Cu.import("resource://gre/modules/JSON.jsm");
|
||||
|
||||
function debug(aMsg) {
|
||||
aMsg = ("SessionStore: " + aMsg).replace(/\S{80}/g, "$&\n");
|
||||
|
@ -1618,7 +1622,7 @@ SessionStoreService.prototype = {
|
|||
try {
|
||||
cookieManager.add(cookie.host, cookie.path || "", cookie.name || "", cookie.value, !!cookie.secure, !!cookie.httponly, true, "expiry" in cookie ? cookie.expiry : MAX_EXPIRY);
|
||||
}
|
||||
catch (ex) { Components.utils.reportError(ex); } // don't let a single cookie stop recovering
|
||||
catch (ex) { Cu.reportError(ex); } // don't let a single cookie stop recovering
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1909,12 +1913,12 @@ SessionStoreService.prototype = {
|
|||
* safe eval'ing
|
||||
*/
|
||||
_safeEval: function sss_safeEval(aStr) {
|
||||
return Components.utils.evalInSandbox(aStr, EVAL_SANDBOX);
|
||||
return Cu.evalInSandbox(aStr, EVAL_SANDBOX);
|
||||
},
|
||||
|
||||
/**
|
||||
* Converts a JavaScript object into a JSON string
|
||||
* (see http://www.json.org/ for the full grammar).
|
||||
* (see http://www.json.org/ for more information).
|
||||
*
|
||||
* The inverse operation consists of eval("(" + JSON_string + ")");
|
||||
* and should be provably safe.
|
||||
|
@ -1923,73 +1927,13 @@ SessionStoreService.prototype = {
|
|||
* @return the object's JSON representation
|
||||
*/
|
||||
_toJSONString: function sss_toJSONString(aJSObject) {
|
||||
// these characters have a special escape notation
|
||||
const charMap = { "\b": "\\b", "\t": "\\t", "\n": "\\n", "\f": "\\f",
|
||||
"\r": "\\r", '"': '\\"', "\\": "\\\\" };
|
||||
// we use a single string builder for efficiency reasons
|
||||
var parts = [];
|
||||
var str = JSON.toString(aJSObject, ["_tab", "_hosts"] /* keys to drop */);
|
||||
|
||||
// this recursive function walks through all objects and appends their
|
||||
// JSON representation to the string builder
|
||||
function jsonIfy(aObj) {
|
||||
if (typeof aObj == "boolean") {
|
||||
parts.push(aObj ? "true" : "false");
|
||||
}
|
||||
else if (typeof aObj == "number" && isFinite(aObj)) {
|
||||
// there is no representation for infinite numbers or for NaN!
|
||||
parts.push(aObj.toString());
|
||||
}
|
||||
else if (typeof aObj == "string") {
|
||||
aObj = aObj.replace(/[\\"\x00-\x1F\u0080-\uFFFF]/g, function($0) {
|
||||
// use the special escape notation if one exists, otherwise
|
||||
// produce a general unicode escape sequence
|
||||
return charMap[$0] ||
|
||||
"\\u" + ("0000" + $0.charCodeAt(0).toString(16)).slice(-4);
|
||||
});
|
||||
parts.push('"' + aObj + '"')
|
||||
}
|
||||
else if (aObj == null) {
|
||||
parts.push("null");
|
||||
}
|
||||
else if (aObj instanceof Array || aObj instanceof EVAL_SANDBOX.Array) {
|
||||
parts.push("[");
|
||||
for (var i = 0; i < aObj.length; i++) {
|
||||
jsonIfy(aObj[i]);
|
||||
parts.push(",");
|
||||
}
|
||||
if (parts[parts.length - 1] == ",")
|
||||
parts.pop(); // drop the trailing colon
|
||||
parts.push("]");
|
||||
}
|
||||
else if (typeof aObj == "object") {
|
||||
parts.push("{");
|
||||
for (var key in aObj) {
|
||||
if (key == "_tab")
|
||||
continue; // XXXzeniko we might even want to drop all private members
|
||||
|
||||
jsonIfy(key.toString());
|
||||
parts.push(":");
|
||||
jsonIfy(aObj[key]);
|
||||
parts.push(",");
|
||||
}
|
||||
if (parts[parts.length - 1] == ",")
|
||||
parts.pop(); // drop the trailing colon
|
||||
parts.push("}");
|
||||
}
|
||||
else {
|
||||
throw new Error("No JSON representation for this object!");
|
||||
}
|
||||
}
|
||||
jsonIfy(aJSObject);
|
||||
|
||||
var newJSONString = parts.join(" ");
|
||||
// sanity check - so that API consumers can just eval this string
|
||||
if (/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(
|
||||
newJSONString.replace(/"(\\.|[^"\\])*"/g, "")
|
||||
))
|
||||
if (!JSON.isMostlyHarmless(str))
|
||||
throw new Error("JSON conversion failed unexpectedly!");
|
||||
|
||||
return newJSONString;
|
||||
return str;
|
||||
},
|
||||
|
||||
/* ........ Storage API .............. */
|
||||
|
|
|
@ -0,0 +1,174 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Simon Bünzli <zeniko@gmail.com>
|
||||
* Portions created by the Initial Developer are Copyright (C) 2006-2007
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
/**
|
||||
* Utilities for JavaScript code to handle JSON content.
|
||||
* See http://www.json.org/ for comprehensive information about JSON.
|
||||
*
|
||||
* Import this module through
|
||||
*
|
||||
* Components.utils.import("resource://gre/modules/JSON.jsm");
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* var newJSONString = JSON.toString( GIVEN_JAVASCRIPT_OBJECT );
|
||||
* var newJavaScriptObject = JSON.fromString( GIVEN_JSON_STRING );
|
||||
*
|
||||
* Note: For your own safety, Objects/Arrays returned by
|
||||
* JSON.fromString aren't instanceof Object/Array.
|
||||
*/
|
||||
|
||||
EXPORTED_SYMBOLS = ["JSON"];
|
||||
|
||||
// The following code is a loose adaption of Douglas Crockford's code
|
||||
// from http://www.json.org/json.js (public domain'd)
|
||||
|
||||
// Notable differences:
|
||||
// * Unserializable values such as |undefined| or functions aren't
|
||||
// silently dropped but always lead to a TypeError.
|
||||
// * An optional key blacklist has been added to JSON.toString
|
||||
|
||||
var JSON = {
|
||||
/**
|
||||
* Converts a JavaScript object into a JSON string.
|
||||
*
|
||||
* @param aJSObject is the object to be converted
|
||||
* @param aKeysToDrop is an optional array of keys which will be
|
||||
* ignored in all objects during the serialization
|
||||
* @return the object's JSON representation
|
||||
*
|
||||
* Note: aJSObject MUST not contain cyclic references.
|
||||
*/
|
||||
toString: function JSON_toString(aJSObject, aKeysToDrop) {
|
||||
// these characters have a special escape notation
|
||||
const charMap = { "\b": "\\b", "\t": "\\t", "\n": "\\n", "\f": "\\f",
|
||||
"\r": "\\r", '"': '\\"', "\\": "\\\\" };
|
||||
|
||||
// we use a single string builder for efficiency reasons
|
||||
var pieces = [];
|
||||
|
||||
// this recursive function walks through all objects and appends their
|
||||
// JSON representation (in one or several pieces) to the string builder
|
||||
function append_piece(aObj) {
|
||||
if (typeof aObj == "boolean") {
|
||||
pieces.push(aObj ? "true" : "false");
|
||||
}
|
||||
else if (typeof aObj == "number" && isFinite(aObj)) {
|
||||
// there is no representation for infinite numbers or for NaN!
|
||||
pieces.push(aObj.toString());
|
||||
}
|
||||
else if (typeof aObj == "string") {
|
||||
aObj = aObj.replace(/[\\"\x00-\x1F\u0080-\uFFFF]/g, function($0) {
|
||||
// use the special escape notation if one exists, otherwise
|
||||
// produce a general unicode escape sequence
|
||||
return charMap[$0] ||
|
||||
"\\u" + ("0000" + $0.charCodeAt(0).toString(16)).slice(-4);
|
||||
});
|
||||
pieces.push('"' + aObj + '"')
|
||||
}
|
||||
else if (aObj === null) {
|
||||
pieces.push("null");
|
||||
}
|
||||
// if it looks like an array, treat it as such - this is required
|
||||
// for all arrays from either outside this module or a sandbox
|
||||
else if (aObj instanceof Array ||
|
||||
typeof aObj == "object" && "length" in aObj &&
|
||||
(aObj.length === 0 || aObj[aObj.length - 1] !== undefined)) {
|
||||
pieces.push("[");
|
||||
for (var i = 0; i < aObj.length; i++) {
|
||||
append_piece(aObj[i]);
|
||||
pieces.push(",");
|
||||
}
|
||||
if (pieces[pieces.length - 1] == ",")
|
||||
pieces.pop(); // drop the trailing colon
|
||||
pieces.push("]");
|
||||
}
|
||||
else if (typeof aObj == "object") {
|
||||
pieces.push("{");
|
||||
for (var key in aObj) {
|
||||
// allow callers to pass objects containing private data which
|
||||
// they don't want the JSON string to contain (so they don't
|
||||
// have to manually pre-process the object)
|
||||
if (aKeysToDrop && aKeysToDrop.indexOf(key) != -1)
|
||||
continue;
|
||||
|
||||
append_piece(key.toString());
|
||||
pieces.push(":");
|
||||
append_piece(aObj[key]);
|
||||
pieces.push(",");
|
||||
}
|
||||
if (pieces[pieces.length - 1] == ",")
|
||||
pieces.pop(); // drop the trailing colon
|
||||
pieces.push("}");
|
||||
}
|
||||
else {
|
||||
throw new TypeError("No JSON representation for this object!");
|
||||
}
|
||||
}
|
||||
append_piece(aJSObject);
|
||||
|
||||
return pieces.join("");
|
||||
},
|
||||
|
||||
/**
|
||||
* Converts a JSON string into a JavaScript object.
|
||||
*
|
||||
* @param aJSONString is the string to be converted
|
||||
* @return a JavaScript object for the given JSON representation
|
||||
*/
|
||||
fromString: function JSON_fromString(aJSONString) {
|
||||
if (!this.isMostlyHarmless(aJSONString))
|
||||
throw new SyntaxError("No valid JSON string!");
|
||||
|
||||
var s = new Components.utils.Sandbox("about:blank");
|
||||
return Components.utils.evalInSandbox("(" + aJSONString + ")", s);
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks whether the given string contains potentially harmful
|
||||
* content which might be executed during its evaluation
|
||||
* (no parser, thus not 100% safe! Best to use a Sandbox for evaluation)
|
||||
*
|
||||
* @param aString is the string to be tested
|
||||
* @return a boolean
|
||||
*/
|
||||
isMostlyHarmless: function JSON_isMostlyHarmless(aString) {
|
||||
const maybeHarmful = /[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/;
|
||||
const jsonStrings = /"(\\.|[^"\\\n\r])*"/g;
|
||||
|
||||
return !maybeHarmful.test(aString.replace(jsonStrings, ""));
|
||||
}
|
||||
};
|
|
@ -56,7 +56,7 @@ REQUIRES = xpcom \
|
|||
|
||||
CPPSRCS = mozJSComponentLoader.cpp mozJSSubScriptLoader.cpp
|
||||
|
||||
EXTRA_JS_MODULES = XPCOMUtils.jsm
|
||||
EXTRA_JS_MODULES = XPCOMUtils.jsm JSON.jsm
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
|
|
|
@ -0,0 +1,129 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is mozilla.org code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Simon Bünzli <zeniko@gmail.com>
|
||||
* Portions created by the Initial Developer are Copyright (C) 2007
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
function run_test() {
|
||||
// converts an object to a JSON string and tests its integrity
|
||||
function toJSONString(a) {
|
||||
var res = JSON.toString(a);
|
||||
if (!JSON.isMostlyHarmless(res))
|
||||
throw new SyntaxError("Invalid JSON string: " + res);
|
||||
return res;
|
||||
}
|
||||
|
||||
// ensures that an object can't be converted to a JSON string
|
||||
function isInvalidType(a) {
|
||||
try {
|
||||
JSON.toString(a);
|
||||
return false;
|
||||
} catch (ex) {
|
||||
return ex instanceof TypeError;
|
||||
}
|
||||
}
|
||||
// ensures that a string can't be converted back to a JavaScript object
|
||||
function isInvalidSyntax(a) {
|
||||
try {
|
||||
JSON.fromString(a);
|
||||
return false;
|
||||
} catch (ex) {
|
||||
return ex instanceof SyntaxError;
|
||||
}
|
||||
}
|
||||
|
||||
Components.utils.import("resource://gre/modules/JSON.jsm");
|
||||
do_check_eq(typeof(JSON), "object");
|
||||
|
||||
// some of the tests are adapted from /testing/mochitest/tests/test_Base.js
|
||||
do_check_eq(toJSONString(true), "true");
|
||||
do_check_eq(toJSONString(false), "false");
|
||||
|
||||
do_check_eq(toJSONString(1), "1");
|
||||
do_check_eq(toJSONString(1.23), "1.23");
|
||||
do_check_eq(toJSONString(1.23e-45), "1.23e-45");
|
||||
|
||||
do_check_true(isInvalidType(Infinity));
|
||||
do_check_true(isInvalidType(NaN));
|
||||
|
||||
do_check_eq(toJSONString("Foo-Bar \b\t\n\f\r\"\\ \x01€"),
|
||||
'"Foo-Bar \\b\\t\\n\\f\\r\\"\\\\ \\u0001\\u20ac"');
|
||||
|
||||
do_check_eq(toJSONString(null), "null");
|
||||
do_check_true(isInvalidType(undefined));
|
||||
|
||||
do_check_eq(toJSONString([1, "2", 3.3]), '[1,"2",3.3]');
|
||||
// duck-typed Array (since we'll never really get something instanceof Array)
|
||||
do_check_eq(toJSONString({ 0: 0, 1: "1", 2: -2.2, length: 3 }), '[0,"1",-2.2]');
|
||||
|
||||
var obj = { a: 1, b: "2", c: [-3e+30] };
|
||||
do_check_eq(toJSONString(obj), '{"a":1,"b":"2","c":[-3e+30]}');
|
||||
do_check_eq(JSON.toString(obj, ["b", "c"] /* keys to drop */), '{"a":1}');
|
||||
|
||||
do_check_true(isInvalidType(function() { }));
|
||||
|
||||
// make sure that toJSONString actually works...
|
||||
do_check_eq(toJSONString(obj), JSON.toString(obj));
|
||||
|
||||
do_check_eq(JSON.fromString("true"), true);
|
||||
do_check_eq(JSON.fromString("false"), false);
|
||||
do_check_eq(JSON.fromString("1"), 1);
|
||||
do_check_eq(JSON.fromString('"2.2"'), "2.2");
|
||||
do_check_eq(JSON.fromString("1.23e-45"), 1.23e-45);
|
||||
do_check_true(isInvalidSyntax("NaN"));
|
||||
do_check_eq(JSON.fromString('"Foo-Bar \\b\\t\\n\\f\\r\\"\\\\ \\u0001\\u20ac"'),
|
||||
"Foo-Bar \b\t\n\f\r\"\\ \x01€");
|
||||
do_check_true(isInvalidSyntax('"multi\nline"'));
|
||||
do_check_eq(JSON.fromString("null"), null);
|
||||
do_check_true(isInvalidSyntax("."));
|
||||
|
||||
var res = JSON.fromString('[1,"2",3.3]');
|
||||
do_check_eq(res.length, 3);
|
||||
do_check_eq(res[2], 3.3);
|
||||
// res is an instance of the sandbox's array
|
||||
do_check_false(res instanceof Array);
|
||||
|
||||
res = JSON.fromString(toJSONString(obj));
|
||||
do_check_eq(res.a, obj.a);
|
||||
do_check_eq(res.b, obj.b);
|
||||
do_check_eq(res.c.length, obj.c.length);
|
||||
do_check_eq(res.c[0], obj.c[0]);
|
||||
|
||||
// those would throw on JSON.fromString if there's no object |a|
|
||||
do_check_true(JSON.isMostlyHarmless("a"));
|
||||
do_check_true(JSON.isMostlyHarmless("a[0]"));
|
||||
do_check_true(JSON.isMostlyHarmless('a["alert(\\"P0wn3d!\\");"]'));
|
||||
|
||||
do_check_false(JSON.isMostlyHarmless('(function() { alert("P0wn3d!"); })()'));
|
||||
do_check_false(JSON.isMostlyHarmless('{ get a() { return "P0wn3d!"; } }'));
|
||||
}
|
Загрузка…
Ссылка в новой задаче