netwerk/base/src/nsUnicharStreamLoader.cpp

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

mercurial