Line data Source code
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 <algorithm>
11 : #include "mozilla/Encoding.h"
12 :
13 : // 1024 bytes is specified in
14 : // http://www.whatwg.org/specs/web-apps/current-work/#charset for HTML; for
15 : // other resource types (e.g. CSS) typically fewer bytes are fine too, since
16 : // they only look at things right at the beginning of the data.
17 : #define SNIFFING_BUFFER_SIZE 1024
18 :
19 : using namespace mozilla;
20 :
21 : NS_IMETHODIMP
22 53 : nsUnicharStreamLoader::Init(nsIUnicharStreamLoaderObserver *aObserver)
23 : {
24 53 : NS_ENSURE_ARG_POINTER(aObserver);
25 :
26 53 : mObserver = aObserver;
27 :
28 53 : if (!mRawData.SetCapacity(SNIFFING_BUFFER_SIZE, fallible))
29 0 : return NS_ERROR_OUT_OF_MEMORY;
30 :
31 53 : return NS_OK;
32 : }
33 :
34 : nsresult
35 53 : nsUnicharStreamLoader::Create(nsISupports *aOuter,
36 : REFNSIID aIID,
37 : void **aResult)
38 : {
39 53 : if (aOuter) return NS_ERROR_NO_AGGREGATION;
40 :
41 53 : nsUnicharStreamLoader* it = new nsUnicharStreamLoader();
42 53 : NS_ADDREF(it);
43 53 : nsresult rv = it->QueryInterface(aIID, aResult);
44 53 : NS_RELEASE(it);
45 53 : return rv;
46 : }
47 :
48 663 : NS_IMPL_ISUPPORTS(nsUnicharStreamLoader, nsIUnicharStreamLoader,
49 : nsIRequestObserver, nsIStreamListener)
50 :
51 : NS_IMETHODIMP
52 106 : nsUnicharStreamLoader::GetChannel(nsIChannel **aChannel)
53 : {
54 106 : NS_IF_ADDREF(*aChannel = mChannel);
55 106 : return NS_OK;
56 : }
57 :
58 : NS_IMETHODIMP
59 0 : nsUnicharStreamLoader::GetCharset(nsACString& aCharset)
60 : {
61 0 : aCharset = mCharset;
62 0 : return NS_OK;
63 : }
64 :
65 : /* nsIRequestObserver implementation */
66 : NS_IMETHODIMP
67 53 : nsUnicharStreamLoader::OnStartRequest(nsIRequest*, nsISupports*)
68 : {
69 53 : return NS_OK;
70 : }
71 :
72 : NS_IMETHODIMP
73 53 : nsUnicharStreamLoader::OnStopRequest(nsIRequest *aRequest,
74 : nsISupports *aContext,
75 : nsresult aStatus)
76 : {
77 53 : if (!mObserver) {
78 0 : NS_ERROR("nsUnicharStreamLoader::OnStopRequest called before ::Init");
79 0 : return NS_ERROR_UNEXPECTED;
80 : }
81 :
82 53 : mContext = aContext;
83 53 : mChannel = do_QueryInterface(aRequest);
84 :
85 53 : nsresult rv = NS_OK;
86 53 : if (mRawData.Length() > 0 && NS_SUCCEEDED(aStatus)) {
87 14 : MOZ_ASSERT(mBuffer.Length() == 0,
88 : "should not have both decoded and raw data");
89 14 : rv = DetermineCharset();
90 : }
91 :
92 53 : if (NS_FAILED(rv)) {
93 : // Call the observer but pass it no data.
94 0 : mObserver->OnStreamComplete(this, mContext, rv, EmptyString());
95 : } else {
96 53 : mObserver->OnStreamComplete(this, mContext, aStatus, mBuffer);
97 : }
98 :
99 53 : mObserver = nullptr;
100 53 : mDecoder = nullptr;
101 53 : mContext = nullptr;
102 53 : mChannel = nullptr;
103 53 : mCharset.Truncate();
104 53 : mRawData.Truncate();
105 53 : mRawBuffer.Truncate();
106 53 : mBuffer.Truncate();
107 53 : return rv;
108 : }
109 :
110 : NS_IMETHODIMP
111 0 : nsUnicharStreamLoader::GetRawBuffer(nsACString& aRawBuffer)
112 : {
113 0 : aRawBuffer = mRawBuffer;
114 0 : return NS_OK;
115 : }
116 :
117 : /* nsIStreamListener implementation */
118 : NS_IMETHODIMP
119 53 : nsUnicharStreamLoader::OnDataAvailable(nsIRequest *aRequest,
120 : nsISupports *aContext,
121 : nsIInputStream *aInputStream,
122 : uint64_t aSourceOffset,
123 : uint32_t aCount)
124 : {
125 53 : if (!mObserver) {
126 0 : NS_ERROR("nsUnicharStreamLoader::OnDataAvailable called before ::Init");
127 0 : return NS_ERROR_UNEXPECTED;
128 : }
129 :
130 53 : mContext = aContext;
131 53 : mChannel = do_QueryInterface(aRequest);
132 :
133 53 : nsresult rv = NS_OK;
134 53 : if (mDecoder) {
135 : // process everything we've got
136 : uint32_t dummy;
137 0 : aInputStream->ReadSegments(WriteSegmentFun, this, aCount, &dummy);
138 : } else {
139 : // no decoder yet. Read up to SNIFFING_BUFFER_SIZE octets into
140 : // mRawData (this is the cutoff specified in
141 : // draft-abarth-mime-sniff-06). If we can get that much, then go
142 : // ahead and fire charset detection and read the rest. Otherwise
143 : // wait for more data.
144 :
145 53 : uint32_t haveRead = mRawData.Length();
146 53 : uint32_t toRead = std::min(SNIFFING_BUFFER_SIZE - haveRead, aCount);
147 : uint32_t n;
148 53 : char *here = mRawData.BeginWriting() + haveRead;
149 :
150 53 : rv = aInputStream->Read(here, toRead, &n);
151 53 : if (NS_SUCCEEDED(rv)) {
152 53 : mRawData.SetLength(haveRead + n);
153 53 : if (mRawData.Length() == SNIFFING_BUFFER_SIZE) {
154 39 : rv = DetermineCharset();
155 39 : if (NS_SUCCEEDED(rv)) {
156 : // process what's left
157 : uint32_t dummy;
158 39 : aInputStream->ReadSegments(WriteSegmentFun, this, aCount - n, &dummy);
159 : }
160 : } else {
161 14 : MOZ_ASSERT(n == aCount, "didn't read as much as was available");
162 : }
163 : }
164 : }
165 :
166 53 : mContext = nullptr;
167 53 : mChannel = nullptr;
168 53 : return rv;
169 : }
170 :
171 : nsresult
172 53 : nsUnicharStreamLoader::DetermineCharset()
173 : {
174 106 : nsresult rv = mObserver->OnDetermineCharset(this, mContext,
175 106 : mRawData, mCharset);
176 53 : if (NS_FAILED(rv) || mCharset.IsEmpty()) {
177 : // The observer told us nothing useful
178 0 : mCharset.AssignLiteral("UTF-8");
179 : }
180 :
181 : // Sadly, nsIUnicharStreamLoader is exposed to extensions, so we can't
182 : // assume mozilla::css::Loader to be the only caller. Special-casing
183 : // replacement, since it's not invariant under a second label resolution
184 : // operation.
185 53 : if (mCharset.EqualsLiteral("replacement")) {
186 0 : mDecoder = REPLACEMENT_ENCODING->NewDecoderWithBOMRemoval();
187 : } else {
188 53 : const Encoding* encoding = Encoding::ForLabelNoReplacement(mCharset);
189 53 : if (!encoding) {
190 : // If we got replacement here, the caller was not mozilla::css::Loader
191 : // but an extension.
192 0 : return NS_ERROR_UCONV_NOCONV;
193 : }
194 53 : mDecoder = encoding->NewDecoderWithBOMRemoval();
195 : }
196 :
197 : // Process the data into mBuffer
198 : uint32_t dummy;
199 53 : rv = WriteSegmentFun(nullptr, this,
200 : mRawData.BeginReading(),
201 : 0, mRawData.Length(),
202 53 : &dummy);
203 53 : mRawData.Truncate();
204 53 : return rv;
205 : }
206 :
207 : nsresult
208 99 : nsUnicharStreamLoader::WriteSegmentFun(nsIInputStream *,
209 : void *aClosure,
210 : const char *aSegment,
211 : uint32_t,
212 : uint32_t aCount,
213 : uint32_t *aWriteCount)
214 : {
215 99 : nsUnicharStreamLoader* self = static_cast<nsUnicharStreamLoader*>(aClosure);
216 :
217 99 : nsAString::size_type haveRead(self->mBuffer.Length());
218 :
219 99 : CheckedInt<size_t> needed = self->mDecoder->MaxUTF16BufferLength(aCount);
220 99 : if (!needed.isValid()) {
221 0 : return NS_ERROR_OUT_OF_MEMORY;
222 : }
223 :
224 99 : CheckedInt<nsAString::size_type> capacity(needed.value());
225 99 : capacity += haveRead;
226 99 : if (!capacity.isValid()) {
227 0 : return NS_ERROR_OUT_OF_MEMORY;
228 : }
229 :
230 99 : if (!self->mBuffer.SetCapacity(capacity.value(), fallible)) {
231 0 : return NS_ERROR_OUT_OF_MEMORY;
232 : }
233 :
234 99 : if (!self->mRawBuffer.Append(aSegment, aCount, fallible)) {
235 0 : return NS_ERROR_OUT_OF_MEMORY;
236 : }
237 :
238 : uint32_t result;
239 : size_t read;
240 : size_t written;
241 : bool hadErrors;
242 :
243 297 : Tie(result, read, written, hadErrors) = self->mDecoder->DecodeToUTF16(
244 : AsBytes(MakeSpan(aSegment, aCount)),
245 99 : MakeSpan(self->mBuffer.BeginWriting() + haveRead, needed.value()),
246 99 : false);
247 99 : MOZ_ASSERT(result == kInputEmpty);
248 99 : MOZ_ASSERT(read == aCount);
249 : Unused << hadErrors;
250 :
251 99 : CheckedInt<nsAString::size_type> newLen(written);
252 99 : newLen += haveRead;
253 99 : if (!newLen.isValid()) {
254 0 : return NS_ERROR_OUT_OF_MEMORY;
255 : }
256 :
257 99 : self->mBuffer.SetLength(newLen.value());
258 99 : *aWriteCount = aCount;
259 99 : return NS_OK;
260 : }
|