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 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is mozilla.org code.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * Mozilla Corporation.
20 : * Portions created by the Initial Developer are Copyright (C) 2007
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * Dave Camp <dcamp@mozilla.com>
25 : * Robert Sayre <sayrer@gmail.com>
26 : * Nils Maier <maierman@web.de>
27 : *
28 : * Alternatively, the contents of this file may be used under the terms of
29 : * either the GNU General Public License Version 2 or later (the "GPL"), or
30 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 : * in which case the provisions of the GPL or the LGPL are applicable instead
32 : * of those above. If you wish to allow use of your version of this file only
33 : * under the terms of either the GPL or the LGPL, and not to allow others to
34 : * use your version of this file under the terms of the MPL, indicate your
35 : * decision by deleting the provisions above and replace them with the notice
36 : * and other provisions required by the GPL or the LGPL. If you do not delete
37 : * the provisions above, a recipient may use your version of this file under
38 : * the terms of any one of the MPL, the GPL or the LGPL.
39 : *
40 : * ***** END LICENSE BLOCK ***** */
41 :
42 : #include "jsapi.h"
43 : #include "jsdbgapi.h"
44 : #include "nsIServiceManager.h"
45 : #include "nsJSON.h"
46 : #include "nsIXPConnect.h"
47 : #include "nsIXPCScriptable.h"
48 : #include "nsStreamUtils.h"
49 : #include "nsIInputStream.h"
50 : #include "nsStringStream.h"
51 : #include "nsICharsetConverterManager.h"
52 : #include "nsXPCOMStrings.h"
53 : #include "nsNetUtil.h"
54 : #include "nsContentUtils.h"
55 : #include "nsIScriptError.h"
56 : #include "nsCRTGlue.h"
57 : #include "nsAutoPtr.h"
58 : #include "nsIScriptSecurityManager.h"
59 :
60 : static const char kXPConnectServiceCID[] = "@mozilla.org/js/xpc/XPConnect;1";
61 :
62 : #define JSON_STREAM_BUFSIZE 4096
63 :
64 2424 : NS_INTERFACE_MAP_BEGIN(nsJSON)
65 2424 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIJSON)
66 2121 : NS_INTERFACE_MAP_ENTRY(nsIJSON)
67 1818 : NS_INTERFACE_MAP_END
68 :
69 909 : NS_IMPL_ADDREF(nsJSON)
70 909 : NS_IMPL_RELEASE(nsJSON)
71 :
72 303 : nsJSON::nsJSON()
73 : {
74 303 : }
75 :
76 606 : nsJSON::~nsJSON()
77 : {
78 1212 : }
79 :
80 : enum DeprecationWarning { EncodeWarning, DecodeWarning };
81 :
82 : static nsresult
83 23 : WarnDeprecatedMethod(DeprecationWarning warning)
84 : {
85 : return nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
86 : "DOM Core", nsnull,
87 : nsContentUtils::eDOM_PROPERTIES,
88 : warning == EncodeWarning
89 : ? "nsIJSONEncodeDeprecatedWarning"
90 23 : : "nsIJSONDecodeDeprecatedWarning");
91 : }
92 :
93 : NS_IMETHODIMP
94 20 : nsJSON::Encode(const JS::Value& aValue, JSContext* cx, PRUint8 aArgc, nsAString &aJSON)
95 : {
96 : // This function should only be called from JS.
97 20 : nsresult rv = WarnDeprecatedMethod(EncodeWarning);
98 20 : if (NS_FAILED(rv))
99 0 : return rv;
100 :
101 20 : if (aArgc == 0) {
102 1 : aJSON.Truncate();
103 1 : aJSON.SetIsVoid(true);
104 1 : return NS_OK;
105 : }
106 :
107 38 : nsJSONWriter writer;
108 19 : rv = EncodeInternal(cx, aValue, &writer);
109 :
110 : // FIXME: bug 408838. Get exception types sorted out
111 19 : if (NS_SUCCEEDED(rv) || rv == NS_ERROR_INVALID_ARG) {
112 19 : rv = NS_OK;
113 : // if we didn't consume anything, it's not JSON, so return null
114 19 : if (!writer.DidWrite()) {
115 14 : aJSON.Truncate();
116 14 : aJSON.SetIsVoid(true);
117 : } else {
118 5 : writer.FlushBuffer();
119 5 : aJSON.Append(writer.mOutputString);
120 : }
121 : }
122 :
123 19 : return rv;
124 : }
125 :
126 : static const char UTF8BOM[] = "\xEF\xBB\xBF";
127 : static const char UTF16LEBOM[] = "\xFF\xFE";
128 : static const char UTF16BEBOM[] = "\xFE\xFF";
129 :
130 319 : static nsresult CheckCharset(const char* aCharset)
131 : {
132 : // Check that the charset is permissible
133 319 : if (!(strcmp(aCharset, "UTF-8") == 0 ||
134 22 : strcmp(aCharset, "UTF-16LE") == 0 ||
135 22 : strcmp(aCharset, "UTF-16BE") == 0)) {
136 0 : return NS_ERROR_INVALID_ARG;
137 : }
138 :
139 319 : return NS_OK;
140 : }
141 :
142 : NS_IMETHODIMP
143 33 : nsJSON::EncodeToStream(nsIOutputStream *aStream,
144 : const char* aCharset,
145 : const bool aWriteBOM,
146 : const JS::Value& val,
147 : JSContext* cx,
148 : PRUint8 aArgc)
149 : {
150 : // This function should only be called from JS.
151 33 : NS_ENSURE_ARG(aStream);
152 : nsresult rv;
153 :
154 33 : rv = CheckCharset(aCharset);
155 33 : NS_ENSURE_SUCCESS(rv, rv);
156 :
157 : // Check to see if we have a buffered stream
158 66 : nsCOMPtr<nsIOutputStream> bufferedStream;
159 : // FIXME: bug 408514.
160 : // NS_OutputStreamIsBuffered(aStream) asserts on file streams...
161 : //if (!NS_OutputStreamIsBuffered(aStream)) {
162 33 : rv = NS_NewBufferedOutputStream(getter_AddRefs(bufferedStream),
163 33 : aStream, 4096);
164 33 : NS_ENSURE_SUCCESS(rv, rv);
165 : // aStream = bufferedStream;
166 : //}
167 :
168 : PRUint32 ignored;
169 33 : if (aWriteBOM) {
170 3 : if (strcmp(aCharset, "UTF-8") == 0)
171 1 : rv = aStream->Write(UTF8BOM, 3, &ignored);
172 2 : else if (strcmp(aCharset, "UTF-16LE") == 0)
173 1 : rv = aStream->Write(UTF16LEBOM, 2, &ignored);
174 1 : else if (strcmp(aCharset, "UTF-16BE") == 0)
175 1 : rv = aStream->Write(UTF16BEBOM, 2, &ignored);
176 3 : NS_ENSURE_SUCCESS(rv, rv);
177 : }
178 :
179 66 : nsJSONWriter writer(bufferedStream);
180 33 : rv = writer.SetCharset(aCharset);
181 33 : NS_ENSURE_SUCCESS(rv, rv);
182 :
183 33 : if (aArgc == 0) {
184 0 : return NS_OK;
185 : }
186 :
187 33 : rv = EncodeInternal(cx, val, &writer);
188 33 : NS_ENSURE_SUCCESS(rv, rv);
189 :
190 33 : rv = bufferedStream->Flush();
191 :
192 33 : return rv;
193 : }
194 :
195 : static JSBool
196 38 : WriteCallback(const jschar *buf, uint32_t len, void *data)
197 : {
198 38 : nsJSONWriter *writer = static_cast<nsJSONWriter*>(data);
199 38 : nsresult rv = writer->Write((const PRUnichar*)buf, (PRUint32)len);
200 38 : if (NS_FAILED(rv))
201 0 : return JS_FALSE;
202 :
203 38 : return JS_TRUE;
204 : }
205 :
206 : NS_IMETHODIMP
207 0 : nsJSON::EncodeFromJSVal(jsval *value, JSContext *cx, nsAString &result)
208 : {
209 0 : result.Truncate();
210 :
211 : // Begin a new request
212 0 : JSAutoRequest ar(cx);
213 :
214 0 : JSAutoEnterCompartment ac;
215 : JSObject *obj;
216 0 : nsIScriptSecurityManager *ssm = nsnull;
217 0 : if (JSVAL_IS_OBJECT(*value) && (obj = JSVAL_TO_OBJECT(*value))) {
218 0 : if (!ac.enter(cx, obj)) {
219 0 : return NS_ERROR_FAILURE;
220 : }
221 :
222 0 : nsCOMPtr<nsIPrincipal> principal;
223 0 : ssm = nsContentUtils::GetSecurityManager();
224 0 : nsresult rv = ssm->GetObjectPrincipal(cx, obj, getter_AddRefs(principal));
225 0 : NS_ENSURE_SUCCESS(rv, rv);
226 :
227 0 : JSStackFrame *fp = nsnull;
228 0 : rv = ssm->PushContextPrincipal(cx, JS_FrameIterator(cx, &fp), principal);
229 0 : NS_ENSURE_SUCCESS(rv, rv);
230 : }
231 :
232 0 : nsJSONWriter writer;
233 : JSBool ok = JS_Stringify(cx, value, NULL, JSVAL_NULL,
234 0 : WriteCallback, &writer);
235 :
236 0 : if (ssm) {
237 0 : ssm->PopContextPrincipal(cx);
238 : }
239 :
240 0 : if (!ok) {
241 0 : return NS_ERROR_XPC_BAD_CONVERT_JS;
242 : }
243 :
244 0 : NS_ENSURE_TRUE(writer.DidWrite(), NS_ERROR_UNEXPECTED);
245 0 : writer.FlushBuffer();
246 0 : result.Assign(writer.mOutputString);
247 0 : return NS_OK;
248 : }
249 :
250 : nsresult
251 52 : nsJSON::EncodeInternal(JSContext* cx, const JS::Value& aValue, nsJSONWriter* writer)
252 : {
253 104 : JSAutoRequest ar(cx);
254 :
255 : // Backward compatibility:
256 : // nsIJSON does not allow to serialize anything other than objects
257 52 : if (!JSVAL_IS_OBJECT(aValue)) {
258 3 : return NS_ERROR_INVALID_ARG;
259 : }
260 :
261 49 : JSObject* obj = JSVAL_TO_OBJECT(aValue);
262 49 : if (!obj) {
263 1 : return NS_ERROR_INVALID_ARG;
264 : }
265 :
266 48 : JS::Value val = aValue;
267 :
268 : /* Backward compatibility:
269 : * Manually call toJSON if implemented by the object and check that
270 : * the result is still an object
271 : * Note: It is perfectly fine to not implement toJSON, so it is
272 : * perfectly fine for GetMethod to fail
273 : */
274 : jsval toJSON;
275 104 : if (JS_GetMethod(cx, obj, "toJSON", NULL, &toJSON) &&
276 47 : !JSVAL_IS_PRIMITIVE(toJSON) &&
277 9 : JS_ObjectIsCallable(cx, JSVAL_TO_OBJECT(toJSON))) {
278 : // If toJSON is implemented, it must not throw
279 9 : if (!JS_CallFunctionValue(cx, obj, toJSON, 0, NULL, &val)) {
280 1 : if (JS_IsExceptionPending(cx))
281 : // passing NS_OK will throw the pending exception
282 1 : return NS_OK;
283 :
284 : // No exception, but still failed
285 0 : return NS_ERROR_FAILURE;
286 : }
287 :
288 : // Backward compatibility:
289 : // nsIJSON does not allow to serialize anything other than objects
290 8 : if (JSVAL_IS_PRIMITIVE(val))
291 4 : return NS_ERROR_INVALID_ARG;
292 : }
293 : // GetMethod may have thrown
294 39 : else if (JS_IsExceptionPending(cx))
295 : // passing NS_OK will throw the pending exception
296 1 : return NS_OK;
297 :
298 : // Backward compatibility:
299 : // function/xml shall not pass, just "plain" objects and arrays
300 42 : JSType type = JS_TypeOfValue(cx, val);
301 42 : if (type == JSTYPE_FUNCTION || type == JSTYPE_XML)
302 4 : return NS_ERROR_INVALID_ARG;
303 :
304 : // We're good now; try to stringify
305 38 : if (!JS_Stringify(cx, &val, NULL, JSVAL_NULL, WriteCallback, writer))
306 0 : return NS_ERROR_FAILURE;
307 :
308 38 : return NS_OK;
309 : }
310 :
311 :
312 19 : nsJSONWriter::nsJSONWriter() : mStream(nsnull),
313 : mBuffer(nsnull),
314 : mBufferCount(0),
315 : mDidWrite(false),
316 19 : mEncoder(nsnull)
317 : {
318 19 : }
319 :
320 33 : nsJSONWriter::nsJSONWriter(nsIOutputStream *aStream) : mStream(aStream),
321 : mBuffer(nsnull),
322 : mBufferCount(0),
323 : mDidWrite(false),
324 33 : mEncoder(nsnull)
325 : {
326 33 : }
327 :
328 104 : nsJSONWriter::~nsJSONWriter()
329 : {
330 52 : delete [] mBuffer;
331 104 : }
332 :
333 : nsresult
334 33 : nsJSONWriter::SetCharset(const char* aCharset)
335 : {
336 33 : nsresult rv = NS_OK;
337 33 : if (mStream) {
338 : nsCOMPtr<nsICharsetConverterManager> ccm =
339 66 : do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
340 33 : NS_ENSURE_SUCCESS(rv, rv);
341 33 : rv = ccm->GetUnicodeEncoder(aCharset, getter_AddRefs(mEncoder));
342 33 : NS_ENSURE_SUCCESS(rv, rv);
343 33 : rv = mEncoder->SetOutputErrorBehavior(nsIUnicodeEncoder::kOnError_Signal,
344 33 : nsnull, nsnull);
345 33 : NS_ENSURE_SUCCESS(rv, rv);
346 : }
347 :
348 33 : return rv;
349 : }
350 :
351 : nsresult
352 38 : nsJSONWriter::Write(const PRUnichar *aBuffer, PRUint32 aLength)
353 : {
354 38 : if (mStream) {
355 33 : return WriteToStream(mStream, mEncoder, aBuffer, aLength);
356 : }
357 :
358 5 : if (!mDidWrite) {
359 5 : mBuffer = new PRUnichar[JSON_STREAM_BUFSIZE];
360 5 : if (!mBuffer)
361 0 : return NS_ERROR_OUT_OF_MEMORY;
362 5 : mDidWrite = true;
363 : }
364 :
365 5 : if (JSON_STREAM_BUFSIZE <= aLength + mBufferCount) {
366 1 : mOutputString.Append(mBuffer, mBufferCount);
367 1 : mBufferCount = 0;
368 : }
369 :
370 5 : if (JSON_STREAM_BUFSIZE <= aLength) {
371 : // we know mBufferCount is 0 because we know we hit the if above
372 1 : mOutputString.Append(aBuffer, aLength);
373 : } else {
374 4 : memcpy(&mBuffer[mBufferCount], aBuffer, aLength * sizeof(PRUnichar));
375 4 : mBufferCount += aLength;
376 : }
377 :
378 5 : return NS_OK;
379 : }
380 :
381 19 : bool nsJSONWriter::DidWrite()
382 : {
383 19 : return mDidWrite;
384 : }
385 :
386 : void
387 5 : nsJSONWriter::FlushBuffer()
388 : {
389 5 : mOutputString.Append(mBuffer, mBufferCount);
390 5 : }
391 :
392 : nsresult
393 33 : nsJSONWriter::WriteToStream(nsIOutputStream *aStream,
394 : nsIUnicodeEncoder *encoder,
395 : const PRUnichar *aBuffer,
396 : PRUint32 aLength)
397 : {
398 : nsresult rv;
399 33 : PRInt32 srcLength = aLength;
400 : PRUint32 bytesWritten;
401 :
402 : // The bytes written to the stream might differ from the PRUnichar size
403 : PRInt32 aDestLength;
404 33 : rv = encoder->GetMaxLength(aBuffer, srcLength, &aDestLength);
405 33 : NS_ENSURE_SUCCESS(rv, rv);
406 :
407 : // create the buffer we need
408 33 : char* destBuf = (char *) NS_Alloc(aDestLength);
409 33 : if (!destBuf)
410 0 : return NS_ERROR_OUT_OF_MEMORY;
411 :
412 33 : rv = encoder->Convert(aBuffer, &srcLength, destBuf, &aDestLength);
413 33 : if (NS_SUCCEEDED(rv))
414 33 : rv = aStream->Write(destBuf, aDestLength, &bytesWritten);
415 :
416 33 : NS_Free(destBuf);
417 33 : mDidWrite = true;
418 :
419 33 : return rv;
420 : }
421 :
422 : NS_IMETHODIMP
423 3 : nsJSON::Decode(const nsAString& json, JSContext* cx, JS::Value* aRetval)
424 : {
425 3 : nsresult rv = WarnDeprecatedMethod(DecodeWarning);
426 3 : if (NS_FAILED(rv))
427 0 : return rv;
428 :
429 : const PRUnichar *data;
430 3 : PRUint32 len = NS_StringGetData(json, &data);
431 6 : nsCOMPtr<nsIInputStream> stream;
432 3 : rv = NS_NewByteInputStream(getter_AddRefs(stream),
433 : reinterpret_cast<const char*>(data),
434 : len * sizeof(PRUnichar),
435 3 : NS_ASSIGNMENT_DEPEND);
436 3 : NS_ENSURE_SUCCESS(rv, rv);
437 3 : return DecodeInternal(cx, stream, len, false, aRetval);
438 : }
439 :
440 : NS_IMETHODIMP
441 286 : nsJSON::DecodeFromStream(nsIInputStream *aStream, PRInt32 aContentLength,
442 : JSContext* cx, JS::Value* aRetval)
443 : {
444 286 : return DecodeInternal(cx, aStream, aContentLength, true, aRetval);
445 : }
446 :
447 : NS_IMETHODIMP
448 0 : nsJSON::DecodeToJSVal(const nsAString &str, JSContext *cx, jsval *result)
449 : {
450 0 : JSAutoRequest ar(cx);
451 :
452 0 : if (!JS_ParseJSON(cx, static_cast<const jschar*>(PromiseFlatString(str).get()),
453 0 : str.Length(), result)) {
454 0 : return NS_ERROR_UNEXPECTED;
455 : }
456 :
457 0 : return NS_OK;
458 : }
459 :
460 : nsresult
461 303 : nsJSON::DecodeInternal(JSContext* cx,
462 : nsIInputStream *aStream,
463 : PRInt32 aContentLength,
464 : bool aNeedsConverter,
465 : JS::Value* aRetval,
466 : DecodingMode mode /* = STRICT */)
467 : {
468 606 : JSAutoRequest ar(cx);
469 :
470 : // Consume the stream
471 606 : nsCOMPtr<nsIChannel> jsonChannel;
472 303 : if (!mURI) {
473 302 : NS_NewURI(getter_AddRefs(mURI), NS_LITERAL_CSTRING("about:blank"), 0, 0 );
474 302 : if (!mURI)
475 0 : return NS_ERROR_OUT_OF_MEMORY;
476 : }
477 :
478 : nsresult rv =
479 303 : NS_NewInputStreamChannel(getter_AddRefs(jsonChannel), mURI, aStream,
480 606 : NS_LITERAL_CSTRING("application/json"));
481 303 : if (!jsonChannel || NS_FAILED(rv))
482 0 : return NS_ERROR_FAILURE;
483 :
484 : nsRefPtr<nsJSONListener> jsonListener =
485 606 : new nsJSONListener(cx, aRetval, aNeedsConverter, mode);
486 :
487 : //XXX this stream pattern should be consolidated in netwerk
488 303 : rv = jsonListener->OnStartRequest(jsonChannel, nsnull);
489 303 : if (NS_FAILED(rv)) {
490 0 : jsonChannel->Cancel(rv);
491 0 : return rv;
492 : }
493 :
494 : nsresult status;
495 303 : jsonChannel->GetStatus(&status);
496 303 : PRUint32 offset = 0;
497 909 : while (NS_SUCCEEDED(status)) {
498 : PRUint32 available;
499 606 : rv = aStream->Available(&available);
500 606 : if (rv == NS_BASE_STREAM_CLOSED) {
501 0 : rv = NS_OK;
502 0 : break;
503 : }
504 606 : if (NS_FAILED(rv)) {
505 0 : jsonChannel->Cancel(rv);
506 0 : break;
507 : }
508 606 : if (!available)
509 303 : break; // blocking input stream has none available when done
510 :
511 303 : rv = jsonListener->OnDataAvailable(jsonChannel, nsnull,
512 303 : aStream, offset, available);
513 303 : if (NS_FAILED(rv)) {
514 0 : jsonChannel->Cancel(rv);
515 0 : break;
516 : }
517 :
518 303 : offset += available;
519 303 : jsonChannel->GetStatus(&status);
520 : }
521 303 : NS_ENSURE_SUCCESS(rv, rv);
522 :
523 303 : rv = jsonListener->OnStopRequest(jsonChannel, nsnull, status);
524 303 : NS_ENSURE_SUCCESS(rv, rv);
525 :
526 303 : return NS_OK;
527 : }
528 :
529 :
530 : NS_IMETHODIMP
531 14 : nsJSON::LegacyDecode(const nsAString& json, JSContext* cx, JS::Value* aRetval)
532 : {
533 : const PRUnichar *data;
534 14 : PRUint32 len = NS_StringGetData(json, &data);
535 28 : nsCOMPtr<nsIInputStream> stream;
536 14 : nsresult rv = NS_NewByteInputStream(getter_AddRefs(stream),
537 : (const char*) data,
538 : len * sizeof(PRUnichar),
539 14 : NS_ASSIGNMENT_DEPEND);
540 14 : NS_ENSURE_SUCCESS(rv, rv);
541 14 : return DecodeInternal(cx, stream, len, false, aRetval, LEGACY);
542 : }
543 :
544 : NS_IMETHODIMP
545 0 : nsJSON::LegacyDecodeFromStream(nsIInputStream *aStream, PRInt32 aContentLength,
546 : JSContext* cx, JS::Value* aRetval)
547 : {
548 0 : return DecodeInternal(cx, aStream, aContentLength, true, aRetval, LEGACY);
549 : }
550 :
551 : NS_IMETHODIMP
552 0 : nsJSON::LegacyDecodeToJSVal(const nsAString &str, JSContext *cx, jsval *result)
553 : {
554 0 : JSAutoRequest ar(cx);
555 :
556 0 : if (!js::ParseJSONWithReviver(cx, static_cast<const jschar*>(PromiseFlatString(str).get()),
557 : str.Length(), JS::NullValue(),
558 0 : result, LEGACY)) {
559 0 : return NS_ERROR_UNEXPECTED;
560 : }
561 :
562 0 : return NS_OK;
563 : }
564 :
565 : nsresult
566 303 : NS_NewJSON(nsISupports* aOuter, REFNSIID aIID, void** aResult)
567 : {
568 303 : nsJSON* json = new nsJSON();
569 303 : if (!json)
570 0 : return NS_ERROR_OUT_OF_MEMORY;
571 :
572 303 : NS_ADDREF(json);
573 303 : *aResult = json;
574 :
575 303 : return NS_OK;
576 : }
577 :
578 303 : nsJSONListener::nsJSONListener(JSContext *cx, jsval *rootVal,
579 : bool needsConverter,
580 : DecodingMode mode /* = STRICT */)
581 : : mNeedsConverter(needsConverter),
582 : mCx(cx),
583 : mRootVal(rootVal),
584 303 : mDecodingMode(mode)
585 : {
586 303 : }
587 :
588 606 : nsJSONListener::~nsJSONListener()
589 : {
590 1212 : }
591 :
592 0 : NS_INTERFACE_MAP_BEGIN(nsJSONListener)
593 0 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsJSONListener)
594 0 : NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
595 0 : NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
596 0 : NS_INTERFACE_MAP_END
597 :
598 303 : NS_IMPL_ADDREF(nsJSONListener)
599 303 : NS_IMPL_RELEASE(nsJSONListener)
600 :
601 : NS_IMETHODIMP
602 303 : nsJSONListener::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext)
603 : {
604 303 : mSniffBuffer.Truncate();
605 303 : mDecoder = nsnull;
606 :
607 303 : return NS_OK;
608 : }
609 :
610 : NS_IMETHODIMP
611 303 : nsJSONListener::OnStopRequest(nsIRequest *aRequest, nsISupports *aContext,
612 : nsresult aStatusCode)
613 : {
614 : nsresult rv;
615 :
616 : // This can happen with short UTF-8 messages (<4 bytes)
617 303 : if (!mSniffBuffer.IsEmpty()) {
618 : // Just consume mSniffBuffer
619 1 : rv = ProcessBytes(nsnull, 0);
620 1 : NS_ENSURE_SUCCESS(rv, rv);
621 : }
622 :
623 303 : const jschar* chars = reinterpret_cast<const jschar*>(mBufferedChars.Elements());
624 : JSBool ok = js::ParseJSONWithReviver(mCx, chars,
625 : (uint32) mBufferedChars.Length(),
626 : js::NullValue(), mRootVal,
627 303 : mDecodingMode);
628 303 : mBufferedChars.TruncateLength(0);
629 303 : return ok ? NS_OK : NS_ERROR_FAILURE;
630 : }
631 :
632 : NS_IMETHODIMP
633 303 : nsJSONListener::OnDataAvailable(nsIRequest *aRequest, nsISupports *aContext,
634 : nsIInputStream *aStream,
635 : PRUint32 aOffset, PRUint32 aLength)
636 : {
637 303 : nsresult rv = NS_OK;
638 :
639 303 : if (mNeedsConverter && mSniffBuffer.Length() < 4) {
640 286 : PRUint32 readCount = (aLength < 4) ? aLength : 4;
641 286 : rv = NS_ConsumeStream(aStream, readCount, mSniffBuffer);
642 286 : NS_ENSURE_SUCCESS(rv, rv);
643 :
644 286 : if (mSniffBuffer.Length() < 4)
645 1 : return NS_OK;
646 : }
647 :
648 : char buffer[JSON_STREAM_BUFSIZE];
649 302 : unsigned long bytesRemaining = aLength - mSniffBuffer.Length();
650 924 : while (bytesRemaining) {
651 : unsigned int bytesRead;
652 : rv = aStream->Read(buffer,
653 320 : NS_MIN((unsigned long)sizeof(buffer), bytesRemaining),
654 640 : &bytesRead);
655 320 : NS_ENSURE_SUCCESS(rv, rv);
656 320 : rv = ProcessBytes(buffer, bytesRead);
657 320 : NS_ENSURE_SUCCESS(rv, rv);
658 320 : bytesRemaining -= bytesRead;
659 : }
660 :
661 302 : return rv;
662 : }
663 :
664 : nsresult
665 321 : nsJSONListener::ProcessBytes(const char* aBuffer, PRUint32 aByteLength)
666 : {
667 : nsresult rv;
668 : // Check for BOM, or sniff charset
669 642 : nsCAutoString charset;
670 321 : if (mNeedsConverter && !mDecoder) {
671 572 : if (!nsContentUtils::CheckForBOM((const unsigned char*) mSniffBuffer.get(),
672 572 : mSniffBuffer.Length(), charset)) {
673 : // OK, found no BOM, sniff the first character to see what this is
674 : // See section 3 of RFC4627 for details on why this works.
675 286 : const char *buffer = mSniffBuffer.get();
676 286 : if (mSniffBuffer.Length() >= 4) {
677 285 : if (buffer[0] == 0x00 && buffer[1] != 0x00 &&
678 0 : buffer[2] == 0x00 && buffer[3] != 0x00) {
679 0 : charset = "UTF-16BE";
680 285 : } else if (buffer[0] != 0x00 && buffer[1] == 0x00 &&
681 0 : buffer[2] != 0x00 && buffer[3] == 0x00) {
682 0 : charset = "UTF-16LE";
683 855 : } else if (buffer[0] != 0x00 && buffer[1] != 0x00 &&
684 570 : buffer[2] != 0x00 && buffer[3] != 0x00) {
685 285 : charset = "UTF-8";
686 : }
687 : } else {
688 : // Not enough bytes to sniff, assume UTF-8
689 1 : charset = "UTF-8";
690 : }
691 : }
692 :
693 : // We should have a unicode charset by now
694 286 : rv = CheckCharset(charset.get());
695 286 : NS_ENSURE_SUCCESS(rv, rv);
696 : nsCOMPtr<nsICharsetConverterManager> ccm =
697 572 : do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
698 286 : NS_ENSURE_SUCCESS(rv, rv);
699 286 : rv = ccm->GetUnicodeDecoderRaw(charset.get(), getter_AddRefs(mDecoder));
700 286 : NS_ENSURE_SUCCESS(rv, rv);
701 :
702 : // consume the sniffed bytes
703 286 : rv = ConsumeConverted(mSniffBuffer.get(), mSniffBuffer.Length());
704 286 : NS_ENSURE_SUCCESS(rv, rv);
705 572 : mSniffBuffer.Truncate();
706 : }
707 :
708 321 : if (!aBuffer)
709 1 : return NS_OK;
710 :
711 320 : if (mNeedsConverter) {
712 285 : rv = ConsumeConverted(aBuffer, aByteLength);
713 : } else {
714 35 : PRUint32 unichars = aByteLength / sizeof(PRUnichar);
715 35 : rv = Consume((PRUnichar *) aBuffer, unichars);
716 : }
717 :
718 320 : return rv;
719 : }
720 :
721 : nsresult
722 571 : nsJSONListener::ConsumeConverted(const char* aBuffer, PRUint32 aByteLength)
723 : {
724 : nsresult rv;
725 571 : PRInt32 unicharLength = 0;
726 571 : PRInt32 srcLen = aByteLength;
727 :
728 571 : rv = mDecoder->GetMaxLength(aBuffer, srcLen, &unicharLength);
729 571 : NS_ENSURE_SUCCESS(rv, rv);
730 :
731 571 : PRUnichar* endelems = mBufferedChars.AppendElements(unicharLength);
732 571 : PRInt32 preLength = unicharLength;
733 571 : rv = mDecoder->Convert(aBuffer, &srcLen, endelems, &unicharLength);
734 571 : if (NS_FAILED(rv))
735 0 : return rv;
736 571 : NS_ABORT_IF_FALSE(preLength >= unicharLength, "GetMaxLength lied");
737 571 : if (preLength > unicharLength)
738 571 : mBufferedChars.TruncateLength(mBufferedChars.Length() - (preLength - unicharLength));
739 571 : return NS_OK;
740 : }
741 :
742 : nsresult
743 35 : nsJSONListener::Consume(const PRUnichar* aBuffer, PRUint32 aByteLength)
744 : {
745 35 : if (!mBufferedChars.AppendElements(aBuffer, aByteLength))
746 0 : return NS_ERROR_FAILURE;
747 :
748 35 : return NS_OK;
749 : }
|