netwerk/base/src/nsUnicharStreamLoader.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/netwerk/base/src/nsUnicharStreamLoader.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,238 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include "mozilla/DebugOnly.h"
    1.10 +
    1.11 +#include "nsUnicharStreamLoader.h"
    1.12 +#include "nsIInputStream.h"
    1.13 +#include "nsICharsetConverterManager.h"
    1.14 +#include "nsServiceManagerUtils.h"
    1.15 +#include <algorithm>
    1.16 +
    1.17 +// 1024 bytes is specified in
    1.18 +// http://www.whatwg.org/specs/web-apps/current-work/#charset for HTML; for
    1.19 +// other resource types (e.g. CSS) typically fewer bytes are fine too, since
    1.20 +// they only look at things right at the beginning of the data.
    1.21 +#define SNIFFING_BUFFER_SIZE 1024
    1.22 +
    1.23 +using namespace mozilla;
    1.24 +
    1.25 +NS_IMETHODIMP
    1.26 +nsUnicharStreamLoader::Init(nsIUnicharStreamLoaderObserver *aObserver)
    1.27 +{
    1.28 +  NS_ENSURE_ARG_POINTER(aObserver);
    1.29 +
    1.30 +  mObserver = aObserver;
    1.31 +
    1.32 +  if (!mRawData.SetCapacity(SNIFFING_BUFFER_SIZE, fallible_t()))
    1.33 +    return NS_ERROR_OUT_OF_MEMORY;
    1.34 +
    1.35 +  return NS_OK;
    1.36 +}
    1.37 +
    1.38 +nsresult
    1.39 +nsUnicharStreamLoader::Create(nsISupports *aOuter,
    1.40 +                              REFNSIID aIID,
    1.41 +                              void **aResult)
    1.42 +{
    1.43 +  if (aOuter) return NS_ERROR_NO_AGGREGATION;
    1.44 +
    1.45 +  nsUnicharStreamLoader* it = new nsUnicharStreamLoader();
    1.46 +  NS_ADDREF(it);
    1.47 +  nsresult rv = it->QueryInterface(aIID, aResult);
    1.48 +  NS_RELEASE(it);
    1.49 +  return rv;
    1.50 +}
    1.51 +
    1.52 +NS_IMPL_ISUPPORTS(nsUnicharStreamLoader, nsIUnicharStreamLoader,
    1.53 +                  nsIRequestObserver, nsIStreamListener)
    1.54 +
    1.55 +/* readonly attribute nsIChannel channel; */
    1.56 +NS_IMETHODIMP
    1.57 +nsUnicharStreamLoader::GetChannel(nsIChannel **aChannel)
    1.58 +{
    1.59 +  NS_IF_ADDREF(*aChannel = mChannel);
    1.60 +  return NS_OK;
    1.61 +}
    1.62 +
    1.63 +/* readonly attribute nsACString charset */
    1.64 +NS_IMETHODIMP
    1.65 +nsUnicharStreamLoader::GetCharset(nsACString& aCharset)
    1.66 +{
    1.67 +  aCharset = mCharset;
    1.68 +  return NS_OK;
    1.69 +}
    1.70 +
    1.71 +/* nsIRequestObserver implementation */
    1.72 +NS_IMETHODIMP
    1.73 +nsUnicharStreamLoader::OnStartRequest(nsIRequest*, nsISupports*)
    1.74 +{
    1.75 +  return NS_OK;
    1.76 +}
    1.77 +
    1.78 +NS_IMETHODIMP
    1.79 +nsUnicharStreamLoader::OnStopRequest(nsIRequest *aRequest,
    1.80 +                                     nsISupports *aContext,
    1.81 +                                     nsresult aStatus)
    1.82 +{
    1.83 +  if (!mObserver) {
    1.84 +    NS_ERROR("nsUnicharStreamLoader::OnStopRequest called before ::Init");
    1.85 +    return NS_ERROR_UNEXPECTED;
    1.86 +  }
    1.87 +
    1.88 +  mContext = aContext;
    1.89 +  mChannel = do_QueryInterface(aRequest);
    1.90 +
    1.91 +  nsresult rv = NS_OK;
    1.92 +  if (mRawData.Length() > 0 && NS_SUCCEEDED(aStatus)) {
    1.93 +    NS_ABORT_IF_FALSE(mBuffer.Length() == 0,
    1.94 +                      "should not have both decoded and raw data");
    1.95 +    rv = DetermineCharset();
    1.96 +  }
    1.97 +
    1.98 +  if (NS_FAILED(rv)) {
    1.99 +    // Call the observer but pass it no data.
   1.100 +    mObserver->OnStreamComplete(this, mContext, rv, EmptyString());
   1.101 +  } else {
   1.102 +    mObserver->OnStreamComplete(this, mContext, aStatus, mBuffer);
   1.103 +  }
   1.104 +
   1.105 +  mObserver = nullptr;
   1.106 +  mDecoder = nullptr;
   1.107 +  mContext = nullptr;
   1.108 +  mChannel = nullptr;
   1.109 +  mCharset.Truncate();
   1.110 +  mBuffer.Truncate();
   1.111 +  return rv;
   1.112 +}
   1.113 +
   1.114 +/* nsIStreamListener implementation */
   1.115 +NS_IMETHODIMP
   1.116 +nsUnicharStreamLoader::OnDataAvailable(nsIRequest *aRequest,
   1.117 +                                       nsISupports *aContext,
   1.118 +                                       nsIInputStream *aInputStream,
   1.119 +                                       uint64_t aSourceOffset,
   1.120 +                                       uint32_t aCount)
   1.121 +{
   1.122 +  if (!mObserver) {
   1.123 +    NS_ERROR("nsUnicharStreamLoader::OnDataAvailable called before ::Init");
   1.124 +    return NS_ERROR_UNEXPECTED;
   1.125 +  }
   1.126 +
   1.127 +  mContext = aContext;
   1.128 +  mChannel = do_QueryInterface(aRequest);
   1.129 +
   1.130 +  nsresult rv = NS_OK;
   1.131 +  if (mDecoder) {
   1.132 +    // process everything we've got
   1.133 +    uint32_t dummy;
   1.134 +    aInputStream->ReadSegments(WriteSegmentFun, this, aCount, &dummy);
   1.135 +  } else {
   1.136 +    // no decoder yet.  Read up to SNIFFING_BUFFER_SIZE octets into
   1.137 +    // mRawData (this is the cutoff specified in
   1.138 +    // draft-abarth-mime-sniff-06).  If we can get that much, then go
   1.139 +    // ahead and fire charset detection and read the rest.  Otherwise
   1.140 +    // wait for more data.
   1.141 +
   1.142 +    uint32_t haveRead = mRawData.Length();
   1.143 +    uint32_t toRead = std::min(SNIFFING_BUFFER_SIZE - haveRead, aCount);
   1.144 +    uint32_t n;
   1.145 +    char *here = mRawData.BeginWriting() + haveRead;
   1.146 +
   1.147 +    rv = aInputStream->Read(here, toRead, &n);
   1.148 +    if (NS_SUCCEEDED(rv)) {
   1.149 +      mRawData.SetLength(haveRead + n);
   1.150 +      if (mRawData.Length() == SNIFFING_BUFFER_SIZE) {
   1.151 +        rv = DetermineCharset();
   1.152 +        if (NS_SUCCEEDED(rv)) {
   1.153 +          // process what's left
   1.154 +          uint32_t dummy;
   1.155 +          aInputStream->ReadSegments(WriteSegmentFun, this, aCount - n, &dummy);
   1.156 +        }
   1.157 +      } else {
   1.158 +        NS_ABORT_IF_FALSE(n == aCount, "didn't read as much as was available");
   1.159 +      }
   1.160 +    }
   1.161 +  }
   1.162 +
   1.163 +  mContext = nullptr;
   1.164 +  mChannel = nullptr;
   1.165 +  return rv;
   1.166 +}
   1.167 +
   1.168 +/* internal */
   1.169 +static NS_DEFINE_CID(kCharsetConverterManagerCID,
   1.170 +                     NS_ICHARSETCONVERTERMANAGER_CID);
   1.171 +
   1.172 +nsresult
   1.173 +nsUnicharStreamLoader::DetermineCharset()
   1.174 +{
   1.175 +  nsresult rv = mObserver->OnDetermineCharset(this, mContext,
   1.176 +                                              mRawData, mCharset);
   1.177 +  if (NS_FAILED(rv) || mCharset.IsEmpty()) {
   1.178 +    // The observer told us nothing useful
   1.179 +    mCharset.AssignLiteral("UTF-8");
   1.180 +  }
   1.181 +
   1.182 +  // Create the decoder for this character set
   1.183 +  nsCOMPtr<nsICharsetConverterManager> ccm =
   1.184 +    do_GetService(kCharsetConverterManagerCID, &rv);
   1.185 +  if (NS_FAILED(rv)) return rv;
   1.186 +
   1.187 +  // Sadly, nsIUnicharStreamLoader is exposed to extensions, so we can't
   1.188 +  // assume mozilla::css::Loader to be the only caller. Since legacy
   1.189 +  // charset alias code doesn't know about the replacement encoding,
   1.190 +  // special-case it here, but let other stuff go through legacy alias
   1.191 +  // resolution for now.
   1.192 +  if (mCharset.EqualsLiteral("replacement")) {
   1.193 +    rv = ccm->GetUnicodeDecoderRaw(mCharset.get(), getter_AddRefs(mDecoder));
   1.194 +  } else {
   1.195 +    rv = ccm->GetUnicodeDecoder(mCharset.get(), getter_AddRefs(mDecoder));
   1.196 +  }
   1.197 +  if (NS_FAILED(rv)) return rv;
   1.198 +
   1.199 +  // Process the data into mBuffer
   1.200 +  uint32_t dummy;
   1.201 +  rv = WriteSegmentFun(nullptr, this,
   1.202 +                       mRawData.BeginReading(),
   1.203 +                       0, mRawData.Length(),
   1.204 +                       &dummy);
   1.205 +  mRawData.Truncate();
   1.206 +  return rv;
   1.207 +}
   1.208 +
   1.209 +NS_METHOD
   1.210 +nsUnicharStreamLoader::WriteSegmentFun(nsIInputStream *,
   1.211 +                                       void *aClosure,
   1.212 +                                       const char *aSegment,
   1.213 +                                       uint32_t,
   1.214 +                                       uint32_t aCount,
   1.215 +                                       uint32_t *aWriteCount)
   1.216 +{
   1.217 +  nsUnicharStreamLoader* self = static_cast<nsUnicharStreamLoader*>(aClosure);
   1.218 +
   1.219 +  uint32_t haveRead = self->mBuffer.Length();
   1.220 +  int32_t srcLen = aCount;
   1.221 +  int32_t dstLen;
   1.222 +  self->mDecoder->GetMaxLength(aSegment, srcLen, &dstLen);
   1.223 +
   1.224 +  uint32_t capacity = haveRead + dstLen;
   1.225 +  if (!self->mBuffer.SetCapacity(capacity, fallible_t())) {
   1.226 +    return NS_ERROR_OUT_OF_MEMORY;
   1.227 +  }
   1.228 +
   1.229 +  DebugOnly<nsresult> rv =
   1.230 +    self->mDecoder->Convert(aSegment,
   1.231 +                            &srcLen,
   1.232 +                            self->mBuffer.BeginWriting() + haveRead,
   1.233 +                            &dstLen);
   1.234 +  MOZ_ASSERT(NS_SUCCEEDED(rv));
   1.235 +  MOZ_ASSERT(srcLen == static_cast<int32_t>(aCount));
   1.236 +  haveRead += dstLen;
   1.237 +
   1.238 +  self->mBuffer.SetLength(haveRead);
   1.239 +  *aWriteCount = aCount;
   1.240 +  return NS_OK;
   1.241 +}

mercurial