netwerk/base/src/nsUnicharStreamLoader.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "mozilla/DebugOnly.h"
michael@0 7
michael@0 8 #include "nsUnicharStreamLoader.h"
michael@0 9 #include "nsIInputStream.h"
michael@0 10 #include "nsICharsetConverterManager.h"
michael@0 11 #include "nsServiceManagerUtils.h"
michael@0 12 #include <algorithm>
michael@0 13
michael@0 14 // 1024 bytes is specified in
michael@0 15 // http://www.whatwg.org/specs/web-apps/current-work/#charset for HTML; for
michael@0 16 // other resource types (e.g. CSS) typically fewer bytes are fine too, since
michael@0 17 // they only look at things right at the beginning of the data.
michael@0 18 #define SNIFFING_BUFFER_SIZE 1024
michael@0 19
michael@0 20 using namespace mozilla;
michael@0 21
michael@0 22 NS_IMETHODIMP
michael@0 23 nsUnicharStreamLoader::Init(nsIUnicharStreamLoaderObserver *aObserver)
michael@0 24 {
michael@0 25 NS_ENSURE_ARG_POINTER(aObserver);
michael@0 26
michael@0 27 mObserver = aObserver;
michael@0 28
michael@0 29 if (!mRawData.SetCapacity(SNIFFING_BUFFER_SIZE, fallible_t()))
michael@0 30 return NS_ERROR_OUT_OF_MEMORY;
michael@0 31
michael@0 32 return NS_OK;
michael@0 33 }
michael@0 34
michael@0 35 nsresult
michael@0 36 nsUnicharStreamLoader::Create(nsISupports *aOuter,
michael@0 37 REFNSIID aIID,
michael@0 38 void **aResult)
michael@0 39 {
michael@0 40 if (aOuter) return NS_ERROR_NO_AGGREGATION;
michael@0 41
michael@0 42 nsUnicharStreamLoader* it = new nsUnicharStreamLoader();
michael@0 43 NS_ADDREF(it);
michael@0 44 nsresult rv = it->QueryInterface(aIID, aResult);
michael@0 45 NS_RELEASE(it);
michael@0 46 return rv;
michael@0 47 }
michael@0 48
michael@0 49 NS_IMPL_ISUPPORTS(nsUnicharStreamLoader, nsIUnicharStreamLoader,
michael@0 50 nsIRequestObserver, nsIStreamListener)
michael@0 51
michael@0 52 /* readonly attribute nsIChannel channel; */
michael@0 53 NS_IMETHODIMP
michael@0 54 nsUnicharStreamLoader::GetChannel(nsIChannel **aChannel)
michael@0 55 {
michael@0 56 NS_IF_ADDREF(*aChannel = mChannel);
michael@0 57 return NS_OK;
michael@0 58 }
michael@0 59
michael@0 60 /* readonly attribute nsACString charset */
michael@0 61 NS_IMETHODIMP
michael@0 62 nsUnicharStreamLoader::GetCharset(nsACString& aCharset)
michael@0 63 {
michael@0 64 aCharset = mCharset;
michael@0 65 return NS_OK;
michael@0 66 }
michael@0 67
michael@0 68 /* nsIRequestObserver implementation */
michael@0 69 NS_IMETHODIMP
michael@0 70 nsUnicharStreamLoader::OnStartRequest(nsIRequest*, nsISupports*)
michael@0 71 {
michael@0 72 return NS_OK;
michael@0 73 }
michael@0 74
michael@0 75 NS_IMETHODIMP
michael@0 76 nsUnicharStreamLoader::OnStopRequest(nsIRequest *aRequest,
michael@0 77 nsISupports *aContext,
michael@0 78 nsresult aStatus)
michael@0 79 {
michael@0 80 if (!mObserver) {
michael@0 81 NS_ERROR("nsUnicharStreamLoader::OnStopRequest called before ::Init");
michael@0 82 return NS_ERROR_UNEXPECTED;
michael@0 83 }
michael@0 84
michael@0 85 mContext = aContext;
michael@0 86 mChannel = do_QueryInterface(aRequest);
michael@0 87
michael@0 88 nsresult rv = NS_OK;
michael@0 89 if (mRawData.Length() > 0 && NS_SUCCEEDED(aStatus)) {
michael@0 90 NS_ABORT_IF_FALSE(mBuffer.Length() == 0,
michael@0 91 "should not have both decoded and raw data");
michael@0 92 rv = DetermineCharset();
michael@0 93 }
michael@0 94
michael@0 95 if (NS_FAILED(rv)) {
michael@0 96 // Call the observer but pass it no data.
michael@0 97 mObserver->OnStreamComplete(this, mContext, rv, EmptyString());
michael@0 98 } else {
michael@0 99 mObserver->OnStreamComplete(this, mContext, aStatus, mBuffer);
michael@0 100 }
michael@0 101
michael@0 102 mObserver = nullptr;
michael@0 103 mDecoder = nullptr;
michael@0 104 mContext = nullptr;
michael@0 105 mChannel = nullptr;
michael@0 106 mCharset.Truncate();
michael@0 107 mBuffer.Truncate();
michael@0 108 return rv;
michael@0 109 }
michael@0 110
michael@0 111 /* nsIStreamListener implementation */
michael@0 112 NS_IMETHODIMP
michael@0 113 nsUnicharStreamLoader::OnDataAvailable(nsIRequest *aRequest,
michael@0 114 nsISupports *aContext,
michael@0 115 nsIInputStream *aInputStream,
michael@0 116 uint64_t aSourceOffset,
michael@0 117 uint32_t aCount)
michael@0 118 {
michael@0 119 if (!mObserver) {
michael@0 120 NS_ERROR("nsUnicharStreamLoader::OnDataAvailable called before ::Init");
michael@0 121 return NS_ERROR_UNEXPECTED;
michael@0 122 }
michael@0 123
michael@0 124 mContext = aContext;
michael@0 125 mChannel = do_QueryInterface(aRequest);
michael@0 126
michael@0 127 nsresult rv = NS_OK;
michael@0 128 if (mDecoder) {
michael@0 129 // process everything we've got
michael@0 130 uint32_t dummy;
michael@0 131 aInputStream->ReadSegments(WriteSegmentFun, this, aCount, &dummy);
michael@0 132 } else {
michael@0 133 // no decoder yet. Read up to SNIFFING_BUFFER_SIZE octets into
michael@0 134 // mRawData (this is the cutoff specified in
michael@0 135 // draft-abarth-mime-sniff-06). If we can get that much, then go
michael@0 136 // ahead and fire charset detection and read the rest. Otherwise
michael@0 137 // wait for more data.
michael@0 138
michael@0 139 uint32_t haveRead = mRawData.Length();
michael@0 140 uint32_t toRead = std::min(SNIFFING_BUFFER_SIZE - haveRead, aCount);
michael@0 141 uint32_t n;
michael@0 142 char *here = mRawData.BeginWriting() + haveRead;
michael@0 143
michael@0 144 rv = aInputStream->Read(here, toRead, &n);
michael@0 145 if (NS_SUCCEEDED(rv)) {
michael@0 146 mRawData.SetLength(haveRead + n);
michael@0 147 if (mRawData.Length() == SNIFFING_BUFFER_SIZE) {
michael@0 148 rv = DetermineCharset();
michael@0 149 if (NS_SUCCEEDED(rv)) {
michael@0 150 // process what's left
michael@0 151 uint32_t dummy;
michael@0 152 aInputStream->ReadSegments(WriteSegmentFun, this, aCount - n, &dummy);
michael@0 153 }
michael@0 154 } else {
michael@0 155 NS_ABORT_IF_FALSE(n == aCount, "didn't read as much as was available");
michael@0 156 }
michael@0 157 }
michael@0 158 }
michael@0 159
michael@0 160 mContext = nullptr;
michael@0 161 mChannel = nullptr;
michael@0 162 return rv;
michael@0 163 }
michael@0 164
michael@0 165 /* internal */
michael@0 166 static NS_DEFINE_CID(kCharsetConverterManagerCID,
michael@0 167 NS_ICHARSETCONVERTERMANAGER_CID);
michael@0 168
michael@0 169 nsresult
michael@0 170 nsUnicharStreamLoader::DetermineCharset()
michael@0 171 {
michael@0 172 nsresult rv = mObserver->OnDetermineCharset(this, mContext,
michael@0 173 mRawData, mCharset);
michael@0 174 if (NS_FAILED(rv) || mCharset.IsEmpty()) {
michael@0 175 // The observer told us nothing useful
michael@0 176 mCharset.AssignLiteral("UTF-8");
michael@0 177 }
michael@0 178
michael@0 179 // Create the decoder for this character set
michael@0 180 nsCOMPtr<nsICharsetConverterManager> ccm =
michael@0 181 do_GetService(kCharsetConverterManagerCID, &rv);
michael@0 182 if (NS_FAILED(rv)) return rv;
michael@0 183
michael@0 184 // Sadly, nsIUnicharStreamLoader is exposed to extensions, so we can't
michael@0 185 // assume mozilla::css::Loader to be the only caller. Since legacy
michael@0 186 // charset alias code doesn't know about the replacement encoding,
michael@0 187 // special-case it here, but let other stuff go through legacy alias
michael@0 188 // resolution for now.
michael@0 189 if (mCharset.EqualsLiteral("replacement")) {
michael@0 190 rv = ccm->GetUnicodeDecoderRaw(mCharset.get(), getter_AddRefs(mDecoder));
michael@0 191 } else {
michael@0 192 rv = ccm->GetUnicodeDecoder(mCharset.get(), getter_AddRefs(mDecoder));
michael@0 193 }
michael@0 194 if (NS_FAILED(rv)) return rv;
michael@0 195
michael@0 196 // Process the data into mBuffer
michael@0 197 uint32_t dummy;
michael@0 198 rv = WriteSegmentFun(nullptr, this,
michael@0 199 mRawData.BeginReading(),
michael@0 200 0, mRawData.Length(),
michael@0 201 &dummy);
michael@0 202 mRawData.Truncate();
michael@0 203 return rv;
michael@0 204 }
michael@0 205
michael@0 206 NS_METHOD
michael@0 207 nsUnicharStreamLoader::WriteSegmentFun(nsIInputStream *,
michael@0 208 void *aClosure,
michael@0 209 const char *aSegment,
michael@0 210 uint32_t,
michael@0 211 uint32_t aCount,
michael@0 212 uint32_t *aWriteCount)
michael@0 213 {
michael@0 214 nsUnicharStreamLoader* self = static_cast<nsUnicharStreamLoader*>(aClosure);
michael@0 215
michael@0 216 uint32_t haveRead = self->mBuffer.Length();
michael@0 217 int32_t srcLen = aCount;
michael@0 218 int32_t dstLen;
michael@0 219 self->mDecoder->GetMaxLength(aSegment, srcLen, &dstLen);
michael@0 220
michael@0 221 uint32_t capacity = haveRead + dstLen;
michael@0 222 if (!self->mBuffer.SetCapacity(capacity, fallible_t())) {
michael@0 223 return NS_ERROR_OUT_OF_MEMORY;
michael@0 224 }
michael@0 225
michael@0 226 DebugOnly<nsresult> rv =
michael@0 227 self->mDecoder->Convert(aSegment,
michael@0 228 &srcLen,
michael@0 229 self->mBuffer.BeginWriting() + haveRead,
michael@0 230 &dstLen);
michael@0 231 MOZ_ASSERT(NS_SUCCEEDED(rv));
michael@0 232 MOZ_ASSERT(srcLen == static_cast<int32_t>(aCount));
michael@0 233 haveRead += dstLen;
michael@0 234
michael@0 235 self->mBuffer.SetLength(haveRead);
michael@0 236 *aWriteCount = aCount;
michael@0 237 return NS_OK;
michael@0 238 }

mercurial