Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
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/. */
6 #include "nsUnicharInputStream.h"
7 #include "nsIInputStream.h"
8 #include "nsIServiceManager.h"
9 #include "nsString.h"
10 #include "nsTArray.h"
11 #include "nsAutoPtr.h"
12 #include "nsCRT.h"
13 #include "nsStreamUtils.h"
14 #include "nsUTF8Utils.h"
15 #include "mozilla/Attributes.h"
16 #include <fcntl.h>
17 #if defined(XP_WIN)
18 #include <io.h>
19 #else
20 #include <unistd.h>
21 #endif
23 #define STRING_BUFFER_SIZE 8192
25 class StringUnicharInputStream MOZ_FINAL : public nsIUnicharInputStream {
26 public:
27 StringUnicharInputStream(const nsAString& aString) :
28 mString(aString), mPos(0), mLen(aString.Length()) { }
30 NS_DECL_ISUPPORTS
31 NS_DECL_NSIUNICHARINPUTSTREAM
33 nsString mString;
34 uint32_t mPos;
35 uint32_t mLen;
37 private:
38 ~StringUnicharInputStream() { }
39 };
41 NS_IMETHODIMP
42 StringUnicharInputStream::Read(char16_t* aBuf,
43 uint32_t aCount,
44 uint32_t *aReadCount)
45 {
46 if (mPos >= mLen) {
47 *aReadCount = 0;
48 return NS_OK;
49 }
50 nsAString::const_iterator iter;
51 mString.BeginReading(iter);
52 const char16_t* us = iter.get();
53 uint32_t amount = mLen - mPos;
54 if (amount > aCount) {
55 amount = aCount;
56 }
57 memcpy(aBuf, us + mPos, sizeof(char16_t) * amount);
58 mPos += amount;
59 *aReadCount = amount;
60 return NS_OK;
61 }
63 NS_IMETHODIMP
64 StringUnicharInputStream::ReadSegments(nsWriteUnicharSegmentFun aWriter,
65 void* aClosure,
66 uint32_t aCount, uint32_t *aReadCount)
67 {
68 uint32_t bytesWritten;
69 uint32_t totalBytesWritten = 0;
71 nsresult rv;
72 aCount = XPCOM_MIN(mString.Length() - mPos, aCount);
74 nsAString::const_iterator iter;
75 mString.BeginReading(iter);
77 while (aCount) {
78 rv = aWriter(this, aClosure, iter.get() + mPos,
79 totalBytesWritten, aCount, &bytesWritten);
81 if (NS_FAILED(rv)) {
82 // don't propagate errors to the caller
83 break;
84 }
86 aCount -= bytesWritten;
87 totalBytesWritten += bytesWritten;
88 mPos += bytesWritten;
89 }
91 *aReadCount = totalBytesWritten;
93 return NS_OK;
94 }
96 NS_IMETHODIMP
97 StringUnicharInputStream::ReadString(uint32_t aCount, nsAString& aString,
98 uint32_t* aReadCount)
99 {
100 if (mPos >= mLen) {
101 *aReadCount = 0;
102 return NS_OK;
103 }
104 uint32_t amount = mLen - mPos;
105 if (amount > aCount) {
106 amount = aCount;
107 }
108 aString = Substring(mString, mPos, amount);
109 mPos += amount;
110 *aReadCount = amount;
111 return NS_OK;
112 }
114 nsresult StringUnicharInputStream::Close()
115 {
116 mPos = mLen;
117 return NS_OK;
118 }
120 NS_IMPL_ISUPPORTS(StringUnicharInputStream, nsIUnicharInputStream)
122 //----------------------------------------------------------------------
124 class UTF8InputStream MOZ_FINAL : public nsIUnicharInputStream {
125 public:
126 UTF8InputStream();
127 nsresult Init(nsIInputStream* aStream);
129 NS_DECL_ISUPPORTS
130 NS_DECL_NSIUNICHARINPUTSTREAM
132 private:
133 ~UTF8InputStream();
135 protected:
136 int32_t Fill(nsresult * aErrorCode);
138 static void CountValidUTF8Bytes(const char *aBuf, uint32_t aMaxBytes, uint32_t& aValidUTF8bytes, uint32_t& aValidUTF16CodeUnits);
140 nsCOMPtr<nsIInputStream> mInput;
141 FallibleTArray<char> mByteData;
142 FallibleTArray<char16_t> mUnicharData;
144 uint32_t mByteDataOffset;
145 uint32_t mUnicharDataOffset;
146 uint32_t mUnicharDataLength;
147 };
149 UTF8InputStream::UTF8InputStream() :
150 mByteDataOffset(0),
151 mUnicharDataOffset(0),
152 mUnicharDataLength(0)
153 {
154 }
156 nsresult
157 UTF8InputStream::Init(nsIInputStream* aStream)
158 {
159 if (!mByteData.SetCapacity(STRING_BUFFER_SIZE) ||
160 !mUnicharData.SetCapacity(STRING_BUFFER_SIZE)) {
161 return NS_ERROR_OUT_OF_MEMORY;
162 }
163 mInput = aStream;
165 return NS_OK;
166 }
168 NS_IMPL_ISUPPORTS(UTF8InputStream,nsIUnicharInputStream)
170 UTF8InputStream::~UTF8InputStream()
171 {
172 Close();
173 }
175 nsresult UTF8InputStream::Close()
176 {
177 mInput = nullptr;
178 mByteData.Clear();
179 mUnicharData.Clear();
180 return NS_OK;
181 }
183 nsresult UTF8InputStream::Read(char16_t* aBuf,
184 uint32_t aCount,
185 uint32_t *aReadCount)
186 {
187 NS_ASSERTION(mUnicharDataLength >= mUnicharDataOffset, "unsigned madness");
188 uint32_t readCount = mUnicharDataLength - mUnicharDataOffset;
189 nsresult errorCode;
190 if (0 == readCount) {
191 // Fill the unichar buffer
192 int32_t bytesRead = Fill(&errorCode);
193 if (bytesRead <= 0) {
194 *aReadCount = 0;
195 return errorCode;
196 }
197 readCount = bytesRead;
198 }
199 if (readCount > aCount) {
200 readCount = aCount;
201 }
202 memcpy(aBuf, mUnicharData.Elements() + mUnicharDataOffset,
203 readCount * sizeof(char16_t));
204 mUnicharDataOffset += readCount;
205 *aReadCount = readCount;
206 return NS_OK;
207 }
209 NS_IMETHODIMP
210 UTF8InputStream::ReadSegments(nsWriteUnicharSegmentFun aWriter,
211 void* aClosure,
212 uint32_t aCount, uint32_t *aReadCount)
213 {
214 NS_ASSERTION(mUnicharDataLength >= mUnicharDataOffset, "unsigned madness");
215 uint32_t bytesToWrite = mUnicharDataLength - mUnicharDataOffset;
216 nsresult rv = NS_OK;
217 if (0 == bytesToWrite) {
218 // Fill the unichar buffer
219 int32_t bytesRead = Fill(&rv);
220 if (bytesRead <= 0) {
221 *aReadCount = 0;
222 return rv;
223 }
224 bytesToWrite = bytesRead;
225 }
227 if (bytesToWrite > aCount)
228 bytesToWrite = aCount;
230 uint32_t bytesWritten;
231 uint32_t totalBytesWritten = 0;
233 while (bytesToWrite) {
234 rv = aWriter(this, aClosure,
235 mUnicharData.Elements() + mUnicharDataOffset,
236 totalBytesWritten, bytesToWrite, &bytesWritten);
238 if (NS_FAILED(rv)) {
239 // don't propagate errors to the caller
240 break;
241 }
243 bytesToWrite -= bytesWritten;
244 totalBytesWritten += bytesWritten;
245 mUnicharDataOffset += bytesWritten;
246 }
248 *aReadCount = totalBytesWritten;
250 return NS_OK;
251 }
253 NS_IMETHODIMP
254 UTF8InputStream::ReadString(uint32_t aCount, nsAString& aString,
255 uint32_t* aReadCount)
256 {
257 NS_ASSERTION(mUnicharDataLength >= mUnicharDataOffset, "unsigned madness");
258 uint32_t readCount = mUnicharDataLength - mUnicharDataOffset;
259 nsresult errorCode;
260 if (0 == readCount) {
261 // Fill the unichar buffer
262 int32_t bytesRead = Fill(&errorCode);
263 if (bytesRead <= 0) {
264 *aReadCount = 0;
265 return errorCode;
266 }
267 readCount = bytesRead;
268 }
269 if (readCount > aCount) {
270 readCount = aCount;
271 }
272 const char16_t* buf = mUnicharData.Elements() + mUnicharDataOffset;
273 aString.Assign(buf, readCount);
275 mUnicharDataOffset += readCount;
276 *aReadCount = readCount;
277 return NS_OK;
278 }
280 int32_t UTF8InputStream::Fill(nsresult * aErrorCode)
281 {
282 if (nullptr == mInput) {
283 // We already closed the stream!
284 *aErrorCode = NS_BASE_STREAM_CLOSED;
285 return -1;
286 }
288 NS_ASSERTION(mByteData.Length() >= mByteDataOffset, "unsigned madness");
289 uint32_t remainder = mByteData.Length() - mByteDataOffset;
290 mByteDataOffset = remainder;
291 uint32_t nb;
292 *aErrorCode = NS_FillArray(mByteData, mInput, remainder, &nb);
293 if (nb == 0) {
294 // Because we assume a many to one conversion, the lingering data
295 // in the byte buffer must be a partial conversion
296 // fragment. Because we know that we have received no more new
297 // data to add to it, we can't convert it. Therefore, we discard
298 // it.
299 return nb;
300 }
301 NS_ASSERTION(remainder + nb == mByteData.Length(), "bad nb");
303 // Now convert as much of the byte buffer to unicode as possible
304 uint32_t srcLen, dstLen;
305 CountValidUTF8Bytes(mByteData.Elements(),remainder + nb, srcLen, dstLen);
307 // the number of UCS2 characters should always be <= the number of
308 // UTF8 chars
309 NS_ASSERTION( (remainder+nb >= srcLen), "cannot be longer than out buffer");
310 NS_ASSERTION(dstLen <= mUnicharData.Capacity(),
311 "Ouch. I would overflow my buffer if I wasn't so careful.");
312 if (dstLen > mUnicharData.Capacity()) return 0;
314 ConvertUTF8toUTF16 converter(mUnicharData.Elements());
316 nsASingleFragmentCString::const_char_iterator start = mByteData.Elements();
317 nsASingleFragmentCString::const_char_iterator end = mByteData.Elements() + srcLen;
319 copy_string(start, end, converter);
320 if (converter.Length() != dstLen) {
321 *aErrorCode = NS_BASE_STREAM_BAD_CONVERSION;
322 return -1;
323 }
325 mUnicharDataOffset = 0;
326 mUnicharDataLength = dstLen;
327 mByteDataOffset = srcLen;
329 return dstLen;
330 }
332 void
333 UTF8InputStream::CountValidUTF8Bytes(const char* aBuffer, uint32_t aMaxBytes, uint32_t& aValidUTF8bytes, uint32_t& aValidUTF16CodeUnits)
334 {
335 const char *c = aBuffer;
336 const char *end = aBuffer + aMaxBytes;
337 const char *lastchar = c; // pre-initialize in case of 0-length buffer
338 uint32_t utf16length = 0;
339 while (c < end && *c) {
340 lastchar = c;
341 utf16length++;
343 if (UTF8traits::isASCII(*c))
344 c++;
345 else if (UTF8traits::is2byte(*c))
346 c += 2;
347 else if (UTF8traits::is3byte(*c))
348 c += 3;
349 else if (UTF8traits::is4byte(*c)) {
350 c += 4;
351 utf16length++; // add 1 more because this will be converted to a
352 // surrogate pair.
353 }
354 else if (UTF8traits::is5byte(*c))
355 c += 5;
356 else if (UTF8traits::is6byte(*c))
357 c += 6;
358 else {
359 NS_WARNING("Unrecognized UTF8 string in UTF8InputStream::CountValidUTF8Bytes()");
360 break; // Otherwise we go into an infinite loop. But what happens now?
361 }
362 }
363 if (c > end) {
364 c = lastchar;
365 utf16length--;
366 }
368 aValidUTF8bytes = c - aBuffer;
369 aValidUTF16CodeUnits = utf16length;
370 }
372 NS_IMPL_QUERY_INTERFACE(nsSimpleUnicharStreamFactory,
373 nsIFactory,
374 nsISimpleUnicharStreamFactory)
376 NS_IMETHODIMP_(MozExternalRefCountType) nsSimpleUnicharStreamFactory::AddRef() { return 2; }
377 NS_IMETHODIMP_(MozExternalRefCountType) nsSimpleUnicharStreamFactory::Release() { return 1; }
379 NS_IMETHODIMP
380 nsSimpleUnicharStreamFactory::CreateInstance(nsISupports* aOuter, REFNSIID aIID,
381 void **aResult)
382 {
383 return NS_ERROR_NOT_IMPLEMENTED;
384 }
386 NS_IMETHODIMP
387 nsSimpleUnicharStreamFactory::LockFactory(bool aLock)
388 {
389 return NS_OK;
390 }
392 NS_IMETHODIMP
393 nsSimpleUnicharStreamFactory::CreateInstanceFromString(const nsAString& aString,
394 nsIUnicharInputStream* *aResult)
395 {
396 StringUnicharInputStream* it = new StringUnicharInputStream(aString);
397 if (!it) {
398 return NS_ERROR_OUT_OF_MEMORY;
399 }
401 NS_ADDREF(*aResult = it);
402 return NS_OK;
403 }
405 NS_IMETHODIMP
406 nsSimpleUnicharStreamFactory::CreateInstanceFromUTF8Stream(nsIInputStream* aStreamToWrap,
407 nsIUnicharInputStream* *aResult)
408 {
409 *aResult = nullptr;
411 // Create converter input stream
412 nsRefPtr<UTF8InputStream> it = new UTF8InputStream();
413 if (!it)
414 return NS_ERROR_OUT_OF_MEMORY;
416 nsresult rv = it->Init(aStreamToWrap);
417 if (NS_FAILED(rv))
418 return rv;
420 NS_ADDREF(*aResult = it);
421 return NS_OK;
422 }
424 nsSimpleUnicharStreamFactory*
425 nsSimpleUnicharStreamFactory::GetInstance()
426 {
427 static const nsSimpleUnicharStreamFactory kInstance;
428 return const_cast<nsSimpleUnicharStreamFactory*>(&kInstance);
429 }