1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/src/json/nsJSON.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,652 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ts=2 sw=2 et tw=79: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "jsapi.h" 1.11 +#include "js/CharacterEncoding.h" 1.12 +#include "js/OldDebugAPI.h" 1.13 +#include "nsJSON.h" 1.14 +#include "nsIXPConnect.h" 1.15 +#include "nsIXPCScriptable.h" 1.16 +#include "nsStreamUtils.h" 1.17 +#include "nsIInputStream.h" 1.18 +#include "nsStringStream.h" 1.19 +#include "mozilla/dom/EncodingUtils.h" 1.20 +#include "nsIUnicodeEncoder.h" 1.21 +#include "nsIUnicodeDecoder.h" 1.22 +#include "nsXPCOMStrings.h" 1.23 +#include "nsNetUtil.h" 1.24 +#include "nsContentUtils.h" 1.25 +#include "nsIScriptError.h" 1.26 +#include "nsCRTGlue.h" 1.27 +#include "nsAutoPtr.h" 1.28 +#include "nsIScriptSecurityManager.h" 1.29 +#include "mozilla/Maybe.h" 1.30 +#include <algorithm> 1.31 + 1.32 +using mozilla::dom::EncodingUtils; 1.33 + 1.34 +#define JSON_STREAM_BUFSIZE 4096 1.35 + 1.36 +NS_INTERFACE_MAP_BEGIN(nsJSON) 1.37 + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIJSON) 1.38 + NS_INTERFACE_MAP_ENTRY(nsIJSON) 1.39 +NS_INTERFACE_MAP_END 1.40 + 1.41 +NS_IMPL_ADDREF(nsJSON) 1.42 +NS_IMPL_RELEASE(nsJSON) 1.43 + 1.44 +nsJSON::nsJSON() 1.45 +{ 1.46 +} 1.47 + 1.48 +nsJSON::~nsJSON() 1.49 +{ 1.50 +} 1.51 + 1.52 +enum DeprecationWarning { EncodeWarning, DecodeWarning }; 1.53 + 1.54 +static nsresult 1.55 +WarnDeprecatedMethod(DeprecationWarning warning) 1.56 +{ 1.57 + return nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, 1.58 + NS_LITERAL_CSTRING("DOM Core"), nullptr, 1.59 + nsContentUtils::eDOM_PROPERTIES, 1.60 + warning == EncodeWarning 1.61 + ? "nsIJSONEncodeDeprecatedWarning" 1.62 + : "nsIJSONDecodeDeprecatedWarning"); 1.63 +} 1.64 + 1.65 +NS_IMETHODIMP 1.66 +nsJSON::Encode(JS::Handle<JS::Value> aValue, JSContext* cx, uint8_t aArgc, 1.67 + nsAString &aJSON) 1.68 +{ 1.69 + // This function should only be called from JS. 1.70 + nsresult rv = WarnDeprecatedMethod(EncodeWarning); 1.71 + if (NS_FAILED(rv)) 1.72 + return rv; 1.73 + 1.74 + if (aArgc == 0) { 1.75 + aJSON.Truncate(); 1.76 + aJSON.SetIsVoid(true); 1.77 + return NS_OK; 1.78 + } 1.79 + 1.80 + nsJSONWriter writer; 1.81 + rv = EncodeInternal(cx, aValue, &writer); 1.82 + 1.83 + // FIXME: bug 408838. Get exception types sorted out 1.84 + if (NS_SUCCEEDED(rv) || rv == NS_ERROR_INVALID_ARG) { 1.85 + rv = NS_OK; 1.86 + // if we didn't consume anything, it's not JSON, so return null 1.87 + if (!writer.DidWrite()) { 1.88 + aJSON.Truncate(); 1.89 + aJSON.SetIsVoid(true); 1.90 + } else { 1.91 + writer.FlushBuffer(); 1.92 + aJSON.Append(writer.mOutputString); 1.93 + } 1.94 + } 1.95 + 1.96 + return rv; 1.97 +} 1.98 + 1.99 +static const char UTF8BOM[] = "\xEF\xBB\xBF"; 1.100 +static const char UTF16LEBOM[] = "\xFF\xFE"; 1.101 +static const char UTF16BEBOM[] = "\xFE\xFF"; 1.102 + 1.103 +static nsresult CheckCharset(const char* aCharset) 1.104 +{ 1.105 + // Check that the charset is permissible 1.106 + if (!(strcmp(aCharset, "UTF-8") == 0 || 1.107 + strcmp(aCharset, "UTF-16LE") == 0 || 1.108 + strcmp(aCharset, "UTF-16BE") == 0)) { 1.109 + return NS_ERROR_INVALID_ARG; 1.110 + } 1.111 + 1.112 + return NS_OK; 1.113 +} 1.114 + 1.115 +NS_IMETHODIMP 1.116 +nsJSON::EncodeToStream(nsIOutputStream *aStream, 1.117 + const char* aCharset, 1.118 + const bool aWriteBOM, 1.119 + JS::Handle<JS::Value> val, 1.120 + JSContext* cx, 1.121 + uint8_t aArgc) 1.122 +{ 1.123 + // This function should only be called from JS. 1.124 + NS_ENSURE_ARG(aStream); 1.125 + nsresult rv; 1.126 + 1.127 + rv = CheckCharset(aCharset); 1.128 + NS_ENSURE_SUCCESS(rv, rv); 1.129 + 1.130 + // Check to see if we have a buffered stream 1.131 + nsCOMPtr<nsIOutputStream> bufferedStream; 1.132 + // FIXME: bug 408514. 1.133 + // NS_OutputStreamIsBuffered(aStream) asserts on file streams... 1.134 + //if (!NS_OutputStreamIsBuffered(aStream)) { 1.135 + rv = NS_NewBufferedOutputStream(getter_AddRefs(bufferedStream), 1.136 + aStream, 4096); 1.137 + NS_ENSURE_SUCCESS(rv, rv); 1.138 + // aStream = bufferedStream; 1.139 + //} 1.140 + 1.141 + uint32_t ignored; 1.142 + if (aWriteBOM) { 1.143 + if (strcmp(aCharset, "UTF-8") == 0) 1.144 + rv = aStream->Write(UTF8BOM, 3, &ignored); 1.145 + else if (strcmp(aCharset, "UTF-16LE") == 0) 1.146 + rv = aStream->Write(UTF16LEBOM, 2, &ignored); 1.147 + else if (strcmp(aCharset, "UTF-16BE") == 0) 1.148 + rv = aStream->Write(UTF16BEBOM, 2, &ignored); 1.149 + NS_ENSURE_SUCCESS(rv, rv); 1.150 + } 1.151 + 1.152 + nsJSONWriter writer(bufferedStream); 1.153 + rv = writer.SetCharset(aCharset); 1.154 + NS_ENSURE_SUCCESS(rv, rv); 1.155 + 1.156 + if (aArgc == 0) { 1.157 + return NS_OK; 1.158 + } 1.159 + 1.160 + rv = EncodeInternal(cx, val, &writer); 1.161 + NS_ENSURE_SUCCESS(rv, rv); 1.162 + 1.163 + rv = bufferedStream->Flush(); 1.164 + 1.165 + return rv; 1.166 +} 1.167 + 1.168 +static bool 1.169 +WriteCallback(const jschar *buf, uint32_t len, void *data) 1.170 +{ 1.171 + nsJSONWriter *writer = static_cast<nsJSONWriter*>(data); 1.172 + nsresult rv = writer->Write((const char16_t*)buf, (uint32_t)len); 1.173 + if (NS_FAILED(rv)) 1.174 + return false; 1.175 + 1.176 + return true; 1.177 +} 1.178 + 1.179 +NS_IMETHODIMP 1.180 +nsJSON::EncodeFromJSVal(JS::Value *value, JSContext *cx, nsAString &result) 1.181 +{ 1.182 + result.Truncate(); 1.183 + 1.184 + mozilla::Maybe<JSAutoCompartment> ac; 1.185 + if (value->isObject()) { 1.186 + JS::Rooted<JSObject*> obj(cx, &value->toObject()); 1.187 + ac.construct(cx, obj); 1.188 + } 1.189 + 1.190 + nsJSONWriter writer; 1.191 + JS::Rooted<JS::Value> vp(cx, *value); 1.192 + if (!JS_Stringify(cx, &vp, JS::NullPtr(), JS::NullHandleValue, WriteCallback, &writer)) { 1.193 + return NS_ERROR_XPC_BAD_CONVERT_JS; 1.194 + } 1.195 + *value = vp; 1.196 + 1.197 + NS_ENSURE_TRUE(writer.DidWrite(), NS_ERROR_UNEXPECTED); 1.198 + writer.FlushBuffer(); 1.199 + result.Assign(writer.mOutputString); 1.200 + return NS_OK; 1.201 +} 1.202 + 1.203 +nsresult 1.204 +nsJSON::EncodeInternal(JSContext* cx, const JS::Value& aValue, 1.205 + nsJSONWriter* writer) 1.206 +{ 1.207 + // Backward compatibility: 1.208 + // nsIJSON does not allow to serialize anything other than objects 1.209 + if (!aValue.isObject()) { 1.210 + return NS_ERROR_INVALID_ARG; 1.211 + } 1.212 + JS::Rooted<JSObject*> obj(cx, &aValue.toObject()); 1.213 + 1.214 + /* Backward compatibility: 1.215 + * Manually call toJSON if implemented by the object and check that 1.216 + * the result is still an object 1.217 + * Note: It is perfectly fine to not implement toJSON, so it is 1.218 + * perfectly fine for GetMethod to fail 1.219 + */ 1.220 + JS::Rooted<JS::Value> val(cx, aValue); 1.221 + JS::Rooted<JS::Value> toJSON(cx); 1.222 + if (JS_GetProperty(cx, obj, "toJSON", &toJSON) && 1.223 + toJSON.isObject() && 1.224 + JS_ObjectIsCallable(cx, &toJSON.toObject())) { 1.225 + // If toJSON is implemented, it must not throw 1.226 + if (!JS_CallFunctionValue(cx, obj, toJSON, JS::HandleValueArray::empty(), &val)) { 1.227 + if (JS_IsExceptionPending(cx)) 1.228 + // passing NS_OK will throw the pending exception 1.229 + return NS_OK; 1.230 + 1.231 + // No exception, but still failed 1.232 + return NS_ERROR_FAILURE; 1.233 + } 1.234 + 1.235 + // Backward compatibility: 1.236 + // nsIJSON does not allow to serialize anything other than objects 1.237 + if (val.isPrimitive()) 1.238 + return NS_ERROR_INVALID_ARG; 1.239 + } 1.240 + // GetMethod may have thrown 1.241 + else if (JS_IsExceptionPending(cx)) 1.242 + // passing NS_OK will throw the pending exception 1.243 + return NS_OK; 1.244 + 1.245 + // Backward compatibility: 1.246 + // function shall not pass, just "plain" objects and arrays 1.247 + JSType type = JS_TypeOfValue(cx, val); 1.248 + if (type == JSTYPE_FUNCTION) 1.249 + return NS_ERROR_INVALID_ARG; 1.250 + 1.251 + // We're good now; try to stringify 1.252 + if (!JS_Stringify(cx, &val, JS::NullPtr(), JS::NullHandleValue, WriteCallback, writer)) 1.253 + return NS_ERROR_FAILURE; 1.254 + 1.255 + return NS_OK; 1.256 +} 1.257 + 1.258 + 1.259 +nsJSONWriter::nsJSONWriter() : mStream(nullptr), 1.260 + mBuffer(nullptr), 1.261 + mBufferCount(0), 1.262 + mDidWrite(false), 1.263 + mEncoder(nullptr) 1.264 +{ 1.265 +} 1.266 + 1.267 +nsJSONWriter::nsJSONWriter(nsIOutputStream *aStream) : mStream(aStream), 1.268 + mBuffer(nullptr), 1.269 + mBufferCount(0), 1.270 + mDidWrite(false), 1.271 + mEncoder(nullptr) 1.272 +{ 1.273 +} 1.274 + 1.275 +nsJSONWriter::~nsJSONWriter() 1.276 +{ 1.277 + delete [] mBuffer; 1.278 +} 1.279 + 1.280 +nsresult 1.281 +nsJSONWriter::SetCharset(const char* aCharset) 1.282 +{ 1.283 + nsresult rv = NS_OK; 1.284 + if (mStream) { 1.285 + mEncoder = EncodingUtils::EncoderForEncoding(aCharset); 1.286 + rv = mEncoder->SetOutputErrorBehavior(nsIUnicodeEncoder::kOnError_Signal, 1.287 + nullptr, '\0'); 1.288 + NS_ENSURE_SUCCESS(rv, rv); 1.289 + } 1.290 + 1.291 + return rv; 1.292 +} 1.293 + 1.294 +nsresult 1.295 +nsJSONWriter::Write(const char16_t *aBuffer, uint32_t aLength) 1.296 +{ 1.297 + if (mStream) { 1.298 + return WriteToStream(mStream, mEncoder, aBuffer, aLength); 1.299 + } 1.300 + 1.301 + if (!mDidWrite) { 1.302 + mBuffer = new char16_t[JSON_STREAM_BUFSIZE]; 1.303 + if (!mBuffer) 1.304 + return NS_ERROR_OUT_OF_MEMORY; 1.305 + mDidWrite = true; 1.306 + } 1.307 + 1.308 + if (JSON_STREAM_BUFSIZE <= aLength + mBufferCount) { 1.309 + mOutputString.Append(mBuffer, mBufferCount); 1.310 + mBufferCount = 0; 1.311 + } 1.312 + 1.313 + if (JSON_STREAM_BUFSIZE <= aLength) { 1.314 + // we know mBufferCount is 0 because we know we hit the if above 1.315 + mOutputString.Append(aBuffer, aLength); 1.316 + } else { 1.317 + memcpy(&mBuffer[mBufferCount], aBuffer, aLength * sizeof(char16_t)); 1.318 + mBufferCount += aLength; 1.319 + } 1.320 + 1.321 + return NS_OK; 1.322 +} 1.323 + 1.324 +bool nsJSONWriter::DidWrite() 1.325 +{ 1.326 + return mDidWrite; 1.327 +} 1.328 + 1.329 +void 1.330 +nsJSONWriter::FlushBuffer() 1.331 +{ 1.332 + mOutputString.Append(mBuffer, mBufferCount); 1.333 +} 1.334 + 1.335 +nsresult 1.336 +nsJSONWriter::WriteToStream(nsIOutputStream *aStream, 1.337 + nsIUnicodeEncoder *encoder, 1.338 + const char16_t *aBuffer, 1.339 + uint32_t aLength) 1.340 +{ 1.341 + nsresult rv; 1.342 + int32_t srcLength = aLength; 1.343 + uint32_t bytesWritten; 1.344 + 1.345 + // The bytes written to the stream might differ from the char16_t size 1.346 + int32_t aDestLength; 1.347 + rv = encoder->GetMaxLength(aBuffer, srcLength, &aDestLength); 1.348 + NS_ENSURE_SUCCESS(rv, rv); 1.349 + 1.350 + // create the buffer we need 1.351 + char* destBuf = (char *) NS_Alloc(aDestLength); 1.352 + if (!destBuf) 1.353 + return NS_ERROR_OUT_OF_MEMORY; 1.354 + 1.355 + rv = encoder->Convert(aBuffer, &srcLength, destBuf, &aDestLength); 1.356 + if (NS_SUCCEEDED(rv)) 1.357 + rv = aStream->Write(destBuf, aDestLength, &bytesWritten); 1.358 + 1.359 + NS_Free(destBuf); 1.360 + mDidWrite = true; 1.361 + 1.362 + return rv; 1.363 +} 1.364 + 1.365 +NS_IMETHODIMP 1.366 +nsJSON::Decode(const nsAString& json, JSContext* cx, 1.367 + JS::MutableHandle<JS::Value> aRetval) 1.368 +{ 1.369 + nsresult rv = WarnDeprecatedMethod(DecodeWarning); 1.370 + if (NS_FAILED(rv)) 1.371 + return rv; 1.372 + 1.373 + const char16_t *data; 1.374 + uint32_t len = NS_StringGetData(json, &data); 1.375 + nsCOMPtr<nsIInputStream> stream; 1.376 + rv = NS_NewByteInputStream(getter_AddRefs(stream), 1.377 + reinterpret_cast<const char*>(data), 1.378 + len * sizeof(char16_t), 1.379 + NS_ASSIGNMENT_DEPEND); 1.380 + NS_ENSURE_SUCCESS(rv, rv); 1.381 + return DecodeInternal(cx, stream, len, false, aRetval); 1.382 +} 1.383 + 1.384 +NS_IMETHODIMP 1.385 +nsJSON::DecodeFromStream(nsIInputStream *aStream, int32_t aContentLength, 1.386 + JSContext* cx, JS::MutableHandle<JS::Value> aRetval) 1.387 +{ 1.388 + return DecodeInternal(cx, aStream, aContentLength, true, aRetval); 1.389 +} 1.390 + 1.391 +NS_IMETHODIMP 1.392 +nsJSON::DecodeToJSVal(const nsAString &str, JSContext *cx, 1.393 + JS::MutableHandle<JS::Value> result) 1.394 +{ 1.395 + if (!JS_ParseJSON(cx, static_cast<const jschar*>(PromiseFlatString(str).get()), 1.396 + str.Length(), result)) { 1.397 + return NS_ERROR_UNEXPECTED; 1.398 + } 1.399 + return NS_OK; 1.400 +} 1.401 + 1.402 +nsresult 1.403 +nsJSON::DecodeInternal(JSContext* cx, 1.404 + nsIInputStream *aStream, 1.405 + int32_t aContentLength, 1.406 + bool aNeedsConverter, 1.407 + JS::MutableHandle<JS::Value> aRetval) 1.408 +{ 1.409 + // Consume the stream 1.410 + nsCOMPtr<nsIChannel> jsonChannel; 1.411 + if (!mURI) { 1.412 + NS_NewURI(getter_AddRefs(mURI), NS_LITERAL_CSTRING("about:blank"), 0, 0 ); 1.413 + if (!mURI) 1.414 + return NS_ERROR_OUT_OF_MEMORY; 1.415 + } 1.416 + 1.417 + nsresult rv = 1.418 + NS_NewInputStreamChannel(getter_AddRefs(jsonChannel), mURI, aStream, 1.419 + NS_LITERAL_CSTRING("application/json")); 1.420 + if (!jsonChannel || NS_FAILED(rv)) 1.421 + return NS_ERROR_FAILURE; 1.422 + 1.423 + nsRefPtr<nsJSONListener> jsonListener = 1.424 + new nsJSONListener(cx, aRetval.address(), aNeedsConverter); 1.425 + 1.426 + //XXX this stream pattern should be consolidated in netwerk 1.427 + rv = jsonListener->OnStartRequest(jsonChannel, nullptr); 1.428 + if (NS_FAILED(rv)) { 1.429 + jsonChannel->Cancel(rv); 1.430 + return rv; 1.431 + } 1.432 + 1.433 + nsresult status; 1.434 + jsonChannel->GetStatus(&status); 1.435 + uint64_t offset = 0; 1.436 + while (NS_SUCCEEDED(status)) { 1.437 + uint64_t available; 1.438 + rv = aStream->Available(&available); 1.439 + if (rv == NS_BASE_STREAM_CLOSED) { 1.440 + rv = NS_OK; 1.441 + break; 1.442 + } 1.443 + if (NS_FAILED(rv)) { 1.444 + jsonChannel->Cancel(rv); 1.445 + break; 1.446 + } 1.447 + if (!available) 1.448 + break; // blocking input stream has none available when done 1.449 + 1.450 + if (available > UINT32_MAX) 1.451 + available = UINT32_MAX; 1.452 + 1.453 + rv = jsonListener->OnDataAvailable(jsonChannel, nullptr, 1.454 + aStream, 1.455 + offset, 1.456 + (uint32_t)available); 1.457 + if (NS_FAILED(rv)) { 1.458 + jsonChannel->Cancel(rv); 1.459 + break; 1.460 + } 1.461 + 1.462 + offset += available; 1.463 + jsonChannel->GetStatus(&status); 1.464 + } 1.465 + NS_ENSURE_SUCCESS(rv, rv); 1.466 + 1.467 + rv = jsonListener->OnStopRequest(jsonChannel, nullptr, status); 1.468 + NS_ENSURE_SUCCESS(rv, rv); 1.469 + 1.470 + return NS_OK; 1.471 +} 1.472 + 1.473 +nsresult 1.474 +NS_NewJSON(nsISupports* aOuter, REFNSIID aIID, void** aResult) 1.475 +{ 1.476 + nsJSON* json = new nsJSON(); 1.477 + if (!json) 1.478 + return NS_ERROR_OUT_OF_MEMORY; 1.479 + 1.480 + NS_ADDREF(json); 1.481 + *aResult = json; 1.482 + 1.483 + return NS_OK; 1.484 +} 1.485 + 1.486 +nsJSONListener::nsJSONListener(JSContext *cx, JS::Value *rootVal, 1.487 + bool needsConverter) 1.488 + : mNeedsConverter(needsConverter), 1.489 + mCx(cx), 1.490 + mRootVal(rootVal) 1.491 +{ 1.492 +} 1.493 + 1.494 +nsJSONListener::~nsJSONListener() 1.495 +{ 1.496 +} 1.497 + 1.498 +NS_INTERFACE_MAP_BEGIN(nsJSONListener) 1.499 + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsJSONListener) 1.500 + NS_INTERFACE_MAP_ENTRY(nsIRequestObserver) 1.501 + NS_INTERFACE_MAP_ENTRY(nsIStreamListener) 1.502 +NS_INTERFACE_MAP_END 1.503 + 1.504 +NS_IMPL_ADDREF(nsJSONListener) 1.505 +NS_IMPL_RELEASE(nsJSONListener) 1.506 + 1.507 +NS_IMETHODIMP 1.508 +nsJSONListener::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext) 1.509 +{ 1.510 + mSniffBuffer.Truncate(); 1.511 + mDecoder = nullptr; 1.512 + 1.513 + return NS_OK; 1.514 +} 1.515 + 1.516 +NS_IMETHODIMP 1.517 +nsJSONListener::OnStopRequest(nsIRequest *aRequest, nsISupports *aContext, 1.518 + nsresult aStatusCode) 1.519 +{ 1.520 + nsresult rv; 1.521 + 1.522 + // This can happen with short UTF-8 messages (<4 bytes) 1.523 + if (!mSniffBuffer.IsEmpty()) { 1.524 + // Just consume mSniffBuffer 1.525 + rv = ProcessBytes(nullptr, 0); 1.526 + NS_ENSURE_SUCCESS(rv, rv); 1.527 + } 1.528 + 1.529 + JS::Rooted<JS::Value> reviver(mCx, JS::NullValue()), value(mCx); 1.530 + 1.531 + JS::ConstTwoByteChars chars(reinterpret_cast<const jschar*>(mBufferedChars.Elements()), 1.532 + mBufferedChars.Length()); 1.533 + bool ok = JS_ParseJSONWithReviver(mCx, chars.get(), 1.534 + uint32_t(mBufferedChars.Length()), 1.535 + reviver, &value); 1.536 + 1.537 + *mRootVal = value; 1.538 + mBufferedChars.TruncateLength(0); 1.539 + return ok ? NS_OK : NS_ERROR_FAILURE; 1.540 +} 1.541 + 1.542 +NS_IMETHODIMP 1.543 +nsJSONListener::OnDataAvailable(nsIRequest *aRequest, nsISupports *aContext, 1.544 + nsIInputStream *aStream, 1.545 + uint64_t aOffset, uint32_t aLength) 1.546 +{ 1.547 + nsresult rv = NS_OK; 1.548 + 1.549 + if (mNeedsConverter && mSniffBuffer.Length() < 4) { 1.550 + uint32_t readCount = (aLength < 4) ? aLength : 4; 1.551 + rv = NS_ConsumeStream(aStream, readCount, mSniffBuffer); 1.552 + NS_ENSURE_SUCCESS(rv, rv); 1.553 + 1.554 + if (mSniffBuffer.Length() < 4) 1.555 + return NS_OK; 1.556 + } 1.557 + 1.558 + char buffer[JSON_STREAM_BUFSIZE]; 1.559 + unsigned long bytesRemaining = aLength - mSniffBuffer.Length(); 1.560 + while (bytesRemaining) { 1.561 + unsigned int bytesRead; 1.562 + rv = aStream->Read(buffer, 1.563 + std::min((unsigned long)sizeof(buffer), bytesRemaining), 1.564 + &bytesRead); 1.565 + NS_ENSURE_SUCCESS(rv, rv); 1.566 + rv = ProcessBytes(buffer, bytesRead); 1.567 + NS_ENSURE_SUCCESS(rv, rv); 1.568 + bytesRemaining -= bytesRead; 1.569 + } 1.570 + 1.571 + return rv; 1.572 +} 1.573 + 1.574 +nsresult 1.575 +nsJSONListener::ProcessBytes(const char* aBuffer, uint32_t aByteLength) 1.576 +{ 1.577 + nsresult rv; 1.578 + // Check for BOM, or sniff charset 1.579 + nsAutoCString charset; 1.580 + if (mNeedsConverter && !mDecoder) { 1.581 + if (!nsContentUtils::CheckForBOM((const unsigned char*) mSniffBuffer.get(), 1.582 + mSniffBuffer.Length(), charset)) { 1.583 + // OK, found no BOM, sniff the first character to see what this is 1.584 + // See section 3 of RFC4627 for details on why this works. 1.585 + const char *buffer = mSniffBuffer.get(); 1.586 + if (mSniffBuffer.Length() >= 4) { 1.587 + if (buffer[0] == 0x00 && buffer[1] != 0x00 && 1.588 + buffer[2] == 0x00 && buffer[3] != 0x00) { 1.589 + charset = "UTF-16BE"; 1.590 + } else if (buffer[0] != 0x00 && buffer[1] == 0x00 && 1.591 + buffer[2] != 0x00 && buffer[3] == 0x00) { 1.592 + charset = "UTF-16LE"; 1.593 + } else if (buffer[0] != 0x00 && buffer[1] != 0x00 && 1.594 + buffer[2] != 0x00 && buffer[3] != 0x00) { 1.595 + charset = "UTF-8"; 1.596 + } 1.597 + } else { 1.598 + // Not enough bytes to sniff, assume UTF-8 1.599 + charset = "UTF-8"; 1.600 + } 1.601 + } 1.602 + 1.603 + // We should have a unicode charset by now 1.604 + rv = CheckCharset(charset.get()); 1.605 + NS_ENSURE_SUCCESS(rv, rv); 1.606 + mDecoder = EncodingUtils::DecoderForEncoding(charset); 1.607 + 1.608 + // consume the sniffed bytes 1.609 + rv = ConsumeConverted(mSniffBuffer.get(), mSniffBuffer.Length()); 1.610 + NS_ENSURE_SUCCESS(rv, rv); 1.611 + mSniffBuffer.Truncate(); 1.612 + } 1.613 + 1.614 + if (!aBuffer) 1.615 + return NS_OK; 1.616 + 1.617 + if (mNeedsConverter) { 1.618 + rv = ConsumeConverted(aBuffer, aByteLength); 1.619 + } else { 1.620 + uint32_t unichars = aByteLength / sizeof(char16_t); 1.621 + rv = Consume((char16_t *) aBuffer, unichars); 1.622 + } 1.623 + 1.624 + return rv; 1.625 +} 1.626 + 1.627 +nsresult 1.628 +nsJSONListener::ConsumeConverted(const char* aBuffer, uint32_t aByteLength) 1.629 +{ 1.630 + nsresult rv; 1.631 + int32_t unicharLength = 0; 1.632 + int32_t srcLen = aByteLength; 1.633 + 1.634 + rv = mDecoder->GetMaxLength(aBuffer, srcLen, &unicharLength); 1.635 + NS_ENSURE_SUCCESS(rv, rv); 1.636 + 1.637 + char16_t* endelems = mBufferedChars.AppendElements(unicharLength); 1.638 + int32_t preLength = unicharLength; 1.639 + rv = mDecoder->Convert(aBuffer, &srcLen, endelems, &unicharLength); 1.640 + if (NS_FAILED(rv)) 1.641 + return rv; 1.642 + NS_ABORT_IF_FALSE(preLength >= unicharLength, "GetMaxLength lied"); 1.643 + if (preLength > unicharLength) 1.644 + mBufferedChars.TruncateLength(mBufferedChars.Length() - (preLength - unicharLength)); 1.645 + return NS_OK; 1.646 +} 1.647 + 1.648 +nsresult 1.649 +nsJSONListener::Consume(const char16_t* aBuffer, uint32_t aByteLength) 1.650 +{ 1.651 + if (!mBufferedChars.AppendElements(aBuffer, aByteLength)) 1.652 + return NS_ERROR_FAILURE; 1.653 + 1.654 + return NS_OK; 1.655 +}