LCOV - code coverage report
Current view: directory - image/src - RasterImage.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 1219 539 44.2 %
Date: 2012-06-02 Functions: 101 67 66.3 %

       1                 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
       2                 :  * ***** BEGIN LICENSE BLOCK *****
       3                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       4                 :  *
       5                 :  * The contents of this file are subject to the Mozilla Public License Version
       6                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       7                 :  * the License. You may obtain a copy of the License at
       8                 :  * http://www.mozilla.org/MPL/
       9                 :  *
      10                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      11                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      12                 :  * for the specific language governing rights and limitations under the
      13                 :  * License.
      14                 :  *
      15                 :  * The Original Code is mozilla.org code.
      16                 :  *
      17                 :  * The Initial Developer of the Original Code is
      18                 :  * Netscape Communications Corporation.
      19                 :  * Portions created by the Initial Developer are Copyright (C) 2001
      20                 :  * the Initial Developer. All Rights Reserved.
      21                 :  *
      22                 :  * Contributor(s):
      23                 :  *   Stuart Parmenter <pavlov@netscape.com>
      24                 :  *   Chris Saari <saari@netscape.com>
      25                 :  *   Asko Tontti <atontti@cc.hut.fi>
      26                 :  *   Arron Mogge <paper@animecity.nu>
      27                 :  *   Andrew Smith
      28                 :  *   Federico Mena-Quintero <federico@novell.com>
      29                 :  *   Bobby Holley <bobbyholley@gmail.com>
      30                 :  *
      31                 :  * Alternatively, the contents of this file may be used under the terms of
      32                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      33                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      34                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      35                 :  * of those above. If you wish to allow use of your version of this file only
      36                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      37                 :  * use your version of this file under the terms of the MPL, indicate your
      38                 :  * decision by deleting the provisions above and replace them with the notice
      39                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      40                 :  * the provisions above, a recipient may use your version of this file under
      41                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      42                 :  *
      43                 :  * ***** END LICENSE BLOCK ***** */
      44                 : 
      45                 : #include "base/histogram.h"
      46                 : #include "nsComponentManagerUtils.h"
      47                 : #include "imgIContainerObserver.h"
      48                 : #include "ImageErrors.h"
      49                 : #include "Decoder.h"
      50                 : #include "imgIDecoderObserver.h"
      51                 : #include "RasterImage.h"
      52                 : #include "nsIInterfaceRequestor.h"
      53                 : #include "nsIInterfaceRequestorUtils.h"
      54                 : #include "nsAutoPtr.h"
      55                 : #include "nsStringStream.h"
      56                 : #include "prmem.h"
      57                 : #include "prenv.h"
      58                 : #include "ImageLogging.h"
      59                 : #include "ImageLayers.h"
      60                 : 
      61                 : #include "nsPNGDecoder.h"
      62                 : #include "nsGIFDecoder2.h"
      63                 : #include "nsJPEGDecoder.h"
      64                 : #include "nsBMPDecoder.h"
      65                 : #include "nsICODecoder.h"
      66                 : #include "nsIconDecoder.h"
      67                 : 
      68                 : #include "gfxContext.h"
      69                 : 
      70                 : #include "mozilla/Preferences.h"
      71                 : #include "mozilla/StandardInteger.h"
      72                 : #include "mozilla/Telemetry.h"
      73                 : #include "mozilla/TimeStamp.h"
      74                 : #include "mozilla/ClearOnShutdown.h"
      75                 : 
      76                 : using namespace mozilla;
      77                 : using namespace mozilla::image;
      78                 : using namespace mozilla::layers;
      79                 : 
      80                 : // a mask for flags that will affect the decoding
      81                 : #define DECODE_FLAGS_MASK (imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA | imgIContainer::FLAG_DECODE_NO_COLORSPACE_CONVERSION)
      82                 : #define DECODE_FLAGS_DEFAULT 0
      83                 : 
      84                 : /* Accounting for compressed data */
      85                 : #if defined(PR_LOGGING)
      86            1464 : static PRLogModuleInfo *gCompressedImageAccountingLog = PR_NewLogModule ("CompressedImageAccounting");
      87                 : #else
      88                 : #define gCompressedImageAccountingLog
      89                 : #endif
      90                 : 
      91                 : // Tweakable progressive decoding parameters.  These are initialized to 0 here
      92                 : // because otherwise, we have to initialize them in a static initializer, which
      93                 : // makes us slower to start up.
      94                 : static bool gInitializedPrefCaches = false;
      95                 : static PRUint32 gDecodeBytesAtATime = 0;
      96                 : static PRUint32 gMaxMSBeforeYield = 0;
      97                 : static PRUint32 gMaxBytesForSyncDecode = 0;
      98                 : 
      99                 : static void
     100               5 : InitPrefCaches()
     101                 : {
     102                 :   Preferences::AddUintVarCache(&gDecodeBytesAtATime,
     103               5 :                                "image.mem.decode_bytes_at_a_time", 200000);
     104                 :   Preferences::AddUintVarCache(&gMaxMSBeforeYield,
     105               5 :                                "image.mem.max_ms_before_yield", 400);
     106                 :   Preferences::AddUintVarCache(&gMaxBytesForSyncDecode,
     107               5 :                                "image.mem.max_bytes_for_sync_decode", 150000);
     108               5 :   gInitializedPrefCaches = true;
     109               5 : }
     110                 : 
     111                 : /* We define our own error checking macros here for 2 reasons:
     112                 :  *
     113                 :  * 1) Most of the failures we encounter here will (hopefully) be
     114                 :  * the result of decoding failures (ie, bad data) and not code
     115                 :  * failures. As such, we don't want to clutter up debug consoles
     116                 :  * with spurious messages about NS_ENSURE_SUCCESS failures.
     117                 :  *
     118                 :  * 2) We want to set the internal error flag, shutdown properly,
     119                 :  * and end up in an error state.
     120                 :  *
     121                 :  * So this macro should be called when the desired failure behavior
     122                 :  * is to put the container into an error state and return failure.
     123                 :  * It goes without saying that macro won't compile outside of a
     124                 :  * non-static RasterImage method.
     125                 :  */
     126                 : #define LOG_CONTAINER_ERROR                      \
     127                 :   PR_BEGIN_MACRO                                 \
     128                 :   PR_LOG (gImgLog, PR_LOG_ERROR,                 \
     129                 :           ("RasterImage: [this=%p] Error "      \
     130                 :            "detected at line %u for image of "   \
     131                 :            "type %s\n", this, __LINE__,          \
     132                 :            mSourceDataMimeType.get()));          \
     133                 :   PR_END_MACRO
     134                 : 
     135                 : #define CONTAINER_ENSURE_SUCCESS(status)      \
     136                 :   PR_BEGIN_MACRO                              \
     137                 :   nsresult _status = status; /* eval once */  \
     138                 :   if (_status) {                              \
     139                 :     LOG_CONTAINER_ERROR;                      \
     140                 :     DoError();                                \
     141                 :     return _status;                           \
     142                 :   }                                           \
     143                 :  PR_END_MACRO
     144                 : 
     145                 : #define CONTAINER_ENSURE_TRUE(arg, rv)  \
     146                 :   PR_BEGIN_MACRO                        \
     147                 :   if (!(arg)) {                         \
     148                 :     LOG_CONTAINER_ERROR;                \
     149                 :     DoError();                          \
     150                 :     return rv;                          \
     151                 :   }                                     \
     152                 :   PR_END_MACRO
     153                 : 
     154                 : 
     155                 : 
     156                 : static int num_containers;
     157                 : static int num_discardable_containers;
     158                 : static PRInt64 total_source_bytes;
     159                 : static PRInt64 discardable_source_bytes;
     160                 : 
     161                 : /* Are we globally disabling image discarding? */
     162                 : static bool
     163              67 : DiscardingEnabled()
     164                 : {
     165                 :   static bool inited;
     166                 :   static bool enabled;
     167                 : 
     168              67 :   if (!inited) {
     169               4 :     inited = true;
     170                 : 
     171               4 :     enabled = (PR_GetEnv("MOZ_DISABLE_IMAGE_DISCARD") == nsnull);
     172                 :   }
     173                 : 
     174              67 :   return enabled;
     175                 : }
     176                 : 
     177                 : namespace mozilla {
     178                 : namespace image {
     179                 : 
     180            1464 : /* static */ nsRefPtr<RasterImage::DecodeWorker> RasterImage::DecodeWorker::sSingleton;
     181                 : 
     182                 : #ifndef DEBUG
     183                 : NS_IMPL_ISUPPORTS3(RasterImage, imgIContainer, nsIProperties,
     184                 :                    nsISupportsWeakReference)
     185                 : #else
     186             385 : NS_IMPL_ISUPPORTS4(RasterImage, imgIContainer, nsIProperties,
     187                 :                    imgIContainerDebug, nsISupportsWeakReference)
     188                 : #endif
     189                 : 
     190                 : //******************************************************************************
     191              24 : RasterImage::RasterImage(imgStatusTracker* aStatusTracker) :
     192                 :   Image(aStatusTracker), // invoke superclass's constructor
     193                 :   mSize(0,0),
     194                 :   mFrameDecodeFlags(DECODE_FLAGS_DEFAULT),
     195                 :   mAnim(nsnull),
     196                 :   mLoopCount(-1),
     197                 :   mObserver(nsnull),
     198                 :   mLockCount(0),
     199                 :   mDecoder(nsnull),
     200                 :   mDecodeRequest(this),
     201                 :   mBytesDecoded(0),
     202                 :   mDecodeCount(0),
     203                 : #ifdef DEBUG
     204                 :   mFramesNotified(0),
     205                 : #endif
     206                 :   mHasSize(false),
     207                 :   mDecodeOnDraw(false),
     208                 :   mMultipart(false),
     209                 :   mDiscardable(false),
     210                 :   mHasSourceData(false),
     211                 :   mDecoded(false),
     212                 :   mHasBeenDecoded(false),
     213                 :   mInDecoder(false),
     214              24 :   mAnimationFinished(false)
     215                 : {
     216                 :   // Set up the discard tracker node.
     217              24 :   mDiscardTrackerNode.curr = this;
     218              24 :   mDiscardTrackerNode.prev = mDiscardTrackerNode.next = nsnull;
     219              24 :   Telemetry::GetHistogramById(Telemetry::IMAGE_DECODE_COUNT)->Add(0);
     220                 : 
     221                 :   // Statistics
     222              24 :   num_containers++;
     223                 : 
     224                 :   // Register our pref observers if we haven't yet.
     225              24 :   if (NS_UNLIKELY(!gInitializedPrefCaches)) {
     226               5 :     InitPrefCaches();
     227                 :   }
     228              24 : }
     229                 : 
     230                 : //******************************************************************************
     231              72 : RasterImage::~RasterImage()
     232                 : {
     233              24 :   delete mAnim;
     234                 : 
     235              43 :   for (unsigned int i = 0; i < mFrames.Length(); ++i)
     236              19 :     delete mFrames[i];
     237                 : 
     238                 :   // Discardable statistics
     239              24 :   if (mDiscardable) {
     240               8 :     num_discardable_containers--;
     241               8 :     discardable_source_bytes -= mSourceData.Length();
     242                 : 
     243               8 :     PR_LOG (gCompressedImageAccountingLog, PR_LOG_DEBUG,
     244                 :             ("CompressedImageAccounting: destroying RasterImage %p.  "
     245                 :              "Total Containers: %d, Discardable containers: %d, "
     246                 :              "Total source bytes: %lld, Source bytes for discardable containers %lld",
     247                 :              this,
     248                 :              num_containers,
     249                 :              num_discardable_containers,
     250                 :              total_source_bytes,
     251                 :              discardable_source_bytes));
     252                 :   }
     253                 : 
     254              24 :   DiscardTracker::Remove(&mDiscardTrackerNode);
     255                 : 
     256                 :   // If we have a decoder open, shut it down
     257              24 :   if (mDecoder) {
     258               0 :     nsresult rv = ShutdownDecoder(eShutdownIntent_Interrupted);
     259               0 :     if (NS_FAILED(rv))
     260               0 :       NS_WARNING("Failed to shut down decoder in destructor!");
     261                 :   }
     262                 : 
     263                 :   // Total statistics
     264              24 :   num_containers--;
     265              24 :   total_source_bytes -= mSourceData.Length();
     266              96 : }
     267                 : 
     268                 : nsresult
     269              24 : RasterImage::Init(imgIDecoderObserver *aObserver,
     270                 :                   const char* aMimeType,
     271                 :                   const char* aURIString,
     272                 :                   PRUint32 aFlags)
     273                 : {
     274                 :   // We don't support re-initialization
     275              24 :   if (mInitialized)
     276               0 :     return NS_ERROR_ILLEGAL_VALUE;
     277                 : 
     278                 :   // Not sure an error can happen before init, but be safe
     279              24 :   if (mError)
     280               0 :     return NS_ERROR_FAILURE;
     281                 : 
     282              24 :   NS_ENSURE_ARG_POINTER(aMimeType);
     283                 : 
     284                 :   // We must be non-discardable and non-decode-on-draw for
     285                 :   // multipart channels
     286              24 :   NS_ABORT_IF_FALSE(!(aFlags & INIT_FLAG_MULTIPART) ||
     287                 :                     (!(aFlags & INIT_FLAG_DISCARDABLE) &&
     288                 :                      !(aFlags & INIT_FLAG_DECODE_ON_DRAW)),
     289                 :                     "Can't be discardable or decode-on-draw for multipart");
     290                 : 
     291                 :   // Store initialization data
     292              24 :   mObserver = do_GetWeakReference(aObserver);
     293              24 :   mSourceDataMimeType.Assign(aMimeType);
     294              24 :   mURIString.Assign(aURIString);
     295              24 :   mDiscardable = !!(aFlags & INIT_FLAG_DISCARDABLE);
     296              24 :   mDecodeOnDraw = !!(aFlags & INIT_FLAG_DECODE_ON_DRAW);
     297              24 :   mMultipart = !!(aFlags & INIT_FLAG_MULTIPART);
     298                 : 
     299                 :   // Statistics
     300              24 :   if (mDiscardable) {
     301               8 :     num_discardable_containers++;
     302               8 :     discardable_source_bytes += mSourceData.Length();
     303                 :   }
     304                 : 
     305                 :   // If we're being called from ExtractFrame (used by borderimage),
     306                 :   // we don't actually do any decoding. Bail early.
     307                 :   // XXX - This should be removed when we fix borderimage
     308              24 :   if (mSourceDataMimeType.Length() == 0) {
     309               2 :     mInitialized = true;
     310               2 :     return NS_OK;
     311                 :   }
     312                 : 
     313                 :   // Instantiate the decoder
     314                 :   //
     315                 :   // If we're doing decode-on-draw, we want to do a quick first pass to get
     316                 :   // the size but nothing else. We instantiate another decoder later to do
     317                 :   // the full decoding.
     318              22 :   nsresult rv = InitDecoder(/* aDoSizeDecode = */ mDecodeOnDraw);
     319              22 :   CONTAINER_ENSURE_SUCCESS(rv);
     320                 : 
     321                 :   // Mark us as initialized
     322              18 :   mInitialized = true;
     323                 : 
     324              18 :   return NS_OK;
     325                 : }
     326                 : 
     327                 : bool
     328               0 : RasterImage::AdvanceFrame(TimeStamp aTime, nsIntRect* aDirtyRect)
     329                 : {
     330               0 :   NS_ASSERTION(aTime <= TimeStamp::Now(),
     331                 :                "Given time appears to be in the future");
     332                 : 
     333               0 :   imgFrame* nextFrame = nsnull;
     334               0 :   PRUint32 currentFrameIndex = mAnim->currentAnimationFrameIndex;
     335               0 :   PRUint32 nextFrameIndex = mAnim->currentAnimationFrameIndex + 1;
     336               0 :   PRUint32 timeout = 0;
     337               0 :   mImageContainer = nsnull;
     338                 : 
     339                 :   // Figure out if we have the next full frame. This is more complicated than
     340                 :   // just checking for mFrames.Length() because decoders append their frames
     341                 :   // before they're filled in.
     342               0 :   NS_ABORT_IF_FALSE(mDecoder || nextFrameIndex <= mFrames.Length(),
     343                 :                     "How did we get 2 indices too far by incrementing?");
     344                 : 
     345                 :   // If we don't have a decoder, we know we've got everything we're going to
     346                 :   // get. If we do, we only display fully-downloaded frames; everything else
     347                 :   // gets delayed.
     348               0 :   bool haveFullNextFrame = !mDecoder ||
     349               0 :                            nextFrameIndex < mDecoder->GetCompleteFrameCount();
     350                 : 
     351                 :   // If we're done decoding the next frame, go ahead and display it now and
     352                 :   // reinit with the next frame's delay time.
     353               0 :   if (haveFullNextFrame) {
     354               0 :     if (mFrames.Length() == nextFrameIndex) {
     355                 :       // End of Animation, unless we are looping forever
     356                 : 
     357                 :       // If animation mode is "loop once", it's time to stop animating
     358               0 :       if (mAnimationMode == kLoopOnceAnimMode || mLoopCount == 0) {
     359               0 :         mAnimationFinished = true;
     360               0 :         EvaluateAnimation();
     361                 :       }
     362                 : 
     363                 :       // We may have used compositingFrame to build a frame, and then copied
     364                 :       // it back into mFrames[..].  If so, delete composite to save memory
     365               0 :       if (mAnim->compositingFrame && mAnim->lastCompositedFrameIndex == -1) {
     366               0 :         mAnim->compositingFrame = nsnull;
     367                 :       }
     368                 : 
     369               0 :       nextFrameIndex = 0;
     370                 : 
     371               0 :       if (mLoopCount > 0) {
     372               0 :         mLoopCount--;
     373                 :       }
     374                 : 
     375               0 :       if (!mAnimating) {
     376                 :         // break out early if we are actually done animating
     377               0 :         return false;
     378                 :       }
     379                 :     }
     380                 : 
     381               0 :     if (!(nextFrame = mFrames[nextFrameIndex])) {
     382                 :       // something wrong with the next frame, skip it
     383               0 :       mAnim->currentAnimationFrameIndex = nextFrameIndex;
     384               0 :       return false;
     385                 :     }
     386                 : 
     387               0 :     timeout = nextFrame->GetTimeout();
     388                 : 
     389                 :   } else {
     390                 :     // Uh oh, the frame we want to show is currently being decoded (partial)
     391                 :     // Wait until the next refresh driver tick and try again
     392               0 :     return false;
     393                 :   }
     394                 : 
     395               0 :   if (!(timeout > 0)) {
     396               0 :     mAnimationFinished = true;
     397               0 :     EvaluateAnimation();
     398                 :   }
     399                 : 
     400               0 :   if (nextFrameIndex == 0) {
     401               0 :     *aDirtyRect = mAnim->firstFrameRefreshArea;
     402                 :   } else {
     403               0 :     imgFrame *curFrame = mFrames[currentFrameIndex];
     404                 : 
     405               0 :     if (!curFrame) {
     406               0 :       return false;
     407                 :     }
     408                 : 
     409                 :     // Change frame
     410               0 :     if (NS_FAILED(DoComposite(aDirtyRect, curFrame,
     411                 :                               nextFrame, nextFrameIndex))) {
     412                 :       // something went wrong, move on to next
     413               0 :       NS_WARNING("RasterImage::AdvanceFrame(): Compositing of frame failed");
     414               0 :       nextFrame->SetCompositingFailed(true);
     415               0 :       mAnim->currentAnimationFrameIndex = nextFrameIndex;
     416               0 :       mAnim->currentAnimationFrameTime = aTime;
     417               0 :       return false;
     418                 :     }
     419                 : 
     420               0 :     nextFrame->SetCompositingFailed(false);
     421                 :   }
     422                 : 
     423                 :   // Set currentAnimationFrameIndex at the last possible moment
     424               0 :   mAnim->currentAnimationFrameIndex = nextFrameIndex;
     425               0 :   mAnim->currentAnimationFrameTime = aTime;
     426                 : 
     427               0 :   return true;
     428                 : }
     429                 : 
     430                 : //******************************************************************************
     431                 : // [notxpcom] void requestRefresh ([const] in TimeStamp aTime);
     432                 : NS_IMETHODIMP_(void)
     433               0 : RasterImage::RequestRefresh(const mozilla::TimeStamp& aTime)
     434                 : {
     435               0 :   if (!mAnimating || !ShouldAnimate()) {
     436               0 :     return;
     437                 :   }
     438                 : 
     439               0 :   EnsureAnimExists();
     440                 : 
     441                 :   // only advance the frame if the current time is greater than or
     442                 :   // equal to the current frame's end time.
     443               0 :   TimeStamp currentFrameEndTime = GetCurrentImgFrameEndTime();
     444               0 :   bool frameAdvanced = false;
     445                 : 
     446                 :   // The dirtyRect variable will contain an accumulation of the sub-rectangles
     447                 :   // that are dirty for each frame we advance in AdvanceFrame().
     448               0 :   nsIntRect dirtyRect;
     449                 : 
     450               0 :   while (currentFrameEndTime <= aTime) {
     451               0 :     TimeStamp oldFrameEndTime = currentFrameEndTime;
     452               0 :     nsIntRect frameDirtyRect;
     453               0 :     bool didAdvance = AdvanceFrame(aTime, &frameDirtyRect);
     454               0 :     frameAdvanced = frameAdvanced || didAdvance;
     455               0 :     currentFrameEndTime = GetCurrentImgFrameEndTime();
     456                 : 
     457                 :     // Accumulate the dirty area.
     458               0 :     dirtyRect = dirtyRect.Union(frameDirtyRect);
     459                 : 
     460                 :     // if we didn't advance a frame, and our frame end time didn't change,
     461                 :     // then we need to break out of this loop & wait for the frame(s)
     462                 :     // to finish downloading
     463               0 :     if (!frameAdvanced && (currentFrameEndTime == oldFrameEndTime)) {
     464               0 :       break;
     465                 :     }
     466                 :   }
     467                 : 
     468               0 :   if (frameAdvanced) {
     469               0 :     nsCOMPtr<imgIContainerObserver> observer(do_QueryReferent(mObserver));
     470                 : 
     471               0 :     if (!observer) {
     472               0 :       NS_ERROR("Refreshing image after its imgRequest is gone");
     473               0 :       StopAnimation();
     474                 :       return;
     475                 :     }
     476                 : 
     477                 :     // Notify listeners that our frame has actually changed, but do this only
     478                 :     // once for all frames that we've now passed (if AdvanceFrame() was called
     479                 :     // more than once).
     480                 :     #ifdef DEBUG
     481               0 :       mFramesNotified++;
     482                 :     #endif
     483                 : 
     484               0 :     observer->FrameChanged(nsnull, this, &dirtyRect);
     485                 :   }
     486                 : }
     487                 : 
     488                 : //******************************************************************************
     489                 : /* [noscript] imgIContainer extractFrame(PRUint32 aWhichFrame,
     490                 :  *                                       [const] in nsIntRect aRegion,
     491                 :  *                                       in PRUint32 aFlags); */
     492                 : NS_IMETHODIMP
     493               3 : RasterImage::ExtractFrame(PRUint32 aWhichFrame,
     494                 :                           const nsIntRect &aRegion,
     495                 :                           PRUint32 aFlags,
     496                 :                           imgIContainer **_retval)
     497                 : {
     498               3 :   NS_ENSURE_ARG_POINTER(_retval);
     499                 : 
     500                 :   nsresult rv;
     501                 : 
     502               3 :   if (aWhichFrame > FRAME_MAX_VALUE)
     503               0 :     return NS_ERROR_INVALID_ARG;
     504                 : 
     505               3 :   if (mError)
     506               1 :     return NS_ERROR_FAILURE;
     507                 : 
     508                 :   // Disallowed in the API
     509               2 :   if (mInDecoder && (aFlags & imgIContainer::FLAG_SYNC_DECODE))
     510               0 :     return NS_ERROR_FAILURE;
     511                 : 
     512                 :   // Make a new container. This should switch to another class with bug 505959.
     513               4 :   nsRefPtr<RasterImage> img(new RasterImage());
     514                 : 
     515                 :   // We don't actually have a mimetype in this case. The empty string tells the
     516                 :   // init routine not to try to instantiate a decoder. This should be fixed in
     517                 :   // bug 505959.
     518               2 :   img->Init(nsnull, "", "", INIT_FLAG_NONE);
     519               2 :   img->SetSize(aRegion.width, aRegion.height);
     520               2 :   img->mDecoded = true; // Also, we need to mark the image as decoded
     521               2 :   img->mHasBeenDecoded = true;
     522               2 :   img->mFrameDecodeFlags = aFlags & DECODE_FLAGS_MASK;
     523                 : 
     524               2 :   if (img->mFrameDecodeFlags != mFrameDecodeFlags) {
     525                 :     // if we can't discard, then we're screwed; we have no way
     526                 :     // to re-decode.  Similarly if we aren't allowed to do a sync
     527                 :     // decode.
     528               0 :     if (!(aFlags & FLAG_SYNC_DECODE))
     529               0 :       return NS_ERROR_NOT_AVAILABLE;
     530               0 :     if (!CanForciblyDiscard() || mDecoder || mAnim)
     531               0 :       return NS_ERROR_NOT_AVAILABLE;
     532               0 :     ForceDiscard();
     533                 : 
     534               0 :     mFrameDecodeFlags = img->mFrameDecodeFlags;
     535                 :   }
     536                 :   
     537                 :   // If a synchronous decode was requested, do it
     538               2 :   if (aFlags & FLAG_SYNC_DECODE) {
     539               2 :     rv = SyncDecode();
     540               2 :     CONTAINER_ENSURE_SUCCESS(rv);
     541                 :   }
     542                 : 
     543                 :   // Get the frame. If it's not there, it's probably the caller's fault for
     544                 :   // not waiting for the data to be loaded from the network or not passing
     545                 :   // FLAG_SYNC_DECODE
     546                 :   PRUint32 frameIndex = (aWhichFrame == FRAME_FIRST) ?
     547               2 :                         0 : GetCurrentImgFrameIndex();
     548               2 :   imgFrame *frame = GetDrawableImgFrame(frameIndex);
     549               2 :   if (!frame) {
     550               0 :     *_retval = nsnull;
     551               0 :     return NS_ERROR_FAILURE;
     552                 :   }
     553                 : 
     554                 :   // The frame can be smaller than the image. We want to extract only the part
     555                 :   // of the frame that actually exists.
     556               2 :   nsIntRect framerect = frame->GetRect();
     557               2 :   framerect.IntersectRect(framerect, aRegion);
     558                 : 
     559               2 :   if (framerect.IsEmpty())
     560               0 :     return NS_ERROR_NOT_AVAILABLE;
     561                 : 
     562               4 :   nsAutoPtr<imgFrame> subframe;
     563               2 :   rv = frame->Extract(framerect, getter_Transfers(subframe));
     564               2 :   if (NS_FAILED(rv))
     565               0 :     return rv;
     566                 : 
     567               2 :   img->mFrames.AppendElement(subframe.forget());
     568                 : 
     569               2 :   img->mStatusTracker->RecordLoaded();
     570               2 :   img->mStatusTracker->RecordDecoded();
     571                 : 
     572               2 :   *_retval = img.forget().get();
     573                 : 
     574               2 :   return NS_OK;
     575                 : }
     576                 : 
     577                 : //******************************************************************************
     578                 : /* readonly attribute PRInt32 width; */
     579                 : NS_IMETHODIMP
     580               7 : RasterImage::GetWidth(PRInt32 *aWidth)
     581                 : {
     582               7 :   NS_ENSURE_ARG_POINTER(aWidth);
     583                 : 
     584               7 :   if (mError) {
     585               1 :     *aWidth = 0;
     586               1 :     return NS_ERROR_FAILURE;
     587                 :   }
     588                 : 
     589               6 :   *aWidth = mSize.width;
     590               6 :   return NS_OK;
     591                 : }
     592                 : 
     593                 : //******************************************************************************
     594                 : /* readonly attribute PRInt32 height; */
     595                 : NS_IMETHODIMP
     596               7 : RasterImage::GetHeight(PRInt32 *aHeight)
     597                 : {
     598               7 :   NS_ENSURE_ARG_POINTER(aHeight);
     599                 : 
     600               7 :   if (mError) {
     601               1 :     *aHeight = 0;
     602               1 :     return NS_ERROR_FAILURE;
     603                 :   }
     604                 : 
     605               6 :   *aHeight = mSize.height;
     606               6 :   return NS_OK;
     607                 : }
     608                 : 
     609                 : //******************************************************************************
     610                 : /* unsigned short GetType(); */
     611                 : NS_IMETHODIMP
     612               0 : RasterImage::GetType(PRUint16 *aType)
     613                 : {
     614               0 :   NS_ENSURE_ARG_POINTER(aType);
     615                 : 
     616               0 :   *aType = GetType();
     617               0 :   return NS_OK;
     618                 : }
     619                 : 
     620                 : //******************************************************************************
     621                 : /* [noscript, notxpcom] PRUint16 GetType(); */
     622                 : NS_IMETHODIMP_(PRUint16)
     623              29 : RasterImage::GetType()
     624                 : {
     625              29 :   return imgIContainer::TYPE_RASTER;
     626                 : }
     627                 : 
     628                 : imgFrame*
     629              64 : RasterImage::GetImgFrameNoDecode(PRUint32 framenum)
     630                 : {
     631              64 :   if (!mAnim) {
     632              59 :     NS_ASSERTION(framenum == 0, "Don't ask for a frame > 0 if we're not animated!");
     633              59 :     return mFrames.SafeElementAt(0, nsnull);
     634                 :   }
     635               5 :   if (mAnim->lastCompositedFrameIndex == PRInt32(framenum))
     636               0 :     return mAnim->compositingFrame;
     637               5 :   return mFrames.SafeElementAt(framenum, nsnull);
     638                 : }
     639                 : 
     640                 : imgFrame*
     641              32 : RasterImage::GetImgFrame(PRUint32 framenum)
     642                 : {
     643              32 :   nsresult rv = WantDecodedFrames();
     644              32 :   CONTAINER_ENSURE_TRUE(NS_SUCCEEDED(rv), nsnull);
     645              32 :   return GetImgFrameNoDecode(framenum);
     646                 : }
     647                 : 
     648                 : imgFrame*
     649              21 : RasterImage::GetDrawableImgFrame(PRUint32 framenum)
     650                 : {
     651              21 :   imgFrame *frame = GetImgFrame(framenum);
     652                 : 
     653                 :   // We will return a paletted frame if it's not marked as compositing failed
     654                 :   // so we can catch crashes for reasons we haven't investigated.
     655              21 :   if (frame && frame->GetCompositingFailed())
     656               0 :     return nsnull;
     657              21 :   return frame;
     658                 : }
     659                 : 
     660                 : PRUint32
     661              27 : RasterImage::GetCurrentImgFrameIndex() const
     662                 : {
     663              27 :   if (mAnim)
     664               1 :     return mAnim->currentAnimationFrameIndex;
     665                 : 
     666              26 :   return 0;
     667                 : }
     668                 : 
     669                 : TimeStamp
     670               0 : RasterImage::GetCurrentImgFrameEndTime() const
     671                 : {
     672               0 :   imgFrame* currentFrame = mFrames[mAnim->currentAnimationFrameIndex];
     673               0 :   TimeStamp currentFrameTime = mAnim->currentAnimationFrameTime;
     674               0 :   PRInt64 timeout = currentFrame->GetTimeout();
     675                 : 
     676               0 :   if (timeout < 0) {
     677                 :     // We need to return a sentinel value in this case, because our logic
     678                 :     // doesn't work correctly if we have a negative timeout value. The reason
     679                 :     // this positive infinity was chosen was because it works with the loop in
     680                 :     // RequestRefresh() above.
     681               0 :     return TimeStamp() + TimeDuration::FromMilliseconds(UINT64_MAX);
     682                 :   }
     683                 : 
     684               0 :   TimeDuration durationOfTimeout = TimeDuration::FromMilliseconds(timeout);
     685               0 :   TimeStamp currentFrameEndTime = currentFrameTime + durationOfTimeout;
     686                 : 
     687               0 :   return currentFrameEndTime;
     688                 : }
     689                 : 
     690                 : imgFrame*
     691               2 : RasterImage::GetCurrentImgFrame()
     692                 : {
     693               2 :   return GetImgFrame(GetCurrentImgFrameIndex());
     694                 : }
     695                 : 
     696                 : imgFrame*
     697               0 : RasterImage::GetCurrentDrawableImgFrame()
     698                 : {
     699               0 :   return GetDrawableImgFrame(GetCurrentImgFrameIndex());
     700                 : }
     701                 : 
     702                 : //******************************************************************************
     703                 : /* readonly attribute boolean currentFrameIsOpaque; */
     704                 : NS_IMETHODIMP
     705               0 : RasterImage::GetCurrentFrameIsOpaque(bool *aIsOpaque)
     706                 : {
     707               0 :   NS_ENSURE_ARG_POINTER(aIsOpaque);
     708                 : 
     709               0 :   if (mError)
     710               0 :     return NS_ERROR_FAILURE;
     711                 : 
     712                 :   // See if we can get an image frame
     713               0 :   imgFrame *curframe = GetCurrentImgFrame();
     714                 : 
     715                 :   // If we don't get a frame, the safe answer is "not opaque"
     716               0 :   if (!curframe)
     717               0 :     *aIsOpaque = false;
     718                 : 
     719                 :   // Otherwise, we can make a more intelligent decision
     720                 :   else {
     721               0 :     *aIsOpaque = !curframe->GetNeedsBackground();
     722                 : 
     723                 :     // We are also transparent if the current frame's size doesn't cover our
     724                 :     // entire area.
     725               0 :     nsIntRect framerect = curframe->GetRect();
     726               0 :     *aIsOpaque = *aIsOpaque && framerect.IsEqualInterior(nsIntRect(0, 0, mSize.width, mSize.height));
     727                 :   }
     728                 : 
     729               0 :   return NS_OK;
     730                 : }
     731                 : 
     732                 : void
     733               2 : RasterImage::GetCurrentFrameRect(nsIntRect& aRect)
     734                 : {
     735                 :   // Get the current frame
     736               2 :   imgFrame* curframe = GetCurrentImgFrame();
     737                 : 
     738                 :   // If we have the frame, use that rectangle
     739               2 :   if (curframe) {
     740               2 :     aRect = curframe->GetRect();
     741                 :   } else {
     742                 :     // If the frame doesn't exist, we pass the empty rectangle. It's not clear
     743                 :     // whether this is appropriate in general, but at the moment the only
     744                 :     // consumer of this method is imgStatusTracker (when it wants to figure out
     745                 :     // dirty rectangles to send out batched observer updates). This should
     746                 :     // probably be revisited when we fix bug 503973.
     747               0 :     aRect.MoveTo(0, 0);
     748               0 :     aRect.SizeTo(0, 0);
     749                 :   }
     750               2 : }
     751                 : 
     752                 : PRUint32
     753               4 : RasterImage::GetCurrentFrameIndex()
     754                 : {
     755               4 :   return GetCurrentImgFrameIndex();
     756                 : }
     757                 : 
     758                 : PRUint32
     759             230 : RasterImage::GetNumFrames()
     760                 : {
     761             230 :   return mFrames.Length();
     762                 : }
     763                 : 
     764                 : //******************************************************************************
     765                 : /* readonly attribute boolean animated; */
     766                 : NS_IMETHODIMP
     767              16 : RasterImage::GetAnimated(bool *aAnimated)
     768                 : {
     769              16 :   if (mError)
     770               2 :     return NS_ERROR_FAILURE;
     771                 : 
     772              14 :   NS_ENSURE_ARG_POINTER(aAnimated);
     773                 : 
     774                 :   // If we have mAnim, we can know for sure
     775              14 :   if (mAnim) {
     776               0 :     *aAnimated = true;
     777               0 :     return NS_OK;
     778                 :   }
     779                 : 
     780                 :   // Otherwise, we need to have been decoded to know for sure, since if we were
     781                 :   // decoded at least once mAnim would have been created for animated images
     782              14 :   if (!mHasBeenDecoded)
     783              12 :     return NS_ERROR_NOT_AVAILABLE;
     784                 : 
     785                 :   // We know for sure
     786               2 :   *aAnimated = false;
     787                 : 
     788               2 :   return NS_OK;
     789                 : }
     790                 : 
     791                 : 
     792                 : //******************************************************************************
     793                 : /* [noscript] gfxImageSurface copyFrame(in PRUint32 aWhichFrame,
     794                 :  *                                      in PRUint32 aFlags); */
     795                 : NS_IMETHODIMP
     796              19 : RasterImage::CopyFrame(PRUint32 aWhichFrame,
     797                 :                        PRUint32 aFlags,
     798                 :                        gfxImageSurface **_retval)
     799                 : {
     800              19 :   if (aWhichFrame > FRAME_MAX_VALUE)
     801               0 :     return NS_ERROR_INVALID_ARG;
     802                 : 
     803              19 :   if (mError)
     804               0 :     return NS_ERROR_FAILURE;
     805                 : 
     806                 :   // Disallowed in the API
     807              19 :   if (mInDecoder && (aFlags & imgIContainer::FLAG_SYNC_DECODE))
     808               0 :     return NS_ERROR_FAILURE;
     809                 : 
     810                 :   nsresult rv;
     811                 : 
     812              19 :   PRUint32 desiredDecodeFlags = aFlags & DECODE_FLAGS_MASK;
     813              19 :   if (desiredDecodeFlags != mFrameDecodeFlags) {
     814                 :     // if we can't discard, then we're screwed; we have no way
     815                 :     // to re-decode.  Similarly if we aren't allowed to do a sync
     816                 :     // decode.
     817               0 :     if (!(aFlags & FLAG_SYNC_DECODE))
     818               0 :       return NS_ERROR_NOT_AVAILABLE;
     819               0 :     if (!CanForciblyDiscard() || mDecoder || mAnim)
     820               0 :       return NS_ERROR_NOT_AVAILABLE;
     821               0 :     ForceDiscard();
     822                 : 
     823               0 :     mFrameDecodeFlags = desiredDecodeFlags;
     824                 :   }
     825                 : 
     826                 :   // If requested, synchronously flush any data we have lying around to the decoder
     827              19 :   if (aFlags & FLAG_SYNC_DECODE) {
     828              19 :     rv = SyncDecode();
     829              19 :     CONTAINER_ENSURE_SUCCESS(rv);
     830                 :   }
     831                 : 
     832              19 :   NS_ENSURE_ARG_POINTER(_retval);
     833                 : 
     834                 :   // Get the frame. If it's not there, it's probably the caller's fault for
     835                 :   // not waiting for the data to be loaded from the network or not passing
     836                 :   // FLAG_SYNC_DECODE
     837                 :   PRUint32 frameIndex = (aWhichFrame == FRAME_FIRST) ?
     838              19 :                         0 : GetCurrentImgFrameIndex();
     839              19 :   imgFrame *frame = GetDrawableImgFrame(frameIndex);
     840              19 :   if (!frame) {
     841               0 :     *_retval = nsnull;
     842               0 :     return NS_ERROR_FAILURE;
     843                 :   }
     844                 : 
     845              38 :   nsRefPtr<gfxPattern> pattern;
     846              19 :   frame->GetPattern(getter_AddRefs(pattern));
     847              19 :   nsIntRect intframerect = frame->GetRect();
     848              19 :   gfxRect framerect(intframerect.x, intframerect.y, intframerect.width, intframerect.height);
     849                 : 
     850                 :   // Create a 32-bit image surface of our size, but draw using the frame's
     851                 :   // rect, implicitly padding the frame out to the image's size.
     852                 :   nsRefPtr<gfxImageSurface> imgsurface = new gfxImageSurface(gfxIntSize(mSize.width, mSize.height),
     853              57 :                                                              gfxASurface::ImageFormatARGB32);
     854              38 :   gfxContext ctx(imgsurface);
     855              19 :   ctx.SetOperator(gfxContext::OPERATOR_SOURCE);
     856              19 :   ctx.Rectangle(framerect);
     857              19 :   ctx.Translate(framerect.TopLeft());
     858              19 :   ctx.SetPattern(pattern);
     859              19 :   ctx.Fill();
     860                 : 
     861              19 :   *_retval = imgsurface.forget().get();
     862              19 :   return NS_OK;
     863                 : }
     864                 : 
     865                 : //******************************************************************************
     866                 : /* [noscript] gfxASurface getFrame(in PRUint32 aWhichFrame,
     867                 :  *                                 in PRUint32 aFlags); */
     868                 : NS_IMETHODIMP
     869               0 : RasterImage::GetFrame(PRUint32 aWhichFrame,
     870                 :                       PRUint32 aFlags,
     871                 :                       gfxASurface **_retval)
     872                 : {
     873               0 :   if (aWhichFrame > FRAME_MAX_VALUE)
     874               0 :     return NS_ERROR_INVALID_ARG;
     875                 : 
     876               0 :   if (mError)
     877               0 :     return NS_ERROR_FAILURE;
     878                 : 
     879                 :   // Disallowed in the API
     880               0 :   if (mInDecoder && (aFlags & imgIContainer::FLAG_SYNC_DECODE))
     881               0 :     return NS_ERROR_FAILURE;
     882                 : 
     883               0 :   nsresult rv = NS_OK;
     884                 : 
     885               0 :   if (mDecoded) {
     886                 :     // If we have decoded data, and it is not a perfect match for what we are
     887                 :     // looking for, we must discard to be able to generate the proper data.
     888               0 :     PRUint32 desiredDecodeFlags = aFlags & DECODE_FLAGS_MASK;
     889               0 :     if (desiredDecodeFlags != mFrameDecodeFlags) {
     890                 :       // if we can't discard, then we're screwed; we have no way
     891                 :       // to re-decode.  Similarly if we aren't allowed to do a sync
     892                 :       // decode.
     893               0 :       if (!(aFlags & FLAG_SYNC_DECODE))
     894               0 :         return NS_ERROR_NOT_AVAILABLE;
     895               0 :       if (!CanForciblyDiscard() || mDecoder || mAnim)
     896               0 :         return NS_ERROR_NOT_AVAILABLE;
     897                 :   
     898               0 :       ForceDiscard();
     899                 :   
     900               0 :       mFrameDecodeFlags = desiredDecodeFlags;
     901                 :     }
     902                 :   }
     903                 : 
     904                 :   // If the caller requested a synchronous decode, do it
     905               0 :   if (aFlags & FLAG_SYNC_DECODE) {
     906               0 :     rv = SyncDecode();
     907               0 :     CONTAINER_ENSURE_SUCCESS(rv);
     908                 :   }
     909                 : 
     910                 :   // Get the frame. If it's not there, it's probably the caller's fault for
     911                 :   // not waiting for the data to be loaded from the network or not passing
     912                 :   // FLAG_SYNC_DECODE
     913                 :   PRUint32 frameIndex = (aWhichFrame == FRAME_FIRST) ?
     914               0 :                           0 : GetCurrentImgFrameIndex();
     915               0 :   imgFrame *frame = GetDrawableImgFrame(frameIndex);
     916               0 :   if (!frame) {
     917               0 :     *_retval = nsnull;
     918               0 :     return NS_ERROR_FAILURE;
     919                 :   }
     920                 : 
     921               0 :   nsRefPtr<gfxASurface> framesurf;
     922                 : 
     923                 :   // If this frame covers the entire image, we can just reuse its existing
     924                 :   // surface.
     925               0 :   nsIntRect framerect = frame->GetRect();
     926               0 :   if (framerect.x == 0 && framerect.y == 0 &&
     927                 :       framerect.width == mSize.width &&
     928                 :       framerect.height == mSize.height)
     929               0 :     rv = frame->GetSurface(getter_AddRefs(framesurf));
     930                 : 
     931                 :   // The image doesn't have a surface because it's been optimized away. Create
     932                 :   // one.
     933               0 :   if (!framesurf) {
     934               0 :     nsRefPtr<gfxImageSurface> imgsurf;
     935               0 :     rv = CopyFrame(aWhichFrame, aFlags, getter_AddRefs(imgsurf));
     936               0 :     framesurf = imgsurf;
     937                 :   }
     938                 : 
     939               0 :   *_retval = framesurf.forget().get();
     940                 : 
     941               0 :   return rv;
     942                 : }
     943                 : 
     944                 : 
     945                 : NS_IMETHODIMP
     946               0 : RasterImage::GetImageContainer(ImageContainer **_retval)
     947                 : {
     948               0 :   if (mImageContainer) {
     949               0 :     *_retval = mImageContainer;
     950               0 :     NS_ADDREF(*_retval);
     951               0 :     return NS_OK;
     952                 :   }
     953                 :   
     954               0 :   CairoImage::Data cairoData;
     955               0 :   nsRefPtr<gfxASurface> imageSurface;
     956               0 :   nsresult rv = GetFrame(FRAME_CURRENT, FLAG_SYNC_DECODE, getter_AddRefs(imageSurface));
     957               0 :   NS_ENSURE_SUCCESS(rv, rv);
     958                 : 
     959               0 :   cairoData.mSurface = imageSurface;
     960               0 :   GetWidth(&cairoData.mSize.width);
     961               0 :   GetHeight(&cairoData.mSize.height);
     962                 : 
     963               0 :   mImageContainer = LayerManager::CreateImageContainer();
     964                 :   
     965                 :   // Now create a CairoImage to display the surface.
     966               0 :   layers::Image::Format cairoFormat = layers::Image::CAIRO_SURFACE;
     967               0 :   nsRefPtr<layers::Image> image = mImageContainer->CreateImage(&cairoFormat, 1);
     968               0 :   NS_ASSERTION(image, "Failed to create Image");
     969                 : 
     970               0 :   NS_ASSERTION(image->GetFormat() == cairoFormat, "Wrong format");
     971               0 :   static_cast<CairoImage*>(image.get())->SetData(cairoData);
     972               0 :   mImageContainer->SetCurrentImage(image);
     973                 : 
     974               0 :   *_retval = mImageContainer;
     975               0 :   NS_ADDREF(*_retval);
     976               0 :   return NS_OK;
     977                 : }
     978                 : 
     979                 : size_t
     980               6 : RasterImage::HeapSizeOfSourceWithComputedFallback(nsMallocSizeOfFun aMallocSizeOf) const
     981                 : {
     982                 :   // n == 0 is possible for two reasons. 
     983                 :   // - This is a zero-length image.
     984                 :   // - We're on a platform where moz_malloc_size_of always returns 0.
     985                 :   // In either case the fallback works appropriately.
     986               6 :   size_t n = mSourceData.SizeOfExcludingThis(aMallocSizeOf);
     987               6 :   if (n == 0) {
     988               0 :     n = mSourceData.Length();
     989               0 :     NS_ABORT_IF_FALSE(StoringSourceData() || (n == 0),
     990                 :                       "Non-zero source data size when we aren't storing it?");
     991                 :   }
     992               6 :   return n;
     993                 : }
     994                 : 
     995                 : static size_t
     996              18 : SizeOfDecodedWithComputedFallbackIfHeap(
     997                 :   const nsTArray<imgFrame*>& aFrames, gfxASurface::MemoryLocation aLocation,
     998                 :   nsMallocSizeOfFun aMallocSizeOf)
     999                 : {
    1000              18 :   size_t n = 0;
    1001              30 :   for (PRUint32 i = 0; i < aFrames.Length(); ++i) {
    1002              12 :     imgFrame* frame = aFrames.SafeElementAt(i, nsnull);
    1003              12 :     NS_ABORT_IF_FALSE(frame, "Null frame in frame array!");
    1004              12 :     n += frame->SizeOfExcludingThisWithComputedFallbackIfHeap(aLocation, aMallocSizeOf);
    1005                 :   }
    1006                 : 
    1007              18 :   return n;
    1008                 : }
    1009                 : 
    1010                 : size_t
    1011               6 : RasterImage::HeapSizeOfDecodedWithComputedFallback(nsMallocSizeOfFun aMallocSizeOf) const
    1012                 : {
    1013                 :   return SizeOfDecodedWithComputedFallbackIfHeap(
    1014               6 :            mFrames, gfxASurface::MEMORY_IN_PROCESS_HEAP, aMallocSizeOf);
    1015                 : }
    1016                 : 
    1017                 : size_t
    1018               6 : RasterImage::NonHeapSizeOfDecoded() const
    1019                 : {
    1020               6 :   return SizeOfDecodedWithComputedFallbackIfHeap(mFrames, gfxASurface::MEMORY_IN_PROCESS_NONHEAP, NULL);
    1021                 : }
    1022                 : 
    1023                 : size_t
    1024               6 : RasterImage::OutOfProcessSizeOfDecoded() const
    1025                 : {
    1026               6 :   return SizeOfDecodedWithComputedFallbackIfHeap(mFrames, gfxASurface::MEMORY_OUT_OF_PROCESS, NULL);
    1027                 : }
    1028                 : 
    1029                 : void
    1030               0 : RasterImage::DeleteImgFrame(PRUint32 framenum)
    1031                 : {
    1032               0 :   NS_ABORT_IF_FALSE(framenum < mFrames.Length(), "Deleting invalid frame!");
    1033                 : 
    1034               0 :   delete mFrames[framenum];
    1035               0 :   mFrames[framenum] = nsnull;
    1036               0 : }
    1037                 : 
    1038                 : nsresult
    1039              17 : RasterImage::InternalAddFrameHelper(PRUint32 framenum, imgFrame *aFrame,
    1040                 :                                     PRUint8 **imageData, PRUint32 *imageLength,
    1041                 :                                     PRUint32 **paletteData, PRUint32 *paletteLength)
    1042                 : {
    1043              17 :   NS_ABORT_IF_FALSE(framenum <= mFrames.Length(), "Invalid frame index!");
    1044              17 :   if (framenum > mFrames.Length())
    1045               0 :     return NS_ERROR_INVALID_ARG;
    1046                 : 
    1047              34 :   nsAutoPtr<imgFrame> frame(aFrame);
    1048                 : 
    1049              17 :   if (paletteData && paletteLength)
    1050               2 :     frame->GetPaletteData(paletteData, paletteLength);
    1051                 : 
    1052              17 :   frame->GetImageData(imageData, imageLength);
    1053                 : 
    1054                 :   // We are in the middle of decoding. This will be unlocked when we finish the
    1055                 :   // decoder->Write() call.
    1056              17 :   frame->LockImageData();
    1057                 : 
    1058              17 :   mFrames.InsertElementAt(framenum, frame.forget());
    1059                 : 
    1060              17 :   return NS_OK;
    1061                 : }
    1062                 :                                   
    1063                 : nsresult
    1064              17 : RasterImage::InternalAddFrame(PRUint32 framenum,
    1065                 :                               PRInt32 aX, PRInt32 aY,
    1066                 :                               PRInt32 aWidth, PRInt32 aHeight,
    1067                 :                               gfxASurface::gfxImageFormat aFormat,
    1068                 :                               PRUint8 aPaletteDepth,
    1069                 :                               PRUint8 **imageData,
    1070                 :                               PRUint32 *imageLength,
    1071                 :                               PRUint32 **paletteData,
    1072                 :                               PRUint32 *paletteLength)
    1073                 : {
    1074                 :   // We assume that we're in the middle of decoding because we unlock the
    1075                 :   // previous frame when we create a new frame, and only when decoding do we
    1076                 :   // lock frames.
    1077              17 :   NS_ABORT_IF_FALSE(mInDecoder, "Only decoders may add frames!");
    1078                 : 
    1079              17 :   NS_ABORT_IF_FALSE(framenum <= mFrames.Length(), "Invalid frame index!");
    1080              17 :   if (framenum > mFrames.Length())
    1081               0 :     return NS_ERROR_INVALID_ARG;
    1082                 : 
    1083              34 :   nsAutoPtr<imgFrame> frame(new imgFrame());
    1084                 : 
    1085              17 :   nsresult rv = frame->Init(aX, aY, aWidth, aHeight, aFormat, aPaletteDepth);
    1086              17 :   NS_ENSURE_SUCCESS(rv, rv);
    1087                 : 
    1088                 :   // We know we are in a decoder. Therefore, we must unlock the previous frame
    1089                 :   // when we move on to decoding into the next frame.
    1090              17 :   if (mFrames.Length() > 0) {
    1091               2 :     imgFrame *prevframe = mFrames.ElementAt(mFrames.Length() - 1);
    1092               2 :     prevframe->UnlockImageData();
    1093                 :   }
    1094                 : 
    1095              17 :   if (mFrames.Length() == 0) {
    1096                 :     return InternalAddFrameHelper(framenum, frame.forget(), imageData, imageLength, 
    1097              15 :                                   paletteData, paletteLength);
    1098                 :   }
    1099                 : 
    1100               2 :   if (mFrames.Length() == 1) {
    1101                 :     // Since we're about to add our second frame, initialize animation stuff
    1102               1 :     EnsureAnimExists();
    1103                 :     
    1104                 :     // If we dispose of the first frame by clearing it, then the
    1105                 :     // First Frame's refresh area is all of itself.
    1106                 :     // RESTORE_PREVIOUS is invalid (assumed to be DISPOSE_CLEAR)
    1107               1 :     PRInt32 frameDisposalMethod = mFrames[0]->GetFrameDisposalMethod();
    1108               1 :     if (frameDisposalMethod == kDisposeClear ||
    1109                 :         frameDisposalMethod == kDisposeRestorePrevious)
    1110               1 :       mAnim->firstFrameRefreshArea = mFrames[0]->GetRect();
    1111                 :   }
    1112                 : 
    1113                 :   // Calculate firstFrameRefreshArea
    1114                 :   // Some gifs are huge but only have a small area that they animate
    1115                 :   // We only need to refresh that small area when Frame 0 comes around again
    1116               2 :   nsIntRect frameRect = frame->GetRect();
    1117                 :   mAnim->firstFrameRefreshArea.UnionRect(mAnim->firstFrameRefreshArea, 
    1118               2 :                                          frameRect);
    1119                 :   
    1120                 :   rv = InternalAddFrameHelper(framenum, frame.forget(), imageData, imageLength,
    1121               2 :                               paletteData, paletteLength);
    1122                 :   
    1123                 :   // We may be able to start animating, if we now have enough frames
    1124               2 :   EvaluateAnimation();
    1125                 :   
    1126               2 :   return rv;
    1127                 : }
    1128                 : 
    1129                 : nsresult
    1130              21 : RasterImage::SetSize(PRInt32 aWidth, PRInt32 aHeight)
    1131                 : {
    1132              21 :   if (mError)
    1133               0 :     return NS_ERROR_FAILURE;
    1134                 : 
    1135                 :   // Ensure that we have positive values
    1136                 :   // XXX - Why isn't the size unsigned? Should this be changed?
    1137              21 :   if ((aWidth < 0) || (aHeight < 0))
    1138               0 :     return NS_ERROR_INVALID_ARG;
    1139                 : 
    1140                 :   // if we already have a size, check the new size against the old one
    1141              21 :   if (mHasSize &&
    1142                 :       ((aWidth != mSize.width) || (aHeight != mSize.height))) {
    1143                 : 
    1144                 :     // Alter the warning depending on whether the channel is multipart
    1145               0 :     if (!mMultipart)
    1146               0 :       NS_WARNING("Image changed size on redecode! This should not happen!");
    1147                 :     else
    1148               0 :       NS_WARNING("Multipart channel sent an image of a different size");
    1149                 : 
    1150                 :     // Make the decoder aware of the error so that it doesn't try to call
    1151                 :     // FinishInternal during ShutdownDecoder.
    1152               0 :     if (mDecoder)
    1153               0 :       mDecoder->PostResizeError();
    1154                 : 
    1155               0 :     DoError();
    1156               0 :     return NS_ERROR_UNEXPECTED;
    1157                 :   }
    1158                 : 
    1159                 :   // Set the size and flag that we have it
    1160              21 :   mSize.SizeTo(aWidth, aHeight);
    1161              21 :   mHasSize = true;
    1162                 : 
    1163              21 :   return NS_OK;
    1164                 : }
    1165                 : 
    1166                 : nsresult
    1167              17 : RasterImage::EnsureFrame(PRUint32 aFrameNum, PRInt32 aX, PRInt32 aY,
    1168                 :                          PRInt32 aWidth, PRInt32 aHeight,
    1169                 :                          gfxASurface::gfxImageFormat aFormat,
    1170                 :                          PRUint8 aPaletteDepth,
    1171                 :                          PRUint8 **imageData, PRUint32 *imageLength,
    1172                 :                          PRUint32 **paletteData, PRUint32 *paletteLength)
    1173                 : {
    1174              17 :   if (mError)
    1175               0 :     return NS_ERROR_FAILURE;
    1176                 : 
    1177              17 :   NS_ENSURE_ARG_POINTER(imageData);
    1178              17 :   NS_ENSURE_ARG_POINTER(imageLength);
    1179              17 :   NS_ABORT_IF_FALSE(aFrameNum <= mFrames.Length(), "Invalid frame index!");
    1180                 : 
    1181              17 :   if (aPaletteDepth > 0) {
    1182               2 :     NS_ENSURE_ARG_POINTER(paletteData);
    1183               2 :     NS_ENSURE_ARG_POINTER(paletteLength);
    1184                 :   }
    1185                 : 
    1186              17 :   if (aFrameNum > mFrames.Length())
    1187               0 :     return NS_ERROR_INVALID_ARG;
    1188                 : 
    1189                 :   // Adding a frame that doesn't already exist.
    1190              17 :   if (aFrameNum == mFrames.Length())
    1191                 :     return InternalAddFrame(aFrameNum, aX, aY, aWidth, aHeight, aFormat, 
    1192                 :                             aPaletteDepth, imageData, imageLength,
    1193              17 :                             paletteData, paletteLength);
    1194                 : 
    1195               0 :   imgFrame *frame = GetImgFrame(aFrameNum);
    1196               0 :   if (!frame)
    1197                 :     return InternalAddFrame(aFrameNum, aX, aY, aWidth, aHeight, aFormat, 
    1198                 :                             aPaletteDepth, imageData, imageLength,
    1199               0 :                             paletteData, paletteLength);
    1200                 : 
    1201                 :   // See if we can re-use the frame that already exists.
    1202               0 :   nsIntRect rect = frame->GetRect();
    1203               0 :   if (rect.x == aX && rect.y == aY && rect.width == aWidth &&
    1204               0 :       rect.height == aHeight && frame->GetFormat() == aFormat &&
    1205               0 :       frame->GetPaletteDepth() == aPaletteDepth) {
    1206               0 :     frame->GetImageData(imageData, imageLength);
    1207               0 :     if (paletteData) {
    1208               0 :       frame->GetPaletteData(paletteData, paletteLength);
    1209                 :     }
    1210                 : 
    1211                 :     // We can re-use the frame if it has image data.
    1212               0 :     if (*imageData && paletteData && *paletteData) {
    1213               0 :       return NS_OK;
    1214                 :     }
    1215               0 :     if (*imageData && !paletteData) {
    1216               0 :       return NS_OK;
    1217                 :     }
    1218                 :   }
    1219                 : 
    1220               0 :   DeleteImgFrame(aFrameNum);
    1221                 :   return InternalAddFrame(aFrameNum, aX, aY, aWidth, aHeight, aFormat,
    1222                 :                           aPaletteDepth, imageData, imageLength,
    1223               0 :                           paletteData, paletteLength);
    1224                 : }
    1225                 : 
    1226                 : nsresult
    1227              15 : RasterImage::EnsureFrame(PRUint32 aFramenum, PRInt32 aX, PRInt32 aY,
    1228                 :                          PRInt32 aWidth, PRInt32 aHeight,
    1229                 :                          gfxASurface::gfxImageFormat aFormat,
    1230                 :                          PRUint8** imageData, PRUint32* imageLength)
    1231                 : {
    1232                 :   return EnsureFrame(aFramenum, aX, aY, aWidth, aHeight, aFormat,
    1233                 :                      /* aPaletteDepth = */ 0, imageData, imageLength,
    1234                 :                      /* aPaletteData = */ nsnull,
    1235              15 :                      /* aPaletteLength = */ nsnull);
    1236                 : }
    1237                 : 
    1238                 : void
    1239              32 : RasterImage::FrameUpdated(PRUint32 aFrameNum, nsIntRect &aUpdatedRect)
    1240                 : {
    1241              32 :   NS_ABORT_IF_FALSE(aFrameNum < mFrames.Length(), "Invalid frame index!");
    1242                 : 
    1243              32 :   imgFrame *frame = GetImgFrameNoDecode(aFrameNum);
    1244              32 :   NS_ABORT_IF_FALSE(frame, "Calling FrameUpdated on frame that doesn't exist!");
    1245                 : 
    1246              32 :   frame->ImageUpdated(aUpdatedRect);
    1247                 :   // The image has changed, so we need to invalidate our cached ImageContainer.
    1248              32 :   mImageContainer = NULL;
    1249              32 : }
    1250                 : 
    1251                 : nsresult
    1252               4 : RasterImage::SetFrameDisposalMethod(PRUint32 aFrameNum,
    1253                 :                                     PRInt32 aDisposalMethod)
    1254                 : {
    1255               4 :   if (mError)
    1256               0 :     return NS_ERROR_FAILURE;
    1257                 : 
    1258               4 :   NS_ABORT_IF_FALSE(aFrameNum < mFrames.Length(), "Invalid frame index!");
    1259               4 :   if (aFrameNum >= mFrames.Length())
    1260               0 :     return NS_ERROR_INVALID_ARG;
    1261                 : 
    1262               4 :   imgFrame *frame = GetImgFrame(aFrameNum);
    1263               4 :   NS_ABORT_IF_FALSE(frame,
    1264                 :                     "Calling SetFrameDisposalMethod on frame that doesn't exist!");
    1265               4 :   NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
    1266                 : 
    1267               4 :   frame->SetFrameDisposalMethod(aDisposalMethod);
    1268                 : 
    1269               4 :   return NS_OK;
    1270                 : }
    1271                 : 
    1272                 : nsresult
    1273               4 : RasterImage::SetFrameTimeout(PRUint32 aFrameNum, PRInt32 aTimeout)
    1274                 : {
    1275               4 :   if (mError)
    1276               0 :     return NS_ERROR_FAILURE;
    1277                 : 
    1278               4 :   NS_ABORT_IF_FALSE(aFrameNum < mFrames.Length(), "Invalid frame index!");
    1279               4 :   if (aFrameNum >= mFrames.Length())
    1280               0 :     return NS_ERROR_INVALID_ARG;
    1281                 : 
    1282               4 :   imgFrame *frame = GetImgFrame(aFrameNum);
    1283               4 :   NS_ABORT_IF_FALSE(frame, "Calling SetFrameTimeout on frame that doesn't exist!");
    1284               4 :   NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
    1285                 : 
    1286               4 :   frame->SetTimeout(aTimeout);
    1287                 : 
    1288               4 :   return NS_OK;
    1289                 : }
    1290                 : 
    1291                 : nsresult
    1292               0 : RasterImage::SetFrameBlendMethod(PRUint32 aFrameNum, PRInt32 aBlendMethod)
    1293                 : {
    1294               0 :   if (mError)
    1295               0 :     return NS_ERROR_FAILURE;
    1296                 : 
    1297               0 :   NS_ABORT_IF_FALSE(aFrameNum < mFrames.Length(), "Invalid frame index!");
    1298               0 :   if (aFrameNum >= mFrames.Length())
    1299               0 :     return NS_ERROR_INVALID_ARG;
    1300                 : 
    1301               0 :   imgFrame *frame = GetImgFrame(aFrameNum);
    1302               0 :   NS_ABORT_IF_FALSE(frame, "Calling SetFrameBlendMethod on frame that doesn't exist!");
    1303               0 :   NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
    1304                 : 
    1305               0 :   frame->SetBlendMethod(aBlendMethod);
    1306                 : 
    1307               0 :   return NS_OK;
    1308                 : }
    1309                 : 
    1310                 : nsresult
    1311               1 : RasterImage::SetFrameHasNoAlpha(PRUint32 aFrameNum)
    1312                 : {
    1313               1 :   if (mError)
    1314               0 :     return NS_ERROR_FAILURE;
    1315                 : 
    1316               1 :   NS_ABORT_IF_FALSE(aFrameNum < mFrames.Length(), "Invalid frame index!");
    1317               1 :   if (aFrameNum >= mFrames.Length())
    1318               0 :     return NS_ERROR_INVALID_ARG;
    1319                 : 
    1320               1 :   imgFrame *frame = GetImgFrame(aFrameNum);
    1321               1 :   NS_ABORT_IF_FALSE(frame, "Calling SetFrameHasNoAlpha on frame that doesn't exist!");
    1322               1 :   NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
    1323                 : 
    1324               1 :   frame->SetHasNoAlpha();
    1325                 : 
    1326               1 :   return NS_OK;
    1327                 : }
    1328                 : 
    1329                 : nsresult
    1330              15 : RasterImage::DecodingComplete()
    1331                 : {
    1332              15 :   if (mError)
    1333               0 :     return NS_ERROR_FAILURE;
    1334                 : 
    1335                 :   // Flag that we're done decoding.
    1336                 :   // XXX - these should probably be combined when we fix animated image
    1337                 :   // discarding with bug 500402.
    1338              15 :   mDecoded = true;
    1339              15 :   mHasBeenDecoded = true;
    1340                 : 
    1341                 :   nsresult rv;
    1342                 : 
    1343                 :   // We now have one of the qualifications for discarding. Re-evaluate.
    1344              15 :   if (CanDiscard()) {
    1345               0 :     NS_ABORT_IF_FALSE(!DiscardingActive(),
    1346                 :                       "We shouldn't have been discardable before this");
    1347               0 :     rv = DiscardTracker::Reset(&mDiscardTrackerNode);
    1348               0 :     CONTAINER_ENSURE_SUCCESS(rv);
    1349                 :   }
    1350                 : 
    1351                 :   // If there's only 1 frame, optimize it. Optimizing animated images
    1352                 :   // is not supported.
    1353                 :   //
    1354                 :   // We don't optimize the frame for multipart images because we reuse
    1355                 :   // the frame.
    1356              15 :   if ((mFrames.Length() == 1) && !mMultipart) {
    1357              14 :     rv = mFrames[0]->Optimize();
    1358              14 :     NS_ENSURE_SUCCESS(rv, rv);
    1359                 :   }
    1360                 : 
    1361              15 :   return NS_OK;
    1362                 : }
    1363                 : 
    1364                 : //******************************************************************************
    1365                 : /* void StartAnimation () */
    1366                 : nsresult
    1367               0 : RasterImage::StartAnimation()
    1368                 : {
    1369               0 :   if (mError)
    1370               0 :     return NS_ERROR_FAILURE;
    1371                 : 
    1372               0 :   NS_ABORT_IF_FALSE(ShouldAnimate(), "Should not animate!");
    1373                 : 
    1374               0 :   EnsureAnimExists();
    1375                 : 
    1376               0 :   imgFrame* currentFrame = GetCurrentImgFrame();
    1377               0 :   if (currentFrame) {
    1378               0 :     if (currentFrame->GetTimeout() < 0) { // -1 means display this frame forever
    1379               0 :       mAnimationFinished = true;
    1380               0 :       return NS_ERROR_ABORT;
    1381                 :     }
    1382                 : 
    1383                 :     // We need to set the time that this initial frame was first displayed, as
    1384                 :     // this is used in AdvanceFrame().
    1385               0 :     mAnim->currentAnimationFrameTime = TimeStamp::Now();
    1386                 :   }
    1387                 :   
    1388               0 :   return NS_OK;
    1389                 : }
    1390                 : 
    1391                 : //******************************************************************************
    1392                 : /* void stopAnimation (); */
    1393                 : nsresult
    1394               0 : RasterImage::StopAnimation()
    1395                 : {
    1396               0 :   NS_ABORT_IF_FALSE(mAnimating, "Should be animating!");
    1397                 : 
    1398               0 :   if (mError)
    1399               0 :     return NS_ERROR_FAILURE;
    1400                 : 
    1401               0 :   return NS_OK;
    1402                 : }
    1403                 : 
    1404                 : //******************************************************************************
    1405                 : /* void resetAnimation (); */
    1406                 : NS_IMETHODIMP
    1407               0 : RasterImage::ResetAnimation()
    1408                 : {
    1409               0 :   if (mError)
    1410               0 :     return NS_ERROR_FAILURE;
    1411                 : 
    1412               0 :   if (mAnimationMode == kDontAnimMode || 
    1413               0 :       !mAnim || mAnim->currentAnimationFrameIndex == 0)
    1414               0 :     return NS_OK;
    1415                 : 
    1416               0 :   mAnimationFinished = false;
    1417                 : 
    1418               0 :   if (mAnimating)
    1419               0 :     StopAnimation();
    1420                 : 
    1421               0 :   mAnim->lastCompositedFrameIndex = -1;
    1422               0 :   mAnim->currentAnimationFrameIndex = 0;
    1423               0 :   mImageContainer = nsnull;
    1424                 : 
    1425                 :   // Note - We probably want to kick off a redecode somewhere around here when
    1426                 :   // we fix bug 500402.
    1427                 : 
    1428                 :   // Update display if we were animating before
    1429               0 :   nsCOMPtr<imgIContainerObserver> observer(do_QueryReferent(mObserver));
    1430               0 :   if (mAnimating && observer)
    1431               0 :     observer->FrameChanged(nsnull, this, &(mAnim->firstFrameRefreshArea));
    1432                 : 
    1433               0 :   if (ShouldAnimate()) {
    1434               0 :     StartAnimation();
    1435                 :     // The animation may not have been running before, if mAnimationFinished
    1436                 :     // was false (before we changed it to true in this function). So, mark the
    1437                 :     // animation as running.
    1438               0 :     mAnimating = true;
    1439                 :   }
    1440                 : 
    1441               0 :   return NS_OK;
    1442                 : }
    1443                 : 
    1444                 : void
    1445               4 : RasterImage::SetLoopCount(PRInt32 aLoopCount)
    1446                 : {
    1447               4 :   if (mError)
    1448               0 :     return;
    1449                 : 
    1450                 :   // -1  infinite
    1451                 :   //  0  no looping, one iteration
    1452                 :   //  1  one loop, two iterations
    1453                 :   //  ...
    1454               4 :   mLoopCount = aLoopCount;
    1455                 : }
    1456                 : 
    1457                 : nsresult
    1458              57 : RasterImage::AddSourceData(const char *aBuffer, PRUint32 aCount)
    1459                 : {
    1460              57 :   if (mError)
    1461               0 :     return NS_ERROR_FAILURE;
    1462                 : 
    1463              57 :   NS_ENSURE_ARG_POINTER(aBuffer);
    1464              57 :   nsresult rv = NS_OK;
    1465                 : 
    1466                 :   // We should not call this if we're not initialized
    1467              57 :   NS_ABORT_IF_FALSE(mInitialized, "Calling AddSourceData() on uninitialized "
    1468                 :                                   "RasterImage!");
    1469                 : 
    1470                 :   // We should not call this if we're already finished adding source data
    1471              57 :   NS_ABORT_IF_FALSE(!mHasSourceData, "Calling AddSourceData() after calling "
    1472                 :                                      "sourceDataComplete()!");
    1473                 : 
    1474                 :   // This call should come straight from necko - no reentrancy allowed
    1475              57 :   NS_ABORT_IF_FALSE(!mInDecoder, "Re-entrant call to AddSourceData!");
    1476                 : 
    1477                 :   // If we're not storing source data, write it directly to the decoder
    1478              57 :   if (!StoringSourceData()) {
    1479              53 :     rv = WriteToDecoder(aBuffer, aCount);
    1480              53 :     CONTAINER_ENSURE_SUCCESS(rv);
    1481                 : 
    1482                 :     // We're not storing source data, so this data is probably coming straight
    1483                 :     // from the network. In this case, we want to display data as soon as we
    1484                 :     // get it, so we want to flush invalidations after every write.
    1485             106 :     nsRefPtr<Decoder> kungFuDeathGrip = mDecoder;
    1486              53 :     mInDecoder = true;
    1487              53 :     mDecoder->FlushInvalidations();
    1488              53 :     mInDecoder = false;
    1489                 :   }
    1490                 : 
    1491                 :   // Otherwise, we're storing data in the source buffer
    1492                 :   else {
    1493                 : 
    1494                 :     // Store the data
    1495               4 :     char *newElem = mSourceData.AppendElements(aBuffer, aCount);
    1496               4 :     if (!newElem)
    1497               0 :       return NS_ERROR_OUT_OF_MEMORY;
    1498                 : 
    1499                 :     // If there's a decoder open, that means we want to do more decoding.
    1500                 :     // Wake up the worker.
    1501               4 :     if (mDecoder) {
    1502               4 :       DecodeWorker::Singleton()->RequestDecode(this);
    1503                 :     }
    1504                 :   }
    1505                 : 
    1506                 :   // Statistics
    1507              57 :   total_source_bytes += aCount;
    1508              57 :   if (mDiscardable)
    1509               4 :     discardable_source_bytes += aCount;
    1510              57 :   PR_LOG (gCompressedImageAccountingLog, PR_LOG_DEBUG,
    1511                 :           ("CompressedImageAccounting: Added compressed data to RasterImage %p (%s). "
    1512                 :            "Total Containers: %d, Discardable containers: %d, "
    1513                 :            "Total source bytes: %lld, Source bytes for discardable containers %lld",
    1514                 :            this,
    1515                 :            mSourceDataMimeType.get(),
    1516                 :            num_containers,
    1517                 :            num_discardable_containers,
    1518                 :            total_source_bytes,
    1519                 :            discardable_source_bytes));
    1520                 : 
    1521              57 :   return NS_OK;
    1522                 : }
    1523                 : 
    1524                 : /* Note!  buf must be declared as char buf[9]; */
    1525                 : // just used for logging and hashing the header
    1526                 : static void
    1527               0 : get_header_str (char *buf, char *data, PRSize data_len)
    1528                 : {
    1529                 :   int i;
    1530                 :   int n;
    1531                 :   static char hex[] = "0123456789abcdef";
    1532                 : 
    1533               0 :   n = data_len < 4 ? data_len : 4;
    1534                 : 
    1535               0 :   for (i = 0; i < n; i++) {
    1536               0 :     buf[i * 2]     = hex[(data[i] >> 4) & 0x0f];
    1537               0 :     buf[i * 2 + 1] = hex[data[i] & 0x0f];
    1538                 :   }
    1539                 : 
    1540               0 :   buf[i * 2] = 0;
    1541               0 : }
    1542                 : 
    1543                 : nsresult
    1544              22 : RasterImage::SourceDataComplete()
    1545                 : {
    1546              22 :   if (mError)
    1547               4 :     return NS_ERROR_FAILURE;
    1548                 : 
    1549                 :   // If we've been called before, ignore. Otherwise, flag that we have everything
    1550              18 :   if (mHasSourceData)
    1551               0 :     return NS_OK;
    1552              18 :   mHasSourceData = true;
    1553                 : 
    1554                 :   // This call should come straight from necko - no reentrancy allowed
    1555              18 :   NS_ABORT_IF_FALSE(!mInDecoder, "Re-entrant call to AddSourceData!");
    1556                 : 
    1557                 :   // If we're not storing any source data, then all the data was written
    1558                 :   // directly to the decoder in the AddSourceData() calls. This means we're
    1559                 :   // done, so we can shut down the decoder.
    1560              18 :   if (!StoringSourceData()) {
    1561              14 :     nsresult rv = ShutdownDecoder(eShutdownIntent_Done);
    1562              14 :     CONTAINER_ENSURE_SUCCESS(rv);
    1563                 :   }
    1564                 : 
    1565                 :   // If there's a decoder open, synchronously decode the beginning of the image
    1566                 :   // to check for errors and get the image's size.  (If we already have the
    1567                 :   // image's size, this does nothing.)  Then kick off an async decode of the
    1568                 :   // rest of the image.
    1569              17 :   if (mDecoder) {
    1570               4 :     nsresult rv = DecodeWorker::Singleton()->DecodeUntilSizeAvailable(this);
    1571               4 :     CONTAINER_ENSURE_SUCCESS(rv);
    1572                 :   }
    1573                 : 
    1574                 :   // If DecodeUntilSizeAvailable didn't finish the decode, let the decode worker
    1575                 :   // finish decoding this image.
    1576              17 :   if (mDecoder) {
    1577               0 :     DecodeWorker::Singleton()->RequestDecode(this);
    1578                 :   }
    1579                 : 
    1580                 :   // Free up any extra space in the backing buffer
    1581              17 :   mSourceData.Compact();
    1582                 : 
    1583                 :   // Log header information
    1584              17 :   if (PR_LOG_TEST(gCompressedImageAccountingLog, PR_LOG_DEBUG)) {
    1585                 :     char buf[9];
    1586               0 :     get_header_str(buf, mSourceData.Elements(), mSourceData.Length());
    1587               0 :     PR_LOG (gCompressedImageAccountingLog, PR_LOG_DEBUG,
    1588                 :             ("CompressedImageAccounting: RasterImage::SourceDataComplete() - data "
    1589                 :              "is done for container %p (%s) - header %p is 0x%s (length %d)",
    1590                 :              this,
    1591                 :              mSourceDataMimeType.get(),
    1592                 :              mSourceData.Elements(),
    1593                 :              buf,
    1594                 :              mSourceData.Length()));
    1595                 :   }
    1596                 : 
    1597                 :   // We now have one of the qualifications for discarding. Re-evaluate.
    1598              17 :   if (CanDiscard()) {
    1599               0 :     nsresult rv = DiscardTracker::Reset(&mDiscardTrackerNode);
    1600               0 :     CONTAINER_ENSURE_SUCCESS(rv);
    1601                 :   }
    1602              17 :   return NS_OK;
    1603                 : }
    1604                 : 
    1605                 : nsresult
    1606               0 : RasterImage::NewSourceData()
    1607                 : {
    1608                 :   nsresult rv;
    1609                 : 
    1610               0 :   if (mError)
    1611               0 :     return NS_ERROR_FAILURE;
    1612                 : 
    1613                 :   // The source data should be complete before calling this
    1614               0 :   NS_ABORT_IF_FALSE(mHasSourceData,
    1615                 :                     "Calling NewSourceData before SourceDataComplete!");
    1616               0 :   if (!mHasSourceData)
    1617               0 :     return NS_ERROR_ILLEGAL_VALUE;
    1618                 : 
    1619                 :   // Only supported for multipart channels. It wouldn't be too hard to change this,
    1620                 :   // but it would involve making sure that things worked for decode-on-draw and
    1621                 :   // discarding. Presently there's no need for this, so we don't.
    1622               0 :   NS_ABORT_IF_FALSE(mMultipart, "NewSourceData not supported for multipart");
    1623               0 :   if (!mMultipart)
    1624               0 :     return NS_ERROR_ILLEGAL_VALUE;
    1625                 : 
    1626                 :   // We're multipart, so we shouldn't be storing source data
    1627               0 :   NS_ABORT_IF_FALSE(!StoringSourceData(),
    1628                 :                     "Shouldn't be storing source data for multipart");
    1629                 : 
    1630                 :   // We're not storing the source data and we got SourceDataComplete. We should
    1631                 :   // have shut down the previous decoder
    1632               0 :   NS_ABORT_IF_FALSE(!mDecoder, "Shouldn't have a decoder in NewSourceData");
    1633                 : 
    1634                 :   // The decoder was shut down and we didn't flag an error, so we should be decoded
    1635               0 :   NS_ABORT_IF_FALSE(mDecoded, "Should be decoded in NewSourceData");
    1636                 : 
    1637                 :   // Reset some flags
    1638               0 :   mDecoded = false;
    1639               0 :   mHasSourceData = false;
    1640                 : 
    1641                 :   // We're decode-on-load here. Open up a new decoder just like what happens when
    1642                 :   // we call Init() for decode-on-load images.
    1643               0 :   rv = InitDecoder(/* aDoSizeDecode = */ false);
    1644               0 :   CONTAINER_ENSURE_SUCCESS(rv);
    1645                 : 
    1646               0 :   return NS_OK;
    1647                 : }
    1648                 : 
    1649                 : nsresult
    1650               0 : RasterImage::SetSourceSizeHint(PRUint32 sizeHint)
    1651                 : {
    1652               0 :   if (sizeHint && StoringSourceData())
    1653               0 :     return mSourceData.SetCapacity(sizeHint) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
    1654               0 :   return NS_OK;
    1655                 : }
    1656                 : 
    1657                 : //******************************************************************************
    1658                 : // DoComposite gets called when the timer for animation get fired and we have to
    1659                 : // update the composited frame of the animation.
    1660                 : nsresult
    1661               0 : RasterImage::DoComposite(nsIntRect* aDirtyRect,
    1662                 :                          imgFrame* aPrevFrame,
    1663                 :                          imgFrame* aNextFrame,
    1664                 :                          PRInt32 aNextFrameIndex)
    1665                 : {
    1666               0 :   NS_ENSURE_ARG_POINTER(aDirtyRect);
    1667               0 :   NS_ENSURE_ARG_POINTER(aPrevFrame);
    1668               0 :   NS_ENSURE_ARG_POINTER(aNextFrame);
    1669                 : 
    1670               0 :   PRInt32 prevFrameDisposalMethod = aPrevFrame->GetFrameDisposalMethod();
    1671               0 :   if (prevFrameDisposalMethod == kDisposeRestorePrevious &&
    1672               0 :       !mAnim->compositingPrevFrame)
    1673               0 :     prevFrameDisposalMethod = kDisposeClear;
    1674                 : 
    1675               0 :   nsIntRect prevFrameRect = aPrevFrame->GetRect();
    1676                 :   bool isFullPrevFrame = (prevFrameRect.x == 0 && prevFrameRect.y == 0 &&
    1677                 :                           prevFrameRect.width == mSize.width &&
    1678               0 :                           prevFrameRect.height == mSize.height);
    1679                 : 
    1680                 :   // Optimization: DisposeClearAll if the previous frame is the same size as
    1681                 :   //               container and it's clearing itself
    1682               0 :   if (isFullPrevFrame && 
    1683                 :       (prevFrameDisposalMethod == kDisposeClear))
    1684               0 :     prevFrameDisposalMethod = kDisposeClearAll;
    1685                 : 
    1686               0 :   PRInt32 nextFrameDisposalMethod = aNextFrame->GetFrameDisposalMethod();
    1687               0 :   nsIntRect nextFrameRect = aNextFrame->GetRect();
    1688                 :   bool isFullNextFrame = (nextFrameRect.x == 0 && nextFrameRect.y == 0 &&
    1689                 :                           nextFrameRect.width == mSize.width &&
    1690               0 :                           nextFrameRect.height == mSize.height);
    1691                 : 
    1692               0 :   if (!aNextFrame->GetIsPaletted()) {
    1693                 :     // Optimization: Skip compositing if the previous frame wants to clear the
    1694                 :     //               whole image
    1695               0 :     if (prevFrameDisposalMethod == kDisposeClearAll) {
    1696               0 :       aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
    1697               0 :       return NS_OK;
    1698                 :     }
    1699                 :   
    1700                 :     // Optimization: Skip compositing if this frame is the same size as the
    1701                 :     //               container and it's fully drawing over prev frame (no alpha)
    1702               0 :     if (isFullNextFrame &&
    1703                 :         (nextFrameDisposalMethod != kDisposeRestorePrevious) &&
    1704               0 :         !aNextFrame->GetHasAlpha()) {
    1705               0 :       aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
    1706               0 :       return NS_OK;
    1707                 :     }
    1708                 :   }
    1709                 : 
    1710                 :   // Calculate area that needs updating
    1711               0 :   switch (prevFrameDisposalMethod) {
    1712                 :     default:
    1713                 :     case kDisposeNotSpecified:
    1714                 :     case kDisposeKeep:
    1715               0 :       *aDirtyRect = nextFrameRect;
    1716               0 :       break;
    1717                 : 
    1718                 :     case kDisposeClearAll:
    1719                 :       // Whole image container is cleared
    1720               0 :       aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
    1721               0 :       break;
    1722                 : 
    1723                 :     case kDisposeClear:
    1724                 :       // Calc area that needs to be redrawn (the combination of previous and
    1725                 :       // this frame)
    1726                 :       // XXX - This could be done with multiple framechanged calls
    1727                 :       //       Having prevFrame way at the top of the image, and nextFrame
    1728                 :       //       way at the bottom, and both frames being small, we'd be
    1729                 :       //       telling framechanged to refresh the whole image when only two
    1730                 :       //       small areas are needed.
    1731               0 :       aDirtyRect->UnionRect(nextFrameRect, prevFrameRect);
    1732               0 :       break;
    1733                 : 
    1734                 :     case kDisposeRestorePrevious:
    1735               0 :       aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
    1736               0 :       break;
    1737                 :   }
    1738                 : 
    1739                 :   // Optimization:
    1740                 :   //   Skip compositing if the last composited frame is this frame
    1741                 :   //   (Only one composited frame was made for this animation.  Example:
    1742                 :   //    Only Frame 3 of a 10 frame image required us to build a composite frame
    1743                 :   //    On the second loop, we do not need to rebuild the frame
    1744                 :   //    since it's still sitting in compositingFrame)
    1745               0 :   if (mAnim->lastCompositedFrameIndex == aNextFrameIndex) {
    1746               0 :     return NS_OK;
    1747                 :   }
    1748                 : 
    1749               0 :   bool needToBlankComposite = false;
    1750                 : 
    1751                 :   // Create the Compositing Frame
    1752               0 :   if (!mAnim->compositingFrame) {
    1753               0 :     mAnim->compositingFrame = new imgFrame();
    1754                 :     nsresult rv = mAnim->compositingFrame->Init(0, 0, mSize.width, mSize.height,
    1755               0 :                                                 gfxASurface::ImageFormatARGB32);
    1756               0 :     if (NS_FAILED(rv)) {
    1757               0 :       mAnim->compositingFrame = nsnull;
    1758               0 :       return rv;
    1759                 :     }
    1760               0 :     needToBlankComposite = true;
    1761               0 :   } else if (aNextFrameIndex != mAnim->lastCompositedFrameIndex+1) {
    1762                 : 
    1763                 :     // If we are not drawing on top of last composited frame, 
    1764                 :     // then we are building a new composite frame, so let's clear it first.
    1765               0 :     needToBlankComposite = true;
    1766                 :   }
    1767                 : 
    1768                 :   // More optimizations possible when next frame is not transparent
    1769                 :   // But if the next frame has kDisposeRestorePrevious,
    1770                 :   // this "no disposal" optimization is not possible, 
    1771                 :   // because the frame in "after disposal operation" state 
    1772                 :   // needs to be stored in compositingFrame, so it can be 
    1773                 :   // copied into compositingPrevFrame later.
    1774               0 :   bool doDisposal = true;
    1775               0 :   if (!aNextFrame->GetHasAlpha() &&
    1776                 :       nextFrameDisposalMethod != kDisposeRestorePrevious) {
    1777               0 :     if (isFullNextFrame) {
    1778                 :       // Optimization: No need to dispose prev.frame when 
    1779                 :       // next frame is full frame and not transparent.
    1780               0 :       doDisposal = false;
    1781                 :       // No need to blank the composite frame
    1782               0 :       needToBlankComposite = false;
    1783                 :     } else {
    1784               0 :       if ((prevFrameRect.x >= nextFrameRect.x) &&
    1785                 :           (prevFrameRect.y >= nextFrameRect.y) &&
    1786                 :           (prevFrameRect.x + prevFrameRect.width <= nextFrameRect.x + nextFrameRect.width) &&
    1787                 :           (prevFrameRect.y + prevFrameRect.height <= nextFrameRect.y + nextFrameRect.height)) {
    1788                 :         // Optimization: No need to dispose prev.frame when 
    1789                 :         // next frame fully overlaps previous frame.
    1790               0 :         doDisposal = false;
    1791                 :       }
    1792                 :     }      
    1793                 :   }
    1794                 : 
    1795               0 :   if (doDisposal) {
    1796                 :     // Dispose of previous: clear, restore, or keep (copy)
    1797               0 :     switch (prevFrameDisposalMethod) {
    1798                 :       case kDisposeClear:
    1799               0 :         if (needToBlankComposite) {
    1800                 :           // If we just created the composite, it could have anything in it's
    1801                 :           // buffer. Clear whole frame
    1802               0 :           ClearFrame(mAnim->compositingFrame);
    1803                 :         } else {
    1804                 :           // Only blank out previous frame area (both color & Mask/Alpha)
    1805               0 :           ClearFrame(mAnim->compositingFrame, prevFrameRect);
    1806                 :         }
    1807               0 :         break;
    1808                 :   
    1809                 :       case kDisposeClearAll:
    1810               0 :         ClearFrame(mAnim->compositingFrame);
    1811               0 :         break;
    1812                 :   
    1813                 :       case kDisposeRestorePrevious:
    1814                 :         // It would be better to copy only the area changed back to
    1815                 :         // compositingFrame.
    1816               0 :         if (mAnim->compositingPrevFrame) {
    1817               0 :           CopyFrameImage(mAnim->compositingPrevFrame, mAnim->compositingFrame);
    1818                 :   
    1819                 :           // destroy only if we don't need it for this frame's disposal
    1820               0 :           if (nextFrameDisposalMethod != kDisposeRestorePrevious)
    1821               0 :             mAnim->compositingPrevFrame = nsnull;
    1822                 :         } else {
    1823               0 :           ClearFrame(mAnim->compositingFrame);
    1824                 :         }
    1825               0 :         break;
    1826                 :       
    1827                 :       default:
    1828                 :         // Copy previous frame into compositingFrame before we put the new frame on top
    1829                 :         // Assumes that the previous frame represents a full frame (it could be
    1830                 :         // smaller in size than the container, as long as the frame before it erased
    1831                 :         // itself)
    1832                 :         // Note: Frame 1 never gets into DoComposite(), so (aNextFrameIndex - 1) will
    1833                 :         // always be a valid frame number.
    1834               0 :         if (mAnim->lastCompositedFrameIndex != aNextFrameIndex - 1) {
    1835               0 :           if (isFullPrevFrame && !aPrevFrame->GetIsPaletted()) {
    1836                 :             // Just copy the bits
    1837               0 :             CopyFrameImage(aPrevFrame, mAnim->compositingFrame);
    1838                 :           } else {
    1839               0 :             if (needToBlankComposite) {
    1840                 :               // Only blank composite when prev is transparent or not full.
    1841               0 :               if (aPrevFrame->GetHasAlpha() || !isFullPrevFrame) {
    1842               0 :                 ClearFrame(mAnim->compositingFrame);
    1843                 :               }
    1844                 :             }
    1845               0 :             DrawFrameTo(aPrevFrame, mAnim->compositingFrame, prevFrameRect);
    1846                 :           }
    1847                 :         }
    1848                 :     }
    1849               0 :   } else if (needToBlankComposite) {
    1850                 :     // If we just created the composite, it could have anything in it's
    1851                 :     // buffers. Clear them
    1852               0 :     ClearFrame(mAnim->compositingFrame);
    1853                 :   }
    1854                 : 
    1855                 :   // Check if the frame we are composing wants the previous image restored afer
    1856                 :   // it is done. Don't store it (again) if last frame wanted its image restored
    1857                 :   // too
    1858               0 :   if ((nextFrameDisposalMethod == kDisposeRestorePrevious) &&
    1859                 :       (prevFrameDisposalMethod != kDisposeRestorePrevious)) {
    1860                 :     // We are storing the whole image.
    1861                 :     // It would be better if we just stored the area that nextFrame is going to
    1862                 :     // overwrite.
    1863               0 :     if (!mAnim->compositingPrevFrame) {
    1864               0 :       mAnim->compositingPrevFrame = new imgFrame();
    1865                 :       nsresult rv = mAnim->compositingPrevFrame->Init(0, 0, mSize.width, mSize.height,
    1866               0 :                                                       gfxASurface::ImageFormatARGB32);
    1867               0 :       if (NS_FAILED(rv)) {
    1868               0 :         mAnim->compositingPrevFrame = nsnull;
    1869               0 :         return rv;
    1870                 :       }
    1871                 :     }
    1872                 : 
    1873               0 :     CopyFrameImage(mAnim->compositingFrame, mAnim->compositingPrevFrame);
    1874                 :   }
    1875                 : 
    1876                 :   // blit next frame into it's correct spot
    1877               0 :   DrawFrameTo(aNextFrame, mAnim->compositingFrame, nextFrameRect);
    1878                 : 
    1879                 :   // Set timeout of CompositeFrame to timeout of frame we just composed
    1880                 :   // Bug 177948
    1881               0 :   PRInt32 timeout = aNextFrame->GetTimeout();
    1882               0 :   mAnim->compositingFrame->SetTimeout(timeout);
    1883                 : 
    1884                 :   // Tell the image that it is fully 'downloaded'.
    1885               0 :   nsresult rv = mAnim->compositingFrame->ImageUpdated(mAnim->compositingFrame->GetRect());
    1886               0 :   if (NS_FAILED(rv)) {
    1887               0 :     return rv;
    1888                 :   }
    1889                 : 
    1890                 :   // We don't want to keep composite images for 8bit frames.
    1891                 :   // Also this optimization won't work if the next frame has 
    1892                 :   // kDisposeRestorePrevious, because it would need to be restored 
    1893                 :   // into "after prev disposal but before next blend" state, 
    1894                 :   // not into empty frame.
    1895               0 :   if (isFullNextFrame && mAnimationMode == kNormalAnimMode && mLoopCount != 0 &&
    1896                 :       nextFrameDisposalMethod != kDisposeRestorePrevious &&
    1897               0 :       !aNextFrame->GetIsPaletted()) {
    1898                 :     // We have a composited full frame
    1899                 :     // Store the composited frame into the mFrames[..] so we don't have to
    1900                 :     // continuously re-build it
    1901                 :     // Then set the previous frame's disposal to CLEAR_ALL so we just draw the
    1902                 :     // frame next time around
    1903               0 :     if (CopyFrameImage(mAnim->compositingFrame, aNextFrame)) {
    1904               0 :       aPrevFrame->SetFrameDisposalMethod(kDisposeClearAll);
    1905               0 :       mAnim->lastCompositedFrameIndex = -1;
    1906               0 :       return NS_OK;
    1907                 :     }
    1908                 :   }
    1909                 : 
    1910               0 :   mAnim->lastCompositedFrameIndex = aNextFrameIndex;
    1911                 : 
    1912               0 :   return NS_OK;
    1913                 : }
    1914                 : 
    1915                 : //******************************************************************************
    1916                 : // Fill aFrame with black. Does also clears the mask.
    1917                 : void
    1918               0 : RasterImage::ClearFrame(imgFrame *aFrame)
    1919                 : {
    1920               0 :   if (!aFrame)
    1921               0 :     return;
    1922                 : 
    1923               0 :   nsresult rv = aFrame->LockImageData();
    1924               0 :   if (NS_FAILED(rv))
    1925               0 :     return;
    1926                 : 
    1927               0 :   nsRefPtr<gfxASurface> surf;
    1928               0 :   aFrame->GetSurface(getter_AddRefs(surf));
    1929                 : 
    1930                 :   // Erase the surface to transparent
    1931               0 :   gfxContext ctx(surf);
    1932               0 :   ctx.SetOperator(gfxContext::OPERATOR_CLEAR);
    1933               0 :   ctx.Paint();
    1934                 : 
    1935               0 :   aFrame->UnlockImageData();
    1936                 : }
    1937                 : 
    1938                 : //******************************************************************************
    1939                 : void
    1940               0 : RasterImage::ClearFrame(imgFrame *aFrame, nsIntRect &aRect)
    1941                 : {
    1942               0 :   if (!aFrame || aRect.width <= 0 || aRect.height <= 0)
    1943               0 :     return;
    1944                 : 
    1945               0 :   nsresult rv = aFrame->LockImageData();
    1946               0 :   if (NS_FAILED(rv))
    1947               0 :     return;
    1948                 : 
    1949               0 :   nsRefPtr<gfxASurface> surf;
    1950               0 :   aFrame->GetSurface(getter_AddRefs(surf));
    1951                 : 
    1952                 :   // Erase the destination rectangle to transparent
    1953               0 :   gfxContext ctx(surf);
    1954               0 :   ctx.SetOperator(gfxContext::OPERATOR_CLEAR);
    1955               0 :   ctx.Rectangle(gfxRect(aRect.x, aRect.y, aRect.width, aRect.height));
    1956               0 :   ctx.Fill();
    1957                 : 
    1958               0 :   aFrame->UnlockImageData();
    1959                 : }
    1960                 : 
    1961                 : 
    1962                 : //******************************************************************************
    1963                 : // Whether we succeed or fail will not cause a crash, and there's not much
    1964                 : // we can do about a failure, so there we don't return a nsresult
    1965                 : bool
    1966               0 : RasterImage::CopyFrameImage(imgFrame *aSrcFrame,
    1967                 :                             imgFrame *aDstFrame)
    1968                 : {
    1969                 :   PRUint8* aDataSrc;
    1970                 :   PRUint8* aDataDest;
    1971                 :   PRUint32 aDataLengthSrc;
    1972                 :   PRUint32 aDataLengthDest;
    1973                 : 
    1974               0 :   if (!aSrcFrame || !aDstFrame)
    1975               0 :     return false;
    1976                 : 
    1977               0 :   if (NS_FAILED(aDstFrame->LockImageData()))
    1978               0 :     return false;
    1979                 : 
    1980                 :   // Copy Image Over
    1981               0 :   aSrcFrame->GetImageData(&aDataSrc, &aDataLengthSrc);
    1982               0 :   aDstFrame->GetImageData(&aDataDest, &aDataLengthDest);
    1983               0 :   if (!aDataDest || !aDataSrc || aDataLengthDest != aDataLengthSrc) {
    1984               0 :     aDstFrame->UnlockImageData();
    1985               0 :     return false;
    1986                 :   }
    1987               0 :   memcpy(aDataDest, aDataSrc, aDataLengthSrc);
    1988               0 :   aDstFrame->UnlockImageData();
    1989                 : 
    1990               0 :   return true;
    1991                 : }
    1992                 : 
    1993                 : //******************************************************************************
    1994                 : /* 
    1995                 :  * aSrc is the current frame being drawn,
    1996                 :  * aDst is the composition frame where the current frame is drawn into.
    1997                 :  * aSrcRect is the size of the current frame, and the position of that frame
    1998                 :  *          in the composition frame.
    1999                 :  */
    2000                 : nsresult
    2001               0 : RasterImage::DrawFrameTo(imgFrame *aSrc,
    2002                 :                          imgFrame *aDst,
    2003                 :                          nsIntRect& aSrcRect)
    2004                 : {
    2005               0 :   NS_ENSURE_ARG_POINTER(aSrc);
    2006               0 :   NS_ENSURE_ARG_POINTER(aDst);
    2007                 : 
    2008               0 :   nsIntRect dstRect = aDst->GetRect();
    2009                 : 
    2010                 :   // According to both AGIF and APNG specs, offsets are unsigned
    2011               0 :   if (aSrcRect.x < 0 || aSrcRect.y < 0) {
    2012               0 :     NS_WARNING("RasterImage::DrawFrameTo: negative offsets not allowed");
    2013               0 :     return NS_ERROR_FAILURE;
    2014                 :   }
    2015                 :   // Outside the destination frame, skip it
    2016               0 :   if ((aSrcRect.x > dstRect.width) || (aSrcRect.y > dstRect.height)) {
    2017               0 :     return NS_OK;
    2018                 :   }
    2019                 : 
    2020               0 :   if (aSrc->GetIsPaletted()) {
    2021                 :     // Larger than the destination frame, clip it
    2022               0 :     PRInt32 width = NS_MIN(aSrcRect.width, dstRect.width - aSrcRect.x);
    2023               0 :     PRInt32 height = NS_MIN(aSrcRect.height, dstRect.height - aSrcRect.y);
    2024                 : 
    2025                 :     // The clipped image must now fully fit within destination image frame
    2026               0 :     NS_ASSERTION((aSrcRect.x >= 0) && (aSrcRect.y >= 0) &&
    2027                 :                  (aSrcRect.x + width <= dstRect.width) &&
    2028                 :                  (aSrcRect.y + height <= dstRect.height),
    2029                 :                 "RasterImage::DrawFrameTo: Invalid aSrcRect");
    2030                 : 
    2031                 :     // clipped image size may be smaller than source, but not larger
    2032               0 :     NS_ASSERTION((width <= aSrcRect.width) && (height <= aSrcRect.height),
    2033                 :                  "RasterImage::DrawFrameTo: source must be smaller than dest");
    2034                 : 
    2035               0 :     if (NS_FAILED(aDst->LockImageData()))
    2036               0 :       return NS_ERROR_FAILURE;
    2037                 : 
    2038                 :     // Get pointers to image data
    2039                 :     PRUint32 size;
    2040                 :     PRUint8 *srcPixels;
    2041                 :     PRUint32 *colormap;
    2042                 :     PRUint32 *dstPixels;
    2043                 : 
    2044               0 :     aSrc->GetImageData(&srcPixels, &size);
    2045               0 :     aSrc->GetPaletteData(&colormap, &size);
    2046               0 :     aDst->GetImageData((PRUint8 **)&dstPixels, &size);
    2047               0 :     if (!srcPixels || !dstPixels || !colormap) {
    2048               0 :       aDst->UnlockImageData();
    2049               0 :       return NS_ERROR_FAILURE;
    2050                 :     }
    2051                 : 
    2052                 :     // Skip to the right offset
    2053               0 :     dstPixels += aSrcRect.x + (aSrcRect.y * dstRect.width);
    2054               0 :     if (!aSrc->GetHasAlpha()) {
    2055               0 :       for (PRInt32 r = height; r > 0; --r) {
    2056               0 :         for (PRInt32 c = 0; c < width; c++) {
    2057               0 :           dstPixels[c] = colormap[srcPixels[c]];
    2058                 :         }
    2059                 :         // Go to the next row in the source resp. destination image
    2060               0 :         srcPixels += aSrcRect.width;
    2061               0 :         dstPixels += dstRect.width;
    2062                 :       }
    2063                 :     } else {
    2064               0 :       for (PRInt32 r = height; r > 0; --r) {
    2065               0 :         for (PRInt32 c = 0; c < width; c++) {
    2066               0 :           const PRUint32 color = colormap[srcPixels[c]];
    2067               0 :           if (color)
    2068               0 :             dstPixels[c] = color;
    2069                 :         }
    2070                 :         // Go to the next row in the source resp. destination image
    2071               0 :         srcPixels += aSrcRect.width;
    2072               0 :         dstPixels += dstRect.width;
    2073                 :       }
    2074                 :     }
    2075                 : 
    2076               0 :     aDst->UnlockImageData();
    2077               0 :     return NS_OK;
    2078                 :   }
    2079                 : 
    2080               0 :   nsRefPtr<gfxPattern> srcPatt;
    2081               0 :   aSrc->GetPattern(getter_AddRefs(srcPatt));
    2082                 : 
    2083               0 :   aDst->LockImageData();
    2084               0 :   nsRefPtr<gfxASurface> dstSurf;
    2085               0 :   aDst->GetSurface(getter_AddRefs(dstSurf));
    2086                 : 
    2087               0 :   gfxContext dst(dstSurf);
    2088               0 :   dst.Translate(gfxPoint(aSrcRect.x, aSrcRect.y));
    2089               0 :   dst.Rectangle(gfxRect(0, 0, aSrcRect.width, aSrcRect.height), true);
    2090                 :   
    2091                 :   // first clear the surface if the blend flag says so
    2092               0 :   PRInt32 blendMethod = aSrc->GetBlendMethod();
    2093               0 :   if (blendMethod == kBlendSource) {
    2094               0 :     gfxContext::GraphicsOperator defaultOperator = dst.CurrentOperator();
    2095               0 :     dst.SetOperator(gfxContext::OPERATOR_CLEAR);
    2096               0 :     dst.Fill();
    2097               0 :     dst.SetOperator(defaultOperator);
    2098                 :   }
    2099               0 :   dst.SetPattern(srcPatt);
    2100               0 :   dst.Paint();
    2101                 : 
    2102               0 :   aDst->UnlockImageData();
    2103                 : 
    2104               0 :   return NS_OK;
    2105                 : }
    2106                 : 
    2107                 : 
    2108                 : /********* Methods to implement lazy allocation of nsIProperties object *************/
    2109                 : NS_IMETHODIMP
    2110               0 : RasterImage::Get(const char *prop, const nsIID & iid, void * *result)
    2111                 : {
    2112               0 :   if (!mProperties)
    2113               0 :     return NS_ERROR_FAILURE;
    2114               0 :   return mProperties->Get(prop, iid, result);
    2115                 : }
    2116                 : 
    2117                 : NS_IMETHODIMP
    2118               0 : RasterImage::Set(const char *prop, nsISupports *value)
    2119                 : {
    2120               0 :   if (!mProperties)
    2121               0 :     mProperties = do_CreateInstance("@mozilla.org/properties;1");
    2122               0 :   if (!mProperties)
    2123               0 :     return NS_ERROR_OUT_OF_MEMORY;
    2124               0 :   return mProperties->Set(prop, value);
    2125                 : }
    2126                 : 
    2127                 : NS_IMETHODIMP
    2128               0 : RasterImage::Has(const char *prop, bool *_retval)
    2129                 : {
    2130               0 :   NS_ENSURE_ARG_POINTER(_retval);
    2131               0 :   if (!mProperties) {
    2132               0 :     *_retval = false;
    2133               0 :     return NS_OK;
    2134                 :   }
    2135               0 :   return mProperties->Has(prop, _retval);
    2136                 : }
    2137                 : 
    2138                 : NS_IMETHODIMP
    2139               0 : RasterImage::Undefine(const char *prop)
    2140                 : {
    2141               0 :   if (!mProperties)
    2142               0 :     return NS_ERROR_FAILURE;
    2143               0 :   return mProperties->Undefine(prop);
    2144                 : }
    2145                 : 
    2146                 : NS_IMETHODIMP
    2147               0 : RasterImage::GetKeys(PRUint32 *count, char ***keys)
    2148                 : {
    2149               0 :   if (!mProperties) {
    2150               0 :     *count = 0;
    2151               0 :     *keys = nsnull;
    2152               0 :     return NS_OK;
    2153                 :   }
    2154               0 :   return mProperties->GetKeys(count, keys);
    2155                 : }
    2156                 : 
    2157                 : void
    2158               0 : RasterImage::Discard(bool force)
    2159                 : {
    2160                 :   // We should be ok for discard
    2161               0 :   NS_ABORT_IF_FALSE(force ? CanForciblyDiscard() : CanDiscard(), "Asked to discard but can't!");
    2162                 : 
    2163                 :   // We should never discard when we have an active decoder
    2164               0 :   NS_ABORT_IF_FALSE(!mDecoder, "Asked to discard with open decoder!");
    2165                 : 
    2166                 :   // As soon as an image becomes animated, it becomes non-discardable and any
    2167                 :   // timers are cancelled.
    2168               0 :   NS_ABORT_IF_FALSE(!mAnim, "Asked to discard for animated image!");
    2169                 : 
    2170                 :   // For post-operation logging
    2171               0 :   int old_frame_count = mFrames.Length();
    2172                 : 
    2173                 :   // Delete all the decoded frames, then clear the array.
    2174               0 :   for (int i = 0; i < old_frame_count; ++i)
    2175               0 :     delete mFrames[i];
    2176               0 :   mFrames.Clear();
    2177                 : 
    2178                 :   // Flag that we no longer have decoded frames for this image
    2179               0 :   mDecoded = false;
    2180                 : 
    2181                 :   // Notify that we discarded
    2182               0 :   nsCOMPtr<imgIDecoderObserver> observer(do_QueryReferent(mObserver));
    2183               0 :   if (observer)
    2184               0 :     observer->OnDiscard(nsnull);
    2185                 : 
    2186               0 :   if (force)
    2187               0 :     DiscardTracker::Remove(&mDiscardTrackerNode);
    2188                 : 
    2189                 :   // Log
    2190               0 :   PR_LOG(gCompressedImageAccountingLog, PR_LOG_DEBUG,
    2191                 :          ("CompressedImageAccounting: discarded uncompressed image "
    2192                 :           "data from RasterImage %p (%s) - %d frames (cached count: %d); "
    2193                 :           "Total Containers: %d, Discardable containers: %d, "
    2194                 :           "Total source bytes: %lld, Source bytes for discardable containers %lld",
    2195                 :           this,
    2196                 :           mSourceDataMimeType.get(),
    2197                 :           old_frame_count,
    2198                 :           mFrames.Length(),
    2199                 :           num_containers,
    2200                 :           num_discardable_containers,
    2201                 :           total_source_bytes,
    2202                 :           discardable_source_bytes));
    2203               0 : }
    2204                 : 
    2205                 : // Helper method to determine if we can discard an image
    2206                 : bool
    2207              67 : RasterImage::CanDiscard() {
    2208              67 :   return (DiscardingEnabled() && // Globally enabled...
    2209                 :           mDiscardable &&        // ...Enabled at creation time...
    2210                 :           (mLockCount == 0) &&   // ...not temporarily disabled...
    2211                 :           mHasSourceData &&      // ...have the source data...
    2212              67 :           mDecoded);             // ...and have something to discard.
    2213                 : }
    2214                 : 
    2215                 : bool
    2216               0 : RasterImage::CanForciblyDiscard() {
    2217                 :   return mDiscardable &&         // ...Enabled at creation time...
    2218               0 :          mHasSourceData;         // ...have the source data...
    2219                 : }
    2220                 : 
    2221                 : // Helper method to tell us whether the clock is currently running for
    2222                 : // discarding this image. Mainly for assertions.
    2223                 : bool
    2224              26 : RasterImage::DiscardingActive() {
    2225              26 :   return !!(mDiscardTrackerNode.prev || mDiscardTrackerNode.next);
    2226                 : }
    2227                 : 
    2228                 : // Helper method to determine if we're storing the source data in a buffer
    2229                 : // or just writing it directly to the decoder
    2230                 : bool
    2231              92 : RasterImage::StoringSourceData() const {
    2232              92 :   return (mDecodeOnDraw || mDiscardable);
    2233                 : }
    2234                 : 
    2235                 : 
    2236                 : // Sets up a decoder for this image. It is an error to call this function
    2237                 : // when decoding is already in process (ie - when mDecoder is non-null).
    2238                 : nsresult
    2239              24 : RasterImage::InitDecoder(bool aDoSizeDecode)
    2240                 : {
    2241                 :   // Ensure that the decoder is not already initialized
    2242              24 :   NS_ABORT_IF_FALSE(!mDecoder, "Calling InitDecoder() while already decoding!");
    2243                 :   
    2244                 :   // We shouldn't be firing up a decoder if we already have the frames decoded
    2245              24 :   NS_ABORT_IF_FALSE(!mDecoded, "Calling InitDecoder() but already decoded!");
    2246                 : 
    2247                 :   // Since we're not decoded, we should not have a discard timer active
    2248              24 :   NS_ABORT_IF_FALSE(!DiscardingActive(), "Discard Timer active in InitDecoder()!");
    2249                 : 
    2250                 :   // Figure out which decoder we want
    2251              24 :   eDecoderType type = GetDecoderType(mSourceDataMimeType.get());
    2252              24 :   CONTAINER_ENSURE_TRUE(type != eDecoderType_unknown, NS_IMAGELIB_ERROR_NO_DECODER);
    2253                 : 
    2254              40 :   nsCOMPtr<imgIDecoderObserver> observer(do_QueryReferent(mObserver));
    2255                 :   // Instantiate the appropriate decoder
    2256              20 :   switch (type) {
    2257                 :     case eDecoderType_png:
    2258              12 :       mDecoder = new nsPNGDecoder(*this, observer);
    2259               6 :       break;
    2260                 :     case eDecoderType_gif:
    2261               8 :       mDecoder = new nsGIFDecoder2(*this, observer);
    2262               4 :       break;
    2263                 :     case eDecoderType_jpeg:
    2264              10 :       mDecoder = new nsJPEGDecoder(*this, observer);
    2265               5 :       break;
    2266                 :     case eDecoderType_bmp:
    2267               0 :       mDecoder = new nsBMPDecoder(*this, observer);
    2268               0 :       break;
    2269                 :     case eDecoderType_ico:
    2270              10 :       mDecoder = new nsICODecoder(*this, observer);
    2271               5 :       break;
    2272                 :     case eDecoderType_icon:
    2273               0 :       mDecoder = new nsIconDecoder(*this, observer);
    2274               0 :       break;
    2275                 :     default:
    2276               0 :       NS_ABORT_IF_FALSE(0, "Shouldn't get here!");
    2277                 :   }
    2278                 : 
    2279                 :   // Initialize the decoder
    2280              20 :   mDecoder->SetSizeDecode(aDoSizeDecode);
    2281              20 :   mDecoder->SetDecodeFlags(mFrameDecodeFlags);
    2282              20 :   mDecoder->Init();
    2283              20 :   CONTAINER_ENSURE_SUCCESS(mDecoder->GetDecoderError());
    2284                 : 
    2285              20 :   if (!aDoSizeDecode) {
    2286              16 :     Telemetry::GetHistogramById(Telemetry::IMAGE_DECODE_COUNT)->Subtract(mDecodeCount);
    2287              16 :     mDecodeCount++;
    2288              16 :     Telemetry::GetHistogramById(Telemetry::IMAGE_DECODE_COUNT)->Add(mDecodeCount);
    2289                 :   }
    2290                 : 
    2291              20 :   return NS_OK;
    2292                 : }
    2293                 : 
    2294                 : // Flushes, closes, and nulls-out a decoder. Cleans up any related decoding
    2295                 : // state. It is an error to call this function when there is no initialized
    2296                 : // decoder.
    2297                 : // 
    2298                 : // aIntent specifies the intent of the shutdown. If aIntent is
    2299                 : // eShutdownIntent_Done, an error is flagged if we didn't get what we should
    2300                 : // have out of the decode. If aIntent is eShutdownIntent_Interrupted, we don't
    2301                 : // check this. If aIntent is eShutdownIntent_Error, we shut down in error mode.
    2302                 : nsresult
    2303              20 : RasterImage::ShutdownDecoder(eShutdownIntent aIntent)
    2304                 : {
    2305                 :   // Ensure that our intent is valid
    2306              20 :   NS_ABORT_IF_FALSE((aIntent >= 0) || (aIntent < eShutdownIntent_AllCount),
    2307                 :                     "Invalid shutdown intent");
    2308                 : 
    2309                 :   // Ensure that the decoder is initialized
    2310              20 :   NS_ABORT_IF_FALSE(mDecoder, "Calling ShutdownDecoder() with no active decoder!");
    2311                 : 
    2312                 :   // Figure out what kind of decode we were doing before we get rid of our decoder
    2313              20 :   bool wasSizeDecode = mDecoder->IsSizeDecode();
    2314                 : 
    2315                 :   // Finalize the decoder
    2316                 :   // null out mDecoder, _then_ check for errors on the close (otherwise the
    2317                 :   // error routine might re-invoke ShutdownDecoder)
    2318              40 :   nsRefPtr<Decoder> decoder = mDecoder;
    2319              20 :   mDecoder = nsnull;
    2320                 : 
    2321              20 :   mInDecoder = true;
    2322              20 :   decoder->Finish();
    2323              20 :   mInDecoder = false;
    2324                 : 
    2325                 :   // Kill off our decode request, if it's pending.  (If not, this call is
    2326                 :   // harmless.)
    2327              20 :   DecodeWorker::Singleton()->StopDecoding(this);
    2328                 : 
    2329              20 :   nsresult decoderStatus = decoder->GetDecoderError();
    2330              20 :   if (NS_FAILED(decoderStatus)) {
    2331               0 :     DoError();
    2332               0 :     return decoderStatus;
    2333                 :   }
    2334                 : 
    2335                 :   // We just shut down the decoder. If we didn't get what we want, but expected
    2336                 :   // to, flag an error
    2337              20 :   bool failed = false;
    2338              20 :   if (wasSizeDecode && !mHasSize)
    2339               0 :     failed = true;
    2340              20 :   if (!wasSizeDecode && !mDecoded)
    2341               1 :     failed = true;
    2342              20 :   if ((aIntent == eShutdownIntent_Done) && failed) {
    2343               1 :     DoError();
    2344               1 :     return NS_ERROR_FAILURE;
    2345                 :   }
    2346                 : 
    2347                 :   // Reset number of decoded bytes
    2348              19 :   mBytesDecoded = 0;
    2349                 : 
    2350              19 :   return NS_OK;
    2351                 : }
    2352                 : 
    2353                 : // Writes the data to the decoder, updating the total number of bytes written.
    2354                 : nsresult
    2355              59 : RasterImage::WriteToDecoder(const char *aBuffer, PRUint32 aCount)
    2356                 : {
    2357                 :   // We should have a decoder
    2358              59 :   NS_ABORT_IF_FALSE(mDecoder, "Trying to write to null decoder!");
    2359                 : 
    2360                 :   // The decoder will start decoding into the current frame (if we have one).
    2361                 :   // When it needs to add another frame, we will unlock this frame and lock the
    2362                 :   // new frame.
    2363                 :   // Our invariant is that, while in the decoder, the last frame is always
    2364                 :   // locked, and all others are unlocked.
    2365              59 :   if (mFrames.Length() > 0) {
    2366              21 :     imgFrame *curframe = mFrames.ElementAt(mFrames.Length() - 1);
    2367              21 :     curframe->LockImageData();
    2368                 :   }
    2369                 : 
    2370                 :   // Write
    2371             118 :   nsRefPtr<Decoder> kungFuDeathGrip = mDecoder;
    2372              59 :   mInDecoder = true;
    2373              59 :   mDecoder->Write(aBuffer, aCount);
    2374              59 :   mInDecoder = false;
    2375                 : 
    2376                 :   // We unlock the current frame, even if that frame is different from the
    2377                 :   // frame we entered the decoder with. (See above.)
    2378              59 :   if (mFrames.Length() > 0) {
    2379              36 :     imgFrame *curframe = mFrames.ElementAt(mFrames.Length() - 1);
    2380              36 :     curframe->UnlockImageData();
    2381                 :   }
    2382                 : 
    2383              59 :   if (!mDecoder)
    2384               0 :     return NS_ERROR_FAILURE;
    2385                 :     
    2386              59 :   CONTAINER_ENSURE_SUCCESS(mDecoder->GetDecoderError());
    2387                 : 
    2388                 :   // Keep track of the total number of bytes written over the lifetime of the
    2389                 :   // decoder
    2390              59 :   mBytesDecoded += aCount;
    2391                 : 
    2392              59 :   return NS_OK;
    2393                 : }
    2394                 : 
    2395                 : // This function is called in situations where it's clear that we want the
    2396                 : // frames in decoded form (Draw, GetFrame, CopyFrame, ExtractFrame, etc).
    2397                 : // If we're completely decoded, this method resets the discard timer (if
    2398                 : // we're discardable), since wanting the frames now is a good indicator of
    2399                 : // wanting them again soon. If we're not decoded, this method kicks off
    2400                 : // asynchronous decoding to generate the frames.
    2401                 : nsresult
    2402              32 : RasterImage::WantDecodedFrames()
    2403                 : {
    2404                 :   nsresult rv;
    2405                 : 
    2406                 :   // If we can discard, the clock should be running. Reset it.
    2407              32 :   if (CanDiscard()) {
    2408               0 :     NS_ABORT_IF_FALSE(DiscardingActive(),
    2409                 :                       "Decoded and discardable but discarding not activated!");
    2410               0 :     rv = DiscardTracker::Reset(&mDiscardTrackerNode);
    2411               0 :     CONTAINER_ENSURE_SUCCESS(rv);
    2412                 :   }
    2413                 : 
    2414                 :   // Request a decode (no-op if we're decoded)
    2415              32 :   return RequestDecode();
    2416                 : }
    2417                 : 
    2418                 : //******************************************************************************
    2419                 : /* void requestDecode() */
    2420                 : NS_IMETHODIMP
    2421              32 : RasterImage::RequestDecode()
    2422                 : {
    2423                 :   nsresult rv;
    2424                 : 
    2425              32 :   if (mError)
    2426               0 :     return NS_ERROR_FAILURE;
    2427                 : 
    2428                 :   // If we're fully decoded, we have nothing to do
    2429              32 :   if (mDecoded)
    2430              23 :     return NS_OK;
    2431                 : 
    2432                 :   // If we're not storing source data, we have nothing to do
    2433               9 :   if (!StoringSourceData())
    2434               3 :     return NS_OK;
    2435                 : 
    2436                 :   // If we've already got a full decoder running, we have nothing to do
    2437               6 :   if (mDecoder && !mDecoder->IsSizeDecode())
    2438               6 :     return NS_OK;
    2439                 : 
    2440                 :   // If our callstack goes through a size decoder, we have a problem.
    2441                 :   // We need to shutdown the size decode and replace it with  a full
    2442                 :   // decoder, but can't do that from within the decoder itself. Thus, we post
    2443                 :   // an asynchronous event to the event loop to do it later. Since
    2444                 :   // RequestDecode() is an asynchronous function this works fine (though it's
    2445                 :   // a little slower).
    2446               0 :   if (mInDecoder) {
    2447               0 :     nsRefPtr<imgDecodeRequestor> requestor = new imgDecodeRequestor(this);
    2448               0 :     return NS_DispatchToCurrentThread(requestor);
    2449                 :   }
    2450                 : 
    2451                 : 
    2452                 :   // If we have a size decode open, interrupt it and shut it down; or if
    2453                 :   // the decoder has different flags than what we need
    2454               0 :   if (mDecoder &&
    2455               0 :       (mDecoder->IsSizeDecode() || mDecoder->GetDecodeFlags() != mFrameDecodeFlags))
    2456                 :   {
    2457               0 :     rv = ShutdownDecoder(eShutdownIntent_Interrupted);
    2458               0 :     CONTAINER_ENSURE_SUCCESS(rv);
    2459                 :   }
    2460                 : 
    2461                 :   // If we don't have a decoder, create one 
    2462               0 :   if (!mDecoder) {
    2463               0 :     NS_ABORT_IF_FALSE(mFrames.IsEmpty(), "Trying to decode to non-empty frame-array");
    2464               0 :     rv = InitDecoder(/* aDoSizeDecode = */ false);
    2465               0 :     CONTAINER_ENSURE_SUCCESS(rv);
    2466                 :   }
    2467                 : 
    2468                 :   // If we've read all the data we have, we're done
    2469               0 :   if (mBytesDecoded == mSourceData.Length())
    2470               0 :     return NS_OK;
    2471                 : 
    2472                 :   // If it's a smallish image, it's not worth it to do things async
    2473               0 :   if (!mDecoded && !mInDecoder && mHasSourceData && (mSourceData.Length() < gMaxBytesForSyncDecode))
    2474               0 :     return SyncDecode();
    2475                 : 
    2476                 :   // If we get this far, dispatch the worker. We do this instead of starting
    2477                 :   // any immediate decoding to guarantee that all our decode notifications are
    2478                 :   // dispatched asynchronously, and to ensure we stay responsive.
    2479               0 :   DecodeWorker::Singleton()->RequestDecode(this);
    2480                 : 
    2481               0 :   return NS_OK;
    2482                 : }
    2483                 : 
    2484                 : // Synchronously decodes as much data as possible
    2485                 : nsresult
    2486              21 : RasterImage::SyncDecode()
    2487                 : {
    2488                 :   nsresult rv;
    2489                 : 
    2490                 :   // If we're decoded already, no worries
    2491              21 :   if (mDecoded)
    2492              19 :     return NS_OK;
    2493                 : 
    2494                 :   // If we're not storing source data, there isn't much to do here
    2495               2 :   if (!StoringSourceData())
    2496               0 :     return NS_OK;
    2497                 : 
    2498                 :   // We really have no good way of forcing a synchronous decode if we're being
    2499                 :   // called in a re-entrant manner (ie, from an event listener fired by a
    2500                 :   // decoder), because the decoding machinery is already tied up. We thus explicitly
    2501                 :   // disallow this type of call in the API, and check for it in API methods.
    2502               2 :   NS_ABORT_IF_FALSE(!mInDecoder, "Yikes, forcing sync in reentrant call!");
    2503                 : 
    2504                 :   // If we have a size decoder open, or one with different flags than
    2505                 :   // what we need, shut it down
    2506               2 :   if (mDecoder &&
    2507               2 :       (mDecoder->IsSizeDecode() || mDecoder->GetDecodeFlags() != mFrameDecodeFlags))
    2508                 :   {
    2509               0 :     rv = ShutdownDecoder(eShutdownIntent_Interrupted);
    2510               0 :     CONTAINER_ENSURE_SUCCESS(rv);
    2511                 :   }
    2512                 : 
    2513                 :   // If we don't have a decoder, create one 
    2514               2 :   if (!mDecoder) {
    2515               2 :     NS_ABORT_IF_FALSE(mFrames.IsEmpty(), "Trying to decode to non-empty frame-array");
    2516               2 :     rv = InitDecoder(/* aDoSizeDecode = */ false);
    2517               2 :     CONTAINER_ENSURE_SUCCESS(rv);
    2518                 :   }
    2519                 : 
    2520                 :   // Write everything we have
    2521               2 :   rv = WriteToDecoder(mSourceData.Elements() + mBytesDecoded,
    2522               4 :                       mSourceData.Length() - mBytesDecoded);
    2523               2 :   CONTAINER_ENSURE_SUCCESS(rv);
    2524                 : 
    2525                 :   // When we're doing a sync decode, we want to get as much information from the
    2526                 :   // image as possible. We've send the decoder all of our data, so now's a good
    2527                 :   // time  to flush any invalidations (in case we don't have all the data and what
    2528                 :   // we got left us mid-frame).
    2529               4 :   nsRefPtr<Decoder> kungFuDeathGrip = mDecoder;
    2530               2 :   mInDecoder = true;
    2531               2 :   mDecoder->FlushInvalidations();
    2532               2 :   mInDecoder = false;
    2533                 : 
    2534                 :   // If we finished the decode, shutdown the decoder
    2535               2 :   if (mDecoder && IsDecodeFinished()) {
    2536               2 :     rv = ShutdownDecoder(eShutdownIntent_Done);
    2537               2 :     CONTAINER_ENSURE_SUCCESS(rv);
    2538                 :   }
    2539                 : 
    2540                 :   // All good if no errors!
    2541               2 :   return mError ? NS_ERROR_FAILURE : NS_OK;
    2542                 : }
    2543                 : 
    2544                 : //******************************************************************************
    2545                 : /* [noscript] void draw(in gfxContext aContext,
    2546                 :  *                      in gfxGraphicsFilter aFilter,
    2547                 :  *                      [const] in gfxMatrix aUserSpaceToImageSpace,
    2548                 :  *                      [const] in gfxRect aFill,
    2549                 :  *                      [const] in nsIntRect aSubimage,
    2550                 :  *                      [const] in nsIntSize aViewportSize,
    2551                 :  *                      in PRUint32 aFlags); */
    2552                 : NS_IMETHODIMP
    2553               0 : RasterImage::Draw(gfxContext *aContext,
    2554                 :                   gfxPattern::GraphicsFilter aFilter,
    2555                 :                   const gfxMatrix &aUserSpaceToImageSpace,
    2556                 :                   const gfxRect &aFill,
    2557                 :                   const nsIntRect &aSubimage,
    2558                 :                   const nsIntSize& /*aViewportSize - ignored*/,
    2559                 :                   PRUint32 aFlags)
    2560                 : {
    2561               0 :   if (mError)
    2562               0 :     return NS_ERROR_FAILURE;
    2563                 : 
    2564                 :   // Disallowed in the API
    2565               0 :   if (mInDecoder && (aFlags & imgIContainer::FLAG_SYNC_DECODE))
    2566               0 :     return NS_ERROR_FAILURE;
    2567                 : 
    2568                 :   // Illegal -- you can't draw with non-default decode flags.
    2569                 :   // (Disabling colorspace conversion might make sense to allow, but
    2570                 :   // we don't currently.)
    2571               0 :   if ((aFlags & DECODE_FLAGS_MASK) != DECODE_FLAGS_DEFAULT)
    2572               0 :     return NS_ERROR_FAILURE;
    2573                 : 
    2574               0 :   NS_ENSURE_ARG_POINTER(aContext);
    2575                 : 
    2576                 :   // We can only draw with the default decode flags
    2577               0 :   if (mFrameDecodeFlags != DECODE_FLAGS_DEFAULT) {
    2578               0 :     if (!CanForciblyDiscard())
    2579               0 :       return NS_ERROR_NOT_AVAILABLE;
    2580               0 :     ForceDiscard();
    2581                 : 
    2582               0 :     mFrameDecodeFlags = DECODE_FLAGS_DEFAULT;
    2583                 :   }
    2584                 : 
    2585                 :   // If this image is a candidate for discarding, reset its position in the
    2586                 :   // discard tracker so we're less likely to discard it right away.
    2587                 :   //
    2588                 :   // (We don't normally draw unlocked images, so this conditition will usually
    2589                 :   // be false.  But we will draw unlocked images if image locking is globally
    2590                 :   // disabled via the content.image.allow_locking pref.)
    2591               0 :   if (DiscardingActive()) {
    2592               0 :     DiscardTracker::Reset(&mDiscardTrackerNode);
    2593                 :   }
    2594                 : 
    2595                 :   // We use !mDecoded && mHasSourceData to mean discarded.
    2596               0 :   if (!mDecoded && mHasSourceData) {
    2597               0 :       mDrawStartTime = TimeStamp::Now();
    2598                 : 
    2599                 :       // We're drawing this image, so indicate that we should decode it as soon
    2600                 :       // as possible.
    2601               0 :       DecodeWorker::Singleton()->MarkAsASAP(this);
    2602                 :   }
    2603                 : 
    2604                 :   // If a synchronous draw is requested, flush anything that might be sitting around
    2605               0 :   if (aFlags & FLAG_SYNC_DECODE) {
    2606               0 :     nsresult rv = SyncDecode();
    2607               0 :     NS_ENSURE_SUCCESS(rv, rv);
    2608                 :   }
    2609                 : 
    2610               0 :   imgFrame *frame = GetCurrentDrawableImgFrame();
    2611               0 :   if (!frame) {
    2612               0 :     return NS_OK; // Getting the frame (above) touches the image and kicks off decoding
    2613                 :   }
    2614                 : 
    2615               0 :   nsIntRect framerect = frame->GetRect();
    2616                 :   nsIntMargin padding(framerect.x, framerect.y, 
    2617               0 :                       mSize.width - framerect.XMost(),
    2618               0 :                       mSize.height - framerect.YMost());
    2619                 : 
    2620               0 :   frame->Draw(aContext, aFilter, aUserSpaceToImageSpace, aFill, padding, aSubimage);
    2621                 : 
    2622               0 :   if (mDecoded && !mDrawStartTime.IsNull()) {
    2623               0 :       TimeDuration drawLatency = TimeStamp::Now() - mDrawStartTime;
    2624               0 :       Telemetry::Accumulate(Telemetry::IMAGE_DECODE_ON_DRAW_LATENCY, PRInt32(drawLatency.ToMicroseconds()));
    2625                 :       // clear the value of mDrawStartTime
    2626               0 :       mDrawStartTime = TimeStamp();
    2627                 :   }
    2628               0 :   return NS_OK;
    2629                 : }
    2630                 : 
    2631                 : //******************************************************************************
    2632                 : /* [notxpcom] nsIFrame GetRootLayoutFrame() */
    2633                 : nsIFrame*
    2634               0 : RasterImage::GetRootLayoutFrame()
    2635                 : {
    2636               0 :   return nsnull;
    2637                 : }
    2638                 : 
    2639                 : //******************************************************************************
    2640                 : /* void lockImage() */
    2641                 : NS_IMETHODIMP
    2642               4 : RasterImage::LockImage()
    2643                 : {
    2644               4 :   if (mError)
    2645               0 :     return NS_ERROR_FAILURE;
    2646                 : 
    2647                 :   // Cancel the discard timer if it's there
    2648               4 :   DiscardTracker::Remove(&mDiscardTrackerNode);
    2649                 : 
    2650                 :   // Increment the lock count
    2651               4 :   mLockCount++;
    2652                 : 
    2653               4 :   return NS_OK;
    2654                 : }
    2655                 : 
    2656                 : //******************************************************************************
    2657                 : /* void unlockImage() */
    2658                 : NS_IMETHODIMP
    2659               3 : RasterImage::UnlockImage()
    2660                 : {
    2661               3 :   if (mError)
    2662               1 :     return NS_ERROR_FAILURE;
    2663                 : 
    2664                 :   // It's an error to call this function if the lock count is 0
    2665               2 :   NS_ABORT_IF_FALSE(mLockCount > 0,
    2666                 :                     "Calling UnlockImage with mLockCount == 0!");
    2667               2 :   if (mLockCount == 0)
    2668               0 :     return NS_ERROR_ABORT;
    2669                 : 
    2670                 :   // We're locked, so discarding should not be active
    2671               2 :   NS_ABORT_IF_FALSE(!DiscardingActive(), "Locked, but discarding activated");
    2672                 : 
    2673                 :   // Decrement our lock count
    2674               2 :   mLockCount--;
    2675                 : 
    2676                 :   // If we've decoded this image once before, we're currently decoding again,
    2677                 :   // and our lock count is now zero (so nothing is forcing us to keep the
    2678                 :   // decoded data around), try to cancel the decode and throw away whatever
    2679                 :   // we've decoded.
    2680               2 :   if (mHasBeenDecoded && mDecoder &&
    2681               0 :       mLockCount == 0 && CanForciblyDiscard()) {
    2682               0 :     PR_LOG(gCompressedImageAccountingLog, PR_LOG_DEBUG,
    2683                 :            ("RasterImage[0x%p] canceling decode because image "
    2684                 :             "is now unlocked.", this));
    2685               0 :     ShutdownDecoder(eShutdownIntent_Interrupted);
    2686               0 :     ForceDiscard();
    2687               0 :     return NS_OK;
    2688                 :   }
    2689                 : 
    2690                 :   // Otherwise, we might still be a candidate for discarding in the future.  If
    2691                 :   // we are, add ourselves to the discard tracker.
    2692               2 :   if (CanDiscard()) {
    2693               1 :     nsresult rv = DiscardTracker::Reset(&mDiscardTrackerNode);
    2694               1 :     CONTAINER_ENSURE_SUCCESS(rv);
    2695                 :   }
    2696                 : 
    2697               2 :   return NS_OK;
    2698                 : }
    2699                 : 
    2700                 : //******************************************************************************
    2701                 : /* void requestDiscard() */
    2702                 : NS_IMETHODIMP
    2703               0 : RasterImage::RequestDiscard()
    2704                 : {
    2705               0 :   if (CanDiscard()) {
    2706               0 :     ForceDiscard();
    2707                 :   }
    2708                 : 
    2709               0 :   return NS_OK;
    2710                 : }
    2711                 : 
    2712                 : // Flushes up to aMaxBytes to the decoder.
    2713                 : nsresult
    2714               4 : RasterImage::DecodeSomeData(PRUint32 aMaxBytes)
    2715                 : {
    2716                 :   // We should have a decoder if we get here
    2717               4 :   NS_ABORT_IF_FALSE(mDecoder, "trying to decode without decoder!");
    2718                 : 
    2719                 :   // If we have nothing to decode, return
    2720               4 :   if (mBytesDecoded == mSourceData.Length())
    2721               0 :     return NS_OK;
    2722                 : 
    2723                 : 
    2724                 :   // write the proper amount of data
    2725                 :   PRUint32 bytesToDecode = NS_MIN(aMaxBytes,
    2726               4 :                                   mSourceData.Length() - mBytesDecoded);
    2727               4 :   nsresult rv = WriteToDecoder(mSourceData.Elements() + mBytesDecoded,
    2728               4 :                                bytesToDecode);
    2729                 : 
    2730               4 :   return rv;
    2731                 : }
    2732                 : 
    2733                 : // There are various indicators that tell us we're finished with the decode
    2734                 : // task at hand and can shut down the decoder.
    2735                 : //
    2736                 : // This method may not be called if there is no decoder.
    2737                 : bool
    2738               6 : RasterImage::IsDecodeFinished()
    2739                 : {
    2740                 :   // Precondition
    2741               6 :   NS_ABORT_IF_FALSE(mDecoder, "Can't call IsDecodeFinished() without decoder!");
    2742                 : 
    2743                 :   // Assume it's not finished
    2744               6 :   bool decodeFinished = false;
    2745                 : 
    2746                 :   // There shouldn't be any reason to call this if we're not storing
    2747                 :   // source data
    2748               6 :   NS_ABORT_IF_FALSE(StoringSourceData(),
    2749                 :                     "just shut down on SourceDataComplete!");
    2750                 : 
    2751                 :   // The decode is complete if we got what we wanted...
    2752               6 :   if (mDecoder->IsSizeDecode()) {
    2753               4 :     if (mHasSize)
    2754               4 :       decodeFinished = true;
    2755                 :   }
    2756                 :   else {
    2757               2 :     if (mDecoded)
    2758               1 :       decodeFinished = true;
    2759                 :   }
    2760                 : 
    2761                 :   // ...or if we have all the source data and wrote all the source data.
    2762                 :   //
    2763                 :   // (NB - This can be distinct from the above case even for non-erroneous
    2764                 :   // images because the decoder might not call DecodingComplete() until we
    2765                 :   // call Close() in ShutdownDecoder())
    2766               6 :   if (mHasSourceData && (mBytesDecoded == mSourceData.Length()))
    2767               6 :     decodeFinished = true;
    2768                 : 
    2769               6 :   return decodeFinished;
    2770                 : }
    2771                 : 
    2772                 : // Indempotent error flagging routine. If a decoder is open, shuts it down.
    2773                 : void
    2774              10 : RasterImage::DoError()
    2775                 : {
    2776                 :   // If we've flagged an error before, we have nothing to do
    2777              10 :   if (mError)
    2778               5 :     return;
    2779                 : 
    2780                 :   // If we're mid-decode, shut down the decoder.
    2781               5 :   if (mDecoder)
    2782               0 :     ShutdownDecoder(eShutdownIntent_Error);
    2783                 : 
    2784                 :   // Put the container in an error state
    2785               5 :   mError = true;
    2786                 : 
    2787                 :   // Log our error
    2788               5 :   LOG_CONTAINER_ERROR;
    2789                 : }
    2790                 : 
    2791                 : // nsIInputStream callback to copy the incoming image data directly to the
    2792                 : // RasterImage without processing. The RasterImage is passed as the closure.
    2793                 : // Always reads everything it gets, even if the data is erroneous.
    2794                 : NS_METHOD
    2795              57 : RasterImage::WriteToRasterImage(nsIInputStream* /* unused */,
    2796                 :                                 void*          aClosure,
    2797                 :                                 const char*    aFromRawSegment,
    2798                 :                                 PRUint32       /* unused */,
    2799                 :                                 PRUint32       aCount,
    2800                 :                                 PRUint32*      aWriteCount)
    2801                 : {
    2802                 :   // Retrieve the RasterImage
    2803              57 :   RasterImage* image = static_cast<RasterImage*>(aClosure);
    2804                 : 
    2805                 :   // Copy the source data. Unless we hit OOM, we squelch the return value
    2806                 :   // here, because returning an error means that ReadSegments stops
    2807                 :   // reading data, violating our invariant that we read everything we get.
    2808                 :   // If we hit OOM then we fail and the load is aborted.
    2809              57 :   nsresult rv = image->AddSourceData(aFromRawSegment, aCount);
    2810              57 :   if (rv == NS_ERROR_OUT_OF_MEMORY) {
    2811               0 :     image->DoError();
    2812               0 :     return rv;
    2813                 :   }
    2814                 : 
    2815                 :   // We wrote everything we got
    2816              57 :   *aWriteCount = aCount;
    2817                 : 
    2818              57 :   return NS_OK;
    2819                 : }
    2820                 : 
    2821                 : bool
    2822               2 : RasterImage::ShouldAnimate()
    2823                 : {
    2824               2 :   return Image::ShouldAnimate() && mFrames.Length() >= 2 &&
    2825               2 :          !mAnimationFinished;
    2826                 : }
    2827                 : 
    2828                 : /* readonly attribute PRUint32 framesNotified; */
    2829                 : #ifdef DEBUG
    2830                 : NS_IMETHODIMP
    2831               0 : RasterImage::GetFramesNotified(PRUint32 *aFramesNotified)
    2832                 : {
    2833               0 :   NS_ENSURE_ARG_POINTER(aFramesNotified);
    2834                 : 
    2835               0 :   *aFramesNotified = mFramesNotified;
    2836                 : 
    2837               0 :   return NS_OK;
    2838                 : }
    2839                 : #endif
    2840                 : 
    2841                 : /* static */ RasterImage::DecodeWorker*
    2842              28 : RasterImage::DecodeWorker::Singleton()
    2843                 : {
    2844              28 :   if (!sSingleton) {
    2845               4 :     sSingleton = new DecodeWorker();
    2846               4 :     ClearOnShutdown(&sSingleton);
    2847                 :   }
    2848                 : 
    2849              28 :   return sSingleton;
    2850                 : }
    2851                 : 
    2852                 : void
    2853               0 : RasterImage::DecodeWorker::MarkAsASAP(RasterImage* aImg)
    2854                 : {
    2855               0 :   DecodeRequest* request = &aImg->mDecodeRequest;
    2856                 : 
    2857                 :   // If we're already an ASAP request, there's nothing to do here.
    2858               0 :   if (request->mIsASAP) {
    2859               0 :     return;
    2860                 :   }
    2861                 : 
    2862               0 :   request->mIsASAP = true;
    2863                 : 
    2864               0 :   if (request->isInList()) {
    2865                 :     // If the decode request is in a list, it must be in the normal decode
    2866                 :     // requests list -- if it had been in the ASAP list, then mIsASAP would
    2867                 :     // have been true above.  Move the request to the ASAP list.
    2868               0 :     request->remove();
    2869               0 :     mASAPDecodeRequests.insertBack(request);
    2870                 : 
    2871                 :     // Since request is in a list, one of the decode worker's lists is
    2872                 :     // non-empty, so the worker should be pending in the event loop.
    2873                 :     //
    2874                 :     // (Note that this invariant only holds while we are not in Run(), because
    2875                 :     // DecodeSomeOfImage adds requests to the decode worker using
    2876                 :     // AddDecodeRequest, not RequestDecode, and AddDecodeRequest does not call
    2877                 :     // EnsurePendingInEventLoop.  Therefore, it is an error to call MarkAsASAP
    2878                 :     // from within DecodeWorker::Run.)
    2879               0 :     MOZ_ASSERT(mPendingInEventLoop);
    2880                 :   }
    2881                 : }
    2882                 : 
    2883                 : void
    2884               4 : RasterImage::DecodeWorker::AddDecodeRequest(DecodeRequest* aRequest)
    2885                 : {
    2886               4 :   if (aRequest->isInList()) {
    2887                 :     // The image is already in our list of images to decode, so we don't have
    2888                 :     // to do anything here.
    2889               0 :     return;
    2890                 :   }
    2891                 : 
    2892               4 :   if (aRequest->mIsASAP) {
    2893               0 :     mASAPDecodeRequests.insertBack(aRequest);
    2894                 :   } else {
    2895               4 :     mNormalDecodeRequests.insertBack(aRequest);
    2896                 :   }
    2897                 : }
    2898                 : 
    2899                 : void
    2900               4 : RasterImage::DecodeWorker::RequestDecode(RasterImage* aImg)
    2901                 : {
    2902               4 :   AddDecodeRequest(&aImg->mDecodeRequest);
    2903               4 :   EnsurePendingInEventLoop();
    2904               4 : }
    2905                 : 
    2906                 : void
    2907               4 : RasterImage::DecodeWorker::EnsurePendingInEventLoop()
    2908                 : {
    2909               4 :   if (!mPendingInEventLoop) {
    2910               4 :     mPendingInEventLoop = true;
    2911               4 :     NS_DispatchToCurrentThread(this);
    2912                 :   }
    2913               4 : }
    2914                 : 
    2915                 : void
    2916              20 : RasterImage::DecodeWorker::StopDecoding(RasterImage* aImg)
    2917                 : {
    2918              20 :   DecodeRequest* request = &aImg->mDecodeRequest;
    2919              20 :   if (request->isInList()) {
    2920               4 :     request->remove();
    2921                 :   }
    2922              20 :   request->mDecodeTime = TimeDuration(0);
    2923              20 :   request->mIsASAP = false;
    2924              20 : }
    2925                 : 
    2926                 : NS_IMETHODIMP
    2927               4 : RasterImage::DecodeWorker::Run()
    2928                 : {
    2929                 :   // We just got called back by the event loop; therefore, we're no longer
    2930                 :   // pending.
    2931               4 :   mPendingInEventLoop = false;
    2932                 : 
    2933               4 :   TimeStamp eventStart = TimeStamp::Now();
    2934                 : 
    2935                 :   // Now decode until we either run out of time or run out of images.
    2936               0 :   do {
    2937                 :     // Try to get an ASAP request to handle.  If there isn't one, try to get a
    2938                 :     // normal request.  If no normal request is pending either, then we're done
    2939                 :     // here.
    2940               4 :     DecodeRequest* request = mASAPDecodeRequests.popFirst();
    2941               4 :     if (!request)
    2942               4 :       request = mNormalDecodeRequests.popFirst();
    2943               4 :     if (!request)
    2944               4 :       break;
    2945                 : 
    2946                 :     // This has to be a strong pointer, because DecodeSomeOfImage may destroy
    2947                 :     // image->mDecoder, which may be holding the only other reference to image.
    2948               0 :     nsRefPtr<RasterImage> image = request->mImage;
    2949               0 :     DecodeSomeOfImage(image);
    2950                 : 
    2951                 :     // If we aren't yet finished decoding and we have more data in hand, add
    2952                 :     // this request to the back of the list.
    2953               0 :     if (image->mDecoder &&
    2954               0 :         !image->mError &&
    2955               0 :         !image->IsDecodeFinished() &&
    2956               0 :         image->mSourceData.Length() > image->mBytesDecoded) {
    2957               0 :       AddDecodeRequest(request);
    2958                 :     }
    2959                 : 
    2960               0 :   } while ((TimeStamp::Now() - eventStart).ToMilliseconds() <= gMaxMSBeforeYield);
    2961                 : 
    2962                 :   // If decode requests are pending, re-post ourself to the event loop.
    2963               4 :   if (!mASAPDecodeRequests.isEmpty() || !mNormalDecodeRequests.isEmpty()) {
    2964               0 :     EnsurePendingInEventLoop();
    2965                 :   }
    2966                 : 
    2967                 :   Telemetry::Accumulate(Telemetry::IMAGE_DECODE_LATENCY,
    2968               4 :                         PRUint32((TimeStamp::Now() - eventStart).ToMilliseconds()));
    2969                 : 
    2970               4 :   return NS_OK;
    2971                 : }
    2972                 : 
    2973                 : nsresult
    2974               4 : RasterImage::DecodeWorker::DecodeUntilSizeAvailable(RasterImage* aImg)
    2975                 : {
    2976               4 :   return DecodeSomeOfImage(aImg, DECODE_TYPE_UNTIL_SIZE);
    2977                 : }
    2978                 : 
    2979                 : nsresult
    2980               4 : RasterImage::DecodeWorker::DecodeSomeOfImage(
    2981                 :   RasterImage* aImg,
    2982                 :   DecodeType aDecodeType /* = DECODE_TYPE_NORMAL */)
    2983                 : {
    2984               4 :   NS_ABORT_IF_FALSE(aImg->mInitialized,
    2985                 :                     "Worker active for uninitialized container!");
    2986                 : 
    2987               4 :   if (aDecodeType == DECODE_TYPE_UNTIL_SIZE && aImg->mHasSize)
    2988               0 :     return NS_OK;
    2989                 : 
    2990                 :   // If an error is flagged, it probably happened while we were waiting
    2991                 :   // in the event queue.
    2992               4 :   if (aImg->mError)
    2993               0 :     return NS_OK;
    2994                 : 
    2995                 :   // If mDecoded or we don't have a decoder, we must have finished already (for
    2996                 :   // example, a synchronous decode request came while the worker was pending).
    2997               4 :   if (!aImg->mDecoder || aImg->mDecoded)
    2998               0 :     return NS_OK;
    2999                 : 
    3000               8 :   nsRefPtr<Decoder> decoderKungFuDeathGrip = aImg->mDecoder;
    3001                 : 
    3002                 :   PRUint32 maxBytes;
    3003               4 :   if (aImg->mDecoder->IsSizeDecode()) {
    3004                 :     // Decode all available data if we're a size decode; they're cheap, and we
    3005                 :     // want them to be more or less synchronous.
    3006               4 :     maxBytes = aImg->mSourceData.Length();
    3007                 :   } else {
    3008                 :     // We're only guaranteed to decode this many bytes, so in particular,
    3009                 :     // gDecodeBytesAtATime should be set high enough for us to read the size
    3010                 :     // from most images.
    3011               0 :     maxBytes = gDecodeBytesAtATime;
    3012                 :   }
    3013                 : 
    3014               4 :   PRInt32 chunkCount = 0;
    3015               4 :   TimeStamp start = TimeStamp::Now();
    3016               4 :   TimeStamp deadline = start + TimeDuration::FromMilliseconds(gMaxMSBeforeYield);
    3017                 : 
    3018                 :   // Decode some chunks of data.
    3019               0 :   do {
    3020               4 :     chunkCount++;
    3021               4 :     nsresult rv = aImg->DecodeSomeData(maxBytes);
    3022               4 :     if (NS_FAILED(rv)) {
    3023               0 :       aImg->DoError();
    3024               0 :       return rv;
    3025                 :     }
    3026                 : 
    3027                 :     // We keep decoding chunks until either:
    3028                 :     //  * we're an UNTIL_SIZE decode and we get the size,
    3029                 :     //  * we don't have any data left to decode,
    3030                 :     //  * the decode completes, or
    3031                 :     //  * we run out of time.
    3032                 : 
    3033               4 :     if (aDecodeType == DECODE_TYPE_UNTIL_SIZE && aImg->mHasSize)
    3034               4 :       break;
    3035                 : 
    3036               0 :   } while (aImg->mSourceData.Length() > aImg->mBytesDecoded &&
    3037               0 :            !aImg->IsDecodeFinished() &&
    3038               0 :            TimeStamp::Now() < deadline);
    3039                 : 
    3040               4 :   aImg->mDecodeRequest.mDecodeTime += (TimeStamp::Now() - start);
    3041                 : 
    3042               4 :   if (chunkCount && !aImg->mDecoder->IsSizeDecode()) {
    3043               0 :     Telemetry::Accumulate(Telemetry::IMAGE_DECODE_CHUNKS, chunkCount);
    3044                 :   }
    3045                 : 
    3046                 :   // Flush invalidations (and therefore paint) now that we've decoded all the
    3047                 :   // chunks we're going to.
    3048                 :   //
    3049                 :   // However, don't paint if:
    3050                 :   //
    3051                 :   //  * This was an until-size decode.  Until-size decodes are always followed
    3052                 :   //    by normal decodes, so don't bother painting.
    3053                 :   //
    3054                 :   //  * The decoder flagged an error.  The decoder may have written garbage
    3055                 :   //    into the output buffer; don't paint it to the screen.
    3056                 :   //
    3057                 :   //  * We have all the source data.  This disables progressive display of
    3058                 :   //    previously-decoded images, thus letting us finish decoding faster,
    3059                 :   //    since we don't waste time painting while we decode.
    3060                 :   //    Decoder::PostFrameStop() will flush invalidations once the decode is
    3061                 :   //    done.
    3062                 : 
    3063               4 :   if (aDecodeType != DECODE_TYPE_UNTIL_SIZE &&
    3064               0 :       !aImg->mDecoder->HasError() &&
    3065               0 :       !aImg->mHasSourceData) {
    3066               0 :     aImg->mInDecoder = true;
    3067               0 :     aImg->mDecoder->FlushInvalidations();
    3068               0 :     aImg->mInDecoder = false;
    3069                 :   }
    3070                 : 
    3071                 :   // If the decode finished, shut down the decoder.
    3072               4 :   if (aImg->mDecoder && aImg->IsDecodeFinished()) {
    3073                 : 
    3074                 :     // Do some telemetry if this isn't a size decode.
    3075               4 :     DecodeRequest* request = &aImg->mDecodeRequest;
    3076               4 :     if (!aImg->mDecoder->IsSizeDecode()) {
    3077                 :       Telemetry::Accumulate(Telemetry::IMAGE_DECODE_TIME,
    3078               0 :                             PRInt32(request->mDecodeTime.ToMicroseconds()));
    3079                 : 
    3080                 :       // We record the speed for only some decoders. The rest have
    3081                 :       // SpeedHistogram return HistogramCount.
    3082               0 :       Telemetry::ID id = aImg->mDecoder->SpeedHistogram();
    3083               0 :       if (id < Telemetry::HistogramCount) {
    3084                 :           PRInt32 KBps = PRInt32(request->mImage->mBytesDecoded /
    3085               0 :                                  (1024 * request->mDecodeTime.ToSeconds()));
    3086               0 :           Telemetry::Accumulate(id, KBps);
    3087                 :       }
    3088                 :     }
    3089                 : 
    3090               4 :     nsresult rv = aImg->ShutdownDecoder(RasterImage::eShutdownIntent_Done);
    3091               4 :     if (NS_FAILED(rv)) {
    3092               0 :       aImg->DoError();
    3093               0 :       return rv;
    3094                 :     }
    3095                 :   }
    3096                 : 
    3097               4 :   return NS_OK;
    3098                 : }
    3099                 : 
    3100                 : } // namespace image
    3101            4392 : } // namespace mozilla

Generated by: LCOV version 1.7