tor-browser

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

RasterImage.cpp (56894B)


      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 // Must #include ImageLogging.h before any IPDL-generated files or other files
      7 // that #include prlog.h
      8 #include "RasterImage.h"
      9 
     10 #include <stdint.h>
     11 
     12 #include <algorithm>
     13 #include <utility>
     14 
     15 #include "DecodePool.h"
     16 #include "Decoder.h"
     17 #include "FrameAnimator.h"
     18 #include "GeckoProfiler.h"
     19 #include "IDecodingTask.h"
     20 #include "ImageLogging.h"
     21 #include "ImageRegion.h"
     22 #include "LookupResult.h"
     23 #include "OrientedImage.h"
     24 #include "SourceBuffer.h"
     25 #include "SurfaceCache.h"
     26 #include "gfx2DGlue.h"
     27 #include "gfxContext.h"
     28 #include "gfxPlatform.h"
     29 #include "mozilla/ClearOnShutdown.h"
     30 #include "mozilla/RefPtr.h"
     31 #include "mozilla/SizeOfState.h"
     32 #include "mozilla/StaticPrefs_image.h"
     33 #include "mozilla/glean/ImageDecodersMetrics.h"
     34 #include "mozilla/TimeStamp.h"
     35 
     36 #include "mozilla/gfx/2D.h"
     37 #include "nsComponentManagerUtils.h"
     38 #include "nsError.h"
     39 #include "nsIConsoleService.h"
     40 #include "nsIInputStream.h"
     41 #include "nsIScriptError.h"
     42 #include "nsISupportsPrimitives.h"
     43 #include "nsMemory.h"
     44 #include "nsPresContext.h"
     45 #include "nsProperties.h"
     46 #include "prenv.h"
     47 #include "prsystem.h"
     48 #include "WindowRenderer.h"
     49 
     50 namespace mozilla {
     51 
     52 using namespace gfx;
     53 using namespace layers;
     54 
     55 namespace image {
     56 
     57 using std::ceil;
     58 using std::min;
     59 
     60 #ifndef DEBUG
     61 NS_IMPL_ISUPPORTS(RasterImage, imgIContainer)
     62 #else
     63 NS_IMPL_ISUPPORTS(RasterImage, imgIContainer, imgIContainerDebug)
     64 #endif
     65 
     66 //******************************************************************************
     67 RasterImage::RasterImage(nsIURI* aURI /* = nullptr */)
     68    : ImageResource(aURI),  // invoke superclass's constructor
     69      mSize(0, 0),
     70      mLockCount(0),
     71      mDecoderType(DecoderType::UNKNOWN),
     72      mDecodeCount(0),
     73 #ifdef DEBUG
     74      mFramesNotified(0),
     75 #endif
     76      mSourceBuffer(MakeNotNull<SourceBuffer*>()) {
     77 }
     78 
     79 //******************************************************************************
     80 RasterImage::~RasterImage() {
     81  // Make sure our SourceBuffer is marked as complete. This will ensure that any
     82  // outstanding decoders terminate.
     83  if (!mSourceBuffer->IsComplete()) {
     84    mSourceBuffer->Complete(NS_ERROR_ABORT);
     85  }
     86 
     87  // Release all frames from the surface cache.
     88  SurfaceCache::RemoveImage(ImageKey(this));
     89 
     90  // Record Telemetry.
     91  glean::image_decode::count.AccumulateSingleSample(mDecodeCount);
     92 }
     93 
     94 nsresult RasterImage::Init(const char* aMimeType, uint32_t aFlags) {
     95  // We don't support re-initialization
     96  if (mInitialized) {
     97    return NS_ERROR_ILLEGAL_VALUE;
     98  }
     99 
    100  // Not sure an error can happen before init, but be safe
    101  if (mError) {
    102    return NS_ERROR_FAILURE;
    103  }
    104 
    105  // We want to avoid redecodes for transient images.
    106  MOZ_ASSERT_IF(aFlags & INIT_FLAG_TRANSIENT,
    107                !(aFlags & INIT_FLAG_DISCARDABLE));
    108 
    109  // Store initialization data
    110  StoreDiscardable(!!(aFlags & INIT_FLAG_DISCARDABLE));
    111  StoreWantFullDecode(!!(aFlags & INIT_FLAG_DECODE_IMMEDIATELY));
    112  StoreTransient(!!(aFlags & INIT_FLAG_TRANSIENT));
    113  StoreSyncLoad(!!(aFlags & INIT_FLAG_SYNC_LOAD));
    114 
    115  // Use the MIME type to select a decoder type, and make sure there *is* a
    116  // decoder for this MIME type.
    117  NS_ENSURE_ARG_POINTER(aMimeType);
    118  mDecoderType = DecoderFactory::GetDecoderType(aMimeType);
    119  if (mDecoderType == DecoderType::UNKNOWN) {
    120    return NS_ERROR_FAILURE;
    121  }
    122 
    123  // Lock this image's surfaces in the SurfaceCache if we're not discardable.
    124  if (!LoadDiscardable()) {
    125    mLockCount++;
    126    SurfaceCache::LockImage(ImageKey(this));
    127  }
    128 
    129  // Set the default flags according to the decoder type to allow preferences to
    130  // be stored if necessary.
    131  mDefaultDecoderFlags =
    132      DecoderFactory::GetDefaultDecoderFlagsForType(mDecoderType);
    133 
    134  // Mark us as initialized
    135  mInitialized = true;
    136 
    137  return NS_OK;
    138 }
    139 
    140 //******************************************************************************
    141 NS_IMETHODIMP_(void)
    142 RasterImage::RequestRefresh(const TimeStamp& aTime) {
    143  if (HadRecentRefresh(aTime)) {
    144    return;
    145  }
    146 
    147  EvaluateAnimation();
    148 
    149  if (!mAnimating) {
    150    return;
    151  }
    152 
    153  RefreshResult res;
    154  if (mAnimationState) {
    155    MOZ_ASSERT(mFrameAnimator);
    156    res = mFrameAnimator->RequestRefresh(*mAnimationState, aTime);
    157  }
    158 
    159 #ifdef DEBUG
    160  if (res.mFrameAdvanced) {
    161    mFramesNotified++;
    162  }
    163 #endif
    164 
    165  // Notify listeners that our frame has actually changed, but do this only
    166  // once for all frames that we've now passed (if AdvanceFrame() was called
    167  // more than once).
    168  if (!res.mDirtyRect.IsEmpty() || res.mFrameAdvanced) {
    169    auto dirtyRect = OrientedIntRect::FromUnknownRect(res.mDirtyRect);
    170    NotifyProgress(NoProgress, dirtyRect);
    171  }
    172 
    173  if (res.mAnimationFinished) {
    174    StoreAnimationFinished(true);
    175    EvaluateAnimation();
    176  }
    177 }
    178 
    179 //******************************************************************************
    180 NS_IMETHODIMP
    181 RasterImage::GetWidth(int32_t* aWidth) {
    182  NS_ENSURE_ARG_POINTER(aWidth);
    183 
    184  if (mError) {
    185    *aWidth = 0;
    186    return NS_ERROR_FAILURE;
    187  }
    188 
    189  *aWidth = mSize.width;
    190  return NS_OK;
    191 }
    192 
    193 //******************************************************************************
    194 NS_IMETHODIMP
    195 RasterImage::GetHeight(int32_t* aHeight) {
    196  NS_ENSURE_ARG_POINTER(aHeight);
    197 
    198  if (mError) {
    199    *aHeight = 0;
    200    return NS_ERROR_FAILURE;
    201  }
    202 
    203  *aHeight = mSize.height;
    204  return NS_OK;
    205 }
    206 
    207 //******************************************************************************
    208 NS_IMETHODIMP
    209 RasterImage::GetIntrinsicSize(ImageIntrinsicSize* aIntrinsicSize) {
    210  NS_ENSURE_ARG_POINTER(aIntrinsicSize);
    211 
    212  if (mError) {
    213    return NS_ERROR_FAILURE;
    214  }
    215 
    216  aIntrinsicSize->mWidth = Some(mSize.width);
    217  aIntrinsicSize->mHeight = Some(mSize.height);
    218  return NS_OK;
    219 }
    220 
    221 //******************************************************************************
    222 void RasterImage::MediaFeatureValuesChangedAllDocuments(
    223    const mozilla::MediaFeatureChange& aChange) {}
    224 
    225 //******************************************************************************
    226 nsresult RasterImage::GetNativeSizes(nsTArray<IntSize>& aNativeSizes) {
    227  if (mError) {
    228    return NS_ERROR_FAILURE;
    229  }
    230 
    231  aNativeSizes.Clear();
    232 
    233  if (mNativeSizes.IsEmpty()) {
    234    aNativeSizes.AppendElement(mSize.ToUnknownSize());
    235  } else {
    236    for (const auto& size : mNativeSizes) {
    237      aNativeSizes.AppendElement(size.ToUnknownSize());
    238    }
    239  }
    240 
    241  return NS_OK;
    242 }
    243 
    244 //******************************************************************************
    245 size_t RasterImage::GetNativeSizesLength() {
    246  if (mError || !LoadHasSize()) {
    247    return 0;
    248  }
    249 
    250  if (mNativeSizes.IsEmpty()) {
    251    return 1;
    252  }
    253 
    254  return mNativeSizes.Length();
    255 }
    256 
    257 //******************************************************************************
    258 NS_IMETHODIMP
    259 RasterImage::GetIntrinsicSizeInAppUnits(nsSize* aSize) {
    260  if (mError) {
    261    return NS_ERROR_FAILURE;
    262  }
    263 
    264  *aSize = nsSize(nsPresContext::CSSPixelsToAppUnits(mSize.width),
    265                  nsPresContext::CSSPixelsToAppUnits(mSize.height));
    266  return NS_OK;
    267 }
    268 
    269 //******************************************************************************
    270 AspectRatio RasterImage::GetIntrinsicRatio() {
    271  if (mError) {
    272    return {};
    273  }
    274  return AspectRatio::FromSize(mSize.width, mSize.height);
    275 }
    276 
    277 NS_IMETHODIMP_(Orientation)
    278 RasterImage::GetOrientation() { return mOrientation; }
    279 
    280 NS_IMETHODIMP_(Resolution)
    281 RasterImage::GetResolution() { return mResolution; }
    282 
    283 //******************************************************************************
    284 NS_IMETHODIMP
    285 RasterImage::GetType(uint16_t* aType) {
    286  NS_ENSURE_ARG_POINTER(aType);
    287 
    288  *aType = imgIContainer::TYPE_RASTER;
    289  return NS_OK;
    290 }
    291 
    292 NS_IMETHODIMP
    293 RasterImage::GetProviderId(uint32_t* aId) {
    294  NS_ENSURE_ARG_POINTER(aId);
    295 
    296  *aId = ImageResource::GetImageProviderId();
    297  return NS_OK;
    298 }
    299 
    300 LookupResult RasterImage::LookupFrameInternal(const OrientedIntSize& aSize,
    301                                              uint32_t aFlags,
    302                                              PlaybackType aPlaybackType,
    303                                              bool aMarkUsed) {
    304  if (mAnimationState && aPlaybackType == PlaybackType::eAnimated) {
    305    MOZ_ASSERT(mFrameAnimator);
    306    MOZ_ASSERT(ToSurfaceFlags(aFlags) == DefaultSurfaceFlags(),
    307               "Can't composite frames with non-default surface flags");
    308    return mFrameAnimator->GetCompositedFrame(*mAnimationState, aMarkUsed);
    309  }
    310 
    311  SurfaceFlags surfaceFlags = ToSurfaceFlags(aFlags);
    312 
    313  // We don't want any substitution for sync decodes, and substitution would be
    314  // illegal when high quality downscaling is disabled, so we use
    315  // SurfaceCache::Lookup in this case.
    316  if ((aFlags & FLAG_SYNC_DECODE) || !(aFlags & FLAG_HIGH_QUALITY_SCALING)) {
    317    return SurfaceCache::Lookup(
    318        ImageKey(this),
    319        RasterSurfaceKey(aSize.ToUnknownSize(), surfaceFlags,
    320                         PlaybackType::eStatic),
    321        aMarkUsed);
    322  }
    323 
    324  // We'll return the best match we can find to the requested frame.
    325  return SurfaceCache::LookupBestMatch(
    326      ImageKey(this),
    327      RasterSurfaceKey(aSize.ToUnknownSize(), surfaceFlags,
    328                       PlaybackType::eStatic),
    329      aMarkUsed);
    330 }
    331 
    332 LookupResult RasterImage::LookupFrame(const OrientedIntSize& aSize,
    333                                      uint32_t aFlags,
    334                                      PlaybackType aPlaybackType,
    335                                      bool aMarkUsed) {
    336  MOZ_ASSERT(NS_IsMainThread());
    337 
    338  // If we're opaque, we don't need to care about premultiplied alpha, because
    339  // that can only matter for frames with transparency.
    340  if (IsOpaque()) {
    341    aFlags &= ~FLAG_DECODE_NO_PREMULTIPLY_ALPHA;
    342  }
    343 
    344  OrientedIntSize requestedSize =
    345      CanDownscaleDuringDecode(aSize, aFlags) ? aSize : mSize;
    346  if (requestedSize.IsEmpty()) {
    347    // Can't decode to a surface of zero size.
    348    return LookupResult(MatchType::NOT_FOUND);
    349  }
    350 
    351  LookupResult result =
    352      LookupFrameInternal(requestedSize, aFlags, aPlaybackType, aMarkUsed);
    353 
    354  if (!result && !LoadHasSize()) {
    355    // We can't request a decode without knowing our intrinsic size. Give up.
    356    return LookupResult(MatchType::NOT_FOUND);
    357  }
    358 
    359  // We want to trigger a decode if and only if:
    360  // 1) There is no pending decode
    361  // 2) There is no acceptable size decoded
    362  // 3) The pending decode has not produced a frame yet, a sync decode is
    363  // requested, and we have all the source data. Without the source data, we
    364  // will just trigger another async decode anyways.
    365  //
    366  // TODO(aosmond): We should better handle case 3. We should actually return
    367  // TEMPORARY_ERROR or NOT_READY if we don't have all the source data and a
    368  // sync decode is requested. If there is a pending decode and we have all the
    369  // source data, we should always be able to block on the frame's monitor --
    370  // perhaps this could be accomplished by preallocating the first frame buffer
    371  // when we create the decoder.
    372  const bool syncDecode = aFlags & FLAG_SYNC_DECODE;
    373  const bool avoidRedecode = aFlags & FLAG_AVOID_REDECODE_FOR_SIZE;
    374  if (result.Type() == MatchType::NOT_FOUND ||
    375      (result.Type() == MatchType::SUBSTITUTE_BECAUSE_NOT_FOUND &&
    376       !avoidRedecode) ||
    377      (syncDecode && !avoidRedecode && !result && LoadAllSourceData())) {
    378    // We don't have a copy of this frame, and there's no decoder working on
    379    // one. (Or we're sync decoding and the existing decoder hasn't even started
    380    // yet.) Trigger decoding so it'll be available next time.
    381    MOZ_ASSERT(aPlaybackType != PlaybackType::eAnimated ||
    382                   StaticPrefs::image_mem_animated_discardable_AtStartup() ||
    383                   !mAnimationState || mAnimationState->KnownFrameCount() < 1,
    384               "Animated frames should be locked");
    385 
    386    // The surface cache may suggest the preferred size we are supposed to
    387    // decode at. This should only happen if we accept substitutions.
    388    if (!result.SuggestedSize().IsEmpty()) {
    389      MOZ_ASSERT(!syncDecode && (aFlags & FLAG_HIGH_QUALITY_SCALING));
    390      requestedSize = OrientedIntSize::FromUnknownSize(result.SuggestedSize());
    391    }
    392 
    393    bool ranSync = false, failed = false;
    394    Decode(requestedSize, aFlags, aPlaybackType, ranSync, failed);
    395    if (failed) {
    396      result.SetFailedToRequestDecode();
    397    }
    398 
    399    // If we can or did sync decode, we should already have the frame.
    400    if (ranSync || syncDecode) {
    401      result =
    402          LookupFrameInternal(requestedSize, aFlags, aPlaybackType, aMarkUsed);
    403    }
    404  }
    405 
    406  if (!result) {
    407    // We still weren't able to get a frame. Give up.
    408    return result;
    409  }
    410 
    411  // Sync decoding guarantees that we got the frame, but if it's owned by an
    412  // async decoder that's currently running, the contents of the frame may not
    413  // be available yet. Make sure we get everything.
    414  if (LoadAllSourceData() && syncDecode) {
    415    result.Surface()->WaitUntilFinished();
    416  }
    417 
    418  // If we could have done some decoding in this function we need to check if
    419  // that decoding encountered an error and hence aborted the surface. We want
    420  // to avoid calling IsAborted if we weren't passed any sync decode flag
    421  // because IsAborted acquires the monitor for the imgFrame.
    422  if (aFlags & (FLAG_SYNC_DECODE | FLAG_SYNC_DECODE_IF_FAST) &&
    423      result.Surface()->IsAborted()) {
    424    DrawableSurface tmp = std::move(result.Surface());
    425    return result;
    426  }
    427 
    428  return result;
    429 }
    430 
    431 bool RasterImage::IsOpaque() {
    432  if (mError) {
    433    return false;
    434  }
    435 
    436  Progress progress = mProgressTracker->GetProgress();
    437 
    438  // If we haven't yet finished decoding, the safe answer is "not opaque".
    439  if (!(progress & FLAG_DECODE_COMPLETE)) {
    440    return false;
    441  }
    442 
    443  // Other, we're opaque if FLAG_HAS_TRANSPARENCY is not set.
    444  return !(progress & FLAG_HAS_TRANSPARENCY);
    445 }
    446 
    447 NS_IMETHODIMP_(bool)
    448 RasterImage::WillDrawOpaqueNow() {
    449  if (!IsOpaque()) {
    450    return false;
    451  }
    452 
    453  if (mAnimationState) {
    454    if (!StaticPrefs::image_mem_animated_discardable_AtStartup()) {
    455      // We never discard frames of animated images.
    456      return true;
    457    } else {
    458      if (mAnimationState->GetCompositedFrameInvalid()) {
    459        // We're not going to draw anything at all.
    460        return false;
    461      }
    462    }
    463  }
    464 
    465  // If we are not locked our decoded data could get discard at any time (ie
    466  // between the call to this function and when we are asked to draw), so we
    467  // have to return false if we are unlocked.
    468  if (mLockCount == 0) {
    469    return false;
    470  }
    471 
    472  LookupResult result = SurfaceCache::LookupBestMatch(
    473      ImageKey(this),
    474      RasterSurfaceKey(mSize.ToUnknownSize(), DefaultSurfaceFlags(),
    475                       PlaybackType::eStatic),
    476      /* aMarkUsed = */ false);
    477  MatchType matchType = result.Type();
    478  if (matchType == MatchType::NOT_FOUND || matchType == MatchType::PENDING ||
    479      !result.Surface()->IsFinished()) {
    480    return false;
    481  }
    482 
    483  return true;
    484 }
    485 
    486 void RasterImage::OnSurfaceDiscarded(const SurfaceKey& aSurfaceKey) {
    487  MOZ_ASSERT(mProgressTracker);
    488 
    489  bool animatedFramesDiscarded =
    490      mAnimationState && aSurfaceKey.Playback() == PlaybackType::eAnimated;
    491 
    492  nsCOMPtr<nsIEventTarget> eventTarget = do_GetMainThread();
    493 
    494  RefPtr<RasterImage> image = this;
    495  nsCOMPtr<nsIRunnable> ev =
    496      NS_NewRunnableFunction("RasterImage::OnSurfaceDiscarded", [=]() -> void {
    497        image->OnSurfaceDiscardedInternal(animatedFramesDiscarded);
    498      });
    499  eventTarget->Dispatch(ev.forget(), NS_DISPATCH_NORMAL);
    500 }
    501 
    502 void RasterImage::OnSurfaceDiscardedInternal(bool aAnimatedFramesDiscarded) {
    503  MOZ_ASSERT(NS_IsMainThread());
    504 
    505  if (aAnimatedFramesDiscarded && mAnimationState) {
    506    MOZ_ASSERT(StaticPrefs::image_mem_animated_discardable_AtStartup());
    507 
    508    IntRect rect = mAnimationState->UpdateState(this, mSize.ToUnknownSize());
    509 
    510    auto dirtyRect = OrientedIntRect::FromUnknownRect(rect);
    511    NotifyProgress(NoProgress, dirtyRect);
    512  }
    513 
    514  if (mProgressTracker) {
    515    mProgressTracker->OnDiscard();
    516  }
    517 }
    518 
    519 //******************************************************************************
    520 NS_IMETHODIMP
    521 RasterImage::GetAnimated(bool* aAnimated) {
    522  if (mError) {
    523    return NS_ERROR_FAILURE;
    524  }
    525 
    526  NS_ENSURE_ARG_POINTER(aAnimated);
    527 
    528  // If we have an AnimationState, we can know for sure.
    529  if (mAnimationState) {
    530    *aAnimated = true;
    531    return NS_OK;
    532  }
    533 
    534  // Otherwise, we need to have been decoded to know for sure, since if we were
    535  // decoded at least once mAnimationState would have been created for animated
    536  // images. This is true even though we check for animation during the
    537  // metadata decode, because we may still discover animation only during the
    538  // full decode for corrupt images.
    539  if (!LoadHasBeenDecoded()) {
    540    return NS_ERROR_NOT_AVAILABLE;
    541  }
    542 
    543  // We know for sure
    544  *aAnimated = false;
    545 
    546  return NS_OK;
    547 }
    548 
    549 //******************************************************************************
    550 NS_IMETHODIMP_(int32_t)
    551 RasterImage::GetFirstFrameDelay() {
    552  if (mError) {
    553    return -1;
    554  }
    555 
    556  bool animated = false;
    557  if (NS_FAILED(GetAnimated(&animated)) || !animated) {
    558    return -1;
    559  }
    560 
    561  MOZ_ASSERT(mAnimationState, "Animated images should have an AnimationState");
    562  return mAnimationState->FirstFrameTimeout().AsEncodedValueDeprecated();
    563 }
    564 
    565 NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
    566 RasterImage::GetFrame(uint32_t aWhichFrame, uint32_t aFlags) {
    567  return GetFrameAtSize(mSize.ToUnknownSize(), aWhichFrame, aFlags);
    568 }
    569 
    570 NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
    571 RasterImage::GetFrameAtSize(const IntSize& aSize, uint32_t aWhichFrame,
    572                            uint32_t aFlags) {
    573  MOZ_ASSERT(aWhichFrame <= FRAME_MAX_VALUE);
    574 
    575  AutoProfilerImagePaintMarker PROFILER_RAII(this);
    576 #ifdef DEBUG
    577  NotifyDrawingObservers();
    578 #endif
    579 
    580  if (aSize.IsEmpty() || aWhichFrame > FRAME_MAX_VALUE || mError) {
    581    return nullptr;
    582  }
    583 
    584  auto size = OrientedIntSize::FromUnknownSize(aSize);
    585 
    586  // Get the frame. If it's not there, it's probably the caller's fault for
    587  // not waiting for the data to be loaded from the network or not passing
    588  // FLAG_SYNC_DECODE.
    589  LookupResult result = LookupFrame(size, aFlags, ToPlaybackType(aWhichFrame),
    590                                    /* aMarkUsed = */ true);
    591  if (!result) {
    592    // The OS threw this frame away and we couldn't redecode it.
    593    return nullptr;
    594  }
    595 
    596  return result.Surface()->GetSourceSurface();
    597 }
    598 
    599 NS_IMETHODIMP_(bool)
    600 RasterImage::IsImageContainerAvailable(WindowRenderer* aRenderer,
    601                                       uint32_t aFlags) {
    602  return LoadHasSize();
    603 }
    604 
    605 NS_IMETHODIMP_(ImgDrawResult)
    606 RasterImage::GetImageProvider(WindowRenderer* aRenderer,
    607                              const gfx::IntSize& aSize,
    608                              const SVGImageContext& aSVGContext,
    609                              const Maybe<ImageIntRegion>& aRegion,
    610                              uint32_t aFlags,
    611                              WebRenderImageProvider** aProvider) {
    612  MOZ_ASSERT(NS_IsMainThread());
    613  MOZ_ASSERT(aRenderer);
    614 
    615  if (mError) {
    616    return ImgDrawResult::BAD_IMAGE;
    617  }
    618 
    619  if (!LoadHasSize()) {
    620    return ImgDrawResult::NOT_READY;
    621  }
    622 
    623  if (aSize.IsEmpty()) {
    624    return ImgDrawResult::BAD_ARGS;
    625  }
    626 
    627  // We check the minimum size because while we support downscaling, we do not
    628  // support upscaling. If aRequestedSize > mSize, we will never give a larger
    629  // surface than mSize. If mSize > aRequestedSize, and mSize > maxTextureSize,
    630  // we still want to use image containers if aRequestedSize <= maxTextureSize.
    631  int32_t maxTextureSize = aRenderer->GetMaxTextureSize();
    632  if (min(mSize.width, aSize.width) > maxTextureSize ||
    633      min(mSize.height, aSize.height) > maxTextureSize) {
    634    return ImgDrawResult::NOT_SUPPORTED;
    635  }
    636 
    637  AutoProfilerImagePaintMarker PROFILER_RAII(this);
    638 #ifdef DEBUG
    639  NotifyDrawingObservers();
    640 #endif
    641 
    642  // Get the frame. If it's not there, it's probably the caller's fault for
    643  // not waiting for the data to be loaded from the network or not passing
    644  // FLAG_SYNC_DECODE.
    645  LookupResult result = LookupFrame(OrientedIntSize::FromUnknownSize(aSize),
    646                                    aFlags, PlaybackType::eAnimated,
    647                                    /* aMarkUsed = */ true);
    648  if (!result) {
    649    // The OS threw this frame away and we couldn't redecode it.
    650    return ImgDrawResult::NOT_READY;
    651  }
    652 
    653  if (!result.Surface()->IsFinished()) {
    654    result.Surface().TakeProvider(aProvider);
    655    return ImgDrawResult::INCOMPLETE;
    656  }
    657 
    658  result.Surface().TakeProvider(aProvider);
    659  switch (result.Type()) {
    660    case MatchType::SUBSTITUTE_BECAUSE_NOT_FOUND:
    661    case MatchType::SUBSTITUTE_BECAUSE_PENDING:
    662      return ImgDrawResult::WRONG_SIZE;
    663    default:
    664      return ImgDrawResult::SUCCESS;
    665  }
    666 }
    667 
    668 size_t RasterImage::SizeOfSourceWithComputedFallback(
    669    SizeOfState& aState) const {
    670  return mSourceBuffer->SizeOfIncludingThisWithComputedFallback(
    671      aState.mMallocSizeOf);
    672 }
    673 
    674 bool RasterImage::SetMetadata(const ImageMetadata& aMetadata,
    675                              bool aFromMetadataDecode) {
    676  MOZ_ASSERT(NS_IsMainThread());
    677 
    678  if (mError) {
    679    return true;
    680  }
    681 
    682  mResolution = aMetadata.GetResolution();
    683 
    684  if (aMetadata.HasSize()) {
    685    auto metadataSize = aMetadata.GetSize();
    686    if (metadataSize.width < 0 || metadataSize.height < 0) {
    687      NS_WARNING("Image has negative intrinsic size");
    688      DoError();
    689      return true;
    690    }
    691 
    692    MOZ_ASSERT(aMetadata.HasOrientation());
    693    Orientation orientation = aMetadata.GetOrientation();
    694 
    695    // If we already have a size, check the new size against the old one.
    696    if (LoadHasSize() &&
    697        (metadataSize != mSize || orientation != mOrientation)) {
    698      NS_WARNING(
    699          "Image changed size or orientation on redecode! "
    700          "This should not happen!");
    701      DoError();
    702      return true;
    703    }
    704 
    705    // Set the size and flag that we have it.
    706    mOrientation = orientation;
    707    mSize = metadataSize;
    708    mNativeSizes.Clear();
    709    for (const auto& nativeSize : aMetadata.GetNativeSizes()) {
    710      mNativeSizes.AppendElement(nativeSize);
    711    }
    712    StoreHasSize(true);
    713  }
    714 
    715  MOZ_ASSERT_IF(mAnimationState && !aFromMetadataDecode,
    716                mAnimationState->LoopCount() == aMetadata.GetLoopCount());
    717 
    718  if (LoadHasSize() && aMetadata.HasAnimation() && !mAnimationState) {
    719    // We're becoming animated, so initialize animation stuff.
    720    mAnimationState.emplace(mAnimationMode);
    721    mFrameAnimator = MakeUnique<FrameAnimator>(this, mSize.ToUnknownSize());
    722 
    723    if (!StaticPrefs::image_mem_animated_discardable_AtStartup()) {
    724      // We don't support discarding animated images (See bug 414259).
    725      // Lock the image and throw away the key.
    726      LockImage();
    727    }
    728 
    729    if (!aFromMetadataDecode) {
    730      // The metadata decode reported that this image isn't animated, but we
    731      // discovered that it actually was during the full decode. This is a
    732      // rare failure that only occurs for corrupt images. To recover, we need
    733      // to discard all existing surfaces and redecode.
    734      return false;
    735    }
    736  }
    737 
    738  if (mAnimationState) {
    739    mAnimationState->SetLoopCount(aMetadata.GetLoopCount());
    740    mAnimationState->SetFirstFrameTimeout(aMetadata.GetFirstFrameTimeout());
    741 
    742    if (aMetadata.HasLoopLength()) {
    743      mAnimationState->SetLoopLength(aMetadata.GetLoopLength());
    744    }
    745    if (aMetadata.HasFirstFrameRefreshArea()) {
    746      mAnimationState->SetFirstFrameRefreshArea(
    747          aMetadata.GetFirstFrameRefreshArea());
    748    }
    749  }
    750 
    751  if (aMetadata.HasHotspot()) {
    752    // NOTE(heycam): We shouldn't have any image formats that support both
    753    // orientation and hotspots, so we assert that rather than add code
    754    // to orient the hotspot point correctly.
    755    MOZ_ASSERT(mOrientation.IsIdentity(), "Would need to orient hotspot point");
    756 
    757    auto hotspot = aMetadata.GetHotspot();
    758    mHotspot.x = std::clamp(hotspot.x.value, 0, mSize.width - 1);
    759    mHotspot.y = std::clamp(hotspot.y.value, 0, mSize.height - 1);
    760  }
    761 
    762  return true;
    763 }
    764 
    765 NS_IMETHODIMP
    766 RasterImage::SetAnimationMode(uint16_t aAnimationMode) {
    767  if (mAnimationState) {
    768    mAnimationState->SetAnimationMode(aAnimationMode);
    769  }
    770  return SetAnimationModeInternal(aAnimationMode);
    771 }
    772 
    773 //******************************************************************************
    774 
    775 nsresult RasterImage::StartAnimation() {
    776  if (mError) {
    777    return NS_ERROR_FAILURE;
    778  }
    779 
    780  MOZ_ASSERT(ShouldAnimate(), "Should not animate!");
    781 
    782  // If we're not ready to animate, then set mPendingAnimation, which will cause
    783  // us to start animating if and when we do become ready.
    784  StorePendingAnimation(!mAnimationState ||
    785                        mAnimationState->KnownFrameCount() < 1);
    786  if (LoadPendingAnimation()) {
    787    return NS_OK;
    788  }
    789 
    790  // Don't bother to animate if we're displaying the first frame forever.
    791  if (mAnimationState->GetCurrentAnimationFrameIndex() == 0 &&
    792      mAnimationState->FirstFrameTimeout() == FrameTimeout::Forever()) {
    793    StoreAnimationFinished(true);
    794    return NS_ERROR_ABORT;
    795  }
    796 
    797  // We need to set the time that this initial frame was first displayed, as
    798  // this is used in AdvanceFrame().
    799  mAnimationState->InitAnimationFrameTimeIfNecessary();
    800 
    801  return NS_OK;
    802 }
    803 
    804 //******************************************************************************
    805 nsresult RasterImage::StopAnimation() {
    806  MOZ_ASSERT(mAnimating, "Should be animating!");
    807 
    808  nsresult rv = NS_OK;
    809  if (mError) {
    810    rv = NS_ERROR_FAILURE;
    811  } else {
    812    mAnimationState->SetAnimationFrameTime(TimeStamp());
    813  }
    814 
    815  mAnimating = false;
    816  return rv;
    817 }
    818 
    819 //******************************************************************************
    820 NS_IMETHODIMP
    821 RasterImage::ResetAnimation() {
    822  if (mError) {
    823    return NS_ERROR_FAILURE;
    824  }
    825 
    826  StorePendingAnimation(false);
    827 
    828  if (mAnimationMode == kDontAnimMode || !mAnimationState ||
    829      mAnimationState->GetCurrentAnimationFrameIndex() == 0) {
    830    return NS_OK;
    831  }
    832 
    833  StoreAnimationFinished(false);
    834 
    835  if (mAnimating) {
    836    StopAnimation();
    837  }
    838 
    839  MOZ_ASSERT(mAnimationState, "Should have AnimationState");
    840  MOZ_ASSERT(mFrameAnimator, "Should have FrameAnimator");
    841  mFrameAnimator->ResetAnimation(*mAnimationState);
    842 
    843  IntRect area = mAnimationState->FirstFrameRefreshArea();
    844  NotifyProgress(NoProgress, OrientedIntRect::FromUnknownRect(area));
    845 
    846  // Start the animation again. It may not have been running before, if
    847  // mAnimationFinished was true before entering this function.
    848  EvaluateAnimation();
    849 
    850  return NS_OK;
    851 }
    852 
    853 //******************************************************************************
    854 NS_IMETHODIMP_(void)
    855 RasterImage::SetAnimationStartTime(const TimeStamp& aTime) {
    856  if (mError || mAnimationMode == kDontAnimMode || mAnimating ||
    857      !mAnimationState) {
    858    return;
    859  }
    860 
    861  mAnimationState->SetAnimationFrameTime(aTime);
    862 }
    863 
    864 NS_IMETHODIMP_(float)
    865 RasterImage::GetFrameIndex(uint32_t aWhichFrame) {
    866  MOZ_ASSERT(aWhichFrame <= FRAME_MAX_VALUE, "Invalid argument");
    867  return (aWhichFrame == FRAME_FIRST || !mAnimationState)
    868             ? 0.0f
    869             : mAnimationState->GetCurrentAnimationFrameIndex();
    870 }
    871 
    872 NS_IMETHODIMP_(IntRect)
    873 RasterImage::GetImageSpaceInvalidationRect(const IntRect& aRect) {
    874  // Note that we do not transform aRect into an UnorientedIntRect, since
    875  // RasterImage::NotifyProgress notifies all consumers of the image using
    876  // OrientedIntRect values.  (This is unlike OrientedImage, which notifies
    877  // using inner image coordinates.)
    878  return aRect;
    879 }
    880 
    881 nsresult RasterImage::OnImageDataComplete(nsIRequest*, nsresult aStatus,
    882                                          bool aLastPart) {
    883  MOZ_ASSERT(NS_IsMainThread());
    884 
    885  // Record that we have all the data we're going to get now.
    886  StoreAllSourceData(true);
    887 
    888  // Let decoders know that there won't be any more data coming.
    889  mSourceBuffer->Complete(aStatus);
    890 
    891  // Allow a synchronous metadata decode if mSyncLoad was set, or if we're
    892  // running on a single thread (in which case waiting for the async metadata
    893  // decoder could delay this image's load event quite a bit), or if this image
    894  // is transient.
    895  bool canSyncDecodeMetadata =
    896      LoadSyncLoad() || LoadTransient() || DecodePool::NumberOfCores() < 2;
    897 
    898  if (canSyncDecodeMetadata && !LoadHasSize()) {
    899    // We're loading this image synchronously, so it needs to be usable after
    900    // this call returns.  Since we haven't gotten our size yet, we need to do a
    901    // synchronous metadata decode here.
    902    DecodeMetadata(FLAG_SYNC_DECODE);
    903  }
    904 
    905  // Determine our final status, giving precedence to Necko failure codes. We
    906  // check after running the metadata decode in case it triggered an error.
    907  nsresult finalStatus = mError ? NS_ERROR_FAILURE : NS_OK;
    908  if (NS_FAILED(aStatus)) {
    909    finalStatus = aStatus;
    910  }
    911 
    912  // If loading failed, report an error.
    913  if (NS_FAILED(finalStatus)) {
    914    DoError();
    915  }
    916 
    917  Progress loadProgress = LoadCompleteProgress(aLastPart, mError, finalStatus);
    918 
    919  if (!LoadHasSize() && !mError) {
    920    // We don't have our size yet, so we'll fire the load event in SetSize().
    921    MOZ_ASSERT(!canSyncDecodeMetadata,
    922               "Firing load async after metadata sync decode?");
    923    mLoadProgress = Some(loadProgress);
    924    return finalStatus;
    925  }
    926 
    927  NotifyForLoadEvent(loadProgress);
    928 
    929  return finalStatus;
    930 }
    931 
    932 void RasterImage::NotifyForLoadEvent(Progress aProgress) {
    933  MOZ_ASSERT(LoadHasSize() || mError,
    934             "Need to know size before firing load event");
    935  MOZ_ASSERT(
    936      !LoadHasSize() || (mProgressTracker->GetProgress() & FLAG_SIZE_AVAILABLE),
    937      "Should have notified that the size is available if we have it");
    938 
    939  // If we encountered an error, make sure we notify for that as well.
    940  if (mError) {
    941    aProgress |= FLAG_HAS_ERROR;
    942  }
    943 
    944  // Notify our listeners, which will fire this image's load event.
    945  NotifyProgress(aProgress);
    946 }
    947 
    948 nsresult RasterImage::OnImageDataAvailable(nsIRequest*,
    949                                           nsIInputStream* aInputStream,
    950                                           uint64_t, uint32_t aCount) {
    951  nsresult rv = mSourceBuffer->AppendFromInputStream(aInputStream, aCount);
    952  if (NS_SUCCEEDED(rv) && !LoadSomeSourceData()) {
    953    StoreSomeSourceData(true);
    954    if (!LoadSyncLoad()) {
    955      // Create an async metadata decoder and verify we succeed in doing so.
    956      rv = DecodeMetadata(DECODE_FLAGS_DEFAULT);
    957    }
    958  }
    959 
    960  if (NS_FAILED(rv)) {
    961    DoError();
    962  }
    963  return rv;
    964 }
    965 
    966 nsresult RasterImage::SetSourceSizeHint(uint32_t aSizeHint) {
    967  if (aSizeHint == 0) {
    968    return NS_OK;
    969  }
    970 
    971  nsresult rv = mSourceBuffer->ExpectLength(aSizeHint);
    972  if (rv == NS_ERROR_OUT_OF_MEMORY) {
    973    // Flush memory, try to get some back, and try again.
    974    rv = nsMemory::HeapMinimize(true);
    975    if (NS_SUCCEEDED(rv)) {
    976      rv = mSourceBuffer->ExpectLength(aSizeHint);
    977    }
    978  }
    979 
    980  return rv;
    981 }
    982 
    983 nsresult RasterImage::GetHotspotX(int32_t* aX) {
    984  *aX = mHotspot.x;
    985  return NS_OK;
    986 }
    987 
    988 nsresult RasterImage::GetHotspotY(int32_t* aY) {
    989  *aY = mHotspot.y;
    990  return NS_OK;
    991 }
    992 
    993 void RasterImage::Discard() {
    994  MOZ_ASSERT(NS_IsMainThread());
    995  MOZ_ASSERT(CanDiscard(), "Asked to discard but can't");
    996  MOZ_ASSERT(!mAnimationState ||
    997                 StaticPrefs::image_mem_animated_discardable_AtStartup(),
    998             "Asked to discard for animated image");
    999 
   1000  // Delete all the decoded frames.
   1001  SurfaceCache::RemoveImage(ImageKey(this));
   1002 
   1003  if (mAnimationState) {
   1004    IntRect rect = mAnimationState->UpdateState(this, mSize.ToUnknownSize());
   1005 
   1006    auto dirtyRect = OrientedIntRect::FromUnknownRect(rect);
   1007    NotifyProgress(NoProgress, dirtyRect);
   1008  }
   1009 
   1010  // Notify that we discarded.
   1011  if (mProgressTracker) {
   1012    mProgressTracker->OnDiscard();
   1013  }
   1014 }
   1015 
   1016 bool RasterImage::CanDiscard() {
   1017  return LoadAllSourceData() &&
   1018         // Can discard animated images if the pref is set
   1019         (!mAnimationState ||
   1020          StaticPrefs::image_mem_animated_discardable_AtStartup());
   1021 }
   1022 
   1023 NS_IMETHODIMP
   1024 RasterImage::StartDecoding(uint32_t aFlags, uint32_t aWhichFrame) {
   1025  if (mError) {
   1026    return NS_ERROR_FAILURE;
   1027  }
   1028 
   1029  if (!LoadHasSize()) {
   1030    StoreWantFullDecode(true);
   1031    return NS_OK;
   1032  }
   1033 
   1034  uint32_t flags = (aFlags & FLAG_ASYNC_NOTIFY) | FLAG_SYNC_DECODE_IF_FAST |
   1035                   FLAG_HIGH_QUALITY_SCALING;
   1036  return RequestDecodeForSize(mSize.ToUnknownSize(), flags, aWhichFrame);
   1037 }
   1038 
   1039 bool RasterImage::StartDecodingWithResult(uint32_t aFlags,
   1040                                          uint32_t aWhichFrame) {
   1041  if (mError) {
   1042    return false;
   1043  }
   1044 
   1045  if (!LoadHasSize()) {
   1046    StoreWantFullDecode(true);
   1047    return false;
   1048  }
   1049 
   1050  uint32_t flags = (aFlags & FLAG_ASYNC_NOTIFY) | FLAG_SYNC_DECODE_IF_FAST |
   1051                   FLAG_HIGH_QUALITY_SCALING;
   1052  LookupResult result = RequestDecodeForSizeInternal(mSize, flags, aWhichFrame);
   1053  DrawableSurface surface = std::move(result.Surface());
   1054  return surface && surface->IsFinished();
   1055 }
   1056 
   1057 bool RasterImage::HasDecodedPixels() {
   1058  LookupResult result = SurfaceCache::LookupBestMatch(
   1059      ImageKey(this),
   1060      RasterSurfaceKey(mSize.ToUnknownSize(), DefaultSurfaceFlags(),
   1061                       PlaybackType::eStatic),
   1062      /* aMarkUsed = */ false);
   1063  MatchType matchType = result.Type();
   1064  if (matchType == MatchType::NOT_FOUND || matchType == MatchType::PENDING ||
   1065      !bool(result.Surface())) {
   1066    return false;
   1067  }
   1068 
   1069  return !result.Surface()->GetDecodedRect().IsEmpty();
   1070 }
   1071 
   1072 imgIContainer::DecodeResult RasterImage::RequestDecodeWithResult(
   1073    uint32_t aFlags, uint32_t aWhichFrame) {
   1074  MOZ_ASSERT(NS_IsMainThread());
   1075 
   1076  if (mError) {
   1077    return imgIContainer::DECODE_REQUEST_FAILED;
   1078  }
   1079 
   1080  uint32_t flags = aFlags | FLAG_ASYNC_NOTIFY;
   1081  LookupResult result = RequestDecodeForSizeInternal(mSize, flags, aWhichFrame);
   1082  DrawableSurface surface = std::move(result.Surface());
   1083  if (surface && surface->IsFinished()) {
   1084    return imgIContainer::DECODE_SURFACE_AVAILABLE;
   1085  }
   1086  if (result.GetFailedToRequestDecode()) {
   1087    return imgIContainer::DECODE_REQUEST_FAILED;
   1088  }
   1089  return imgIContainer::DECODE_REQUESTED;
   1090 }
   1091 
   1092 NS_IMETHODIMP
   1093 RasterImage::RequestDecodeForSize(const IntSize& aSize, uint32_t aFlags,
   1094                                  uint32_t aWhichFrame) {
   1095  MOZ_ASSERT(NS_IsMainThread());
   1096 
   1097  if (mError) {
   1098    return NS_ERROR_FAILURE;
   1099  }
   1100 
   1101  RequestDecodeForSizeInternal(OrientedIntSize::FromUnknownSize(aSize), aFlags,
   1102                               aWhichFrame);
   1103 
   1104  return NS_OK;
   1105 }
   1106 
   1107 LookupResult RasterImage::RequestDecodeForSizeInternal(
   1108    const OrientedIntSize& aSize, uint32_t aFlags, uint32_t aWhichFrame) {
   1109  MOZ_ASSERT(NS_IsMainThread());
   1110 
   1111  if (aWhichFrame > FRAME_MAX_VALUE) {
   1112    return LookupResult(MatchType::NOT_FOUND);
   1113  }
   1114 
   1115  if (mError) {
   1116    LookupResult result = LookupResult(MatchType::NOT_FOUND);
   1117    result.SetFailedToRequestDecode();
   1118    return result;
   1119  }
   1120 
   1121  if (!LoadHasSize()) {
   1122    StoreWantFullDecode(true);
   1123    return LookupResult(MatchType::NOT_FOUND);
   1124  }
   1125 
   1126  // Decide whether to sync decode images we can decode quickly. Here we are
   1127  // explicitly trading off flashing for responsiveness in the case that we're
   1128  // redecoding an image (see bug 845147).
   1129  bool shouldSyncDecodeIfFast =
   1130      !LoadHasBeenDecoded() && (aFlags & FLAG_SYNC_DECODE_IF_FAST);
   1131 
   1132  uint32_t flags =
   1133      shouldSyncDecodeIfFast ? aFlags : aFlags & ~FLAG_SYNC_DECODE_IF_FAST;
   1134 
   1135  // Perform a frame lookup, which will implicitly start decoding if needed.
   1136  return LookupFrame(aSize, flags, ToPlaybackType(aWhichFrame),
   1137                     /* aMarkUsed = */ false);
   1138 }
   1139 
   1140 static bool LaunchDecodingTask(IDecodingTask* aTask, RasterImage* aImage,
   1141                               uint32_t aFlags, bool aHaveSourceData) {
   1142  if (aHaveSourceData) {
   1143    nsCString uri(aImage->GetURIString());
   1144 
   1145    // If we have all the data, we can sync decode if requested.
   1146    if (aFlags & imgIContainer::FLAG_SYNC_DECODE) {
   1147      DecodePool::Singleton()->SyncRunIfPossible(aTask, uri);
   1148      return true;
   1149    }
   1150 
   1151    if (aFlags & imgIContainer::FLAG_SYNC_DECODE_IF_FAST) {
   1152      return DecodePool::Singleton()->SyncRunIfPreferred(aTask, uri);
   1153    }
   1154  }
   1155 
   1156  // Perform an async decode. We also take this path if we don't have all the
   1157  // source data yet, since sync decoding is impossible in that situation.
   1158  DecodePool::Singleton()->AsyncRun(aTask);
   1159  return false;
   1160 }
   1161 
   1162 void RasterImage::Decode(const OrientedIntSize& aSize, uint32_t aFlags,
   1163                         PlaybackType aPlaybackType, bool& aOutRanSync,
   1164                         bool& aOutFailed) {
   1165  MOZ_ASSERT(NS_IsMainThread());
   1166 
   1167  if (mError) {
   1168    aOutFailed = true;
   1169    return;
   1170  }
   1171 
   1172  // If we don't have a size yet, we can't do any other decoding.
   1173  if (!LoadHasSize()) {
   1174    StoreWantFullDecode(true);
   1175    return;
   1176  }
   1177 
   1178  // We're about to decode again, which may mean that some of the previous sizes
   1179  // we've decoded at aren't useful anymore. We can allow them to expire from
   1180  // the cache by unlocking them here. When the decode finishes, it will send an
   1181  // invalidation that will cause all instances of this image to redraw. If this
   1182  // image is locked, any surfaces that are still useful will become locked
   1183  // again when LookupFrame touches them, and the remainder will eventually
   1184  // expire.
   1185  SurfaceCache::UnlockEntries(ImageKey(this));
   1186 
   1187  // Determine which flags we need to decode this image with.
   1188  DecoderFlags decoderFlags = mDefaultDecoderFlags;
   1189  if (aFlags & FLAG_ASYNC_NOTIFY) {
   1190    decoderFlags |= DecoderFlags::ASYNC_NOTIFY;
   1191  }
   1192  if (LoadTransient()) {
   1193    decoderFlags |= DecoderFlags::IMAGE_IS_TRANSIENT;
   1194  }
   1195  if (LoadHasBeenDecoded()) {
   1196    decoderFlags |= DecoderFlags::IS_REDECODE;
   1197  }
   1198  if ((aFlags & FLAG_SYNC_DECODE) || !(aFlags & FLAG_HIGH_QUALITY_SCALING)) {
   1199    // Used SurfaceCache::Lookup instead of SurfaceCache::LookupBestMatch. That
   1200    // means the caller can handle a differently sized surface to be returned
   1201    // at any point.
   1202    decoderFlags |= DecoderFlags::CANNOT_SUBSTITUTE;
   1203  }
   1204 
   1205  SurfaceFlags surfaceFlags = ToSurfaceFlags(aFlags);
   1206  if (IsOpaque()) {
   1207    // If there's no transparency, it doesn't matter whether we premultiply
   1208    // alpha or not.
   1209    surfaceFlags &= ~SurfaceFlags::NO_PREMULTIPLY_ALPHA;
   1210  }
   1211 
   1212  // Create a decoder.
   1213  RefPtr<IDecodingTask> task;
   1214  nsresult rv;
   1215  bool animated = mAnimationState && aPlaybackType == PlaybackType::eAnimated;
   1216  if (animated) {
   1217    size_t currentFrame = mAnimationState->GetCurrentAnimationFrameIndex();
   1218    rv = DecoderFactory::CreateAnimationDecoder(
   1219        mDecoderType, WrapNotNull(this), mSourceBuffer, mSize.ToUnknownSize(),
   1220        decoderFlags, surfaceFlags, currentFrame, getter_AddRefs(task));
   1221  } else {
   1222    rv = DecoderFactory::CreateDecoder(mDecoderType, WrapNotNull(this),
   1223                                       mSourceBuffer, mSize.ToUnknownSize(),
   1224                                       aSize.ToUnknownSize(), decoderFlags,
   1225                                       surfaceFlags, getter_AddRefs(task));
   1226  }
   1227 
   1228  if (rv == NS_ERROR_ALREADY_INITIALIZED) {
   1229    // We raced with an already pending decoder, and it finished before we
   1230    // managed to insert the new decoder. Pretend we did a sync call to make
   1231    // the caller lookup in the surface cache again.
   1232    MOZ_ASSERT(!task);
   1233    aOutRanSync = true;
   1234    return;
   1235  }
   1236 
   1237  if (animated) {
   1238    // We pass false for aAllowInvalidation because we may be asked to use
   1239    // async notifications. Any potential invalidation here will be sent when
   1240    // RequestRefresh is called, or NotifyDecodeComplete.
   1241 #ifdef DEBUG
   1242    IntRect rect =
   1243 #endif
   1244        mAnimationState->UpdateState(this, mSize.ToUnknownSize(), false);
   1245    MOZ_ASSERT(rect.IsEmpty());
   1246  }
   1247 
   1248  // Make sure DecoderFactory was able to create a decoder successfully.
   1249  if (NS_FAILED(rv)) {
   1250    MOZ_ASSERT(!task);
   1251    aOutFailed = true;
   1252    return;
   1253  }
   1254 
   1255  MOZ_ASSERT(task);
   1256  mDecodeCount++;
   1257 
   1258  // We're ready to decode; start the decoder.
   1259  aOutRanSync = LaunchDecodingTask(task, this, aFlags, LoadAllSourceData());
   1260 }
   1261 
   1262 NS_IMETHODIMP
   1263 RasterImage::DecodeMetadata(uint32_t aFlags) {
   1264  if (mError) {
   1265    return NS_ERROR_FAILURE;
   1266  }
   1267 
   1268  MOZ_ASSERT(!LoadHasSize(), "Should not do unnecessary metadata decodes");
   1269 
   1270  // Create a decoder.
   1271  RefPtr<IDecodingTask> task = DecoderFactory::CreateMetadataDecoder(
   1272      mDecoderType, WrapNotNull(this), mDefaultDecoderFlags, mSourceBuffer);
   1273 
   1274  // Make sure DecoderFactory was able to create a decoder successfully.
   1275  if (!task) {
   1276    return NS_ERROR_FAILURE;
   1277  }
   1278 
   1279  // We're ready to decode; start the decoder.
   1280  LaunchDecodingTask(task, this, aFlags, LoadAllSourceData());
   1281  return NS_OK;
   1282 }
   1283 
   1284 void RasterImage::RecoverFromInvalidFrames(const OrientedIntSize& aSize,
   1285                                           uint32_t aFlags) {
   1286  if (!LoadHasSize()) {
   1287    return;
   1288  }
   1289 
   1290  NS_WARNING("A RasterImage's frames became invalid. Attempting to recover...");
   1291 
   1292  // Discard all existing frames, since they're probably all now invalid.
   1293  SurfaceCache::RemoveImage(ImageKey(this));
   1294 
   1295  // Relock the image if it's supposed to be locked.
   1296  if (mLockCount > 0) {
   1297    SurfaceCache::LockImage(ImageKey(this));
   1298  }
   1299 
   1300  bool unused1, unused2;
   1301 
   1302  // Animated images require some special handling, because we normally require
   1303  // that they never be discarded.
   1304  if (mAnimationState) {
   1305    Decode(mSize, aFlags | FLAG_SYNC_DECODE, PlaybackType::eAnimated, unused1,
   1306           unused2);
   1307    ResetAnimation();
   1308    return;
   1309  }
   1310 
   1311  // For non-animated images, it's fine to recover using an async decode.
   1312  Decode(aSize, aFlags, PlaybackType::eStatic, unused1, unused2);
   1313 }
   1314 
   1315 bool RasterImage::CanDownscaleDuringDecode(const OrientedIntSize& aSize,
   1316                                           uint32_t aFlags) {
   1317  // Check basic requirements: downscale-during-decode is enabled, Skia is
   1318  // available, this image isn't transient, we have all the source data and know
   1319  // our size, and the flags allow us to do it.
   1320  if (!LoadHasSize() || LoadTransient() ||
   1321      !StaticPrefs::image_downscale_during_decode_enabled() ||
   1322      !(aFlags & imgIContainer::FLAG_HIGH_QUALITY_SCALING)) {
   1323    return false;
   1324  }
   1325 
   1326  // We don't downscale animated images during decode.
   1327  if (mAnimationState) {
   1328    return false;
   1329  }
   1330 
   1331  // Never upscale.
   1332  if (aSize.width >= mSize.width || aSize.height >= mSize.height) {
   1333    return false;
   1334  }
   1335 
   1336  // Zero or negative width or height is unacceptable.
   1337  if (aSize.width < 1 || aSize.height < 1) {
   1338    return false;
   1339  }
   1340 
   1341  // There's no point in scaling if we can't store the result.
   1342  if (!SurfaceCache::CanHold(aSize.ToUnknownSize())) {
   1343    return false;
   1344  }
   1345 
   1346  return true;
   1347 }
   1348 
   1349 ImgDrawResult RasterImage::DrawInternal(DrawableSurface&& aSurface,
   1350                                        gfxContext* aContext,
   1351                                        const OrientedIntSize& aSize,
   1352                                        const ImageRegion& aRegion,
   1353                                        SamplingFilter aSamplingFilter,
   1354                                        uint32_t aFlags, float aOpacity) {
   1355  gfxContextMatrixAutoSaveRestore saveMatrix(aContext);
   1356  ImageRegion region(aRegion);
   1357  bool frameIsFinished = aSurface->IsFinished();
   1358 
   1359  AutoProfilerImagePaintMarker PROFILER_RAII(this);
   1360 #ifdef DEBUG
   1361  NotifyDrawingObservers();
   1362 #endif
   1363 
   1364  // By now we may have a frame with the requested size. If not, we need to
   1365  // adjust the drawing parameters accordingly.
   1366  IntSize finalSize = aSurface->GetSize();
   1367  bool couldRedecodeForBetterFrame = false;
   1368  if (finalSize != aSize.ToUnknownSize()) {
   1369    gfx::MatrixScales scale(double(aSize.width) / finalSize.width,
   1370                            double(aSize.height) / finalSize.height);
   1371    aContext->Multiply(gfx::Matrix::Scaling(scale));
   1372    region.Scale(1.0 / scale.xScale, 1.0 / scale.yScale);
   1373 
   1374    couldRedecodeForBetterFrame = CanDownscaleDuringDecode(aSize, aFlags);
   1375  }
   1376 
   1377  if (!aSurface->Draw(aContext, region, aSamplingFilter, aFlags, aOpacity)) {
   1378    RecoverFromInvalidFrames(aSize, aFlags);
   1379    return ImgDrawResult::TEMPORARY_ERROR;
   1380  }
   1381  if (!frameIsFinished) {
   1382    return ImgDrawResult::INCOMPLETE;
   1383  }
   1384  if (couldRedecodeForBetterFrame) {
   1385    return ImgDrawResult::WRONG_SIZE;
   1386  }
   1387  return ImgDrawResult::SUCCESS;
   1388 }
   1389 
   1390 //******************************************************************************
   1391 NS_IMETHODIMP_(ImgDrawResult)
   1392 RasterImage::Draw(gfxContext* aContext, const IntSize& aSize,
   1393                  const ImageRegion& aRegion, uint32_t aWhichFrame,
   1394                  SamplingFilter aSamplingFilter,
   1395                  const SVGImageContext& /*aSVGContext - ignored*/,
   1396                  uint32_t aFlags, float aOpacity) {
   1397  if (aWhichFrame > FRAME_MAX_VALUE) {
   1398    return ImgDrawResult::BAD_ARGS;
   1399  }
   1400 
   1401  if (mError) {
   1402    return ImgDrawResult::BAD_IMAGE;
   1403  }
   1404 
   1405  // Illegal -- you can't draw with non-default decode flags.
   1406  // (Disabling colorspace conversion might make sense to allow, but
   1407  // we don't currently.)
   1408  if (ToSurfaceFlags(aFlags) != DefaultSurfaceFlags()) {
   1409    return ImgDrawResult::BAD_ARGS;
   1410  }
   1411 
   1412  if (!aContext) {
   1413    return ImgDrawResult::BAD_ARGS;
   1414  }
   1415 
   1416  if (mAnimationConsumers == 0 && mAnimationState) {
   1417    SendOnUnlockedDraw(aFlags);
   1418  }
   1419 
   1420  // If we're not using SamplingFilter::GOOD, we shouldn't high-quality scale or
   1421  // downscale during decode.
   1422  uint32_t flags = aSamplingFilter == SamplingFilter::GOOD
   1423                       ? aFlags
   1424                       : aFlags & ~FLAG_HIGH_QUALITY_SCALING;
   1425 
   1426  auto size = OrientedIntSize::FromUnknownSize(aSize);
   1427  LookupResult result = LookupFrame(size, flags, ToPlaybackType(aWhichFrame),
   1428                                    /* aMarkUsed = */ true);
   1429  if (!result) {
   1430    // Getting the frame (above) touches the image and kicks off decoding.
   1431    if (mDrawStartTime.IsNull()) {
   1432      mDrawStartTime = TimeStamp::Now();
   1433    }
   1434    return ImgDrawResult::NOT_READY;
   1435  }
   1436 
   1437  bool shouldRecordTelemetry =
   1438      !mDrawStartTime.IsNull() && result.Surface()->IsFinished();
   1439 
   1440  ImgDrawResult drawResult =
   1441      DrawInternal(std::move(result.Surface()), aContext, size, aRegion,
   1442                   aSamplingFilter, flags, aOpacity);
   1443 
   1444  if (shouldRecordTelemetry) {
   1445    TimeDuration drawLatency = TimeStamp::Now() - mDrawStartTime;
   1446    glean::image_decode::on_draw_latency.AccumulateRawDuration(drawLatency);
   1447    mDrawStartTime = TimeStamp();
   1448  }
   1449 
   1450  return drawResult;
   1451 }
   1452 
   1453 //******************************************************************************
   1454 
   1455 NS_IMETHODIMP
   1456 RasterImage::LockImage() {
   1457  MOZ_ASSERT(NS_IsMainThread(),
   1458             "Main thread to encourage serialization with UnlockImage");
   1459  if (mError) {
   1460    return NS_ERROR_FAILURE;
   1461  }
   1462 
   1463  // Increment the lock count
   1464  mLockCount++;
   1465 
   1466  // Lock this image's surfaces in the SurfaceCache.
   1467  if (mLockCount == 1) {
   1468    SurfaceCache::LockImage(ImageKey(this));
   1469  }
   1470 
   1471  return NS_OK;
   1472 }
   1473 
   1474 //******************************************************************************
   1475 
   1476 NS_IMETHODIMP
   1477 RasterImage::UnlockImage() {
   1478  MOZ_ASSERT(NS_IsMainThread(),
   1479             "Main thread to encourage serialization with LockImage");
   1480  if (mError) {
   1481    return NS_ERROR_FAILURE;
   1482  }
   1483 
   1484  // It's an error to call this function if the lock count is 0
   1485  MOZ_ASSERT(mLockCount > 0, "Calling UnlockImage with mLockCount == 0!");
   1486  if (mLockCount == 0) {
   1487    return NS_ERROR_ABORT;
   1488  }
   1489 
   1490  // Decrement our lock count
   1491  mLockCount--;
   1492 
   1493  // Unlock this image's surfaces in the SurfaceCache.
   1494  if (mLockCount == 0) {
   1495    SurfaceCache::UnlockImage(ImageKey(this));
   1496  }
   1497 
   1498  return NS_OK;
   1499 }
   1500 
   1501 //******************************************************************************
   1502 
   1503 NS_IMETHODIMP
   1504 RasterImage::RequestDiscard() {
   1505  if (LoadDiscardable() &&  // Enabled at creation time...
   1506      mLockCount == 0 &&    // ...not temporarily disabled...
   1507      CanDiscard()) {
   1508    Discard();
   1509  }
   1510 
   1511  return NS_OK;
   1512 }
   1513 
   1514 // Idempotent error flagging routine. If a decoder is open, shuts it down.
   1515 void RasterImage::DoError() {
   1516  // If we've flagged an error before, we have nothing to do
   1517  if (mError) {
   1518    return;
   1519  }
   1520 
   1521  // We can't safely handle errors off-main-thread, so dispatch a worker to
   1522  // do it.
   1523  if (!NS_IsMainThread()) {
   1524    HandleErrorWorker::DispatchIfNeeded(this);
   1525    return;
   1526  }
   1527 
   1528  // Put the container in an error state.
   1529  mError = true;
   1530 
   1531  // Stop animation and release our FrameAnimator.
   1532  if (mAnimating) {
   1533    StopAnimation();
   1534  }
   1535  mAnimationState = Nothing();
   1536  mFrameAnimator = nullptr;
   1537 
   1538  // Release all locks.
   1539  mLockCount = 0;
   1540  SurfaceCache::UnlockImage(ImageKey(this));
   1541 
   1542  // Release all frames from the surface cache.
   1543  SurfaceCache::RemoveImage(ImageKey(this));
   1544 
   1545  // Invalidate to get rid of any partially-drawn image content.
   1546  auto dirtyRect = OrientedIntRect({0, 0}, mSize);
   1547  // Make sure to provide a non-empty rect so a FRAME_UPDATE notification goes
   1548  // out otherwise consumers might not get any kind of update whatsoever.
   1549  if (dirtyRect.IsEmpty()) {
   1550    dirtyRect.width = dirtyRect.height = 1;
   1551  }
   1552  NotifyProgress(NoProgress, dirtyRect);
   1553 
   1554  MOZ_LOG(gImgLog, LogLevel::Error,
   1555          ("RasterImage: [this=%p] Error detected for image\n", this));
   1556 }
   1557 
   1558 /* static */
   1559 void RasterImage::HandleErrorWorker::DispatchIfNeeded(RasterImage* aImage) {
   1560  RefPtr<HandleErrorWorker> worker = new HandleErrorWorker(aImage);
   1561  NS_DispatchToMainThread(worker);
   1562 }
   1563 
   1564 RasterImage::HandleErrorWorker::HandleErrorWorker(RasterImage* aImage)
   1565    : Runnable("image::RasterImage::HandleErrorWorker"), mImage(aImage) {
   1566  MOZ_ASSERT(mImage, "Should have image");
   1567 }
   1568 
   1569 NS_IMETHODIMP
   1570 RasterImage::HandleErrorWorker::Run() {
   1571  mImage->DoError();
   1572 
   1573  return NS_OK;
   1574 }
   1575 
   1576 bool RasterImage::ShouldAnimate() {
   1577  return ImageResource::ShouldAnimate() && mAnimationState &&
   1578         mAnimationState->KnownFrameCount() >= 1 && !LoadAnimationFinished();
   1579 }
   1580 
   1581 #ifdef DEBUG
   1582 NS_IMETHODIMP
   1583 RasterImage::GetFramesNotified(uint32_t* aFramesNotified) {
   1584  NS_ENSURE_ARG_POINTER(aFramesNotified);
   1585 
   1586  *aFramesNotified = mFramesNotified;
   1587 
   1588  return NS_OK;
   1589 }
   1590 #endif
   1591 
   1592 void RasterImage::NotifyProgress(
   1593    Progress aProgress,
   1594    const OrientedIntRect& aInvalidRect /* = OrientedIntRect() */,
   1595    const Maybe<uint32_t>& aFrameCount /* = Nothing() */,
   1596    DecoderFlags aDecoderFlags /* = DefaultDecoderFlags() */,
   1597    SurfaceFlags aSurfaceFlags /* = DefaultSurfaceFlags() */) {
   1598  MOZ_ASSERT(NS_IsMainThread());
   1599 
   1600  // Ensure that we stay alive long enough to finish notifying.
   1601  RefPtr<RasterImage> image = this;
   1602 
   1603  OrientedIntRect invalidRect = aInvalidRect;
   1604 
   1605  if (!(aDecoderFlags & DecoderFlags::FIRST_FRAME_ONLY)) {
   1606    // We may have decoded new animation frames; update our animation state.
   1607    MOZ_ASSERT_IF(aFrameCount && *aFrameCount > 1, mAnimationState || mError);
   1608    if (mAnimationState && aFrameCount) {
   1609      mAnimationState->UpdateKnownFrameCount(*aFrameCount);
   1610    }
   1611 
   1612    // If we should start animating right now, do so.
   1613    if (mAnimationState && aFrameCount == Some(1u) && LoadPendingAnimation() &&
   1614        ShouldAnimate()) {
   1615      StartAnimation();
   1616    }
   1617 
   1618    if (mAnimationState) {
   1619      IntRect rect = mAnimationState->UpdateState(this, mSize.ToUnknownSize());
   1620 
   1621      invalidRect.UnionRect(invalidRect,
   1622                            OrientedIntRect::FromUnknownRect(rect));
   1623    }
   1624  }
   1625 
   1626  // Tell the observers what happened.
   1627  image->mProgressTracker->SyncNotifyProgress(aProgress,
   1628                                              invalidRect.ToUnknownRect());
   1629 }
   1630 
   1631 void RasterImage::NotifyDecodeComplete(
   1632    const DecoderFinalStatus& aStatus, const ImageMetadata& aMetadata,
   1633    const DecoderTelemetry& aTelemetry, Progress aProgress,
   1634    const OrientedIntRect& aInvalidRect, const Maybe<uint32_t>& aFrameCount,
   1635    DecoderFlags aDecoderFlags, SurfaceFlags aSurfaceFlags) {
   1636  MOZ_ASSERT(NS_IsMainThread());
   1637 
   1638  // If the decoder detected an error, log it to the error console.
   1639  if (aStatus.mShouldReportError) {
   1640    ReportDecoderError();
   1641  }
   1642 
   1643  // Record all the metadata the decoder gathered about this image.
   1644  bool metadataOK = SetMetadata(aMetadata, aStatus.mWasMetadataDecode);
   1645  if (!metadataOK) {
   1646    // This indicates a serious error that requires us to discard all existing
   1647    // surfaces and redecode to recover. We'll drop the results from this
   1648    // decoder on the floor, since they aren't valid.
   1649    RecoverFromInvalidFrames(mSize, FromSurfaceFlags(aSurfaceFlags));
   1650    return;
   1651  }
   1652 
   1653  MOZ_ASSERT(mError || LoadHasSize() || !aMetadata.HasSize(),
   1654             "SetMetadata should've gotten a size");
   1655 
   1656  if (!aStatus.mWasMetadataDecode && aStatus.mFinished) {
   1657    // Flag that we've been decoded before.
   1658    StoreHasBeenDecoded(true);
   1659  }
   1660 
   1661  // Send out any final notifications.
   1662  NotifyProgress(aProgress, aInvalidRect, aFrameCount, aDecoderFlags,
   1663                 aSurfaceFlags);
   1664 
   1665  if (!(aDecoderFlags & DecoderFlags::FIRST_FRAME_ONLY)) {
   1666    // We may have decoded new animation frames; update our animation state.
   1667    MOZ_ASSERT_IF(aFrameCount && *aFrameCount > 1, mAnimationState || mError);
   1668    if (mAnimationState && aFrameCount) {
   1669      mAnimationState->UpdateKnownFrameCount(*aFrameCount);
   1670    }
   1671 
   1672    // If we should start animating right now, do so.
   1673    if (mAnimationState && aFrameCount == Some(1u) && LoadPendingAnimation() &&
   1674        ShouldAnimate()) {
   1675      StartAnimation();
   1676    }
   1677 
   1678    if (mAnimationState && LoadHasBeenDecoded()) {
   1679      // We've finished a full decode of all animation frames and our
   1680      // AnimationState has been notified about them all, so let it know not to
   1681      // expect anymore.
   1682      mAnimationState->NotifyDecodeComplete();
   1683 
   1684      IntRect rect = mAnimationState->UpdateState(this, mSize.ToUnknownSize());
   1685 
   1686      if (!rect.IsEmpty()) {
   1687        auto dirtyRect = OrientedIntRect::FromUnknownRect(rect);
   1688        NotifyProgress(NoProgress, dirtyRect);
   1689      }
   1690    }
   1691  }
   1692 
   1693  // Do some telemetry if this isn't a metadata decode.
   1694  if (!aStatus.mWasMetadataDecode) {
   1695    if (aTelemetry.mChunkCount) {
   1696      glean::image_decode::chunks.AccumulateSingleSample(
   1697          aTelemetry.mChunkCount);
   1698    }
   1699 
   1700    if (aStatus.mFinished) {
   1701      glean::image_decode::time.AccumulateRawDuration(aTelemetry.mDecodeTime);
   1702 
   1703      if (aTelemetry.mSpeedMetric && aTelemetry.mBytesDecoded) {
   1704        (*aTelemetry.mSpeedMetric).Accumulate(aTelemetry.Speed());
   1705      }
   1706    }
   1707  }
   1708 
   1709  // Only act on errors if we have no usable frames from the decoder.
   1710  if (aStatus.mHadError &&
   1711      (!mAnimationState || mAnimationState->KnownFrameCount() == 0)) {
   1712    DoError();
   1713  } else if (aStatus.mWasMetadataDecode && !LoadHasSize()) {
   1714    DoError();
   1715  }
   1716 
   1717  // XXX(aosmond): Can we get this far without mFinished == true?
   1718  if (aStatus.mFinished && aStatus.mWasMetadataDecode) {
   1719    // If we were waiting to fire the load event, go ahead and fire it now.
   1720    if (mLoadProgress) {
   1721      NotifyForLoadEvent(*mLoadProgress);
   1722      mLoadProgress = Nothing();
   1723    }
   1724 
   1725    // If we were a metadata decode and a full decode was requested, do it.
   1726    if (LoadWantFullDecode()) {
   1727      StoreWantFullDecode(false);
   1728      RequestDecodeForSizeInternal(mSize,
   1729                                   DECODE_FLAGS_DEFAULT |
   1730                                       FLAG_HIGH_QUALITY_SCALING |
   1731                                       FLAG_AVOID_REDECODE_FOR_SIZE,
   1732                                   FRAME_CURRENT);
   1733    }
   1734  }
   1735 }
   1736 
   1737 void RasterImage::ReportDecoderError() {
   1738  nsCOMPtr<nsIConsoleService> consoleService =
   1739      do_GetService(NS_CONSOLESERVICE_CONTRACTID);
   1740  nsCOMPtr<nsIScriptError> errorObject =
   1741      do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
   1742 
   1743  if (consoleService && errorObject) {
   1744    nsAutoString msg(u"Image corrupt or truncated."_ns);
   1745    nsAutoCString src;
   1746    if (GetURI()) {
   1747      if (!GetSpecTruncatedTo1k(src)) {
   1748        msg += u" URI in this note truncated due to length."_ns;
   1749      }
   1750    }
   1751    if (NS_SUCCEEDED(errorObject->InitWithWindowID(msg, src, 0, 0,
   1752                                                   nsIScriptError::errorFlag,
   1753                                                   "Image", InnerWindowID()))) {
   1754      consoleService->LogMessage(errorObject);
   1755    }
   1756  }
   1757 }
   1758 
   1759 already_AddRefed<imgIContainer> RasterImage::Unwrap() {
   1760  nsCOMPtr<imgIContainer> self(this);
   1761  return self.forget();
   1762 }
   1763 
   1764 void RasterImage::PropagateUseCounters(dom::Document*) {
   1765  // No use counters.
   1766 }
   1767 
   1768 IntSize RasterImage::OptimalImageSizeForDest(const gfxSize& aDest,
   1769                                             uint32_t aWhichFrame,
   1770                                             SamplingFilter aSamplingFilter,
   1771                                             uint32_t aFlags) {
   1772  MOZ_ASSERT(aDest.width >= 0 || ceil(aDest.width) <= INT32_MAX ||
   1773                 aDest.height >= 0 || ceil(aDest.height) <= INT32_MAX,
   1774             "Unexpected destination size");
   1775 
   1776  if (mSize.IsEmpty() || aDest.IsEmpty()) {
   1777    return IntSize(0, 0);
   1778  }
   1779 
   1780  auto dest = OrientedIntSize::FromUnknownSize(
   1781      IntSize::Ceil(aDest.width, aDest.height));
   1782 
   1783  if (aSamplingFilter == SamplingFilter::GOOD &&
   1784      CanDownscaleDuringDecode(dest, aFlags)) {
   1785    return dest.ToUnknownSize();
   1786  }
   1787 
   1788  // We can't scale to this size. Use our intrinsic size for now.
   1789  return mSize.ToUnknownSize();
   1790 }
   1791 
   1792 }  // namespace image
   1793 }  // namespace mozilla