Bug 1286165 (Part 2) - Advance decoders' SourceBufferIterator directly in StreamingLexer. r=edwin,njn
authorSeth Fowler <mark.seth.fowler@gmail.com>
Thu, 14 Jul 2016 22:39:39 -0700
changeset 345207 e2069b87af4c2291c9d2ef391c7759c3464d0341
parent 345206 ab4ae8b37941493a3ec12506d28fc82ec3bf5c34
child 345208 bf3df70f738edf245f4d1bc9bdc88efd881460e5
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, njn
bugs1286165
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 1286165 (Part 2) - Advance decoders' SourceBufferIterator directly in StreamingLexer. r=edwin,njn
image/Decoder.cpp
image/Decoder.h
image/StreamingLexer.h
image/decoders/nsBMPDecoder.cpp
image/decoders/nsBMPDecoder.h
image/decoders/nsGIFDecoder2.cpp
image/decoders/nsGIFDecoder2.h
image/decoders/nsICODecoder.cpp
image/decoders/nsICODecoder.h
image/decoders/nsIconDecoder.cpp
image/decoders/nsIconDecoder.h
image/decoders/nsJPEGDecoder.cpp
image/decoders/nsJPEGDecoder.h
image/decoders/nsPNGDecoder.cpp
image/decoders/nsPNGDecoder.h
--- a/image/Decoder.cpp
+++ b/image/Decoder.cpp
@@ -102,83 +102,50 @@ Decoder::Init()
   nsresult rv = InitInternal();
 
   mInitialized = true;
 
   return rv;
 }
 
 nsresult
-Decoder::Decode(NotNull<IResumable*> aOnResume)
+Decoder::Decode(IResumable* aOnResume /* = nullptr */)
 {
   MOZ_ASSERT(mInitialized, "Should be initialized here");
   MOZ_ASSERT(mIterator, "Should have a SourceBufferIterator");
 
   // If we're already done, don't attempt to keep decoding.
   if (GetDecodeDone()) {
     return HasError() ? NS_ERROR_FAILURE : NS_OK;
   }
 
   Maybe<TerminalState> terminalState;
-
-  // We keep decoding chunks until the decode completes (i.e., we reach a
-  // terminal state) or there are no more chunks available.
   {
-    PROFILER_LABEL("ImageDecoder", "Decode",
-                   js::ProfileEntry::Category::GRAPHICS);
+    PROFILER_LABEL("ImageDecoder", "Decode", js::ProfileEntry::Category::GRAPHICS);
     AutoRecordDecoderTelemetry telemetry(this);
 
-    do {
-      if (GetDecodeDone()) {
-        MOZ_ASSERT_UNREACHABLE("Finished decode without reaching terminal state?");
-        terminalState = Some(TerminalState::SUCCESS);
-        break;
-      }
-
-      switch (mIterator->AdvanceOrScheduleResume(aOnResume.get())) {
-        case SourceBufferIterator::WAITING:
-          // We can't continue because the rest of the data hasn't arrived from
-          // the network yet. We don't have to do anything special; the
-          // SourceBufferIterator will ensure that Decode() gets called again on a
-          // DecodePool thread when more data is available.
-          return NS_OK;
-
-        case SourceBufferIterator::COMPLETE:
-          // Normally even if the data is truncated, we want decoding to
-          // succeed so we can display whatever we got. However, if the
-          // SourceBuffer was completed with a failing status, we want to fail.
-          // This happens only in exceptional situations like SourceBuffer
-          // itself encountering a failure due to OOM.
-          terminalState = NS_SUCCEEDED(mIterator->CompletionStatus())
-                        ? Some(TerminalState::SUCCESS)
-                        : Some(TerminalState::FAILURE);
-
-          break;
-
-        case SourceBufferIterator::READY:
-          // Pass the data along to the implementation.
-          terminalState = DoDecode(*mIterator);
-          break;
-
-        default:
-          MOZ_ASSERT_UNREACHABLE("Unknown SourceBufferIterator state");
-          terminalState = Some(TerminalState::FAILURE);
-      }
-    } while (!terminalState);
+    terminalState = DoDecode(*mIterator, aOnResume);
   }
 
-  MOZ_ASSERT(terminalState);
+  if (!terminalState) {
+    // We need more data to continue. If @aOnResume was non-null, the
+    // SourceBufferIterator will automatically reschedule us. Otherwise, it's up
+    // to the caller.
+    return NS_OK;
+  }
+
+  // We reached a terminal state; we're now done decoding.
   mReachedTerminalState = true;
 
   // If decoding failed, record that fact.
   if (terminalState == Some(TerminalState::FAILURE)) {
     PostError();
   }
 
-  // We're done decoding; perform final cleanup.
+  // Perform final cleanup.
   CompleteDecode();
 
   return HasError() ? NS_ERROR_FAILURE : NS_OK;
 }
 
 bool
 Decoder::ShouldSyncDecode(size_t aByteLimit)
 {
--- a/image/Decoder.h
+++ b/image/Decoder.h
@@ -40,22 +40,22 @@ public:
    *
    * @return NS_OK if the decoder could be initialized successfully.
    */
   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.
+   * If more data is needed and @aOnResume is non-null, Decode() will schedule
+   * @aOnResume to be called when more data is available.
    *
    * Any errors are reported by setting the appropriate state on the decoder.
    */
-  nsresult Decode(NotNull<IResumable*> aOnResume);
+  nsresult Decode(IResumable* aOnResume = nullptr);
 
   /**
    * Given a maximum number of bytes we're willing to decode, @aByteLimit,
    * returns true if we should attempt to run this decoder synchronously.
    */
   bool ShouldSyncDecode(size_t aByteLimit);
 
   /**
@@ -292,17 +292,18 @@ 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
    * return a failing nsresult.
    */
   virtual nsresult InitInternal();
-  virtual Maybe<TerminalState> DoDecode(SourceBufferIterator& aIterator) = 0;
+  virtual Maybe<TerminalState> DoDecode(SourceBufferIterator& aIterator,
+                                        IResumable* aOnResume) = 0;
   virtual nsresult BeforeFinishInternal();
   virtual nsresult FinishInternal();
   virtual nsresult FinishWithErrorInternal();
 
   /*
    * Progress notifications.
    */
 
--- a/image/StreamingLexer.h
+++ b/image/StreamingLexer.h
@@ -258,18 +258,70 @@ class StreamingLexer
 {
 public:
   explicit StreamingLexer(LexerTransition<State> aStartState)
     : mTransition(aStartState)
     , mToReadUnbuffered(0)
   { }
 
   template <typename Func>
+  Maybe<TerminalState> Lex(SourceBufferIterator& aIterator,
+                           IResumable* aOnResume,
+                           Func aFunc)
+  {
+    if (mTransition.NextStateIsTerminal()) {
+      // We've already reached a terminal state. We never deliver any more data
+      // in this case; just return the terminal state again immediately.
+      return Some(mTransition.NextStateAsTerminal());
+    }
+
+    do {
+      switch (aIterator.AdvanceOrScheduleResume(aOnResume)) {
+        case SourceBufferIterator::WAITING:
+          // We can't continue because the rest of the data hasn't arrived from
+          // the network yet. We don't have to do anything special; the
+          // SourceBufferIterator will ensure that |aOnResume| gets called when
+          // more data is available.
+          return Nothing();
+
+        case SourceBufferIterator::COMPLETE:
+          // Normally even if the data is truncated, we want decoding to
+          // succeed so we can display whatever we got. However, if the
+          // SourceBuffer was completed with a failing status, we want to fail.
+          // This happens only in exceptional situations like SourceBuffer
+          // itself encountering a failure due to OOM.
+          mTransition = NS_SUCCEEDED(aIterator.CompletionStatus())
+                      ? Transition::TerminateSuccess()
+                      : Transition::TerminateFailure();
+          break;
+
+        case SourceBufferIterator::READY:
+          // Process the new data that became available. This may result in us
+          // transitioning to a terminal state; we'll check if that happened at
+          // the bottom of the loop.
+          MOZ_ASSERT(aIterator.Data());
+          MOZ_ASSERT(aIterator.Length() > 0);
+          Lex(aIterator.Data(), aIterator.Length(), aFunc);
+          break;
+
+        default:
+          MOZ_ASSERT_UNREACHABLE("Unknown SourceBufferIterator state");
+          mTransition = Transition::TerminateFailure();
+      }
+    } while (!mTransition.NextStateIsTerminal());
+
+    // We're done. Return the terminal state.
+    return Some(mTransition.NextStateAsTerminal());
+  }
+
+  template <typename Func>
   Maybe<TerminalState> Lex(const char* aInput, size_t aLength, Func aFunc)
   {
+    MOZ_ASSERT(aInput);
+
     if (mTransition.NextStateIsTerminal()) {
       // We've already reached a terminal state. We never deliver any more data
       // in this case; just return the terminal state again immediately.
       return Some(mTransition.NextStateAsTerminal());
     }
 
     if (mToReadUnbuffered > 0) {
       // We're continuing an unbuffered read.
--- a/image/decoders/nsBMPDecoder.cpp
+++ b/image/decoders/nsBMPDecoder.cpp
@@ -442,23 +442,21 @@ nsBMPDecoder::FinishRow()
     }
   } else {
     PostInvalidation(IntRect(0, mCurrentRow, mH.mWidth, 1));
   }
   mCurrentRow--;
 }
 
 Maybe<TerminalState>
-nsBMPDecoder::DoDecode(SourceBufferIterator& aIterator)
+nsBMPDecoder::DoDecode(SourceBufferIterator& aIterator, IResumable* aOnResume)
 {
   MOZ_ASSERT(!HasError(), "Shouldn't call DoDecode after error!");
-  MOZ_ASSERT(aIterator.Data());
-  MOZ_ASSERT(aIterator.Length() > 0);
 
-  return mLexer.Lex(aIterator.Data(), aIterator.Length(),
+  return mLexer.Lex(aIterator, aOnResume,
                     [=](State aState, const char* aData, size_t aLength) {
     switch (aState) {
       case State::FILE_HEADER:      return ReadFileHeader(aData, aLength);
       case State::INFO_HEADER_SIZE: return ReadInfoHeaderSize(aData, aLength);
       case State::INFO_HEADER_REST: return ReadInfoHeaderRest(aData, aLength);
       case State::BITFIELDS:        return ReadBitfields(aData, aLength);
       case State::COLOR_TABLE:      return ReadColorTable(aData, aLength);
       case State::GAP:              return SkipGap();
--- a/image/decoders/nsBMPDecoder.h
+++ b/image/decoders/nsBMPDecoder.h
@@ -137,17 +137,18 @@ public:
   /// Mark this BMP as being within an ICO file. Only used for testing purposes
   /// because the ICO-specific constructor does this marking automatically.
   void SetIsWithinICO() { mIsWithinICO = true; }
 
   /// Did the BMP file have alpha data of any kind? (Only use this after the
   /// bitmap has been fully decoded.)
   bool HasTransparency() const { return mDoesHaveTransparency; }
 
-  Maybe<TerminalState> DoDecode(SourceBufferIterator& aIterator) override;
+  Maybe<TerminalState> DoDecode(SourceBufferIterator& aIterator,
+                                IResumable* aOnResume) override;
   nsresult BeforeFinishInternal() override;
   nsresult FinishInternal() override;
 
 private:
   friend class DecoderFactory;
 
   enum class State {
     FILE_HEADER,
--- a/image/decoders/nsGIFDecoder2.cpp
+++ b/image/decoders/nsGIFDecoder2.cpp
@@ -452,23 +452,21 @@ ConvertColormap(uint32_t* aColormap, uin
   // NB: can't use 32-bit reads, they might read off the end of the buffer
   while (c--) {
     from -= 3;
     *--to = gfxPackedPixel(0xFF, from[0], from[1], from[2]);
   }
 }
 
 Maybe<TerminalState>
-nsGIFDecoder2::DoDecode(SourceBufferIterator& aIterator)
+nsGIFDecoder2::DoDecode(SourceBufferIterator& aIterator, IResumable* aOnResume)
 {
   MOZ_ASSERT(!HasError(), "Shouldn't call DoDecode after error!");
-  MOZ_ASSERT(aIterator.Data());
-  MOZ_ASSERT(aIterator.Length() > 0);
 
-  return mLexer.Lex(aIterator.Data(), aIterator.Length(),
+  return mLexer.Lex(aIterator, aOnResume,
                     [=](State aState, const char* aData, size_t aLength) {
     switch(aState) {
       case State::GIF_HEADER:
         return ReadGIFHeader(aData);
       case State::SCREEN_DESCRIPTOR:
         return ReadScreenDescriptor(aData);
       case State::GLOBAL_COLOR_TABLE:
         return ReadGlobalColorTable(aData, aLength);
--- a/image/decoders/nsGIFDecoder2.h
+++ b/image/decoders/nsGIFDecoder2.h
@@ -19,17 +19,18 @@ class RasterImage;
 //////////////////////////////////////////////////////////////////////
 // nsGIFDecoder2 Definition
 
 class nsGIFDecoder2 : public Decoder
 {
 public:
   ~nsGIFDecoder2();
 
-  Maybe<TerminalState> DoDecode(SourceBufferIterator& aIterator) override;
+  Maybe<TerminalState> DoDecode(SourceBufferIterator& aIterator,
+                                IResumable* aOnResume) override;
   nsresult FinishInternal() override;
   virtual Telemetry::ID SpeedHistogram() override;
 
 private:
   friend class DecoderFactory;
 
   // Decoders should only be instantiated via DecoderFactory.
   explicit nsGIFDecoder2(RasterImage* aImage);
--- a/image/decoders/nsICODecoder.cpp
+++ b/image/decoders/nsICODecoder.cpp
@@ -49,17 +49,16 @@ nsICODecoder::GetNumColors()
     }
   }
   return numColors;
 }
 
 nsICODecoder::nsICODecoder(RasterImage* aImage)
   : Decoder(aImage)
   , mLexer(Transition::To(ICOState::HEADER, ICOHEADERSIZE))
-  , mDoNotResume(WrapNotNull(new DoNotResume))
   , mBiggestResourceColorDepth(0)
   , mBestResourceDelta(INT_MIN)
   , mBestResourceColorDepth(0)
   , mNumIcons(0)
   , mCurrIcon(0)
   , mBPP(0)
   , mMaskRowSize(0)
   , mCurrMaskLine(0)
@@ -90,17 +89,17 @@ nsICODecoder::GetFinalStateFromContained
   }
 
   MOZ_ASSERT(mContainedSourceBuffer,
              "Should have a SourceBuffer if we have a decoder");
 
   // Let the contained decoder finish up if necessary.
   if (!mContainedSourceBuffer->IsComplete()) {
     mContainedSourceBuffer->Complete(NS_OK);
-    mContainedDecoder->Decode(mDoNotResume);
+    mContainedDecoder->Decode();
   }
 
   // Make our state the same as the state of the contained decoder.
   mDecodeDone = mContainedDecoder->GetDecodeDone();
   mDecodeAborted = mContainedDecoder->WasAborted();
   mProgress |= mContainedDecoder->TakeProgress();
   mInvalidRect.UnionRect(mInvalidRect, mContainedDecoder->TakeInvalidRect());
   mCurrentFrame = mContainedDecoder->GetCurrentFrameRef();
@@ -585,23 +584,21 @@ nsICODecoder::FinishResource()
       mContainedDecoder->GetSize() != GetRealSize()) {
     return Transition::TerminateFailure();
   }
 
   return Transition::TerminateSuccess();
 }
 
 Maybe<TerminalState>
-nsICODecoder::DoDecode(SourceBufferIterator& aIterator)
+nsICODecoder::DoDecode(SourceBufferIterator& aIterator, IResumable* aOnResume)
 {
   MOZ_ASSERT(!HasError(), "Shouldn't call DoDecode after error!");
-  MOZ_ASSERT(aIterator.Data());
-  MOZ_ASSERT(aIterator.Length() > 0);
 
-  return mLexer.Lex(aIterator.Data(), aIterator.Length(),
+  return mLexer.Lex(aIterator, aOnResume,
                     [=](ICOState aState, const char* aData, size_t aLength) {
     switch (aState) {
       case ICOState::HEADER:
         return ReadHeader(aData);
       case ICOState::DIR_ENTRY:
         return ReadDirEntry(aData);
       case ICOState::SKIP_TO_RESOURCE:
         return Transition::ContinueUnbuffered(ICOState::SKIP_TO_RESOURCE);
@@ -642,17 +639,17 @@ nsICODecoder::WriteToContainedDecoder(co
   mContainedSourceBuffer->Append(aBuffer, aCount);
 
   bool succeeded = true;
 
   // Write to the contained decoder. If we run out of data, the ICO decoder will
   // get resumed when there's more data available, as usual, so we don't need
   // the contained decoder to get resumed too. To avoid that, we provide an
   // IResumable which just does nothing.
-  if (NS_FAILED(mContainedDecoder->Decode(mDoNotResume))) {
+  if (NS_FAILED(mContainedDecoder->Decode())) {
     succeeded = false;
   }
 
   // Make our state the same as the state of the contained decoder, and
   // propagate errors.
   mProgress |= mContainedDecoder->TakeProgress();
   mInvalidRect.UnionRect(mInvalidRect, mContainedDecoder->TakeInvalidRect());
   if (mContainedDecoder->HasError()) {
--- a/image/decoders/nsICODecoder.h
+++ b/image/decoders/nsICODecoder.h
@@ -6,17 +6,16 @@
 
 #ifndef mozilla_image_decoders_nsICODecoder_h
 #define mozilla_image_decoders_nsICODecoder_h
 
 #include "StreamingLexer.h"
 #include "Decoder.h"
 #include "imgFrame.h"
 #include "mozilla/gfx/2D.h"
-#include "mozilla/NotNull.h"
 #include "nsBMPDecoder.h"
 #include "nsPNGDecoder.h"
 #include "ICOFileHeaders.h"
 
 namespace mozilla {
 namespace image {
 
 class RasterImage;
@@ -65,17 +64,18 @@ public:
   gfx::IntSize GetRealSize() const
   {
     return gfx::IntSize(GetRealWidth(), GetRealHeight());
   }
 
   /// @return The offset from the beginning of the ICO to the first resource.
   size_t FirstResourceOffset() const;
 
-  Maybe<TerminalState> DoDecode(SourceBufferIterator& aIterator) override;
+  Maybe<TerminalState> DoDecode(SourceBufferIterator& aIterator,
+                                IResumable* aOnResume) override;
   nsresult FinishInternal() override;
   nsresult FinishWithErrorInternal() override;
 
 private:
   friend class DecoderFactory;
 
   // Decoders should only be instantiated via DecoderFactory.
   explicit nsICODecoder(RasterImage* aImage);
@@ -106,32 +106,19 @@ private:
   LexerTransition<ICOState> ReadPNG(const char* aData, uint32_t aLen);
   LexerTransition<ICOState> ReadBIH(const char* aData);
   LexerTransition<ICOState> ReadBMP(const char* aData, uint32_t aLen);
   LexerTransition<ICOState> PrepareForMask();
   LexerTransition<ICOState> ReadMaskRow(const char* aData);
   LexerTransition<ICOState> FinishMask();
   LexerTransition<ICOState> FinishResource();
 
-  // A helper implementation of IResumable which just does nothing; see
-  // WriteToContainedDecoder() for more details.
-  class DoNotResume final : public IResumable
-  {
-  public:
-    NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DoNotResume, override)
-    void Resume() override { }
-
-  private:
-    virtual ~DoNotResume() { }
-  };
-
   StreamingLexer<ICOState, 32> mLexer; // The lexer.
   RefPtr<Decoder> mContainedDecoder; // Either a BMP or PNG decoder.
   RefPtr<SourceBuffer> mContainedSourceBuffer;  // SourceBuffer for mContainedDecoder.
-  NotNull<RefPtr<IResumable>> mDoNotResume;  // IResumable helper for SourceBuffer.
   UniquePtr<uint8_t[]> mMaskBuffer;    // A temporary buffer for the alpha mask.
   char mBIHraw[bmp::InfoHeaderLength::WIN_ICO]; // The bitmap information header.
   IconDirEntry mDirEntry;              // The dir entry for the selected resource.
   gfx::IntSize mBiggestResourceSize;   // Used to select the intrinsic size.
   gfx::IntSize mBiggestResourceHotSpot; // Used to select the intrinsic size.
   uint16_t mBiggestResourceColorDepth; // Used to select the intrinsic size.
   int32_t mBestResourceDelta;          // Used to select the best resource.
   uint16_t mBestResourceColorDepth;    // Used to select the best resource.
--- a/image/decoders/nsIconDecoder.cpp
+++ b/image/decoders/nsIconDecoder.cpp
@@ -22,23 +22,21 @@ nsIconDecoder::nsIconDecoder(RasterImage
 {
   // Nothing to do
 }
 
 nsIconDecoder::~nsIconDecoder()
 { }
 
 Maybe<TerminalState>
-nsIconDecoder::DoDecode(SourceBufferIterator& aIterator)
+nsIconDecoder::DoDecode(SourceBufferIterator& aIterator, IResumable* aOnResume)
 {
   MOZ_ASSERT(!HasError(), "Shouldn't call DoDecode after error!");
-  MOZ_ASSERT(aIterator.Data());
-  MOZ_ASSERT(aIterator.Length() > 0);
 
-  return mLexer.Lex(aIterator.Data(), aIterator.Length(),
+  return mLexer.Lex(aIterator, aOnResume,
                     [=](State aState, const char* aData, size_t aLength) {
     switch (aState) {
       case State::HEADER:
         return ReadHeader(aData);
       case State::ROW_OF_PIXELS:
         return ReadRowOfPixels(aData, aLength);
       case State::FINISH:
         return Finish();
--- a/image/decoders/nsIconDecoder.h
+++ b/image/decoders/nsIconDecoder.h
@@ -32,17 +32,18 @@ class RasterImage;
 //
 ////////////////////////////////////////////////////////////////////////////////
 
 class nsIconDecoder : public Decoder
 {
 public:
   virtual ~nsIconDecoder();
 
-  Maybe<TerminalState> DoDecode(SourceBufferIterator& aIterator) override;
+  Maybe<TerminalState> DoDecode(SourceBufferIterator& aIterator,
+                                IResumable* aOnResume) override;
 
 private:
   friend class DecoderFactory;
 
   // Decoders should only be instantiated via DecoderFactory.
   explicit nsIconDecoder(RasterImage* aImage);
 
   enum class State {
--- a/image/decoders/nsJPEGDecoder.cpp
+++ b/image/decoders/nsJPEGDecoder.cpp
@@ -178,23 +178,21 @@ nsJPEGDecoder::FinishInternal()
       !IsMetadataDecode()) {
     mState = JPEG_DONE;
   }
 
   return NS_OK;
 }
 
 Maybe<TerminalState>
-nsJPEGDecoder::DoDecode(SourceBufferIterator& aIterator)
+nsJPEGDecoder::DoDecode(SourceBufferIterator& aIterator, IResumable* aOnResume)
 {
   MOZ_ASSERT(!HasError(), "Shouldn't call DoDecode after error!");
-  MOZ_ASSERT(aIterator.Data());
-  MOZ_ASSERT(aIterator.Length() > 0);
 
-  return mLexer.Lex(aIterator.Data(), aIterator.Length(),
+  return mLexer.Lex(aIterator, aOnResume,
                     [=](State aState, const char* aData, size_t aLength) {
     switch (aState) {
       case State::JPEG_DATA:
         return ReadJPEGData(aData, aLength);
       case State::FINISHED_JPEG_DATA:
         return FinishedJPEGData();
     }
     MOZ_CRASH("Unknown State");
--- a/image/decoders/nsJPEGDecoder.h
+++ b/image/decoders/nsJPEGDecoder.h
@@ -53,17 +53,18 @@ public:
   virtual ~nsJPEGDecoder();
 
   virtual void SetSampleSize(int aSampleSize) override
   {
     mSampleSize = aSampleSize;
   }
 
   nsresult InitInternal() override;
-  Maybe<TerminalState> DoDecode(SourceBufferIterator& aIterator) override;
+  Maybe<TerminalState> DoDecode(SourceBufferIterator& aIterator,
+                                IResumable* aOnResume) override;
   nsresult FinishInternal() override;
 
   virtual Telemetry::ID SpeedHistogram() override;
   void NotifyDone();
 
 protected:
   Orientation ReadOrientationFromEXIF();
   void OutputScanlines(bool* suspend);
--- a/image/decoders/nsPNGDecoder.cpp
+++ b/image/decoders/nsPNGDecoder.cpp
@@ -343,23 +343,21 @@ nsPNGDecoder::InitInternal()
                               nsPNGDecoder::info_callback,
                               nsPNGDecoder::row_callback,
                               nsPNGDecoder::end_callback);
 
   return NS_OK;
 }
 
 Maybe<TerminalState>
-nsPNGDecoder::DoDecode(SourceBufferIterator& aIterator)
+nsPNGDecoder::DoDecode(SourceBufferIterator& aIterator, IResumable* aOnResume)
 {
   MOZ_ASSERT(!HasError(), "Shouldn't call DoDecode after error!");
-  MOZ_ASSERT(aIterator.Data());
-  MOZ_ASSERT(aIterator.Length() > 0);
 
-  return mLexer.Lex(aIterator.Data(), aIterator.Length(),
+  return mLexer.Lex(aIterator, aOnResume,
                     [=](State aState, const char* aData, size_t aLength) {
     switch (aState) {
       case State::PNG_DATA:
         return ReadPNGData(aData, aLength);
       case State::FINISHED_PNG_DATA:
         return FinishedPNGData();
     }
     MOZ_CRASH("Unknown State");
--- a/image/decoders/nsPNGDecoder.h
+++ b/image/decoders/nsPNGDecoder.h
@@ -18,17 +18,18 @@ namespace image {
 class RasterImage;
 
 class nsPNGDecoder : public Decoder
 {
 public:
   virtual ~nsPNGDecoder();
 
   nsresult InitInternal() override;
-  Maybe<TerminalState> DoDecode(SourceBufferIterator& aIterator) override;
+  Maybe<TerminalState> DoDecode(SourceBufferIterator& aIterator,
+                                IResumable* aOnResume) override;
   virtual Telemetry::ID SpeedHistogram() override;
 
   /// @return true if this PNG is a valid ICO resource.
   bool IsValidICO() const;
 
 private:
   friend class DecoderFactory;