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 +}