LCOV - app.info - dom/src/json/nsJSON.cpp

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-04-21 | 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             925 :   while (bytesRemaining) {
         651                 :     unsigned int bytesRead;
         652                 :     rv = aStream->Read(buffer,
         653             321 :                        NS_MIN((unsigned long)sizeof(buffer), bytesRemaining),
         654             642 :                        &bytesRead);
         655             321 :     NS_ENSURE_SUCCESS(rv, rv);
         656             321 :     rv = ProcessBytes(buffer, bytesRead);
         657             321 :     NS_ENSURE_SUCCESS(rv, rv);
         658             321 :     bytesRemaining -= bytesRead;
         659                 :   }
         660                 :
         661             302 :   return rv;
         662                 : }
         663                 :
         664                 : nsresult
         665             322 : nsJSONListener::ProcessBytes(const char* aBuffer, PRUint32 aByteLength)
         666                 : {
         667                 :   nsresult rv;
         668                 :   // Check for BOM, or sniff charset
         669             644 :   nsCAutoString charset;
         670             322 :   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             322 :   if (!aBuffer)
         709               1 :     return NS_OK;
         710                 :
         711             321 :   if (mNeedsConverter) {
         712             285 :     rv = ConsumeConverted(aBuffer, aByteLength);
         713                 :   } else {
         714              36 :     PRUint32 unichars = aByteLength / sizeof(PRUnichar);
         715              36 :     rv = Consume((PRUnichar *) aBuffer, unichars);
         716                 :   }
         717                 :
         718             321 :   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              36 : nsJSONListener::Consume(const PRUnichar* aBuffer, PRUint32 aByteLength)
         744                 : {
         745              36 :   if (!mBufferedChars.AppendElements(aBuffer, aByteLength))
         746               0 :     return NS_ERROR_FAILURE;
         747                 :
         748              36 :   return NS_OK;
         749                 : }

Generated by: LCOV version 1.7