LCOV - code coverage report
Current view: directory - image/src - imgRequest.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 524 347 66.2 %
Date: 2012-06-02 Functions: 50 34 68.0 %

       1                 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
       2                 :  *
       3                 :  * ***** BEGIN LICENSE BLOCK *****
       4                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       5                 :  *
       6                 :  * The contents of this file are subject to the Mozilla Public License Version
       7                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       8                 :  * the License. You may obtain a copy of the License at
       9                 :  * http://www.mozilla.org/MPL/
      10                 :  *
      11                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      12                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      13                 :  * for the specific language governing rights and limitations under the
      14                 :  * License.
      15                 :  *
      16                 :  * The Original Code is mozilla.org code.
      17                 :  *
      18                 :  * The Initial Developer of the Original Code is
      19                 :  * Netscape Communications Corporation.
      20                 :  * Portions created by the Initial Developer are Copyright (C) 2001
      21                 :  * the Initial Developer. All Rights Reserved.
      22                 :  *
      23                 :  * Contributor(s):
      24                 :  *   Stuart Parmenter <stuart@mozilla.com>
      25                 :  *   Bobby Holley <bobbyholley@gmail.com>
      26                 :  *
      27                 :  * Alternatively, the contents of this file may be used under the terms of
      28                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      29                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      30                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      31                 :  * of those above. If you wish to allow use of your version of this file only
      32                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      33                 :  * use your version of this file under the terms of the MPL, indicate your
      34                 :  * decision by deleting the provisions above and replace them with the notice
      35                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      36                 :  * the provisions above, a recipient may use your version of this file under
      37                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      38                 :  *
      39                 :  * ***** END LICENSE BLOCK ***** */
      40                 : 
      41                 : #include "imgRequest.h"
      42                 : #include "ImageLogging.h"
      43                 : 
      44                 : /* We end up pulling in windows.h because we eventually hit gfxWindowsSurface;
      45                 :  * windows.h defines LoadImage, so we have to #undef it or imgLoader::LoadImage
      46                 :  * gets changed.
      47                 :  * This #undef needs to be in multiple places because we don't always pull
      48                 :  * headers in in the same order.
      49                 :  */
      50                 : #undef LoadImage
      51                 : 
      52                 : #include "imgLoader.h"
      53                 : #include "imgRequestProxy.h"
      54                 : #include "RasterImage.h"
      55                 : #include "VectorImage.h"
      56                 : 
      57                 : #include "imgILoader.h"
      58                 : 
      59                 : #include "netCore.h"
      60                 : 
      61                 : #include "nsIChannel.h"
      62                 : #include "nsICachingChannel.h"
      63                 : #include "nsILoadGroup.h"
      64                 : #include "nsIInputStream.h"
      65                 : #include "nsIMultiPartChannel.h"
      66                 : #include "nsIHttpChannel.h"
      67                 : 
      68                 : #include "nsIComponentManager.h"
      69                 : #include "nsIInterfaceRequestorUtils.h"
      70                 : #include "nsIServiceManager.h"
      71                 : #include "nsISupportsPrimitives.h"
      72                 : #include "nsIScriptSecurityManager.h"
      73                 : 
      74                 : #include "nsICacheVisitor.h"
      75                 : 
      76                 : #include "nsString.h"
      77                 : #include "nsXPIDLString.h"
      78                 : #include "plstr.h" // PL_strcasestr(...)
      79                 : #include "nsNetUtil.h"
      80                 : #include "nsIProtocolHandler.h"
      81                 : 
      82                 : #include "mozilla/Preferences.h"
      83                 : 
      84                 : #include "DiscardTracker.h"
      85                 : #include "nsAsyncRedirectVerifyHelper.h"
      86                 : 
      87                 : #define SVG_MIMETYPE "image/svg+xml"
      88                 : 
      89                 : using namespace mozilla;
      90                 : using namespace mozilla::image;
      91                 : 
      92                 : static bool gInitializedPrefCaches = false;
      93                 : static bool gDecodeOnDraw = false;
      94                 : static bool gDiscardable = false;
      95                 : 
      96                 : static void
      97               3 : InitPrefCaches()
      98                 : {
      99               3 :   Preferences::AddBoolVarCache(&gDiscardable, "image.mem.discardable");
     100               3 :   Preferences::AddBoolVarCache(&gDecodeOnDraw, "image.mem.decodeondraw");
     101               3 :   gInitializedPrefCaches = true;
     102               3 : }
     103                 : 
     104                 : #if defined(PR_LOGGING)
     105            1464 : PRLogModuleInfo *gImgLog = PR_NewLogModule("imgRequest");
     106                 : #endif
     107                 : 
     108             372 : NS_IMPL_ISUPPORTS8(imgRequest,
     109                 :                    imgIDecoderObserver, imgIContainerObserver,
     110                 :                    nsIStreamListener, nsIRequestObserver,
     111                 :                    nsISupportsWeakReference,
     112                 :                    nsIChannelEventSink,
     113                 :                    nsIInterfaceRequestor,
     114                 :                    nsIAsyncVerifyRedirectCallback)
     115                 : 
     116               8 : imgRequest::imgRequest() : 
     117                 :   mValidator(nsnull), mImageSniffers("image-sniffing-services"),
     118                 :   mInnerWindowId(0), mCORSMode(imgIRequest::CORS_NONE),
     119                 :   mDecodeRequested(false), mIsMultiPartChannel(false), mGotData(false),
     120               8 :   mIsInCache(false)
     121                 : {
     122                 :   // Register our pref observers if we haven't yet.
     123               8 :   if (NS_UNLIKELY(!gInitializedPrefCaches)) {
     124               3 :     InitPrefCaches();
     125                 :   }
     126               8 : }
     127                 : 
     128              24 : imgRequest::~imgRequest()
     129                 : {
     130               8 :   if (mURI) {
     131              16 :     nsCAutoString spec;
     132               8 :     mURI->GetSpec(spec);
     133               8 :     LOG_FUNC_WITH_PARAM(gImgLog, "imgRequest::~imgRequest()", "keyuri", spec.get());
     134                 :   } else
     135               0 :     LOG_FUNC(gImgLog, "imgRequest::~imgRequest()");
     136              32 : }
     137                 : 
     138               8 : nsresult imgRequest::Init(nsIURI *aURI,
     139                 :                           nsIURI *aCurrentURI,
     140                 :                           nsIRequest *aRequest,
     141                 :                           nsIChannel *aChannel,
     142                 :                           imgCacheEntry *aCacheEntry,
     143                 :                           void *aLoadId,
     144                 :                           nsIPrincipal* aLoadingPrincipal,
     145                 :                           PRInt32 aCORSMode)
     146                 : {
     147               8 :   LOG_FUNC(gImgLog, "imgRequest::Init");
     148                 : 
     149               8 :   NS_ABORT_IF_FALSE(!mImage, "Multiple calls to init");
     150               8 :   NS_ABORT_IF_FALSE(aURI, "No uri");
     151               8 :   NS_ABORT_IF_FALSE(aCurrentURI, "No current uri");
     152               8 :   NS_ABORT_IF_FALSE(aRequest, "No request");
     153               8 :   NS_ABORT_IF_FALSE(aChannel, "No channel");
     154                 : 
     155               8 :   mProperties = do_CreateInstance("@mozilla.org/properties;1");
     156                 : 
     157               8 :   mStatusTracker = new imgStatusTracker(nsnull);
     158                 : 
     159               8 :   mURI = aURI;
     160               8 :   mCurrentURI = aCurrentURI;
     161               8 :   mRequest = aRequest;
     162               8 :   mChannel = aChannel;
     163               8 :   mTimedChannel = do_QueryInterface(mChannel);
     164                 : 
     165               8 :   mLoadingPrincipal = aLoadingPrincipal;
     166               8 :   mCORSMode = aCORSMode;
     167                 : 
     168               8 :   mChannel->GetNotificationCallbacks(getter_AddRefs(mPrevChannelSink));
     169                 : 
     170               8 :   NS_ASSERTION(mPrevChannelSink != this,
     171                 :                "Initializing with a channel that already calls back to us!");
     172                 : 
     173               8 :   mChannel->SetNotificationCallbacks(this);
     174                 : 
     175               8 :   mCacheEntry = aCacheEntry;
     176                 : 
     177               8 :   SetLoadId(aLoadId);
     178                 : 
     179               8 :   return NS_OK;
     180                 : }
     181                 : 
     182                 : imgStatusTracker&
     183              67 : imgRequest::GetStatusTracker()
     184                 : {
     185              67 :   if (mImage) {
     186              48 :     NS_ABORT_IF_FALSE(!mStatusTracker,
     187                 :                       "Should have given mStatusTracker to mImage");
     188              48 :     return mImage->GetStatusTracker();
     189                 :   } else {
     190              19 :     NS_ABORT_IF_FALSE(mStatusTracker,
     191                 :                       "Should have mStatusTracker until we create mImage");
     192              19 :     return *mStatusTracker;
     193                 :   }
     194                 : }
     195                 : 
     196               0 : void imgRequest::SetCacheEntry(imgCacheEntry *entry)
     197                 : {
     198               0 :   mCacheEntry = entry;
     199               0 : }
     200                 : 
     201               0 : bool imgRequest::HasCacheEntry() const
     202                 : {
     203               0 :   return mCacheEntry != nsnull;
     204                 : }
     205                 : 
     206              27 : nsresult imgRequest::AddProxy(imgRequestProxy *proxy)
     207                 : {
     208              27 :   NS_PRECONDITION(proxy, "null imgRequestProxy passed in");
     209              54 :   LOG_SCOPE_WITH_PARAM(gImgLog, "imgRequest::AddProxy", "proxy", proxy);
     210                 : 
     211                 :   // If we're empty before adding, we have to tell the loader we now have
     212                 :   // proxies.
     213              27 :   if (mObservers.IsEmpty()) {
     214               8 :     NS_ABORT_IF_FALSE(mURI, "Trying to SetHasProxies without key uri.");
     215               8 :     imgLoader::SetHasProxies(mURI);
     216                 :   }
     217                 : 
     218                 :   // If we don't have any current observers, we should restart any animation.
     219              27 :   if (mImage && !HaveProxyWithObserver(proxy) && proxy->HasObserver()) {
     220               0 :     LOG_MSG(gImgLog, "imgRequest::AddProxy", "resetting animation");
     221                 : 
     222               0 :     mImage->ResetAnimation();
     223                 :   }
     224                 : 
     225              27 :   proxy->SetPrincipal(mPrincipal);
     226                 : 
     227              27 :   return mObservers.AppendElementUnlessExists(proxy) ?
     228              27 :     NS_OK : NS_ERROR_OUT_OF_MEMORY;
     229                 : }
     230                 : 
     231              27 : nsresult imgRequest::RemoveProxy(imgRequestProxy *proxy, nsresult aStatus, bool aNotify)
     232                 : {
     233              54 :   LOG_SCOPE_WITH_PARAM(gImgLog, "imgRequest::RemoveProxy", "proxy", proxy);
     234                 : 
     235                 :   // This will remove our animation consumers, so after removing
     236                 :   // this proxy, we don't end up without proxies with observers, but still
     237                 :   // have animation consumers.
     238              27 :   proxy->ClearAnimationConsumers();
     239                 : 
     240              27 :   if (!mObservers.RemoveElement(proxy)) {
     241                 :     // Not one of our proxies; we're done
     242               0 :     return NS_OK;
     243                 :   }
     244                 : 
     245                 :   // Let the status tracker do its thing before we potentially call Cancel()
     246                 :   // below, because Cancel() may result in OnStopRequest being called back
     247                 :   // before Cancel() returns, leaving the image in a different state then the
     248                 :   // one it was in at this point.
     249                 : 
     250              27 :   imgStatusTracker& statusTracker = GetStatusTracker();
     251              27 :   statusTracker.EmulateRequestFinished(proxy, aStatus, !aNotify);
     252                 : 
     253              27 :   if (mObservers.IsEmpty()) {
     254                 :     // If we have no observers, there's nothing holding us alive. If we haven't
     255                 :     // been cancelled and thus removed from the cache, tell the image loader so
     256                 :     // we can be evicted from the cache.
     257               8 :     if (mCacheEntry) {
     258               4 :       NS_ABORT_IF_FALSE(mURI, "Removing last observer without key uri.");
     259                 : 
     260               4 :       imgLoader::SetHasNoProxies(mURI, mCacheEntry);
     261                 :     } 
     262                 : #if defined(PR_LOGGING)
     263                 :     else {
     264               8 :       nsCAutoString spec;
     265               4 :       mURI->GetSpec(spec);
     266               4 :       LOG_MSG_WITH_PARAM(gImgLog, "imgRequest::RemoveProxy no cache entry", "uri", spec.get());
     267                 :     }
     268                 : #endif
     269                 : 
     270                 :     /* If |aStatus| is a failure code, then cancel the load if it is still in progress.
     271                 :        Otherwise, let the load continue, keeping 'this' in the cache with no observers.
     272                 :        This way, if a proxy is destroyed without calling cancel on it, it won't leak
     273                 :        and won't leave a bad pointer in mObservers.
     274                 :      */
     275               8 :     if (statusTracker.IsLoading() && NS_FAILED(aStatus)) {
     276               0 :       LOG_MSG(gImgLog, "imgRequest::RemoveProxy", "load in progress.  canceling");
     277                 : 
     278               0 :       this->Cancel(NS_BINDING_ABORTED);
     279                 :     }
     280                 : 
     281                 :     /* break the cycle from the cache entry. */
     282               8 :     mCacheEntry = nsnull;
     283                 :   }
     284                 : 
     285                 :   // If a proxy is removed for a reason other than its owner being
     286                 :   // changed, remove the proxy from the loadgroup.
     287              27 :   if (aStatus != NS_IMAGELIB_CHANGING_OWNER)
     288              27 :     proxy->RemoveFromLoadGroup(true);
     289                 : 
     290              27 :   return NS_OK;
     291                 : }
     292                 : 
     293               0 : void imgRequest::CancelAndAbort(nsresult aStatus)
     294                 : {
     295               0 :   LOG_SCOPE(gImgLog, "imgRequest::CancelAndAbort");
     296                 : 
     297               0 :   Cancel(aStatus);
     298                 : 
     299                 :   // It's possible for the channel to fail to open after we've set our
     300                 :   // notification callbacks. In that case, make sure to break the cycle between
     301                 :   // the channel and us, because it won't.
     302               0 :   if (mChannel) {
     303               0 :     mChannel->SetNotificationCallbacks(mPrevChannelSink);
     304               0 :     mPrevChannelSink = nsnull;
     305                 :   }
     306               0 : }
     307                 : 
     308               8 : void imgRequest::Cancel(nsresult aStatus)
     309                 : {
     310                 :   /* The Cancel() method here should only be called by this class. */
     311                 : 
     312              16 :   LOG_SCOPE(gImgLog, "imgRequest::Cancel");
     313                 : 
     314               8 :   imgStatusTracker& statusTracker = GetStatusTracker();
     315               8 :   statusTracker.RecordCancel();
     316                 : 
     317               8 :   RemoveFromCache();
     318                 : 
     319               8 :   if (mRequest && statusTracker.IsLoading())
     320               4 :     mRequest->Cancel(aStatus);
     321               8 : }
     322                 : 
     323              40 : nsresult imgRequest::GetURI(nsIURI **aURI)
     324                 : {
     325              40 :   LOG_FUNC(gImgLog, "imgRequest::GetURI");
     326                 : 
     327              40 :   if (mURI) {
     328              40 :     *aURI = mURI;
     329              40 :     NS_ADDREF(*aURI);
     330              40 :     return NS_OK;
     331                 :   }
     332                 : 
     333               0 :   return NS_ERROR_FAILURE;
     334                 : }
     335                 : 
     336               0 : nsresult imgRequest::GetSecurityInfo(nsISupports **aSecurityInfo)
     337                 : {
     338               0 :   LOG_FUNC(gImgLog, "imgRequest::GetSecurityInfo");
     339                 : 
     340                 :   // Missing security info means this is not a security load
     341                 :   // i.e. it is not an error when security info is missing
     342               0 :   NS_IF_ADDREF(*aSecurityInfo = mSecurityInfo);
     343               0 :   return NS_OK;
     344                 : }
     345                 : 
     346               8 : void imgRequest::RemoveFromCache()
     347                 : {
     348              16 :   LOG_SCOPE(gImgLog, "imgRequest::RemoveFromCache");
     349                 : 
     350               8 :   if (mIsInCache) {
     351                 :     // mCacheEntry is nulled out when we have no more observers.
     352               4 :     if (mCacheEntry)
     353               4 :       imgLoader::RemoveFromCache(mCacheEntry);
     354                 :     else
     355               0 :       imgLoader::RemoveFromCache(mURI);
     356                 :   }
     357                 : 
     358               8 :   mCacheEntry = nsnull;
     359               8 : }
     360                 : 
     361               8 : bool imgRequest::HaveProxyWithObserver(imgRequestProxy* aProxyToIgnore) const
     362                 : {
     363              16 :   nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
     364                 :   imgRequestProxy* proxy;
     365               8 :   while (iter.HasMore()) {
     366               8 :     proxy = iter.GetNext();
     367               8 :     if (proxy == aProxyToIgnore) {
     368               0 :       continue;
     369                 :     }
     370                 :     
     371               8 :     if (proxy->HasObserver()) {
     372               8 :       return true;
     373                 :     }
     374                 :   }
     375                 :   
     376               0 :   return false;
     377                 : }
     378                 : 
     379               0 : PRInt32 imgRequest::Priority() const
     380                 : {
     381               0 :   PRInt32 priority = nsISupportsPriority::PRIORITY_NORMAL;
     382               0 :   nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(mRequest);
     383               0 :   if (p)
     384               0 :     p->GetPriority(&priority);
     385               0 :   return priority;
     386                 : }
     387                 : 
     388               0 : void imgRequest::AdjustPriority(imgRequestProxy *proxy, PRInt32 delta)
     389                 : {
     390                 :   // only the first proxy is allowed to modify the priority of this image load.
     391                 :   //
     392                 :   // XXX(darin): this is probably not the most optimal algorithm as we may want
     393                 :   // to increase the priority of requests that have a lot of proxies.  the key
     394                 :   // concern though is that image loads remain lower priority than other pieces
     395                 :   // of content such as link clicks, CSS, and JS.
     396                 :   //
     397               0 :   if (mObservers.SafeElementAt(0, nsnull) != proxy)
     398               0 :     return;
     399                 : 
     400               0 :   nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(mRequest);
     401               0 :   if (p)
     402               0 :     p->AdjustPriority(delta);
     403                 : }
     404                 : 
     405              16 : void imgRequest::SetIsInCache(bool incache)
     406                 : {
     407              16 :   LOG_FUNC_WITH_PARAM(gImgLog, "imgRequest::SetIsCacheable", "incache", incache);
     408              16 :   mIsInCache = incache;
     409              16 : }
     410                 : 
     411               6 : void imgRequest::UpdateCacheEntrySize()
     412                 : {
     413               6 :   if (mCacheEntry) {
     414               6 :     mCacheEntry->SetDataSize(mImage->SizeOfData());
     415                 : 
     416                 : #ifdef DEBUG_joe
     417                 :     nsCAutoString url;
     418                 :     mURI->GetSpec(url);
     419                 :     printf("CACHEPUT: %d %s %d\n", time(NULL), url.get(), imageSize);
     420                 : #endif
     421                 :   }
     422               6 : }
     423                 : 
     424               8 : void imgRequest::SetCacheValidation(imgCacheEntry* aCacheEntry, nsIRequest* aRequest)
     425                 : {
     426                 :   /* get the expires info */
     427               8 :   if (aCacheEntry) {
     428              16 :     nsCOMPtr<nsICachingChannel> cacheChannel(do_QueryInterface(aRequest));
     429               8 :     if (cacheChannel) {
     430               8 :       nsCOMPtr<nsISupports> cacheToken;
     431               4 :       cacheChannel->GetCacheToken(getter_AddRefs(cacheToken));
     432               4 :       if (cacheToken) {
     433               8 :         nsCOMPtr<nsICacheEntryInfo> entryDesc(do_QueryInterface(cacheToken));
     434               4 :         if (entryDesc) {
     435                 :           PRUint32 expiration;
     436                 :           /* get the expiration time from the caching channel's token */
     437               4 :           entryDesc->GetExpirationTime(&expiration);
     438                 : 
     439                 :           // Expiration time defaults to 0. We set the expiration time on our
     440                 :           // entry if it hasn't been set yet.
     441               4 :           if (aCacheEntry->GetExpiryTime() == 0)
     442               4 :             aCacheEntry->SetExpiryTime(expiration);
     443                 :         }
     444                 :       }
     445                 :     }
     446                 : 
     447                 :     // Determine whether the cache entry must be revalidated when we try to use it.
     448                 :     // Currently, only HTTP specifies this information...
     449              16 :     nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequest));
     450               8 :     if (httpChannel) {
     451               4 :       bool bMustRevalidate = false;
     452                 : 
     453               4 :       httpChannel->IsNoStoreResponse(&bMustRevalidate);
     454                 : 
     455               4 :       if (!bMustRevalidate) {
     456               4 :         httpChannel->IsNoCacheResponse(&bMustRevalidate);
     457                 :       }
     458                 : 
     459               4 :       if (!bMustRevalidate) {
     460               8 :         nsCAutoString cacheHeader;
     461                 : 
     462               8 :         httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("Cache-Control"),
     463               4 :                                             cacheHeader);
     464               4 :         if (PL_strcasestr(cacheHeader.get(), "must-revalidate")) {
     465               0 :           bMustRevalidate = true;
     466                 :         }
     467                 :       }
     468                 : 
     469                 :       // Cache entries default to not needing to validate. We ensure that
     470                 :       // multiple calls to this function don't override an earlier decision to
     471                 :       // validate by making validation a one-way decision.
     472               4 :       if (bMustRevalidate)
     473               0 :         aCacheEntry->SetMustValidate(bMustRevalidate);
     474                 :     }
     475                 : 
     476                 :     // We always need to validate file URIs.
     477              16 :     nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
     478               8 :     if (channel) {
     479              16 :       nsCOMPtr<nsIURI> uri;
     480               8 :       channel->GetURI(getter_AddRefs(uri));
     481               8 :       bool isfile = false;
     482               8 :       uri->SchemeIs("file", &isfile);
     483               8 :       if (isfile)
     484               0 :         aCacheEntry->SetMustValidate(isfile);
     485                 :     }
     486                 :   }
     487               8 : }
     488                 : 
     489                 : nsresult
     490               0 : imgRequest::LockImage()
     491                 : {
     492               0 :   return mImage->LockImage();
     493                 : }
     494                 : 
     495                 : nsresult
     496               0 : imgRequest::UnlockImage()
     497                 : {
     498               0 :   return mImage->UnlockImage();
     499                 : }
     500                 : 
     501                 : nsresult
     502               0 : imgRequest::RequestDecode()
     503                 : {
     504                 :   // If we've initialized our image, we can request a decode.
     505               0 :   if (mImage) {
     506               0 :     return mImage->RequestDecode();
     507                 :   }
     508                 : 
     509                 :   // Otherwise, flag to do it when we get the image
     510               0 :   mDecodeRequested = true;
     511                 : 
     512               0 :   return NS_OK;
     513                 : }
     514                 : 
     515                 : /** imgIContainerObserver methods **/
     516                 : 
     517                 : /* [noscript] void frameChanged (in imgIRequest request,
     518                 :                                  in imgIContainer container,
     519                 :                                  in nsIntRect dirtyRect); */
     520               0 : NS_IMETHODIMP imgRequest::FrameChanged(imgIRequest *request,
     521                 :                                        imgIContainer *container,
     522                 :                                        const nsIntRect *dirtyRect)
     523                 : {
     524               0 :   LOG_SCOPE(gImgLog, "imgRequest::FrameChanged");
     525               0 :   NS_ABORT_IF_FALSE(mImage,
     526                 :                     "FrameChanged callback before we've created our image");
     527                 : 
     528               0 :   mImage->GetStatusTracker().RecordFrameChanged(container, dirtyRect);
     529                 : 
     530               0 :   nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
     531               0 :   while (iter.HasMore()) {
     532               0 :     mImage->GetStatusTracker().SendFrameChanged(iter.GetNext(), container, dirtyRect);
     533                 :   }
     534                 : 
     535               0 :   return NS_OK;
     536                 : }
     537                 : 
     538                 : /** imgIDecoderObserver methods **/
     539                 : 
     540                 : /* void onStartDecode (in imgIRequest request); */
     541               2 : NS_IMETHODIMP imgRequest::OnStartDecode(imgIRequest *request)
     542                 : {
     543               4 :   LOG_SCOPE(gImgLog, "imgRequest::OnStartDecode");
     544               2 :   NS_ABORT_IF_FALSE(mImage,
     545                 :                     "OnStartDecode callback before we've created our image");
     546                 : 
     547                 : 
     548               2 :   mImage->GetStatusTracker().RecordStartDecode();
     549                 : 
     550               4 :   nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
     551              14 :   while (iter.HasMore()) {
     552              10 :     mImage->GetStatusTracker().SendStartDecode(iter.GetNext());
     553                 :   }
     554                 : 
     555                 :   /* In the case of streaming jpegs, it is possible to get multiple OnStartDecodes which
     556                 :      indicates the beginning of a new decode.
     557                 :      The cache entry's size therefore needs to be reset to 0 here.  If we do not do this,
     558                 :      the code in imgRequest::OnStopFrame will continue to increase the data size cumulatively.
     559                 :    */
     560               2 :   if (mCacheEntry)
     561               2 :     mCacheEntry->SetDataSize(0);
     562                 : 
     563               2 :   return NS_OK;
     564                 : }
     565                 : 
     566               0 : NS_IMETHODIMP imgRequest::OnStartRequest(imgIRequest *aRequest)
     567                 : {
     568               0 :   NS_NOTREACHED("imgRequest(imgIDecoderObserver)::OnStartRequest");
     569               0 :   return NS_OK;
     570                 : }
     571                 : 
     572                 : /* void onStartContainer (in imgIRequest request, in imgIContainer image); */
     573               6 : NS_IMETHODIMP imgRequest::OnStartContainer(imgIRequest *request, imgIContainer *image)
     574                 : {
     575              12 :   LOG_SCOPE(gImgLog, "imgRequest::OnStartContainer");
     576                 : 
     577               6 :   NS_ASSERTION(image, "imgRequest::OnStartContainer called with a null image!");
     578               6 :   if (!image) return NS_ERROR_UNEXPECTED;
     579                 : 
     580               6 :   NS_ABORT_IF_FALSE(mImage,
     581                 :                     "OnStartContainer callback before we've created our image");
     582               6 :   NS_ABORT_IF_FALSE(image == mImage,
     583                 :                     "OnStartContainer callback from an image we don't own");
     584               6 :   mImage->GetStatusTracker().RecordStartContainer(image);
     585                 : 
     586              12 :   nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
     587              32 :   while (iter.HasMore()) {
     588              20 :     mImage->GetStatusTracker().SendStartContainer(iter.GetNext(), image);
     589                 :   }
     590                 : 
     591               6 :   return NS_OK;
     592                 : }
     593                 : 
     594                 : /* void onStartFrame (in imgIRequest request, in unsigned long frame); */
     595               4 : NS_IMETHODIMP imgRequest::OnStartFrame(imgIRequest *request,
     596                 :                                        PRUint32 frame)
     597                 : {
     598               8 :   LOG_SCOPE(gImgLog, "imgRequest::OnStartFrame");
     599               4 :   NS_ABORT_IF_FALSE(mImage,
     600                 :                     "OnStartFrame callback before we've created our image");
     601                 : 
     602               4 :   mImage->GetStatusTracker().RecordStartFrame(frame);
     603                 : 
     604               8 :   nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
     605              28 :   while (iter.HasMore()) {
     606              20 :     mImage->GetStatusTracker().SendStartFrame(iter.GetNext(), frame);
     607                 :   }
     608                 : 
     609               4 :   return NS_OK;
     610                 : }
     611                 : 
     612                 : /* [noscript] void onDataAvailable (in imgIRequest request, in boolean aCurrentFrame, [const] in nsIntRect rect); */
     613               2 : NS_IMETHODIMP imgRequest::OnDataAvailable(imgIRequest *request,
     614                 :                                           bool aCurrentFrame,
     615                 :                                           const nsIntRect * rect)
     616                 : {
     617               4 :   LOG_SCOPE(gImgLog, "imgRequest::OnDataAvailable");
     618               2 :   NS_ABORT_IF_FALSE(mImage,
     619                 :                     "OnDataAvailable callback before we've created our image");
     620                 : 
     621               2 :   mImage->GetStatusTracker().RecordDataAvailable(aCurrentFrame, rect);
     622                 : 
     623               4 :   nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
     624              14 :   while (iter.HasMore()) {
     625              10 :     mImage->GetStatusTracker().SendDataAvailable(iter.GetNext(), aCurrentFrame, rect);
     626                 :   }
     627                 : 
     628               2 :   return NS_OK;
     629                 : }
     630                 : 
     631                 : /* void onStopFrame (in imgIRequest request, in unsigned long frame); */
     632               4 : NS_IMETHODIMP imgRequest::OnStopFrame(imgIRequest *request,
     633                 :                                       PRUint32 frame)
     634                 : {
     635               8 :   LOG_SCOPE(gImgLog, "imgRequest::OnStopFrame");
     636               4 :   NS_ABORT_IF_FALSE(mImage,
     637                 :                     "OnStopFrame callback before we've created our image");
     638                 : 
     639               4 :   mImage->GetStatusTracker().RecordStopFrame(frame);
     640                 : 
     641               8 :   nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
     642              28 :   while (iter.HasMore()) {
     643              20 :     mImage->GetStatusTracker().SendStopFrame(iter.GetNext(), frame);
     644                 :   }
     645                 : 
     646               4 :   return NS_OK;
     647                 : }
     648                 : 
     649                 : /* void onStopContainer (in imgIRequest request, in imgIContainer image); */
     650               2 : NS_IMETHODIMP imgRequest::OnStopContainer(imgIRequest *request,
     651                 :                                           imgIContainer *image)
     652                 : {
     653               4 :   LOG_SCOPE(gImgLog, "imgRequest::OnStopContainer");
     654               2 :   NS_ABORT_IF_FALSE(mImage,
     655                 :                     "OnDataContainer callback before we've created our image");
     656                 : 
     657               2 :   mImage->GetStatusTracker().RecordStopContainer(image);
     658                 : 
     659               4 :   nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
     660              14 :   while (iter.HasMore()) {
     661              10 :     mImage->GetStatusTracker().SendStopContainer(iter.GetNext(), image);
     662                 :   }
     663                 : 
     664               2 :   return NS_OK;
     665                 : }
     666                 : 
     667                 : /* void onStopDecode (in imgIRequest request, in nsresult status, in wstring statusArg); */
     668               2 : NS_IMETHODIMP imgRequest::OnStopDecode(imgIRequest *aRequest,
     669                 :                                        nsresult aStatus,
     670                 :                                        const PRUnichar *aStatusArg)
     671                 : {
     672               4 :   LOG_SCOPE(gImgLog, "imgRequest::OnStopDecode");
     673               2 :   NS_ABORT_IF_FALSE(mImage,
     674                 :                     "OnDataDecode callback before we've created our image");
     675                 : 
     676                 :   // We finished the decode, and thus have the decoded frames. Update the cache
     677                 :   // entry size to take this into account.
     678               2 :   UpdateCacheEntrySize();
     679                 : 
     680               2 :   mImage->GetStatusTracker().RecordStopDecode(aStatus, aStatusArg);
     681                 : 
     682               4 :   nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
     683              14 :   while (iter.HasMore()) {
     684              20 :     mImage->GetStatusTracker().SendStopDecode(iter.GetNext(), aStatus,
     685              20 :                                               aStatusArg);
     686                 :   }
     687                 : 
     688                 :   // RasterImage and everything below it is completely correct and
     689                 :   // bulletproof about its handling of decoder notifications.
     690                 :   // Unfortunately, here and above we have to make some gross and
     691                 :   // inappropriate use of things to get things to work without
     692                 :   // completely overhauling the decoder observer interface (this will,
     693                 :   // thankfully, happen in bug 505385). From imgRequest and above (for
     694                 :   // the time being), OnStopDecode is just a companion to OnStopRequest
     695                 :   // that signals success or failure of the _load_ (not the _decode_).
     696                 :   // Within imgStatusTracker, we ignore OnStopDecode notifications from the
     697                 :   // decoder and RasterImage and generate our own every time we send
     698                 :   // OnStopRequest. From within SendStopDecode, we actually send
     699                 :   // OnStopContainer.  For more information, see bug 435296.
     700                 : 
     701               2 :   return NS_OK;
     702                 : }
     703                 : 
     704               0 : NS_IMETHODIMP imgRequest::OnStopRequest(imgIRequest *aRequest,
     705                 :                                         bool aLastPart)
     706                 : {
     707               0 :   NS_NOTREACHED("imgRequest(imgIDecoderObserver)::OnStopRequest");
     708               0 :   return NS_OK;
     709                 : }
     710                 : 
     711                 : /* void onDiscard (in imgIRequest request); */
     712               0 : NS_IMETHODIMP imgRequest::OnDiscard(imgIRequest *aRequest)
     713                 : {
     714               0 :   NS_ABORT_IF_FALSE(mImage,
     715                 :                     "OnDiscard callback before we've created our image");
     716                 : 
     717               0 :   mImage->GetStatusTracker().RecordDiscard();
     718                 : 
     719                 :   // Update the cache entry size, since we just got rid of frame data
     720               0 :   UpdateCacheEntrySize();
     721                 : 
     722               0 :   nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
     723               0 :   while (iter.HasMore()) {
     724               0 :     mImage->GetStatusTracker().SendDiscard(iter.GetNext());
     725                 :   }
     726                 : 
     727               0 :   return NS_OK;
     728                 : }
     729                 : 
     730               1 : NS_IMETHODIMP imgRequest::OnImageIsAnimated(imgIRequest *aRequest)
     731                 : {
     732               1 :   NS_ABORT_IF_FALSE(mImage,
     733                 :                     "OnImageIsAnimated callback before we've created our image");
     734               1 :   mImage->GetStatusTracker().RecordImageIsAnimated();
     735                 : 
     736               2 :   nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
     737               7 :   while (iter.HasMore()) {
     738               5 :     mImage->GetStatusTracker().SendImageIsAnimated(iter.GetNext());
     739                 :   }
     740                 : 
     741               1 :   return NS_OK;
     742                 : }
     743                 : 
     744                 : /** nsIRequestObserver methods **/
     745                 : 
     746                 : /* void onStartRequest (in nsIRequest request, in nsISupports ctxt); */
     747               8 : NS_IMETHODIMP imgRequest::OnStartRequest(nsIRequest *aRequest, nsISupports *ctxt)
     748                 : {
     749              16 :   LOG_SCOPE(gImgLog, "imgRequest::OnStartRequest");
     750                 : 
     751                 :   // Figure out if we're multipart
     752              16 :   nsCOMPtr<nsIMultiPartChannel> mpchan(do_QueryInterface(aRequest));
     753               8 :   if (mpchan)
     754               0 :       mIsMultiPartChannel = true;
     755                 : 
     756                 :   // If we're not multipart, we shouldn't have an image yet
     757               8 :   NS_ABORT_IF_FALSE(mIsMultiPartChannel || !mImage,
     758                 :                     "Already have an image for non-multipart request");
     759                 : 
     760                 :   // If we're multipart, and our image is initialized, fix things up for another round
     761               8 :   if (mIsMultiPartChannel && mImage) {
     762               0 :     if (mImage->GetType() == imgIContainer::TYPE_RASTER) {
     763                 :       // Inform the RasterImage that we have new source data
     764               0 :       static_cast<RasterImage*>(mImage.get())->NewSourceData();
     765                 :     } else {  // imageType == imgIContainer::TYPE_VECTOR
     766               0 :       nsCOMPtr<nsIStreamListener> imageAsStream = do_QueryInterface(mImage);
     767               0 :       NS_ABORT_IF_FALSE(imageAsStream,
     768                 :                         "SVG-typed Image failed QI to nsIStreamListener");
     769               0 :       imageAsStream->OnStartRequest(aRequest, ctxt);
     770                 :     }
     771                 :   }
     772                 : 
     773                 :   /*
     774                 :    * If mRequest is null here, then we need to set it so that we'll be able to
     775                 :    * cancel it if our Cancel() method is called.  Note that this can only
     776                 :    * happen for multipart channels.  We could simply not null out mRequest for
     777                 :    * non-last parts, if GetIsLastPart() were reliable, but it's not.  See
     778                 :    * https://bugzilla.mozilla.org/show_bug.cgi?id=339610
     779                 :    */
     780               8 :   if (!mRequest) {
     781               0 :     NS_ASSERTION(mpchan,
     782                 :                  "We should have an mRequest here unless we're multipart");
     783               0 :     nsCOMPtr<nsIChannel> chan;
     784               0 :     mpchan->GetBaseChannel(getter_AddRefs(chan));
     785               0 :     mRequest = chan;
     786                 :   }
     787                 : 
     788               8 :   imgStatusTracker& statusTracker = GetStatusTracker();
     789               8 :   statusTracker.RecordStartRequest();
     790                 : 
     791              16 :   nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
     792               8 :   if (channel)
     793               8 :     channel->GetSecurityInfo(getter_AddRefs(mSecurityInfo));
     794                 : 
     795              16 :   nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
     796              35 :   while (iter.HasMore()) {
     797              19 :     statusTracker.SendStartRequest(iter.GetNext());
     798                 :   }
     799                 : 
     800                 :   /* Get our principal */
     801              16 :   nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
     802               8 :   if (chan) {
     803                 :     nsCOMPtr<nsIScriptSecurityManager> secMan =
     804              16 :       do_GetService("@mozilla.org/scriptsecuritymanager;1");
     805               8 :     if (secMan) {
     806               8 :       nsresult rv = secMan->GetChannelPrincipal(chan,
     807               8 :                                                 getter_AddRefs(mPrincipal));
     808               8 :       if (NS_FAILED(rv)) {
     809               0 :         return rv;
     810                 :       }
     811                 : 
     812                 :       // Tell all of our proxies that we have a principal.
     813              16 :       nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
     814              35 :       while (iter.HasMore()) {
     815              19 :         iter.GetNext()->SetPrincipal(mPrincipal);
     816                 :       }
     817                 :     }
     818                 :   }
     819                 : 
     820               8 :   SetCacheValidation(mCacheEntry, aRequest);
     821                 : 
     822                 :   // Shouldn't we be dead already if this gets hit?  Probably multipart/x-mixed-replace...
     823               8 :   if (mObservers.IsEmpty()) {
     824               0 :     this->Cancel(NS_IMAGELIB_ERROR_FAILURE);
     825                 :   }
     826                 : 
     827               8 :   return NS_OK;
     828                 : }
     829                 : 
     830                 : /* void onStopRequest (in nsIRequest request, in nsISupports ctxt, in nsresult status); */
     831               8 : NS_IMETHODIMP imgRequest::OnStopRequest(nsIRequest *aRequest, nsISupports *ctxt, nsresult status)
     832                 : {
     833               8 :   LOG_FUNC(gImgLog, "imgRequest::OnStopRequest");
     834                 : 
     835               8 :   bool lastPart = true;
     836              16 :   nsCOMPtr<nsIMultiPartChannel> mpchan(do_QueryInterface(aRequest));
     837               8 :   if (mpchan)
     838               0 :     mpchan->GetIsLastPart(&lastPart);
     839                 : 
     840                 :   // XXXldb What if this is a non-last part of a multipart request?
     841                 :   // xxx before we release our reference to mRequest, lets
     842                 :   // save the last status that we saw so that the
     843                 :   // imgRequestProxy will have access to it.
     844               8 :   if (mRequest) {
     845               8 :     mRequest = nsnull;  // we no longer need the request
     846                 :   }
     847                 : 
     848                 :   // stop holding a ref to the channel, since we don't need it anymore
     849               8 :   if (mChannel) {
     850               8 :     mChannel->SetNotificationCallbacks(mPrevChannelSink);
     851               8 :     mPrevChannelSink = nsnull;
     852               8 :     mChannel = nsnull;
     853                 :   }
     854                 : 
     855                 :   // Tell the image that it has all of the source data. Note that this can
     856                 :   // trigger a failure, since the image might be waiting for more non-optional
     857                 :   // data and this is the point where we break the news that it's not coming.
     858               8 :   if (mImage) {
     859                 :     nsresult rv;
     860               8 :     if (mImage->GetType() == imgIContainer::TYPE_RASTER) {
     861                 :       // Notify the image
     862               8 :       rv = static_cast<RasterImage*>(mImage.get())->SourceDataComplete();
     863                 :     } else { // imageType == imgIContainer::TYPE_VECTOR
     864               0 :       nsCOMPtr<nsIStreamListener> imageAsStream = do_QueryInterface(mImage);
     865               0 :       NS_ABORT_IF_FALSE(imageAsStream,
     866                 :                         "SVG-typed Image failed QI to nsIStreamListener");
     867               0 :       rv = imageAsStream->OnStopRequest(aRequest, ctxt, status);
     868                 :     }
     869                 : 
     870                 :     // If we got an error in the SourceDataComplete() / OnStopRequest() call,
     871                 :     // we don't want to proceed as if nothing bad happened. However, we also
     872                 :     // want to give precedence to failure status codes from necko, since
     873                 :     // presumably they're more meaningful.
     874               8 :     if (NS_FAILED(rv) && NS_SUCCEEDED(status))
     875               0 :       status = rv;
     876                 :   }
     877                 : 
     878               8 :   imgStatusTracker& statusTracker = GetStatusTracker();
     879               8 :   statusTracker.RecordStopRequest(lastPart, status);
     880                 : 
     881                 :   // If the request went through, update the cache entry size. Otherwise,
     882                 :   // cancel the request, which removes us from the cache.
     883               8 :   if (mImage && NS_SUCCEEDED(status)) {
     884                 :     // We update the cache entry size here because this is where we finish
     885                 :     // loading compressed source data, which is part of our size calculus.
     886               4 :     UpdateCacheEntrySize();
     887                 :   }
     888                 :   else {
     889                 :     // stops animations, removes from cache
     890               4 :     this->Cancel(status);
     891                 :   }
     892                 : 
     893                 :   /* notify the kids */
     894              16 :   nsTObserverArray<imgRequestProxy*>::ForwardIterator srIter(mObservers);
     895              39 :   while (srIter.HasMore()) {
     896              23 :     statusTracker.SendStopRequest(srIter.GetNext(), lastPart, status);
     897                 :   }
     898                 : 
     899               8 :   mTimedChannel = nsnull;
     900               8 :   return NS_OK;
     901                 : }
     902                 : 
     903                 : /* prototype for these defined below */
     904                 : static NS_METHOD sniff_mimetype_callback(nsIInputStream* in, void* closure, const char* fromRawSegment,
     905                 :                                          PRUint32 toOffset, PRUint32 count, PRUint32 *writeCount);
     906                 : 
     907                 : /** nsIStreamListener methods **/
     908                 : 
     909                 : /* void onDataAvailable (in nsIRequest request, in nsISupports ctxt, in nsIInputStream inStr, in unsigned long sourceOffset, in unsigned long count); */
     910               8 : NS_IMETHODIMP imgRequest::OnDataAvailable(nsIRequest *aRequest, nsISupports *ctxt, nsIInputStream *inStr, PRUint32 sourceOffset, PRUint32 count)
     911                 : {
     912              16 :   LOG_SCOPE_WITH_PARAM(gImgLog, "imgRequest::OnDataAvailable", "count", count);
     913                 : 
     914               8 :   NS_ASSERTION(aRequest, "imgRequest::OnDataAvailable -- no request!");
     915                 : 
     916                 :   nsresult rv;
     917                 : 
     918                 :   PRUint16 imageType;
     919               8 :   if (mGotData) {
     920               0 :     imageType = mImage->GetType();
     921                 :   } else {
     922              16 :     LOG_SCOPE(gImgLog, "imgRequest::OnDataAvailable |First time through... finding mimetype|");
     923                 : 
     924               8 :     mGotData = true;
     925                 : 
     926                 :     /* look at the first few bytes and see if we can tell what the data is from that
     927                 :      * since servers tend to lie. :(
     928                 :      */
     929                 :     PRUint32 out;
     930               8 :     inStr->ReadSegments(sniff_mimetype_callback, this, count, &out);
     931                 : 
     932                 : #ifdef NS_DEBUG
     933                 :     /* NS_WARNING if the content type from the channel isn't the same if the sniffing */
     934                 : #endif
     935                 : 
     936              16 :     nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
     937               8 :     if (mContentType.IsEmpty()) {
     938               8 :       LOG_SCOPE(gImgLog, "imgRequest::OnDataAvailable |sniffing of mimetype failed|");
     939                 : 
     940               4 :       rv = NS_ERROR_FAILURE;
     941               4 :       if (chan) {
     942               4 :         rv = chan->GetContentType(mContentType);
     943                 :       }
     944                 : 
     945               4 :       if (NS_FAILED(rv)) {
     946               0 :         PR_LOG(gImgLog, PR_LOG_ERROR,
     947                 :                ("[this=%p] imgRequest::OnDataAvailable -- Content type unavailable from the channel\n",
     948                 :                 this));
     949                 : 
     950               0 :         this->Cancel(NS_IMAGELIB_ERROR_FAILURE);
     951                 : 
     952               0 :         return NS_BINDING_ABORTED;
     953                 :       }
     954                 : 
     955               8 :       LOG_MSG(gImgLog, "imgRequest::OnDataAvailable", "Got content type from the channel");
     956                 :     }
     957                 : 
     958                 :     /* now we have mimetype, so we can infer the image type that we want */
     959               8 :     if (mContentType.EqualsLiteral(SVG_MIMETYPE)) {
     960               0 :       mImage = new VectorImage(mStatusTracker.forget());
     961                 :     } else {
     962              16 :       mImage = new RasterImage(mStatusTracker.forget());
     963                 :     }
     964               8 :     mImage->SetInnerWindowID(mInnerWindowId);
     965               8 :     imageType = mImage->GetType();
     966                 : 
     967                 :     // Notify any imgRequestProxys that are observing us that we have an Image.
     968              16 :     nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
     969              35 :     while (iter.HasMore()) {
     970              19 :       iter.GetNext()->SetImage(mImage);
     971                 :     }
     972                 : 
     973                 :     /* set our mimetype as a property */
     974              16 :     nsCOMPtr<nsISupportsCString> contentType(do_CreateInstance("@mozilla.org/supports-cstring;1"));
     975               8 :     if (contentType) {
     976               8 :       contentType->SetData(mContentType);
     977               8 :       mProperties->Set("type", contentType);
     978                 :     }
     979                 : 
     980                 :     /* set our content disposition as a property */
     981              16 :     nsCAutoString disposition;
     982               8 :     if (chan) {
     983               8 :       chan->GetContentDispositionHeader(disposition);
     984                 :     }
     985               8 :     if (!disposition.IsEmpty()) {
     986               0 :       nsCOMPtr<nsISupportsCString> contentDisposition(do_CreateInstance("@mozilla.org/supports-cstring;1"));
     987               0 :       if (contentDisposition) {
     988               0 :         contentDisposition->SetData(disposition);
     989               0 :         mProperties->Set("content-disposition", contentDisposition);
     990                 :       }
     991                 :     }
     992                 : 
     993               8 :     LOG_MSG_WITH_PARAM(gImgLog, "imgRequest::OnDataAvailable", "content type", mContentType.get());
     994                 : 
     995                 :     //
     996                 :     // Figure out our Image initialization flags
     997                 :     //
     998                 : 
     999                 :     // We default to the static globals
    1000               8 :     bool isDiscardable = gDiscardable;
    1001               8 :     bool doDecodeOnDraw = gDecodeOnDraw;
    1002                 : 
    1003                 :     // We want UI to be as snappy as possible and not to flicker. Disable discarding
    1004                 :     // and decode-on-draw for chrome URLS
    1005               8 :     bool isChrome = false;
    1006               8 :     rv = mURI->SchemeIs("chrome", &isChrome);
    1007               8 :     if (NS_SUCCEEDED(rv) && isChrome)
    1008               0 :       isDiscardable = doDecodeOnDraw = false;
    1009                 : 
    1010                 :     // We don't want resources like the "loading" icon to be discardable or
    1011                 :     // decode-on-draw either.
    1012               8 :     bool isResource = false;
    1013               8 :     rv = mURI->SchemeIs("resource", &isResource);
    1014               8 :     if (NS_SUCCEEDED(rv) && isResource)
    1015               0 :       isDiscardable = doDecodeOnDraw = false;
    1016                 : 
    1017                 :     // For multipart/x-mixed-replace, we basically want a direct channel to the
    1018                 :     // decoder. Disable both for this case as well.
    1019               8 :     if (mIsMultiPartChannel)
    1020               0 :       isDiscardable = doDecodeOnDraw = false;
    1021                 : 
    1022                 :     // We have all the information we need
    1023               8 :     PRUint32 imageFlags = Image::INIT_FLAG_NONE;
    1024               8 :     if (isDiscardable)
    1025               8 :       imageFlags |= Image::INIT_FLAG_DISCARDABLE;
    1026               8 :     if (doDecodeOnDraw)
    1027               8 :       imageFlags |= Image::INIT_FLAG_DECODE_ON_DRAW;
    1028               8 :     if (mIsMultiPartChannel)
    1029               0 :       imageFlags |= Image::INIT_FLAG_MULTIPART;
    1030                 : 
    1031                 :     // Get our URI string
    1032              16 :     nsCAutoString uriString;
    1033               8 :     rv = mURI->GetSpec(uriString);
    1034               8 :     if (NS_FAILED(rv))
    1035               0 :       uriString.Assign("<unknown image URI>");
    1036                 : 
    1037                 :     // Initialize the image that we created above. For RasterImages, this
    1038                 :     // instantiates a decoder behind the scenes, so if we don't have a decoder
    1039                 :     // for this mimetype we'll find out about it here.
    1040               8 :     rv = mImage->Init(this, mContentType.get(), uriString.get(), imageFlags);
    1041               8 :     if (NS_FAILED(rv)) { // Probably bad mimetype
    1042                 : 
    1043               4 :       this->Cancel(rv);
    1044               4 :       return NS_BINDING_ABORTED;
    1045                 :     }
    1046                 : 
    1047               4 :     if (imageType == imgIContainer::TYPE_RASTER) {
    1048                 :       /* Use content-length as a size hint for http channels. */
    1049               8 :       nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequest));
    1050               4 :       if (httpChannel) {
    1051               0 :         nsCAutoString contentLength;
    1052               0 :         rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("content-length"),
    1053               0 :                                             contentLength);
    1054               0 :         if (NS_SUCCEEDED(rv)) {
    1055               0 :           PRInt32 len = contentLength.ToInteger(&rv);
    1056                 : 
    1057                 :           // Pass anything usable on so that the RasterImage can preallocate
    1058                 :           // its source buffer
    1059               0 :           if (len > 0) {
    1060               0 :             PRUint32 sizeHint = (PRUint32) len;
    1061               0 :             sizeHint = NS_MIN<PRUint32>(sizeHint, 20000000); /* Bound by something reasonable */
    1062               0 :             RasterImage* rasterImage = static_cast<RasterImage*>(mImage.get());
    1063               0 :             rv = rasterImage->SetSourceSizeHint(sizeHint);
    1064               0 :             if (NS_FAILED(rv)) {
    1065                 :               // Flush memory, try to get some back, and try again
    1066               0 :               rv = nsMemory::HeapMinimize(true);
    1067               0 :               rv |= rasterImage->SetSourceSizeHint(sizeHint);
    1068                 :               // If we've still failed at this point, things are going downhill
    1069               0 :               if (NS_FAILED(rv)) {
    1070               0 :                 NS_WARNING("About to hit OOM in imagelib!");
    1071                 :               }
    1072                 :             }
    1073                 :           }
    1074                 :         }
    1075                 :       }
    1076                 :     }
    1077                 : 
    1078               4 :     if (imageType == imgIContainer::TYPE_RASTER) {
    1079                 :       // If we were waiting on the image to do something, now's our chance.
    1080               4 :       if (mDecodeRequested) {
    1081               0 :         mImage->RequestDecode();
    1082                 :       }
    1083                 :     } else { // imageType == imgIContainer::TYPE_VECTOR
    1084               0 :       nsCOMPtr<nsIStreamListener> imageAsStream = do_QueryInterface(mImage);
    1085               0 :       NS_ABORT_IF_FALSE(imageAsStream,
    1086                 :                         "SVG-typed Image failed QI to nsIStreamListener");
    1087               0 :       imageAsStream->OnStartRequest(aRequest, nsnull);
    1088                 :     }
    1089                 :   }
    1090                 : 
    1091               4 :   if (imageType == imgIContainer::TYPE_RASTER) {
    1092                 :     // WriteToRasterImage always consumes everything it gets
    1093                 :     // if it doesn't run out of memory
    1094                 :     PRUint32 bytesRead;
    1095                 :     rv = inStr->ReadSegments(RasterImage::WriteToRasterImage,
    1096               4 :                              static_cast<void*>(mImage),
    1097               8 :                              count, &bytesRead);
    1098               4 :     NS_ABORT_IF_FALSE(bytesRead == count || mImage->HasError(),
    1099                 :   "WriteToRasterImage should consume everything or the image must be in error!");
    1100                 :   } else { // imageType == imgIContainer::TYPE_VECTOR
    1101               0 :     nsCOMPtr<nsIStreamListener> imageAsStream = do_QueryInterface(mImage);
    1102               0 :     rv = imageAsStream->OnDataAvailable(aRequest, ctxt, inStr,
    1103               0 :                                         sourceOffset, count);
    1104                 :   }
    1105               4 :   if (NS_FAILED(rv)) {
    1106               0 :     PR_LOG(gImgLog, PR_LOG_WARNING,
    1107                 :            ("[this=%p] imgRequest::OnDataAvailable -- "
    1108                 :             "copy to RasterImage failed\n", this));
    1109               0 :     this->Cancel(NS_IMAGELIB_ERROR_FAILURE);
    1110               0 :     return NS_BINDING_ABORTED;
    1111                 :   }
    1112                 : 
    1113               4 :   return NS_OK;
    1114                 : }
    1115                 : 
    1116               8 : static NS_METHOD sniff_mimetype_callback(nsIInputStream* in,
    1117                 :                                          void* closure,
    1118                 :                                          const char* fromRawSegment,
    1119                 :                                          PRUint32 toOffset,
    1120                 :                                          PRUint32 count,
    1121                 :                                          PRUint32 *writeCount)
    1122                 : {
    1123               8 :   imgRequest *request = static_cast<imgRequest*>(closure);
    1124                 : 
    1125               8 :   NS_ASSERTION(request, "request is null!");
    1126                 : 
    1127               8 :   if (count > 0)
    1128               8 :     request->SniffMimeType(fromRawSegment, count);
    1129                 : 
    1130               8 :   *writeCount = 0;
    1131               8 :   return NS_ERROR_FAILURE;
    1132                 : }
    1133                 : 
    1134                 : void
    1135               8 : imgRequest::SniffMimeType(const char *buf, PRUint32 len)
    1136                 : {
    1137               8 :   imgLoader::GetMimeTypeFromContent(buf, len, mContentType);
    1138                 : 
    1139                 :   // The vast majority of the time, imgLoader will find a gif/jpeg/png image
    1140                 :   // and fill mContentType with the sniffed MIME type.
    1141               8 :   if (!mContentType.IsEmpty())
    1142               4 :     return;
    1143                 : 
    1144                 :   // When our sniffing fails, we want to query registered image decoders
    1145                 :   // to see if they can identify the image. If we always trusted the server
    1146                 :   // to send the right MIME, images sent as text/plain would not be rendered.
    1147               4 :   const nsCOMArray<nsIContentSniffer>& sniffers = mImageSniffers.GetEntries();
    1148               4 :   PRUint32 length = sniffers.Count();
    1149               4 :   for (PRUint32 i = 0; i < length; ++i) {
    1150                 :     nsresult rv =
    1151               0 :       sniffers[i]->GetMIMETypeFromContent(nsnull, (const PRUint8 *) buf, len, mContentType);
    1152               0 :     if (NS_SUCCEEDED(rv) && !mContentType.IsEmpty()) {
    1153               0 :       return;
    1154                 :     }
    1155                 :   }
    1156                 : }
    1157                 : 
    1158                 : 
    1159                 : /** nsIInterfaceRequestor methods **/
    1160                 : 
    1161                 : NS_IMETHODIMP
    1162              16 : imgRequest::GetInterface(const nsIID & aIID, void **aResult)
    1163                 : {
    1164              16 :   if (!mPrevChannelSink || aIID.Equals(NS_GET_IID(nsIChannelEventSink)))
    1165              16 :     return QueryInterface(aIID, aResult);
    1166                 : 
    1167               0 :   NS_ASSERTION(mPrevChannelSink != this, 
    1168                 :                "Infinite recursion - don't keep track of channel sinks that are us!");
    1169               0 :   return mPrevChannelSink->GetInterface(aIID, aResult);
    1170                 : }
    1171                 : 
    1172                 : /** nsIChannelEventSink methods **/
    1173                 : NS_IMETHODIMP
    1174               0 : imgRequest::AsyncOnChannelRedirect(nsIChannel *oldChannel,
    1175                 :                                    nsIChannel *newChannel, PRUint32 flags,
    1176                 :                                    nsIAsyncVerifyRedirectCallback *callback)
    1177                 : {
    1178               0 :   NS_ASSERTION(mRequest && mChannel, "Got a channel redirect after we nulled out mRequest!");
    1179               0 :   NS_ASSERTION(mChannel == oldChannel, "Got a channel redirect for an unknown channel!");
    1180               0 :   NS_ASSERTION(newChannel, "Got a redirect to a NULL channel!");
    1181                 : 
    1182               0 :   SetCacheValidation(mCacheEntry, oldChannel);
    1183                 : 
    1184                 :   // Prepare for callback
    1185               0 :   mRedirectCallback = callback;
    1186               0 :   mNewRedirectChannel = newChannel;
    1187                 : 
    1188               0 :   nsCOMPtr<nsIChannelEventSink> sink(do_GetInterface(mPrevChannelSink));
    1189               0 :   if (sink) {
    1190               0 :     nsresult rv = sink->AsyncOnChannelRedirect(oldChannel, newChannel, flags,
    1191               0 :                                                this);
    1192               0 :     if (NS_FAILED(rv)) {
    1193               0 :         mRedirectCallback = nsnull;
    1194               0 :         mNewRedirectChannel = nsnull;
    1195                 :     }
    1196               0 :     return rv;
    1197                 :   }
    1198                 : 
    1199               0 :   (void) OnRedirectVerifyCallback(NS_OK);
    1200               0 :   return NS_OK;
    1201                 : }
    1202                 : 
    1203                 : NS_IMETHODIMP
    1204               0 : imgRequest::OnRedirectVerifyCallback(nsresult result)
    1205                 : {
    1206               0 :   NS_ASSERTION(mRedirectCallback, "mRedirectCallback not set in callback");
    1207               0 :   NS_ASSERTION(mNewRedirectChannel, "mNewRedirectChannel not set in callback");
    1208                 : 
    1209               0 :   if (NS_FAILED(result)) {
    1210               0 :       mRedirectCallback->OnRedirectVerifyCallback(result);
    1211               0 :       mRedirectCallback = nsnull;
    1212               0 :       mNewRedirectChannel = nsnull;
    1213               0 :       return NS_OK;
    1214                 :   }
    1215                 : 
    1216               0 :   mChannel = mNewRedirectChannel;
    1217               0 :   mTimedChannel = do_QueryInterface(mChannel);
    1218               0 :   mNewRedirectChannel = nsnull;
    1219                 : 
    1220                 : #if defined(PR_LOGGING)
    1221               0 :   nsCAutoString oldspec;
    1222               0 :   if (mCurrentURI)
    1223               0 :     mCurrentURI->GetSpec(oldspec);
    1224               0 :   LOG_MSG_WITH_PARAM(gImgLog, "imgRequest::OnChannelRedirect", "old", oldspec.get());
    1225                 : #endif
    1226                 : 
    1227                 :   // make sure we have a protocol that returns data rather than opens
    1228                 :   // an external application, e.g. mailto:
    1229               0 :   mChannel->GetURI(getter_AddRefs(mCurrentURI));
    1230               0 :   bool doesNotReturnData = false;
    1231                 :   nsresult rv =
    1232                 :     NS_URIChainHasFlags(mCurrentURI, nsIProtocolHandler::URI_DOES_NOT_RETURN_DATA,
    1233               0 :                         &doesNotReturnData);
    1234                 : 
    1235               0 :   if (NS_SUCCEEDED(rv) && doesNotReturnData)
    1236               0 :     rv = NS_ERROR_ABORT;
    1237                 : 
    1238               0 :   if (NS_FAILED(rv)) {
    1239               0 :     mRedirectCallback->OnRedirectVerifyCallback(rv);
    1240               0 :     mRedirectCallback = nsnull;
    1241               0 :     return NS_OK;
    1242                 :   }
    1243                 : 
    1244               0 :   mRedirectCallback->OnRedirectVerifyCallback(NS_OK);
    1245               0 :   mRedirectCallback = nsnull;
    1246               0 :   return NS_OK;
    1247            4392 : }

Generated by: LCOV version 1.7