LCOV - code coverage report
Current view: top level - netwerk/base - nsUnicharStreamLoader.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 90 111 81.1 %
Date: 2017-07-14 16:53:18 Functions: 11 13 84.6 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       5             : 
       6             : #include "mozilla/DebugOnly.h"
       7             : 
       8             : #include "nsUnicharStreamLoader.h"
       9             : #include "nsIInputStream.h"
      10             : #include <algorithm>
      11             : #include "mozilla/Encoding.h"
      12             : 
      13             : // 1024 bytes is specified in
      14             : // http://www.whatwg.org/specs/web-apps/current-work/#charset for HTML; for
      15             : // other resource types (e.g. CSS) typically fewer bytes are fine too, since
      16             : // they only look at things right at the beginning of the data.
      17             : #define SNIFFING_BUFFER_SIZE 1024
      18             : 
      19             : using namespace mozilla;
      20             : 
      21             : NS_IMETHODIMP
      22          53 : nsUnicharStreamLoader::Init(nsIUnicharStreamLoaderObserver *aObserver)
      23             : {
      24          53 :   NS_ENSURE_ARG_POINTER(aObserver);
      25             : 
      26          53 :   mObserver = aObserver;
      27             : 
      28          53 :   if (!mRawData.SetCapacity(SNIFFING_BUFFER_SIZE, fallible))
      29           0 :     return NS_ERROR_OUT_OF_MEMORY;
      30             : 
      31          53 :   return NS_OK;
      32             : }
      33             : 
      34             : nsresult
      35          53 : nsUnicharStreamLoader::Create(nsISupports *aOuter,
      36             :                               REFNSIID aIID,
      37             :                               void **aResult)
      38             : {
      39          53 :   if (aOuter) return NS_ERROR_NO_AGGREGATION;
      40             : 
      41          53 :   nsUnicharStreamLoader* it = new nsUnicharStreamLoader();
      42          53 :   NS_ADDREF(it);
      43          53 :   nsresult rv = it->QueryInterface(aIID, aResult);
      44          53 :   NS_RELEASE(it);
      45          53 :   return rv;
      46             : }
      47             : 
      48         663 : NS_IMPL_ISUPPORTS(nsUnicharStreamLoader, nsIUnicharStreamLoader,
      49             :                   nsIRequestObserver, nsIStreamListener)
      50             : 
      51             : NS_IMETHODIMP
      52         106 : nsUnicharStreamLoader::GetChannel(nsIChannel **aChannel)
      53             : {
      54         106 :   NS_IF_ADDREF(*aChannel = mChannel);
      55         106 :   return NS_OK;
      56             : }
      57             : 
      58             : NS_IMETHODIMP
      59           0 : nsUnicharStreamLoader::GetCharset(nsACString& aCharset)
      60             : {
      61           0 :   aCharset = mCharset;
      62           0 :   return NS_OK;
      63             : }
      64             : 
      65             : /* nsIRequestObserver implementation */
      66             : NS_IMETHODIMP
      67          53 : nsUnicharStreamLoader::OnStartRequest(nsIRequest*, nsISupports*)
      68             : {
      69          53 :   return NS_OK;
      70             : }
      71             : 
      72             : NS_IMETHODIMP
      73          53 : nsUnicharStreamLoader::OnStopRequest(nsIRequest *aRequest,
      74             :                                      nsISupports *aContext,
      75             :                                      nsresult aStatus)
      76             : {
      77          53 :   if (!mObserver) {
      78           0 :     NS_ERROR("nsUnicharStreamLoader::OnStopRequest called before ::Init");
      79           0 :     return NS_ERROR_UNEXPECTED;
      80             :   }
      81             : 
      82          53 :   mContext = aContext;
      83          53 :   mChannel = do_QueryInterface(aRequest);
      84             : 
      85          53 :   nsresult rv = NS_OK;
      86          53 :   if (mRawData.Length() > 0 && NS_SUCCEEDED(aStatus)) {
      87          14 :     MOZ_ASSERT(mBuffer.Length() == 0,
      88             :                "should not have both decoded and raw data");
      89          14 :     rv = DetermineCharset();
      90             :   }
      91             : 
      92          53 :   if (NS_FAILED(rv)) {
      93             :     // Call the observer but pass it no data.
      94           0 :     mObserver->OnStreamComplete(this, mContext, rv, EmptyString());
      95             :   } else {
      96          53 :     mObserver->OnStreamComplete(this, mContext, aStatus, mBuffer);
      97             :   }
      98             : 
      99          53 :   mObserver = nullptr;
     100          53 :   mDecoder = nullptr;
     101          53 :   mContext = nullptr;
     102          53 :   mChannel = nullptr;
     103          53 :   mCharset.Truncate();
     104          53 :   mRawData.Truncate();
     105          53 :   mRawBuffer.Truncate();
     106          53 :   mBuffer.Truncate();
     107          53 :   return rv;
     108             : }
     109             : 
     110             : NS_IMETHODIMP
     111           0 : nsUnicharStreamLoader::GetRawBuffer(nsACString& aRawBuffer)
     112             : {
     113           0 :   aRawBuffer = mRawBuffer;
     114           0 :   return NS_OK;
     115             : }
     116             : 
     117             : /* nsIStreamListener implementation */
     118             : NS_IMETHODIMP
     119          53 : nsUnicharStreamLoader::OnDataAvailable(nsIRequest *aRequest,
     120             :                                        nsISupports *aContext,
     121             :                                        nsIInputStream *aInputStream,
     122             :                                        uint64_t aSourceOffset,
     123             :                                        uint32_t aCount)
     124             : {
     125          53 :   if (!mObserver) {
     126           0 :     NS_ERROR("nsUnicharStreamLoader::OnDataAvailable called before ::Init");
     127           0 :     return NS_ERROR_UNEXPECTED;
     128             :   }
     129             : 
     130          53 :   mContext = aContext;
     131          53 :   mChannel = do_QueryInterface(aRequest);
     132             : 
     133          53 :   nsresult rv = NS_OK;
     134          53 :   if (mDecoder) {
     135             :     // process everything we've got
     136             :     uint32_t dummy;
     137           0 :     aInputStream->ReadSegments(WriteSegmentFun, this, aCount, &dummy);
     138             :   } else {
     139             :     // no decoder yet.  Read up to SNIFFING_BUFFER_SIZE octets into
     140             :     // mRawData (this is the cutoff specified in
     141             :     // draft-abarth-mime-sniff-06).  If we can get that much, then go
     142             :     // ahead and fire charset detection and read the rest.  Otherwise
     143             :     // wait for more data.
     144             : 
     145          53 :     uint32_t haveRead = mRawData.Length();
     146          53 :     uint32_t toRead = std::min(SNIFFING_BUFFER_SIZE - haveRead, aCount);
     147             :     uint32_t n;
     148          53 :     char *here = mRawData.BeginWriting() + haveRead;
     149             : 
     150          53 :     rv = aInputStream->Read(here, toRead, &n);
     151          53 :     if (NS_SUCCEEDED(rv)) {
     152          53 :       mRawData.SetLength(haveRead + n);
     153          53 :       if (mRawData.Length() == SNIFFING_BUFFER_SIZE) {
     154          39 :         rv = DetermineCharset();
     155          39 :         if (NS_SUCCEEDED(rv)) {
     156             :           // process what's left
     157             :           uint32_t dummy;
     158          39 :           aInputStream->ReadSegments(WriteSegmentFun, this, aCount - n, &dummy);
     159             :         }
     160             :       } else {
     161          14 :         MOZ_ASSERT(n == aCount, "didn't read as much as was available");
     162             :       }
     163             :     }
     164             :   }
     165             : 
     166          53 :   mContext = nullptr;
     167          53 :   mChannel = nullptr;
     168          53 :   return rv;
     169             : }
     170             : 
     171             : nsresult
     172          53 : nsUnicharStreamLoader::DetermineCharset()
     173             : {
     174         106 :   nsresult rv = mObserver->OnDetermineCharset(this, mContext,
     175         106 :                                               mRawData, mCharset);
     176          53 :   if (NS_FAILED(rv) || mCharset.IsEmpty()) {
     177             :     // The observer told us nothing useful
     178           0 :     mCharset.AssignLiteral("UTF-8");
     179             :   }
     180             : 
     181             :   // Sadly, nsIUnicharStreamLoader is exposed to extensions, so we can't
     182             :   // assume mozilla::css::Loader to be the only caller. Special-casing
     183             :   // replacement, since it's not invariant under a second label resolution
     184             :   // operation.
     185          53 :   if (mCharset.EqualsLiteral("replacement")) {
     186           0 :     mDecoder = REPLACEMENT_ENCODING->NewDecoderWithBOMRemoval();
     187             :   } else {
     188          53 :     const Encoding* encoding = Encoding::ForLabelNoReplacement(mCharset);
     189          53 :     if (!encoding) {
     190             :       // If we got replacement here, the caller was not mozilla::css::Loader
     191             :       // but an extension.
     192           0 :       return NS_ERROR_UCONV_NOCONV;
     193             :     }
     194          53 :     mDecoder = encoding->NewDecoderWithBOMRemoval();
     195             :   }
     196             : 
     197             :   // Process the data into mBuffer
     198             :   uint32_t dummy;
     199          53 :   rv = WriteSegmentFun(nullptr, this,
     200             :                        mRawData.BeginReading(),
     201             :                        0, mRawData.Length(),
     202          53 :                        &dummy);
     203          53 :   mRawData.Truncate();
     204          53 :   return rv;
     205             : }
     206             : 
     207             : nsresult
     208          99 : nsUnicharStreamLoader::WriteSegmentFun(nsIInputStream *,
     209             :                                        void *aClosure,
     210             :                                        const char *aSegment,
     211             :                                        uint32_t,
     212             :                                        uint32_t aCount,
     213             :                                        uint32_t *aWriteCount)
     214             : {
     215          99 :   nsUnicharStreamLoader* self = static_cast<nsUnicharStreamLoader*>(aClosure);
     216             : 
     217          99 :   nsAString::size_type haveRead(self->mBuffer.Length());
     218             : 
     219          99 :   CheckedInt<size_t> needed = self->mDecoder->MaxUTF16BufferLength(aCount);
     220          99 :   if (!needed.isValid()) {
     221           0 :     return NS_ERROR_OUT_OF_MEMORY;
     222             :   }
     223             : 
     224          99 :   CheckedInt<nsAString::size_type> capacity(needed.value());
     225          99 :   capacity += haveRead;
     226          99 :   if (!capacity.isValid()) {
     227           0 :     return NS_ERROR_OUT_OF_MEMORY;
     228             :   }
     229             : 
     230          99 :   if (!self->mBuffer.SetCapacity(capacity.value(), fallible)) {
     231           0 :     return NS_ERROR_OUT_OF_MEMORY;
     232             :   }
     233             : 
     234          99 :   if (!self->mRawBuffer.Append(aSegment, aCount, fallible)) {
     235           0 :     return NS_ERROR_OUT_OF_MEMORY;
     236             :   }
     237             : 
     238             :   uint32_t result;
     239             :   size_t read;
     240             :   size_t written;
     241             :   bool hadErrors;
     242             : 
     243         297 :   Tie(result, read, written, hadErrors) = self->mDecoder->DecodeToUTF16(
     244             :     AsBytes(MakeSpan(aSegment, aCount)),
     245          99 :     MakeSpan(self->mBuffer.BeginWriting() + haveRead, needed.value()),
     246          99 :     false);
     247          99 :   MOZ_ASSERT(result == kInputEmpty);
     248          99 :   MOZ_ASSERT(read == aCount);
     249             :   Unused << hadErrors;
     250             : 
     251          99 :   CheckedInt<nsAString::size_type> newLen(written);
     252          99 :   newLen += haveRead;
     253          99 :   if (!newLen.isValid()) {
     254           0 :     return NS_ERROR_OUT_OF_MEMORY;
     255             :   }
     256             : 
     257          99 :   self->mBuffer.SetLength(newLen.value());
     258          99 :   *aWriteCount = aCount;
     259          99 :   return NS_OK;
     260             : }

Generated by: LCOV version 1.13