--- a/dom/public/idl/Makefile.in
+++ b/dom/public/idl/Makefile.in
@@ -53,16 +53,17 @@ DIRS = \
css \
traversal \
range \
xbl \
xpath \
ls \
xul \
storage \
+ json \
offline
ifdef MOZ_SVG
DIRS += svg
endif
include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/dom/public/idl/json/Makefile.in
@@ -0,0 +1,57 @@
+#
+# ***** 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 Mozilla Corporation.
+# Portions created by the Initial Developer are Copyright (C) 2007
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+# Robert Sayre <sayrer@gmail.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either of 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 *****
+
+DEPTH = ../../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE = dom
+XPIDL_MODULE = dom_json
+GRE_MODULE = 1
+
+XPIDLSRCS = \
+ nsIJSON.idl \
+ $(NULL)
+
+SDK_XPIDLSRCS = \
+ nsIJSON.idl \
+ $(NULL)
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/dom/public/idl/json/nsIJSON.idl
@@ -0,0 +1,66 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** 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
+ * Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Robert Sayre <sayrer@gmail.com>
+ *
+ * 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 ***** */
+
+#include "domstubs.idl"
+
+interface nsIInputStream;
+interface nsIOutputStream;
+interface nsIScriptGlobalObject;
+
+/**
+ * Encode and decode JSON text.
+ */
+[scriptable, uuid(45464c36-efde-4cb5-8e00-07480533ff35)]
+interface nsIJSON : nsISupports
+{
+ AString encode(/* in JSObject value,
+ /* [optional] in JSObject whitelist */);
+
+ void encodeToStream(in nsIOutputStream stream,
+ in string charset,
+ in boolean writeBOM
+ /* in JSObject value,
+ /* [optional] in JSObject optFilter */);
+
+ void /* JSObject */ decode(in AString str
+ /* , [optional] in JSObject whitelist */);
+
+ void /* JSObject */ decodeFromStream(in nsIInputStream stream,
+ in long contentLength
+ /*[optional] in JSObject optFilter */);
+};
--- a/dom/src/Makefile.in
+++ b/dom/src/Makefile.in
@@ -37,17 +37,17 @@
DEPTH = ../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
-DIRS = base jsurl events storage offline
+DIRS = base jsurl events storage offline json
include $(topsrcdir)/config/rules.mk
_FILES = \
$(srcdir)/res/hiddenWindow.html \
$(NULL)
libs::
new file mode 100644
--- /dev/null
+++ b/dom/src/json/Makefile.in
@@ -0,0 +1,80 @@
+#
+# ***** 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 Mozilla Corporation.
+# Portions created by the Initial Developer are Copyright (C) 2007
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+# Robert Sayre <sayrer@gmail.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either of 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 *****
+
+DEPTH = ../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE = dom
+LIBRARY_NAME = json_s
+LIBXUL_LIBRARY = 1
+
+REQUIRES = xpcom \
+ string \
+ content \
+ caps \
+ js \
+ locale \
+ layout \
+ necko \
+ pref \
+ uconv \
+ unicharutil \
+ widget \
+ xpconnect \
+ $(NULL)
+
+CPPSRCS = \
+ nsJSON.cpp \
+ $(NULL)
+
+FORCE_STATIC_LIB = 1
+
+LOCAL_INCLUDES = \
+ -I$(srcdir)/../base \
+ -I$(topsrcdir)/content/events/src
+
+DEFINES += -D_IMPL_NS_LAYOUT
+
+ifdef ENABLE_TESTS
+ DIRS += test
+endif
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/dom/src/json/nsJSON.cpp
@@ -0,0 +1,1245 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** 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
+ * Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Dave Camp <dcamp@mozilla.com>
+ * Robert Sayre <sayrer@gmail.com>
+ *
+ * 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 ***** */
+
+#include "jsapi.h"
+#include "jsdtoa.h"
+#include "jsnum.h"
+#include "jsbool.h"
+#include "jsarena.h"
+#include "jscntxt.h"
+#include "jsinterp.h"
+#include "jsiter.h"
+#include "jstypes.h"
+#include "nsIServiceManager.h"
+#include "nsJSON.h"
+#include "nsIXPConnect.h"
+#include "nsIXPCScriptable.h"
+#include "nsStreamUtils.h"
+#include "nsIInputStream.h"
+#include "nsStringStream.h"
+#include "nsICharsetConverterManager.h"
+#include "nsXPCOMStrings.h"
+#include "nsNetUtil.h"
+#include "nsContentUtils.h"
+#include "nsCRTGlue.h"
+#include "nsAutoPtr.h"
+
+static const char kXPConnectServiceCID[] = "@mozilla.org/js/xpc/XPConnect;1";
+
+NS_INTERFACE_MAP_BEGIN(nsJSON)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIJSON)
+ NS_INTERFACE_MAP_ENTRY(nsIJSON)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_ADDREF(nsJSON)
+NS_IMPL_RELEASE(nsJSON)
+
+nsJSON::nsJSON()
+{
+}
+
+nsJSON::~nsJSON()
+{
+}
+
+//
+// AString encode(in JSObject value, [optional] in JSObject whitelist);
+//
+NS_IMETHODIMP
+nsJSON::Encode(nsAString &aJSON)
+{
+ // This function should only be called from JS.
+ nsresult rv;
+
+ nsAutoPtr<nsJSONWriter> writer(new nsJSONWriter());
+ if (!writer)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ rv = EncodeInternal(writer);
+
+ // FIXME: bug 408838. Get exception types sorted out
+ if (NS_SUCCEEDED(rv) || rv == NS_ERROR_INVALID_ARG) {
+ rv = NS_OK;
+ // if we didn't consume anything, it's not JSON, so return null
+ if (writer->mBuffer.IsEmpty()) {
+ aJSON.Truncate();
+ aJSON.SetIsVoid(PR_TRUE);
+ } else {
+ aJSON.Append(writer->mBuffer);
+ }
+ }
+
+ return rv;
+}
+
+static const char UTF8BOM[] = "\xEF\xBB\xBF";
+static const char UTF16LEBOM[] = "\xFF\xFE";
+static const char UTF16BEBOM[] = "\xFE\xFF";
+static const char UTF32LEBOM[] = "\xFF\xFE\0\0";
+static const char UTF32BEBOM[] = "\0\0\xFE\xFF";
+
+static nsresult CheckCharset(const char* aCharset)
+{
+ // Check that the charset is permissible
+ if (!(strcmp(aCharset, "UTF-8") == 0 ||
+ strcmp(aCharset, "UTF-16LE") == 0 ||
+ strcmp(aCharset, "UTF-16BE") == 0 ||
+ strcmp(aCharset, "UTF-32LE") == 0 ||
+ strcmp(aCharset, "UTF-32BE") == 0)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ return NS_OK;
+}
+
+//
+// void EncodeToStream(in nsIOutputStream stream
+// /* in JSObject value,
+// /* [optional] in JSObject whitelist */);
+//
+NS_IMETHODIMP
+nsJSON::EncodeToStream(nsIOutputStream *aStream,
+ const char* aCharset,
+ const PRBool aWriteBOM)
+{
+ // This function should only be called from JS.
+ NS_ENSURE_ARG(aStream);
+ nsresult rv;
+
+ rv = CheckCharset(aCharset);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Check to see if we have a buffered stream
+ nsCOMPtr<nsIOutputStream> bufferedStream;
+ // FIXME: bug 408514.
+ // NS_OutputStreamIsBuffered(aStream) asserts on file streams...
+ //if (!NS_OutputStreamIsBuffered(aStream)) {
+ rv = NS_NewBufferedOutputStream(getter_AddRefs(bufferedStream),
+ aStream, 4096);
+ NS_ENSURE_SUCCESS(rv, rv);
+ // aStream = bufferedStream;
+ //}
+
+ PRUint32 ignored;
+ if (aWriteBOM) {
+ if (strcmp(aCharset, "UTF-8") == 0)
+ rv = aStream->Write(UTF8BOM, 3, &ignored);
+ else if (strcmp(aCharset, "UTF-16LE") == 0)
+ rv = aStream->Write(UTF16LEBOM, 2, &ignored);
+ else if (strcmp(aCharset, "UTF-16BE") == 0)
+ rv = aStream->Write(UTF16BEBOM, 2, &ignored);
+ else if (strcmp(aCharset, "UTF-32LE") == 0)
+ rv = aStream->Write(UTF32LEBOM, 4, &ignored);
+ else if (strcmp(aCharset, "UTF-32BE") == 0)
+ rv = aStream->Write(UTF32BEBOM, 4, &ignored);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ nsAutoPtr<nsJSONWriter> writer(new nsJSONWriter(bufferedStream));
+ if (!writer)
+ return NS_ERROR_OUT_OF_MEMORY;
+ rv = writer->SetCharset(aCharset);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = EncodeInternal(writer);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = bufferedStream->Flush();
+
+ return rv;
+}
+
+nsresult
+nsJSON::EncodeInternal(nsJSONWriter *writer)
+{
+ nsresult rv;
+ nsIXPConnect *xpc = nsContentUtils::XPConnect();
+ if (!xpc)
+ return NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIXPCNativeCallContext> cc;
+ rv = xpc->GetCurrentNativeCallContext(getter_AddRefs(cc));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ JSContext *cx = nsnull;
+ rv = cc->GetJSContext(&cx);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ JSAutoRequest ar(cx);
+
+ PRUint32 argc = 0;
+ rv = cc->GetArgc(&argc);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Now fish for the JS arguments. If it's a call to encode, we'll
+ // want the first two arguments. If it's a call to encodeToStream,
+ // we'll want the fourth and fifth;
+ PRUint32 firstArg = writer->mStream ? 3 : 0;
+
+ // Get the object we're going to serialize.
+ JSObject *inputObj = nsnull;
+ jsval *argv = nsnull;
+ rv = cc->GetArgvPtr(&argv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (argc <= firstArg ||
+ !(JSVAL_IS_OBJECT(argv[firstArg]) &&
+ (inputObj = JSVAL_TO_OBJECT(argv[firstArg])))) {
+ // return if it's not something we can deal with
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ JSObject *whitelist = nsnull;
+
+ // If there's a second argument here, it should be an array.
+ if (argc >= firstArg + 2 &&
+ !(JSVAL_IS_OBJECT(argv[firstArg + 1]) &&
+ (whitelist = JSVAL_TO_OBJECT(argv[firstArg + 1])) &&
+ JS_IsArrayObject(cx, whitelist))) {
+ whitelist = nsnull; // bogus whitelists are ignored
+ }
+
+ jsval *vp = &argv[firstArg];
+
+ JSBool ok = ToJSON(cx, vp);
+ JSType type;
+ if (!(ok && !JSVAL_IS_PRIMITIVE(*vp) &&
+ (type = JS_TypeOfValue(cx, *vp)) != JSTYPE_FUNCTION &&
+ type != JSTYPE_XML)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ return EncodeObject(cx, vp, writer, whitelist, 0);
+}
+
+// N.B: vp must be rooted.
+nsresult
+nsJSON::EncodeObject(JSContext *cx, jsval *vp, nsJSONWriter *writer,
+ JSObject *whitelist, PRUint32 depth)
+{
+ NS_ENSURE_ARG(vp);
+ NS_ENSURE_ARG(writer);
+
+ if (depth > JSON_MAX_DEPTH) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsresult rv;
+ JSObject *obj = JSVAL_TO_OBJECT(*vp);
+ JSBool isArray = JS_IsArrayObject(cx, obj);
+ PRUnichar output = PRUnichar(isArray ? '[' : '{');
+ rv = writer->Write(&output, 1);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ JSBool ok = JS_TRUE;
+
+ ok = js_ValueToIterator(cx, JSITER_ENUMERATE, vp);
+ if (!ok)
+ return NS_ERROR_FAILURE;
+
+ JSObject *iterObj = JSVAL_TO_OBJECT(*vp);
+
+ jsval outputValue = JSVAL_VOID;
+ JSAutoTempValueRooter tvr(cx, 1, &outputValue);
+
+ jsval key;
+ PRBool memberWritten = PR_FALSE;
+ do {
+ outputValue = JSVAL_VOID;
+ ok = js_CallIteratorNext(cx, iterObj, &key);
+
+ if (!ok)
+ break;
+
+ if (key == JSVAL_HOLE)
+ break;
+
+ JSString *ks;
+ if (JSVAL_IS_STRING(key)) {
+ ks = JSVAL_TO_STRING(key);
+ } else {
+ ks = JS_ValueToString(cx, key);
+ if (!ks) {
+ ok = JS_FALSE;
+ break;
+ }
+ }
+
+ ok = JS_GetUCProperty(cx, obj, JS_GetStringChars(ks),
+ JS_GetStringLength(ks), &outputValue);
+ if (!ok)
+ break;
+
+ // if this is an array, holes are transmitted as null
+ if (isArray && outputValue == JSVAL_VOID) {
+ outputValue = JSVAL_NULL;
+ } else if (JSVAL_IS_OBJECT(outputValue)) {
+ ok = ToJSON(cx, &outputValue);
+ if (!ok)
+ break;
+ }
+
+ // elide undefined values
+ if (outputValue == JSVAL_VOID)
+ continue;
+
+ // output a comma unless this is the first member to write
+ if (memberWritten) {
+ output = PRUnichar(',');
+ rv = writer->Write(&output, 1);
+ }
+ memberWritten = PR_TRUE;
+
+ JSType type = JS_TypeOfValue(cx, outputValue);
+
+ // Can't encode these types, so drop them
+ if (type == JSTYPE_FUNCTION || type == JSTYPE_XML)
+ break;
+
+ // Be careful below, this string is weakly rooted.
+ JSString *s;
+
+ // If this isn't an array, we need to output a key
+ if (!isArray) {
+ nsAutoString keyOutput;
+ s = JS_ValueToString(cx, key);
+ if (!s) {
+ ok = JS_FALSE;
+ break;
+ }
+
+ rv = writer->WriteString((PRUnichar*)JS_GetStringChars(s),
+ JS_GetStringLength(s));
+ if (NS_FAILED(rv))
+ break;
+ output = PRUnichar(':');
+ rv = writer->Write(&output, 1);
+ if (NS_FAILED(rv))
+ break;
+ }
+
+ if (!JSVAL_IS_PRIMITIVE(outputValue)) {
+ // recurse
+ rv = EncodeObject(cx, &outputValue, writer, whitelist, depth + 1);
+ if (NS_FAILED(rv))
+ break;
+ } else {
+ nsAutoString valueOutput;
+ s = JS_ValueToString(cx, outputValue);
+ if (!s) {
+ ok = JS_FALSE;
+ break;
+ }
+
+ if (type == JSTYPE_STRING) {
+ rv = writer->WriteString((PRUnichar*)JS_GetStringChars(s),
+ JS_GetStringLength(s));
+ continue;
+ }
+
+ if (type == JSTYPE_NUMBER) {
+ if (JSVAL_IS_DOUBLE(outputValue)) {
+ jsdouble d = *JSVAL_TO_DOUBLE(outputValue);
+ if (!JSDOUBLE_IS_FINITE(d))
+ valueOutput.Append(NS_LITERAL_STRING("null"));
+ else
+ valueOutput.Append((PRUnichar*)JS_GetStringChars(s));
+ } else {
+ valueOutput.Append((PRUnichar*)JS_GetStringChars(s));
+ }
+ } else if (type == JSTYPE_BOOLEAN) {
+ valueOutput.Append((PRUnichar*)JS_GetStringChars(s));
+ } else if (JSVAL_IS_NULL(outputValue)) {
+ valueOutput.Append(NS_LITERAL_STRING("null"));
+ } else {
+ rv = NS_ERROR_FAILURE; // encoding error
+ break;
+ }
+
+ rv = writer->Write(valueOutput.get(), valueOutput.Length());
+ }
+
+ } while (NS_SUCCEEDED(rv));
+
+ // Always close the iterator, but make sure not to stomp on OK
+ ok &= js_CloseIterator(cx, *vp);
+
+ if (!ok)
+ rv = NS_ERROR_FAILURE; // encoding error or propagate? FIXME: Bug 408838.
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ output = PRUnichar(isArray ? ']' : '}');
+ rv = writer->Write(&output, 1);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return rv;
+}
+
+JSBool
+nsJSON::ToJSON(JSContext *cx, jsval *vp)
+{
+ // Now we check to see whether the return value implements toJSON()
+ JSBool ok = JS_TRUE;
+ char *toJSON = "toJSON";
+
+ if (!JSVAL_IS_PRIMITIVE(*vp)) {
+ JSObject *obj = JSVAL_TO_OBJECT(*vp);
+ jsval toJSONVal = nsnull;
+ ok = JS_GetProperty(cx, obj, toJSON, &toJSONVal);
+ if (ok && JS_TypeOfValue(cx, toJSONVal) == JSTYPE_FUNCTION) {
+ ok = JS_CallFunctionValue(cx, obj, toJSONVal, 0, nsnull, vp);
+ }
+ }
+
+ return ok;
+}
+
+nsJSONWriter::nsJSONWriter() : mStream(nsnull), mEncoder(nsnull)
+{
+}
+
+nsJSONWriter::nsJSONWriter(nsIOutputStream *aStream) : mStream(aStream),
+ mEncoder(nsnull)
+{
+}
+
+nsJSONWriter::~nsJSONWriter()
+{
+}
+
+nsresult
+nsJSONWriter::SetCharset(const char* aCharset)
+{
+ nsresult rv = NS_OK;
+ if (mStream) {
+ nsCOMPtr<nsICharsetConverterManager> ccm =
+ do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = ccm->GetUnicodeEncoder(aCharset, getter_AddRefs(mEncoder));
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = mEncoder->SetOutputErrorBehavior(nsIUnicodeEncoder::kOnError_Signal,
+ nsnull, nsnull);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return rv;
+}
+
+static const PRUnichar quote = PRUnichar('"');
+static const PRUnichar backslash = PRUnichar('\\');
+static const PRUnichar unicodeEscape[] = {'\\', 'u', '0', '0', '\0'};
+
+nsresult
+nsJSONWriter::WriteString(const PRUnichar *aBuffer, PRUint32 aLength)
+{
+ nsresult rv;
+ rv = Write("e, 1);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ PRUint32 mark = 0;
+ PRUint32 i;
+ for (i = 0; i < aLength; ++i) {
+ if (aBuffer[i] == quote || aBuffer[i] == backslash) {
+ rv = Write(&aBuffer[mark], i - mark);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = Write(&backslash, 1);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = Write(&aBuffer[i], 1);
+ NS_ENSURE_SUCCESS(rv, rv);
+ mark = i + 1;
+ } else if (aBuffer[i] <= 31 || aBuffer[i] == 127) {
+ rv = Write(&aBuffer[mark], i - mark);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsAutoString unicode;
+ unicode.Append(unicodeEscape);
+ nsAutoString charCode;
+ charCode.AppendInt(aBuffer[i], 16);
+ if (charCode.Length() == 1)
+ unicode.Append('0');
+ unicode.Append(charCode);
+ rv = Write(unicode.get(), unicode.Length());
+ NS_ENSURE_SUCCESS(rv, rv);
+ mark = i + 1;
+ }
+ }
+
+ if (mark < aLength) {
+ rv = Write(&aBuffer[mark], aLength - mark);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ rv = Write("e, 1);
+ NS_ENSURE_SUCCESS(rv, rv);
+ return rv;
+}
+
+nsresult
+nsJSONWriter::Write(const PRUnichar *aBuffer, PRUint32 aLength)
+{
+ nsresult rv = NS_OK;
+ if (mStream) {
+ rv = WriteToStream(mStream, mEncoder, aBuffer, aLength);
+ } else {
+ mBuffer.Append(aBuffer, aLength);
+ }
+
+ return rv;
+}
+
+nsresult
+nsJSONWriter::WriteToStream(nsIOutputStream *aStream,
+ nsIUnicodeEncoder *encoder,
+ const PRUnichar *aBuffer,
+ PRUint32 aLength)
+{
+ nsresult rv;
+ PRInt32 srcLength = aLength;
+ PRUint32 bytesWritten;
+
+ // The bytes written to the stream might differ from the PRUnichar size
+ PRInt32 aDestLength;
+ rv = encoder->GetMaxLength(aBuffer, srcLength, &aDestLength);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // create the buffer we need
+ char* destBuf = (char *) NS_Alloc(aDestLength);
+ if (!destBuf)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ rv = encoder->Convert(aBuffer, &srcLength, destBuf, &aDestLength);
+ if (NS_SUCCEEDED(rv))
+ rv = aStream->Write(destBuf, aDestLength, &bytesWritten);
+
+ NS_Free(destBuf);
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsJSON::Decode(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);
+}
+
+NS_IMETHODIMP
+nsJSON::DecodeFromStream(nsIInputStream *aStream, PRInt32 aContentLength)
+{
+ return DecodeInternal(aStream, aContentLength, PR_TRUE);
+}
+
+nsresult
+nsJSON::DecodeInternal(nsIInputStream *aStream,
+ PRInt32 aContentLength,
+ PRBool aNeedsConverter)
+{
+ nsresult rv;
+ nsIXPConnect *xpc = nsContentUtils::XPConnect();
+ if (!xpc)
+ return NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIXPCNativeCallContext> cc;
+ rv = xpc->GetCurrentNativeCallContext(getter_AddRefs(cc));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ jsval *retvalPtr;
+ rv = cc->GetRetValPtr(&retvalPtr);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ JSContext *cx = nsnull;
+ rv = cc->GetJSContext(&cx);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ JSAutoRequest ar(cx);
+
+ // Consume the stream
+ nsCOMPtr<nsIChannel> jsonChannel;
+ nsCOMPtr<nsIURI> uri;
+ NS_NewURI(getter_AddRefs(uri), NS_LITERAL_CSTRING("about:blank"), 0, 0 );
+ if (!uri)
+ return NS_ERROR_OUT_OF_MEMORY;
+ rv = NS_NewInputStreamChannel(getter_AddRefs(jsonChannel), uri, aStream,
+ NS_LITERAL_CSTRING("application/json"));
+ if (!jsonChannel || NS_FAILED(rv))
+ return NS_ERROR_FAILURE;
+
+ nsRefPtr<nsJSONListener>
+ jsonListener(new nsJSONListener(cx, retvalPtr, aNeedsConverter));
+
+ if (!jsonListener)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ //XXX this stream pattern should be consolidated in netwerk
+ rv = jsonListener->OnStartRequest(jsonChannel, nsnull);
+ if (NS_FAILED(rv)) {
+ jsonChannel->Cancel(rv);
+ return rv;
+ }
+
+ nsresult status;
+ jsonChannel->GetStatus(&status);
+ PRUint32 offset = 0;
+ while (NS_SUCCEEDED(status)) {
+ PRUint32 available;
+ rv = aStream->Available(&available);
+ if (rv == NS_BASE_STREAM_CLOSED) {
+ rv = NS_OK;
+ break;
+ }
+ if (NS_FAILED(rv)) {
+ jsonChannel->Cancel(rv);
+ break;
+ }
+ if (!available)
+ break; // blocking input stream has none available when done
+
+ rv = jsonListener->OnDataAvailable(jsonChannel, nsnull,
+ aStream, offset, available);
+ if (NS_FAILED(rv)) {
+ jsonChannel->Cancel(rv);
+ break;
+ }
+
+ offset += available;
+ jsonChannel->GetStatus(&status);
+ }
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = jsonListener->OnStopRequest(jsonChannel, nsnull, status);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = cc->SetReturnValueWasSet(PR_TRUE);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+NS_NewJSON(nsISupports* aOuter, REFNSIID aIID, void** aResult)
+{
+ nsJSON* json = new nsJSON();
+ if (!json)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(json);
+ *aResult = json;
+
+ return NS_OK;
+}
+
+
+JS_STATIC_DLL_CALLBACK(void)
+trace_json_stack(JSTracer *trc, JSTempValueRooter *tvr)
+{
+ nsJSONObjectStack *tmp = static_cast<nsJSONObjectStack *>(tvr);
+
+ for (PRUint32 i = 0; i < tmp->Length(); ++i) {
+ JS_CALL_OBJECT_TRACER(trc, tmp->ElementAt(i),
+ "JSON decoder stack member");
+ }
+}
+
+nsJSONListener::nsJSONListener(JSContext *cx, jsval *rootVal,
+ PRBool needsConverter)
+ : mLineNum(0),
+ mColumn(0),
+ mHexChar(0),
+ mNumHex(0),
+ mCx(cx),
+ mRootVal(rootVal),
+ mNeedsConverter(needsConverter),
+ mStatep(mStateStack)
+{
+ NS_ASSERTION(mCx, "Must have a JSContext");
+ *mStatep = JSON_PARSE_STATE_INIT;
+ JS_PUSH_TEMP_ROOT_TRACE(cx, trace_json_stack, &mObjectStack);
+}
+
+nsJSONListener::~nsJSONListener()
+{
+ JS_POP_TEMP_ROOT(mCx, &mObjectStack);
+}
+
+NS_INTERFACE_MAP_BEGIN(nsJSONListener)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsJSONListener)
+ NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
+ NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_ADDREF(nsJSONListener)
+NS_IMPL_RELEASE(nsJSONListener)
+
+NS_IMETHODIMP
+nsJSONListener::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext)
+{
+ mHexChar = 0;
+ mNumHex = 0;
+ mSniffBuffer.Truncate();
+ mDecoder = nsnull;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsJSONListener::OnStopRequest(nsIRequest *aRequest, nsISupports *aContext,
+ nsresult aStatusCode)
+{
+ nsresult rv;
+
+ // This can happen with short UTF-8 messages
+ if (!mSniffBuffer.IsEmpty()) {
+ rv = ProcessBytes(mSniffBuffer.get(), mSniffBuffer.Length());
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ if (!mObjectStack.IsEmpty() || *mStatep != JSON_PARSE_STATE_FINISHED)
+ return NS_ERROR_FAILURE;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsJSONListener::OnDataAvailable(nsIRequest *aRequest, nsISupports *aContext,
+ nsIInputStream *aStream,
+ PRUint32 aOffset, PRUint32 aLength)
+{
+ PRUint32 contentLength;
+ aStream->Available(&contentLength);
+ nsresult rv;
+
+ if (mNeedsConverter && mSniffBuffer.Length() < 4) {
+ PRUint32 readCount = (aLength < 4) ? aLength : 4;
+ rv = NS_ConsumeStream(aStream, readCount, mSniffBuffer);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (mSniffBuffer.Length() < 4)
+ return NS_OK;
+ }
+
+ char buffer[JSON_PARSER_BUFSIZE];
+ unsigned long bytesRemaining = aLength - mSniffBuffer.Length();
+ while (bytesRemaining) {
+ unsigned int bytesRead;
+ rv = aStream->Read(buffer,
+ PR_MIN(sizeof(buffer), bytesRemaining),
+ &bytesRead);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = ProcessBytes(buffer, bytesRead);
+ NS_ENSURE_SUCCESS(rv, rv);
+ bytesRemaining -= bytesRead;
+ }
+
+ return rv;
+}
+
+nsresult
+nsJSONListener::ProcessBytes(const char* aBuffer, PRUint32 aByteLength)
+{
+ nsresult rv;
+ // Check for BOM, or sniff charset
+ nsCAutoString charset;
+ if (mNeedsConverter && !mDecoder) {
+ if (!nsContentUtils::CheckForBOM((const unsigned char*) mSniffBuffer.get(),
+ mSniffBuffer.Length(), charset)) {
+ // OK, found no BOM, sniff the first character to see what this is
+ // See section 3 of RFC4627 for details on why this works.
+ const char *buffer = mSniffBuffer.get();
+ if (mSniffBuffer.Length() >= 4) {
+ if (buffer[0] == 0x00 && buffer[1] == 0x00 &&
+ buffer[2] == 0x00 && buffer[3] != 0x00) {
+ charset = "UTF-32BE";
+ } else if (buffer[0] == 0x00 && buffer[1] != 0x00 &&
+ buffer[2] == 0x00 && buffer[3] != 0x00) {
+ charset = "UTF-16BE";
+ } else if (buffer[0] != 0x00 && buffer[1] == 0x00 &&
+ buffer[2] == 0x00 && buffer[3] == 0x00) {
+ charset = "UTF-32LE";
+ } else if (buffer[0] != 0x00 && buffer[1] == 0x00 &&
+ buffer[2] != 0x00 && buffer[3] == 0x00) {
+ charset = "UTF-16LE";
+ } else if (buffer[0] != 0x00 && buffer[1] != 0x00 &&
+ buffer[2] != 0x00 && buffer[3] != 0x00) {
+ charset = "UTF-8";
+ }
+ }
+ }
+
+ // We should have a unicode charset by now
+ rv = CheckCharset(charset.get());
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsICharsetConverterManager> ccm =
+ do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = ccm->GetUnicodeDecoderRaw(charset.get(), getter_AddRefs(mDecoder));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // consume the sniffed bytes
+ rv = ConsumeConverted(mSniffBuffer.get(), mSniffBuffer.Length());
+ NS_ENSURE_SUCCESS(rv, rv);
+ mSniffBuffer.Truncate();
+ }
+
+ if (mNeedsConverter) {
+ rv = ConsumeConverted(aBuffer, aByteLength);
+ } else {
+ PRUint32 unichars = aByteLength / sizeof(PRUnichar);
+ rv = Consume((PRUnichar *) aBuffer, unichars);
+ }
+
+ return rv;
+}
+
+nsresult
+nsJSONListener::ConsumeConverted(const char* aBuffer, PRUint32 aByteLength)
+{
+ nsresult rv;
+ PRInt32 unicharLength = 0;
+ PRInt32 srcLen = aByteLength;
+
+ rv = mDecoder->GetMaxLength(aBuffer, srcLen, &unicharLength);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsAutoArrayPtr<PRUnichar> ustr(new PRUnichar[unicharLength]);
+ NS_ENSURE_TRUE(ustr, NS_ERROR_OUT_OF_MEMORY);
+ rv = mDecoder->Convert(aBuffer, &srcLen, ustr, &unicharLength);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = Consume(ustr.get(), unicharLength);
+
+ return rv;
+}
+
+nsresult
+nsJSONListener::PopState()
+{
+ mStatep--;
+ if (mStatep < mStateStack) {
+ mStatep = mStateStack;
+ return NS_ERROR_FAILURE;
+ } else if (*mStatep == JSON_PARSE_STATE_INIT) {
+ *mStatep = JSON_PARSE_STATE_FINISHED;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsJSONListener::PushState(JSONParserState state)
+{
+ if (*mStatep == JSON_PARSE_STATE_FINISHED)
+ return NS_ERROR_FAILURE; // extra input
+
+ mStatep++;
+ if ((uint32)(mStatep - mStateStack) >= JS_ARRAY_LENGTH(mStateStack))
+ return NS_ERROR_FAILURE; // too deep
+
+ *mStatep = state;
+
+ return NS_OK;
+}
+
+nsresult
+nsJSONListener::Consume(const PRUnichar *data, PRUint32 len)
+{
+ nsresult rv;
+ nsString numchars = NS_LITERAL_STRING("-+0123456789eE.");
+ PRUint32 i;
+
+ if (*mStatep == JSON_PARSE_STATE_INIT) {
+ PushState(JSON_PARSE_STATE_VALUE);
+ }
+
+ for (i = 0; i < len; i++) {
+ PRUnichar c = data[i];
+ if (c == '\n') {
+ mLineNum++;
+ mColumn = 0;
+ } else {
+ mColumn++;
+ }
+
+ switch (*mStatep) {
+ case JSON_PARSE_STATE_VALUE :
+ if (c == '{') {
+ *mStatep = JSON_PARSE_STATE_OBJECT;
+ rv = this->OpenObject();
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = PushState(JSON_PARSE_STATE_OBJECT_PAIR);
+ NS_ENSURE_SUCCESS(rv, rv);
+ } else if (c == '[') {
+ *mStatep = JSON_PARSE_STATE_ARRAY;
+ rv = this->OpenArray();
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = PushState(JSON_PARSE_STATE_VALUE);
+ NS_ENSURE_SUCCESS(rv, rv);
+ } else if (c == ']') {
+ // empty array
+ rv = PopState();
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (*mStatep != JSON_PARSE_STATE_ARRAY) {
+ return NS_ERROR_FAILURE; // unexpected char
+ }
+ rv = this->CloseArray();
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = PopState();
+ NS_ENSURE_SUCCESS(rv, rv);
+ } else if (c == '}') {
+ // we should only find these in OBJECT_KEY state
+ return NS_ERROR_FAILURE; // unexpected failure
+ } else if (c == '"') {
+ *mStatep = JSON_PARSE_STATE_STRING;
+ } else if (numchars.FindChar(c) >= 0) {
+ *mStatep = JSON_PARSE_STATE_NUMBER;
+ mStringBuffer.Append(c);
+ } else if (NS_IsAsciiAlpha(c)) {
+ *mStatep = JSON_PARSE_STATE_KEYWORD;
+ mStringBuffer.Append(c);
+ } else if (!NS_IsAsciiWhitespace(c)) {
+ return NS_ERROR_FAILURE; // unexpected
+ }
+ break;
+ case JSON_PARSE_STATE_OBJECT :
+ if (c == '}') {
+ rv = this->CloseObject();
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = PopState();
+ NS_ENSURE_SUCCESS(rv, rv);
+ } else if (c == ',') {
+ rv = PushState(JSON_PARSE_STATE_OBJECT_PAIR);
+ NS_ENSURE_SUCCESS(rv, rv);
+ } else if (c == ']') {
+ return NS_ERROR_FAILURE; // unexpected
+ } else if (!NS_IsAsciiWhitespace(c)) {
+ return NS_ERROR_FAILURE; // unexpected
+ }
+ break;
+ case JSON_PARSE_STATE_ARRAY :
+ if (c == ']') {
+ rv = this->CloseArray();
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = PopState();
+ NS_ENSURE_SUCCESS(rv, rv);
+ } else if (c == ',') {
+ rv = PushState(JSON_PARSE_STATE_VALUE);
+ NS_ENSURE_SUCCESS(rv, rv);
+ } else if (!NS_IsAsciiWhitespace(c)) {
+ return NS_ERROR_FAILURE; // unexpected
+ }
+ break;
+ case JSON_PARSE_STATE_OBJECT_PAIR :
+ if (c == '"') {
+ // we want to be waiting for a : when the string has been read
+ *mStatep = JSON_PARSE_STATE_OBJECT_IN_PAIR;
+ PushState(JSON_PARSE_STATE_STRING);
+ } else if (c == '}') {
+ rv = this->CloseObject();
+ NS_ENSURE_SUCCESS(rv, rv);
+ // pop off the object_pair state
+ rv = PopState();
+ NS_ENSURE_SUCCESS(rv, rv);
+ // pop off the object state
+ rv = PopState();
+ NS_ENSURE_SUCCESS(rv, rv);
+ } else if (c == ']' || !NS_IsAsciiWhitespace(c)) {
+ return NS_ERROR_FAILURE; // unexpected
+ }
+ break;
+ case JSON_PARSE_STATE_OBJECT_IN_PAIR:
+ if (c == ':') {
+ *mStatep = JSON_PARSE_STATE_VALUE;
+ } else if (!NS_IsAsciiWhitespace(c)) {
+ return NS_ERROR_FAILURE; // unexpected
+ }
+ break;
+ case JSON_PARSE_STATE_STRING:
+ if (c == '"') {
+ rv = PopState();
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = HandleString();
+ NS_ENSURE_SUCCESS(rv, rv);
+ } else if (c == '\\') {
+ *mStatep = JSON_PARSE_STATE_STRING_ESCAPE;
+ } else {
+ mStringBuffer.Append(c);
+ }
+ break;
+ case JSON_PARSE_STATE_STRING_ESCAPE:
+ switch(c) {
+ case '"':
+ case '\\':
+ case '/':
+ break;
+ case 'b' : c = '\b'; break;
+ case 'f' : c = '\f'; break;
+ case 'n' : c = '\n'; break;
+ case 'r' : c = '\r'; break;
+ case 't' : c = '\t'; break;
+ default :
+ if (c == 'u') {
+ mNumHex = 0;
+ mHexChar = 0;
+ *mStatep = JSON_PARSE_STATE_STRING_HEX;
+ continue;
+ } else {
+ return NS_ERROR_FAILURE; // unexpected
+ }
+ }
+
+ mStringBuffer.Append(c);
+ *mStatep = JSON_PARSE_STATE_STRING;
+ break;
+ case JSON_PARSE_STATE_STRING_HEX:
+ if (('0' <= c) && (c <= '9')) {
+ mHexChar = (mHexChar << 4) | (c - '0');
+ } else if (('a' <= c) && (c <= 'f')) {
+ mHexChar = (mHexChar << 4) | (c - 'a' + 0x0a);
+ } else if (('A' <= c) && (c <= 'F')) {
+ mHexChar = (mHexChar << 4) | (c - 'A' + 0x0a);
+ } else {
+ return NS_ERROR_FAILURE; // unexpected
+ }
+
+ if (++(mNumHex) == 4) {
+ mStringBuffer.Append(mHexChar);
+ mHexChar = 0;
+ mNumHex = 0;
+ *mStatep = JSON_PARSE_STATE_STRING;
+ }
+ break;
+ case JSON_PARSE_STATE_KEYWORD:
+ if (NS_IsAsciiAlpha(c)) {
+ mStringBuffer.Append(c);
+ } else {
+ // this character isn't part of the keyword, process it again
+ i--;
+ rv = PopState();
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = HandleKeyword();
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ break;
+ case JSON_PARSE_STATE_NUMBER:
+ if (numchars.FindChar(c) >= 0) {
+ mStringBuffer.Append(c);
+ } else {
+ // this character isn't part of the number, process it again
+ i--;
+ rv = PopState();
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = HandleNumber();
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ break;
+ case JSON_PARSE_STATE_FINISHED:
+ if (!NS_IsAsciiWhitespace(c)) {
+ return NS_ERROR_FAILURE; // extra input
+ }
+ break;
+ default:
+ NS_NOTREACHED("Invalid JSON parser state");
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsJSONListener::PushValue(JSObject *aParent, jsval aValue)
+{
+ JSAutoTempValueRooter tvr(mCx, 1, &aValue);
+
+ JSBool ok;
+ if (JS_IsArrayObject(mCx, aParent)) {
+ jsuint len;
+ ok = JS_GetArrayLength(mCx, aParent, &len);
+ if (ok) {
+ ok = JS_SetElement(mCx, aParent, len, &aValue);
+ }
+ } else {
+ ok = JS_DefineUCProperty(mCx, aParent, (jschar *) mObjectKey.get(),
+ mObjectKey.Length(), aValue,
+ NULL, NULL, JSPROP_ENUMERATE);
+ }
+
+ return ok ? NS_OK : NS_ERROR_FAILURE;
+}
+
+nsresult
+nsJSONListener::PushObject(JSObject *aObj)
+{
+ if (mObjectStack.Length() >= JSON_MAX_DEPTH)
+ return NS_ERROR_FAILURE; // decoding error
+
+ // Check if this is the root object
+ if (mObjectStack.IsEmpty()) {
+ *mRootVal = OBJECT_TO_JSVAL(aObj);
+ if (!mObjectStack.AppendElement(aObj))
+ return NS_ERROR_OUT_OF_MEMORY;
+ return NS_OK;
+ }
+
+ nsresult rv;
+ JSObject *parent = mObjectStack.ElementAt(mObjectStack.Length() - 1);
+ rv = PushValue(parent, OBJECT_TO_JSVAL(aObj));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!mObjectStack.AppendElement(aObj))
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ return rv;
+}
+
+nsresult
+nsJSONListener::OpenObject()
+{
+ if (*mStatep != JSON_PARSE_STATE_VALUE &&
+ *mStatep != JSON_PARSE_STATE_OBJECT) {
+ return NS_ERROR_FAILURE;
+ }
+
+ JSObject *obj = JS_NewObject(mCx, NULL, NULL, NULL);
+ if (!obj)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ return PushObject(obj);
+}
+
+nsresult
+nsJSONListener::OpenArray()
+{
+ if (*mStatep != JSON_PARSE_STATE_VALUE &&
+ *mStatep != JSON_PARSE_STATE_ARRAY) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Add an array to an existing array or object
+ JSObject *arr = JS_NewArrayObject(mCx, 0, NULL);
+ if (!arr)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ return PushObject(arr);
+}
+
+nsresult
+nsJSONListener::CloseObject()
+{
+ if (!mObjectStack.SetLength(mObjectStack.Length() - 1))
+ return NS_ERROR_FAILURE;
+
+ return NS_OK;
+}
+
+nsresult
+nsJSONListener::CloseArray()
+{
+ return this->CloseObject();
+}
+
+nsresult
+nsJSONListener::HandleString()
+{
+ nsresult rv = NS_OK;
+ if (*mStatep == JSON_PARSE_STATE_OBJECT_IN_PAIR) {
+ mObjectKey = mStringBuffer;
+ } else {
+ JSObject *obj = mObjectStack.ElementAt(mObjectStack.Length() - 1);
+ JSString *str = JS_NewUCStringCopyN(mCx, (jschar *) mStringBuffer.get(),
+ mStringBuffer.Length());
+ if (!str)
+ return NS_ERROR_OUT_OF_MEMORY;
+ rv = PushValue(obj, STRING_TO_JSVAL(str));
+ }
+
+ mStringBuffer.Truncate();
+ return rv;
+}
+
+nsresult
+nsJSONListener::HandleNumber()
+{
+ nsresult rv;
+ JSObject *obj = mObjectStack.ElementAt(mObjectStack.Length() - 1);
+
+ char *estr;
+ int err;
+ double val = JS_strtod(NS_ConvertUTF16toUTF8(mStringBuffer).get(),
+ &estr, &err);
+ if (err == JS_DTOA_ENOMEM) {
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ } else if (err || *estr) {
+ rv = NS_ERROR_FAILURE; // decode error
+ } else {
+ // ok
+ jsval numVal;
+ if (JS_NewNumberValue(mCx, val, &numVal)) {
+ rv = PushValue(obj, numVal);
+ } else {
+ rv = NS_ERROR_FAILURE; // decode error
+ }
+ }
+
+ mStringBuffer.Truncate();
+
+ return rv;
+}
+
+nsresult
+nsJSONListener::HandleKeyword()
+{
+ JSObject *obj = mObjectStack.ElementAt(mObjectStack.Length() - 1);
+ jsval keyword;
+ if (mStringBuffer.Equals(NS_LITERAL_STRING("null"))) {
+ keyword = JSVAL_NULL;
+ } else if (mStringBuffer.Equals(NS_LITERAL_STRING("true"))) {
+ keyword = JSVAL_TRUE;
+ } else if (mStringBuffer.Equals(NS_LITERAL_STRING("false"))) {
+ keyword = JSVAL_FALSE;
+ } else {
+ return NS_ERROR_FAILURE;
+ }
+
+ mStringBuffer.Truncate();
+
+ return PushValue(obj, keyword);
+}
new file mode 100644
--- /dev/null
+++ b/dom/src/json/nsJSON.h
@@ -0,0 +1,164 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** 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
+ * Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Robert Sayre <sayrer@gmail.com>
+ *
+ * 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 ***** */
+
+#ifndef nsJSON_h__
+#define nsJSON_h__
+
+#include "jsprvtd.h"
+#include "nsIJSON.h"
+#include "nsString.h"
+#include "nsCOMPtr.h"
+#include "nsIOutputStream.h"
+#include "nsIUnicodeEncoder.h"
+#include "nsIUnicodeDecoder.h"
+#include "nsIRequestObserver.h"
+#include "nsIStreamListener.h"
+#include "nsTArray.h"
+
+#define JSON_MAX_DEPTH 2048
+#define JSON_PARSER_BUFSIZE 1024
+class nsJSONWriter
+{
+public:
+ nsJSONWriter();
+ nsJSONWriter(nsIOutputStream *aStream);
+ virtual ~nsJSONWriter();
+ nsresult SetCharset(const char *aCharset);
+ nsString mBuffer;
+ nsCOMPtr<nsIOutputStream> mStream;
+ nsresult WriteString(const PRUnichar* aBuffer, PRUint32);
+ nsresult Write(const PRUnichar *aBuffer, PRUint32 aLength);
+
+protected:
+ nsresult WriteToStream(nsIOutputStream *aStream, nsIUnicodeEncoder *encoder,
+ const PRUnichar *aBuffer, PRUint32 aLength);
+
+ nsCOMPtr<nsIUnicodeEncoder> mEncoder;
+};
+
+class nsJSON : public nsIJSON
+{
+public:
+ nsJSON();
+ virtual ~nsJSON();
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIJSON
+
+protected:
+ JSBool ToJSON(JSContext *cx, jsval *vp);
+ nsresult EncodeObject(JSContext *cx, jsval *vp, nsJSONWriter *writer,
+ JSObject *whitelist, PRUint32 depth);
+ nsresult EncodeInternal(nsJSONWriter *writer);
+ nsresult DecodeInternal(nsIInputStream *aStream,
+ PRInt32 aContentLength,
+ PRBool aNeedsConverter);
+};
+
+NS_IMETHODIMP
+NS_NewJSON(nsISupports* aOuter, REFNSIID aIID, void** aResult);
+
+enum JSONParserState {
+ JSON_PARSE_STATE_INIT,
+ JSON_PARSE_STATE_VALUE,
+ JSON_PARSE_STATE_OBJECT,
+ JSON_PARSE_STATE_OBJECT_PAIR,
+ JSON_PARSE_STATE_OBJECT_IN_PAIR,
+ JSON_PARSE_STATE_ARRAY,
+ JSON_PARSE_STATE_STRING,
+ JSON_PARSE_STATE_STRING_ESCAPE,
+ JSON_PARSE_STATE_STRING_HEX,
+ JSON_PARSE_STATE_NUMBER,
+ JSON_PARSE_STATE_KEYWORD,
+ JSON_PARSE_STATE_FINISHED
+};
+
+class nsJSONObjectStack : public nsTArray<JSObject *>,
+ public JSTempValueRooter
+{
+};
+
+class nsJSONListener : public nsIStreamListener
+{
+public:
+ nsJSONListener(JSContext *cx, jsval *rootVal, PRBool needsConverter);
+ virtual ~nsJSONListener();
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIREQUESTOBSERVER
+ NS_DECL_NSISTREAMLISTENER
+
+protected:
+ PRUint32 mLineNum;
+ PRUint32 mColumn;
+
+ /* Used while handling \uNNNN in strings */
+ PRUnichar mHexChar;
+ PRUint8 mNumHex;
+
+ JSContext *mCx;
+ jsval *mRootVal;
+ PRBool mNeedsConverter;
+ nsCOMPtr<nsIUnicodeDecoder> mDecoder;
+ JSONParserState *mStatep;
+ JSONParserState mStateStack[JSON_MAX_DEPTH];
+ nsString mStringBuffer;
+ nsCString mSniffBuffer;
+
+ nsresult PushState(JSONParserState state);
+ nsresult PopState();
+ nsresult ProcessBytes(const char* aBuffer, PRUint32 aByteLength);
+ nsresult ConsumeConverted(const char* aBuffer, PRUint32 aByteLength);
+ nsresult Consume(const PRUnichar *data, PRUint32 len);
+
+ // These handle parsed tokens. Could be split to separate interface.
+ nsJSONObjectStack mObjectStack;
+
+ nsresult PushValue(JSObject *aParent, jsval aValue);
+ nsresult PushObject(JSObject *aObj);
+ nsresult OpenObject();
+ nsresult CloseObject();
+ nsresult OpenArray();
+ nsresult CloseArray();
+ nsresult HandleString();
+ nsresult HandleNumber();
+ nsresult HandleKeyword();
+ nsString mObjectKey;
+};
+
+#endif
new file mode 100644
--- /dev/null
+++ b/dom/src/json/test/Makefile.in
@@ -0,0 +1,49 @@
+#
+# ***** 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 Mozilla Corporation.
+# Portions created by the Initial Developer are Copyright (C) 2007
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+# Robert Sayre <sayrer@gmail.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either of 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 *****
+
+DEPTH = ../../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE = json_test
+
+XPCSHELL_TESTS = unit
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/dom/src/json/test/json2.js
@@ -0,0 +1,263 @@
+/*
+ json2.js
+ 2007-11-06
+
+ Public Domain
+
+ See http://www.JSON.org/js.html
+
+ This file creates a global JSON object containing two methods:
+
+ JSON.stringify(value, whitelist)
+ value any JavaScript value, usually an object or array.
+
+ whitelist an optional that determines how object values are
+ stringified.
+
+ This method produces a JSON text from a JavaScript value.
+ There are three possible ways to stringify an object, depending
+ on the optional whitelist parameter.
+
+ If an object has a toJSON method, then the toJSON() method will be
+ called. The value returned from the toJSON method will be
+ stringified.
+
+ Otherwise, if the optional whitelist parameter is an array, then
+ the elements of the array will be used to select members of the
+ object for stringification.
+
+ Otherwise, if there is no whitelist parameter, then all of the
+ members of the object will be stringified.
+
+ Values that do not have JSON representaions, such as undefined or
+ functions, will not be serialized. Such values in objects will be
+ dropped, in arrays will be replaced with null. JSON.stringify()
+ returns undefined. Dates will be stringified as quoted ISO dates.
+
+ Example:
+
+ var text = JSON.stringify(['e', {pluribus: 'unum'}]);
+ // text is '["e",{"pluribus":"unum"}]'
+
+ JSON.parse(text, filter)
+ This method parses a JSON text to produce an object or
+ array. It can throw a SyntaxError exception.
+
+ The optional filter parameter is a function that can filter and
+ transform the results. It receives each of the keys and values, and
+ its return value is used instead of the original value. If it
+ returns what it received, then structure is not modified. If it
+ returns undefined then the member is deleted.
+
+ Example:
+
+ // Parse the text. If a key contains the string 'date' then
+ // convert the value to a date.
+
+ myData = JSON.parse(text, function (key, value) {
+ return key.indexOf('date') >= 0 ? new Date(value) : value;
+ });
+
+ This is a reference implementation. You are free to copy, modify, or
+ redistribute.
+
+ Use your own copy. It is extremely unwise to load third party
+ code into your pages.
+*/
+
+/*jslint evil: true */
+/*extern JSON */
+
+if (!this.JSON) {
+
+ JSON = function () {
+
+ function f(n) { // Format integers to have at least two digits.
+ return n < 10 ? '0' + n : n;
+ }
+
+ Date.prototype.toJSON = function () {
+
+// Eventually, this method will be based on the date.toISOString method.
+
+ return this.getUTCFullYear() + '-' +
+ f(this.getUTCMonth() + 1) + '-' +
+ f(this.getUTCDate()) + 'T' +
+ f(this.getUTCHours()) + ':' +
+ f(this.getUTCMinutes()) + ':' +
+ f(this.getUTCSeconds()) + 'Z';
+ };
+
+
+ var m = { // table of character substitutions
+ '\b': '\\b',
+ '\t': '\\t',
+ '\n': '\\n',
+ '\f': '\\f',
+ '\r': '\\r',
+ '"' : '\\"',
+ '\\': '\\\\'
+ };
+
+ function stringify(value, whitelist) {
+ var a, // The array holding the partial texts.
+ i, // The loop counter.
+ k, // The member key.
+ l, // Length.
+ r = /["\\\x00-\x1f\x7f-\x9f]/g,
+ v; // The member value.
+
+ switch (typeof value) {
+ case 'string':
+
+// If the string contains no control characters, no quote characters, and no
+// backslash characters, then we can safely slap some quotes around it.
+// Otherwise we must also replace the offending characters with safe sequences.
+
+ return r.test(value) ?
+ '"' + value.replace(r, function (a) {
+ var c = m[a];
+ if (c) {
+ return c;
+ }
+ c = a.charCodeAt();
+ return '\\u00' + Math.floor(c / 16).toString(16) +
+ (c % 16).toString(16);
+ }) + '"' :
+ '"' + value + '"';
+
+ case 'number':
+
+// JSON numbers must be finite. Encode non-finite numbers as null.
+
+ return isFinite(value) ? String(value) : 'null';
+
+ case 'boolean':
+ case 'null':
+ return String(value);
+
+ case 'object':
+
+// Due to a specification blunder in ECMAScript,
+// typeof null is 'object', so watch out for that case.
+
+ if (!value) {
+ return 'null';
+ }
+
+// If the object has a toJSON method, call it, and stringify the result.
+
+ if (typeof value.toJSON === 'function') {
+ return stringify(value.toJSON());
+ }
+ a = [];
+ if (typeof value.length === 'number' &&
+ !(value.propertyIsEnumerable('length'))) {
+
+// The object is an array. Stringify every element. Use null as a placeholder
+// for non-JSON values.
+
+ l = value.length;
+ for (i = 0; i < l; i += 1) {
+ a.push(stringify(value[i], whitelist) || 'null');
+ }
+
+// Join all of the elements together and wrap them in brackets.
+
+ return '[' + a.join(',') + ']';
+ }
+ if (whitelist) {
+
+// If a whitelist (array of keys) is provided, use it to select the components
+// of the object.
+
+ l = whitelist.length;
+ for (i = 0; i < l; i += 1) {
+ k = whitelist[i];
+ if (typeof k === 'string') {
+ v = stringify(value[k], whitelist);
+ if (v) {
+ a.push(stringify(k) + ':' + v);
+ }
+ }
+ }
+ } else {
+
+// Otherwise, iterate through all of the keys in the object.
+
+ for (k in value) {
+ if (typeof k === 'string') {
+ v = stringify(value[k], whitelist);
+ if (v) {
+ a.push(stringify(k) + ':' + v);
+ }
+ }
+ }
+ }
+
+// Join all of the member texts together and wrap them in braces.
+
+ return '{' + a.join(',') + '}';
+ }
+ return undefined;
+ }
+
+ return {
+ stringify: stringify,
+ parse: function (text, filter) {
+ var j;
+
+ function walk(k, v) {
+ var i, n;
+ if (v && typeof v === 'object') {
+ for (i in v) {
+ if (Object.prototype.hasOwnProperty.apply(v, [i])) {
+ n = walk(i, v[i]);
+ if (n !== undefined) {
+ v[i] = n;
+ }
+ }
+ }
+ }
+ return filter(k, v);
+ }
+
+
+// Parsing happens in three stages. In the first stage, we run the text against
+// regular expressions that look for non-JSON patterns. We are especially
+// concerned with '()' and 'new' because they can cause invocation, and '='
+// because it can cause mutation. But just to be safe, we want to reject all
+// unexpected forms.
+
+// We split the first stage into 4 regexp operations in order to work around
+// crippling inefficiencies in IE's and Safari's regexp engines. First we
+// replace all backslash pairs with '@' (a non-JSON character). Second, we
+// replace all simple value tokens with ']' characters. Third, we delete all
+// open brackets that follow a colon or comma or that begin the text. Finally,
+// we look to see that the remaining characters are only whitespace or ']' or
+// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
+
+ if (/^[\],:{}\s]*$/.test(text.replace(/\\./g, '@').
+replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(:?[eE][+\-]?\d+)?/g, ']').
+replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
+
+// In the second stage we use the eval function to compile the text into a
+// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
+// in JavaScript: it can begin a block or an object literal. We wrap the text
+// in parens to eliminate the ambiguity.
+
+ j = eval('(' + text + ')');
+
+// In the optional third stage, we recursively walk the new structure, passing
+// each name/value pair to a filter function for possible transformation.
+
+ return typeof filter === 'function' ? walk('', j) : j;
+ }
+
+// If the text is not JSON parseable, then a SyntaxError is thrown.
+
+ throw new SyntaxError('parseJSON');
+ }
+ };
+ }();
+}
new file mode 100644
--- /dev/null
+++ b/dom/src/json/test/pass1.json
@@ -0,0 +1,58 @@
+[
+ "JSON Test Pattern pass1",
+ {"object with 1 member":["array with 1 element"]},
+ {},
+ [],
+ -42,
+ true,
+ false,
+ null,
+ {
+ "integer": 1234567890,
+ "real": -9876.543210,
+ "e": 0.123456789e-12,
+ "E": 1.234567890E+34,
+ "": 23456789012E66,
+ "zero": 0,
+ "one": 1,
+ "space": " ",
+ "quote": "\"",
+ "backslash": "\\",
+ "controls": "\b\f\n\r\t",
+ "slash": "/ & \/",
+ "alpha": "abcdefghijklmnopqrstuvwyz",
+ "ALPHA": "ABCDEFGHIJKLMNOPQRSTUVWYZ",
+ "digit": "0123456789",
+ "0123456789": "digit",
+ "special": "`1~!@#$%^&*()_+-={':[,]}|;.</>?",
+ "hex": "\u0123\u4567\u89AB\uCDEF\uabcd\uef4A",
+ "true": true,
+ "false": false,
+ "null": null,
+ "array":[ ],
+ "object":{ },
+ "address": "50 St. James Street",
+ "url": "http://www.JSON.org/",
+ "comment": "// /* <!-- --",
+ "# -- --> */": " ",
+ " s p a c e d " :[1,2 , 3
+
+,
+
+4 , 5 , 6 ,7 ],"compact":[1,2,3,4,5,6,7],
+ "jsontext": "{\"object with 1 member\":[\"array with 1 element\"]}",
+ "quotes": "" \u0022 %22 0x22 034 "",
+ "\/\\\"\uCAFE\uBABE\uAB98\uFCDE\ubcda\uef4A\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',./<>?"
+: "A key can be any string"
+ },
+ 0.5 ,98.6
+,
+99.44
+,
+
+1066,
+1e1,
+0.1e1,
+1e-1,
+1e00,2e+00,2e-00
+,"rosebud"]
new file mode 100644
--- /dev/null
+++ b/dom/src/json/test/pass3.json
@@ -0,0 +1,7 @@
+{
+ "JSON Test Pattern pass3": {
+ "The outermost value": "must be an object or array.",
+ "In this test": "It is an object."
+ }
+}
+
new file mode 100644
--- /dev/null
+++ b/dom/src/json/test/unit/head_json.js
@@ -0,0 +1,19 @@
+var Ci = Components.interfaces;
+var Cc = Components.classes;
+
+var nativeJSON = Cc["@mozilla.org/dom/json;1"].createInstance(Ci.nsIJSON);
+var dirSvc = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);
+var workingDir = dirSvc.get("TmpD", Ci.nsIFile);
+
+var outputName = "json-test-output";
+var outputDir = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
+outputDir.initWithFile(workingDir);
+outputDir.append(outputName);
+
+if (!outputDir.exists()) {
+ outputDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0777);
+} else if (!outputDir.isDirectory()) {
+ do_throw(outputName + " is not a directory?")
+}
+var JSON = null;
+do_import_script("dom/src/json/test/json2.js");
new file mode 100644
--- /dev/null
+++ b/dom/src/json/test/unit/test_decode.js
@@ -0,0 +1,161 @@
+function decode_strings() {
+ // empty object
+ var x = nativeJSON.decode("{}");
+ do_check_eq(typeof x, "object");
+
+ // empty array
+ x = nativeJSON.decode("[]");
+ do_check_eq(typeof x, "object");
+ do_check_eq(x.length, 0);
+ do_check_eq(x.constructor, Array);
+
+ // one element array
+ x = nativeJSON.decode("[[]]");
+ do_check_eq(typeof x, "object");
+ do_check_eq(x.length, 1);
+ do_check_eq(x.constructor, Array);
+ do_check_eq(x[0].constructor, Array);
+
+ // multiple arrays
+ x = nativeJSON.decode("[[],[],[]]");
+ do_check_eq(typeof x, "object");
+ do_check_eq(x.length, 3);
+ do_check_eq(x.constructor, Array);
+ do_check_eq(x[0].constructor, Array);
+ do_check_eq(x[1].constructor, Array);
+ do_check_eq(x[2].constructor, Array);
+
+ // array key/value
+ x = nativeJSON.decode('{"foo":[]}');
+ do_check_eq(typeof x, "object");
+ do_check_eq(typeof x.foo, "object");
+ do_check_eq(x.foo.constructor, Array);
+ x = nativeJSON.decode('{"foo":[], "bar":[]}');
+ do_check_eq(typeof x, "object");
+ do_check_eq(typeof x.foo, "object");
+ do_check_eq(x.foo.constructor, Array);
+ do_check_eq(typeof x.bar, "object");
+ do_check_eq(x.bar.constructor, Array);
+
+ // nesting
+ x = nativeJSON.decode('{"foo":[{}]}');
+ do_check_eq(x.foo.constructor, Array);
+ do_check_eq(x.foo.length, 1);
+ do_check_eq(typeof x.foo[0], "object");
+ x = nativeJSON.decode('{"foo":[{"foo":[{"foo":{}}]}]}');
+ do_check_eq(x.foo[0].foo[0].foo.constructor, Object);
+ x = nativeJSON.decode('{"foo":[{"foo":[{"foo":[]}]}]}');
+ do_check_eq(x.foo[0].foo[0].foo.constructor, Array);
+
+ // strings
+ x = nativeJSON.decode('{"foo":"bar"}');
+ do_check_eq(x.foo, "bar");
+ x = nativeJSON.decode('["foo", "bar", "baz"]');
+ do_check_eq(x[0], "foo");
+ do_check_eq(x[1], "bar");
+ do_check_eq(x[2], "baz");
+
+ // numbers
+ x = nativeJSON.decode('{"foo":5.5, "bar":5}');
+ do_check_eq(x.foo, 5.5);
+ do_check_eq(x.bar, 5);
+
+ // keywords
+ x = nativeJSON.decode('{"foo": true, "bar":false, "baz":null}');
+ do_check_eq(x.foo, true);
+ do_check_eq(x.bar, false);
+ do_check_eq(x.baz, null);
+
+ // short escapes
+ x = nativeJSON.decode('{"foo": "\\"", "bar":"\\\\", "baz":"\\b","qux":"\\f", "quux":"\\n", "quuux":"\\r","quuuux":"\\t"}');
+ do_check_eq(x.foo, '"');
+ do_check_eq(x.bar, '\\');
+ do_check_eq(x.baz, '\b');
+ do_check_eq(x.qux, '\f');
+ do_check_eq(x.quux, "\n");
+ do_check_eq(x.quuux, "\r");
+ do_check_eq(x.quuuux, "\t");
+
+ // unicode escape
+ x = nativeJSON.decode('{"foo":"hmm\\u006dmm"}');
+ do_check_eq("hmm\u006dmm", x.foo);
+
+ x = nativeJSON.decode('{"JSON Test Pattern pass3": {"The outermost value": "must be an object or array.","In this test": "It is an object." }}');
+}
+
+function test_files() {
+ function read_file(path) {
+ try {
+ var f = do_get_file(path);
+ var istream = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(Ci.nsIFileInputStream);
+ istream.init(f, -1, -1, false);
+ var x = nativeJSON.decodeFromStream(istream, istream.available());
+ } finally {
+ istream.close();
+ }
+ return x;
+ }
+
+ var x = read_file("/dom/src/json/test/pass3.json");
+ do_check_eq(x["JSON Test Pattern pass3"]["The outermost value"], "must be an object or array.");
+ do_check_eq(x["JSON Test Pattern pass3"]["In this test"], "It is an object.");
+
+ x = read_file("/dom/src/json/test/pass1.json");
+ do_check_eq(x[0], "JSON Test Pattern pass1");
+ do_check_eq(x[1]["object with 1 member"][0], "array with 1 element");
+ do_check_eq(x[2].constructor, Object);
+ do_check_eq(x[3].constructor, Array);
+ do_check_eq(x[4], -42);
+ do_check_eq(x[5], true);
+ do_check_eq(x[6], false);
+ do_check_eq(x[7], null);
+ do_check_eq(x[8].constructor, Object);
+ do_check_eq(x[8]["integer"], 1234567890);
+ do_check_eq(x[8]["real"], -9876.543210);
+ do_check_eq(x[8]["e"], 0.123456789e-12);
+ do_check_eq(x[8]["E"], 1.234567890E+34);
+ do_check_eq(x[8][""], 23456789012E66);
+ do_check_eq(x[8]["zero"], 0);
+ do_check_eq(x[8]["one"], 1);
+ do_check_eq(x[8]["space"], " ");
+ do_check_eq(x[8]["quote"], "\"");
+ do_check_eq(x[8]["backslash"], "\\");
+ do_check_eq(x[8]["controls"], "\b\f\n\r\t");
+ do_check_eq(x[8]["slash"], "/ & /");
+ do_check_eq(x[8]["alpha"], "abcdefghijklmnopqrstuvwyz");
+ do_check_eq(x[8]["ALPHA"], "ABCDEFGHIJKLMNOPQRSTUVWYZ");
+ do_check_eq(x[8]["digit"], "0123456789");
+ do_check_eq(x[8]["0123456789"], "digit");
+ do_check_eq(x[8]["special"], "`1~!@#$%^&*()_+-={':[,]}|;.</>?");
+ do_check_eq(x[8]["hex"], "\u0123\u4567\u89AB\uCDEF\uabcd\uef4A");
+ do_check_eq(x[8]["true"], true);
+ do_check_eq(x[8]["false"], false);
+ do_check_eq(x[8]["null"], null);
+ do_check_eq(x[8]["array"].length, 0);
+ do_check_eq(x[8]["object"].constructor, Object);
+ do_check_eq(x[8]["address"], "50 St. James Street");
+ do_check_eq(x[8]["url"], "http://www.JSON.org/");
+ do_check_eq(x[8]["comment"], "// /* <!-- --");
+ do_check_eq(x[8]["# -- --> */"], " ");
+ do_check_eq(x[8][" s p a c e d "].length, 7);
+ do_check_eq(x[8]["compact"].length, 7);
+ do_check_eq(x[8]["jsontext"], "{\"object with 1 member\":[\"array with 1 element\"]}");
+ do_check_eq(x[8]["quotes"], "" \u0022 %22 0x22 034 "");
+ do_check_eq(x[8]["\/\\\"\uCAFE\uBABE\uAB98\uFCDE\ubcda\uef4A\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',./<>?"], "A key can be any string");
+ do_check_eq(x[9], 0.5);
+ do_check_eq(x[10], 98.6);
+ do_check_eq(x[11], 99.44);
+ do_check_eq(x[12], 1066);
+ do_check_eq(x[13], 1e1);
+ do_check_eq(x[14], 0.1e1);
+ do_check_eq(x[15], 1e-1);
+ do_check_eq(x[16], 1e00);
+ do_check_eq(x[17], 2e+00);
+ do_check_eq(x[18], 2e-00);
+ do_check_eq(x[19], "rosebud");
+}
+
+function run_test() {
+ decode_strings();
+ test_files();
+}
new file mode 100644
--- /dev/null
+++ b/dom/src/json/test/unit/test_encode.js
@@ -0,0 +1,237 @@
+// returns a list of [string, object] pairs to test encoding
+function getTestPairs() {
+ var testPairs = [
+ ["{}", {}],
+ ["[]", []],
+ ['{"foo":"bar"}', {"foo":"bar"}],
+ ['{"null":null}', {"null":null}],
+ ['{"five":5}', {"five":5}],
+ ['{"five":5,"six":6}', {"five":5, "six":6}],
+ ['{"x":{"y":"z"}}', {"x":{"y":"z"}}],
+ ['{"w":{"x":{"y":"z"}}}', {"w":{"x":{"y":"z"}}}],
+ ['[1,2,3]', [1,2,3]],
+ ['{"w":{"x":{"y":[1,2,3]}}}', {"w":{"x":{"y":[1,2,3]}}}],
+ ['{"false":false}', {"false":false}],
+ ['{"true":true}', {"true":true}],
+ ['{"child has two members":{"this":"one","2":"and this one"}}',
+ {"child has two members": {"this":"one", 2:"and this one"}}],
+ ['{"x":{"a":"b","c":{"y":"z"},"f":"g"}}',
+ {"x":{"a":"b","c":{"y":"z"},"f":"g"}}],
+ ['{"x":[1,{"y":"z"},3]}', {"x":[1,{"y":"z"},3]}],
+ //['{"0":"h","1":"m","2":"m"}', new String("hmm")],
+ ['[1,null,3]',[1,,3]],
+ [null, function test(){}],
+ [null, dump],
+ ['{"mm\\\"mm":"hmm"}',{"mm\"mm":"hmm"}],
+ ['{"mm\\\"mm\\\"mm":"hmm"}',{"mm\"mm\"mm":"hmm"}],
+ ['{"\\\"":"hmm"}',{'"':"hmm"}],
+ ['{"\\\\":"hmm"}',{'\\':"hmm"}],
+ ['{"mmm\\\\mmm":"hmm"}',{'mmm\\mmm':"hmm"}],
+ ['{"mmm\\\\mmm\\\\mmm":"hmm"}',{'mmm\\mmm\\mmm':"hmm"}],
+ ['{"mm\\u000bmm":"hmm"}',{"mm\u000bmm":"hmm"}],
+ ['{"mm\\u0000mm":"hmm"}',{"mm\u0000mm":"hmm"}]
+ ];
+
+ var x = {"free":"variable"}
+ testPairs.push(['{"free":"variable"}', x]);
+ testPairs.push(['{"y":{"free":"variable"}}', {"y":x}]);
+
+ // array prop
+ var x = {
+ a: [1,2,3]
+ }
+ testPairs.push(['{"a":[1,2,3]}', x])
+
+ var y = {
+ foo: function(hmm) { return hmm; }
+ }
+ testPairs.push(['{"y":{}}',{"y":y}]);
+
+ // test toJSON
+ var hmm = {
+ toJSON: function() { return {"foo":"bar"}}
+ }
+ testPairs.push(['{"hmm":{"foo":"bar"}}', {"hmm":hmm}]);
+ testPairs.push(['{"foo":"bar"}', hmm]); // on the root
+
+ // toJSON on prototype
+ var Y = function() {
+ this.d = "e";
+ }
+ Y.prototype = {
+ not:"there?",
+ toJSON: function() { return {"foo":"bar"}}
+ };
+ var y = new Y();
+ testPairs.push(['{"foo":"bar"}', y.toJSON()]);
+ testPairs.push(['{"foo":"bar"}', y]);
+
+ // return undefined from toJSON
+ var hmm = {
+ toJSON: function() { return; }
+ }
+ testPairs.push(['{}', {"hmm":hmm}]);
+
+ // array with named prop
+ var x= new Array();
+ x[0] = 1;
+ x.foo = "bar";
+ //testPairs.push(['[1]', x]);
+
+ // prototype
+ var X = function() { this.a = "b" }
+ X.prototype = {c:"d"}
+ var y = new X();
+ testPairs.push(['{"a":"b","c":"d"}', y]);
+
+ // useless roots will be dropped
+ testPairs.push([null, null]);
+ testPairs.push([null, ""]);
+ testPairs.push([null, undefined]);
+ testPairs.push([null, 5]);
+
+ // custom iterator: JS 1.7+
+ var x = {
+ "a": "foo",
+ b: "not included",
+ c: "bar",
+ "4": "qux",
+ __iterator__: function() { return (function() { yield "a"; yield "c"; yield 4; })() }
+ }
+ do_check_eq('{"a":"foo","c":"bar","4":"qux"}', nativeJSON.encode(x));
+
+ return testPairs;
+}
+
+function testStringEncode() {
+ // test empty arg
+ do_check_eq(null, nativeJSON.encode());
+
+ var pairs = getTestPairs();
+ for each(pair in pairs) {
+ var nativeResult = nativeJSON.encode(pair[1]);
+ var crockfordResult = JSON.stringify(pair[1]);
+ do_check_eq(pair[0], nativeResult);
+
+ // Don't follow json2.js handling of non-objects
+ if (pair[1] && (typeof pair[1] == "object")) {
+ do_check_eq(crockfordResult, nativeResult);
+ }
+ }
+}
+
+function testOutputStreams() {
+ function writeToFile(obj, charset, writeBOM) {
+ var jsonFile = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
+ jsonFile.initWithFile(outputDir);
+ jsonFile.append("test.json");
+ jsonFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0600);
+ var stream = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(Ci.nsIFileOutputStream);
+ try {
+ stream.init(jsonFile, 0x04 | 0x08 | 0x20, 0600, 0); // write, create, truncate
+ nativeJSON.encodeToStream(stream, charset, writeBOM, obj);
+ } finally {
+ stream.close();
+ }
+ return jsonFile;
+ }
+
+ var pairs = getTestPairs();
+ for each(pair in pairs) {
+ if (pair[1] && (typeof pair[1] == "object")) {
+ var utf8File = writeToFile(pair[1], "UTF-8", false);
+ var utf16LEFile = writeToFile(pair[1], "UTF-16LE", false);
+ var utf16BEFile = writeToFile(pair[1], "UTF-16BE", false);
+ var utf32LEFile = writeToFile(pair[1], "UTF-32LE", false);
+ var utf32BEFile = writeToFile(pair[1], "UTF-32BE", false);
+
+ // all ascii with no BOMs, so this will work
+ do_check_eq(utf16LEFile.fileSize / 2, utf8File.fileSize);
+ do_check_eq(utf32LEFile.fileSize / 4, utf8File.fileSize);
+ do_check_eq(utf16LEFile.fileSize, utf16BEFile.fileSize);
+ do_check_eq(utf32LEFile.fileSize, utf32BEFile.fileSize);
+ }
+ }
+
+ // check BOMs
+ var f = writeToFile({},"UTF-8", true);
+ do_check_eq(f.fileSize, 5);
+ var f = writeToFile({},"UTF-16LE", true);
+ do_check_eq(f.fileSize, 6);
+ var f = writeToFile({},"UTF-16BE", true);
+ do_check_eq(f.fileSize, 6);
+ var f = writeToFile({},"UTF-32LE", true);
+ do_check_eq(f.fileSize, 12);
+ var f = writeToFile({},"UTF-32BE", true);
+ do_check_eq(f.fileSize, 12);
+
+ outputDir.remove(true);
+}
+
+function throwingToJSON() {
+ var a = {
+ "b": 1,
+ "c": 2,
+ toJSON: function() { throw("uh oh"); }
+ }
+ try {
+ var y = nativeJSON.encode(a);
+ } catch (ex) {}
+}
+
+function throwingIterator() {
+ var a = {
+ "b": 1,
+ "c": 2,
+ __iterator__: function() { yield "b"; throw("uh oh"); }
+ }
+ try {
+ var y = nativeJSON.encode(a);
+ } catch (ex) {}
+}
+
+function deletingIter(x) {
+ return function() {
+ yield "dd";
+ print("after first yield");
+ delete x["a"]["c"];
+ gc();
+ print("about to yield second");
+ yield "ddddd";
+ }
+}
+
+function deleteDuringEncode() {
+ var x = {};
+ x.a = {
+ b: 1,
+ bb: 2,
+ bbb: 3,
+ c: {
+ cc: 2,
+ ccc: 3,
+ d: {
+ dd: 2,
+ ddd: 3,
+ __iterator__: deletingIter(x),
+ dddd: 4,
+ ddddd: 5
+ },
+ cccc: 4,
+ ccccc: 5
+ },
+ bbbb: 4,
+ bbbbb: 5,
+ bbbbbb: 6
+ };
+ var z = nativeJSON.encode(x);
+ print(z);
+}
+
+function run_test() {
+ testStringEncode();
+ testOutputStreams();
+ throwingToJSON();
+ throwingIterator();
+ deleteDuringEncode();
+}
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -131,16 +131,17 @@ EXPORTS = \
jsbool.h \
jsclist.h \
jscntxt.h \
jscompat.h \
jsconfig.h \
jsdate.h \
jsdbgapi.h \
jsdhash.h \
+ jsdtoa.h \
jsemit.h \
jsfun.h \
jsgc.h \
jshash.h \
jsinterp.h \
jsiter.h \
jslock.h \
jslong.h \
--- a/js/src/jsiter.c
+++ b/js/src/jsiter.c
@@ -323,17 +323,17 @@ js_GetNativeIteratorFlags(JSContext *cx,
return 0;
return JSVAL_TO_INT(OBJ_GET_SLOT(cx, iterobj, JSSLOT_ITER_FLAGS));
}
/*
* Call ToObject(v).__iterator__(keyonly) if ToObject(v).__iterator__ exists.
* Otherwise construct the defualt iterator.
*/
-JSBool
+JS_FRIEND_API(JSBool)
js_ValueToIterator(JSContext *cx, uintN flags, jsval *vp)
{
JSObject *obj;
JSTempValueRooter tvr;
JSAtom *atom;
JSClass *clasp;
JSExtendedClass *xclasp;
JSBool ok;
@@ -434,17 +434,17 @@ js_ValueToIterator(JSContext *cx, uintN
if (obj)
JS_POP_TEMP_ROOT(cx, &tvr);
return ok;
bad:
ok = JS_FALSE;
goto out;
}
-JSBool
+JS_FRIEND_API(JSBool)
js_CloseIterator(JSContext *cx, jsval v)
{
JSObject *obj;
JSClass *clasp;
JS_ASSERT(!JSVAL_IS_PRIMITIVE(v));
obj = JSVAL_TO_OBJECT(v);
clasp = OBJ_GET_CLASS(cx, obj);
@@ -588,17 +588,17 @@ CallEnumeratorNext(JSContext *cx, JSObje
return JS_TRUE;
stop:
JS_ASSERT(STOBJ_GET_SLOT(iterobj, JSSLOT_ITER_STATE) == JSVAL_NULL);
*rval = JSVAL_HOLE;
return JS_TRUE;
}
-JSBool
+JS_FRIEND_API(JSBool)
js_CallIteratorNext(JSContext *cx, JSObject *iterobj, jsval *rval)
{
uintN flags;
/* Fast path for native iterators */
if (OBJ_GET_CLASS(cx, iterobj) == &js_IteratorClass) {
flags = JSVAL_TO_INT(OBJ_GET_SLOT(cx, iterobj, JSSLOT_ITER_FLAGS));
if (flags & JSITER_ENUMERATE)
--- a/js/src/jsiter.h
+++ b/js/src/jsiter.h
@@ -53,27 +53,27 @@ JS_BEGIN_EXTERN_C
#define JSITER_KEYVALUE 0x4 /* destructuring for-in wants [key, value] */
/*
* Convert the value stored in *vp to its iteration object. The flags should
* contain JSITER_ENUMERATE if js_ValueToIterator is called when enumerating
* for-in semantics are required, and when the caller can guarantee that the
* iterator will never be exposed to scripts.
*/
-extern JSBool
+extern JS_FRIEND_API(JSBool)
js_ValueToIterator(JSContext *cx, uintN flags, jsval *vp);
-extern JSBool
+extern JS_FRIEND_API(JSBool)
js_CloseIterator(JSContext *cx, jsval v);
/*
* Given iterobj, call iterobj.next(). If the iterator stopped, set *rval to
* JSVAL_HOLE. Otherwise set it to the result of the next call.
*/
-extern JSBool
+extern JS_FRIEND_API(JSBool)
js_CallIteratorNext(JSContext *cx, JSObject *iterobj, jsval *rval);
/*
* Close iterobj, whose class must be js_IteratorClass.
*/
extern void
js_CloseNativeIterator(JSContext *cx, JSObject *iterobj);
--- a/layout/build/Makefile.in
+++ b/layout/build/Makefile.in
@@ -136,16 +136,17 @@ SHARED_LIBRARY_LIBS = \
$(DEPTH)/content/xslt/src/xml/$(LIB_PREFIX)txxml_s.$(LIB_SUFFIX) \
$(DEPTH)/content/xslt/src/xpath/$(LIB_PREFIX)txxpath_s.$(LIB_SUFFIX) \
$(DEPTH)/content/xslt/src/xslt/$(LIB_PREFIX)txxslt_s.$(LIB_SUFFIX) \
$(DEPTH)/content/xbl/src/$(LIB_PREFIX)gkconxbl_s.$(LIB_SUFFIX) \
$(DEPTH)/content/xul/document/src/$(LIB_PREFIX)gkconxuldoc_s.$(LIB_SUFFIX) \
$(DEPTH)/view/src/$(LIB_PREFIX)gkview_s.$(LIB_SUFFIX) \
$(DEPTH)/dom/src/base/$(LIB_PREFIX)jsdombase_s.$(LIB_SUFFIX) \
$(DEPTH)/dom/src/events/$(LIB_PREFIX)jsdomevents_s.$(LIB_SUFFIX) \
+ $(DEPTH)/dom/src/json/$(LIB_PREFIX)json_s.$(LIB_SUFFIX) \
$(DEPTH)/dom/src/jsurl/$(LIB_PREFIX)jsurl_s.$(LIB_SUFFIX) \
$(DEPTH)/dom/src/storage/$(LIB_PREFIX)jsdomstorage_s.$(LIB_SUFFIX) \
$(DEPTH)/dom/src/offline/$(LIB_PREFIX)jsdomoffline_s.$(LIB_SUFFIX) \
$(DEPTH)/editor/libeditor/text/$(LIB_PREFIX)texteditor_s.$(LIB_SUFFIX) \
$(DEPTH)/editor/libeditor/base/$(LIB_PREFIX)editorbase_s.$(LIB_SUFFIX) \
$(NULL)
ifdef NS_PRINTING
@@ -272,16 +273,17 @@ LOCAL_INCLUDES += -I$(srcdir)/../base \
-I$(topsrcdir)/content/xslt/src/xslt \
-I$(topsrcdir)/content/xul/content/src \
-I$(topsrcdir)/content/xul/document/src \
-I$(topsrcdir)/content/xul/templates/src \
-I$(topsrcdir)/content/events/src \
-I$(topsrcdir)/content/xbl/src \
-I$(topsrcdir)/view/src \
-I$(topsrcdir)/dom/src/base \
+ -I$(topsrcdir)/dom/src/json \
-I$(topsrcdir)/dom/src/jsurl \
-I$(topsrcdir)/dom/src/storage \
-I$(topsrcdir)/dom/src/offline \
-I. \
-I$(topsrcdir)/editor/libeditor/base \
-I$(topsrcdir)/editor/libeditor/text \
-I$(topsrcdir)/editor/libeditor/html \
-I$(topsrcdir)/editor/txtsvc/src \
--- a/layout/build/nsLayoutCID.h
+++ b/layout/build/nsLayoutCID.h
@@ -217,9 +217,12 @@
// {b88a4712-eb52-4c10-9b85-bf5894b510f0}
#define NS_DOMSTORAGEMANAGER_CID \
{ 0xb88a4712, 0xeb52, 0x4c10, { 0x9b, 0x85, 0xbf, 0x58, 0x94, 0xb5, 0x10, 0xf0 } }
// {14632191-AC21-4BDF-83E7-2363AD17E838}
#define NS_XULPOPUPMANAGER_CID \
{ 0x14632191, 0xac21, 0x4bdf, { 0x83, 0xe7, 0x23, 0x63, 0xad, 0x17, 0xe8, 0x38 } }
+// {93ad72a6-02cd-4716-9626-d47d5ec275ec}
+#define NS_DOMJSON_CID \
+{ 0x93ad72a6, 0x02cd, 0x4716, { 0x96, 0x26, 0xd4, 0x7d, 0x5e, 0xc2, 0x75, 0xec } }
#endif /* nsLayoutCID_h__ */
--- a/layout/build/nsLayoutModule.cpp
+++ b/layout/build/nsLayoutModule.cpp
@@ -120,16 +120,17 @@
#include "nsDOMException.h"
#include "nsGlobalWindowCommands.h"
#include "nsIControllerCommandTable.h"
#include "nsJSProtocolHandler.h"
#include "nsScriptNameSpaceManager.h"
#include "nsIControllerContext.h"
#include "nsDOMScriptObjectFactory.h"
#include "nsDOMStorage.h"
+#include "nsJSON.h"
// Editor stuff
#include "nsEditorCID.h"
#include "nsEditor.h"
#include "nsPlaintextEditor.h"
#include "nsEditorController.h" //CID
#include "nsIController.h"
#include "nsIControllerContext.h"
@@ -1316,16 +1317,21 @@ static const nsModuleComponentInfo gComp
"@mozilla.org/dom/storage;1",
NS_NewDOMStorage },
{ "DOM Storage Manager",
NS_DOMSTORAGEMANAGER_CID,
"@mozilla.org/dom/storagemanager;1",
nsDOMStorageManagerConstructor },
+ { "DOM JSON",
+ NS_DOMJSON_CID,
+ "@mozilla.org/dom/json;1",
+ NS_NewJSON },
+
{ "Text Editor",
NS_TEXTEDITOR_CID,
"@mozilla.org/editor/texteditor;1",
nsPlaintextEditorConstructor },
#ifndef MOZILLA_PLAINTEXT_EDITOR_ONLY
#ifdef ENABLE_EDITOR_API_LOG
{ "HTML Editor",
--- a/layout/style/nsCSSLoader.cpp
+++ b/layout/style/nsCSSLoader.cpp
@@ -458,46 +458,20 @@ static nsresult GetCharsetFromData(const
// that way even if we don't have a valid @charset rule we can use the BOM to
// get a reasonable charset. If we do have an @charset rule, the string from
// that will override this fallback setting of aCharset.
if (*aStyleSheetData == 0x40 && *(aStyleSheetData+1) == 0x63 /* '@c' */ ) {
// 1-byte ASCII-based encoding (ISO-8859-*, UTF-8, etc), no BOM
step = 1;
pos = 0;
}
- else if (aStyleSheetData[0] == 0xEF &&
- aStyleSheetData[1] == 0xBB &&
- aStyleSheetData[2] == 0xBF) {
- // UTF-8 BOM
- step = 1;
- pos = 3;
- aCharset = "UTF-8";
- }
// Check for a 4-byte encoding BOM before checking for a 2-byte one,
// since the latter can be a proper subset of the former.
else if (aStyleSheetData[0] == 0x00 &&
aStyleSheetData[1] == 0x00 &&
- aStyleSheetData[2] == 0xFE &&
- aStyleSheetData[3] == 0xFF) {
- // big-endian 4-byte encoding BOM
- step = 4;
- pos = 7;
- aCharset = "UTF-32BE";
- }
- else if (aStyleSheetData[0] == 0xFF &&
- aStyleSheetData[1] == 0xFE &&
- aStyleSheetData[2] == 0x00 &&
- aStyleSheetData[3] == 0x00) {
- // little-endian 4-byte encoding BOM
- step = 4;
- pos = 4;
- aCharset = "UTF-32LE";
- }
- else if (aStyleSheetData[0] == 0x00 &&
- aStyleSheetData[1] == 0x00 &&
aStyleSheetData[2] == 0xFF &&
aStyleSheetData[3] == 0xFE) {
// 4-byte encoding BOM in 2143 order
NS_WARNING("Our unicode decoders aren't likely to deal with this one");
step = 4;
pos = 6;
aCharset = "UTF-32";
}
@@ -506,27 +480,38 @@ static nsresult GetCharsetFromData(const
aStyleSheetData[2] == 0x00 &&
aStyleSheetData[3] == 0x00) {
// 4-byte encoding BOM in 3412 order
NS_WARNING("Our unicode decoders aren't likely to deal with this one");
step = 4;
pos = 5;
aCharset = "UTF-32";
}
- else if (aStyleSheetData[0] == 0xFE && aStyleSheetData[1] == 0xFF) {
- // big-endian 2-byte encoding BOM
- step = 2;
- pos = 3;
- aCharset = "UTF-16BE";
- }
- else if (aStyleSheetData[0] == 0xFF && aStyleSheetData[1] == 0xFE) {
- // little-endian 2-byte encoding BOM
- step = 2;
- pos = 2;
- aCharset = "UTF-16LE";
+ else if (nsContentUtils::CheckForBOM(aStyleSheetData,
+ aDataLength, aCharset)) {
+ if (aCharset.Equals("UTF-8")) {
+ step = 1;
+ pos = 3;
+ }
+ else if (aCharset.Equals("UTF-32BE")) {
+ step = 4;
+ pos = 7;
+ }
+ else if (aCharset.Equals("UTF-32LE")) {
+ step = 4;
+ pos = 4;
+ }
+ else if (aCharset.Equals("UTF-16BE")) {
+ step = 2;
+ pos = 3;
+ }
+ else if (aCharset.Equals("UTF-16LE")) {
+ step = 2;
+ pos = 2;
+ }
}
else if (aStyleSheetData[0] == 0x00 &&
aStyleSheetData[1] == 0x00 &&
aStyleSheetData[2] == 0x00 &&
aStyleSheetData[3] == 0x40) {
// big-endian 4-byte encoding, no BOM
step = 4;
pos = 3;
--- a/tools/test-harness/xpcshell-simple/test_all.sh
+++ b/tools/test-harness/xpcshell-simple/test_all.sh
@@ -106,17 +106,17 @@ done
#################
# RUN EACH TEST #
#################
for t in $testdir/test_*.js
do
echo -n "$t: "
- NATIVE_TOPSRCDIR="$native_topsrcdir" TOPSRCDIR="$topsrcdir" $xpcshell -s $headfiles -f $t $tailfiles 2> $t.log 1>&2
+ NATIVE_TOPSRCDIR="$native_topsrcdir" TOPSRCDIR="$topsrcdir" $xpcshell -v 180 -s $headfiles -f $t $tailfiles 2> $t.log 1>&2
rv="$?"
if [ ! "$rv" = "0" -o \
`grep -c '\*\*\* PASS' $t.log` = 0 ]
then
echo "FAIL"
echo "$t.log:"
echo ">>>>>>>"
cat $t.log