tor-browser

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

ImageDecoder.cpp (39384B)


      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/ImageDecoder.h"
      8 
      9 #include <algorithm>
     10 #include <cstdint>
     11 
     12 #include "ImageContainer.h"
     13 #include "ImageDecoderReadRequest.h"
     14 #include "MediaResult.h"
     15 #include "mozilla/Logging.h"
     16 #include "mozilla/StaticPrefs_dom.h"
     17 #include "mozilla/dom/ImageTrack.h"
     18 #include "mozilla/dom/ImageTrackList.h"
     19 #include "mozilla/dom/Promise.h"
     20 #include "mozilla/dom/ReadableStream.h"
     21 #include "mozilla/dom/VideoFrame.h"
     22 #include "mozilla/dom/VideoFrameBinding.h"
     23 #include "mozilla/dom/WebCodecsUtils.h"
     24 #include "mozilla/image/ImageUtils.h"
     25 #include "mozilla/image/SourceBuffer.h"
     26 #include "nsComponentManagerUtils.h"
     27 #include "nsTHashSet.h"
     28 
     29 extern mozilla::LazyLogModule gWebCodecsLog;
     30 
     31 namespace mozilla::dom {
     32 
     33 class ImageDecoder::ControlMessage {
     34 public:
     35  ControlMessage() = default;
     36  virtual ~ControlMessage() = default;
     37 
     38  virtual ConfigureMessage* AsConfigureMessage() { return nullptr; }
     39  virtual DecodeMetadataMessage* AsDecodeMetadataMessage() { return nullptr; }
     40  virtual DecodeFrameMessage* AsDecodeFrameMessage() { return nullptr; }
     41  virtual SelectTrackMessage* AsSelectTrackMessage() { return nullptr; }
     42 };
     43 
     44 class ImageDecoder::ConfigureMessage final
     45    : public ImageDecoder::ControlMessage {
     46 public:
     47  explicit ConfigureMessage(const Maybe<gfx::IntSize>& aOutputSize,
     48                            ColorSpaceConversion aColorSpaceConversion)
     49      : mOutputSize(aOutputSize),
     50        mColorSpaceConversion(aColorSpaceConversion) {}
     51 
     52  ConfigureMessage* AsConfigureMessage() override { return this; }
     53 
     54  const Maybe<gfx::IntSize> mOutputSize;
     55  const ColorSpaceConversion mColorSpaceConversion;
     56 };
     57 
     58 class ImageDecoder::DecodeMetadataMessage final
     59    : public ImageDecoder::ControlMessage {
     60 public:
     61  DecodeMetadataMessage* AsDecodeMetadataMessage() override { return this; }
     62 };
     63 
     64 class ImageDecoder::DecodeFrameMessage final
     65    : public ImageDecoder::ControlMessage {
     66 public:
     67  DecodeFrameMessage* AsDecodeFrameMessage() override { return this; }
     68 };
     69 
     70 class ImageDecoder::SelectTrackMessage final
     71    : public ImageDecoder::ControlMessage {
     72 public:
     73  explicit SelectTrackMessage(uint32_t aSelectedTrack)
     74      : mSelectedTrack(aSelectedTrack) {}
     75 
     76  SelectTrackMessage* AsSelectTrackMessage() override { return this; }
     77 
     78  const uint32_t mSelectedTrack;
     79 };
     80 
     81 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(ImageDecoder)
     82 
     83 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ImageDecoder)
     84  tmp->Destroy();
     85  NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
     86  NS_IMPL_CYCLE_COLLECTION_UNLINK(mTracks)
     87  NS_IMPL_CYCLE_COLLECTION_UNLINK(mReadRequest)
     88  NS_IMPL_CYCLE_COLLECTION_UNLINK(mCompletePromise)
     89  NS_IMPL_CYCLE_COLLECTION_UNLINK(mOutstandingDecodes)
     90  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
     91 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
     92 
     93 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(ImageDecoder)
     94  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
     95  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTracks)
     96  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReadRequest)
     97  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCompletePromise)
     98  for (uint32_t i = 0; i < tmp->mOutstandingDecodes.Length(); ++i) {
     99    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOutstandingDecodes[i].mPromise);
    100  }
    101 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    102 
    103 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ImageDecoder)
    104  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
    105  NS_INTERFACE_MAP_ENTRY(nsISupports)
    106 NS_INTERFACE_MAP_END
    107 
    108 NS_IMPL_CYCLE_COLLECTING_ADDREF(ImageDecoder)
    109 NS_IMPL_CYCLE_COLLECTING_RELEASE(ImageDecoder)
    110 
    111 ImageDecoder::ImageDecoder(nsCOMPtr<nsIGlobalObject>&& aParent,
    112                           const nsAString& aType)
    113    : mParent(std::move(aParent)),
    114      mType(aType),
    115      mFramesTimestamp(image::FrameTimeout::Zero()) {
    116  MOZ_LOG(gWebCodecsLog, LogLevel::Debug,
    117          ("ImageDecoder %p ImageDecoder", this));
    118 }
    119 
    120 ImageDecoder::~ImageDecoder() {
    121  MOZ_LOG(gWebCodecsLog, LogLevel::Debug,
    122          ("ImageDecoder %p ~ImageDecoder", this));
    123  Destroy();
    124 }
    125 
    126 JSObject* ImageDecoder::WrapObject(JSContext* aCx,
    127                                   JS::Handle<JSObject*> aGivenProto) {
    128  AssertIsOnOwningThread();
    129  return ImageDecoder_Binding::Wrap(aCx, this, aGivenProto);
    130 }
    131 
    132 void ImageDecoder::Destroy() {
    133  MOZ_LOG(gWebCodecsLog, LogLevel::Debug, ("ImageDecoder %p Destroy", this));
    134  MOZ_ASSERT(mOutstandingDecodes.IsEmpty());
    135 
    136  if (mReadRequest) {
    137    mReadRequest->Destroy(/* aCancel */ false);
    138    mReadRequest = nullptr;
    139  }
    140 
    141  if (mDecoder) {
    142    mDecoder->Destroy();
    143  }
    144 
    145  if (mTracks) {
    146    mTracks->Destroy();
    147  }
    148 
    149  if (mShutdownWatcher) {
    150    mShutdownWatcher->Destroy();
    151    mShutdownWatcher = nullptr;
    152  }
    153 
    154  mSourceBuffer = nullptr;
    155  mDecoder = nullptr;
    156  mParent = nullptr;
    157 }
    158 
    159 void ImageDecoder::QueueConfigureMessage(
    160    const Maybe<gfx::IntSize>& aOutputSize,
    161    ColorSpaceConversion aColorSpaceConversion) {
    162  mControlMessageQueue.push(
    163      MakeUnique<ConfigureMessage>(aOutputSize, aColorSpaceConversion));
    164 }
    165 
    166 void ImageDecoder::QueueDecodeMetadataMessage() {
    167  mControlMessageQueue.push(MakeUnique<DecodeMetadataMessage>());
    168 }
    169 
    170 void ImageDecoder::QueueDecodeFrameMessage() {
    171  mControlMessageQueue.push(MakeUnique<DecodeFrameMessage>());
    172 }
    173 
    174 void ImageDecoder::QueueSelectTrackMessage(uint32_t aSelectedIndex) {
    175  mControlMessageQueue.push(MakeUnique<SelectTrackMessage>(aSelectedIndex));
    176 }
    177 
    178 void ImageDecoder::ResumeControlMessageQueue() {
    179  MOZ_ASSERT(mMessageQueueBlocked);
    180  mMessageQueueBlocked = false;
    181  ProcessControlMessageQueue();
    182 }
    183 
    184 void ImageDecoder::ProcessControlMessageQueue() {
    185  while (!mMessageQueueBlocked && !mControlMessageQueue.empty()) {
    186    auto& msg = mControlMessageQueue.front();
    187    auto result = MessageProcessedResult::Processed;
    188    if (auto* submsg = msg->AsConfigureMessage()) {
    189      result = ProcessConfigureMessage(submsg);
    190    } else if (auto* submsg = msg->AsDecodeMetadataMessage()) {
    191      result = ProcessDecodeMetadataMessage(submsg);
    192    } else if (auto* submsg = msg->AsDecodeFrameMessage()) {
    193      result = ProcessDecodeFrameMessage(submsg);
    194    } else if (auto* submsg = msg->AsSelectTrackMessage()) {
    195      result = ProcessSelectTrackMessage(submsg);
    196    } else {
    197      MOZ_ASSERT_UNREACHABLE("Unhandled control message type!");
    198    }
    199 
    200    if (result == MessageProcessedResult::NotProcessed) {
    201      break;
    202    }
    203 
    204    mControlMessageQueue.pop();
    205  }
    206 }
    207 
    208 MessageProcessedResult ImageDecoder::ProcessConfigureMessage(
    209    ConfigureMessage* aMsg) {
    210  // 10.2.2. Running a control message to configure the image decoder means
    211  // running these steps:
    212 
    213  // 1. Let supported be the result of running the Check Type Support algorithm
    214  // with init.type.
    215  //
    216  // 2. If supported is false, run the Close ImageDecoder algorithm with a
    217  // NotSupportedError DOMException and return "processed".
    218  //
    219  // Note that DecoderType::ICON is mostly an internal type that we use for
    220  // system icons and shouldn't be exposed for general use on the web. This is
    221  // not to be confused with DecoderType::ICO which is for .ico files.
    222  NS_ConvertUTF16toUTF8 mimeType(mType);
    223  image::DecoderType type = image::ImageUtils::GetDecoderType(mimeType);
    224  if (NS_WARN_IF(type == image::DecoderType::UNKNOWN) ||
    225      NS_WARN_IF(type == image::DecoderType::ICON)) {
    226    MOZ_LOG(gWebCodecsLog, LogLevel::Error,
    227            ("ImageDecoder %p Initialize -- unsupported mime type '%s'", this,
    228             mimeType.get()));
    229    Close(MediaResult(NS_ERROR_DOM_NOT_SUPPORTED_ERR,
    230                      "Unsupported mime type"_ns));
    231    return MessageProcessedResult::Processed;
    232  }
    233 
    234  image::SurfaceFlags surfaceFlags = image::DefaultSurfaceFlags();
    235  switch (aMsg->mColorSpaceConversion) {
    236    case ColorSpaceConversion::None:
    237      surfaceFlags |= image::SurfaceFlags::NO_COLORSPACE_CONVERSION;
    238      break;
    239    case ColorSpaceConversion::Default:
    240      break;
    241    default:
    242      MOZ_LOG(
    243          gWebCodecsLog, LogLevel::Error,
    244          ("ImageDecoder %p Initialize -- unsupported colorspace conversion",
    245           this));
    246      Close(MediaResult(NS_ERROR_DOM_NOT_SUPPORTED_ERR,
    247                        "Unsupported colorspace conversion"_ns));
    248      return MessageProcessedResult::Processed;
    249  }
    250 
    251  // 3. Otherwise, assign the [[codec implementation]] internal slot with an
    252  // implementation supporting init.type
    253  mDecoder = image::ImageUtils::CreateDecoder(mSourceBuffer, type,
    254                                              aMsg->mOutputSize, surfaceFlags);
    255  if (NS_WARN_IF(!mDecoder)) {
    256    MOZ_LOG(gWebCodecsLog, LogLevel::Error,
    257            ("ImageDecoder %p Initialize -- failed to create platform decoder",
    258             this));
    259    Close(MediaResult(NS_ERROR_DOM_NOT_SUPPORTED_ERR,
    260                      "Failed to create platform decoder"_ns));
    261    return MessageProcessedResult::Processed;
    262  }
    263 
    264  // 4. Assign true to [[message queue blocked]].
    265  mMessageQueueBlocked = true;
    266 
    267  NS_DispatchToCurrentThread(NS_NewCancelableRunnableFunction(
    268      "ImageDecoder::ProcessConfigureMessage", [self = RefPtr{this}] {
    269        // 5. Enqueue the following steps to the [[codec work queue]]:
    270        // 5.1. Configure [[codec implementation]] in accordance with the values
    271        //      given for colorSpaceConversion, desiredWidth, and desiredHeight.
    272        // 5.2. Assign false to [[message queue blocked]].
    273        // 5.3. Queue a task to Process the control message queue.
    274        self->ResumeControlMessageQueue();
    275      }));
    276 
    277  // 6. Return "processed".
    278  return MessageProcessedResult::Processed;
    279 }
    280 
    281 MessageProcessedResult ImageDecoder::ProcessDecodeMetadataMessage(
    282    DecodeMetadataMessage* aMsg) {
    283  // 10.2.2. Running a control message to decode track metadata means running
    284  // these steps:
    285 
    286  if (!mDecoder) {
    287    return MessageProcessedResult::Processed;
    288  }
    289 
    290  // 1. Enqueue the following steps to the [[codec work queue]]:
    291  // 1.1. Run the Establish Tracks algorithm.
    292  mDecoder->DecodeMetadata()->Then(
    293      GetCurrentSerialEventTarget(), __func__,
    294      [self = RefPtr{this}](const image::DecodeMetadataResult& aMetadata) {
    295        self->OnMetadataSuccess(aMetadata);
    296      },
    297      [self = RefPtr{this}](const nsresult& aErr) {
    298        self->OnMetadataFailed(aErr);
    299      });
    300  return MessageProcessedResult::Processed;
    301 }
    302 
    303 MessageProcessedResult ImageDecoder::ProcessDecodeFrameMessage(
    304    DecodeFrameMessage* aMsg) {
    305  // 10.4.2. Running a control message to decode the image means running these
    306  // steps:
    307  //
    308  // 1. Enqueue the following steps to the [[codec work queue]]:
    309  // 1.1. Wait for [[tracks established]] to become true.
    310  //
    311  // 1.2. If options.completeFramesOnly is false and the image is a
    312  //      Progressive Image for which the User Agent supports progressive
    313  //      decoding, run the Decode Progressive Frame algorithm with
    314  //      options.frameIndex and promise.
    315  //
    316  // 1.3. Otherwise, run the Decode Complete Frame algorithm with
    317  //      options.frameIndex and promise.
    318  NS_DispatchToCurrentThread(NS_NewCancelableRunnableFunction(
    319      "ImageDecoder::ProcessDecodeFrameMessage",
    320      [self = RefPtr{this}] { self->CheckOutstandingDecodes(); }));
    321  return MessageProcessedResult::Processed;
    322 }
    323 
    324 MessageProcessedResult ImageDecoder::ProcessSelectTrackMessage(
    325    SelectTrackMessage* aMsg) {
    326  // 10.7.2. Running a control message to update the internal selected track
    327  // index means running these steps:
    328  //
    329  // 1. Enqueue the following steps to [[ImageDecoder]]'s [[codec work queue]]:
    330  // 1.1. Assign selectedIndex to [[internal selected track index]].
    331  // 1.2. Remove all entries from [[progressive frame generations]].
    332  //
    333  // At this time, progressive images and multi-track images are not supported.
    334  return MessageProcessedResult::Processed;
    335 }
    336 
    337 void ImageDecoder::CheckOutstandingDecodes() {
    338  // 10.2.5. Resolve Decode (with promise and result)
    339 
    340  // 1. If [[closed]], abort these steps.
    341  if (mClosed || !mTracks) {
    342    return;
    343  }
    344 
    345  ImageTrack* track = mTracks->GetDefaultTrack();
    346  if (!track) {
    347    return;
    348  }
    349 
    350  const uint32_t decodedFrameCount = track->DecodedFrameCount();
    351  const uint32_t frameCount = track->FrameCount();
    352  const bool frameCountComplete = track->FrameCountComplete();
    353  const bool decodedFramesComplete = track->DecodedFramesComplete();
    354 
    355  AutoTArray<OutstandingDecode, 4> resolved;
    356  AutoTArray<OutstandingDecode, 4> rejectedRange;
    357  AutoTArray<OutstandingDecode, 4> rejectedState;
    358  uint32_t minFrameIndex = UINT32_MAX;
    359 
    360  // 3. Remove promise from [[pending decode promises]].
    361  for (uint32_t i = 0; i < mOutstandingDecodes.Length();) {
    362    auto& decode = mOutstandingDecodes[i];
    363    const auto frameIndex = decode.mFrameIndex;
    364    if (frameIndex < decodedFrameCount) {
    365      MOZ_LOG(gWebCodecsLog, LogLevel::Debug,
    366              ("ImageDecoder %p CheckOutstandingDecodes -- resolved index %u",
    367               this, frameIndex));
    368      resolved.AppendElement(std::move(decode));
    369      mOutstandingDecodes.RemoveElementAt(i);
    370    } else if (frameCountComplete && frameCount <= frameIndex) {
    371      // We have gotten the frame count from the decoder, so we must reject any
    372      // unfulfilled requests that are beyond it with a RangeError.
    373      MOZ_LOG(gWebCodecsLog, LogLevel::Warning,
    374              ("ImageDecoder %p CheckOutstandingDecodes -- rejected index %u "
    375               "out-of-bounds",
    376               this, frameIndex));
    377      rejectedRange.AppendElement(std::move(decode));
    378      mOutstandingDecodes.RemoveElementAt(i);
    379    } else if (frameCountComplete && decodedFramesComplete) {
    380      // We have decoded all of the frames, but we produced fewer than the frame
    381      // count indicated. This means we ran into problems while decoding and
    382      // aborted. We must reject any unfulfilled requests with an
    383      // InvalidStateError.
    384      MOZ_LOG(gWebCodecsLog, LogLevel::Warning,
    385              ("ImageDecoder %p CheckOutstandingDecodes -- rejected index %u "
    386               "decode error",
    387               this, frameIndex));
    388      rejectedState.AppendElement(std::move(decode));
    389      mOutstandingDecodes.RemoveElementAt(i);
    390    } else if (!decodedFramesComplete) {
    391      // We haven't gotten the last frame yet, so we can advance to the next
    392      // one.
    393      MOZ_LOG(gWebCodecsLog, LogLevel::Debug,
    394              ("ImageDecoder %p CheckOutstandingDecodes -- pending index %u",
    395               this, frameIndex));
    396      if (frameCount > frameIndex) {
    397        minFrameIndex = std::min(minFrameIndex, frameIndex);
    398      }
    399      ++i;
    400    } else {
    401      // If none of the above, we have finished decoding all the frames we can,
    402      // but we raced against the frame count completion. Once that finishes, we
    403      // will run again, and we can appropriately fail frame requests as either
    404      // out-of-bounds or decoding failures.
    405      MOZ_ASSERT(!frameCountComplete);
    406    }
    407  }
    408 
    409  if (minFrameIndex < UINT32_MAX) {
    410    RequestDecodeFrames(minFrameIndex + 1 - decodedFrameCount);
    411  }
    412 
    413  // 4. Resolve promise with result.
    414  for (const auto& i : resolved) {
    415    ImageDecodeResult result;
    416    result.mImage = track->GetDecodedFrame(i.mFrameIndex);
    417    // TODO(aosmond): progressive images
    418    result.mComplete = true;
    419    i.mPromise->MaybeResolve(result);
    420  }
    421 
    422  for (const auto& i : rejectedRange) {
    423    i.mPromise->MaybeRejectWithRangeError("No more frames available"_ns);
    424  }
    425 
    426  for (const auto& i : rejectedState) {
    427    i.mPromise->MaybeRejectWithInvalidStateError("Error decoding frame"_ns);
    428  }
    429 }
    430 
    431 /* static */ already_AddRefed<ImageDecoder> ImageDecoder::Constructor(
    432    const GlobalObject& aGlobal, const ImageDecoderInit& aInit,
    433    ErrorResult& aRv) {
    434  // 10.2.2.1. If init is not valid ImageDecoderInit, throw a TypeError.
    435  // 10.3.1. If type is not a valid image MIME type, return false.
    436  const auto mimeType = Substring(aInit.mType, 0, 6);
    437  if (!mimeType.Equals(u"image/"_ns)) {
    438    MOZ_LOG(gWebCodecsLog, LogLevel::Error,
    439            ("ImageDecoder Constructor -- bad mime type"));
    440    aRv.ThrowTypeError("Invalid MIME type, must be 'image'");
    441    return nullptr;
    442  }
    443 
    444  RefPtr<ImageDecoderReadRequest> readRequest;
    445 
    446  if (aInit.mData.IsReadableStream()) {
    447    const auto& stream = aInit.mData.GetAsReadableStream();
    448    // 10.3.2. If data is of type ReadableStream and the ReadableStream is
    449    // disturbed or locked, return false.
    450    if (stream->Disturbed() || stream->Locked()) {
    451      MOZ_LOG(gWebCodecsLog, LogLevel::Error,
    452              ("ImageDecoder Constructor -- bad stream"));
    453      aRv.ThrowTypeError("ReadableStream data is disturbed and/or locked");
    454      return nullptr;
    455    }
    456  } else {
    457    // 10.3.3. If data is of type BufferSource:
    458    bool empty;
    459    if (aInit.mData.IsArrayBufferView()) {
    460      const auto& view = aInit.mData.GetAsArrayBufferView();
    461      empty = view.ProcessData(
    462          [](const Span<uint8_t>& aData, JS::AutoCheckCannotGC&&) {
    463            return aData.IsEmpty();
    464          });
    465    } else if (aInit.mData.IsArrayBuffer()) {
    466      const auto& buffer = aInit.mData.GetAsArrayBuffer();
    467      empty = buffer.ProcessData(
    468          [](const Span<uint8_t>& aData, JS::AutoCheckCannotGC&&) {
    469            return aData.IsEmpty();
    470          });
    471    } else {
    472      MOZ_ASSERT_UNREACHABLE("Unsupported data type!");
    473      aRv.ThrowNotSupportedError("Unsupported data type");
    474      return nullptr;
    475    }
    476 
    477    // 10.3.3.1. If data is [detached], return false.
    478    // 10.3.3.2. If data is empty, return false.
    479    if (empty) {
    480      MOZ_LOG(gWebCodecsLog, LogLevel::Error,
    481              ("ImageDecoder Constructor -- detached/empty BufferSource"));
    482      aRv.ThrowTypeError("BufferSource is detached/empty");
    483      return nullptr;
    484    }
    485  }
    486 
    487  // 10.3.4. If desiredWidth exists and desiredHeight does not exist, return
    488  // false.
    489  // 10.3.5. If desiredHeight exists and desiredWidth does not exist, return
    490  // false.
    491  if (aInit.mDesiredHeight.WasPassed() != aInit.mDesiredWidth.WasPassed()) {
    492    MOZ_LOG(gWebCodecsLog, LogLevel::Error,
    493            ("ImageDecoder Constructor -- both/neither desiredHeight/width "
    494             "needed"));
    495    aRv.ThrowTypeError(
    496        "Both or neither of desiredHeight and desiredWidth must be passed");
    497    return nullptr;
    498  }
    499 
    500  nsTHashSet<const JSObject*> transferSet;
    501  for (const auto& buffer : aInit.mTransfer) {
    502    // 10.2.2.2. If init.transfer contains more than one reference to the same
    503    // ArrayBuffer, then throw a DataCloneError DOMException.
    504    if (transferSet.Contains(buffer.Obj())) {
    505      MOZ_LOG(
    506          gWebCodecsLog, LogLevel::Error,
    507          ("ImageDecoder Constructor -- duplicate transferred ArrayBuffer"));
    508      aRv.ThrowDataCloneError(
    509          "Transfer contains duplicate ArrayBuffer objects");
    510      return nullptr;
    511    }
    512    transferSet.Insert(buffer.Obj());
    513    // 10.2.2.3.1. If [[Detached]] internal slot is true, then throw a
    514    // DataCloneError DOMException.
    515    bool empty = buffer.ProcessData(
    516        [&](const Span<uint8_t>& aData, JS::AutoCheckCannotGC&&) {
    517          return aData.IsEmpty();
    518        });
    519    if (empty) {
    520      MOZ_LOG(gWebCodecsLog, LogLevel::Error,
    521              ("ImageDecoder Constructor -- empty/detached transferred "
    522               "ArrayBuffer"));
    523      aRv.ThrowDataCloneError(
    524          "Transfer contains empty/detached ArrayBuffer objects");
    525      return nullptr;
    526    }
    527  }
    528 
    529  // 10.2.2.4. Let d be a new ImageDecoder object. In the steps below, all
    530  //           mentions of ImageDecoder members apply to d unless stated
    531  //           otherwise.
    532  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
    533  auto imageDecoder = MakeRefPtr<ImageDecoder>(std::move(global), aInit.mType);
    534  imageDecoder->Initialize(aGlobal, aInit, aRv);
    535  if (NS_WARN_IF(aRv.Failed())) {
    536    MOZ_LOG(gWebCodecsLog, LogLevel::Error,
    537            ("ImageDecoder Constructor -- initialize failed"));
    538    return nullptr;
    539  }
    540 
    541  // 10.2.2.19. For each transferable in init.transfer:
    542  // 10.2.2.19.1. Perform DetachArrayBuffer on transferable
    543  for (const auto& buffer : aInit.mTransfer) {
    544    JS::Rooted<JSObject*> obj(aGlobal.Context(), buffer.Obj());
    545    JS::DetachArrayBuffer(aGlobal.Context(), obj);
    546  }
    547 
    548  // 10.2.2.20. return d.
    549  return imageDecoder.forget();
    550 }
    551 
    552 /* static */ already_AddRefed<Promise> ImageDecoder::IsTypeSupported(
    553    const GlobalObject& aGlobal, const nsAString& aType, ErrorResult& aRv) {
    554  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
    555  RefPtr<Promise> promise = Promise::Create(global, aRv);
    556  if (NS_WARN_IF(aRv.Failed())) {
    557    return nullptr;
    558  }
    559 
    560  const auto subType = Substring(aType, 0, 6);
    561  if (!subType.Equals(u"image/"_ns)) {
    562    promise->MaybeRejectWithTypeError("Invalid MIME type, must be 'image'"_ns);
    563    return promise.forget();
    564  }
    565 
    566  NS_ConvertUTF16toUTF8 mimeType(aType);
    567  image::DecoderType type = image::ImageUtils::GetDecoderType(mimeType);
    568  promise->MaybeResolve(type != image::DecoderType::UNKNOWN);
    569  return promise.forget();
    570 }
    571 
    572 void ImageDecoder::Initialize(const GlobalObject& aGlobal,
    573                              const ImageDecoderInit& aInit, ErrorResult& aRv) {
    574  mShutdownWatcher = media::ShutdownWatcher::Create(this);
    575  if (!mShutdownWatcher) {
    576    MOZ_LOG(
    577        gWebCodecsLog, LogLevel::Error,
    578        ("ImageDecoder %p Initialize -- create shutdown watcher failed", this));
    579    aRv.ThrowInvalidStateError("Could not create shutdown watcher");
    580    return;
    581  }
    582 
    583  mCompletePromise = Promise::Create(mParent, aRv);
    584  if (NS_WARN_IF(aRv.Failed())) {
    585    MOZ_LOG(gWebCodecsLog, LogLevel::Error,
    586            ("ImageDecoder %p Initialize -- create promise failed", this));
    587    return;
    588  }
    589 
    590  // 10.2.2.8. Assign [[ImageTrackList]] a new ImageTrackList initialized as
    591  // follows:
    592  // 10.2.2.8.1. Assign a new list to [[track list]].
    593  mTracks = MakeAndAddRef<ImageTrackList>(mParent, this);
    594  mTracks->Initialize(aRv);
    595  if (NS_WARN_IF(aRv.Failed())) {
    596    MOZ_LOG(gWebCodecsLog, LogLevel::Error,
    597            ("ImageDecoder %p Initialize -- create tracks failed", this));
    598    return;
    599  }
    600 
    601  mSourceBuffer = MakeRefPtr<image::SourceBuffer>();
    602 
    603  bool transferOwnership = false;
    604  const auto fnSourceBufferFromSpan = [&](const Span<uint8_t>& aData) {
    605    if (transferOwnership) {
    606      // 10.2.2.18.2.1 Let [[encoded data]] reference bytes in data
    607      //               representing an encoded image.
    608      nsresult rv =
    609          mSourceBuffer->AdoptData(reinterpret_cast<char*>(aData.Elements()),
    610                                   aData.Length(), js_realloc, js_free);
    611      if (NS_WARN_IF(NS_FAILED(rv))) {
    612        MOZ_LOG(gWebCodecsLog, LogLevel::Error,
    613                ("ImageDecoder %p Initialize -- failed to adopt source buffer",
    614                 this));
    615        aRv.ThrowRangeError("Could not allocate for encoded source buffer");
    616        return;
    617      }
    618    } else {
    619      nsresult rv = mSourceBuffer->ExpectLength(aData.Length());
    620      if (NS_WARN_IF(NS_FAILED(rv))) {
    621        MOZ_LOG(gWebCodecsLog, LogLevel::Error,
    622                ("ImageDecoder %p Initialize -- failed to pre-allocate source "
    623                 "buffer",
    624                 this));
    625        aRv.ThrowRangeError("Could not allocate for encoded source buffer");
    626        return;
    627      }
    628 
    629      // 10.2.2.18.3.2. Assign a copy of init.data to [[encoded data]].
    630      rv = mSourceBuffer->Append(
    631          reinterpret_cast<const char*>(aData.Elements()), aData.Length());
    632      if (NS_WARN_IF(NS_FAILED(rv))) {
    633        MOZ_LOG(gWebCodecsLog, LogLevel::Error,
    634                ("ImageDecoder %p Initialize -- failed to append source buffer",
    635                 this));
    636        aRv.ThrowRangeError("Could not allocate for encoded source buffer");
    637        return;
    638      }
    639    }
    640    mSourceBuffer->Complete(NS_OK);
    641 
    642    // 10.2.2.18.4. Assign true to [[complete]].
    643    // 10.2.2.18.5. Resolve [[completed promise]].
    644    OnCompleteSuccess();
    645  };
    646 
    647  if (aInit.mData.IsReadableStream()) {
    648    // 10.2.2.17. If init’s data member is of type ReadableStream:
    649    const auto& stream = aInit.mData.GetAsReadableStream();
    650 
    651    // 10.2.2.17.2. Assign false to [[complete]]
    652    MOZ_ASSERT(!mComplete);
    653 
    654    // 10.2.2.17.5. Let reader be the result of getting a reader for data.
    655    // 10.2.2.17.6. In parallel, perform the Fetch Stream Data Loop on d with
    656    //              reader.
    657    mReadRequest = MakeAndAddRef<ImageDecoderReadRequest>(mSourceBuffer);
    658    if (NS_WARN_IF(!mReadRequest->Initialize(aGlobal, this, stream))) {
    659      MOZ_LOG(
    660          gWebCodecsLog, LogLevel::Error,
    661          ("ImageDecoder %p Initialize -- create read request failed", this));
    662      aRv.ThrowInvalidStateError("Could not create reader for ReadableStream");
    663      return;
    664    }
    665  } else if (aInit.mData.IsArrayBufferView()) {
    666    // 10.2.2.18.3.1. Assert that init.data is of type BufferSource.
    667    const auto& view = aInit.mData.GetAsArrayBufferView();
    668    bool isShared;
    669    JS::Rooted<JSObject*> viewObj(aGlobal.Context(), view.Obj());
    670    JSObject* arrayBuffer =
    671        JS_GetArrayBufferViewBuffer(aGlobal.Context(), viewObj, &isShared);
    672    bool inTransferList = false;
    673    for (const auto& transferBuffer : aInit.mTransfer) {
    674      if (arrayBuffer == transferBuffer.Obj()) {
    675        inTransferList = true;
    676        break;
    677      }
    678    }
    679    size_t length;
    680    if (inTransferList) {
    681      length = JS_GetArrayBufferViewByteLength(view.Obj());
    682      // Only transfer ownership if the view's byte offset is 0
    683      // (Otherewise we would have problems with freeing a pointer
    684      //  to halfway through the ArrayBuffer data)
    685      transferOwnership = JS_GetArrayBufferViewByteOffset(view.Obj()) == 0;
    686    }
    687    if (transferOwnership) {
    688      JS::Rooted<JSObject*> bufferObj(aGlobal.Context(), arrayBuffer);
    689      void* data = JS::StealArrayBufferContents(aGlobal.Context(), bufferObj);
    690      fnSourceBufferFromSpan(Span(static_cast<uint8_t*>(data), length));
    691    } else {
    692      view.ProcessFixedData(fnSourceBufferFromSpan);
    693    }
    694    if (aRv.Failed()) {
    695      return;
    696    }
    697  } else if (aInit.mData.IsArrayBuffer()) {
    698    // 10.2.2.18.3.1. Assert that init.data is of type BufferSource.
    699    const auto& buffer = aInit.mData.GetAsArrayBuffer();
    700    for (const auto& transferBuffer : aInit.mTransfer) {
    701      if (buffer.Obj() == transferBuffer.Obj()) {
    702        transferOwnership = true;
    703        break;
    704      }
    705    }
    706    if (transferOwnership) {
    707      JS::Rooted<JSObject*> bufferObj(aGlobal.Context(), buffer.Obj());
    708      size_t length = JS::GetArrayBufferByteLength(bufferObj);
    709      void* data = JS::StealArrayBufferContents(aGlobal.Context(), bufferObj);
    710      fnSourceBufferFromSpan(Span(static_cast<uint8_t*>(data), length));
    711    } else {
    712      buffer.ProcessFixedData(fnSourceBufferFromSpan);
    713    }
    714    if (aRv.Failed()) {
    715      return;
    716    }
    717  } else {
    718    MOZ_ASSERT_UNREACHABLE("Unsupported data type!");
    719    aRv.ThrowNotSupportedError("Unsupported data type");
    720    return;
    721  }
    722 
    723  Maybe<gfx::IntSize> desiredSize;
    724  if (aInit.mDesiredWidth.WasPassed() && aInit.mDesiredHeight.WasPassed()) {
    725    desiredSize.emplace(
    726        std::min(aInit.mDesiredWidth.Value(), static_cast<uint32_t>(INT32_MAX)),
    727        std::min(aInit.mDesiredHeight.Value(),
    728                 static_cast<uint32_t>(INT32_MAX)));
    729  }
    730 
    731  // 10.2.2.17.3 / 10.2.2.18.6.
    732  //   Queue a control message to configure the image decoder with init.
    733  QueueConfigureMessage(desiredSize, aInit.mColorSpaceConversion);
    734 
    735  // 10.2.10.2.2.18.7. Queue a control message to decode track metadata.
    736  //
    737  // Note that for readable streams it doesn't ever say to decode the metadata,
    738  // but we can reasonably assume it means to decode the metadata in parallel
    739  // with the reading of the stream.
    740  QueueDecodeMetadataMessage();
    741 
    742  // 10.2.2.18.8. Process the control message queue.
    743  ProcessControlMessageQueue();
    744 }
    745 
    746 void ImageDecoder::OnSourceBufferComplete(const MediaResult& aResult) {
    747  MOZ_LOG(gWebCodecsLog, LogLevel::Debug,
    748          ("ImageDecoder %p OnSourceBufferComplete -- success %d", this,
    749           NS_SUCCEEDED(aResult.Code())));
    750 
    751  MOZ_ASSERT(mSourceBuffer->IsComplete());
    752 
    753  if (NS_WARN_IF(NS_FAILED(aResult.Code()))) {
    754    OnCompleteFailed(aResult);
    755    return;
    756  }
    757 
    758  OnCompleteSuccess();
    759 }
    760 
    761 void ImageDecoder::OnCompleteSuccess() {
    762  if (mComplete) {
    763    return;
    764  }
    765 
    766  // There are two conditions we need to fulfill before we are complete:
    767  //
    768  // 10.2.1. Internal Slots - [[complete]]
    769  // A boolean indicating whether [[encoded data]] is completely buffered.
    770  //
    771  // 10.6.1. Internal Slots - [[ready promise]]
    772  // NOTE: ImageTrack frameCount can receive subsequent updates until complete
    773  // is true.
    774  if (!mSourceBuffer->IsComplete() || !mHasFrameCount) {
    775    MOZ_LOG(gWebCodecsLog, LogLevel::Debug,
    776            ("ImageDecoder %p OnCompleteSuccess -- not complete yet; "
    777             "sourceBuffer %d, hasFrameCount %d",
    778             this, mSourceBuffer->IsComplete(), mHasFrameCount));
    779    return;
    780  }
    781 
    782  MOZ_LOG(gWebCodecsLog, LogLevel::Debug,
    783          ("ImageDecoder %p OnCompleteSuccess -- complete", this));
    784  mComplete = true;
    785  mCompletePromise->MaybeResolveWithUndefined();
    786 }
    787 
    788 void ImageDecoder::OnCompleteFailed(const MediaResult& aResult) {
    789  if (mComplete) {
    790    return;
    791  }
    792 
    793  MOZ_LOG(gWebCodecsLog, LogLevel::Error,
    794          ("ImageDecoder %p OnCompleteFailed -- complete", this));
    795  mComplete = true;
    796  aResult.RejectTo(mCompletePromise);
    797 }
    798 
    799 void ImageDecoder::OnMetadataSuccess(
    800    const image::DecodeMetadataResult& aMetadata) {
    801  if (mClosed || !mTracks) {
    802    return;
    803  }
    804 
    805  // 10.2.5. Establish Tracks
    806 
    807  // 1. Assert [[tracks established]] is false.
    808  MOZ_ASSERT(!mTracksEstablished);
    809 
    810  // 2. and 3. See ImageDecoder::OnMetadataFailed.
    811 
    812  MOZ_LOG(gWebCodecsLog, LogLevel::Debug,
    813          ("ImageDecoder %p OnMetadataSuccess -- %dx%d, repetitions %d, "
    814           "animated %d, frameCount %u, frameCountComplete %d",
    815           this, aMetadata.mWidth, aMetadata.mHeight, aMetadata.mRepetitions,
    816           aMetadata.mAnimated, aMetadata.mFrameCount,
    817           aMetadata.mFrameCountComplete));
    818 
    819  // 4. - 9., 11. See ImageTrackList::OnMetadataSuccess
    820  mTracks->OnMetadataSuccess(aMetadata);
    821 
    822  // 10. Assign true to [[tracks established]].
    823  mTracksEstablished = true;
    824 
    825  // If our encoded data comes from a ReadableStream, we may not have reached
    826  // the end of the stream yet. As such, our frame count may be incomplete.
    827  OnFrameCountSuccess(image::DecodeFrameCountResult{
    828      aMetadata.mFrameCount, aMetadata.mFrameCountComplete});
    829 }
    830 
    831 void ImageDecoder::OnMetadataFailed(const nsresult& aErr) {
    832  MOZ_LOG(gWebCodecsLog, LogLevel::Error,
    833          ("ImageDecoder %p OnMetadataFailed 0x%08x", this,
    834           static_cast<uint32_t>(aErr)));
    835 
    836  // 10.2.5. Establish Tracks
    837 
    838  // 1. Assert [[tracks established]] is false.
    839  MOZ_ASSERT(!mTracksEstablished);
    840 
    841  // 2. If [[encoded data]] does not contain enough data to determine the
    842  //    number of tracks:
    843  // 2.1. If complete is true, queue a task to run the Close ImageDecoder
    844  //      algorithm.
    845  // 2.2. Abort these steps.
    846  // 3. If the number of tracks is found to be 0, queue a task to run the Close
    847  //    ImageDecoder algorithm and abort these steps.
    848  Close(MediaResult(NS_ERROR_DOM_ENCODING_NOT_SUPPORTED_ERR,
    849                    "Metadata decoding failed"_ns));
    850 }
    851 
    852 void ImageDecoder::RequestFrameCount(uint32_t aKnownFrameCount) {
    853  MOZ_ASSERT(!mHasFrameCount);
    854 
    855  if (NS_WARN_IF(!mDecoder)) {
    856    return;
    857  }
    858 
    859  MOZ_LOG(gWebCodecsLog, LogLevel::Debug,
    860          ("ImageDecoder %p RequestFrameCount -- knownFrameCount %u", this,
    861           aKnownFrameCount));
    862  mDecoder->DecodeFrameCount(aKnownFrameCount)
    863      ->Then(
    864          GetCurrentSerialEventTarget(), __func__,
    865          [self = RefPtr{this}](const image::DecodeFrameCountResult& aResult) {
    866            self->OnFrameCountSuccess(aResult);
    867          },
    868          [self = RefPtr{this}](const nsresult& aErr) {
    869            self->OnFrameCountFailed(aErr);
    870          });
    871 }
    872 
    873 void ImageDecoder::RequestDecodeFrames(uint32_t aFramesToDecode) {
    874  if (!mDecoder || mHasFramePending) {
    875    return;
    876  }
    877 
    878  mHasFramePending = true;
    879 
    880  MOZ_LOG(gWebCodecsLog, LogLevel::Debug,
    881          ("ImageDecoder %p RequestDecodeFrames -- framesToDecode %u", this,
    882           aFramesToDecode));
    883 
    884  mDecoder->DecodeFrames(aFramesToDecode)
    885      ->Then(
    886          GetCurrentSerialEventTarget(), __func__,
    887          [self = RefPtr{this}](const image::DecodeFramesResult& aResult) {
    888            self->OnDecodeFramesSuccess(aResult);
    889          },
    890          [self = RefPtr{this}](const nsresult& aErr) {
    891            self->OnDecodeFramesFailed(aErr);
    892          });
    893 }
    894 
    895 void ImageDecoder::OnFrameCountSuccess(
    896    const image::DecodeFrameCountResult& aResult) {
    897  if (mClosed || !mTracks) {
    898    return;
    899  }
    900 
    901  MOZ_LOG(gWebCodecsLog, LogLevel::Debug,
    902          ("ImageDecoder %p OnFrameCountSuccess -- frameCount %u, finished %d",
    903           this, aResult.mFrameCount, aResult.mFinished));
    904 
    905  // 10.2.5. Update Tracks.
    906 
    907  // 1. Assert [[tracks established]] is true.
    908  MOZ_ASSERT(mTracksEstablished);
    909 
    910  // 2. - 6. See ImageTrackList::OnFrameCountSuccess.
    911  mTracks->OnFrameCountSuccess(aResult);
    912 
    913  if (aResult.mFinished) {
    914    mHasFrameCount = true;
    915    OnCompleteSuccess();
    916  } else {
    917    RequestFrameCount(aResult.mFrameCount);
    918  }
    919 
    920  CheckOutstandingDecodes();
    921 }
    922 
    923 void ImageDecoder::OnFrameCountFailed(const nsresult& aErr) {
    924  MOZ_LOG(gWebCodecsLog, LogLevel::Error,
    925          ("ImageDecoder %p OnFrameCountFailed", this));
    926  Close(MediaResult(NS_ERROR_DOM_ENCODING_NOT_SUPPORTED_ERR,
    927                    "Frame count decoding failed"_ns));
    928 }
    929 
    930 void ImageDecoder::GetType(nsAString& aType) const { aType.Assign(mType); }
    931 
    932 already_AddRefed<Promise> ImageDecoder::Decode(
    933    const ImageDecodeOptions& aOptions, ErrorResult& aRv) {
    934  // 10.2.4. decode(options)
    935 
    936  // 4. Let promise be a new Promise.
    937  RefPtr<Promise> promise = Promise::Create(mParent, aRv);
    938  if (NS_WARN_IF(aRv.Failed())) {
    939    MOZ_LOG(gWebCodecsLog, LogLevel::Error,
    940            ("ImageDecoder %p Decode -- create promise failed", this));
    941    return nullptr;
    942  }
    943 
    944  // NOTE: Calling decode() on the constructed ImageDecoder will trigger a
    945  // NotSupportedError if the User Agent does not support type. This would have
    946  // been set in Close by ProcessConfigureMessage.
    947  if (mTypeNotSupported) {
    948    MOZ_LOG(gWebCodecsLog, LogLevel::Error,
    949            ("ImageDecoder %p Decode -- not supported", this));
    950    promise->MaybeRejectWithNotSupportedError("Unsupported MIME type"_ns);
    951    return promise.forget();
    952  }
    953 
    954  // 1. If [[closed]] is true, return a Promise rejected with an
    955  //    InvalidStateError DOMException.
    956  if (mClosed || !mTracks || !mDecoder) {
    957    MOZ_LOG(gWebCodecsLog, LogLevel::Error,
    958            ("ImageDecoder %p Decode -- closed", this));
    959    promise->MaybeRejectWithInvalidStateError("Closed decoder"_ns);
    960    return promise.forget();
    961  }
    962 
    963  // 2. If [[ImageTrackList]]'s [[selected index]] is '-1', return a Promise
    964  //    rejected with an InvalidStateError DOMException.
    965  //
    966  // This must be balanced with the fact that we might get a decode call before
    967  // the tracks are established and we are supposed to wait.
    968  ImageTrack* track = mTracks->GetSelectedTrack();
    969  if (mTracksEstablished && !track) {
    970    MOZ_LOG(gWebCodecsLog, LogLevel::Error,
    971            ("ImageDecoder %p Decode -- no track selected", this));
    972    promise->MaybeRejectWithInvalidStateError("No track selected"_ns);
    973    return promise.forget();
    974  }
    975 
    976  // 3. If options is undefined, assign a new ImageDecodeOptions to options.
    977  // 5. Append promise to [[pending decode promises]].
    978  mOutstandingDecodes.AppendElement(OutstandingDecode{
    979      promise, aOptions.mFrameIndex, aOptions.mCompleteFramesOnly});
    980 
    981  // 6. Queue a control message to decode the image with options, and promise.
    982  QueueDecodeFrameMessage();
    983 
    984  // 7. Process the control message queue.
    985  ProcessControlMessageQueue();
    986 
    987  // 8. Return promise.
    988  return promise.forget();
    989 }
    990 
    991 void ImageDecoder::OnDecodeFramesSuccess(
    992    const image::DecodeFramesResult& aResult) {
    993  // 10.2.5. Decode Complete Frame (with frameIndex and promise)
    994  MOZ_ASSERT(mHasFramePending);
    995  mHasFramePending = false;
    996 
    997  // 1. Assert that [[tracks established]] is true.
    998  MOZ_ASSERT(mTracksEstablished);
    999 
   1000  if (mClosed || !mTracks) {
   1001    return;
   1002  }
   1003 
   1004  ImageTrack* track = mTracks->GetDefaultTrack();
   1005  if (NS_WARN_IF(!track)) {
   1006    MOZ_ASSERT_UNREACHABLE("Must have default track!");
   1007    return;
   1008  }
   1009 
   1010  track->OnDecodeFramesSuccess(aResult);
   1011 
   1012  CheckOutstandingDecodes();
   1013 }
   1014 
   1015 void ImageDecoder::OnDecodeFramesFailed(const nsresult& aErr) {
   1016  MOZ_ASSERT(mHasFramePending);
   1017  mHasFramePending = false;
   1018 
   1019  MOZ_LOG(gWebCodecsLog, LogLevel::Error,
   1020          ("ImageDecoder %p OnDecodeFramesFailed", this));
   1021 
   1022  AutoTArray<OutstandingDecode, 1> rejected = std::move(mOutstandingDecodes);
   1023  for (const auto& i : rejected) {
   1024    MOZ_LOG(gWebCodecsLog, LogLevel::Error,
   1025            ("ImageDecoder %p OnDecodeFramesFailed -- reject index %u", this,
   1026             i.mFrameIndex));
   1027    i.mPromise->MaybeRejectWithRangeError("No more frames available"_ns);
   1028  }
   1029 }
   1030 
   1031 void ImageDecoder::Reset(const MediaResult& aResult) {
   1032  MOZ_LOG(gWebCodecsLog, LogLevel::Debug, ("ImageDecoder %p Reset", this));
   1033  // 10.2.5. Reset ImageDecoder (with exception)
   1034 
   1035  // 1. Signal [[codec implementation]] to abort any active decoding operation.
   1036  if (mDecoder) {
   1037    mDecoder->CancelDecodeFrames();
   1038  }
   1039 
   1040  // 2. For each decodePromise in [[pending decode promises]]:
   1041  // 2.1. Reject decodePromise with exception.
   1042  // 2.3. Remove decodePromise from [[pending decode promises]].
   1043  AutoTArray<OutstandingDecode, 1> rejected = std::move(mOutstandingDecodes);
   1044  for (const auto& i : rejected) {
   1045    MOZ_LOG(gWebCodecsLog, LogLevel::Debug,
   1046            ("ImageDecoder %p Reset -- reject index %u", this, i.mFrameIndex));
   1047    aResult.RejectTo(i.mPromise);
   1048  }
   1049 }
   1050 
   1051 void ImageDecoder::Close(const MediaResult& aResult) {
   1052  MOZ_LOG(gWebCodecsLog, LogLevel::Debug, ("ImageDecoder %p Close", this));
   1053 
   1054  // 10.2.5. Algorithms - Close ImageDecoder (with exception)
   1055  mClosed = true;
   1056  mTypeNotSupported = aResult.Code() == NS_ERROR_DOM_NOT_SUPPORTED_ERR;
   1057 
   1058  // 1. Run the Reset ImageDecoder algorithm with exception.
   1059  Reset(aResult);
   1060 
   1061  // 3. Clear [[codec implementation]] and release associated system resources.
   1062  if (mDecoder) {
   1063    mDecoder->Destroy();
   1064  }
   1065 
   1066  if (mReadRequest) {
   1067    mReadRequest->Destroy(/* aCancel */ true);
   1068    mReadRequest = nullptr;
   1069  }
   1070 
   1071  mSourceBuffer = nullptr;
   1072  mDecoder = nullptr;
   1073  mType = u""_ns;
   1074 
   1075  // 4. Remove all entries from [[ImageTrackList]].
   1076  // 5. Assign -1 to [[ImageTrackList]]'s [[selected index]].
   1077  if (mTracks) {
   1078    mTracks->MaybeRejectReady(aResult);
   1079    mTracks->Destroy();
   1080  }
   1081 
   1082  if (!mComplete) {
   1083    aResult.RejectTo(mCompletePromise);
   1084    mComplete = true;
   1085  }
   1086 
   1087  if (mShutdownWatcher) {
   1088    mShutdownWatcher->Destroy();
   1089    mShutdownWatcher = nullptr;
   1090  }
   1091 }
   1092 
   1093 void ImageDecoder::Reset() {
   1094  Reset(MediaResult(NS_ERROR_DOM_ABORT_ERR, "Reset decoder"_ns));
   1095 }
   1096 
   1097 void ImageDecoder::Close() {
   1098  Close(MediaResult(NS_ERROR_DOM_ABORT_ERR, "Closed decoder"_ns));
   1099 }
   1100 
   1101 void ImageDecoder::OnShutdown() {
   1102  Close(MediaResult(NS_ERROR_DOM_ABORT_ERR, "Shutdown"_ns));
   1103 }
   1104 
   1105 }  // namespace mozilla::dom