diff options
Diffstat (limited to 'netwerk/base/nsUnicharStreamLoader.cpp')
-rw-r--r-- | netwerk/base/nsUnicharStreamLoader.cpp | 250 |
1 files changed, 250 insertions, 0 deletions
diff --git a/netwerk/base/nsUnicharStreamLoader.cpp b/netwerk/base/nsUnicharStreamLoader.cpp new file mode 100644 index 000000000..115acf9ae --- /dev/null +++ b/netwerk/base/nsUnicharStreamLoader.cpp @@ -0,0 +1,250 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/DebugOnly.h" + +#include "nsUnicharStreamLoader.h" +#include "nsIInputStream.h" +#include <algorithm> +#include "mozilla/dom/EncodingUtils.h" + +// 1024 bytes is specified in +// http://www.whatwg.org/specs/web-apps/current-work/#charset for HTML; for +// other resource types (e.g. CSS) typically fewer bytes are fine too, since +// they only look at things right at the beginning of the data. +#define SNIFFING_BUFFER_SIZE 1024 + +using namespace mozilla; +using mozilla::dom::EncodingUtils; + +NS_IMETHODIMP +nsUnicharStreamLoader::Init(nsIUnicharStreamLoaderObserver *aObserver) +{ + NS_ENSURE_ARG_POINTER(aObserver); + + mObserver = aObserver; + + if (!mRawData.SetCapacity(SNIFFING_BUFFER_SIZE, fallible)) + return NS_ERROR_OUT_OF_MEMORY; + + return NS_OK; +} + +nsresult +nsUnicharStreamLoader::Create(nsISupports *aOuter, + REFNSIID aIID, + void **aResult) +{ + if (aOuter) return NS_ERROR_NO_AGGREGATION; + + nsUnicharStreamLoader* it = new nsUnicharStreamLoader(); + NS_ADDREF(it); + nsresult rv = it->QueryInterface(aIID, aResult); + NS_RELEASE(it); + return rv; +} + +NS_IMPL_ISUPPORTS(nsUnicharStreamLoader, nsIUnicharStreamLoader, + nsIRequestObserver, nsIStreamListener) + +NS_IMETHODIMP +nsUnicharStreamLoader::GetChannel(nsIChannel **aChannel) +{ + NS_IF_ADDREF(*aChannel = mChannel); + return NS_OK; +} + +NS_IMETHODIMP +nsUnicharStreamLoader::GetCharset(nsACString& aCharset) +{ + aCharset = mCharset; + return NS_OK; +} + +/* nsIRequestObserver implementation */ +NS_IMETHODIMP +nsUnicharStreamLoader::OnStartRequest(nsIRequest*, nsISupports*) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsUnicharStreamLoader::OnStopRequest(nsIRequest *aRequest, + nsISupports *aContext, + nsresult aStatus) +{ + if (!mObserver) { + NS_ERROR("nsUnicharStreamLoader::OnStopRequest called before ::Init"); + return NS_ERROR_UNEXPECTED; + } + + mContext = aContext; + mChannel = do_QueryInterface(aRequest); + + nsresult rv = NS_OK; + if (mRawData.Length() > 0 && NS_SUCCEEDED(aStatus)) { + MOZ_ASSERT(mBuffer.Length() == 0, + "should not have both decoded and raw data"); + rv = DetermineCharset(); + } + + if (NS_FAILED(rv)) { + // Call the observer but pass it no data. + mObserver->OnStreamComplete(this, mContext, rv, EmptyString()); + } else { + mObserver->OnStreamComplete(this, mContext, aStatus, mBuffer); + } + + mObserver = nullptr; + mDecoder = nullptr; + mContext = nullptr; + mChannel = nullptr; + mCharset.Truncate(); + mRawData.Truncate(); + mRawBuffer.Truncate(); + mBuffer.Truncate(); + return rv; +} + +NS_IMETHODIMP +nsUnicharStreamLoader::GetRawBuffer(nsACString& aRawBuffer) +{ + aRawBuffer = mRawBuffer; + return NS_OK; +} + +/* nsIStreamListener implementation */ +NS_IMETHODIMP +nsUnicharStreamLoader::OnDataAvailable(nsIRequest *aRequest, + nsISupports *aContext, + nsIInputStream *aInputStream, + uint64_t aSourceOffset, + uint32_t aCount) +{ + if (!mObserver) { + NS_ERROR("nsUnicharStreamLoader::OnDataAvailable called before ::Init"); + return NS_ERROR_UNEXPECTED; + } + + mContext = aContext; + mChannel = do_QueryInterface(aRequest); + + nsresult rv = NS_OK; + if (mDecoder) { + // process everything we've got + uint32_t dummy; + aInputStream->ReadSegments(WriteSegmentFun, this, aCount, &dummy); + } else { + // no decoder yet. Read up to SNIFFING_BUFFER_SIZE octets into + // mRawData (this is the cutoff specified in + // draft-abarth-mime-sniff-06). If we can get that much, then go + // ahead and fire charset detection and read the rest. Otherwise + // wait for more data. + + uint32_t haveRead = mRawData.Length(); + uint32_t toRead = std::min(SNIFFING_BUFFER_SIZE - haveRead, aCount); + uint32_t n; + char *here = mRawData.BeginWriting() + haveRead; + + rv = aInputStream->Read(here, toRead, &n); + if (NS_SUCCEEDED(rv)) { + mRawData.SetLength(haveRead + n); + if (mRawData.Length() == SNIFFING_BUFFER_SIZE) { + rv = DetermineCharset(); + if (NS_SUCCEEDED(rv)) { + // process what's left + uint32_t dummy; + aInputStream->ReadSegments(WriteSegmentFun, this, aCount - n, &dummy); + } + } else { + MOZ_ASSERT(n == aCount, "didn't read as much as was available"); + } + } + } + + mContext = nullptr; + mChannel = nullptr; + return rv; +} + +nsresult +nsUnicharStreamLoader::DetermineCharset() +{ + nsresult rv = mObserver->OnDetermineCharset(this, mContext, + mRawData, mCharset); + if (NS_FAILED(rv) || mCharset.IsEmpty()) { + // The observer told us nothing useful + mCharset.AssignLiteral("UTF-8"); + } + + // Sadly, nsIUnicharStreamLoader is exposed to extensions, so we can't + // assume mozilla::css::Loader to be the only caller. Special-casing + // replacement, since it's not invariant under a second label resolution + // operation. + if (mCharset.EqualsLiteral("replacement")) { + mDecoder = EncodingUtils::DecoderForEncoding(mCharset); + } else { + nsAutoCString charset; + if (!EncodingUtils::FindEncodingForLabelNoReplacement(mCharset, charset)) { + // If we got replacement here, the caller was not mozilla::css::Loader + // but an extension. + return NS_ERROR_UCONV_NOCONV; + } + mDecoder = EncodingUtils::DecoderForEncoding(charset); + } + + // Process the data into mBuffer + uint32_t dummy; + rv = WriteSegmentFun(nullptr, this, + mRawData.BeginReading(), + 0, mRawData.Length(), + &dummy); + mRawData.Truncate(); + return rv; +} + +nsresult +nsUnicharStreamLoader::WriteSegmentFun(nsIInputStream *, + void *aClosure, + const char *aSegment, + uint32_t, + uint32_t aCount, + uint32_t *aWriteCount) +{ + nsUnicharStreamLoader* self = static_cast<nsUnicharStreamLoader*>(aClosure); + + uint32_t haveRead = self->mBuffer.Length(); + int32_t srcLen = aCount; + int32_t dstLen; + + nsresult rv = self->mDecoder->GetMaxLength(aSegment, srcLen, &dstLen); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + uint32_t capacity = haveRead + dstLen; + if (!self->mBuffer.SetCapacity(capacity, fallible)) { + return NS_ERROR_OUT_OF_MEMORY; + } + + if (!self->mRawBuffer.Append(aSegment, aCount, fallible)) { + return NS_ERROR_OUT_OF_MEMORY; + } + + rv = self->mDecoder->Convert(aSegment, + &srcLen, + self->mBuffer.BeginWriting() + haveRead, + &dstLen); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + MOZ_ASSERT(srcLen == static_cast<int32_t>(aCount)); + haveRead += dstLen; + + self->mBuffer.SetLength(haveRead); + *aWriteCount = aCount; + return NS_OK; +} |