summaryrefslogtreecommitdiffstats
path: root/netwerk/base/nsUnicharStreamLoader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'netwerk/base/nsUnicharStreamLoader.cpp')
-rw-r--r--netwerk/base/nsUnicharStreamLoader.cpp250
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;
+}