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