tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

TextDecoderStream.cpp (7393B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "mozilla/dom/TextDecoderStream.h"
      8 
      9 #include "mozilla/Encoding.h"
     10 #include "mozilla/dom/BufferSourceBinding.h"
     11 #include "mozilla/dom/Promise.h"
     12 #include "mozilla/dom/TextDecoderStreamBinding.h"
     13 #include "mozilla/dom/TransformStream.h"
     14 #include "mozilla/dom/TransformerCallbackHelpers.h"
     15 #include "mozilla/dom/UnionTypes.h"
     16 #include "nsContentUtils.h"
     17 #include "nsIGlobalObject.h"
     18 
     19 namespace mozilla::dom {
     20 
     21 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(TextDecoderStream, mGlobal, mStream)
     22 NS_IMPL_CYCLE_COLLECTING_ADDREF(TextDecoderStream)
     23 NS_IMPL_CYCLE_COLLECTING_RELEASE(TextDecoderStream)
     24 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TextDecoderStream)
     25  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
     26  NS_INTERFACE_MAP_ENTRY(nsISupports)
     27 NS_INTERFACE_MAP_END
     28 
     29 TextDecoderStream::TextDecoderStream(nsISupports* aGlobal,
     30                                     const Encoding& aEncoding, bool aFatal,
     31                                     bool aIgnoreBOM, TransformStream& aStream)
     32    : mGlobal(aGlobal), mStream(&aStream) {
     33  mFatal = aFatal;
     34  mIgnoreBOM = aIgnoreBOM;
     35  aEncoding.Name(mEncoding);
     36  if (aIgnoreBOM) {
     37    mDecoder = aEncoding.NewDecoderWithoutBOMHandling();
     38  } else {
     39    mDecoder = aEncoding.NewDecoderWithBOMRemoval();
     40  }
     41 }
     42 
     43 TextDecoderStream::~TextDecoderStream() = default;
     44 
     45 JSObject* TextDecoderStream::WrapObject(JSContext* aCx,
     46                                        JS::Handle<JSObject*> aGivenProto) {
     47  return TextDecoderStream_Binding::Wrap(aCx, this, aGivenProto);
     48 }
     49 
     50 class TextDecoderStreamAlgorithms : public TransformerAlgorithmsWrapper {
     51  NS_DECL_ISUPPORTS_INHERITED
     52  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(TextDecoderStreamAlgorithms,
     53                                           TransformerAlgorithmsBase)
     54 
     55  void SetDecoderStream(TextDecoderStream& aStream) {
     56    mDecoderStream = &aStream;
     57  }
     58 
     59  // The common part of decode-and-enqueue and flush-and-enqueue.
     60  // Note that the most of the decoding algorithm is implemented in
     61  // mozilla::Decoder, and this is mainly about calling it properly.
     62  // https://encoding.spec.whatwg.org/#decode-and-enqueue-a-chunk
     63  // TODO: This does not allow shared array buffers, just as the non-stream
     64  // TextDecoder/Encoder don't. (Bug 1561594)
     65  MOZ_CAN_RUN_SCRIPT void DecodeBufferSourceAndEnqueue(
     66      JSContext* aCx, OwningBufferSource* aInput, bool aFlush,
     67      TransformStreamDefaultController& aController, ErrorResult& aRv) {
     68    nsString outDecodedString;
     69    if (aInput) {
     70      ProcessTypedArrays(*aInput, [&](const Span<const uint8_t>& aData,
     71                                      JS::AutoCheckCannotGC&&) {
     72        mDecoderStream->DecodeNative(aData, !aFlush, outDecodedString, aRv);
     73      });
     74    } else {
     75      mDecoderStream->DecodeNative(Span<const uint8_t>(), !aFlush,
     76                                   outDecodedString, aRv);
     77    }
     78 
     79    if (aRv.Failed()) {
     80      return;
     81    }
     82 
     83    if (outDecodedString.Length()) {
     84      // Step 4.2. If outputChunk is non-empty, then enqueue outputChunk in
     85      // decoder’s transform.
     86      JS::Rooted<JS::Value> outputChunk(aCx);
     87      if (!ToJSValue(aCx, outDecodedString, &outputChunk)) {
     88        JS_ClearPendingException(aCx);
     89        aRv.Throw(NS_ERROR_UNEXPECTED);
     90        return;
     91      }
     92      aController.Enqueue(aCx, outputChunk, aRv);
     93    }
     94  }
     95 
     96  // https://encoding.spec.whatwg.org/#dom-textdecoderstream
     97  MOZ_CAN_RUN_SCRIPT void TransformCallbackImpl(
     98      JS::Handle<JS::Value> aChunk,
     99      TransformStreamDefaultController& aController,
    100      ErrorResult& aRv) override {
    101    // Step 7. Let transformAlgorithm be an algorithm which takes a chunk
    102    // argument and runs the decode and enqueue a chunk algorithm with this and
    103    // chunk.
    104 
    105    // https://encoding.spec.whatwg.org/#decode-and-enqueue-a-chunk
    106 
    107    AutoJSAPI jsapi;
    108    if (!jsapi.Init(aController.GetParentObject())) {
    109      aRv.ThrowUnknownError("Internal error");
    110      return;
    111    }
    112    JSContext* cx = jsapi.cx();
    113 
    114    // Step 1. Let bufferSource be the result of converting chunk to an
    115    // [AllowShared] BufferSource.
    116    RootedUnion<OwningBufferSource> bufferSource(cx);
    117    if (!bufferSource.Init(cx, aChunk)) {
    118      aRv.MightThrowJSException();
    119      aRv.StealExceptionFromJSContext(cx);
    120      return;
    121    }
    122 
    123    DecodeBufferSourceAndEnqueue(cx, &bufferSource, false, aController, aRv);
    124  }
    125 
    126  // https://encoding.spec.whatwg.org/#dom-textdecoderstream
    127  MOZ_CAN_RUN_SCRIPT void FlushCallbackImpl(
    128      TransformStreamDefaultController& aController,
    129      ErrorResult& aRv) override {
    130    // Step 8. Let flushAlgorithm be an algorithm which takes no arguments and
    131    // runs the flush and enqueue algorithm with this.
    132 
    133    AutoJSAPI jsapi;
    134    if (!jsapi.Init(aController.GetParentObject())) {
    135      aRv.ThrowUnknownError("Internal error");
    136      return;
    137    }
    138    JSContext* cx = jsapi.cx();
    139 
    140    // https://encoding.spec.whatwg.org/#flush-and-enqueue
    141    // (The flush and enqueue algorithm is basically a subset of decode and
    142    // enqueue one, so let's reuse it)
    143    DecodeBufferSourceAndEnqueue(cx, nullptr, true, aController, aRv);
    144  }
    145 
    146 private:
    147  ~TextDecoderStreamAlgorithms() override = default;
    148 
    149  RefPtr<TextDecoderStream> mDecoderStream;
    150 };
    151 
    152 NS_IMPL_CYCLE_COLLECTION_INHERITED(TextDecoderStreamAlgorithms,
    153                                   TransformerAlgorithmsBase, mDecoderStream)
    154 NS_IMPL_ADDREF_INHERITED(TextDecoderStreamAlgorithms, TransformerAlgorithmsBase)
    155 NS_IMPL_RELEASE_INHERITED(TextDecoderStreamAlgorithms,
    156                          TransformerAlgorithmsBase)
    157 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TextDecoderStreamAlgorithms)
    158 NS_INTERFACE_MAP_END_INHERITING(TransformerAlgorithmsBase)
    159 
    160 // https://encoding.spec.whatwg.org/#dom-textdecoderstream
    161 already_AddRefed<TextDecoderStream> TextDecoderStream::Constructor(
    162    const GlobalObject& aGlobal, const nsAString& aLabel,
    163    const TextDecoderOptions& aOptions, ErrorResult& aRv) {
    164  // Step 1. Let encoding be the result of getting an encoding from label.
    165  const Encoding* encoding = Encoding::ForLabelNoReplacement(aLabel);
    166 
    167  // Step 2. If encoding is failure or replacement, then throw a RangeError
    168  if (!encoding) {
    169    NS_ConvertUTF16toUTF8 label(aLabel);
    170    label.Trim(" \t\n\f\r");
    171    aRv.ThrowRangeError<MSG_ENCODING_NOT_SUPPORTED>(label);
    172    return nullptr;
    173  }
    174 
    175  // Step 3-6. (Done in the constructor)
    176 
    177  // Step 7-8.
    178  auto algorithms = MakeRefPtr<TextDecoderStreamAlgorithms>();
    179 
    180  // Step 9-10.
    181  RefPtr<TransformStream> transformStream =
    182      TransformStream::CreateGeneric(aGlobal, *algorithms, aRv);
    183  if (aRv.Failed()) {
    184    return nullptr;
    185  }
    186 
    187  // Step 11. (Done in the constructor)
    188  auto decoderStream = MakeRefPtr<TextDecoderStream>(
    189      aGlobal.GetAsSupports(), *encoding, aOptions.mFatal, aOptions.mIgnoreBOM,
    190      *transformStream);
    191  algorithms->SetDecoderStream(*decoderStream);
    192  return decoderStream.forget();
    193 }
    194 
    195 ReadableStream* TextDecoderStream::Readable() const {
    196  return mStream->Readable();
    197 }
    198 
    199 WritableStream* TextDecoderStream::Writable() const {
    200  return mStream->Writable();
    201 }
    202 
    203 }  // namespace mozilla::dom