tor-browser

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

ImageUtils.cpp (19585B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #include "mozilla/image/ImageUtils.h"
      7 #include "DecodePool.h"
      8 #include "Decoder.h"
      9 #include "DecoderFactory.h"
     10 #include "IDecodingTask.h"
     11 #include "mozilla/AppShutdown.h"
     12 #include "mozilla/gfx/2D.h"
     13 #include "mozilla/Logging.h"
     14 #include "nsNetUtil.h"
     15 #include "nsStreamUtils.h"
     16 
     17 namespace mozilla::image {
     18 
     19 static LazyLogModule sLog("ImageUtils");
     20 
     21 AnonymousDecoder::AnonymousDecoder() = default;
     22 
     23 AnonymousDecoder::~AnonymousDecoder() = default;
     24 
     25 class AnonymousDecoderTask : public IDecodingTask {
     26 public:
     27  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AnonymousDecoderTask, final)
     28 
     29  AnonymousDecoderTask(RefPtr<Decoder>&& aDecoder,
     30                       ThreadSafeWeakPtr<AnonymousDecoder>&& aOwner)
     31      : mDecoder(std::move(aDecoder)), mOwner(std::move(aOwner)) {}
     32 
     33  bool ShouldPreferSyncRun() const final { return false; }
     34 
     35  TaskPriority Priority() const final { return TaskPriority::eLow; }
     36 
     37  bool IsValid() const {
     38    return !AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMShutdownFinal) &&
     39           !mOwner.IsDead();
     40  }
     41 
     42  bool MaybeStart() {
     43    if (!IsValid()) {
     44      return false;
     45    }
     46 
     47    MOZ_LOG(sLog, LogLevel::Debug,
     48            ("[%p] AnonymousDecoderTask::Start -- queue", this));
     49    DecodePool::Singleton()->AsyncRun(this);
     50    return true;
     51  }
     52 
     53  void Resume() final {
     54    if (!IsValid()) {
     55      return;
     56    }
     57 
     58    MOZ_LOG(sLog, LogLevel::Debug,
     59            ("[%p] AnonymousDecoderTask::Resume -- queue", this));
     60    DecodePool::Singleton()->AsyncRun(this);
     61  }
     62 
     63  void Run() final {
     64    bool resume = true;
     65    while (!mOwner.IsDead() && resume) {
     66      LexerResult result = mDecoder->Decode(WrapNotNull(this));
     67      if (result == LexerResult(Yield::NEED_MORE_DATA)) {
     68        MOZ_LOG(sLog, LogLevel::Debug,
     69                ("[%p] AnonymousDecoderTask::Run -- need more data", this));
     70        MOZ_ASSERT(result == LexerResult(Yield::NEED_MORE_DATA));
     71        OnNeedMoreData();
     72        return;
     73      }
     74 
     75      // Check if we have a new frame to process.
     76      RefPtr<imgFrame> frame = mDecoder->GetCurrentFrame();
     77      if (frame) {
     78        RefPtr<gfx::SourceSurface> surface = frame->GetSourceSurface();
     79        if (surface) {
     80          MOZ_LOG(sLog, LogLevel::Debug,
     81                  ("[%p] AnonymousDecoderTask::Run -- new frame %p", this,
     82                   frame.get()));
     83          resume = OnFrameAvailable(std::move(frame), std::move(surface));
     84        } else {
     85          MOZ_ASSERT_UNREACHABLE("No surface from frame?");
     86        }
     87      }
     88 
     89      if (result.is<TerminalState>()) {
     90        MOZ_LOG(sLog, LogLevel::Debug,
     91                ("[%p] AnonymousDecoderTask::Run -- complete", this));
     92        OnComplete(result == LexerResult(TerminalState::SUCCESS));
     93        break;
     94      }
     95 
     96      MOZ_ASSERT(result == LexerResult(Yield::OUTPUT_AVAILABLE));
     97    }
     98  }
     99 
    100 protected:
    101  virtual ~AnonymousDecoderTask() = default;
    102 
    103  virtual void OnNeedMoreData() {}
    104 
    105  // Returns true if the caller should continue decoding more frames if
    106  // possible.
    107  virtual bool OnFrameAvailable(RefPtr<imgFrame>&& aFrame,
    108                                RefPtr<gfx::SourceSurface>&& aSurface) {
    109    MOZ_ASSERT_UNREACHABLE("Unhandled frame!");
    110    return true;
    111  }
    112 
    113  virtual void OnComplete(bool aSuccess) = 0;
    114 
    115  RefPtr<Decoder> mDecoder;
    116  ThreadSafeWeakPtr<AnonymousDecoder> mOwner;
    117 };
    118 
    119 class AnonymousMetadataDecoderTask final : public AnonymousDecoderTask {
    120 public:
    121  AnonymousMetadataDecoderTask(RefPtr<Decoder>&& aDecoder,
    122                               ThreadSafeWeakPtr<AnonymousDecoder>&& aOwner)
    123      : AnonymousDecoderTask(std::move(aDecoder), std::move(aOwner)) {}
    124 
    125 protected:
    126  void OnComplete(bool aSuccess) override {
    127    RefPtr<AnonymousDecoder> owner(mOwner);
    128    if (!owner) {
    129      return;
    130    }
    131 
    132    if (!aSuccess) {
    133      owner->OnMetadata(nullptr);
    134      return;
    135    }
    136 
    137    const auto& mdIn = mDecoder->GetImageMetadata();
    138    owner->OnMetadata(&mdIn);
    139  }
    140 };
    141 
    142 class AnonymousFrameCountDecoderTask final : public AnonymousDecoderTask {
    143 public:
    144  AnonymousFrameCountDecoderTask(RefPtr<Decoder>&& aDecoder,
    145                                 ThreadSafeWeakPtr<AnonymousDecoder>&& aOwner)
    146      : AnonymousDecoderTask(std::move(aDecoder), std::move(aOwner)) {}
    147 
    148 protected:
    149  void UpdateFrameCount(bool aComplete) {
    150    RefPtr<AnonymousDecoder> owner(mOwner);
    151    if (!owner) {
    152      return;
    153    }
    154 
    155    const auto& mdIn = mDecoder->GetImageMetadata();
    156    uint32_t frameCount = mdIn.HasFrameCount() ? mdIn.GetFrameCount() : 0;
    157    owner->OnFrameCount(frameCount, aComplete);
    158  }
    159 
    160  void OnNeedMoreData() override { UpdateFrameCount(/* aComplete */ false); }
    161 
    162  void OnComplete(bool aSuccess) override {
    163    UpdateFrameCount(/* aComplete */ true);
    164  }
    165 };
    166 
    167 class AnonymousFramesDecoderTask final : public AnonymousDecoderTask {
    168 public:
    169  AnonymousFramesDecoderTask(RefPtr<Decoder>&& aDecoder,
    170                             ThreadSafeWeakPtr<AnonymousDecoder>&& aOwner)
    171      : AnonymousDecoderTask(std::move(aDecoder), std::move(aOwner)) {}
    172 
    173  void SetOutputSize(const OrientedIntSize& aSize) {
    174    if (mDecoder) {
    175      mDecoder->SetOutputSize(aSize);
    176    }
    177  }
    178 
    179 protected:
    180  bool OnFrameAvailable(RefPtr<imgFrame>&& aFrame,
    181                        RefPtr<gfx::SourceSurface>&& aSurface) override {
    182    RefPtr<AnonymousDecoder> owner(mOwner);
    183    if (!owner) {
    184      return false;
    185    }
    186 
    187    return owner->OnFrameAvailable(std::move(aFrame), std::move(aSurface));
    188  }
    189 
    190  void OnComplete(bool aSuccess) override {
    191    RefPtr<AnonymousDecoder> owner(mOwner);
    192    if (!owner) {
    193      return;
    194    }
    195 
    196    owner->OnFramesComplete();
    197  }
    198 };
    199 
    200 class AnonymousDecoderImpl final : public AnonymousDecoder {
    201 public:
    202  explicit AnonymousDecoderImpl(const Maybe<gfx::IntSize>& aOutputSize)
    203      : mMutex("mozilla::image::AnonymousDecoderImpl::mMutex"),
    204        mOutputSize(aOutputSize) {}
    205 
    206  ~AnonymousDecoderImpl() override { Destroy(); }
    207 
    208 #ifdef MOZ_REFCOUNTED_LEAK_CHECKING
    209  const char* typeName() const override {
    210    return "mozilla::image::AnonymousDecoderImpl";
    211  }
    212 
    213  size_t typeSize() const override { return sizeof(*this); }
    214 #endif
    215 
    216  bool Initialize(RefPtr<Decoder>&& aDecoder) override {
    217    MutexAutoLock lock(mMutex);
    218 
    219    if (NS_WARN_IF(!aDecoder)) {
    220      MOZ_LOG(sLog, LogLevel::Error,
    221              ("[%p] AnonymousDecoderImpl::Initialize -- bad decoder", this));
    222      return false;
    223    }
    224 
    225    RefPtr<Decoder> metadataDecoder =
    226        DecoderFactory::CloneAnonymousMetadataDecoder(aDecoder);
    227    if (NS_WARN_IF(!metadataDecoder)) {
    228      MOZ_LOG(sLog, LogLevel::Error,
    229              ("[%p] AnonymousDecoderImpl::Initialize -- failed clone metadata "
    230               "decoder",
    231               this));
    232      return false;
    233    }
    234 
    235    DecoderFlags flags =
    236        aDecoder->GetDecoderFlags() | DecoderFlags::COUNT_FRAMES;
    237    RefPtr<Decoder> frameCountDecoder =
    238        DecoderFactory::CloneAnonymousMetadataDecoder(aDecoder, Some(flags));
    239    if (NS_WARN_IF(!frameCountDecoder)) {
    240      MOZ_LOG(sLog, LogLevel::Error,
    241              ("[%p] AnonymousDecoderImpl::Initialize -- failed clone frame "
    242               "count decoder",
    243               this));
    244      return false;
    245    }
    246 
    247    mMetadataTask = new AnonymousMetadataDecoderTask(
    248        std::move(metadataDecoder), ThreadSafeWeakPtr<AnonymousDecoder>(this));
    249    mFrameCountTask = new AnonymousFrameCountDecoderTask(
    250        std::move(frameCountDecoder),
    251        ThreadSafeWeakPtr<AnonymousDecoder>(this));
    252    mFramesTask = new AnonymousFramesDecoderTask(
    253        std::move(aDecoder), ThreadSafeWeakPtr<AnonymousDecoder>(this));
    254 
    255    MOZ_LOG(sLog, LogLevel::Debug,
    256            ("[%p] AnonymousDecoderImpl::Initialize -- success", this));
    257    return true;
    258  }
    259 
    260  void Destroy() override {
    261    MutexAutoLock lock(mMutex);
    262    DestroyLocked(NS_ERROR_ABORT);
    263  }
    264 
    265  void DestroyLocked(nsresult aResult) MOZ_REQUIRES(mMutex) {
    266    MOZ_LOG(sLog, LogLevel::Debug,
    267            ("[%p] AnonymousDecoderImpl::Destroy", this));
    268 
    269    mFramesToDecode = 0;
    270    mMetadataTask = nullptr;
    271    mFrameCountTask = nullptr;
    272    mFramesTask = nullptr;
    273    mPendingFramesResult.mFrames.Clear();
    274    mPendingFramesResult.mFinished = true;
    275    mMetadataPromise.RejectIfExists(aResult, __func__);
    276    mFrameCountPromise.RejectIfExists(aResult, __func__);
    277    mFramesPromise.RejectIfExists(aResult, __func__);
    278  }
    279 
    280  void OnMetadata(const ImageMetadata* aMetadata) override {
    281    MutexAutoLock lock(mMutex);
    282 
    283    // We must have already gotten destroyed before metadata decoding finished.
    284    if (!mMetadataTask) {
    285      return;
    286    }
    287 
    288    if (!aMetadata) {
    289      MOZ_LOG(sLog, LogLevel::Error,
    290              ("[%p] AnonymousDecoderImpl::OnMetadata -- failed", this));
    291      DestroyLocked(NS_ERROR_FAILURE);
    292      return;
    293    }
    294 
    295    mMetadataResult.mNativeSizes = aMetadata->GetNativeSizes().Clone();
    296 
    297    const auto size = aMetadata->GetSize();
    298    mMetadataResult.mWidth = size.width;
    299    mMetadataResult.mHeight = size.height;
    300    mMetadataResult.mRepetitions = aMetadata->GetLoopCount();
    301    mMetadataResult.mAnimated = aMetadata->HasAnimation();
    302 
    303    MOZ_LOG(sLog, LogLevel::Debug,
    304            ("[%p] AnonymousDecoderImpl::OnMetadata -- %dx%d, repetitions %d, "
    305             "animated %d",
    306             this, size.width, size.height, mMetadataResult.mRepetitions,
    307             mMetadataResult.mAnimated));
    308 
    309    if (mOutputSize && !mMetadataResult.mAnimated && mFramesTask) {
    310      if (mOutputSize->width <= size.width &&
    311          mOutputSize->height <= size.height) {
    312        MOZ_LOG(
    313            sLog, LogLevel::Debug,
    314            ("[%p] AnonymousDecoderImpl::OnMetadata -- use output size %dx%d",
    315             this, mOutputSize->width, mOutputSize->height));
    316        mFramesTask->SetOutputSize(
    317            OrientedIntSize::FromUnknownSize(*mOutputSize));
    318      } else {
    319        MOZ_LOG(sLog, LogLevel::Debug,
    320                ("[%p] AnonymousDecoderImpl::OnMetadata -- cannot use output "
    321                 "size %dx%d, exceeds metadata size",
    322                 this, mOutputSize->width, mOutputSize->height));
    323      }
    324    }
    325 
    326    if (!mMetadataResult.mAnimated) {
    327      mMetadataResult.mFrameCount = 1;
    328      mMetadataResult.mFrameCountComplete = true;
    329      mMetadataTask = nullptr;
    330      mFrameCountTask = nullptr;
    331    } else if (mFrameCountTask && !mFrameCountTaskRunning) {
    332      MOZ_LOG(
    333          sLog, LogLevel::Debug,
    334          ("[%p] AnonymousDecoderImpl::OnMetadata -- start frame count task",
    335           this));
    336      mFrameCountTaskRunning = mFrameCountTask->MaybeStart();
    337      return;
    338    }
    339 
    340    mMetadataPromise.Resolve(mMetadataResult, __func__);
    341 
    342    if (mFramesTask && mFramesToDecode > 0 && !mFramesTaskRunning) {
    343      MOZ_LOG(sLog, LogLevel::Debug,
    344              ("[%p] AnonymousDecoderImpl::OnMetadata -- start frames task, "
    345               "want %zu",
    346               this, mFramesToDecode));
    347      mFramesTaskRunning = mFramesTask->MaybeStart();
    348    }
    349  }
    350 
    351  void OnFrameCount(uint32_t aFrameCount, bool aComplete) override {
    352    MutexAutoLock lock(mMutex);
    353 
    354    // We must have already gotten destroyed before frame count decoding
    355    // finished.
    356    if (!mFrameCountTask) {
    357      return;
    358    }
    359 
    360    MOZ_LOG(sLog, LogLevel::Debug,
    361            ("[%p] AnonymousDecoderImpl::OnFrameCount -- frameCount %u, "
    362             "complete %d",
    363             this, aFrameCount, aComplete));
    364 
    365    bool resolve = aComplete;
    366    if (mFrameCount < aFrameCount) {
    367      mFrameCount = aFrameCount;
    368      resolve = true;
    369    }
    370 
    371    // If metadata completing is waiting on an updated frame count, resolve it.
    372    mMetadataResult.mFrameCount = mFrameCount;
    373    mMetadataResult.mFrameCountComplete = aComplete;
    374    mMetadataPromise.ResolveIfExists(mMetadataResult, __func__);
    375 
    376    if (mMetadataTask) {
    377      mMetadataTask = nullptr;
    378      if (mFramesTask && mFramesToDecode > 0 && !mFramesTaskRunning) {
    379        MOZ_LOG(
    380            sLog, LogLevel::Debug,
    381            ("[%p] AnonymousDecoderImpl::OnFrameCount -- start frames task, "
    382             "want %zu",
    383             this, mFramesToDecode));
    384        mFramesTaskRunning = mFramesTask->MaybeStart();
    385      }
    386    }
    387 
    388    if (resolve) {
    389      mFrameCountPromise.ResolveIfExists(
    390          DecodeFrameCountResult{aFrameCount, aComplete}, __func__);
    391    }
    392 
    393    if (aComplete) {
    394      mFrameCountTask = nullptr;
    395    }
    396  }
    397 
    398  bool OnFrameAvailable(RefPtr<imgFrame>&& aFrame,
    399                        RefPtr<gfx::SourceSurface>&& aSurface) override {
    400    MutexAutoLock lock(mMutex);
    401 
    402    MOZ_DIAGNOSTIC_ASSERT(mFramesTaskRunning);
    403 
    404    // We must have already gotten destroyed before frame decoding finished.
    405    if (!mFramesTask) {
    406      mFramesTaskRunning = false;
    407      return false;
    408    }
    409 
    410    // Filter duplicate frames.
    411    if (mLastFrame == aFrame) {
    412      return true;
    413    }
    414 
    415    mPendingFramesResult.mFrames.AppendElement(
    416        DecodedFrame{std::move(aSurface), mMetadataResult.mAnimated
    417                                              ? aFrame->GetTimeout()
    418                                              : FrameTimeout::Forever()});
    419    mLastFrame = std::move(aFrame);
    420 
    421    MOZ_LOG(sLog, LogLevel::Debug,
    422            ("[%p] AnonymousDecoderImpl::OnFrameAvailable -- want %zu, got %zu",
    423             this, mFramesToDecode, mPendingFramesResult.mFrames.Length()));
    424 
    425    // Check if we have satisfied the number of requested frames.
    426    if (mFramesToDecode > mPendingFramesResult.mFrames.Length()) {
    427      return true;
    428    }
    429 
    430    mFramesToDecode = 0;
    431    if (!mFramesPromise.IsEmpty()) {
    432      mFramesPromise.Resolve(std::move(mPendingFramesResult), __func__);
    433    }
    434    mFramesTaskRunning = false;
    435    return false;
    436  }
    437 
    438  void OnFramesComplete() override {
    439    MutexAutoLock lock(mMutex);
    440 
    441    // We must have already gotten destroyed before frame decoding finished.
    442    if (!mFramesTask) {
    443      return;
    444    }
    445 
    446    MOZ_LOG(
    447        sLog, LogLevel::Debug,
    448        ("[%p] AnonymousDecoderImpl::OnFramesComplete -- wanted %zu, got %zu",
    449         this, mFramesToDecode, mPendingFramesResult.mFrames.Length()));
    450 
    451    mFramesToDecode = 0;
    452    mPendingFramesResult.mFinished = true;
    453    if (!mFramesPromise.IsEmpty()) {
    454      mFramesPromise.Resolve(std::move(mPendingFramesResult), __func__);
    455    }
    456    mLastFrame = nullptr;
    457    mFramesTask = nullptr;
    458  }
    459 
    460  RefPtr<DecodeMetadataPromise> DecodeMetadata() override {
    461    MutexAutoLock lock(mMutex);
    462 
    463    if (!mMetadataTask) {
    464      MOZ_LOG(sLog, LogLevel::Debug,
    465              ("[%p] AnonymousDecoderImpl::DecodeMetadata -- already complete",
    466               this));
    467      if (mMetadataResult.mWidth > 0 && mMetadataResult.mHeight > 0) {
    468        return DecodeMetadataPromise::CreateAndResolve(mMetadataResult,
    469                                                       __func__);
    470      }
    471      return DecodeMetadataPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
    472    }
    473 
    474    if (!mMetadataTaskRunning) {
    475      MOZ_LOG(sLog, LogLevel::Debug,
    476              ("[%p] AnonymousDecoderImpl::DecodeMetadata -- queue", this));
    477      mMetadataTaskRunning = mMetadataTask->MaybeStart();
    478    }
    479 
    480    return mMetadataPromise.Ensure(__func__);
    481  }
    482 
    483  RefPtr<DecodeFrameCountPromise> DecodeFrameCount(
    484      uint32_t aKnownFrameCount) override {
    485    MutexAutoLock lock(mMutex);
    486 
    487    MOZ_ASSERT(mFrameCountPromise.IsEmpty());
    488 
    489    // If we have finished, or we have an updated frame count, return right
    490    // away. This may drive the frame decoder for the application as the data
    491    // comes in from the network.
    492    if (!mFrameCountTask || aKnownFrameCount < mFrameCount) {
    493      MOZ_LOG(sLog, LogLevel::Debug,
    494              ("[%p] AnonymousDecoderImpl::DecodeFrameCount -- known %u, "
    495               "detected %u, complete %d",
    496               this, aKnownFrameCount, mFrameCount, !mFrameCountTask));
    497      return DecodeFrameCountPromise::CreateAndResolve(
    498          DecodeFrameCountResult{mFrameCount,
    499                                 /* mFinished */ !mFrameCountTask},
    500          __func__);
    501    }
    502 
    503    // mFrameCountTask is launching when metadata decoding is finished.
    504    MOZ_LOG(sLog, LogLevel::Debug,
    505            ("[%p] AnonymousDecoderImpl::DecodeFrameCount -- waiting, known "
    506             "%u, detected %u",
    507             this, aKnownFrameCount, mFrameCount));
    508    return mFrameCountPromise.Ensure(__func__);
    509  }
    510 
    511  RefPtr<DecodeFramesPromise> DecodeFrames(size_t aCount) override {
    512    MutexAutoLock lock(mMutex);
    513 
    514    // If we cleared our task reference, then we know we finished decoding.
    515    if (!mFramesTask) {
    516      mPendingFramesResult.mFinished = true;
    517      return DecodeFramesPromise::CreateAndResolve(
    518          std::move(mPendingFramesResult), __func__);
    519    }
    520 
    521    // If we are not waiting on any frames, then we know we paused decoding.
    522    // If we still are metadata decoding, we need to wait.
    523    if (mFramesToDecode == 0 && !mMetadataTask && !mFramesTaskRunning) {
    524      MOZ_LOG(sLog, LogLevel::Debug,
    525              ("[%p] AnonymousDecoderImpl::DecodeFrames -- queue", this));
    526      mFramesTaskRunning = mFramesTask->MaybeStart();
    527    }
    528 
    529    mFramesToDecode = std::max(mFramesToDecode, aCount);
    530    return mFramesPromise.Ensure(__func__);
    531  }
    532 
    533  void CancelDecodeFrames() override {
    534    MutexAutoLock lock(mMutex);
    535    MOZ_LOG(sLog, LogLevel::Debug,
    536            ("[%p] AnonymousDecoderImpl::CancelDecodeFrames", this));
    537    mFramesToDecode = 0;
    538    mFramesPromise.RejectIfExists(NS_ERROR_ABORT, __func__);
    539  }
    540 
    541 private:
    542  Mutex mMutex;
    543  MozPromiseHolder<DecodeMetadataPromise> mMetadataPromise
    544      MOZ_GUARDED_BY(mMutex);
    545  MozPromiseHolder<DecodeFrameCountPromise> mFrameCountPromise
    546      MOZ_GUARDED_BY(mMutex);
    547  MozPromiseHolder<DecodeFramesPromise> mFramesPromise MOZ_GUARDED_BY(mMutex);
    548  RefPtr<AnonymousFramesDecoderTask> mFramesTask MOZ_GUARDED_BY(mMutex);
    549  RefPtr<AnonymousMetadataDecoderTask> mMetadataTask MOZ_GUARDED_BY(mMutex);
    550  RefPtr<AnonymousFrameCountDecoderTask> mFrameCountTask MOZ_GUARDED_BY(mMutex);
    551  RefPtr<imgFrame> mLastFrame MOZ_GUARDED_BY(mMutex);
    552  DecodeMetadataResult mMetadataResult MOZ_GUARDED_BY(mMutex);
    553  DecodeFramesResult mPendingFramesResult MOZ_GUARDED_BY(mMutex);
    554  Maybe<gfx::IntSize> mOutputSize MOZ_GUARDED_BY(mMutex);
    555  size_t mFramesToDecode MOZ_GUARDED_BY(mMutex) = 1;
    556  uint32_t mFrameCount MOZ_GUARDED_BY(mMutex) = 0;
    557  bool mMetadataTaskRunning MOZ_GUARDED_BY(mMutex) = false;
    558  bool mFrameCountTaskRunning MOZ_GUARDED_BY(mMutex) = false;
    559  bool mFramesTaskRunning MOZ_GUARDED_BY(mMutex) = false;
    560 };
    561 
    562 /* static */ already_AddRefed<AnonymousDecoder> ImageUtils::CreateDecoder(
    563    SourceBuffer* aSourceBuffer, DecoderType aType,
    564    const Maybe<gfx::IntSize>& aOutputSize, SurfaceFlags aSurfaceFlags) {
    565  if (NS_WARN_IF(!aSourceBuffer)) {
    566    return nullptr;
    567  }
    568 
    569  if (NS_WARN_IF(aType == DecoderType::UNKNOWN)) {
    570    return nullptr;
    571  }
    572 
    573  RefPtr<Decoder> decoder = DecoderFactory::CreateAnonymousDecoder(
    574      aType, WrapNotNull(aSourceBuffer), Nothing(),
    575      DecoderFlags::IMAGE_IS_TRANSIENT, aSurfaceFlags);
    576  if (NS_WARN_IF(!decoder)) {
    577    return nullptr;
    578  }
    579 
    580  auto anonymousDecoder = MakeRefPtr<AnonymousDecoderImpl>(aOutputSize);
    581  if (NS_WARN_IF(!anonymousDecoder->Initialize(std::move(decoder)))) {
    582    return nullptr;
    583  }
    584 
    585  return anonymousDecoder.forget();
    586 }
    587 
    588 /* static */ DecoderType ImageUtils::GetDecoderType(
    589    const nsACString& aMimeType) {
    590  return DecoderFactory::GetDecoderType(aMimeType.Data());
    591 }
    592 
    593 }  // namespace mozilla::image