123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251 |
- /* -*- 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;
- }
|