LCOV - code coverage report
Current view: top level - dom/json - nsJSON.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 103 271 38.0 %
Date: 2017-07-14 16:53:18 Functions: 17 38 44.7 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13