Bug 1126038 - Finish decoding off-main-thread. r=tn a=lmandel
authorSeth Fowler <seth@mozilla.com>
Mon, 26 Jan 2015 22:53:20 -0800
changeset 249826 6dc06329002f6cfc7e05ab5da2365000e0cb824c
parent 249825 e19fc07f7163993156cf7fbcd21e7322e610bff1
child 249827 bfbf0f731f5228b3aa0ee4ef58c761b7c7ae93d5
push id4489
push userraliiev@mozilla.com
push dateMon, 23 Feb 2015 15:17:55 +0000
treeherdermozilla-beta@fd7c3dc24146 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstn, lmandel
bugs1126038
milestone37.0a2
Bug 1126038 - Finish decoding off-main-thread. r=tn a=lmandel
image/src/Decoder.cpp
image/src/Decoder.h
--- a/image/src/Decoder.cpp
+++ b/image/src/Decoder.cpp
@@ -30,16 +30,17 @@ Decoder::Decoder(RasterImage* aImage)
   , mChunkCount(0)
   , mDecodeFlags(0)
   , mBytesDecoded(0)
   , mSendPartialInvalidations(false)
   , mDataDone(false)
   , mDecodeDone(false)
   , mDataError(false)
   , mDecodeAborted(false)
+  , mShouldReportError(false)
   , mImageIsTransient(false)
   , mImageIsLocked(false)
   , mFrameCount(0)
   , mFailCode(NS_OK)
   , mNeedsNewFrame(false)
   , mNeedsToFlushData(false)
   , mInitialized(false)
   , mSizeDecode(false)
@@ -141,24 +142,26 @@ Decoder::Decode()
     if (newState == SourceBufferIterator::COMPLETE) {
       mDataDone = true;
 
       nsresult finalStatus = mIterator->CompletionStatus();
       if (NS_FAILED(finalStatus)) {
         PostDataError();
       }
 
+      CompleteDecode();
       return finalStatus;
     }
 
     MOZ_ASSERT(newState == SourceBufferIterator::READY);
 
     Write(mIterator->Data(), mIterator->Length());
   }
 
+  CompleteDecode();
   return HasError() ? NS_ERROR_FAILURE : NS_OK;
 }
 
 void
 Decoder::Resume()
 {
   DecodePool* decodePool = DecodePool::Singleton();
   MOZ_ASSERT(decodePool);
@@ -236,34 +239,61 @@ Decoder::Write(const char* aBuffer, uint
     mNeedsToFlushData = false;
   }
 
   // Finish telemetry.
   mDecodeTime += (TimeStamp::Now() - start);
 }
 
 void
-Decoder::Finish()
+Decoder::CompleteDecode()
 {
-  MOZ_ASSERT(NS_IsMainThread());
-
   // Implementation-specific finalization
   if (!HasError())
     FinishInternal();
 
   // If the implementation left us mid-frame, finish that up.
   if (mInFrame && !HasError())
     PostFrameStop();
 
   // 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.
+  // decoder, we need to send teardown notifications (and report an error to the
+  // console later).
   if (!IsSizeDecode() && !mDecodeDone && !WasAborted()) {
+    mShouldReportError = true;
 
-    // Log data errors to the error console
+    // If we only have a data error, we're usable if we have at least one
+    // complete frame.
+    if (!HasDecoderError() && GetCompleteFrameCount() > 0) {
+      // We're usable, so do exactly what we should have when the decoder
+      // completed.
+      if (mInFrame) {
+        PostFrameStop();
+      }
+      PostDecodeDone();
+    } else {
+      // We're not usable. Record some final progress indicating the error.
+      if (!IsSizeDecode()) {
+        mProgress |= FLAG_DECODE_COMPLETE | FLAG_ONLOAD_UNBLOCKED;
+      }
+      mProgress |= FLAG_HAS_ERROR;
+    }
+  }
+}
+
+void
+Decoder::Finish()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  MOZ_ASSERT(HasError() || !mInFrame, "Finishing while we're still in a frame");
+
+  // If we detected an error in CompleteDecode(), log it to the error console.
+  if (mShouldReportError && !WasAborted()) {
     nsCOMPtr<nsIConsoleService> consoleService =
       do_GetService(NS_CONSOLESERVICE_CONTRACTID);
     nsCOMPtr<nsIScriptError> errorObject =
       do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
 
     if (consoleService && errorObject && !HasDecoderError()) {
       nsAutoString msg(NS_LITERAL_STRING("Image corrupt or truncated: ") +
                        NS_ConvertUTF8toUTF16(mImage->GetURIString()));
@@ -272,33 +302,16 @@ Decoder::Finish()
                          msg,
                          NS_ConvertUTF8toUTF16(mImage->GetURIString()),
                          EmptyString(), 0, 0, nsIScriptError::errorFlag,
                          "Image", mImage->InnerWindowID()
                        ))) {
         consoleService->LogMessage(errorObject);
       }
     }
-
-    // If we only have a data error, we're usable if we have at least one
-    // complete frame.
-    if (!HasDecoderError() && GetCompleteFrameCount() > 0) {
-      // We're usable, so do exactly what we should have when the decoder
-      // completed.
-      if (mInFrame) {
-        PostFrameStop();
-      }
-      PostDecodeDone();
-    } else {
-      // We're not usable. Record some final progress indicating the error.
-      if (!IsSizeDecode()) {
-        mProgress |= FLAG_DECODE_COMPLETE | FLAG_ONLOAD_UNBLOCKED;
-      }
-      mProgress |= FLAG_HAS_ERROR;
-    }
   }
 
   // Set image metadata before calling DecodingComplete, because
   // DecodingComplete calls Optimize().
   mImageMetadata.SetOnImage(mImage);
 
   if (HasSize()) {
     SetSizeOnImage();
@@ -316,18 +329,16 @@ Decoder::Finish()
 
     mImage->OnDecodingComplete();
   }
 }
 
 void
 Decoder::FinishSharedDecoder()
 {
-  MOZ_ASSERT(NS_IsMainThread());
-
   if (!HasError()) {
     FinishInternal();
   }
 }
 
 nsresult
 Decoder::AllocateFrame(const nsIntSize& aTargetSize /* = nsIntSize() */)
 {
--- a/image/src/Decoder.h
+++ b/image/src/Decoder.h
@@ -398,16 +398,23 @@ protected:
   void PostDecoderError(nsresult aFailCode);
 
   // Returns true if we may have stored data that we need to flush now that we
   // have a new frame to decode into. Callers can use Write() to actually
   // flush the data; see the documentation for that method.
   bool NeedsToFlushData() const { return mNeedsToFlushData; }
 
   /**
+   * 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();
+
+  /**
    * Ensures that a given frame number exists with the given parameters, and
    * returns a RawAccessFrameRef for that frame.
    * It is not possible to create sparse frame arrays; you can only append
    * frames to the current frame array, or if there is only one frame in the
    * array, replace that frame.
    * @aTargetSize specifies the target size we're decoding to. If we're not
    * downscaling during decode, this will always be the same as the image's
    * intrinsic size.
@@ -452,16 +459,17 @@ protected:
 
   uint32_t mDecodeFlags;
   size_t mBytesDecoded;
   bool mSendPartialInvalidations;
   bool mDataDone;
   bool mDecodeDone;
   bool mDataError;
   bool mDecodeAborted;
+  bool mShouldReportError;
   bool mImageIsTransient;
   bool mImageIsLocked;
 
 private:
   uint32_t mFrameCount; // Number of frames, including anything in-progress
 
   nsresult mFailCode;