DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Header

Mercurial (1aeaa33a64f9)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "mozilla/css/StreamLoader.h"

#include "mozilla/IntegerTypeTraits.h"
#include "mozilla/Encoding.h"
#include "nsIChannel.h"
#include "nsIInputStream.h"

using namespace mozilla;

namespace mozilla {
namespace css {

StreamLoader::StreamLoader(mozilla::css::SheetLoadData* aSheetLoadData)
    : mSheetLoadData(aSheetLoadData), mStatus(NS_OK) {
  MOZ_ASSERT(!aSheetLoadData->mSheet->IsGecko());
}

StreamLoader::~StreamLoader() {}

NS_IMPL_ISUPPORTS(StreamLoader, nsIStreamListener)

/* nsIRequestObserver implementation */
NS_IMETHODIMP
StreamLoader::OnStartRequest(nsIRequest* aRequest, nsISupports*) {
  // It's kinda bad to let Web content send a number that results
  // in a potentially large allocation directly, but efficiency of
  // compression bombs is so great that it doesn't make much sense
  // to require a site to send one before going ahead and allocating.
  nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
  if (channel) {
    int64_t length;
    nsresult rv = channel->GetContentLength(&length);
    if (NS_SUCCEEDED(rv) && length > 0) {
      if (length > MaxValue<nsACString::size_type>::value) {
        return (mStatus = NS_ERROR_OUT_OF_MEMORY);
      }
      if (!mBytes.SetCapacity(length, mozilla::fallible_t())) {
        return (mStatus = NS_ERROR_OUT_OF_MEMORY);
      }
    }
  }
  return NS_OK;
}

NS_IMETHODIMP
StreamLoader::OnStopRequest(nsIRequest* aRequest, nsISupports* aContext,
                            nsresult aStatus) {
  // Decoded data
  nsCString utf8String;
  // How many bytes of decoded data to skip (3 when skipping UTF-8 BOM needed,
  // 0 otherwise)
  size_t skip = 0;

  const Encoding* encoding;

  nsresult rv = NS_OK;

  {
    // Hold the nsStringBuffer for the bytes from the stack to ensure release
    // no matter which return branch is taken.
    nsCString bytes(mBytes);
    mBytes.Truncate();

    nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);

    if (NS_FAILED(mStatus)) {
      mSheetLoadData->VerifySheetReadyToParse(mStatus, EmptyCString(), channel);
      return mStatus;
    }

    nsresult rv =
        mSheetLoadData->VerifySheetReadyToParse(aStatus, bytes, channel);
    if (rv != NS_OK_PARSE_SHEET) {
      return rv;
    }

    rv = NS_OK;

    size_t bomLength;
    Tie(encoding, bomLength) = Encoding::ForBOM(bytes);
    if (!encoding) {
      // No BOM
      encoding = mSheetLoadData->DetermineNonBOMEncoding(bytes, channel);

      rv = encoding->DecodeWithoutBOMHandling(bytes, utf8String);
    } else if (encoding == UTF_8_ENCODING) {
      // UTF-8 BOM; handling this manually because mozilla::Encoding
      // can't handle this without copying with C++ types and uses
      // infallible allocation with Rust types (which could avoid
      // the copy).

      // First, chop off the BOM.
      auto tail = Span<const uint8_t>(bytes).From(bomLength);
      size_t upTo = Encoding::UTF8ValidUpTo(tail);
      if (upTo == tail.Length()) {
        // No need to copy
        skip = bomLength;
        utf8String.Assign(bytes);
      } else {
        rv = encoding->DecodeWithoutBOMHandling(tail, utf8String, upTo);
      }
    } else {
      // UTF-16LE or UTF-16BE
      rv = encoding->DecodeWithBOMRemoval(bytes, utf8String);
    }
  }  // run destructor for `bytes`

  if (NS_FAILED(rv)) {
    return rv;
  }

  // For reasons I don't understand, factoring the below lines into
  // a method on SheetLoadData resulted in a linker error. Hence,
  // accessing fields of mSheetLoadData from here.
  mSheetLoadData->mEncoding = encoding;
  bool dummy;
  return mSheetLoadData->mLoader->ParseSheet(
      EmptyString(), Span<const uint8_t>(utf8String).From(skip), mSheetLoadData,
      /* aAllowAsync = */ true, dummy);
}

/* nsIStreamListener implementation */
NS_IMETHODIMP
StreamLoader::OnDataAvailable(nsIRequest*, nsISupports*,
                              nsIInputStream* aInputStream, uint64_t,
                              uint32_t aCount) {
  if (NS_FAILED(mStatus)) {
    return mStatus;
  }
  uint32_t dummy;
  return aInputStream->ReadSegments(WriteSegmentFun, this, aCount, &dummy);
}

nsresult StreamLoader::WriteSegmentFun(nsIInputStream*, void* aClosure,
                                       const char* aSegment, uint32_t,
                                       uint32_t aCount, uint32_t* aWriteCount) {
  StreamLoader* self = static_cast<StreamLoader*>(aClosure);
  if (NS_FAILED(self->mStatus)) {
    return self->mStatus;
  }
  if (!self->mBytes.Append(aSegment, aCount, mozilla::fallible_t())) {
    self->mBytes.Truncate();
    return (self->mStatus = NS_ERROR_OUT_OF_MEMORY);
  }
  *aWriteCount = aCount;
  return NS_OK;
}

}  // namespace css
}  // namespace mozilla