dom/src/json/nsJSON.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim: set ts=2 sw=2 et tw=79: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "jsapi.h"
michael@0 8 #include "js/CharacterEncoding.h"
michael@0 9 #include "js/OldDebugAPI.h"
michael@0 10 #include "nsJSON.h"
michael@0 11 #include "nsIXPConnect.h"
michael@0 12 #include "nsIXPCScriptable.h"
michael@0 13 #include "nsStreamUtils.h"
michael@0 14 #include "nsIInputStream.h"
michael@0 15 #include "nsStringStream.h"
michael@0 16 #include "mozilla/dom/EncodingUtils.h"
michael@0 17 #include "nsIUnicodeEncoder.h"
michael@0 18 #include "nsIUnicodeDecoder.h"
michael@0 19 #include "nsXPCOMStrings.h"
michael@0 20 #include "nsNetUtil.h"
michael@0 21 #include "nsContentUtils.h"
michael@0 22 #include "nsIScriptError.h"
michael@0 23 #include "nsCRTGlue.h"
michael@0 24 #include "nsAutoPtr.h"
michael@0 25 #include "nsIScriptSecurityManager.h"
michael@0 26 #include "mozilla/Maybe.h"
michael@0 27 #include <algorithm>
michael@0 28
michael@0 29 using mozilla::dom::EncodingUtils;
michael@0 30
michael@0 31 #define JSON_STREAM_BUFSIZE 4096
michael@0 32
michael@0 33 NS_INTERFACE_MAP_BEGIN(nsJSON)
michael@0 34 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIJSON)
michael@0 35 NS_INTERFACE_MAP_ENTRY(nsIJSON)
michael@0 36 NS_INTERFACE_MAP_END
michael@0 37
michael@0 38 NS_IMPL_ADDREF(nsJSON)
michael@0 39 NS_IMPL_RELEASE(nsJSON)
michael@0 40
michael@0 41 nsJSON::nsJSON()
michael@0 42 {
michael@0 43 }
michael@0 44
michael@0 45 nsJSON::~nsJSON()
michael@0 46 {
michael@0 47 }
michael@0 48
michael@0 49 enum DeprecationWarning { EncodeWarning, DecodeWarning };
michael@0 50
michael@0 51 static nsresult
michael@0 52 WarnDeprecatedMethod(DeprecationWarning warning)
michael@0 53 {
michael@0 54 return nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
michael@0 55 NS_LITERAL_CSTRING("DOM Core"), nullptr,
michael@0 56 nsContentUtils::eDOM_PROPERTIES,
michael@0 57 warning == EncodeWarning
michael@0 58 ? "nsIJSONEncodeDeprecatedWarning"
michael@0 59 : "nsIJSONDecodeDeprecatedWarning");
michael@0 60 }
michael@0 61
michael@0 62 NS_IMETHODIMP
michael@0 63 nsJSON::Encode(JS::Handle<JS::Value> aValue, JSContext* cx, uint8_t aArgc,
michael@0 64 nsAString &aJSON)
michael@0 65 {
michael@0 66 // This function should only be called from JS.
michael@0 67 nsresult rv = WarnDeprecatedMethod(EncodeWarning);
michael@0 68 if (NS_FAILED(rv))
michael@0 69 return rv;
michael@0 70
michael@0 71 if (aArgc == 0) {
michael@0 72 aJSON.Truncate();
michael@0 73 aJSON.SetIsVoid(true);
michael@0 74 return NS_OK;
michael@0 75 }
michael@0 76
michael@0 77 nsJSONWriter writer;
michael@0 78 rv = EncodeInternal(cx, aValue, &writer);
michael@0 79
michael@0 80 // FIXME: bug 408838. Get exception types sorted out
michael@0 81 if (NS_SUCCEEDED(rv) || rv == NS_ERROR_INVALID_ARG) {
michael@0 82 rv = NS_OK;
michael@0 83 // if we didn't consume anything, it's not JSON, so return null
michael@0 84 if (!writer.DidWrite()) {
michael@0 85 aJSON.Truncate();
michael@0 86 aJSON.SetIsVoid(true);
michael@0 87 } else {
michael@0 88 writer.FlushBuffer();
michael@0 89 aJSON.Append(writer.mOutputString);
michael@0 90 }
michael@0 91 }
michael@0 92
michael@0 93 return rv;
michael@0 94 }
michael@0 95
michael@0 96 static const char UTF8BOM[] = "\xEF\xBB\xBF";
michael@0 97 static const char UTF16LEBOM[] = "\xFF\xFE";
michael@0 98 static const char UTF16BEBOM[] = "\xFE\xFF";
michael@0 99
michael@0 100 static nsresult CheckCharset(const char* aCharset)
michael@0 101 {
michael@0 102 // Check that the charset is permissible
michael@0 103 if (!(strcmp(aCharset, "UTF-8") == 0 ||
michael@0 104 strcmp(aCharset, "UTF-16LE") == 0 ||
michael@0 105 strcmp(aCharset, "UTF-16BE") == 0)) {
michael@0 106 return NS_ERROR_INVALID_ARG;
michael@0 107 }
michael@0 108
michael@0 109 return NS_OK;
michael@0 110 }
michael@0 111
michael@0 112 NS_IMETHODIMP
michael@0 113 nsJSON::EncodeToStream(nsIOutputStream *aStream,
michael@0 114 const char* aCharset,
michael@0 115 const bool aWriteBOM,
michael@0 116 JS::Handle<JS::Value> val,
michael@0 117 JSContext* cx,
michael@0 118 uint8_t aArgc)
michael@0 119 {
michael@0 120 // This function should only be called from JS.
michael@0 121 NS_ENSURE_ARG(aStream);
michael@0 122 nsresult rv;
michael@0 123
michael@0 124 rv = CheckCharset(aCharset);
michael@0 125 NS_ENSURE_SUCCESS(rv, rv);
michael@0 126
michael@0 127 // Check to see if we have a buffered stream
michael@0 128 nsCOMPtr<nsIOutputStream> bufferedStream;
michael@0 129 // FIXME: bug 408514.
michael@0 130 // NS_OutputStreamIsBuffered(aStream) asserts on file streams...
michael@0 131 //if (!NS_OutputStreamIsBuffered(aStream)) {
michael@0 132 rv = NS_NewBufferedOutputStream(getter_AddRefs(bufferedStream),
michael@0 133 aStream, 4096);
michael@0 134 NS_ENSURE_SUCCESS(rv, rv);
michael@0 135 // aStream = bufferedStream;
michael@0 136 //}
michael@0 137
michael@0 138 uint32_t ignored;
michael@0 139 if (aWriteBOM) {
michael@0 140 if (strcmp(aCharset, "UTF-8") == 0)
michael@0 141 rv = aStream->Write(UTF8BOM, 3, &ignored);
michael@0 142 else if (strcmp(aCharset, "UTF-16LE") == 0)
michael@0 143 rv = aStream->Write(UTF16LEBOM, 2, &ignored);
michael@0 144 else if (strcmp(aCharset, "UTF-16BE") == 0)
michael@0 145 rv = aStream->Write(UTF16BEBOM, 2, &ignored);
michael@0 146 NS_ENSURE_SUCCESS(rv, rv);
michael@0 147 }
michael@0 148
michael@0 149 nsJSONWriter writer(bufferedStream);
michael@0 150 rv = writer.SetCharset(aCharset);
michael@0 151 NS_ENSURE_SUCCESS(rv, rv);
michael@0 152
michael@0 153 if (aArgc == 0) {
michael@0 154 return NS_OK;
michael@0 155 }
michael@0 156
michael@0 157 rv = EncodeInternal(cx, val, &writer);
michael@0 158 NS_ENSURE_SUCCESS(rv, rv);
michael@0 159
michael@0 160 rv = bufferedStream->Flush();
michael@0 161
michael@0 162 return rv;
michael@0 163 }
michael@0 164
michael@0 165 static bool
michael@0 166 WriteCallback(const jschar *buf, uint32_t len, void *data)
michael@0 167 {
michael@0 168 nsJSONWriter *writer = static_cast<nsJSONWriter*>(data);
michael@0 169 nsresult rv = writer->Write((const char16_t*)buf, (uint32_t)len);
michael@0 170 if (NS_FAILED(rv))
michael@0 171 return false;
michael@0 172
michael@0 173 return true;
michael@0 174 }
michael@0 175
michael@0 176 NS_IMETHODIMP
michael@0 177 nsJSON::EncodeFromJSVal(JS::Value *value, JSContext *cx, nsAString &result)
michael@0 178 {
michael@0 179 result.Truncate();
michael@0 180
michael@0 181 mozilla::Maybe<JSAutoCompartment> ac;
michael@0 182 if (value->isObject()) {
michael@0 183 JS::Rooted<JSObject*> obj(cx, &value->toObject());
michael@0 184 ac.construct(cx, obj);
michael@0 185 }
michael@0 186
michael@0 187 nsJSONWriter writer;
michael@0 188 JS::Rooted<JS::Value> vp(cx, *value);
michael@0 189 if (!JS_Stringify(cx, &vp, JS::NullPtr(), JS::NullHandleValue, WriteCallback, &writer)) {
michael@0 190 return NS_ERROR_XPC_BAD_CONVERT_JS;
michael@0 191 }
michael@0 192 *value = vp;
michael@0 193
michael@0 194 NS_ENSURE_TRUE(writer.DidWrite(), NS_ERROR_UNEXPECTED);
michael@0 195 writer.FlushBuffer();
michael@0 196 result.Assign(writer.mOutputString);
michael@0 197 return NS_OK;
michael@0 198 }
michael@0 199
michael@0 200 nsresult
michael@0 201 nsJSON::EncodeInternal(JSContext* cx, const JS::Value& aValue,
michael@0 202 nsJSONWriter* writer)
michael@0 203 {
michael@0 204 // Backward compatibility:
michael@0 205 // nsIJSON does not allow to serialize anything other than objects
michael@0 206 if (!aValue.isObject()) {
michael@0 207 return NS_ERROR_INVALID_ARG;
michael@0 208 }
michael@0 209 JS::Rooted<JSObject*> obj(cx, &aValue.toObject());
michael@0 210
michael@0 211 /* Backward compatibility:
michael@0 212 * Manually call toJSON if implemented by the object and check that
michael@0 213 * the result is still an object
michael@0 214 * Note: It is perfectly fine to not implement toJSON, so it is
michael@0 215 * perfectly fine for GetMethod to fail
michael@0 216 */
michael@0 217 JS::Rooted<JS::Value> val(cx, aValue);
michael@0 218 JS::Rooted<JS::Value> toJSON(cx);
michael@0 219 if (JS_GetProperty(cx, obj, "toJSON", &toJSON) &&
michael@0 220 toJSON.isObject() &&
michael@0 221 JS_ObjectIsCallable(cx, &toJSON.toObject())) {
michael@0 222 // If toJSON is implemented, it must not throw
michael@0 223 if (!JS_CallFunctionValue(cx, obj, toJSON, JS::HandleValueArray::empty(), &val)) {
michael@0 224 if (JS_IsExceptionPending(cx))
michael@0 225 // passing NS_OK will throw the pending exception
michael@0 226 return NS_OK;
michael@0 227
michael@0 228 // No exception, but still failed
michael@0 229 return NS_ERROR_FAILURE;
michael@0 230 }
michael@0 231
michael@0 232 // Backward compatibility:
michael@0 233 // nsIJSON does not allow to serialize anything other than objects
michael@0 234 if (val.isPrimitive())
michael@0 235 return NS_ERROR_INVALID_ARG;
michael@0 236 }
michael@0 237 // GetMethod may have thrown
michael@0 238 else if (JS_IsExceptionPending(cx))
michael@0 239 // passing NS_OK will throw the pending exception
michael@0 240 return NS_OK;
michael@0 241
michael@0 242 // Backward compatibility:
michael@0 243 // function shall not pass, just "plain" objects and arrays
michael@0 244 JSType type = JS_TypeOfValue(cx, val);
michael@0 245 if (type == JSTYPE_FUNCTION)
michael@0 246 return NS_ERROR_INVALID_ARG;
michael@0 247
michael@0 248 // We're good now; try to stringify
michael@0 249 if (!JS_Stringify(cx, &val, JS::NullPtr(), JS::NullHandleValue, WriteCallback, writer))
michael@0 250 return NS_ERROR_FAILURE;
michael@0 251
michael@0 252 return NS_OK;
michael@0 253 }
michael@0 254
michael@0 255
michael@0 256 nsJSONWriter::nsJSONWriter() : mStream(nullptr),
michael@0 257 mBuffer(nullptr),
michael@0 258 mBufferCount(0),
michael@0 259 mDidWrite(false),
michael@0 260 mEncoder(nullptr)
michael@0 261 {
michael@0 262 }
michael@0 263
michael@0 264 nsJSONWriter::nsJSONWriter(nsIOutputStream *aStream) : mStream(aStream),
michael@0 265 mBuffer(nullptr),
michael@0 266 mBufferCount(0),
michael@0 267 mDidWrite(false),
michael@0 268 mEncoder(nullptr)
michael@0 269 {
michael@0 270 }
michael@0 271
michael@0 272 nsJSONWriter::~nsJSONWriter()
michael@0 273 {
michael@0 274 delete [] mBuffer;
michael@0 275 }
michael@0 276
michael@0 277 nsresult
michael@0 278 nsJSONWriter::SetCharset(const char* aCharset)
michael@0 279 {
michael@0 280 nsresult rv = NS_OK;
michael@0 281 if (mStream) {
michael@0 282 mEncoder = EncodingUtils::EncoderForEncoding(aCharset);
michael@0 283 rv = mEncoder->SetOutputErrorBehavior(nsIUnicodeEncoder::kOnError_Signal,
michael@0 284 nullptr, '\0');
michael@0 285 NS_ENSURE_SUCCESS(rv, rv);
michael@0 286 }
michael@0 287
michael@0 288 return rv;
michael@0 289 }
michael@0 290
michael@0 291 nsresult
michael@0 292 nsJSONWriter::Write(const char16_t *aBuffer, uint32_t aLength)
michael@0 293 {
michael@0 294 if (mStream) {
michael@0 295 return WriteToStream(mStream, mEncoder, aBuffer, aLength);
michael@0 296 }
michael@0 297
michael@0 298 if (!mDidWrite) {
michael@0 299 mBuffer = new char16_t[JSON_STREAM_BUFSIZE];
michael@0 300 if (!mBuffer)
michael@0 301 return NS_ERROR_OUT_OF_MEMORY;
michael@0 302 mDidWrite = true;
michael@0 303 }
michael@0 304
michael@0 305 if (JSON_STREAM_BUFSIZE <= aLength + mBufferCount) {
michael@0 306 mOutputString.Append(mBuffer, mBufferCount);
michael@0 307 mBufferCount = 0;
michael@0 308 }
michael@0 309
michael@0 310 if (JSON_STREAM_BUFSIZE <= aLength) {
michael@0 311 // we know mBufferCount is 0 because we know we hit the if above
michael@0 312 mOutputString.Append(aBuffer, aLength);
michael@0 313 } else {
michael@0 314 memcpy(&mBuffer[mBufferCount], aBuffer, aLength * sizeof(char16_t));
michael@0 315 mBufferCount += aLength;
michael@0 316 }
michael@0 317
michael@0 318 return NS_OK;
michael@0 319 }
michael@0 320
michael@0 321 bool nsJSONWriter::DidWrite()
michael@0 322 {
michael@0 323 return mDidWrite;
michael@0 324 }
michael@0 325
michael@0 326 void
michael@0 327 nsJSONWriter::FlushBuffer()
michael@0 328 {
michael@0 329 mOutputString.Append(mBuffer, mBufferCount);
michael@0 330 }
michael@0 331
michael@0 332 nsresult
michael@0 333 nsJSONWriter::WriteToStream(nsIOutputStream *aStream,
michael@0 334 nsIUnicodeEncoder *encoder,
michael@0 335 const char16_t *aBuffer,
michael@0 336 uint32_t aLength)
michael@0 337 {
michael@0 338 nsresult rv;
michael@0 339 int32_t srcLength = aLength;
michael@0 340 uint32_t bytesWritten;
michael@0 341
michael@0 342 // The bytes written to the stream might differ from the char16_t size
michael@0 343 int32_t aDestLength;
michael@0 344 rv = encoder->GetMaxLength(aBuffer, srcLength, &aDestLength);
michael@0 345 NS_ENSURE_SUCCESS(rv, rv);
michael@0 346
michael@0 347 // create the buffer we need
michael@0 348 char* destBuf = (char *) NS_Alloc(aDestLength);
michael@0 349 if (!destBuf)
michael@0 350 return NS_ERROR_OUT_OF_MEMORY;
michael@0 351
michael@0 352 rv = encoder->Convert(aBuffer, &srcLength, destBuf, &aDestLength);
michael@0 353 if (NS_SUCCEEDED(rv))
michael@0 354 rv = aStream->Write(destBuf, aDestLength, &bytesWritten);
michael@0 355
michael@0 356 NS_Free(destBuf);
michael@0 357 mDidWrite = true;
michael@0 358
michael@0 359 return rv;
michael@0 360 }
michael@0 361
michael@0 362 NS_IMETHODIMP
michael@0 363 nsJSON::Decode(const nsAString& json, JSContext* cx,
michael@0 364 JS::MutableHandle<JS::Value> aRetval)
michael@0 365 {
michael@0 366 nsresult rv = WarnDeprecatedMethod(DecodeWarning);
michael@0 367 if (NS_FAILED(rv))
michael@0 368 return rv;
michael@0 369
michael@0 370 const char16_t *data;
michael@0 371 uint32_t len = NS_StringGetData(json, &data);
michael@0 372 nsCOMPtr<nsIInputStream> stream;
michael@0 373 rv = NS_NewByteInputStream(getter_AddRefs(stream),
michael@0 374 reinterpret_cast<const char*>(data),
michael@0 375 len * sizeof(char16_t),
michael@0 376 NS_ASSIGNMENT_DEPEND);
michael@0 377 NS_ENSURE_SUCCESS(rv, rv);
michael@0 378 return DecodeInternal(cx, stream, len, false, aRetval);
michael@0 379 }
michael@0 380
michael@0 381 NS_IMETHODIMP
michael@0 382 nsJSON::DecodeFromStream(nsIInputStream *aStream, int32_t aContentLength,
michael@0 383 JSContext* cx, JS::MutableHandle<JS::Value> aRetval)
michael@0 384 {
michael@0 385 return DecodeInternal(cx, aStream, aContentLength, true, aRetval);
michael@0 386 }
michael@0 387
michael@0 388 NS_IMETHODIMP
michael@0 389 nsJSON::DecodeToJSVal(const nsAString &str, JSContext *cx,
michael@0 390 JS::MutableHandle<JS::Value> result)
michael@0 391 {
michael@0 392 if (!JS_ParseJSON(cx, static_cast<const jschar*>(PromiseFlatString(str).get()),
michael@0 393 str.Length(), result)) {
michael@0 394 return NS_ERROR_UNEXPECTED;
michael@0 395 }
michael@0 396 return NS_OK;
michael@0 397 }
michael@0 398
michael@0 399 nsresult
michael@0 400 nsJSON::DecodeInternal(JSContext* cx,
michael@0 401 nsIInputStream *aStream,
michael@0 402 int32_t aContentLength,
michael@0 403 bool aNeedsConverter,
michael@0 404 JS::MutableHandle<JS::Value> aRetval)
michael@0 405 {
michael@0 406 // Consume the stream
michael@0 407 nsCOMPtr<nsIChannel> jsonChannel;
michael@0 408 if (!mURI) {
michael@0 409 NS_NewURI(getter_AddRefs(mURI), NS_LITERAL_CSTRING("about:blank"), 0, 0 );
michael@0 410 if (!mURI)
michael@0 411 return NS_ERROR_OUT_OF_MEMORY;
michael@0 412 }
michael@0 413
michael@0 414 nsresult rv =
michael@0 415 NS_NewInputStreamChannel(getter_AddRefs(jsonChannel), mURI, aStream,
michael@0 416 NS_LITERAL_CSTRING("application/json"));
michael@0 417 if (!jsonChannel || NS_FAILED(rv))
michael@0 418 return NS_ERROR_FAILURE;
michael@0 419
michael@0 420 nsRefPtr<nsJSONListener> jsonListener =
michael@0 421 new nsJSONListener(cx, aRetval.address(), aNeedsConverter);
michael@0 422
michael@0 423 //XXX this stream pattern should be consolidated in netwerk
michael@0 424 rv = jsonListener->OnStartRequest(jsonChannel, nullptr);
michael@0 425 if (NS_FAILED(rv)) {
michael@0 426 jsonChannel->Cancel(rv);
michael@0 427 return rv;
michael@0 428 }
michael@0 429
michael@0 430 nsresult status;
michael@0 431 jsonChannel->GetStatus(&status);
michael@0 432 uint64_t offset = 0;
michael@0 433 while (NS_SUCCEEDED(status)) {
michael@0 434 uint64_t available;
michael@0 435 rv = aStream->Available(&available);
michael@0 436 if (rv == NS_BASE_STREAM_CLOSED) {
michael@0 437 rv = NS_OK;
michael@0 438 break;
michael@0 439 }
michael@0 440 if (NS_FAILED(rv)) {
michael@0 441 jsonChannel->Cancel(rv);
michael@0 442 break;
michael@0 443 }
michael@0 444 if (!available)
michael@0 445 break; // blocking input stream has none available when done
michael@0 446
michael@0 447 if (available > UINT32_MAX)
michael@0 448 available = UINT32_MAX;
michael@0 449
michael@0 450 rv = jsonListener->OnDataAvailable(jsonChannel, nullptr,
michael@0 451 aStream,
michael@0 452 offset,
michael@0 453 (uint32_t)available);
michael@0 454 if (NS_FAILED(rv)) {
michael@0 455 jsonChannel->Cancel(rv);
michael@0 456 break;
michael@0 457 }
michael@0 458
michael@0 459 offset += available;
michael@0 460 jsonChannel->GetStatus(&status);
michael@0 461 }
michael@0 462 NS_ENSURE_SUCCESS(rv, rv);
michael@0 463
michael@0 464 rv = jsonListener->OnStopRequest(jsonChannel, nullptr, status);
michael@0 465 NS_ENSURE_SUCCESS(rv, rv);
michael@0 466
michael@0 467 return NS_OK;
michael@0 468 }
michael@0 469
michael@0 470 nsresult
michael@0 471 NS_NewJSON(nsISupports* aOuter, REFNSIID aIID, void** aResult)
michael@0 472 {
michael@0 473 nsJSON* json = new nsJSON();
michael@0 474 if (!json)
michael@0 475 return NS_ERROR_OUT_OF_MEMORY;
michael@0 476
michael@0 477 NS_ADDREF(json);
michael@0 478 *aResult = json;
michael@0 479
michael@0 480 return NS_OK;
michael@0 481 }
michael@0 482
michael@0 483 nsJSONListener::nsJSONListener(JSContext *cx, JS::Value *rootVal,
michael@0 484 bool needsConverter)
michael@0 485 : mNeedsConverter(needsConverter),
michael@0 486 mCx(cx),
michael@0 487 mRootVal(rootVal)
michael@0 488 {
michael@0 489 }
michael@0 490
michael@0 491 nsJSONListener::~nsJSONListener()
michael@0 492 {
michael@0 493 }
michael@0 494
michael@0 495 NS_INTERFACE_MAP_BEGIN(nsJSONListener)
michael@0 496 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsJSONListener)
michael@0 497 NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
michael@0 498 NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
michael@0 499 NS_INTERFACE_MAP_END
michael@0 500
michael@0 501 NS_IMPL_ADDREF(nsJSONListener)
michael@0 502 NS_IMPL_RELEASE(nsJSONListener)
michael@0 503
michael@0 504 NS_IMETHODIMP
michael@0 505 nsJSONListener::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext)
michael@0 506 {
michael@0 507 mSniffBuffer.Truncate();
michael@0 508 mDecoder = nullptr;
michael@0 509
michael@0 510 return NS_OK;
michael@0 511 }
michael@0 512
michael@0 513 NS_IMETHODIMP
michael@0 514 nsJSONListener::OnStopRequest(nsIRequest *aRequest, nsISupports *aContext,
michael@0 515 nsresult aStatusCode)
michael@0 516 {
michael@0 517 nsresult rv;
michael@0 518
michael@0 519 // This can happen with short UTF-8 messages (<4 bytes)
michael@0 520 if (!mSniffBuffer.IsEmpty()) {
michael@0 521 // Just consume mSniffBuffer
michael@0 522 rv = ProcessBytes(nullptr, 0);
michael@0 523 NS_ENSURE_SUCCESS(rv, rv);
michael@0 524 }
michael@0 525
michael@0 526 JS::Rooted<JS::Value> reviver(mCx, JS::NullValue()), value(mCx);
michael@0 527
michael@0 528 JS::ConstTwoByteChars chars(reinterpret_cast<const jschar*>(mBufferedChars.Elements()),
michael@0 529 mBufferedChars.Length());
michael@0 530 bool ok = JS_ParseJSONWithReviver(mCx, chars.get(),
michael@0 531 uint32_t(mBufferedChars.Length()),
michael@0 532 reviver, &value);
michael@0 533
michael@0 534 *mRootVal = value;
michael@0 535 mBufferedChars.TruncateLength(0);
michael@0 536 return ok ? NS_OK : NS_ERROR_FAILURE;
michael@0 537 }
michael@0 538
michael@0 539 NS_IMETHODIMP
michael@0 540 nsJSONListener::OnDataAvailable(nsIRequest *aRequest, nsISupports *aContext,
michael@0 541 nsIInputStream *aStream,
michael@0 542 uint64_t aOffset, uint32_t aLength)
michael@0 543 {
michael@0 544 nsresult rv = NS_OK;
michael@0 545
michael@0 546 if (mNeedsConverter && mSniffBuffer.Length() < 4) {
michael@0 547 uint32_t readCount = (aLength < 4) ? aLength : 4;
michael@0 548 rv = NS_ConsumeStream(aStream, readCount, mSniffBuffer);
michael@0 549 NS_ENSURE_SUCCESS(rv, rv);
michael@0 550
michael@0 551 if (mSniffBuffer.Length() < 4)
michael@0 552 return NS_OK;
michael@0 553 }
michael@0 554
michael@0 555 char buffer[JSON_STREAM_BUFSIZE];
michael@0 556 unsigned long bytesRemaining = aLength - mSniffBuffer.Length();
michael@0 557 while (bytesRemaining) {
michael@0 558 unsigned int bytesRead;
michael@0 559 rv = aStream->Read(buffer,
michael@0 560 std::min((unsigned long)sizeof(buffer), bytesRemaining),
michael@0 561 &bytesRead);
michael@0 562 NS_ENSURE_SUCCESS(rv, rv);
michael@0 563 rv = ProcessBytes(buffer, bytesRead);
michael@0 564 NS_ENSURE_SUCCESS(rv, rv);
michael@0 565 bytesRemaining -= bytesRead;
michael@0 566 }
michael@0 567
michael@0 568 return rv;
michael@0 569 }
michael@0 570
michael@0 571 nsresult
michael@0 572 nsJSONListener::ProcessBytes(const char* aBuffer, uint32_t aByteLength)
michael@0 573 {
michael@0 574 nsresult rv;
michael@0 575 // Check for BOM, or sniff charset
michael@0 576 nsAutoCString charset;
michael@0 577 if (mNeedsConverter && !mDecoder) {
michael@0 578 if (!nsContentUtils::CheckForBOM((const unsigned char*) mSniffBuffer.get(),
michael@0 579 mSniffBuffer.Length(), charset)) {
michael@0 580 // OK, found no BOM, sniff the first character to see what this is
michael@0 581 // See section 3 of RFC4627 for details on why this works.
michael@0 582 const char *buffer = mSniffBuffer.get();
michael@0 583 if (mSniffBuffer.Length() >= 4) {
michael@0 584 if (buffer[0] == 0x00 && buffer[1] != 0x00 &&
michael@0 585 buffer[2] == 0x00 && buffer[3] != 0x00) {
michael@0 586 charset = "UTF-16BE";
michael@0 587 } else if (buffer[0] != 0x00 && buffer[1] == 0x00 &&
michael@0 588 buffer[2] != 0x00 && buffer[3] == 0x00) {
michael@0 589 charset = "UTF-16LE";
michael@0 590 } else if (buffer[0] != 0x00 && buffer[1] != 0x00 &&
michael@0 591 buffer[2] != 0x00 && buffer[3] != 0x00) {
michael@0 592 charset = "UTF-8";
michael@0 593 }
michael@0 594 } else {
michael@0 595 // Not enough bytes to sniff, assume UTF-8
michael@0 596 charset = "UTF-8";
michael@0 597 }
michael@0 598 }
michael@0 599
michael@0 600 // We should have a unicode charset by now
michael@0 601 rv = CheckCharset(charset.get());
michael@0 602 NS_ENSURE_SUCCESS(rv, rv);
michael@0 603 mDecoder = EncodingUtils::DecoderForEncoding(charset);
michael@0 604
michael@0 605 // consume the sniffed bytes
michael@0 606 rv = ConsumeConverted(mSniffBuffer.get(), mSniffBuffer.Length());
michael@0 607 NS_ENSURE_SUCCESS(rv, rv);
michael@0 608 mSniffBuffer.Truncate();
michael@0 609 }
michael@0 610
michael@0 611 if (!aBuffer)
michael@0 612 return NS_OK;
michael@0 613
michael@0 614 if (mNeedsConverter) {
michael@0 615 rv = ConsumeConverted(aBuffer, aByteLength);
michael@0 616 } else {
michael@0 617 uint32_t unichars = aByteLength / sizeof(char16_t);
michael@0 618 rv = Consume((char16_t *) aBuffer, unichars);
michael@0 619 }
michael@0 620
michael@0 621 return rv;
michael@0 622 }
michael@0 623
michael@0 624 nsresult
michael@0 625 nsJSONListener::ConsumeConverted(const char* aBuffer, uint32_t aByteLength)
michael@0 626 {
michael@0 627 nsresult rv;
michael@0 628 int32_t unicharLength = 0;
michael@0 629 int32_t srcLen = aByteLength;
michael@0 630
michael@0 631 rv = mDecoder->GetMaxLength(aBuffer, srcLen, &unicharLength);
michael@0 632 NS_ENSURE_SUCCESS(rv, rv);
michael@0 633
michael@0 634 char16_t* endelems = mBufferedChars.AppendElements(unicharLength);
michael@0 635 int32_t preLength = unicharLength;
michael@0 636 rv = mDecoder->Convert(aBuffer, &srcLen, endelems, &unicharLength);
michael@0 637 if (NS_FAILED(rv))
michael@0 638 return rv;
michael@0 639 NS_ABORT_IF_FALSE(preLength >= unicharLength, "GetMaxLength lied");
michael@0 640 if (preLength > unicharLength)
michael@0 641 mBufferedChars.TruncateLength(mBufferedChars.Length() - (preLength - unicharLength));
michael@0 642 return NS_OK;
michael@0 643 }
michael@0 644
michael@0 645 nsresult
michael@0 646 nsJSONListener::Consume(const char16_t* aBuffer, uint32_t aByteLength)
michael@0 647 {
michael@0 648 if (!mBufferedChars.AppendElements(aBuffer, aByteLength))
michael@0 649 return NS_ERROR_FAILURE;
michael@0 650
michael@0 651 return NS_OK;
michael@0 652 }

mercurial