xpcom/io/nsUnicharInputStream.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

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.

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

mercurial