Bug 1285867 (Part 1) - Remove Decoder's notion of decoder errors. r=edwin
authorSeth Fowler <mark.seth.fowler@gmail.com>
Mon, 11 Jul 2016 00:34:50 -0700
changeset 345191 77fe4e663e345389c4260ea573dbf58afa85da1b
parent 345190 86a5424096d537c318dd3d90b9f69c3dc6c85bdb
child 345192 8c5e700ca1aefa0ff382cbb060b4184a182162ac
push id6389
push userraliiev@mozilla.com
push dateMon, 19 Sep 2016 13:38:22 +0000
treeherdermozilla-beta@01d67bfe6c81 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersedwin
bugs1285867
milestone50.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1285867 (Part 1) - Remove Decoder's notion of decoder errors. r=edwin
image/Decoder.cpp
image/Decoder.h
image/DecoderFactory.cpp
image/RasterImage.cpp
image/decoders/nsICODecoder.cpp
image/decoders/nsJPEGDecoder.cpp
image/decoders/nsJPEGDecoder.h
image/decoders/nsPNGDecoder.cpp
image/decoders/nsPNGDecoder.h
--- a/image/Decoder.cpp
+++ b/image/Decoder.cpp
@@ -53,17 +53,16 @@ private:
 Decoder::Decoder(RasterImage* aImage)
   : mImageData(nullptr)
   , mImageDataLength(0)
   , mColormap(nullptr)
   , mColormapSize(0)
   , mImage(aImage)
   , mProgress(NoProgress)
   , mFrameCount(0)
-  , mFailCode(NS_OK)
   , mChunkCount(0)
   , mDecoderFlags(DefaultDecoderFlags())
   , mSurfaceFlags(DefaultSurfaceFlags())
   , mBytesDecoded(0)
   , mInitialized(false)
   , mMetadataDecode(false)
   , mInFrame(false)
   , mDataDone(false)
@@ -87,34 +86,36 @@ Decoder::~Decoder()
     NS_ReleaseOnMainThread(mImage.forget());
   }
 }
 
 /*
  * Common implementation of the decoder interface.
  */
 
-void
+nsresult
 Decoder::Init()
 {
   // No re-initializing
   MOZ_ASSERT(!mInitialized, "Can't re-initialize a decoder!");
 
   // All decoders must have a SourceBufferIterator.
   MOZ_ASSERT(mIterator);
 
   // It doesn't make sense to decode anything but the first frame if we can't
   // store anything in the SurfaceCache, since only the last frame we decode
   // will be retrievable.
   MOZ_ASSERT(ShouldUseSurfaceCache() || IsFirstFrameDecode());
 
-  // Implementation-specific initialization
-  InitInternal();
+  // Implementation-specific initialization.
+  nsresult rv = InitInternal();
 
   mInitialized = true;
+
+  return rv;
 }
 
 nsresult
 Decoder::Decode(NotNull<IResumable*> aOnResume)
 {
   MOZ_ASSERT(mInitialized, "Should be initialized here");
   MOZ_ASSERT(mIterator, "Should have a SourceBufferIterator");
 
@@ -191,19 +192,19 @@ Decoder::CompleteDecode()
 
   // If PostDecodeDone() has not been called, and this decoder wasn't aborted
   // early because of low-memory conditions or losing a race with another
   // decoder, we need to send teardown notifications (and report an error to the
   // console later).
   if (!IsMetadataDecode() && !mDecodeDone && !WasAborted()) {
     mShouldReportError = true;
 
-    // If we only have a data error, we're usable if we have at least one
-    // complete frame.
-    if (!HasDecoderError() && GetCompleteFrameCount() > 0) {
+    // Even if we encountered an error, we're still usable if we have at least
+    // one complete frame.
+    if (GetCompleteFrameCount() > 0) {
       // We're usable, so do exactly what we should have when the decoder
       // completed.
 
       // Not writing to the entire frame may have left us transparent.
       PostHasTransparency();
 
       if (mInFrame) {
         PostFrameStop();
@@ -286,17 +287,17 @@ Decoder::AllocateFrame(uint32_t aFrameNu
 RawAccessFrameRef
 Decoder::AllocateFrameInternal(uint32_t aFrameNum,
                                const nsIntSize& aTargetSize,
                                const nsIntRect& aFrameRect,
                                SurfaceFormat aFormat,
                                uint8_t aPaletteDepth,
                                imgFrame* aPreviousFrame)
 {
-  if (mDataError || NS_FAILED(mFailCode)) {
+  if (HasError()) {
     return RawAccessFrameRef();
   }
 
   if (aFrameNum != mFrameCount) {
     MOZ_ASSERT_UNREACHABLE("Allocating frames out of order");
     return RawAccessFrameRef();
   }
 
@@ -383,17 +384,17 @@ Decoder::AllocateFrameInternal(uint32_t 
 
   return ref;
 }
 
 /*
  * Hook stubs. Override these as necessary in decoder implementations.
  */
 
-void Decoder::InitInternal() { }
+nsresult Decoder::InitInternal() { return NS_OK; }
 void Decoder::BeforeFinishInternal() { }
 void Decoder::FinishInternal() { }
 void Decoder::FinishWithErrorInternal() { }
 
 /*
  * Progress Notifications
  */
 
@@ -498,32 +499,16 @@ Decoder::PostDataError()
 {
   mDataError = true;
 
   if (mInFrame && mCurrentFrame) {
     mCurrentFrame->Abort();
   }
 }
 
-void
-Decoder::PostDecoderError(nsresult aFailureCode)
-{
-  MOZ_ASSERT(NS_FAILED(aFailureCode), "Not a failure code!");
-
-  mFailCode = aFailureCode;
-
-  // XXXbholley - we should report the image URI here, but imgContainer
-  // needs to know its URI first
-  NS_WARNING("Image decoding error - This is probably a bug!");
-
-  if (mInFrame && mCurrentFrame) {
-    mCurrentFrame->Abort();
-  }
-}
-
 Telemetry::ID
 Decoder::SpeedHistogram()
 {
   // Use HistogramCount as an invalid Histogram ID.
   return Telemetry::HistogramCount;
 }
 
 } // namespace image
--- a/image/Decoder.h
+++ b/image/Decoder.h
@@ -32,18 +32,20 @@ class Decoder
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Decoder)
 
   explicit Decoder(RasterImage* aImage);
 
   /**
    * Initialize an image decoder. Decoders may not be re-initialized.
+   *
+   * @return NS_OK if the decoder could be initialized successfully.
    */
-  void Init();
+  nsresult Init();
 
   /**
    * Decodes, reading all data currently available in the SourceBuffer.
    *
    * If more data is needed, Decode() will schedule @aOnResume to be called when
    * more data is available.
    *
    * Any errors are reported by setting the appropriate state on the decoder.
@@ -179,21 +181,19 @@ public:
   uint32_t GetCompleteFrameCount() {
     return mInFrame ? mFrameCount - 1 : mFrameCount;
   }
 
   // Did we discover that the image we're decoding is animated?
   bool HasAnimation() const { return mImageMetadata.HasAnimation(); }
 
   // Error tracking
-  bool HasError() const { return HasDataError() || HasDecoderError(); }
+  bool HasError() const { return HasDataError(); }
   bool HasDataError() const { return mDataError; }
-  bool HasDecoderError() const { return NS_FAILED(mFailCode); }
   bool ShouldReportError() const { return mShouldReportError; }
-  nsresult GetDecoderError() const { return mFailCode; }
 
   /// Did we finish decoding enough that calling Decode() again would be useless?
   bool GetDecodeDone() const
   {
     return mDecodeDone || (mMetadataDecode && HasSize()) ||
            HasError() || mDataDone;
   }
 
@@ -284,17 +284,17 @@ protected:
   /*
    * Internal hooks. Decoder implementations may override these and
    * only these methods.
    *
    * BeforeFinishInternal() can be used to detect if decoding is in an
    * incomplete state, e.g. due to file truncation, in which case it should
    * call PostDataError().
    */
-  virtual void InitInternal();
+  virtual nsresult InitInternal();
   virtual Maybe<TerminalState> DoDecode(SourceBufferIterator& aIterator) = 0;
   virtual void BeforeFinishInternal();
   virtual void FinishInternal();
   virtual void FinishWithErrorInternal();
 
   /*
    * Progress notifications.
    */
@@ -353,19 +353,18 @@ protected:
   // the stream, or by us calling FinishInternal().
   //
   // May not be called mid-frame.
   //
   // For animated images, specify the loop count. -1 means loop forever, 0
   // means a single iteration, stopping on the last frame.
   void PostDecodeDone(int32_t aLoopCount = 0);
 
-  // Data errors are the fault of the source data, decoder errors are our fault
+  // Report an error in the image data.
   void PostDataError();
-  void PostDecoderError(nsresult aFailCode);
 
   /**
    * CompleteDecode() finishes up the decoding process after Decode() determines
    * that we're finished. It records final progress and does all the cleanup
    * that's possible off-main-thread.
    */
   void CompleteDecode();
 
@@ -410,18 +409,16 @@ private:
   Maybe<SourceBufferIterator> mIterator;
   RawAccessFrameRef mCurrentFrame;
   ImageMetadata mImageMetadata;
   nsIntRect mInvalidRect; // Tracks an invalidation region in the current frame.
   Progress mProgress;
 
   uint32_t mFrameCount; // Number of frames, including anything in-progress
 
-  nsresult mFailCode;
-
   // Telemetry data for this decoder.
   TimeDuration mDecodeTime;
   uint32_t mChunkCount;
 
   DecoderFlags mDecoderFlags;
   SurfaceFlags mSurfaceFlags;
   size_t mBytesDecoded;
 
--- a/image/DecoderFactory.cpp
+++ b/image/DecoderFactory.cpp
@@ -130,18 +130,17 @@ DecoderFactory::CreateDecoder(DecoderTyp
   decoder->SetSampleSize(aSampleSize);
 
   // Set a target size for downscale-during-decode if applicable.
   if (aTargetSize) {
     DebugOnly<nsresult> rv = decoder->SetTargetSize(*aTargetSize);
     MOZ_ASSERT(NS_SUCCEEDED(rv), "Bad downscale-during-decode target size?");
   }
 
-  decoder->Init();
-  if (NS_FAILED(decoder->GetDecoderError())) {
+  if (NS_FAILED(decoder->Init())) {
     return nullptr;
   }
 
   // Add a placeholder to the SurfaceCache so we won't trigger any more decoders
   // with the same parameters.
   IntSize surfaceSize = aTargetSize.valueOr(aIntrinsicSize);
   SurfaceKey surfaceKey =
     RasterSurfaceKey(surfaceSize, aSurfaceFlags, /* aFrameNum = */ 0);
@@ -175,18 +174,17 @@ DecoderFactory::CreateAnimationDecoder(D
   MOZ_ASSERT(decoder, "Should have a decoder now");
 
   // Initialize the decoder.
   decoder->SetMetadataDecode(false);
   decoder->SetIterator(aSourceBuffer->Iterator());
   decoder->SetDecoderFlags(aDecoderFlags | DecoderFlags::IS_REDECODE);
   decoder->SetSurfaceFlags(aSurfaceFlags);
 
-  decoder->Init();
-  if (NS_FAILED(decoder->GetDecoderError())) {
+  if (NS_FAILED(decoder->Init())) {
     return nullptr;
   }
 
   // Add a placeholder for the first frame to the SurfaceCache so we won't
   // trigger any more decoders with the same parameters.
   SurfaceKey surfaceKey =
     RasterSurfaceKey(aIntrinsicSize, aSurfaceFlags, /* aFrameNum = */ 0);
   InsertOutcome outcome =
@@ -213,18 +211,17 @@ DecoderFactory::CreateMetadataDecoder(De
     GetDecoder(aType, aImage, /* aIsRedecode = */ false);
   MOZ_ASSERT(decoder, "Should have a decoder now");
 
   // Initialize the decoder.
   decoder->SetMetadataDecode(true);
   decoder->SetIterator(aSourceBuffer->Iterator());
   decoder->SetSampleSize(aSampleSize);
 
-  decoder->Init();
-  if (NS_FAILED(decoder->GetDecoderError())) {
+  if (NS_FAILED(decoder->Init())) {
     return nullptr;
   }
 
   RefPtr<IDecodingTask> task = new MetadataDecodingTask(WrapNotNull(decoder));
   return task.forget();
 }
 
 /* static */ already_AddRefed<Decoder>
@@ -262,18 +259,17 @@ DecoderFactory::CreateDecoderForICOResou
 
   // Set a target size for downscale-during-decode if applicable.
   const Maybe<IntSize> targetSize = aICODecoder->GetTargetSize();
   if (targetSize) {
     DebugOnly<nsresult> rv = decoder->SetTargetSize(*targetSize);
     MOZ_ASSERT(NS_SUCCEEDED(rv), "Bad downscale-during-decode target size?");
   }
 
-  decoder->Init();
-  if (NS_FAILED(decoder->GetDecoderError())) {
+  if (NS_FAILED(decoder->Init())) {
     return nullptr;
   }
 
   return decoder.forget();
 }
 
 /* static */ already_AddRefed<Decoder>
 DecoderFactory::CreateAnonymousDecoder(DecoderType aType,
@@ -308,18 +304,17 @@ DecoderFactory::CreateAnonymousDecoder(D
   decoder->SetSurfaceFlags(aSurfaceFlags);
 
   // Set a target size for downscale-during-decode if applicable.
   if (aTargetSize) {
     DebugOnly<nsresult> rv = decoder->SetTargetSize(*aTargetSize);
     MOZ_ASSERT(NS_SUCCEEDED(rv), "Bad downscale-during-decode target size?");
   }
 
-  decoder->Init();
-  if (NS_FAILED(decoder->GetDecoderError())) {
+  if (NS_FAILED(decoder->Init())) {
     return nullptr;
   }
 
   return decoder.forget();
 }
 
 /* static */ already_AddRefed<Decoder>
 DecoderFactory::CreateAnonymousMetadataDecoder(DecoderType aType,
@@ -333,18 +328,17 @@ DecoderFactory::CreateAnonymousMetadataD
     GetDecoder(aType, /* aImage = */ nullptr, /* aIsRedecode = */ false);
   MOZ_ASSERT(decoder, "Should have a decoder now");
 
   // Initialize the decoder.
   decoder->SetMetadataDecode(true);
   decoder->SetIterator(aSourceBuffer->Iterator());
   decoder->SetDecoderFlags(DecoderFlags::FIRST_FRAME_ONLY);
 
-  decoder->Init();
-  if (NS_FAILED(decoder->GetDecoderError())) {
+  if (NS_FAILED(decoder->Init())) {
     return nullptr;
   }
 
   return decoder.forget();
 }
 
 } // namespace image
 } // namespace mozilla
--- a/image/RasterImage.cpp
+++ b/image/RasterImage.cpp
@@ -1777,17 +1777,17 @@ RasterImage::FinalizeDecoder(Decoder* aD
 void
 RasterImage::ReportDecoderError(Decoder* aDecoder)
 {
   nsCOMPtr<nsIConsoleService> consoleService =
     do_GetService(NS_CONSOLESERVICE_CONTRACTID);
   nsCOMPtr<nsIScriptError> errorObject =
     do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
 
-  if (consoleService && errorObject && !aDecoder->HasDecoderError()) {
+  if (consoleService && errorObject) {
     nsAutoString msg(NS_LITERAL_STRING("Image corrupt or truncated."));
     nsAutoString src;
     if (GetURI()) {
       nsCString uri;
       if (GetURI()->GetSpecTruncatedTo1k(uri) == ImageURL::TruncatedTo1k) {
         msg += NS_LITERAL_STRING(" URI in this note truncated due to length.");
       }
       src = NS_ConvertUTF8toUTF16(uri);
--- a/image/decoders/nsICODecoder.cpp
+++ b/image/decoders/nsICODecoder.cpp
@@ -98,18 +98,16 @@ nsICODecoder::GetFinalStateFromContained
     if (NS_FAILED(mContainedDecoder->Decode(mDoNotResume))) {
       PostDataError();
     }
   }
 
   // Make our state the same as the state of the contained decoder.
   mDecodeDone = mContainedDecoder->GetDecodeDone();
   mDataError = mDataError || mContainedDecoder->HasDataError();
-  mFailCode = NS_SUCCEEDED(mFailCode) ? mContainedDecoder->GetDecoderError()
-                                      : mFailCode;
   mDecodeAborted = mContainedDecoder->WasAborted();
   mProgress |= mContainedDecoder->TakeProgress();
   mInvalidRect.UnionRect(mInvalidRect, mContainedDecoder->TakeInvalidRect());
   mCurrentFrame = mContainedDecoder->GetCurrentFrameRef();
 
   MOZ_ASSERT(HasError() || !mCurrentFrame || mCurrentFrame->IsFinished());
 }
 
@@ -658,17 +656,14 @@ nsICODecoder::WriteToContainedDecoder(co
   }
 
   // Make our state the same as the state of the contained decoder.
   mProgress |= mContainedDecoder->TakeProgress();
   mInvalidRect.UnionRect(mInvalidRect, mContainedDecoder->TakeInvalidRect());
   if (mContainedDecoder->HasDataError()) {
     PostDataError();
   }
-  if (mContainedDecoder->HasDecoderError()) {
-    PostDecoderError(mContainedDecoder->GetDecoderError());
-  }
 
   return !HasError();
 }
 
 } // namespace image
 } // namespace mozilla
--- a/image/decoders/nsJPEGDecoder.cpp
+++ b/image/decoders/nsJPEGDecoder.cpp
@@ -123,34 +123,33 @@ nsJPEGDecoder::~nsJPEGDecoder()
 }
 
 Telemetry::ID
 nsJPEGDecoder::SpeedHistogram()
 {
   return Telemetry::IMAGE_DECODE_SPEED_JPEG;
 }
 
-void
+nsresult
 nsJPEGDecoder::InitInternal()
 {
   mCMSMode = gfxPlatform::GetCMSMode();
   if (GetSurfaceFlags() & SurfaceFlags::NO_COLORSPACE_CONVERSION) {
     mCMSMode = eCMSMode_Off;
   }
 
   // We set up the normal JPEG error routines, then override error_exit.
   mInfo.err = jpeg_std_error(&mErr.pub);
   //   mInfo.err = jpeg_std_error(&mErr.pub);
   mErr.pub.error_exit = my_error_exit;
   // Establish the setjmp return context for my_error_exit to use.
   if (setjmp(mErr.setjmp_buffer)) {
-    // If we get here, the JPEG code has signaled an error.
-    // We need to clean up the JPEG object, close the input file, and return.
-    PostDecoderError(NS_ERROR_FAILURE);
-    return;
+    // If we get here, the JPEG code has signaled an error, and initialization
+    // has failed.
+    return NS_ERROR_FAILURE;
   }
 
   // Step 1: allocate and initialize JPEG decompression object
   jpeg_create_decompress(&mInfo);
   // Set the source manager
   mInfo.src = &mSourceMgr;
 
   // Step 2: specify data source (eg, a file)
@@ -161,16 +160,18 @@ nsJPEGDecoder::InitInternal()
   mSourceMgr.skip_input_data = skip_input_data;
   mSourceMgr.resync_to_restart = jpeg_resync_to_restart;
   mSourceMgr.term_source = term_source;
 
   // Record app markers for ICC data
   for (uint32_t m = 0; m < 16; m++) {
     jpeg_save_markers(&mInfo, JPEG_APP0 + m, 0xFFFF);
   }
+
+  return NS_OK;
 }
 
 void
 nsJPEGDecoder::FinishInternal()
 {
   // If we're not in any sort of error case, force our state to JPEG_DONE.
   if ((mState != JPEG_DONE && mState != JPEG_SINK_NON_JPEG_TRAILER) &&
       (mState != JPEG_ERROR) &&
@@ -212,17 +213,16 @@ nsJPEGDecoder::ReadJPEGData(const char* 
     if (error_code == NS_ERROR_FAILURE) {
       // Error due to corrupt data. Make sure that we don't feed any more data
       // to libjpeg-turbo.
       mState = JPEG_SINK_NON_JPEG_TRAILER;
       MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug,
              ("} (setjmp returned NS_ERROR_FAILURE)"));
     } else {
       // Error for another reason. (Possibly OOM.)
-      PostDecoderError(error_code);
       mState = JPEG_ERROR;
       MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug,
              ("} (setjmp returned an error)"));
     }
 
     return Transition::TerminateFailure();
   }
 
--- a/image/decoders/nsJPEGDecoder.h
+++ b/image/decoders/nsJPEGDecoder.h
@@ -52,17 +52,17 @@ class nsJPEGDecoder : public Decoder
 public:
   virtual ~nsJPEGDecoder();
 
   virtual void SetSampleSize(int aSampleSize) override
   {
     mSampleSize = aSampleSize;
   }
 
-  virtual void InitInternal() override;
+  nsresult InitInternal() override;
   Maybe<TerminalState> DoDecode(SourceBufferIterator& aIterator) override;
   virtual void FinishInternal() override;
 
   virtual Telemetry::ID SpeedHistogram() override;
   void NotifyDone();
 
 protected:
   Orientation ReadOrientationFromEXIF();
--- a/image/decoders/nsPNGDecoder.cpp
+++ b/image/decoders/nsPNGDecoder.cpp
@@ -257,17 +257,17 @@ nsPNGDecoder::EndImageFrame()
   if (format == gfx::SurfaceFormat::B8G8R8X8) {
     opacity = Opacity::FULLY_OPAQUE;
   }
 
   PostFrameStop(opacity, mAnimInfo.mDispose, mAnimInfo.mTimeout,
                 mAnimInfo.mBlend, Some(mFrameRect));
 }
 
-void
+nsresult
 nsPNGDecoder::InitInternal()
 {
   mCMSMode = gfxPlatform::GetCMSMode();
   if (GetSurfaceFlags() & SurfaceFlags::NO_COLORSPACE_CONVERSION) {
     mCMSMode = eCMSMode_Off;
   }
   mDisablePremultipliedAlpha =
     bool(GetSurfaceFlags() & SurfaceFlags::NO_PREMULTIPLY_ALPHA);
@@ -293,25 +293,23 @@ nsPNGDecoder::InitInternal()
 
   // Initialize the container's source image header
   // Always decode to 24 bit pixdepth
 
   mPNG = png_create_read_struct(PNG_LIBPNG_VER_STRING,
                                 nullptr, nsPNGDecoder::error_callback,
                                 nsPNGDecoder::warning_callback);
   if (!mPNG) {
-    PostDecoderError(NS_ERROR_OUT_OF_MEMORY);
-    return;
+    return NS_ERROR_OUT_OF_MEMORY;
   }
 
   mInfo = png_create_info_struct(mPNG);
   if (!mInfo) {
-    PostDecoderError(NS_ERROR_OUT_OF_MEMORY);
     png_destroy_read_struct(&mPNG, nullptr, nullptr);
-    return;
+    return NS_ERROR_OUT_OF_MEMORY;
   }
 
 #ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
   // Ignore unused chunks
   if (mCMSMode == eCMSMode_Off || IsMetadataDecode()) {
     png_set_keep_unknown_chunks(mPNG, 1, color_chunks, 2);
   }
 
@@ -341,16 +339,17 @@ nsPNGDecoder::InitInternal()
 #endif
 
   // use this as libpng "progressive pointer" (retrieve in callbacks)
   png_set_progressive_read_fn(mPNG, static_cast<png_voidp>(this),
                               nsPNGDecoder::info_callback,
                               nsPNGDecoder::row_callback,
                               nsPNGDecoder::end_callback);
 
+  return NS_OK;
 }
 
 Maybe<TerminalState>
 nsPNGDecoder::DoDecode(SourceBufferIterator& aIterator)
 {
   MOZ_ASSERT(!HasError(), "Shouldn't call DoDecode after error!");
   MOZ_ASSERT(aIterator.Data());
   MOZ_ASSERT(aIterator.Length() > 0);
--- a/image/decoders/nsPNGDecoder.h
+++ b/image/decoders/nsPNGDecoder.h
@@ -17,17 +17,17 @@ namespace mozilla {
 namespace image {
 class RasterImage;
 
 class nsPNGDecoder : public Decoder
 {
 public:
   virtual ~nsPNGDecoder();
 
-  virtual void InitInternal() override;
+  nsresult InitInternal() override;
   Maybe<TerminalState> DoDecode(SourceBufferIterator& aIterator) override;
   virtual Telemetry::ID SpeedHistogram() override;
 
   /// @return true if this PNG is a valid ICO resource.
   bool IsValidICO() const;
 
 private:
   friend class DecoderFactory;