/* static */ void
IDecodingTask::NotifyDecodeComplete(NotNull<RasterImage*> aImage,
                                    NotNull<Decoder*> aDecoder)
{
  MOZ_ASSERT(aDecoder->HasError() || !aDecoder->InFrame(),
             "Decode complete in the middle of a frame?");

  // Capture the decoder's state.
  DecoderFinalStatus finalStatus = aDecoder->FinalStatus();
  ImageMetadata metadata = aDecoder->GetImageMetadata();
  DecoderTelemetry telemetry = aDecoder->Telemetry();
  Progress progress = aDecoder->TakeProgress();
  IntRect invalidRect = aDecoder->TakeInvalidRect();
  Maybe<uint32_t> frameCount = aDecoder->TakeCompleteFrameCount();
  SurfaceFlags surfaceFlags = aDecoder->GetSurfaceFlags();

  // Synchronously notify if we can.
  if (NS_IsMainThread() &&
      !(aDecoder->GetDecoderFlags() & DecoderFlags::ASYNC_NOTIFY)) {
    aImage->NotifyDecodeComplete(finalStatus, metadata, telemetry, progress,
                                 invalidRect, frameCount, surfaceFlags);
    return;
  }

  // We're forced to notify asynchronously.
  NotNull<RefPtr<RasterImage>> image = aImage;
  NS_DispatchToMainThread(NS_NewRunnableFunction([=]() -> void {
    image->NotifyDecodeComplete(finalStatus, metadata, telemetry, progress,
                                invalidRect, frameCount, surfaceFlags);
  }));
}
/* static */ void
IDecodingTask::NotifyProgress(NotNull<RasterImage*> aImage,
                              NotNull<Decoder*> aDecoder)
{
  MOZ_ASSERT(aDecoder->HasProgress() && !aDecoder->IsMetadataDecode());

  // Capture the decoder's state. If we need to notify asynchronously, it's
  // important that we don't wait until the lambda actually runs to capture the
  // state that we're going to notify. That would both introduce data races on
  // the decoder's state and cause inconsistencies between the NotifyProgress()
  // calls we make off-main-thread and the notifications that RasterImage
  // actually receives, which would cause bugs.
  Progress progress = aDecoder->TakeProgress();
  IntRect invalidRect = aDecoder->TakeInvalidRect();
  Maybe<uint32_t> frameCount = aDecoder->TakeCompleteFrameCount();
  SurfaceFlags surfaceFlags = aDecoder->GetSurfaceFlags();

  // Synchronously notify if we can.
  if (NS_IsMainThread() &&
      !(aDecoder->GetDecoderFlags() & DecoderFlags::ASYNC_NOTIFY)) {
    aImage->NotifyProgress(progress, invalidRect,
                           frameCount, surfaceFlags);
    return;
  }

  // We're forced to notify asynchronously.
  NotNull<RefPtr<RasterImage>> image = aImage;
  NS_DispatchToMainThread(NS_NewRunnableFunction([=]() -> void {
    image->NotifyProgress(progress, invalidRect,
                          frameCount, surfaceFlags);
  }));
}
static void
NotifyProgress(NotNull<Decoder*> aDecoder)
{
  MOZ_ASSERT(aDecoder->HasProgress() && !aDecoder->IsMetadataDecode());

  Progress progress = aDecoder->TakeProgress();
  IntRect invalidRect = aDecoder->TakeInvalidRect();
  Maybe<uint32_t> frameCount = aDecoder->TakeCompleteFrameCount();
  SurfaceFlags surfaceFlags = aDecoder->GetSurfaceFlags();

  // Synchronously notify if we can.
  if (NS_IsMainThread() &&
      !(aDecoder->GetDecoderFlags() & DecoderFlags::ASYNC_NOTIFY)) {
    aDecoder->GetImage()->NotifyProgress(progress, invalidRect,
                                         frameCount, surfaceFlags);
    return;
  }

  // We're forced to notify asynchronously.
  NotNull<RefPtr<Decoder>> decoder = aDecoder;
  NS_DispatchToMainThread(NS_NewRunnableFunction([=]() -> void {
    decoder->GetImage()->NotifyProgress(progress, invalidRect,
                                        frameCount, surfaceFlags);
  }));
}
/* static */ already_AddRefed<Decoder>
DecoderFactory::CreateDecoderForICOResource(DecoderType aType,
                                            SourceBufferIterator&& aIterator,
                                            NotNull<nsICODecoder*> aICODecoder,
                                            bool aIsMetadataDecode,
                                            const Maybe<IntSize>& aExpectedSize,
                                            const Maybe<uint32_t>& aDataOffset
                                              /* = Nothing() */)
{
  // Create the decoder.
  RefPtr<Decoder> decoder;
  switch (aType) {
    case DecoderType::BMP:
      MOZ_ASSERT(aDataOffset);
      decoder = new nsBMPDecoder(aICODecoder->GetImageMaybeNull(), *aDataOffset);
      break;

    case DecoderType::PNG:
      MOZ_ASSERT(!aDataOffset);
      decoder = new nsPNGDecoder(aICODecoder->GetImageMaybeNull());
      break;

    default:
      MOZ_ASSERT_UNREACHABLE("Invalid ICO resource decoder type");
      return nullptr;
  }

  MOZ_ASSERT(decoder);

  // Initialize the decoder, copying settings from @aICODecoder.
  decoder->SetMetadataDecode(aIsMetadataDecode);
  decoder->SetIterator(Forward<SourceBufferIterator>(aIterator));
  if (!aIsMetadataDecode) {
    decoder->SetOutputSize(aICODecoder->OutputSize());
  }
  if (aExpectedSize) {
    decoder->SetExpectedSize(*aExpectedSize);
  }
  decoder->SetDecoderFlags(aICODecoder->GetDecoderFlags());
  decoder->SetSurfaceFlags(aICODecoder->GetSurfaceFlags());
  decoder->SetFinalizeFrames(false);

  if (NS_FAILED(decoder->Init())) {
    return nullptr;
  }

  return decoder.forget();
}
static void
NotifyDecodeComplete(NotNull<Decoder*> aDecoder)
{
  // Synchronously notify if we can.
  if (NS_IsMainThread() &&
      !(aDecoder->GetDecoderFlags() & DecoderFlags::ASYNC_NOTIFY)) {
    aDecoder->GetImage()->FinalizeDecoder(aDecoder);
    return;
  }

  // We're forced to notify asynchronously.
  NotNull<RefPtr<Decoder>> decoder = aDecoder;
  NS_DispatchToMainThread(NS_NewRunnableFunction([=]() -> void {
    decoder->GetImage()->FinalizeDecoder(decoder.get());
  }));
}
/* static */ void
IDecodingTask::NotifyProgress(NotNull<Decoder*> aDecoder)
{
  MOZ_ASSERT(aDecoder->HasProgress() && !aDecoder->IsMetadataDecode());

  // Synchronously notify if we can.
  if (NS_IsMainThread() &&
      !(aDecoder->GetDecoderFlags() & DecoderFlags::ASYNC_NOTIFY)) {
    aDecoder->GetImage()->NotifyProgress(aDecoder->TakeProgress(),
                                         aDecoder->TakeInvalidRect(),
                                         aDecoder->GetSurfaceFlags());
    return;
  }

  // We're forced to notify asynchronously.
  NotNull<RefPtr<Decoder>> decoder = aDecoder;
  NS_DispatchToMainThread(NS_NewRunnableFunction([=]() -> void {
    decoder->GetImage()->NotifyProgress(decoder->TakeProgress(),
                                        decoder->TakeInvalidRect(),
                                        decoder->GetSurfaceFlags());
  }));
}