LCOV - code coverage report
Current view: directory - dom/src/json - nsJSON.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 342 272 79.5 %
Date: 2012-06-02 Functions: 42 36 85.7 %

       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                 : }

Generated by: LCOV version 1.7