netwerk/base/src/nsUnicharStreamLoader.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     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/. */
     6 #include "mozilla/DebugOnly.h"
     8 #include "nsUnicharStreamLoader.h"
     9 #include "nsIInputStream.h"
    10 #include "nsICharsetConverterManager.h"
    11 #include "nsServiceManagerUtils.h"
    12 #include <algorithm>
    14 // 1024 bytes is specified in
    15 // http://www.whatwg.org/specs/web-apps/current-work/#charset for HTML; for
    16 // other resource types (e.g. CSS) typically fewer bytes are fine too, since
    17 // they only look at things right at the beginning of the data.
    18 #define SNIFFING_BUFFER_SIZE 1024
    20 using namespace mozilla;
    22 NS_IMETHODIMP
    23 nsUnicharStreamLoader::Init(nsIUnicharStreamLoaderObserver *aObserver)
    24 {
    25   NS_ENSURE_ARG_POINTER(aObserver);
    27   mObserver = aObserver;
    29   if (!mRawData.SetCapacity(SNIFFING_BUFFER_SIZE, fallible_t()))
    30     return NS_ERROR_OUT_OF_MEMORY;
    32   return NS_OK;
    33 }
    35 nsresult
    36 nsUnicharStreamLoader::Create(nsISupports *aOuter,
    37                               REFNSIID aIID,
    38                               void **aResult)
    39 {
    40   if (aOuter) return NS_ERROR_NO_AGGREGATION;
    42   nsUnicharStreamLoader* it = new nsUnicharStreamLoader();
    43   NS_ADDREF(it);
    44   nsresult rv = it->QueryInterface(aIID, aResult);
    45   NS_RELEASE(it);
    46   return rv;
    47 }
    49 NS_IMPL_ISUPPORTS(nsUnicharStreamLoader, nsIUnicharStreamLoader,
    50                   nsIRequestObserver, nsIStreamListener)
    52 /* readonly attribute nsIChannel channel; */
    53 NS_IMETHODIMP
    54 nsUnicharStreamLoader::GetChannel(nsIChannel **aChannel)
    55 {
    56   NS_IF_ADDREF(*aChannel = mChannel);
    57   return NS_OK;
    58 }
    60 /* readonly attribute nsACString charset */
    61 NS_IMETHODIMP
    62 nsUnicharStreamLoader::GetCharset(nsACString& aCharset)
    63 {
    64   aCharset = mCharset;
    65   return NS_OK;
    66 }
    68 /* nsIRequestObserver implementation */
    69 NS_IMETHODIMP
    70 nsUnicharStreamLoader::OnStartRequest(nsIRequest*, nsISupports*)
    71 {
    72   return NS_OK;
    73 }
    75 NS_IMETHODIMP
    76 nsUnicharStreamLoader::OnStopRequest(nsIRequest *aRequest,
    77                                      nsISupports *aContext,
    78                                      nsresult aStatus)
    79 {
    80   if (!mObserver) {
    81     NS_ERROR("nsUnicharStreamLoader::OnStopRequest called before ::Init");
    82     return NS_ERROR_UNEXPECTED;
    83   }
    85   mContext = aContext;
    86   mChannel = do_QueryInterface(aRequest);
    88   nsresult rv = NS_OK;
    89   if (mRawData.Length() > 0 && NS_SUCCEEDED(aStatus)) {
    90     NS_ABORT_IF_FALSE(mBuffer.Length() == 0,
    91                       "should not have both decoded and raw data");
    92     rv = DetermineCharset();
    93   }
    95   if (NS_FAILED(rv)) {
    96     // Call the observer but pass it no data.
    97     mObserver->OnStreamComplete(this, mContext, rv, EmptyString());
    98   } else {
    99     mObserver->OnStreamComplete(this, mContext, aStatus, mBuffer);
   100   }
   102   mObserver = nullptr;
   103   mDecoder = nullptr;
   104   mContext = nullptr;
   105   mChannel = nullptr;
   106   mCharset.Truncate();
   107   mBuffer.Truncate();
   108   return rv;
   109 }
   111 /* nsIStreamListener implementation */
   112 NS_IMETHODIMP
   113 nsUnicharStreamLoader::OnDataAvailable(nsIRequest *aRequest,
   114                                        nsISupports *aContext,
   115                                        nsIInputStream *aInputStream,
   116                                        uint64_t aSourceOffset,
   117                                        uint32_t aCount)
   118 {
   119   if (!mObserver) {
   120     NS_ERROR("nsUnicharStreamLoader::OnDataAvailable called before ::Init");
   121     return NS_ERROR_UNEXPECTED;
   122   }
   124   mContext = aContext;
   125   mChannel = do_QueryInterface(aRequest);
   127   nsresult rv = NS_OK;
   128   if (mDecoder) {
   129     // process everything we've got
   130     uint32_t dummy;
   131     aInputStream->ReadSegments(WriteSegmentFun, this, aCount, &dummy);
   132   } else {
   133     // no decoder yet.  Read up to SNIFFING_BUFFER_SIZE octets into
   134     // mRawData (this is the cutoff specified in
   135     // draft-abarth-mime-sniff-06).  If we can get that much, then go
   136     // ahead and fire charset detection and read the rest.  Otherwise
   137     // wait for more data.
   139     uint32_t haveRead = mRawData.Length();
   140     uint32_t toRead = std::min(SNIFFING_BUFFER_SIZE - haveRead, aCount);
   141     uint32_t n;
   142     char *here = mRawData.BeginWriting() + haveRead;
   144     rv = aInputStream->Read(here, toRead, &n);
   145     if (NS_SUCCEEDED(rv)) {
   146       mRawData.SetLength(haveRead + n);
   147       if (mRawData.Length() == SNIFFING_BUFFER_SIZE) {
   148         rv = DetermineCharset();
   149         if (NS_SUCCEEDED(rv)) {
   150           // process what's left
   151           uint32_t dummy;
   152           aInputStream->ReadSegments(WriteSegmentFun, this, aCount - n, &dummy);
   153         }
   154       } else {
   155         NS_ABORT_IF_FALSE(n == aCount, "didn't read as much as was available");
   156       }
   157     }
   158   }
   160   mContext = nullptr;
   161   mChannel = nullptr;
   162   return rv;
   163 }
   165 /* internal */
   166 static NS_DEFINE_CID(kCharsetConverterManagerCID,
   167                      NS_ICHARSETCONVERTERMANAGER_CID);
   169 nsresult
   170 nsUnicharStreamLoader::DetermineCharset()
   171 {
   172   nsresult rv = mObserver->OnDetermineCharset(this, mContext,
   173                                               mRawData, mCharset);
   174   if (NS_FAILED(rv) || mCharset.IsEmpty()) {
   175     // The observer told us nothing useful
   176     mCharset.AssignLiteral("UTF-8");
   177   }
   179   // Create the decoder for this character set
   180   nsCOMPtr<nsICharsetConverterManager> ccm =
   181     do_GetService(kCharsetConverterManagerCID, &rv);
   182   if (NS_FAILED(rv)) return rv;
   184   // Sadly, nsIUnicharStreamLoader is exposed to extensions, so we can't
   185   // assume mozilla::css::Loader to be the only caller. Since legacy
   186   // charset alias code doesn't know about the replacement encoding,
   187   // special-case it here, but let other stuff go through legacy alias
   188   // resolution for now.
   189   if (mCharset.EqualsLiteral("replacement")) {
   190     rv = ccm->GetUnicodeDecoderRaw(mCharset.get(), getter_AddRefs(mDecoder));
   191   } else {
   192     rv = ccm->GetUnicodeDecoder(mCharset.get(), getter_AddRefs(mDecoder));
   193   }
   194   if (NS_FAILED(rv)) return rv;
   196   // Process the data into mBuffer
   197   uint32_t dummy;
   198   rv = WriteSegmentFun(nullptr, this,
   199                        mRawData.BeginReading(),
   200                        0, mRawData.Length(),
   201                        &dummy);
   202   mRawData.Truncate();
   203   return rv;
   204 }
   206 NS_METHOD
   207 nsUnicharStreamLoader::WriteSegmentFun(nsIInputStream *,
   208                                        void *aClosure,
   209                                        const char *aSegment,
   210                                        uint32_t,
   211                                        uint32_t aCount,
   212                                        uint32_t *aWriteCount)
   213 {
   214   nsUnicharStreamLoader* self = static_cast<nsUnicharStreamLoader*>(aClosure);
   216   uint32_t haveRead = self->mBuffer.Length();
   217   int32_t srcLen = aCount;
   218   int32_t dstLen;
   219   self->mDecoder->GetMaxLength(aSegment, srcLen, &dstLen);
   221   uint32_t capacity = haveRead + dstLen;
   222   if (!self->mBuffer.SetCapacity(capacity, fallible_t())) {
   223     return NS_ERROR_OUT_OF_MEMORY;
   224   }
   226   DebugOnly<nsresult> rv =
   227     self->mDecoder->Convert(aSegment,
   228                             &srcLen,
   229                             self->mBuffer.BeginWriting() + haveRead,
   230                             &dstLen);
   231   MOZ_ASSERT(NS_SUCCEEDED(rv));
   232   MOZ_ASSERT(srcLen == static_cast<int32_t>(aCount));
   233   haveRead += dstLen;
   235   self->mBuffer.SetLength(haveRead);
   236   *aWriteCount = aCount;
   237   return NS_OK;
   238 }

mercurial