michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=2 sw=2 et tw=79: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "jsapi.h" michael@0: #include "js/CharacterEncoding.h" michael@0: #include "js/OldDebugAPI.h" michael@0: #include "nsJSON.h" michael@0: #include "nsIXPConnect.h" michael@0: #include "nsIXPCScriptable.h" michael@0: #include "nsStreamUtils.h" michael@0: #include "nsIInputStream.h" michael@0: #include "nsStringStream.h" michael@0: #include "mozilla/dom/EncodingUtils.h" michael@0: #include "nsIUnicodeEncoder.h" michael@0: #include "nsIUnicodeDecoder.h" michael@0: #include "nsXPCOMStrings.h" michael@0: #include "nsNetUtil.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsIScriptError.h" michael@0: #include "nsCRTGlue.h" michael@0: #include "nsAutoPtr.h" michael@0: #include "nsIScriptSecurityManager.h" michael@0: #include "mozilla/Maybe.h" michael@0: #include michael@0: michael@0: using mozilla::dom::EncodingUtils; michael@0: michael@0: #define JSON_STREAM_BUFSIZE 4096 michael@0: michael@0: NS_INTERFACE_MAP_BEGIN(nsJSON) michael@0: NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIJSON) michael@0: NS_INTERFACE_MAP_ENTRY(nsIJSON) michael@0: NS_INTERFACE_MAP_END michael@0: michael@0: NS_IMPL_ADDREF(nsJSON) michael@0: NS_IMPL_RELEASE(nsJSON) michael@0: michael@0: nsJSON::nsJSON() michael@0: { michael@0: } michael@0: michael@0: nsJSON::~nsJSON() michael@0: { michael@0: } michael@0: michael@0: enum DeprecationWarning { EncodeWarning, DecodeWarning }; michael@0: michael@0: static nsresult michael@0: WarnDeprecatedMethod(DeprecationWarning warning) michael@0: { michael@0: return nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, michael@0: NS_LITERAL_CSTRING("DOM Core"), nullptr, michael@0: nsContentUtils::eDOM_PROPERTIES, michael@0: warning == EncodeWarning michael@0: ? "nsIJSONEncodeDeprecatedWarning" michael@0: : "nsIJSONDecodeDeprecatedWarning"); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSON::Encode(JS::Handle aValue, JSContext* cx, uint8_t aArgc, michael@0: nsAString &aJSON) michael@0: { michael@0: // This function should only be called from JS. michael@0: nsresult rv = WarnDeprecatedMethod(EncodeWarning); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: if (aArgc == 0) { michael@0: aJSON.Truncate(); michael@0: aJSON.SetIsVoid(true); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsJSONWriter writer; michael@0: rv = EncodeInternal(cx, aValue, &writer); michael@0: michael@0: // FIXME: bug 408838. Get exception types sorted out michael@0: if (NS_SUCCEEDED(rv) || rv == NS_ERROR_INVALID_ARG) { michael@0: rv = NS_OK; michael@0: // if we didn't consume anything, it's not JSON, so return null michael@0: if (!writer.DidWrite()) { michael@0: aJSON.Truncate(); michael@0: aJSON.SetIsVoid(true); michael@0: } else { michael@0: writer.FlushBuffer(); michael@0: aJSON.Append(writer.mOutputString); michael@0: } michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: static const char UTF8BOM[] = "\xEF\xBB\xBF"; michael@0: static const char UTF16LEBOM[] = "\xFF\xFE"; michael@0: static const char UTF16BEBOM[] = "\xFE\xFF"; michael@0: michael@0: static nsresult CheckCharset(const char* aCharset) michael@0: { michael@0: // Check that the charset is permissible michael@0: if (!(strcmp(aCharset, "UTF-8") == 0 || michael@0: strcmp(aCharset, "UTF-16LE") == 0 || michael@0: strcmp(aCharset, "UTF-16BE") == 0)) { michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSON::EncodeToStream(nsIOutputStream *aStream, michael@0: const char* aCharset, michael@0: const bool aWriteBOM, michael@0: JS::Handle val, michael@0: JSContext* cx, michael@0: uint8_t aArgc) michael@0: { michael@0: // This function should only be called from JS. michael@0: NS_ENSURE_ARG(aStream); michael@0: nsresult rv; michael@0: michael@0: rv = CheckCharset(aCharset); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // Check to see if we have a buffered stream michael@0: nsCOMPtr bufferedStream; michael@0: // FIXME: bug 408514. michael@0: // NS_OutputStreamIsBuffered(aStream) asserts on file streams... michael@0: //if (!NS_OutputStreamIsBuffered(aStream)) { michael@0: rv = NS_NewBufferedOutputStream(getter_AddRefs(bufferedStream), michael@0: aStream, 4096); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: // aStream = bufferedStream; michael@0: //} michael@0: michael@0: uint32_t ignored; michael@0: if (aWriteBOM) { michael@0: if (strcmp(aCharset, "UTF-8") == 0) michael@0: rv = aStream->Write(UTF8BOM, 3, &ignored); michael@0: else if (strcmp(aCharset, "UTF-16LE") == 0) michael@0: rv = aStream->Write(UTF16LEBOM, 2, &ignored); michael@0: else if (strcmp(aCharset, "UTF-16BE") == 0) michael@0: rv = aStream->Write(UTF16BEBOM, 2, &ignored); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: nsJSONWriter writer(bufferedStream); michael@0: rv = writer.SetCharset(aCharset); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (aArgc == 0) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: rv = EncodeInternal(cx, val, &writer); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = bufferedStream->Flush(); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: static bool michael@0: WriteCallback(const jschar *buf, uint32_t len, void *data) michael@0: { michael@0: nsJSONWriter *writer = static_cast(data); michael@0: nsresult rv = writer->Write((const char16_t*)buf, (uint32_t)len); michael@0: if (NS_FAILED(rv)) michael@0: return false; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSON::EncodeFromJSVal(JS::Value *value, JSContext *cx, nsAString &result) michael@0: { michael@0: result.Truncate(); michael@0: michael@0: mozilla::Maybe ac; michael@0: if (value->isObject()) { michael@0: JS::Rooted obj(cx, &value->toObject()); michael@0: ac.construct(cx, obj); michael@0: } michael@0: michael@0: nsJSONWriter writer; michael@0: JS::Rooted vp(cx, *value); michael@0: if (!JS_Stringify(cx, &vp, JS::NullPtr(), JS::NullHandleValue, WriteCallback, &writer)) { michael@0: return NS_ERROR_XPC_BAD_CONVERT_JS; michael@0: } michael@0: *value = vp; michael@0: michael@0: NS_ENSURE_TRUE(writer.DidWrite(), NS_ERROR_UNEXPECTED); michael@0: writer.FlushBuffer(); michael@0: result.Assign(writer.mOutputString); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsJSON::EncodeInternal(JSContext* cx, const JS::Value& aValue, michael@0: nsJSONWriter* writer) michael@0: { michael@0: // Backward compatibility: michael@0: // nsIJSON does not allow to serialize anything other than objects michael@0: if (!aValue.isObject()) { michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: JS::Rooted obj(cx, &aValue.toObject()); michael@0: michael@0: /* Backward compatibility: michael@0: * Manually call toJSON if implemented by the object and check that michael@0: * the result is still an object michael@0: * Note: It is perfectly fine to not implement toJSON, so it is michael@0: * perfectly fine for GetMethod to fail michael@0: */ michael@0: JS::Rooted val(cx, aValue); michael@0: JS::Rooted toJSON(cx); michael@0: if (JS_GetProperty(cx, obj, "toJSON", &toJSON) && michael@0: toJSON.isObject() && michael@0: JS_ObjectIsCallable(cx, &toJSON.toObject())) { michael@0: // If toJSON is implemented, it must not throw michael@0: if (!JS_CallFunctionValue(cx, obj, toJSON, JS::HandleValueArray::empty(), &val)) { michael@0: if (JS_IsExceptionPending(cx)) michael@0: // passing NS_OK will throw the pending exception michael@0: return NS_OK; michael@0: michael@0: // No exception, but still failed michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: // Backward compatibility: michael@0: // nsIJSON does not allow to serialize anything other than objects michael@0: if (val.isPrimitive()) michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: // GetMethod may have thrown michael@0: else if (JS_IsExceptionPending(cx)) michael@0: // passing NS_OK will throw the pending exception michael@0: return NS_OK; michael@0: michael@0: // Backward compatibility: michael@0: // function shall not pass, just "plain" objects and arrays michael@0: JSType type = JS_TypeOfValue(cx, val); michael@0: if (type == JSTYPE_FUNCTION) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: // We're good now; try to stringify michael@0: if (!JS_Stringify(cx, &val, JS::NullPtr(), JS::NullHandleValue, WriteCallback, writer)) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: nsJSONWriter::nsJSONWriter() : mStream(nullptr), michael@0: mBuffer(nullptr), michael@0: mBufferCount(0), michael@0: mDidWrite(false), michael@0: mEncoder(nullptr) michael@0: { michael@0: } michael@0: michael@0: nsJSONWriter::nsJSONWriter(nsIOutputStream *aStream) : mStream(aStream), michael@0: mBuffer(nullptr), michael@0: mBufferCount(0), michael@0: mDidWrite(false), michael@0: mEncoder(nullptr) michael@0: { michael@0: } michael@0: michael@0: nsJSONWriter::~nsJSONWriter() michael@0: { michael@0: delete [] mBuffer; michael@0: } michael@0: michael@0: nsresult michael@0: nsJSONWriter::SetCharset(const char* aCharset) michael@0: { michael@0: nsresult rv = NS_OK; michael@0: if (mStream) { michael@0: mEncoder = EncodingUtils::EncoderForEncoding(aCharset); michael@0: rv = mEncoder->SetOutputErrorBehavior(nsIUnicodeEncoder::kOnError_Signal, michael@0: nullptr, '\0'); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: nsJSONWriter::Write(const char16_t *aBuffer, uint32_t aLength) michael@0: { michael@0: if (mStream) { michael@0: return WriteToStream(mStream, mEncoder, aBuffer, aLength); michael@0: } michael@0: michael@0: if (!mDidWrite) { michael@0: mBuffer = new char16_t[JSON_STREAM_BUFSIZE]; michael@0: if (!mBuffer) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: mDidWrite = true; michael@0: } michael@0: michael@0: if (JSON_STREAM_BUFSIZE <= aLength + mBufferCount) { michael@0: mOutputString.Append(mBuffer, mBufferCount); michael@0: mBufferCount = 0; michael@0: } michael@0: michael@0: if (JSON_STREAM_BUFSIZE <= aLength) { michael@0: // we know mBufferCount is 0 because we know we hit the if above michael@0: mOutputString.Append(aBuffer, aLength); michael@0: } else { michael@0: memcpy(&mBuffer[mBufferCount], aBuffer, aLength * sizeof(char16_t)); michael@0: mBufferCount += aLength; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool nsJSONWriter::DidWrite() michael@0: { michael@0: return mDidWrite; michael@0: } michael@0: michael@0: void michael@0: nsJSONWriter::FlushBuffer() michael@0: { michael@0: mOutputString.Append(mBuffer, mBufferCount); michael@0: } michael@0: michael@0: nsresult michael@0: nsJSONWriter::WriteToStream(nsIOutputStream *aStream, michael@0: nsIUnicodeEncoder *encoder, michael@0: const char16_t *aBuffer, michael@0: uint32_t aLength) michael@0: { michael@0: nsresult rv; michael@0: int32_t srcLength = aLength; michael@0: uint32_t bytesWritten; michael@0: michael@0: // The bytes written to the stream might differ from the char16_t size michael@0: int32_t aDestLength; michael@0: rv = encoder->GetMaxLength(aBuffer, srcLength, &aDestLength); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // create the buffer we need michael@0: char* destBuf = (char *) NS_Alloc(aDestLength); michael@0: if (!destBuf) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: rv = encoder->Convert(aBuffer, &srcLength, destBuf, &aDestLength); michael@0: if (NS_SUCCEEDED(rv)) michael@0: rv = aStream->Write(destBuf, aDestLength, &bytesWritten); michael@0: michael@0: NS_Free(destBuf); michael@0: mDidWrite = true; michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSON::Decode(const nsAString& json, JSContext* cx, michael@0: JS::MutableHandle aRetval) michael@0: { michael@0: nsresult rv = WarnDeprecatedMethod(DecodeWarning); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: const char16_t *data; michael@0: uint32_t len = NS_StringGetData(json, &data); michael@0: nsCOMPtr stream; michael@0: rv = NS_NewByteInputStream(getter_AddRefs(stream), michael@0: reinterpret_cast(data), michael@0: len * sizeof(char16_t), michael@0: NS_ASSIGNMENT_DEPEND); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: return DecodeInternal(cx, stream, len, false, aRetval); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSON::DecodeFromStream(nsIInputStream *aStream, int32_t aContentLength, michael@0: JSContext* cx, JS::MutableHandle aRetval) michael@0: { michael@0: return DecodeInternal(cx, aStream, aContentLength, true, aRetval); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSON::DecodeToJSVal(const nsAString &str, JSContext *cx, michael@0: JS::MutableHandle result) michael@0: { michael@0: if (!JS_ParseJSON(cx, static_cast(PromiseFlatString(str).get()), michael@0: str.Length(), result)) { michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsJSON::DecodeInternal(JSContext* cx, michael@0: nsIInputStream *aStream, michael@0: int32_t aContentLength, michael@0: bool aNeedsConverter, michael@0: JS::MutableHandle aRetval) michael@0: { michael@0: // Consume the stream michael@0: nsCOMPtr jsonChannel; michael@0: if (!mURI) { michael@0: NS_NewURI(getter_AddRefs(mURI), NS_LITERAL_CSTRING("about:blank"), 0, 0 ); michael@0: if (!mURI) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: nsresult rv = michael@0: NS_NewInputStreamChannel(getter_AddRefs(jsonChannel), mURI, aStream, michael@0: NS_LITERAL_CSTRING("application/json")); michael@0: if (!jsonChannel || NS_FAILED(rv)) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsRefPtr jsonListener = michael@0: new nsJSONListener(cx, aRetval.address(), aNeedsConverter); michael@0: michael@0: //XXX this stream pattern should be consolidated in netwerk michael@0: rv = jsonListener->OnStartRequest(jsonChannel, nullptr); michael@0: if (NS_FAILED(rv)) { michael@0: jsonChannel->Cancel(rv); michael@0: return rv; michael@0: } michael@0: michael@0: nsresult status; michael@0: jsonChannel->GetStatus(&status); michael@0: uint64_t offset = 0; michael@0: while (NS_SUCCEEDED(status)) { michael@0: uint64_t available; michael@0: rv = aStream->Available(&available); michael@0: if (rv == NS_BASE_STREAM_CLOSED) { michael@0: rv = NS_OK; michael@0: break; michael@0: } michael@0: if (NS_FAILED(rv)) { michael@0: jsonChannel->Cancel(rv); michael@0: break; michael@0: } michael@0: if (!available) michael@0: break; // blocking input stream has none available when done michael@0: michael@0: if (available > UINT32_MAX) michael@0: available = UINT32_MAX; michael@0: michael@0: rv = jsonListener->OnDataAvailable(jsonChannel, nullptr, michael@0: aStream, michael@0: offset, michael@0: (uint32_t)available); michael@0: if (NS_FAILED(rv)) { michael@0: jsonChannel->Cancel(rv); michael@0: break; michael@0: } michael@0: michael@0: offset += available; michael@0: jsonChannel->GetStatus(&status); michael@0: } michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = jsonListener->OnStopRequest(jsonChannel, nullptr, status); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: NS_NewJSON(nsISupports* aOuter, REFNSIID aIID, void** aResult) michael@0: { michael@0: nsJSON* json = new nsJSON(); michael@0: if (!json) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: NS_ADDREF(json); michael@0: *aResult = json; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsJSONListener::nsJSONListener(JSContext *cx, JS::Value *rootVal, michael@0: bool needsConverter) michael@0: : mNeedsConverter(needsConverter), michael@0: mCx(cx), michael@0: mRootVal(rootVal) michael@0: { michael@0: } michael@0: michael@0: nsJSONListener::~nsJSONListener() michael@0: { michael@0: } michael@0: michael@0: NS_INTERFACE_MAP_BEGIN(nsJSONListener) michael@0: NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsJSONListener) michael@0: NS_INTERFACE_MAP_ENTRY(nsIRequestObserver) michael@0: NS_INTERFACE_MAP_ENTRY(nsIStreamListener) michael@0: NS_INTERFACE_MAP_END michael@0: michael@0: NS_IMPL_ADDREF(nsJSONListener) michael@0: NS_IMPL_RELEASE(nsJSONListener) michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSONListener::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext) michael@0: { michael@0: mSniffBuffer.Truncate(); michael@0: mDecoder = nullptr; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSONListener::OnStopRequest(nsIRequest *aRequest, nsISupports *aContext, michael@0: nsresult aStatusCode) michael@0: { michael@0: nsresult rv; michael@0: michael@0: // This can happen with short UTF-8 messages (<4 bytes) michael@0: if (!mSniffBuffer.IsEmpty()) { michael@0: // Just consume mSniffBuffer michael@0: rv = ProcessBytes(nullptr, 0); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: JS::Rooted reviver(mCx, JS::NullValue()), value(mCx); michael@0: michael@0: JS::ConstTwoByteChars chars(reinterpret_cast(mBufferedChars.Elements()), michael@0: mBufferedChars.Length()); michael@0: bool ok = JS_ParseJSONWithReviver(mCx, chars.get(), michael@0: uint32_t(mBufferedChars.Length()), michael@0: reviver, &value); michael@0: michael@0: *mRootVal = value; michael@0: mBufferedChars.TruncateLength(0); michael@0: return ok ? NS_OK : NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSONListener::OnDataAvailable(nsIRequest *aRequest, nsISupports *aContext, michael@0: nsIInputStream *aStream, michael@0: uint64_t aOffset, uint32_t aLength) michael@0: { michael@0: nsresult rv = NS_OK; michael@0: michael@0: if (mNeedsConverter && mSniffBuffer.Length() < 4) { michael@0: uint32_t readCount = (aLength < 4) ? aLength : 4; michael@0: rv = NS_ConsumeStream(aStream, readCount, mSniffBuffer); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (mSniffBuffer.Length() < 4) michael@0: return NS_OK; michael@0: } michael@0: michael@0: char buffer[JSON_STREAM_BUFSIZE]; michael@0: unsigned long bytesRemaining = aLength - mSniffBuffer.Length(); michael@0: while (bytesRemaining) { michael@0: unsigned int bytesRead; michael@0: rv = aStream->Read(buffer, michael@0: std::min((unsigned long)sizeof(buffer), bytesRemaining), michael@0: &bytesRead); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: rv = ProcessBytes(buffer, bytesRead); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: bytesRemaining -= bytesRead; michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: nsJSONListener::ProcessBytes(const char* aBuffer, uint32_t aByteLength) michael@0: { michael@0: nsresult rv; michael@0: // Check for BOM, or sniff charset michael@0: nsAutoCString charset; michael@0: if (mNeedsConverter && !mDecoder) { michael@0: if (!nsContentUtils::CheckForBOM((const unsigned char*) mSniffBuffer.get(), michael@0: mSniffBuffer.Length(), charset)) { michael@0: // OK, found no BOM, sniff the first character to see what this is michael@0: // See section 3 of RFC4627 for details on why this works. michael@0: const char *buffer = mSniffBuffer.get(); michael@0: if (mSniffBuffer.Length() >= 4) { michael@0: if (buffer[0] == 0x00 && buffer[1] != 0x00 && michael@0: buffer[2] == 0x00 && buffer[3] != 0x00) { michael@0: charset = "UTF-16BE"; michael@0: } else if (buffer[0] != 0x00 && buffer[1] == 0x00 && michael@0: buffer[2] != 0x00 && buffer[3] == 0x00) { michael@0: charset = "UTF-16LE"; michael@0: } else if (buffer[0] != 0x00 && buffer[1] != 0x00 && michael@0: buffer[2] != 0x00 && buffer[3] != 0x00) { michael@0: charset = "UTF-8"; michael@0: } michael@0: } else { michael@0: // Not enough bytes to sniff, assume UTF-8 michael@0: charset = "UTF-8"; michael@0: } michael@0: } michael@0: michael@0: // We should have a unicode charset by now michael@0: rv = CheckCharset(charset.get()); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: mDecoder = EncodingUtils::DecoderForEncoding(charset); michael@0: michael@0: // consume the sniffed bytes michael@0: rv = ConsumeConverted(mSniffBuffer.get(), mSniffBuffer.Length()); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: mSniffBuffer.Truncate(); michael@0: } michael@0: michael@0: if (!aBuffer) michael@0: return NS_OK; michael@0: michael@0: if (mNeedsConverter) { michael@0: rv = ConsumeConverted(aBuffer, aByteLength); michael@0: } else { michael@0: uint32_t unichars = aByteLength / sizeof(char16_t); michael@0: rv = Consume((char16_t *) aBuffer, unichars); michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: nsJSONListener::ConsumeConverted(const char* aBuffer, uint32_t aByteLength) michael@0: { michael@0: nsresult rv; michael@0: int32_t unicharLength = 0; michael@0: int32_t srcLen = aByteLength; michael@0: michael@0: rv = mDecoder->GetMaxLength(aBuffer, srcLen, &unicharLength); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: char16_t* endelems = mBufferedChars.AppendElements(unicharLength); michael@0: int32_t preLength = unicharLength; michael@0: rv = mDecoder->Convert(aBuffer, &srcLen, endelems, &unicharLength); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: NS_ABORT_IF_FALSE(preLength >= unicharLength, "GetMaxLength lied"); michael@0: if (preLength > unicharLength) michael@0: mBufferedChars.TruncateLength(mBufferedChars.Length() - (preLength - unicharLength)); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsJSONListener::Consume(const char16_t* aBuffer, uint32_t aByteLength) michael@0: { michael@0: if (!mBufferedChars.AppendElements(aBuffer, aByteLength)) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: return NS_OK; michael@0: }