Backed out 4 changesets (bug 1287691) due to Windows build failure
authorMakoto Kato <m_kato@ga2.so-net.ne.jp>
Wed, 20 Jul 2016 11:03:59 +0900
changeset 345793 b925c4294d4467954e53f7a5fb831029b104281d
parent 345792 fcb6731a7e2b83d02dd093ce85ae97cb57770ac8
child 345794 fbf966fce2e32022a132ccef7c472c6b0bbcd490
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)
bugs1287691
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
Backed out 4 changesets (bug 1287691) due to Windows build failure MozReview-Commit-ID: KgV3nU9a64F
image/Decoder.cpp
image/Decoder.h
image/IDecodingTask.cpp
image/IDecodingTask.h
image/decoders/nsGIFDecoder2.cpp
image/decoders/nsGIFDecoder2.h
image/decoders/nsICODecoder.cpp
image/decoders/nsPNGDecoder.cpp
image/decoders/nsPNGDecoder.h
--- a/image/Decoder.cpp
+++ b/image/Decoder.cpp
@@ -101,57 +101,56 @@ Decoder::Init()
   // Implementation-specific initialization.
   nsresult rv = InitInternal();
 
   mInitialized = true;
 
   return rv;
 }
 
-LexerResult
+nsresult
 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 LexerResult(HasError() ? TerminalState::FAILURE
-                                  : TerminalState::SUCCESS);
+    return HasError() ? NS_ERROR_FAILURE : NS_OK;
   }
 
   LexerResult lexerResult(TerminalState::FAILURE);
   {
     PROFILER_LABEL("ImageDecoder", "Decode", js::ProfileEntry::Category::GRAPHICS);
     AutoRecordDecoderTelemetry telemetry(this);
 
     lexerResult =  DoDecode(*mIterator, aOnResume);
   };
 
   if (lexerResult.is<Yield>()) {
-    // We either need more data to continue (in which case either @aOnResume or
-    // the caller will reschedule us to run again later), or the decoder is
-    // yielding to allow the caller access to some intermediate output.
-    return lexerResult;
+    // We need more data to continue. If @aOnResume was non-null, the
+    // SourceBufferIterator will automatically reschedule us. Otherwise, it's up
+    // to the caller.
+    MOZ_ASSERT(lexerResult.as<Yield>() == Yield::NEED_MORE_DATA);
+    return NS_OK;
   }
 
   // We reached a terminal state; we're now done decoding.
   MOZ_ASSERT(lexerResult.is<TerminalState>());
   mReachedTerminalState = true;
 
   // If decoding failed, record that fact.
   if (lexerResult.as<TerminalState>() == TerminalState::FAILURE) {
     PostError();
   }
 
   // Perform final cleanup.
   CompleteDecode();
 
-  return LexerResult(HasError() ? TerminalState::FAILURE
-                                : TerminalState::SUCCESS);
+  return HasError() ? NS_ERROR_FAILURE : NS_OK;
 }
 
 bool
 Decoder::ShouldSyncDecode(size_t aByteLimit)
 {
   MOZ_ASSERT(aByteLimit > 0);
   MOZ_ASSERT(mIterator, "Should have a SourceBufferIterator");
 
@@ -442,16 +441,22 @@ Decoder::PostFrameStop(Opacity aFrameOpa
   mProgress |= FLAG_FRAME_COMPLETE;
 
   // If we're not sending partial invalidations, then we send an invalidation
   // here when the first frame is complete.
   if (!ShouldSendPartialInvalidations() && mFrameCount == 1) {
     mInvalidRect.UnionRect(mInvalidRect,
                            gfx::IntRect(gfx::IntPoint(0, 0), GetSize()));
   }
+
+  // If we are going to keep decoding we should notify now about the first frame being done.
+  if (mImage && mFrameCount == 1 && HasAnimation()) {
+    MOZ_ASSERT(HasProgress());
+    IDecodingTask::NotifyProgress(WrapNotNull(this));
+  }
 }
 
 void
 Decoder::PostInvalidation(const nsIntRect& aRect,
                           const Maybe<nsIntRect>& aRectAtTargetSize
                             /* = Nothing() */)
 {
   // We should be mid-frame
--- a/image/Decoder.h
+++ b/image/Decoder.h
@@ -43,24 +43,19 @@ public:
   nsresult Init();
 
   /**
    * Decodes, reading all data currently available in the SourceBuffer.
    *
    * If more data is needed and @aOnResume is non-null, Decode() will schedule
    * @aOnResume to be called when more data is available.
    *
-   * @return a LexerResult which may indicate:
-   *   - the image has been successfully decoded (TerminalState::SUCCESS), or
-   *   - the image has failed to decode (TerminalState::FAILURE), or
-   *   - the decoder is yielding until it gets more data (Yield::NEED_MORE_DATA), or
-   *   - the decoder is yielding to allow the caller to access intermediate
-   *     output (Yield::OUTPUT_AVAILABLE).
+   * Any errors are reported by setting the appropriate state on the decoder.
    */
-  LexerResult Decode(IResumable* aOnResume = nullptr);
+  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);
 
   /**
--- a/image/IDecodingTask.cpp
+++ b/image/IDecodingTask.cpp
@@ -14,18 +14,18 @@
 
 namespace mozilla {
 namespace image {
 
 ///////////////////////////////////////////////////////////////////////////////
 // Helpers for sending notifications to the image associated with a decoder.
 ///////////////////////////////////////////////////////////////////////////////
 
-static void
-NotifyProgress(NotNull<Decoder*> aDecoder)
+/* 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(),
@@ -80,41 +80,30 @@ DecodingTask::DecodingTask(NotNull<Decod
 {
   MOZ_ASSERT(!mDecoder->IsMetadataDecode(),
              "Use MetadataDecodingTask for metadata decodes");
 }
 
 void
 DecodingTask::Run()
 {
-  while (true) {
-    LexerResult result = mDecoder->Decode(WrapNotNull(this));
+  nsresult rv = mDecoder->Decode(WrapNotNull(this));
 
-    if (result.is<TerminalState>()) {
-      NotifyDecodeComplete(mDecoder);
-      return;  // We're done.
-    }
-
-    MOZ_ASSERT(result.is<Yield>());
-
+  if (NS_SUCCEEDED(rv) && !mDecoder->GetDecodeDone()) {
     // Notify for the progress we've made so far.
     if (mDecoder->HasProgress()) {
       NotifyProgress(mDecoder);
     }
 
-    if (result == LexerResult(Yield::NEED_MORE_DATA)) {
-      // We can't make any more progress right now. The decoder itself will
-      // ensure that we get reenqueued when more data is available; just return
-      // for now.
-      return;
-    }
+    // We don't need to do anything else for this case. The decoder itself will
+    // ensure that we get reenqueued when more data is available.
+    return;
+  }
 
-    // Right now we don't do anything special for other kinds of yields, so just
-    // keep working.
-  }
+  NotifyDecodeComplete(mDecoder);
 }
 
 bool
 DecodingTask::ShouldPreferSyncRun() const
 {
   return mDecoder->ShouldSyncDecode(gfxPrefs::ImageMemDecodeBytesAtATime());
 }
 
@@ -128,59 +117,38 @@ MetadataDecodingTask::MetadataDecodingTa
 {
   MOZ_ASSERT(mDecoder->IsMetadataDecode(),
              "Use DecodingTask for non-metadata decodes");
 }
 
 void
 MetadataDecodingTask::Run()
 {
-  LexerResult result = mDecoder->Decode(WrapNotNull(this));
+  nsresult rv = mDecoder->Decode(WrapNotNull(this));
 
-  if (result.is<TerminalState>()) {
-    NotifyDecodeComplete(mDecoder);
-    return;  // We're done.
-  }
-
-  if (result == LexerResult(Yield::NEED_MORE_DATA)) {
-    // We can't make any more progress right now. We also don't want to report
-    // any progress, because it's important that metadata decode results are
-    // delivered atomically. The decoder itself will ensure that we get
-    // reenqueued when more data is available; just return for now.
+  if (NS_SUCCEEDED(rv) && !mDecoder->GetDecodeDone()) {
+    // It's important that metadata decode results are delivered atomically, so
+    // we'll wait until NotifyDecodeComplete() to report any progress.  We don't
+    // need to do anything else for this case. The decoder itself will ensure
+    // that we get reenqueued when more data is available.
     return;
   }
 
-  MOZ_ASSERT_UNREACHABLE("Metadata decode yielded for an unexpected reason");
+  NotifyDecodeComplete(mDecoder);
 }
 
 
 ///////////////////////////////////////////////////////////////////////////////
 // AnonymousDecodingTask implementation.
 ///////////////////////////////////////////////////////////////////////////////
 
 AnonymousDecodingTask::AnonymousDecodingTask(NotNull<Decoder*> aDecoder)
   : mDecoder(aDecoder)
 { }
 
 void
 AnonymousDecodingTask::Run()
 {
-  while (true) {
-    LexerResult result = mDecoder->Decode(WrapNotNull(this));
-
-    if (result.is<TerminalState>()) {
-      return;  // We're done.
-    }
-
-    if (result == LexerResult(Yield::NEED_MORE_DATA)) {
-      // We can't make any more progress right now. Let the caller decide how to
-      // handle it.
-      return;
-    }
-
-    // Right now we don't do anything special for other kinds of yields, so just
-    // keep working.
-    MOZ_ASSERT(result.is<Yield>());
-  }
+  mDecoder->Decode(WrapNotNull(this));
 }
 
 } // namespace image
 } // namespace mozilla
--- a/image/IDecodingTask.h
+++ b/image/IDecodingTask.h
@@ -45,16 +45,21 @@ public:
 
   /// A default implementation of IResumable which resubmits the task to the
   /// DecodePool. Subclasses can override this if they need different behavior.
   void Resume() override;
 
   /// @return a non-null weak pointer to the Decoder associated with this task.
   virtual NotNull<Decoder*> GetDecoder() const = 0;
 
+  // Notify the Image associated with a Decoder of its progress, sending a
+  // runnable to the main thread if necessary.
+  // XXX(seth): This is a hack that will be removed soon.
+  static void NotifyProgress(NotNull<Decoder*> aDecoder);
+
 protected:
   virtual ~IDecodingTask() { }
 };
 
 
 /**
  * An IDecodingTask implementation for full decodes of images.
  */
--- a/image/decoders/nsGIFDecoder2.cpp
+++ b/image/decoders/nsGIFDecoder2.cpp
@@ -470,18 +470,16 @@ nsGIFDecoder2::DoDecode(SourceBufferIter
       case State::SCREEN_DESCRIPTOR:
         return ReadScreenDescriptor(aData);
       case State::GLOBAL_COLOR_TABLE:
         return ReadGlobalColorTable(aData, aLength);
       case State::FINISHED_GLOBAL_COLOR_TABLE:
         return FinishedGlobalColorTable();
       case State::BLOCK_HEADER:
         return ReadBlockHeader(aData);
-      case State::BLOCK_HEADER_AFTER_YIELD:
-        return Transition::To(State::BLOCK_HEADER, BLOCK_HEADER_LEN);
       case State::EXTENSION_HEADER:
         return ReadExtensionHeader(aData);
       case State::GRAPHIC_CONTROL_EXTENSION:
         return ReadGraphicControlExtension(aData);
       case State::APPLICATION_IDENTIFIER:
         return ReadApplicationIdentifier(aData);
       case State::NETSCAPE_EXTENSION_SUB_BLOCK:
         return ReadNetscapeExtensionSubBlock(aData);
@@ -972,17 +970,17 @@ nsGIFDecoder2::ReadImageDataBlock(const 
 
 LexerTransition<nsGIFDecoder2::State>
 nsGIFDecoder2::ReadImageDataSubBlock(const char* aData)
 {
   const uint8_t subBlockLength = aData[0];
   if (subBlockLength == 0) {
     // We hit the block terminator.
     EndImageFrame();
-    return Transition::ToAfterYield(State::BLOCK_HEADER_AFTER_YIELD);
+    return Transition::To(State::BLOCK_HEADER, BLOCK_HEADER_LEN);
   }
 
   if (mGIFStruct.pixels_remaining == 0) {
     // We've already written to the entire image; we should've hit the block
     // terminator at this point. This image is corrupt, but we'll tolerate it.
 
     if (subBlockLength == GIF_TRAILER) {
       // This GIF is missing the block terminator for the final block; we'll put
--- a/image/decoders/nsGIFDecoder2.h
+++ b/image/decoders/nsGIFDecoder2.h
@@ -75,17 +75,16 @@ private:
   {
     FAILURE,
     SUCCESS,
     GIF_HEADER,
     SCREEN_DESCRIPTOR,
     GLOBAL_COLOR_TABLE,
     FINISHED_GLOBAL_COLOR_TABLE,
     BLOCK_HEADER,
-    BLOCK_HEADER_AFTER_YIELD,
     EXTENSION_HEADER,
     GRAPHIC_CONTROL_EXTENSION,
     APPLICATION_IDENTIFIER,
     NETSCAPE_EXTENSION_SUB_BLOCK,
     NETSCAPE_EXTENSION_DATA,
     IMAGE_DESCRIPTOR,
     LOCAL_COLOR_TABLE,
     FINISHED_LOCAL_COLOR_TABLE,
--- a/image/decoders/nsICODecoder.cpp
+++ b/image/decoders/nsICODecoder.cpp
@@ -640,24 +640,20 @@ 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.
-  LexerResult result = mContainedDecoder->Decode();
-  if (result == LexerResult(TerminalState::FAILURE)) {
+  if (NS_FAILED(mContainedDecoder->Decode())) {
     succeeded = false;
   }
 
-  MOZ_ASSERT(result != LexerResult(Yield::OUTPUT_AVAILABLE),
-             "Unexpected yield");
-
   // 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()) {
     succeeded = false;
   }
 
--- a/image/decoders/nsPNGDecoder.cpp
+++ b/image/decoders/nsPNGDecoder.cpp
@@ -21,18 +21,16 @@
 #include "png.h"
 #include "RasterImage.h"
 #include "SurfacePipeFactory.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/Telemetry.h"
 
 using namespace mozilla::gfx;
 
-using std::min;
-
 namespace mozilla {
 namespace image {
 
 static LazyLogModule sPNGLog("PNGDecoder");
 static LazyLogModule sPNGDecoderAccountingLog("PNGDecoderAccounting");
 
 // Limit image dimensions (bug #251381, #591822, and #967656)
 #ifndef MOZ_PNG_MAX_DIMENSION
@@ -97,18 +95,16 @@ const uint8_t
 nsPNGDecoder::pngSignatureBytes[] = { 137, 80, 78, 71, 13, 10, 26, 10 };
 
 nsPNGDecoder::nsPNGDecoder(RasterImage* aImage)
  : Decoder(aImage)
  , mLexer(Transition::ToUnbuffered(State::FINISHED_PNG_DATA,
                                    State::PNG_DATA,
                                    SIZE_MAX),
           Transition::TerminateSuccess())
- , mNextTransition(Transition::ContinueUnbuffered(State::PNG_DATA))
- , mLastChunkLength(0)
  , mPNG(nullptr)
  , mInfo(nullptr)
  , mCMSLine(nullptr)
  , interlacebuf(nullptr)
  , mInProfile(nullptr)
  , mTransform(nullptr)
  , format(gfx::SurfaceFormat::UNKNOWN)
  , mCMSMode(0)
@@ -176,66 +172,67 @@ nsPNGDecoder::PostHasTransparencyIfNeede
         PostHasTransparency();
       }
       return;
   }
 }
 
 // CreateFrame() is used for both simple and animated images.
 nsresult
-nsPNGDecoder::CreateFrame(const FrameInfo& aFrameInfo)
+nsPNGDecoder::CreateFrame(SurfaceFormat aFormat,
+                          const IntRect& aFrameRect,
+                          bool aIsInterlaced)
 {
   MOZ_ASSERT(HasSize());
   MOZ_ASSERT(!IsMetadataDecode());
 
   // Check if we have transparency, and send notifications if needed.
-  auto transparency = GetTransparencyType(aFrameInfo.mFormat, aFrameInfo.mFrameRect);
+  auto transparency = GetTransparencyType(aFormat, aFrameRect);
   PostHasTransparencyIfNeeded(transparency);
   SurfaceFormat format = transparency == TransparencyType::eNone
                        ? SurfaceFormat::B8G8R8X8
                        : SurfaceFormat::B8G8R8A8;
 
   // Make sure there's no animation or padding if we're downscaling.
   MOZ_ASSERT_IF(mDownscaler, mNumFrames == 0);
   MOZ_ASSERT_IF(mDownscaler, !GetImageMetadata().HasAnimation());
   MOZ_ASSERT_IF(mDownscaler, transparency != TransparencyType::eFrameRect);
 
   IntSize targetSize = mDownscaler ? mDownscaler->TargetSize()
                                    : GetSize();
 
   // If this image is interlaced, we can display better quality intermediate
   // results to the user by post processing them with ADAM7InterpolatingFilter.
-  SurfacePipeFlags pipeFlags = aFrameInfo.mIsInterlaced
+  SurfacePipeFlags pipeFlags = aIsInterlaced
                              ? SurfacePipeFlags::ADAM7_INTERPOLATE
                              : SurfacePipeFlags();
 
   if (mNumFrames == 0) {
     // The first frame may be displayed progressively.
     pipeFlags |= SurfacePipeFlags::PROGRESSIVE_DISPLAY;
   }
 
   Maybe<SurfacePipe> pipe =
-    SurfacePipeFactory::CreateSurfacePipe(this, mNumFrames, GetSize(),
-                                          targetSize, aFrameInfo.mFrameRect,
-                                          format, pipeFlags);
+    SurfacePipeFactory::CreateSurfacePipe(this, mNumFrames, GetSize(), targetSize,
+                                          aFrameRect, format, pipeFlags);
 
   if (!pipe) {
     mPipe = SurfacePipe();
     return NS_ERROR_FAILURE;
   }
 
   mPipe = Move(*pipe);
 
-  mFrameRect = aFrameInfo.mFrameRect;
+  mFrameRect = aFrameRect;
   mPass = 0;
 
   MOZ_LOG(sPNGDecoderAccountingLog, LogLevel::Debug,
          ("PNGDecoderAccounting: nsPNGDecoder::CreateFrame -- created "
           "image frame with %dx%d pixels for decoder %p",
-          mFrameRect.width, mFrameRect.height, this));
+          aFrameRect.width, aFrameRect.height, this));
 
 #ifdef PNG_APNG_SUPPORTED
   if (png_get_valid(mPNG, mInfo, PNG_INFO_acTL)) {
     mAnimInfo = AnimFrameInfo(mPNG, mInfo);
 
     if (mAnimInfo.mDispose == DisposalMethod::CLEAR) {
       // We may have to display the background under this image during
       // animation playback, so we regard it as transparent.
@@ -366,47 +363,36 @@ nsPNGDecoder::DoDecode(SourceBufferItera
     }
     MOZ_CRASH("Unknown State");
   });
 }
 
 LexerTransition<nsPNGDecoder::State>
 nsPNGDecoder::ReadPNGData(const char* aData, size_t aLength)
 {
-  // If we were waiting until after returning from a yield to call
-  // CreateFrame(), call it now.
-  if (mNextFrameInfo) {
-    if (NS_FAILED(CreateFrame(*mNextFrameInfo))) {
-      return Transition::TerminateFailure();
-    }
-
-    MOZ_ASSERT(mImageData, "Should have a buffer now");
-    mNextFrameInfo = Nothing();
-  }
-
   // libpng uses setjmp/longjmp for error handling.
   if (setjmp(png_jmpbuf(mPNG))) {
     return Transition::TerminateFailure();
   }
 
   // Pass the data off to libpng.
-  mLastChunkLength = aLength;
-  mNextTransition = Transition::ContinueUnbuffered(State::PNG_DATA);
   png_process_data(mPNG, mInfo,
                    reinterpret_cast<unsigned char*>(const_cast<char*>((aData))),
                    aLength);
 
-  // Make sure that we've reached a terminal state if decoding is done.
-  MOZ_ASSERT_IF(GetDecodeDone(), mNextTransition.NextStateIsTerminal());
-  MOZ_ASSERT_IF(HasError(), mNextTransition.NextStateIsTerminal());
+  if (HasError()) {
+    return Transition::TerminateFailure();
+  }
 
-  // Continue with whatever transition the callback code requested. We
-  // initialized this to Transition::ContinueUnbuffered(State::PNG_DATA) above,
-  // so by default we just continue the unbuffered read.
-  return mNextTransition;
+  if (GetDecodeDone()) {
+    return Transition::TerminateSuccess();
+  }
+
+  // Keep reading data.
+  return Transition::ContinueUnbuffered(State::PNG_DATA);
 }
 
 LexerTransition<nsPNGDecoder::State>
 nsPNGDecoder::FinishedPNGData()
 {
   // Since we set up an unbuffered read for SIZE_MAX bytes, if we actually read
   // all that data something is really wrong.
   MOZ_ASSERT_UNREACHABLE("Read the entire address space?");
@@ -692,32 +678,31 @@ nsPNGDecoder::info_callback(png_structp 
     // call PostHasTransparency in the metadata decode if we need to. So it's okay
     // to pass IntRect(0, 0, width, height) here for animated images; they will
     // call with the proper first frame rect in the full decode.
     auto transparency = decoder->GetTransparencyType(decoder->format, frameRect);
     decoder->PostHasTransparencyIfNeeded(transparency);
 
     // We have the metadata we're looking for, so stop here, before we allocate
     // buffers below.
-    return decoder->Terminate(png_ptr, TerminalState::SUCCESS);
+    png_process_data_pause(png_ptr, /* save = */ false);
+    return;
   }
 
 #ifdef PNG_APNG_SUPPORTED
   if (isAnimated) {
     png_set_progressive_frame_fn(png_ptr, nsPNGDecoder::frame_info_callback,
                                  nullptr);
   }
 
   if (png_get_first_frame_is_hidden(png_ptr, info_ptr)) {
     decoder->mFrameIsHidden = true;
   } else {
 #endif
-    nsresult rv = decoder->CreateFrame(FrameInfo{ decoder->format,
-                                                  frameRect,
-                                                  isInterlaced });
+    nsresult rv = decoder->CreateFrame(decoder->format, frameRect, isInterlaced);
     if (NS_FAILED(rv)) {
       png_longjmp(decoder->mPNG, 5); // NS_ERROR_OUT_OF_MEMORY
     }
     MOZ_ASSERT(decoder->mImageData, "Should have a buffer now");
 #ifdef PNG_APNG_SUPPORTED
   }
 #endif
 
@@ -909,83 +894,50 @@ nsPNGDecoder::WriteRow(uint8_t* aRow)
       png_longjmp(mPNG, 1);  // Abort the decode.
   }
 
   MOZ_ASSERT(WriteState(result) != WriteState::FAILURE);
 
   PostInvalidationIfNeeded();
 }
 
-void
-nsPNGDecoder::Terminate(png_structp aPNGStruct, TerminalState aState)
-{
-  // Stop processing data. Note that we intentionally ignore the return value of
-  // png_process_data_pause(), which tells us how many bytes of the data that
-  // was passed to png_process_data() have not been consumed yet, because now
-  // that we've reached a terminal state, we won't do any more decoding or call
-  // back into libpng anymore.
-  png_process_data_pause(aPNGStruct, /* save = */ false);
-
-  mNextTransition = aState == TerminalState::SUCCESS
-                  ? Transition::TerminateSuccess()
-                  : Transition::TerminateFailure();
-}
-
-void
-nsPNGDecoder::Yield(png_structp aPNGStruct)
-{
-  // Pause data processing. png_process_data_pause() returns how many bytes of
-  // the data that was passed to png_process_data() have not been consumed yet.
-  // We use this information to tell StreamingLexer where to place us in the
-  // input stream when we come back from the yield.
-  png_size_t pendingBytes = png_process_data_pause(aPNGStruct, /* save = */ false);
-
-  MOZ_ASSERT(pendingBytes < mLastChunkLength);
-  size_t consumedBytes = mLastChunkLength - min(pendingBytes, mLastChunkLength);
-
-  mNextTransition =
-    Transition::ContinueUnbufferedAfterYield(State::PNG_DATA, consumedBytes);
-}
-
 #ifdef PNG_APNG_SUPPORTED
 // got the header of a new frame that's coming
 void
 nsPNGDecoder::frame_info_callback(png_structp png_ptr, png_uint_32 frame_num)
 {
   nsPNGDecoder* decoder =
                static_cast<nsPNGDecoder*>(png_get_progressive_ptr(png_ptr));
 
   // old frame is done
   decoder->EndImageFrame();
 
   if (!decoder->mFrameIsHidden && decoder->IsFirstFrameDecode()) {
     // We're about to get a second non-hidden frame, but we only want the first.
     // Stop decoding now. (And avoid allocating the unnecessary buffers below.)
     decoder->PostDecodeDone();
-    return decoder->Terminate(png_ptr, TerminalState::SUCCESS);
+    png_process_data_pause(png_ptr, /* save = */ false);
+    return;
   }
 
   // Only the first frame can be hidden, so unhide unconditionally here.
   decoder->mFrameIsHidden = false;
 
-  // Save the information necessary to create the frame; we'll actually create
-  // it when we return from the yield.
   const IntRect frameRect(png_get_next_frame_x_offset(png_ptr, decoder->mInfo),
                           png_get_next_frame_y_offset(png_ptr, decoder->mInfo),
                           png_get_next_frame_width(png_ptr, decoder->mInfo),
                           png_get_next_frame_height(png_ptr, decoder->mInfo));
 
   const bool isInterlaced = bool(decoder->interlacebuf);
 
-  decoder->mNextFrameInfo = Some(FrameInfo{ decoder->format,
-                                            frameRect,
-                                            isInterlaced });
-
-  // Yield to the caller to notify them that the previous frame is now complete.
-  return decoder->Yield(png_ptr);
+  nsresult rv = decoder->CreateFrame(decoder->format, frameRect, isInterlaced);
+  if (NS_FAILED(rv)) {
+    png_longjmp(decoder->mPNG, 5); // NS_ERROR_OUT_OF_MEMORY
+  }
+  MOZ_ASSERT(decoder->mImageData, "Should have a buffer now");
 }
 #endif
 
 void
 nsPNGDecoder::end_callback(png_structp png_ptr, png_infop info_ptr)
 {
   /* libpng comments:
    *
@@ -1008,20 +960,19 @@ nsPNGDecoder::end_callback(png_structp p
   int32_t loop_count = 0;
 #ifdef PNG_APNG_SUPPORTED
   if (png_get_valid(png_ptr, info_ptr, PNG_INFO_acTL)) {
     int32_t num_plays = png_get_num_plays(png_ptr, info_ptr);
     loop_count = num_plays - 1;
   }
 #endif
 
-  // Send final notifications.
+  // Send final notifications
   decoder->EndImageFrame();
   decoder->PostDecodeDone(loop_count);
-  return decoder->Terminate(png_ptr, TerminalState::SUCCESS);
 }
 
 
 void
 nsPNGDecoder::error_callback(png_structp png_ptr, png_const_charp error_msg)
 {
   MOZ_LOG(sPNGLog, LogLevel::Error, ("libpng error: %s\n", error_msg));
   png_longjmp(png_ptr, 1);
--- a/image/decoders/nsPNGDecoder.h
+++ b/image/decoders/nsPNGDecoder.h
@@ -31,25 +31,19 @@ public:
   bool IsValidICO() const;
 
 private:
   friend class DecoderFactory;
 
   // Decoders should only be instantiated via DecoderFactory.
   explicit nsPNGDecoder(RasterImage* aImage);
 
-  /// The information necessary to create a frame.
-  struct FrameInfo
-  {
-    gfx::SurfaceFormat mFormat;
-    gfx::IntRect mFrameRect;
-    bool mIsInterlaced;
-  };
-
-  nsresult CreateFrame(const FrameInfo& aFrameInfo);
+  nsresult CreateFrame(gfx::SurfaceFormat aFormat,
+                       const gfx::IntRect& aFrameRect,
+                       bool aIsInterlaced);
   void EndImageFrame();
 
   enum class TransparencyType
   {
     eNone,
     eAlpha,
     eFrameRect
   };
@@ -57,46 +51,27 @@ private:
   TransparencyType GetTransparencyType(gfx::SurfaceFormat aFormat,
                                        const gfx::IntRect& aFrameRect);
   void PostHasTransparencyIfNeeded(TransparencyType aTransparencyType);
 
   void PostInvalidationIfNeeded();
 
   void WriteRow(uint8_t* aRow);
 
-  // Convenience methods to make interacting with StreamingLexer from inside
-  // a libpng callback easier.
-  void Terminate(png_structp aPNGStruct, TerminalState aState);
-  void Yield(png_structp aPNGStruct);
-
   enum class State
   {
     PNG_DATA,
     FINISHED_PNG_DATA
   };
 
   LexerTransition<State> ReadPNGData(const char* aData, size_t aLength);
   LexerTransition<State> FinishedPNGData();
 
   StreamingLexer<State> mLexer;
 
-  // The next lexer state transition. We need to store it here because we can't
-  // directly return arbitrary values from libpng callbacks.
-  LexerTransition<State> mNextTransition;
-
-  // We yield to the caller every time we finish decoding a frame. When this
-  // happens, we need to allocate the next frame after returning from the yield.
-  // |mNextFrameInfo| is used to store the information needed to allocate the
-  // next frame.
-  Maybe<FrameInfo> mNextFrameInfo;
-
-  // The length of the last chunk of data passed to ReadPNGData(). We use this
-  // to arrange to arrive back at the correct spot in the data after yielding.
-  size_t mLastChunkLength;
-
 public:
   png_structp mPNG;
   png_infop mInfo;
   nsIntRect mFrameRect;
   uint8_t* mCMSLine;
   uint8_t* interlacebuf;
   qcms_profile* mInProfile;
   qcms_transform* mTransform;