tor-browser

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

ImageDecoderReadRequest.cpp (9084B)


      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 "ImageDecoderReadRequest.h"
      8 
      9 #include "MediaResult.h"
     10 #include "mozilla/CycleCollectedJSContext.h"
     11 #include "mozilla/Logging.h"
     12 #include "mozilla/dom/ImageDecoder.h"
     13 #include "mozilla/dom/ReadableStream.h"
     14 #include "mozilla/dom/ReadableStreamDefaultReader.h"
     15 #include "mozilla/image/SourceBuffer.h"
     16 
     17 extern mozilla::LazyLogModule gWebCodecsLog;
     18 
     19 namespace mozilla::dom {
     20 
     21 NS_IMPL_CYCLE_COLLECTION_INHERITED(ImageDecoderReadRequest, ReadRequest,
     22                                   mDecoder, mReader)
     23 NS_IMPL_ADDREF_INHERITED(ImageDecoderReadRequest, ReadRequest)
     24 NS_IMPL_RELEASE_INHERITED(ImageDecoderReadRequest, ReadRequest)
     25 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ImageDecoderReadRequest)
     26 NS_INTERFACE_MAP_END_INHERITING(ReadRequest)
     27 
     28 ImageDecoderReadRequest::ImageDecoderReadRequest(
     29    image::SourceBuffer* aSourceBuffer)
     30    : mSourceBuffer(std::move(aSourceBuffer)) {
     31  MOZ_LOG(gWebCodecsLog, LogLevel::Debug,
     32          ("ImageDecoderReadRequest %p ImageDecoderReadRequest", this));
     33 }
     34 
     35 ImageDecoderReadRequest::~ImageDecoderReadRequest() {
     36  MOZ_LOG(gWebCodecsLog, LogLevel::Debug,
     37          ("ImageDecoderReadRequest %p ~ImageDecoderReadRequest", this));
     38 }
     39 
     40 bool ImageDecoderReadRequest::Initialize(const GlobalObject& aGlobal,
     41                                         ImageDecoder* aDecoder,
     42                                         ReadableStream& aStream) {
     43  IgnoredErrorResult rv;
     44  mReader = aStream.GetReader(rv);
     45  if (NS_WARN_IF(rv.Failed())) {
     46    MOZ_LOG(
     47        gWebCodecsLog, LogLevel::Error,
     48        ("ImageDecoderReadRequest %p Initialize -- cannot get stream reader",
     49         this));
     50    mSourceBuffer->Complete(NS_ERROR_FAILURE);
     51    Destroy(/* aCancel */ false);
     52    return false;
     53  }
     54 
     55  mDecoder = aDecoder;
     56  QueueRead();
     57  return true;
     58 }
     59 
     60 void ImageDecoderReadRequest::Destroy(bool aCancel) {
     61  MOZ_LOG(gWebCodecsLog, LogLevel::Debug,
     62          ("ImageDecoderReadRequest %p Destroy", this));
     63 
     64  if (aCancel) {
     65    // Ensure we stop reading from the ReadableStream.
     66    Cancel();
     67  }
     68 
     69  if (mSourceBuffer) {
     70    if (!mSourceBuffer->IsComplete()) {
     71      mSourceBuffer->Complete(NS_ERROR_ABORT);
     72    }
     73    mSourceBuffer = nullptr;
     74  }
     75 
     76  mDecoder = nullptr;
     77  mReader = nullptr;
     78 }
     79 
     80 void ImageDecoderReadRequest::QueueRead() {
     81  class ReadRunnable final : public CancelableRunnable {
     82   public:
     83    explicit ReadRunnable(ImageDecoderReadRequest* aOwner)
     84        : CancelableRunnable(
     85              "mozilla::dom::ImageDecoderReadRequest::QueueRead"),
     86          mOwner(aOwner) {}
     87 
     88    NS_IMETHODIMP Run() override {
     89      mOwner->Read();
     90      mOwner = nullptr;
     91      return NS_OK;
     92    }
     93 
     94    nsresult Cancel() override {
     95      mOwner->Complete(
     96          MediaResult(NS_ERROR_DOM_MEDIA_ABORT_ERR, "Read cancelled"_ns));
     97      mOwner = nullptr;
     98      return NS_OK;
     99    }
    100 
    101   private:
    102    virtual ~ReadRunnable() {
    103      if (mOwner) {
    104        Cancel();
    105      }
    106    }
    107 
    108    RefPtr<ImageDecoderReadRequest> mOwner;
    109  };
    110 
    111  if (!mReader) {
    112    MOZ_LOG(gWebCodecsLog, LogLevel::Debug,
    113            ("ImageDecoderReadRequest %p QueueRead -- destroyed", this));
    114    return;
    115  }
    116 
    117  MOZ_LOG(gWebCodecsLog, LogLevel::Debug,
    118          ("ImageDecoderReadRequest %p QueueRead -- queue", this));
    119  auto task = MakeRefPtr<ReadRunnable>(this);
    120  NS_DispatchToCurrentThread(task.forget());
    121 }
    122 
    123 void ImageDecoderReadRequest::Read() {
    124  if (!mReader || !mDecoder) {
    125    MOZ_LOG(gWebCodecsLog, LogLevel::Debug,
    126            ("ImageDecoderReadRequest %p Read -- destroyed", this));
    127    return;
    128  }
    129 
    130  AutoJSAPI jsapi;
    131  if (!jsapi.Init(mDecoder->GetParentObject())) {
    132    MOZ_LOG(gWebCodecsLog, LogLevel::Debug,
    133            ("ImageDecoderReadRequest %p Read -- no jsapi", this));
    134    Complete(MediaResult(NS_ERROR_DOM_FILE_NOT_READABLE_ERR,
    135                         "Reader cannot init jsapi"_ns));
    136    return;
    137  }
    138 
    139  RefPtr<ImageDecoderReadRequest> self(this);
    140  RefPtr<ReadableStreamDefaultReader> reader(mReader);
    141 
    142  MOZ_LOG(gWebCodecsLog, LogLevel::Debug,
    143          ("ImageDecoderReadRequest %p Read -- begin read chunk", this));
    144 
    145  IgnoredErrorResult err;
    146  reader->ReadChunk(jsapi.cx(), *self, err);
    147  if (NS_WARN_IF(err.Failed())) {
    148    MOZ_LOG(gWebCodecsLog, LogLevel::Error,
    149            ("ImageDecoderReadRequest %p Read -- read chunk failed", this));
    150    Complete(MediaResult(NS_ERROR_DOM_FILE_NOT_READABLE_ERR,
    151                         "Reader cannot read chunk from stream"_ns));
    152  }
    153 
    154  MOZ_LOG(gWebCodecsLog, LogLevel::Debug,
    155          ("ImageDecoderReadRequest %p Read -- end read chunk", this));
    156 }
    157 
    158 void ImageDecoderReadRequest::Cancel() {
    159  RefPtr<ReadableStreamDefaultReader> reader = std::move(mReader);
    160  if (!reader || !mDecoder) {
    161    return;
    162  }
    163 
    164  RefPtr<ImageDecoderReadRequest> self(this);
    165 
    166  AutoJSAPI jsapi;
    167  if (!jsapi.Init(mDecoder->GetParentObject())) {
    168    MOZ_LOG(gWebCodecsLog, LogLevel::Debug,
    169            ("ImageDecoderReadRequest %p Cancel -- no jsapi", this));
    170    return;
    171  }
    172 
    173  ErrorResult rv;
    174  rv.ThrowAbortError("ImageDecoderReadRequest destroyed");
    175 
    176  JS::Rooted<JS::Value> errorValue(jsapi.cx());
    177  if (ToJSValue(jsapi.cx(), std::move(rv), &errorValue)) {
    178    IgnoredErrorResult ignoredRv;
    179    if (RefPtr<Promise> p = reader->Cancel(jsapi.cx(), errorValue, ignoredRv)) {
    180      MOZ_ALWAYS_TRUE(p->SetAnyPromiseIsHandled());
    181    }
    182  }
    183 
    184  jsapi.ClearException();
    185 }
    186 
    187 void ImageDecoderReadRequest::Complete(const MediaResult& aResult) {
    188  if (!mReader) {
    189    return;
    190  }
    191 
    192  MOZ_LOG(gWebCodecsLog, LogLevel::Debug,
    193          ("ImageDecoderReadRequest %p Read -- complete, success %d", this,
    194           NS_SUCCEEDED(aResult.Code())));
    195 
    196  if (mSourceBuffer && !mSourceBuffer->IsComplete()) {
    197    mSourceBuffer->Complete(aResult.Code());
    198  }
    199 
    200  if (mDecoder) {
    201    mDecoder->OnSourceBufferComplete(aResult);
    202  }
    203 
    204  Destroy(/* aCancel */ false);
    205 }
    206 
    207 void ImageDecoderReadRequest::ChunkSteps(JSContext* aCx,
    208                                         JS::Handle<JS::Value> aChunk,
    209                                         ErrorResult& aRv) {
    210  // 10.2.5. Fetch Stream Data Loop (with reader) - chunk steps
    211 
    212  // 1. If [[closed]] is true, abort these steps.
    213  if (!mSourceBuffer) {
    214    return;
    215  }
    216 
    217  // 2. If chunk is not a Uint8Array object, queue a task to run the Close
    218  // ImageDecoder algorithm with a DataError DOMException and abort these steps.
    219  RootedSpiderMonkeyInterface<Uint8Array> chunk(aCx);
    220  if (!aChunk.isObject() || !chunk.Init(&aChunk.toObject())) {
    221    MOZ_LOG(gWebCodecsLog, LogLevel::Error,
    222            ("ImageDecoderReadRequest %p ChunkSteps -- bad chunk", this));
    223    Complete(MediaResult(NS_ERROR_DOM_DATA_ERR,
    224                         "Reader cannot read chunk from stream"_ns));
    225    return;
    226  }
    227 
    228  chunk.ProcessFixedData([&](const Span<uint8_t>& aData) {
    229    MOZ_LOG(gWebCodecsLog, LogLevel::Debug,
    230            ("ImageDecoderReadRequest %p ChunkSteps -- write %zu bytes", this,
    231             aData.Length()));
    232 
    233    // 3. Let bytes be the byte sequence represented by the Uint8Array object.
    234    // 4. Append bytes to the [[encoded data]] internal slot.
    235    nsresult rv = mSourceBuffer->Append(
    236        reinterpret_cast<const char*>(aData.Elements()), aData.Length());
    237    if (NS_WARN_IF(NS_FAILED(rv))) {
    238      MOZ_LOG(
    239          gWebCodecsLog, LogLevel::Debug,
    240          ("ImageDecoderReadRequest %p ChunkSteps -- failed to append", this));
    241      Complete(MediaResult(NS_ERROR_DOM_UNKNOWN_ERR,
    242                           "Reader cannot allocate storage for chunk"_ns));
    243    }
    244 
    245    // 5. If [[tracks established]] is false, run the Establish Tracks
    246    //    algorithm.
    247    // 6. Otherwise, run the Update Tracks algorithm.
    248    //
    249    // Note that these steps will be triggered by the decoder promise callbacks.
    250  });
    251 
    252  // 7. Run the Fetch Stream Data Loop algorithm with reader.
    253  QueueRead();
    254 }
    255 
    256 void ImageDecoderReadRequest::CloseSteps(JSContext* aCx, ErrorResult& aRv) {
    257  MOZ_LOG(gWebCodecsLog, LogLevel::Debug,
    258          ("ImageDecoderReadRequest %p CloseSteps", this));
    259 
    260  // 10.2.5. Fetch Stream Data Loop (with reader) - close steps
    261  // 1. Assign true to [[complete]]
    262  // 2. Resolve [[completed promise]].
    263  Complete(MediaResult(NS_OK));
    264 }
    265 
    266 void ImageDecoderReadRequest::ErrorSteps(JSContext* aCx,
    267                                         JS::Handle<JS::Value> aError,
    268                                         ErrorResult& aRv) {
    269  MOZ_LOG(gWebCodecsLog, LogLevel::Debug,
    270          ("ImageDecoderReadRequest %p ErrorSteps", this));
    271 
    272  // 10.2.5. Fetch Stream Data Loop (with reader) - error steps
    273  // 1. Queue a task to run the Close ImageDecoder algorithm with a
    274  //    NotReadableError DOMException
    275  Complete(MediaResult(NS_ERROR_DOM_FILE_NOT_READABLE_ERR,
    276                       "Reader failed while waiting for chunk from stream"_ns));
    277 }
    278 
    279 }  // namespace mozilla::dom