LCOV - code coverage report
Current view: directory - image/src - imgLoader.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 948 472 49.8 %
Date: 2012-06-02 Functions: 126 74 58.7 %

       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                 :  *   Ehsan Akhgari <ehsan.akhgari@gmail.com>
      25                 :  *
      26                 :  * Alternatively, the contents of this file may be used under the terms of
      27                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      28                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      29                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      30                 :  * of those above. If you wish to allow use of your version of this file only
      31                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      32                 :  * use your version of this file under the terms of the MPL, indicate your
      33                 :  * decision by deleting the provisions above and replace them with the notice
      34                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      35                 :  * the provisions above, a recipient may use your version of this file under
      36                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      37                 :  *
      38                 :  * ***** END LICENSE BLOCK ***** */
      39                 : 
      40                 : #include "mozilla/Attributes.h"
      41                 : #include "mozilla/FunctionTimer.h"
      42                 : #include "mozilla/Preferences.h"
      43                 : 
      44                 : #include "ImageLogging.h"
      45                 : #include "imgLoader.h"
      46                 : #include "imgRequestProxy.h"
      47                 : 
      48                 : #include "RasterImage.h"
      49                 : /* We end up pulling in windows.h because we eventually hit gfxWindowsSurface;
      50                 :  * windows.h defines LoadImage, so we have to #undef it or imgLoader::LoadImage
      51                 :  * gets changed.
      52                 :  * This #undef needs to be in multiple places because we don't always pull
      53                 :  * headers in in the same order.
      54                 :  */
      55                 : #undef LoadImage
      56                 : 
      57                 : #include "nsCOMPtr.h"
      58                 : 
      59                 : #include "nsContentUtils.h"
      60                 : #include "nsCrossSiteListenerProxy.h"
      61                 : #include "nsNetUtil.h"
      62                 : #include "nsStreamUtils.h"
      63                 : #include "nsIHttpChannel.h"
      64                 : #include "nsICachingChannel.h"
      65                 : #include "nsIInterfaceRequestor.h"
      66                 : #include "nsIProgressEventSink.h"
      67                 : #include "nsIChannelEventSink.h"
      68                 : #include "nsIAsyncVerifyRedirectCallback.h"
      69                 : #include "nsIServiceManager.h"
      70                 : #include "nsIFileURL.h"
      71                 : #include "nsThreadUtils.h"
      72                 : #include "nsXPIDLString.h"
      73                 : #include "nsCRT.h"
      74                 : #include "nsIDocument.h"
      75                 : #include "nsPIDOMWindow.h"
      76                 : 
      77                 : #include "netCore.h"
      78                 : 
      79                 : #include "nsURILoader.h"
      80                 : 
      81                 : #include "nsIComponentRegistrar.h"
      82                 : 
      83                 : #include "nsIApplicationCache.h"
      84                 : #include "nsIApplicationCacheContainer.h"
      85                 : 
      86                 : #include "nsIMemoryReporter.h"
      87                 : #include "nsIPrivateBrowsingService.h"
      88                 : 
      89                 : // we want to explore making the document own the load group
      90                 : // so we can associate the document URI with the load group.
      91                 : // until this point, we have an evil hack:
      92                 : #include "nsIHttpChannelInternal.h"  
      93                 : #include "nsIContentSecurityPolicy.h"
      94                 : #include "nsIChannelPolicy.h"
      95                 : 
      96                 : #include "nsContentUtils.h"
      97                 : 
      98                 : using namespace mozilla;
      99                 : using namespace mozilla::image;
     100                 : 
     101                 : #if defined(DEBUG_pavlov) || defined(DEBUG_timeless)
     102                 : #include "nsISimpleEnumerator.h"
     103                 : #include "nsXPCOM.h"
     104                 : #include "nsISupportsPrimitives.h"
     105                 : #include "nsXPIDLString.h"
     106                 : #include "nsComponentManagerUtils.h"
     107                 : 
     108                 : 
     109                 : static void PrintImageDecoders()
     110                 : {
     111                 :   nsCOMPtr<nsIComponentRegistrar> compMgr;
     112                 :   if (NS_FAILED(NS_GetComponentRegistrar(getter_AddRefs(compMgr))) || !compMgr)
     113                 :     return;
     114                 :   nsCOMPtr<nsISimpleEnumerator> enumer;
     115                 :   if (NS_FAILED(compMgr->EnumerateContractIDs(getter_AddRefs(enumer))) || !enumer)
     116                 :     return;
     117                 :   
     118                 :   nsCString str;
     119                 :   nsCOMPtr<nsISupports> s;
     120                 :   bool more = false;
     121                 :   while (NS_SUCCEEDED(enumer->HasMoreElements(&more)) && more) {
     122                 :     enumer->GetNext(getter_AddRefs(s));
     123                 :     if (s) {
     124                 :       nsCOMPtr<nsISupportsCString> ss(do_QueryInterface(s));
     125                 : 
     126                 :       nsCAutoString xcs;
     127                 :       ss->GetData(xcs);
     128                 : 
     129                 :       NS_NAMED_LITERAL_CSTRING(decoderContract, "@mozilla.org/image/decoder;3?type=");
     130                 : 
     131                 :       if (StringBeginsWith(xcs, decoderContract)) {
     132                 :         printf("Have decoder for mime type: %s\n", xcs.get()+decoderContract.Length());
     133                 :       }
     134                 :     }
     135                 :   }
     136                 : }
     137                 : #endif
     138                 : 
     139               0 : NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(ImagesMallocSizeOf, "images")
     140                 : 
     141                 : class imgMemoryReporter MOZ_FINAL :
     142                 :   public nsIMemoryMultiReporter
     143                 : {
     144                 : public:
     145              93 :   imgMemoryReporter()
     146              93 :   {
     147              93 :   }
     148                 : 
     149                 :   NS_DECL_ISUPPORTS
     150                 : 
     151               0 :   NS_IMETHOD GetName(nsACString &name)
     152                 :   {
     153               0 :     name.Assign("images");
     154               0 :     return NS_OK;
     155                 :   }
     156                 : 
     157               0 :   NS_IMETHOD CollectReports(nsIMemoryMultiReporterCallback *callback,
     158                 :                             nsISupports *closure)
     159                 :   {
     160               0 :     AllSizes chrome;
     161               0 :     AllSizes content;
     162               0 :     imgLoader::sChromeCache.EnumerateRead(EntryAllSizes, &chrome);
     163               0 :     imgLoader::sCache.EnumerateRead(EntryAllSizes, &content);
     164                 : 
     165                 : #define REPORT(_path, _kind, _amount, _desc)                                  \
     166                 :     do {                                                                      \
     167                 :       nsresult rv;                                                            \
     168                 :       rv = callback->Callback(EmptyCString(), NS_LITERAL_CSTRING(_path),      \
     169                 :                               _kind, nsIMemoryReporter::UNITS_BYTES, _amount, \
     170                 :                               NS_LITERAL_CSTRING(_desc), closure);            \
     171                 :       NS_ENSURE_SUCCESS(rv, rv);                                              \
     172                 :     } while (0)
     173                 : 
     174               0 :     REPORT("explicit/images/chrome/used/raw",
     175                 :            nsIMemoryReporter::KIND_HEAP, chrome.mUsedRaw,
     176                 :            "Memory used by in-use chrome images (compressed data).");
     177                 : 
     178               0 :     REPORT("explicit/images/chrome/used/uncompressed-heap",
     179                 :            nsIMemoryReporter::KIND_HEAP, chrome.mUsedUncompressedHeap,
     180                 :            "Memory used by in-use chrome images (uncompressed data).");
     181                 : 
     182               0 :     REPORT("explicit/images/chrome/used/uncompressed-nonheap",
     183                 :            nsIMemoryReporter::KIND_NONHEAP, chrome.mUsedUncompressedNonheap,
     184                 :            "Memory used by in-use chrome images (uncompressed data).");
     185                 : 
     186               0 :     REPORT("explicit/images/chrome/unused/raw",
     187                 :            nsIMemoryReporter::KIND_HEAP, chrome.mUnusedRaw,
     188                 :            "Memory used by not in-use chrome images (compressed data).");
     189                 : 
     190               0 :     REPORT("explicit/images/chrome/unused/uncompressed-heap",
     191                 :            nsIMemoryReporter::KIND_HEAP, chrome.mUnusedUncompressedHeap,
     192                 :            "Memory used by not in-use chrome images (uncompressed data).");
     193                 : 
     194               0 :     REPORT("explicit/images/chrome/unused/uncompressed-nonheap",
     195                 :            nsIMemoryReporter::KIND_NONHEAP, chrome.mUnusedUncompressedNonheap,
     196                 :            "Memory used by not in-use chrome images (uncompressed data).");
     197                 : 
     198               0 :     REPORT("explicit/images/content/used/raw",
     199                 :            nsIMemoryReporter::KIND_HEAP, content.mUsedRaw,
     200                 :            "Memory used by in-use content images (compressed data).");
     201                 : 
     202               0 :     REPORT("explicit/images/content/used/uncompressed-heap",
     203                 :            nsIMemoryReporter::KIND_HEAP, content.mUsedUncompressedHeap,
     204                 :            "Memory used by in-use content images (uncompressed data).");
     205                 : 
     206               0 :     REPORT("explicit/images/content/used/uncompressed-nonheap",
     207                 :            nsIMemoryReporter::KIND_NONHEAP, content.mUsedUncompressedNonheap,
     208                 :            "Memory used by in-use content images (uncompressed data).");
     209                 : 
     210               0 :     REPORT("explicit/images/content/unused/raw",
     211                 :            nsIMemoryReporter::KIND_HEAP, content.mUnusedRaw,
     212                 :            "Memory used by not in-use content images (compressed data).");
     213                 : 
     214               0 :     REPORT("explicit/images/content/unused/uncompressed-heap",
     215                 :            nsIMemoryReporter::KIND_HEAP, content.mUnusedUncompressedHeap,
     216                 :            "Memory used by not in-use content images (uncompressed data).");
     217                 : 
     218               0 :     REPORT("explicit/images/content/unused/uncompressed-nonheap",
     219                 :            nsIMemoryReporter::KIND_NONHEAP, content.mUnusedUncompressedNonheap,
     220                 :            "Memory used by not in-use content images (uncompressed data).");
     221                 : 
     222                 : #undef REPORT
     223                 : 
     224               0 :     return NS_OK;
     225                 :   }
     226                 : 
     227               0 :   NS_IMETHOD GetExplicitNonHeap(PRInt64 *n)
     228                 :   {
     229               0 :     size_t n2 = 0;
     230               0 :     imgLoader::sChromeCache.EnumerateRead(EntryExplicitNonHeapSize, &n2);
     231               0 :     imgLoader::sCache.EnumerateRead(EntryExplicitNonHeapSize, &n2);
     232               0 :     *n = n2;
     233               0 :     return NS_OK;
     234                 :   }
     235                 : 
     236               0 :   static PRInt64 GetImagesContentUsedUncompressed()
     237                 :   {
     238               0 :     size_t n = 0;
     239               0 :     imgLoader::sCache.EnumerateRead(EntryUsedUncompressedSize, &n);
     240               0 :     return n;
     241                 :   }
     242                 : 
     243                 : private:
     244                 :   struct AllSizes {
     245                 :     size_t mUsedRaw;
     246                 :     size_t mUsedUncompressedHeap;
     247                 :     size_t mUsedUncompressedNonheap;
     248                 :     size_t mUnusedRaw;
     249                 :     size_t mUnusedUncompressedHeap;
     250                 :     size_t mUnusedUncompressedNonheap;
     251                 : 
     252               0 :     AllSizes() {
     253               0 :       memset(this, 0, sizeof(*this));
     254               0 :     }
     255                 :   };
     256                 : 
     257               0 :   static PLDHashOperator EntryAllSizes(const nsACString&,
     258                 :                                        imgCacheEntry *entry,
     259                 :                                        void *userArg)
     260                 :   {
     261               0 :     nsRefPtr<imgRequest> req = entry->GetRequest();
     262               0 :     Image *image = static_cast<Image*>(req->mImage.get());
     263               0 :     if (image) {
     264               0 :       AllSizes *sizes = static_cast<AllSizes*>(userArg);
     265               0 :       if (entry->HasNoProxies()) {
     266                 :         sizes->mUnusedRaw +=
     267               0 :           image->HeapSizeOfSourceWithComputedFallback(ImagesMallocSizeOf);
     268                 :         sizes->mUnusedUncompressedHeap +=
     269               0 :           image->HeapSizeOfDecodedWithComputedFallback(ImagesMallocSizeOf);
     270               0 :         sizes->mUnusedUncompressedNonheap += image->NonHeapSizeOfDecoded();
     271                 :       } else {
     272                 :         sizes->mUsedRaw +=
     273               0 :           image->HeapSizeOfSourceWithComputedFallback(ImagesMallocSizeOf);
     274                 :         sizes->mUsedUncompressedHeap +=
     275               0 :           image->HeapSizeOfDecodedWithComputedFallback(ImagesMallocSizeOf);
     276               0 :         sizes->mUsedUncompressedNonheap += image->NonHeapSizeOfDecoded();
     277                 :       }
     278                 :     }
     279                 : 
     280               0 :     return PL_DHASH_NEXT;
     281                 :   }
     282                 : 
     283               0 :   static PLDHashOperator EntryExplicitNonHeapSize(const nsACString&,
     284                 :                                                   imgCacheEntry *entry,
     285                 :                                                   void *userArg)
     286                 :   {
     287               0 :     size_t *n = static_cast<size_t*>(userArg);
     288               0 :     nsRefPtr<imgRequest> req = entry->GetRequest();
     289               0 :     Image *image = static_cast<Image*>(req->mImage.get());
     290               0 :     if (image) {
     291               0 :       *n += image->NonHeapSizeOfDecoded();
     292                 :     }
     293                 : 
     294               0 :     return PL_DHASH_NEXT;
     295                 :   }
     296                 : 
     297               0 :   static PLDHashOperator EntryUsedUncompressedSize(const nsACString&,
     298                 :                                                    imgCacheEntry *entry,
     299                 :                                                    void *userArg)
     300                 :   {
     301               0 :     if (!entry->HasNoProxies()) {
     302               0 :       size_t *n = static_cast<size_t*>(userArg);
     303               0 :       nsRefPtr<imgRequest> req = entry->GetRequest();
     304               0 :       Image *image = static_cast<Image*>(req->mImage.get());
     305               0 :       if (image) {
     306               0 :         *n += image->HeapSizeOfDecodedWithComputedFallback(ImagesMallocSizeOf);
     307               0 :         *n += image->NonHeapSizeOfDecoded();
     308                 :       }
     309                 :     }
     310                 : 
     311               0 :     return PL_DHASH_NEXT;
     312                 :   }
     313                 : };
     314                 : 
     315                 : // This is used by telemetry.
     316              93 : NS_MEMORY_REPORTER_IMPLEMENT(
     317                 :   ImagesContentUsedUncompressed,
     318                 :   "images-content-used-uncompressed",
     319                 :   KIND_OTHER,
     320                 :   UNITS_BYTES,
     321                 :   imgMemoryReporter::GetImagesContentUsedUncompressed,
     322                 :   "This is the sum of the 'explicit/images/content/used/uncompressed-heap' "
     323                 :   "and 'explicit/images/content/used/uncompressed-nonheap' numbers.  However, "
     324                 :   "it is measured at a different time and so may give slightly different "
     325             279 :   "results.")
     326                 : 
     327             279 : NS_IMPL_ISUPPORTS1(imgMemoryReporter, nsIMemoryMultiReporter)
     328                 : 
     329              64 : NS_IMPL_ISUPPORTS3(nsProgressNotificationProxy,
     330                 :                      nsIProgressEventSink,
     331                 :                      nsIChannelEventSink,
     332                 :                      nsIInterfaceRequestor)
     333                 : 
     334                 : NS_IMETHODIMP
     335               2 : nsProgressNotificationProxy::OnProgress(nsIRequest* request,
     336                 :                                         nsISupports* ctxt,
     337                 :                                         PRUint64 progress,
     338                 :                                         PRUint64 progressMax)
     339                 : {
     340               4 :   nsCOMPtr<nsILoadGroup> loadGroup;
     341               2 :   request->GetLoadGroup(getter_AddRefs(loadGroup));
     342                 : 
     343               4 :   nsCOMPtr<nsIProgressEventSink> target;
     344                 :   NS_QueryNotificationCallbacks(mOriginalCallbacks,
     345                 :                                 loadGroup,
     346                 :                                 NS_GET_IID(nsIProgressEventSink),
     347               2 :                                 getter_AddRefs(target));
     348               2 :   if (!target)
     349               2 :     return NS_OK;
     350               0 :   return target->OnProgress(mImageRequest, ctxt, progress, progressMax);
     351                 : }
     352                 : 
     353                 : NS_IMETHODIMP
     354               8 : nsProgressNotificationProxy::OnStatus(nsIRequest* request,
     355                 :                                       nsISupports* ctxt,
     356                 :                                       nsresult status,
     357                 :                                       const PRUnichar* statusArg)
     358                 : {
     359              16 :   nsCOMPtr<nsILoadGroup> loadGroup;
     360               8 :   request->GetLoadGroup(getter_AddRefs(loadGroup));
     361                 : 
     362              16 :   nsCOMPtr<nsIProgressEventSink> target;
     363                 :   NS_QueryNotificationCallbacks(mOriginalCallbacks,
     364                 :                                 loadGroup,
     365                 :                                 NS_GET_IID(nsIProgressEventSink),
     366               8 :                                 getter_AddRefs(target));
     367               8 :   if (!target)
     368               8 :     return NS_OK;
     369               0 :   return target->OnStatus(mImageRequest, ctxt, status, statusArg);
     370                 : }
     371                 : 
     372                 : NS_IMETHODIMP
     373               0 : nsProgressNotificationProxy::AsyncOnChannelRedirect(nsIChannel *oldChannel,
     374                 :                                                     nsIChannel *newChannel,
     375                 :                                                     PRUint32 flags,
     376                 :                                                     nsIAsyncVerifyRedirectCallback *cb)
     377                 : {
     378                 :   // Tell the original original callbacks about it too
     379               0 :   nsCOMPtr<nsILoadGroup> loadGroup;
     380               0 :   newChannel->GetLoadGroup(getter_AddRefs(loadGroup));
     381               0 :   nsCOMPtr<nsIChannelEventSink> target;
     382                 :   NS_QueryNotificationCallbacks(mOriginalCallbacks,
     383                 :                                 loadGroup,
     384                 :                                 NS_GET_IID(nsIChannelEventSink),
     385               0 :                                 getter_AddRefs(target));
     386               0 :   if (!target) {
     387               0 :       cb->OnRedirectVerifyCallback(NS_OK);
     388               0 :       return NS_OK;
     389                 :   }
     390                 : 
     391                 :   // Delegate to |target| if set, reusing |cb|
     392               0 :   return target->AsyncOnChannelRedirect(oldChannel, newChannel, flags, cb);
     393                 : }
     394                 : 
     395                 : NS_IMETHODIMP
     396               2 : nsProgressNotificationProxy::GetInterface(const nsIID& iid,
     397                 :                                           void** result)
     398                 : {
     399               2 :   if (iid.Equals(NS_GET_IID(nsIProgressEventSink))) {
     400               2 :     *result = static_cast<nsIProgressEventSink*>(this);
     401               2 :     NS_ADDREF_THIS();
     402               2 :     return NS_OK;
     403                 :   }
     404               0 :   if (iid.Equals(NS_GET_IID(nsIChannelEventSink))) {
     405               0 :     *result = static_cast<nsIChannelEventSink*>(this);
     406               0 :     NS_ADDREF_THIS();
     407               0 :     return NS_OK;
     408                 :   }
     409               0 :   if (mOriginalCallbacks)
     410               0 :     return mOriginalCallbacks->GetInterface(iid, result);
     411               0 :   return NS_NOINTERFACE;
     412                 : }
     413                 : 
     414               8 : static void NewRequestAndEntry(bool aForcePrincipalCheckForCacheEntry,
     415                 :                                imgRequest **aRequest, imgCacheEntry **aEntry)
     416                 : {
     417              16 :   nsRefPtr<imgRequest> request = new imgRequest();
     418              24 :   nsRefPtr<imgCacheEntry> entry = new imgCacheEntry(request, aForcePrincipalCheckForCacheEntry);
     419               8 :   request.forget(aRequest);
     420               8 :   entry.forget(aEntry);
     421               8 : }
     422                 : 
     423               0 : static bool ShouldRevalidateEntry(imgCacheEntry *aEntry,
     424                 :                               nsLoadFlags aFlags,
     425                 :                               bool aHasExpired)
     426                 : {
     427               0 :   bool bValidateEntry = false;
     428                 : 
     429               0 :   if (aFlags & nsIRequest::LOAD_BYPASS_CACHE)
     430               0 :     return false;
     431                 : 
     432               0 :   if (aFlags & nsIRequest::VALIDATE_ALWAYS) {
     433               0 :     bValidateEntry = true;
     434                 :   }
     435               0 :   else if (aEntry->GetMustValidate()) {
     436               0 :     bValidateEntry = true;
     437                 :   }
     438                 :   //
     439                 :   // The cache entry has expired...  Determine whether the stale cache
     440                 :   // entry can be used without validation...
     441                 :   //
     442               0 :   else if (aHasExpired) {
     443                 :     //
     444                 :     // VALIDATE_NEVER and VALIDATE_ONCE_PER_SESSION allow stale cache
     445                 :     // entries to be used unless they have been explicitly marked to
     446                 :     // indicate that revalidation is necessary.
     447                 :     //
     448               0 :     if (aFlags & (nsIRequest::VALIDATE_NEVER | 
     449                 :                   nsIRequest::VALIDATE_ONCE_PER_SESSION)) 
     450                 :     {
     451               0 :       bValidateEntry = false;
     452                 :     }
     453                 :     //
     454                 :     // LOAD_FROM_CACHE allows a stale cache entry to be used... Otherwise,
     455                 :     // the entry must be revalidated.
     456                 :     //
     457               0 :     else if (!(aFlags & nsIRequest::LOAD_FROM_CACHE)) {
     458               0 :       bValidateEntry = true;
     459                 :     }
     460                 :   }
     461                 : 
     462               0 :   return bValidateEntry;
     463                 : }
     464                 : 
     465                 : // Returns true if this request is compatible with the given CORS mode on the
     466                 : // given loading principal, and false if the request may not be reused due
     467                 : // to CORS.
     468                 : static bool
     469               7 : ValidateCORSAndPrincipal(imgRequest* request, bool forcePrincipalCheck,
     470                 :                          PRInt32 corsmode, nsIPrincipal* loadingPrincipal)
     471                 : {
     472                 :   // If the entry's CORS mode doesn't match, or the CORS mode matches but the
     473                 :   // document principal isn't the same, we can't use this request.
     474               7 :   if (request->GetCORSMode() != corsmode) {
     475               0 :     return false;
     476               7 :   } else if (request->GetCORSMode() != imgIRequest::CORS_NONE ||
     477                 :              forcePrincipalCheck) {
     478              12 :     nsCOMPtr<nsIPrincipal> otherprincipal = request->GetLoadingPrincipal();
     479                 : 
     480                 :     // If we previously had a principal, but we don't now, we can't use this
     481                 :     // request.
     482               6 :     if (otherprincipal && !loadingPrincipal) {
     483               0 :       return false;
     484                 :     }
     485                 : 
     486               6 :     if (otherprincipal && loadingPrincipal) {
     487               0 :       bool equals = false;
     488               0 :       otherprincipal->Equals(loadingPrincipal, &equals);
     489               0 :       return equals;
     490                 :     }
     491                 :   }
     492                 : 
     493               7 :   return true;
     494                 : }
     495                 : 
     496               4 : static nsresult NewImageChannel(nsIChannel **aResult,
     497                 :                                 // If aForcePrincipalCheckForCacheEntry is
     498                 :                                 // true, then we will force a principal check
     499                 :                                 // even when not using CORS before assuming we
     500                 :                                 // have a cache hit on a cache entry that we
     501                 :                                 // create for this channel.  This is an out
     502                 :                                 // param that should be set to true if this
     503                 :                                 // channel ends up depending on
     504                 :                                 // aLoadingPrincipal and false otherwise.
     505                 :                                 bool *aForcePrincipalCheckForCacheEntry,
     506                 :                                 nsIURI *aURI,
     507                 :                                 nsIURI *aInitialDocumentURI,
     508                 :                                 nsIURI *aReferringURI,
     509                 :                                 nsILoadGroup *aLoadGroup,
     510                 :                                 const nsCString& aAcceptHeader,
     511                 :                                 nsLoadFlags aLoadFlags,
     512                 :                                 nsIChannelPolicy *aPolicy,
     513                 :                                 nsIPrincipal *aLoadingPrincipal)
     514                 : {
     515                 :   nsresult rv;
     516               8 :   nsCOMPtr<nsIChannel> newChannel;
     517               8 :   nsCOMPtr<nsIHttpChannel> newHttpChannel;
     518                 :  
     519               8 :   nsCOMPtr<nsIInterfaceRequestor> callbacks;
     520                 : 
     521               4 :   if (aLoadGroup) {
     522                 :     // Get the notification callbacks from the load group for the new channel.
     523                 :     //
     524                 :     // XXX: This is not exactly correct, because the network request could be
     525                 :     //      referenced by multiple windows...  However, the new channel needs
     526                 :     //      something.  So, using the 'first' notification callbacks is better
     527                 :     //      than nothing...
     528                 :     //
     529               0 :     aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
     530                 :   }
     531                 : 
     532                 :   // Pass in a NULL loadgroup because this is the underlying network request.
     533                 :   // This request may be referenced by several proxy image requests (psossibly
     534                 :   // in different documents).
     535                 :   // If all of the proxy requests are canceled then this request should be
     536                 :   // canceled too.
     537                 :   //
     538                 :   rv = NS_NewChannel(aResult,
     539                 :                      aURI,        // URI 
     540                 :                      nsnull,      // Cached IOService
     541                 :                      nsnull,      // LoadGroup
     542                 :                      callbacks,   // Notification Callbacks
     543                 :                      aLoadFlags,
     544               4 :                      aPolicy);
     545               4 :   if (NS_FAILED(rv))
     546               0 :     return rv;
     547                 : 
     548               4 :   *aForcePrincipalCheckForCacheEntry = false;
     549                 : 
     550                 :   // Initialize HTTP-specific attributes
     551               4 :   newHttpChannel = do_QueryInterface(*aResult);
     552               4 :   if (newHttpChannel) {
     553               4 :     newHttpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
     554                 :                                      aAcceptHeader,
     555               2 :                                      false);
     556                 : 
     557               4 :     nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal = do_QueryInterface(newHttpChannel);
     558               2 :     NS_ENSURE_TRUE(httpChannelInternal, NS_ERROR_UNEXPECTED);
     559               2 :     httpChannelInternal->SetDocumentURI(aInitialDocumentURI);
     560               4 :     newHttpChannel->SetReferrer(aReferringURI);
     561                 :   }
     562                 : 
     563                 :   // Image channels are loaded by default with reduced priority.
     564               8 :   nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(*aResult);
     565               4 :   if (p) {
     566               2 :     PRUint32 priority = nsISupportsPriority::PRIORITY_LOW;
     567                 : 
     568               2 :     if (aLoadFlags & nsIRequest::LOAD_BACKGROUND)
     569               0 :       ++priority; // further reduce priority for background loads
     570                 : 
     571               2 :     p->AdjustPriority(priority);
     572                 :   }
     573                 : 
     574                 :   bool setOwner = nsContentUtils::SetUpChannelOwner(aLoadingPrincipal,
     575               4 :                                                       *aResult, aURI, false);
     576               4 :   *aForcePrincipalCheckForCacheEntry = setOwner;
     577                 : 
     578               4 :   return NS_OK;
     579                 : }
     580                 : 
     581              24 : static PRUint32 SecondsFromPRTime(PRTime prTime)
     582                 : {
     583              24 :   return PRUint32(PRInt64(prTime) / PRInt64(PR_USEC_PER_SEC));
     584                 : }
     585                 : 
     586               8 : imgCacheEntry::imgCacheEntry(imgRequest *request, bool forcePrincipalCheck)
     587                 :  : mRequest(request),
     588                 :    mDataSize(0),
     589               8 :    mTouchedTime(SecondsFromPRTime(PR_Now())),
     590                 :    mExpiryTime(0),
     591                 :    mMustValidate(false),
     592                 :    // We start off as evicted so we don't try to update the cache. PutIntoCache
     593                 :    // will set this to false.
     594                 :    mEvicted(true),
     595                 :    mHasNoProxies(true),
     596              16 :    mForcePrincipalCheck(forcePrincipalCheck)
     597               8 : {}
     598                 : 
     599              16 : imgCacheEntry::~imgCacheEntry()
     600                 : {
     601               8 :   LOG_FUNC(gImgLog, "imgCacheEntry::~imgCacheEntry()");
     602               8 : }
     603                 : 
     604               9 : void imgCacheEntry::Touch(bool updateTime /* = true */)
     605                 : {
     606              18 :   LOG_SCOPE(gImgLog, "imgCacheEntry::Touch");
     607                 : 
     608               9 :   if (updateTime)
     609               9 :     mTouchedTime = SecondsFromPRTime(PR_Now());
     610                 : 
     611               9 :   UpdateCache();
     612               9 : }
     613                 : 
     614              17 : void imgCacheEntry::UpdateCache(PRInt32 diff /* = 0 */)
     615                 : {
     616                 :   // Don't update the cache if we've been removed from it or it doesn't care
     617                 :   // about our size or usage.
     618              17 :   if (!Evicted() && HasNoProxies()) {
     619               0 :     nsCOMPtr<nsIURI> uri;
     620               0 :     mRequest->GetURI(getter_AddRefs(uri));
     621               0 :     imgLoader::CacheEntriesChanged(uri, diff);
     622                 :   }
     623              17 : }
     624                 : 
     625              10 : void imgCacheEntry::SetHasNoProxies(bool hasNoProxies)
     626                 : {
     627                 : #if defined(PR_LOGGING)
     628              20 :   nsCOMPtr<nsIURI> uri;
     629              10 :   mRequest->GetURI(getter_AddRefs(uri));
     630              20 :   nsCAutoString spec;
     631              10 :   if (uri)
     632              10 :     uri->GetSpec(spec);
     633              10 :   if (hasNoProxies)
     634               2 :     LOG_FUNC_WITH_PARAM(gImgLog, "imgCacheEntry::SetHasNoProxies true", "uri", spec.get());
     635                 :   else
     636               8 :     LOG_FUNC_WITH_PARAM(gImgLog, "imgCacheEntry::SetHasNoProxies false", "uri", spec.get());
     637                 : #endif
     638                 : 
     639              10 :   mHasNoProxies = hasNoProxies;
     640              10 : }
     641                 : 
     642            2928 : imgCacheQueue::imgCacheQueue()
     643                 :  : mDirty(false),
     644            2928 :    mSize(0)
     645            2928 : {}
     646                 : 
     647               0 : void imgCacheQueue::UpdateSize(PRInt32 diff)
     648                 : {
     649               0 :   mSize += diff;
     650               0 : }
     651                 : 
     652               2 : PRUint32 imgCacheQueue::GetSize() const
     653                 : {
     654               2 :   return mSize;
     655                 : }
     656                 : 
     657                 : #include <algorithm>
     658                 : using namespace std;
     659                 : 
     660              10 : void imgCacheQueue::Remove(imgCacheEntry *entry)
     661                 : {
     662              10 :   queueContainer::iterator it = find(mQueue.begin(), mQueue.end(), entry);
     663              10 :   if (it != mQueue.end()) {
     664              10 :     mSize -= (*it)->GetDataSize();
     665              10 :     mQueue.erase(it);
     666              10 :     MarkDirty();
     667                 :   }
     668              10 : }
     669                 : 
     670              10 : void imgCacheQueue::Push(imgCacheEntry *entry)
     671                 : {
     672              10 :   mSize += entry->GetDataSize();
     673                 : 
     674              20 :   nsRefPtr<imgCacheEntry> refptr(entry);
     675              10 :   mQueue.push_back(refptr);
     676              10 :   MarkDirty();
     677              10 : }
     678                 : 
     679               0 : already_AddRefed<imgCacheEntry> imgCacheQueue::Pop()
     680                 : {
     681               0 :   if (mQueue.empty())
     682               0 :     return nsnull;
     683               0 :   if (IsDirty())
     684               0 :     Refresh();
     685                 : 
     686               0 :   nsRefPtr<imgCacheEntry> entry = mQueue[0];
     687               0 :   std::pop_heap(mQueue.begin(), mQueue.end(), imgLoader::CompareCacheEntries);
     688               0 :   mQueue.pop_back();
     689                 : 
     690               0 :   mSize -= entry->GetDataSize();
     691               0 :   imgCacheEntry *ret = entry;
     692               0 :   NS_ADDREF(ret);
     693               0 :   return ret;
     694                 : }
     695                 : 
     696               0 : void imgCacheQueue::Refresh()
     697                 : {
     698               0 :   std::make_heap(mQueue.begin(), mQueue.end(), imgLoader::CompareCacheEntries);
     699               0 :   mDirty = false;
     700               0 : }
     701                 : 
     702              20 : void imgCacheQueue::MarkDirty()
     703                 : {
     704              20 :   mDirty = true;
     705              20 : }
     706                 : 
     707               0 : bool imgCacheQueue::IsDirty()
     708                 : {
     709               0 :   return mDirty;
     710                 : }
     711                 : 
     712              36 : PRUint32 imgCacheQueue::GetNumElements() const
     713                 : {
     714              36 :   return mQueue.size();
     715                 : }
     716                 : 
     717               0 : imgCacheQueue::iterator imgCacheQueue::begin()
     718                 : {
     719               0 :   return mQueue.begin();
     720                 : }
     721               0 : imgCacheQueue::const_iterator imgCacheQueue::begin() const
     722                 : {
     723               0 :   return mQueue.begin();
     724                 : }
     725                 : 
     726               0 : imgCacheQueue::iterator imgCacheQueue::end()
     727                 : {
     728               0 :   return mQueue.end();
     729                 : }
     730               0 : imgCacheQueue::const_iterator imgCacheQueue::end() const
     731                 : {
     732               0 :   return mQueue.end();
     733                 : }
     734                 : 
     735              15 : nsresult imgLoader::CreateNewProxyForRequest(imgRequest *aRequest, nsILoadGroup *aLoadGroup,
     736                 :                                              imgIDecoderObserver *aObserver,
     737                 :                                              nsLoadFlags aLoadFlags, imgIRequest *aProxyRequest,
     738                 :                                              imgIRequest **_retval)
     739                 : {
     740              30 :   LOG_SCOPE_WITH_PARAM(gImgLog, "imgLoader::CreateNewProxyForRequest", "imgRequest", aRequest);
     741                 : 
     742                 :   /* XXX If we move decoding onto separate threads, we should save off the
     743                 :      calling thread here and pass it off to |proxyRequest| so that it call
     744                 :      proxy calls to |aObserver|.
     745                 :    */
     746                 : 
     747                 :   imgRequestProxy *proxyRequest;
     748              15 :   if (aProxyRequest) {
     749               0 :     proxyRequest = static_cast<imgRequestProxy *>(aProxyRequest);
     750                 :   } else {
     751              15 :     proxyRequest = new imgRequestProxy();
     752                 :   }
     753              15 :   NS_ADDREF(proxyRequest);
     754                 : 
     755                 :   /* It is important to call |SetLoadFlags()| before calling |Init()| because
     756                 :      |Init()| adds the request to the loadgroup.
     757                 :    */
     758              15 :   proxyRequest->SetLoadFlags(aLoadFlags);
     759                 : 
     760              30 :   nsCOMPtr<nsIURI> uri;
     761              15 :   aRequest->GetURI(getter_AddRefs(uri));
     762                 : 
     763                 :   // init adds itself to imgRequest's list of observers
     764              15 :   nsresult rv = proxyRequest->Init(aRequest, aLoadGroup, aRequest->mImage, uri, aObserver);
     765              15 :   if (NS_FAILED(rv)) {
     766               0 :     NS_RELEASE(proxyRequest);
     767               0 :     return rv;
     768                 :   }
     769                 : 
     770                 :   // transfer reference to caller
     771              15 :   *_retval = static_cast<imgIRequest*>(proxyRequest);
     772                 : 
     773              15 :   return NS_OK;
     774                 : }
     775                 : 
     776                 : class imgCacheObserver MOZ_FINAL : public nsIObserver
     777             186 : {
     778                 : public:
     779                 :   NS_DECL_ISUPPORTS
     780                 :   NS_DECL_NSIOBSERVER
     781                 : private:
     782                 :   imgLoader mLoader;
     783                 : };
     784                 : 
     785             837 : NS_IMPL_ISUPPORTS1(imgCacheObserver, nsIObserver)
     786                 : 
     787                 : NS_IMETHODIMP
     788               0 : imgCacheObserver::Observe(nsISupports* aSubject, const char* aTopic, const PRUnichar* aSomeData)
     789                 : {
     790               0 :   if (strcmp(aTopic, "memory-pressure") == 0) {
     791               0 :     DiscardTracker::DiscardAll();
     792               0 :     mLoader.MinimizeCaches();
     793               0 :   } else if (strcmp(aTopic, "chrome-flush-skin-caches") == 0 ||
     794               0 :              strcmp(aTopic, "chrome-flush-caches") == 0) {
     795               0 :     mLoader.ClearChromeImageCache();
     796                 :   }
     797               0 :   return NS_OK;
     798                 : }
     799                 : 
     800                 : class imgCacheExpirationTracker MOZ_FINAL
     801                 :   : public nsExpirationTracker<imgCacheEntry, 3>
     802              93 : {
     803                 :   enum { TIMEOUT_SECONDS = 10 };
     804                 : public:
     805                 :   imgCacheExpirationTracker();
     806                 : 
     807                 : protected:
     808                 :   void NotifyExpired(imgCacheEntry *entry);
     809                 : };
     810                 : 
     811              93 : imgCacheExpirationTracker::imgCacheExpirationTracker()
     812              93 :  : nsExpirationTracker<imgCacheEntry, 3>(TIMEOUT_SECONDS * 1000)
     813              93 : {}
     814                 : 
     815               0 : void imgCacheExpirationTracker::NotifyExpired(imgCacheEntry *entry)
     816                 : {
     817                 :   // Hold on to a reference to this entry, because the expiration tracker
     818                 :   // mechanism doesn't.
     819               0 :   nsRefPtr<imgCacheEntry> kungFuDeathGrip(entry);
     820                 : 
     821                 : #if defined(PR_LOGGING)
     822               0 :   nsRefPtr<imgRequest> req(entry->GetRequest());
     823               0 :   if (req) {
     824               0 :     nsCOMPtr<nsIURI> uri;
     825               0 :     req->GetURI(getter_AddRefs(uri));
     826               0 :     nsCAutoString spec;
     827               0 :     uri->GetSpec(spec);
     828               0 :     LOG_FUNC_WITH_PARAM(gImgLog, "imgCacheExpirationTracker::NotifyExpired", "entry", spec.get());
     829                 :   }
     830                 : #endif
     831                 : 
     832                 :   // We can be called multiple times on the same entry. Don't do work multiple
     833                 :   // times.
     834               0 :   if (!entry->Evicted())
     835               0 :     imgLoader::RemoveFromCache(entry);
     836                 : 
     837               0 :   imgLoader::VerifyCacheSizes();
     838               0 : }
     839                 : 
     840                 : imgCacheObserver *gCacheObserver;
     841                 : imgCacheExpirationTracker *gCacheTracker;
     842                 : 
     843            1464 : imgLoader::imgCacheTable imgLoader::sCache;
     844            1464 : imgCacheQueue imgLoader::sCacheQueue;
     845                 : 
     846            1464 : imgLoader::imgCacheTable imgLoader::sChromeCache;
     847            1464 : imgCacheQueue imgLoader::sChromeCacheQueue;
     848                 : 
     849                 : PRFloat64 imgLoader::sCacheTimeWeight;
     850                 : PRUint32 imgLoader::sCacheMaxSize;
     851                 : 
     852            6780 : NS_IMPL_ISUPPORTS5(imgLoader, imgILoader, nsIContentSniffer, imgICache, nsISupportsWeakReference, nsIObserver)
     853                 : 
     854            1647 : imgLoader::imgLoader()
     855                 : {
     856                 :   /* member initializers and constructor code */
     857            1647 : }
     858                 : 
     859            1760 : imgLoader::~imgLoader()
     860                 : {
     861                 :   /* destructor code */
     862            3520 : }
     863                 : 
     864              17 : void imgLoader::VerifyCacheSizes()
     865                 : {
     866                 : #ifdef DEBUG
     867              17 :   if (!gCacheTracker)
     868               0 :     return;
     869                 : 
     870              17 :   PRUint32 cachesize = sCache.Count() + sChromeCache.Count();
     871              17 :   PRUint32 queuesize = sCacheQueue.GetNumElements() + sChromeCacheQueue.GetNumElements();
     872              17 :   PRUint32 trackersize = 0;
     873              42 :   for (nsExpirationTracker<imgCacheEntry, 3>::Iterator it(gCacheTracker); it.Next(); )
     874               8 :     trackersize++;
     875              17 :   NS_ABORT_IF_FALSE(queuesize == trackersize, "Queue and tracker sizes out of sync!");
     876              17 :   NS_ABORT_IF_FALSE(queuesize <= cachesize, "Queue has more elements than cache!");
     877                 : #endif
     878                 : }
     879                 : 
     880              41 : imgLoader::imgCacheTable & imgLoader::GetCache(nsIURI *aURI)
     881                 : {
     882              41 :   bool chrome = false;
     883              41 :   aURI->SchemeIs("chrome", &chrome);
     884              41 :   if (chrome)
     885               0 :     return sChromeCache;
     886                 :   else
     887              41 :     return sCache;
     888                 : }
     889                 : 
     890              26 : imgCacheQueue & imgLoader::GetCacheQueue(nsIURI *aURI)
     891                 : {
     892              26 :   bool chrome = false;
     893              26 :   aURI->SchemeIs("chrome", &chrome);
     894              26 :   if (chrome)
     895               0 :     return sChromeCacheQueue;
     896                 :   else
     897              26 :     return sCacheQueue;
     898                 : }
     899                 : 
     900              93 : nsresult imgLoader::InitCache()
     901                 : {
     902                 :   NS_TIME_FUNCTION;
     903                 : 
     904                 :   nsresult rv;
     905             186 :   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
     906              93 :   if (!os)
     907               0 :     return NS_ERROR_FAILURE;
     908                 :   
     909              93 :   gCacheObserver = new imgCacheObserver();
     910              93 :   NS_ADDREF(gCacheObserver);
     911                 : 
     912              93 :   os->AddObserver(gCacheObserver, "memory-pressure", false);
     913              93 :   os->AddObserver(gCacheObserver, "chrome-flush-skin-caches", false);
     914              93 :   os->AddObserver(gCacheObserver, "chrome-flush-caches", false);
     915                 : 
     916              93 :   gCacheTracker = new imgCacheExpirationTracker();
     917                 : 
     918              93 :   if (!sCache.Init())
     919               0 :       return NS_ERROR_OUT_OF_MEMORY;
     920              93 :   if (!sChromeCache.Init())
     921               0 :       return NS_ERROR_OUT_OF_MEMORY;
     922                 : 
     923                 :   PRInt32 timeweight;
     924              93 :   rv = Preferences::GetInt("image.cache.timeweight", &timeweight);
     925              93 :   if (NS_SUCCEEDED(rv))
     926              93 :     sCacheTimeWeight = timeweight / 1000.0;
     927                 :   else
     928               0 :     sCacheTimeWeight = 0.5;
     929                 : 
     930                 :   PRInt32 cachesize;
     931              93 :   rv = Preferences::GetInt("image.cache.size", &cachesize);
     932              93 :   if (NS_SUCCEEDED(rv))
     933              93 :     sCacheMaxSize = cachesize;
     934                 :   else
     935               0 :     sCacheMaxSize = 5 * 1024 * 1024;
     936                 : 
     937              93 :   NS_RegisterMemoryMultiReporter(new imgMemoryReporter());
     938              93 :   NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(ImagesContentUsedUncompressed));
     939                 :   
     940              93 :   return NS_OK;
     941                 : }
     942                 : 
     943              90 : nsresult imgLoader::Init()
     944                 : {
     945              90 :   ReadAcceptHeaderPref();
     946                 : 
     947              90 :   Preferences::AddWeakObserver(this, "image.http.accept");
     948                 : 
     949                 :   // Listen for when we leave private browsing mode
     950             180 :   nsCOMPtr<nsIObserverService> obService = mozilla::services::GetObserverService();
     951              90 :   if (obService)
     952              90 :     obService->AddObserver(this, NS_PRIVATE_BROWSING_SWITCH_TOPIC, true);
     953                 : 
     954              90 :   return NS_OK;
     955                 : }
     956                 : 
     957                 : NS_IMETHODIMP
     958               4 : imgLoader::Observe(nsISupports* aSubject, const char* aTopic, const PRUnichar* aData)
     959                 : {
     960                 :   // We listen for pref change notifications...
     961               4 :   if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
     962               0 :     if (!strcmp(NS_ConvertUTF16toUTF8(aData).get(), "image.http.accept")) {
     963               0 :       ReadAcceptHeaderPref();
     964                 :     }
     965                 :   }
     966                 : 
     967                 :   // ...and exits from private browsing.
     968               4 :   else if (!strcmp(aTopic, NS_PRIVATE_BROWSING_SWITCH_TOPIC)) {
     969               4 :     if (NS_LITERAL_STRING(NS_PRIVATE_BROWSING_LEAVE).Equals(aData))
     970               2 :       ClearImageCache();
     971                 :   }
     972                 : 
     973                 :   // (Nothing else should bring us here)
     974                 :   else {
     975               0 :     NS_ABORT_IF_FALSE(0, "Invalid topic received");
     976                 :   }
     977                 : 
     978               4 :   return NS_OK;
     979                 : }
     980                 : 
     981              90 : void imgLoader::ReadAcceptHeaderPref()
     982                 : {
     983             180 :   nsAdoptingCString accept = Preferences::GetCString("image.http.accept");
     984              90 :   if (accept)
     985              90 :     mAcceptHeader = accept;
     986                 :   else
     987               0 :     mAcceptHeader = "image/png,image/*;q=0.8,*/*;q=0.5";
     988              90 : }
     989                 : 
     990                 : /* void clearCache (in boolean chrome); */
     991              52 : NS_IMETHODIMP imgLoader::ClearCache(bool chrome)
     992                 : {
     993              52 :   if (chrome)
     994               0 :     return ClearChromeImageCache();
     995                 :   else
     996              52 :     return ClearImageCache();
     997                 : }
     998                 : 
     999                 : /* void removeEntry(in nsIURI uri); */
    1000               0 : NS_IMETHODIMP imgLoader::RemoveEntry(nsIURI *uri)
    1001                 : {
    1002               0 :   if (RemoveFromCache(uri))
    1003               0 :     return NS_OK;
    1004                 : 
    1005               0 :   return NS_ERROR_NOT_AVAILABLE;
    1006                 : }
    1007                 : 
    1008                 : /* imgIRequest findEntry(in nsIURI uri); */
    1009               0 : NS_IMETHODIMP imgLoader::FindEntryProperties(nsIURI *uri, nsIProperties **_retval)
    1010                 : {
    1011               0 :   nsRefPtr<imgCacheEntry> entry;
    1012               0 :   nsCAutoString spec;
    1013               0 :   imgCacheTable &cache = GetCache(uri);
    1014                 : 
    1015               0 :   uri->GetSpec(spec);
    1016               0 :   *_retval = nsnull;
    1017                 : 
    1018               0 :   if (cache.Get(spec, getter_AddRefs(entry)) && entry) {
    1019               0 :     if (gCacheTracker && entry->HasNoProxies())
    1020               0 :       gCacheTracker->MarkUsed(entry);
    1021                 : 
    1022               0 :     nsRefPtr<imgRequest> request = getter_AddRefs(entry->GetRequest());
    1023               0 :     if (request) {
    1024               0 :       *_retval = request->Properties();
    1025               0 :       NS_ADDREF(*_retval);
    1026                 :     }
    1027                 :   }
    1028                 : 
    1029               0 :   return NS_OK;
    1030                 : }
    1031                 : 
    1032              93 : void imgLoader::Shutdown()
    1033                 : {
    1034              93 :   ClearChromeImageCache();
    1035              93 :   ClearImageCache();
    1036              93 :   NS_IF_RELEASE(gCacheObserver);
    1037              93 :   delete gCacheTracker;
    1038              93 :   gCacheTracker = nsnull;
    1039              93 : }
    1040                 : 
    1041              93 : nsresult imgLoader::ClearChromeImageCache()
    1042                 : {
    1043              93 :   return EvictEntries(sChromeCache);
    1044                 : }
    1045                 : 
    1046             147 : nsresult imgLoader::ClearImageCache()
    1047                 : {
    1048             147 :   return EvictEntries(sCache);
    1049                 : }
    1050                 : 
    1051               0 : void imgLoader::MinimizeCaches()
    1052                 : {
    1053               0 :   EvictEntries(sCacheQueue);
    1054               0 :   EvictEntries(sChromeCacheQueue);
    1055               0 : }
    1056                 : 
    1057               8 : bool imgLoader::PutIntoCache(nsIURI *key, imgCacheEntry *entry)
    1058                 : {
    1059               8 :   imgCacheTable &cache = GetCache(key);
    1060                 : 
    1061              16 :   nsCAutoString spec;
    1062               8 :   key->GetSpec(spec);
    1063                 : 
    1064               8 :   LOG_STATIC_FUNC_WITH_PARAM(gImgLog, "imgLoader::PutIntoCache", "uri", spec.get());
    1065                 : 
    1066                 :   // Check to see if this request already exists in the cache and is being
    1067                 :   // loaded on a different thread. If so, don't allow this entry to be added to
    1068                 :   // the cache.
    1069              16 :   nsRefPtr<imgCacheEntry> tmpCacheEntry;
    1070               8 :   if (cache.Get(spec, getter_AddRefs(tmpCacheEntry)) && tmpCacheEntry) {
    1071               0 :     PR_LOG(gImgLog, PR_LOG_DEBUG,
    1072                 :            ("[this=%p] imgLoader::PutIntoCache -- Element already in the cache", nsnull));
    1073               0 :     nsRefPtr<imgRequest> tmpRequest = getter_AddRefs(tmpCacheEntry->GetRequest());
    1074                 : 
    1075                 :     // If it already exists, and we're putting the same key into the cache, we
    1076                 :     // should remove the old version.
    1077               0 :     PR_LOG(gImgLog, PR_LOG_DEBUG,
    1078                 :            ("[this=%p] imgLoader::PutIntoCache -- Replacing cached element", nsnull));
    1079                 : 
    1080               0 :     RemoveFromCache(key);
    1081                 :   } else {
    1082               8 :     PR_LOG(gImgLog, PR_LOG_DEBUG,
    1083                 :            ("[this=%p] imgLoader::PutIntoCache -- Element NOT already in the cache", nsnull));
    1084                 :   }
    1085                 : 
    1086               8 :   if (!cache.Put(spec, entry))
    1087               0 :     return false;
    1088                 : 
    1089                 :   // We can be called to resurrect an evicted entry.
    1090               8 :   if (entry->Evicted())
    1091               8 :     entry->SetEvicted(false);
    1092                 : 
    1093                 :   // If we're resurrecting an entry with no proxies, put it back in the
    1094                 :   // tracker and queue.
    1095               8 :   if (entry->HasNoProxies()) {
    1096               8 :     nsresult addrv = NS_OK;
    1097                 : 
    1098               8 :     if (gCacheTracker)
    1099               8 :       addrv = gCacheTracker->AddObject(entry);
    1100                 : 
    1101               8 :     if (NS_SUCCEEDED(addrv)) {
    1102               8 :       imgCacheQueue &queue = GetCacheQueue(key);
    1103               8 :       queue.Push(entry);
    1104                 :     }
    1105                 :   }
    1106                 : 
    1107              16 :   nsRefPtr<imgRequest> request(getter_AddRefs(entry->GetRequest()));
    1108               8 :   request->SetIsInCache(true);
    1109                 : 
    1110               8 :   return true;
    1111                 : }
    1112                 : 
    1113               4 : bool imgLoader::SetHasNoProxies(nsIURI *key, imgCacheEntry *entry)
    1114                 : {
    1115                 : #if defined(PR_LOGGING)
    1116               8 :   nsCAutoString spec;
    1117               4 :   key->GetSpec(spec);
    1118                 : 
    1119               4 :   LOG_STATIC_FUNC_WITH_PARAM(gImgLog, "imgLoader::SetHasNoProxies", "uri", spec.get());
    1120                 : #endif
    1121                 : 
    1122               4 :   if (entry->Evicted())
    1123               2 :     return false;
    1124                 : 
    1125               2 :   imgCacheQueue &queue = GetCacheQueue(key);
    1126                 : 
    1127               2 :   nsresult addrv = NS_OK;
    1128                 : 
    1129               2 :   if (gCacheTracker)
    1130               2 :     addrv = gCacheTracker->AddObject(entry);
    1131                 : 
    1132               2 :   if (NS_SUCCEEDED(addrv)) {
    1133               2 :     queue.Push(entry);
    1134               2 :     entry->SetHasNoProxies(true);
    1135                 :   }
    1136                 : 
    1137               2 :   imgCacheTable &cache = GetCache(key);
    1138               2 :   CheckCacheLimits(cache, queue);
    1139                 : 
    1140               2 :   return true;
    1141                 : }
    1142                 : 
    1143               8 : bool imgLoader::SetHasProxies(nsIURI *key)
    1144                 : {
    1145               8 :   VerifyCacheSizes();
    1146                 : 
    1147               8 :   imgCacheTable &cache = GetCache(key);
    1148                 : 
    1149              16 :   nsCAutoString spec;
    1150               8 :   key->GetSpec(spec);
    1151                 : 
    1152               8 :   LOG_STATIC_FUNC_WITH_PARAM(gImgLog, "imgLoader::SetHasProxies", "uri", spec.get());
    1153                 : 
    1154              16 :   nsRefPtr<imgCacheEntry> entry;
    1155               8 :   if (cache.Get(spec, getter_AddRefs(entry)) && entry && entry->HasNoProxies()) {
    1156               8 :     imgCacheQueue &queue = GetCacheQueue(key);
    1157               8 :     queue.Remove(entry);
    1158                 : 
    1159               8 :     if (gCacheTracker)
    1160               8 :       gCacheTracker->RemoveObject(entry);
    1161                 : 
    1162               8 :     entry->SetHasNoProxies(false);
    1163                 : 
    1164               8 :     return true;
    1165                 :   }
    1166                 : 
    1167               0 :   return false;
    1168                 : }
    1169                 : 
    1170               0 : void imgLoader::CacheEntriesChanged(nsIURI *uri, PRInt32 sizediff /* = 0 */)
    1171                 : {
    1172               0 :   imgCacheQueue &queue = GetCacheQueue(uri);
    1173               0 :   queue.MarkDirty();
    1174               0 :   queue.UpdateSize(sizediff);
    1175               0 : }
    1176                 : 
    1177               2 : void imgLoader::CheckCacheLimits(imgCacheTable &cache, imgCacheQueue &queue)
    1178                 : {
    1179               2 :   if (queue.GetNumElements() == 0)
    1180               0 :     NS_ASSERTION(queue.GetSize() == 0, 
    1181                 :                  "imgLoader::CheckCacheLimits -- incorrect cache size");
    1182                 : 
    1183                 :   // Remove entries from the cache until we're back under our desired size.
    1184               4 :   while (queue.GetSize() >= sCacheMaxSize) {
    1185                 :     // Remove the first entry in the queue.
    1186               0 :     nsRefPtr<imgCacheEntry> entry(queue.Pop());
    1187                 : 
    1188               0 :     NS_ASSERTION(entry, "imgLoader::CheckCacheLimits -- NULL entry pointer");
    1189                 : 
    1190                 : #if defined(PR_LOGGING)
    1191               0 :     nsRefPtr<imgRequest> req(entry->GetRequest());
    1192               0 :     if (req) {
    1193               0 :       nsCOMPtr<nsIURI> uri;
    1194               0 :       req->GetURI(getter_AddRefs(uri));
    1195               0 :       nsCAutoString spec;
    1196               0 :       uri->GetSpec(spec);
    1197               0 :       LOG_STATIC_FUNC_WITH_PARAM(gImgLog, "imgLoader::CheckCacheLimits", "entry", spec.get());
    1198                 :     }
    1199                 : #endif
    1200                 : 
    1201               0 :     if (entry)
    1202               0 :       RemoveFromCache(entry);
    1203                 :   }
    1204               2 : }
    1205                 : 
    1206               0 : bool imgLoader::ValidateRequestWithNewChannel(imgRequest *request,
    1207                 :                                                 nsIURI *aURI,
    1208                 :                                                 nsIURI *aInitialDocumentURI,
    1209                 :                                                 nsIURI *aReferrerURI,
    1210                 :                                                 nsILoadGroup *aLoadGroup,
    1211                 :                                                 imgIDecoderObserver *aObserver,
    1212                 :                                                 nsISupports *aCX,
    1213                 :                                                 nsLoadFlags aLoadFlags,
    1214                 :                                                 imgIRequest *aExistingRequest,
    1215                 :                                                 imgIRequest **aProxyRequest,
    1216                 :                                                 nsIChannelPolicy *aPolicy,
    1217                 :                                                 nsIPrincipal* aLoadingPrincipal,
    1218                 :                                                 PRInt32 aCORSMode)
    1219                 : {
    1220                 :   // now we need to insert a new channel request object inbetween the real
    1221                 :   // request and the proxy that basically delays loading the image until it
    1222                 :   // gets a 304 or figures out that this needs to be a new request
    1223                 : 
    1224                 :   nsresult rv;
    1225                 : 
    1226                 :   // If we're currently in the middle of validating this request, just hand
    1227                 :   // back a proxy to it; the required work will be done for us.
    1228               0 :   if (request->mValidator) {
    1229                 :     rv = CreateNewProxyForRequest(request, aLoadGroup, aObserver,
    1230                 :                                   aLoadFlags, aExistingRequest, 
    1231               0 :                                   reinterpret_cast<imgIRequest **>(aProxyRequest));
    1232               0 :     if (NS_FAILED(rv)) {
    1233               0 :       return false;
    1234                 :     }
    1235                 : 
    1236               0 :     if (*aProxyRequest) {
    1237               0 :       imgRequestProxy* proxy = static_cast<imgRequestProxy*>(*aProxyRequest);
    1238                 : 
    1239                 :       // We will send notifications from imgCacheValidator::OnStartRequest().
    1240                 :       // In the mean time, we must defer notifications because we are added to
    1241                 :       // the imgRequest's proxy list, and we can get extra notifications
    1242                 :       // resulting from methods such as RequestDecode(). See bug 579122.
    1243               0 :       proxy->SetNotificationsDeferred(true);
    1244                 : 
    1245                 :       // Attach the proxy without notifying
    1246               0 :       request->mValidator->AddProxy(proxy);
    1247                 :     }
    1248                 : 
    1249               0 :     return NS_SUCCEEDED(rv);
    1250                 : 
    1251                 :   } else {
    1252                 :     // We will rely on Necko to cache this request when it's possible, and to
    1253                 :     // tell imgCacheValidator::OnStartRequest whether the request came from its
    1254                 :     // cache.
    1255               0 :     nsCOMPtr<nsIChannel> newChannel;
    1256                 :     bool forcePrincipalCheck;
    1257               0 :     rv = NewImageChannel(getter_AddRefs(newChannel),
    1258                 :                          &forcePrincipalCheck,
    1259                 :                          aURI,
    1260                 :                          aInitialDocumentURI,
    1261                 :                          aReferrerURI,
    1262                 :                          aLoadGroup,
    1263                 :                          mAcceptHeader,
    1264                 :                          aLoadFlags,
    1265                 :                          aPolicy,
    1266               0 :                          aLoadingPrincipal);
    1267               0 :     if (NS_FAILED(rv)) {
    1268               0 :       return false;
    1269                 :     }
    1270                 : 
    1271               0 :     nsCOMPtr<imgIRequest> req;
    1272                 :     rv = CreateNewProxyForRequest(request, aLoadGroup, aObserver,
    1273               0 :                                   aLoadFlags, aExistingRequest, getter_AddRefs(req));
    1274               0 :     if (NS_FAILED(rv)) {
    1275               0 :       return false;
    1276                 :     }
    1277                 : 
    1278                 :     // Make sure that OnStatus/OnProgress calls have the right request set...
    1279                 :     nsRefPtr<nsProgressNotificationProxy> progressproxy =
    1280               0 :         new nsProgressNotificationProxy(newChannel, req);
    1281               0 :     if (!progressproxy)
    1282               0 :       return false;
    1283                 : 
    1284                 :     nsRefPtr<imgCacheValidator> hvc =
    1285               0 :       new imgCacheValidator(progressproxy, request, aCX, forcePrincipalCheck);
    1286                 : 
    1287               0 :     nsCOMPtr<nsIStreamListener> listener = hvc.get();
    1288                 : 
    1289                 :     // We must set the notification callbacks before setting up the
    1290                 :     // CORS listener, because that's also interested inthe
    1291                 :     // notification callbacks.
    1292               0 :     newChannel->SetNotificationCallbacks(hvc);
    1293                 : 
    1294               0 :     if (aCORSMode != imgIRequest::CORS_NONE) {
    1295               0 :       bool withCredentials = aCORSMode == imgIRequest::CORS_USE_CREDENTIALS;
    1296                 :       nsCOMPtr<nsIStreamListener> corsproxy =
    1297               0 :         new nsCORSListenerProxy(hvc, aLoadingPrincipal, newChannel, withCredentials, &rv);
    1298               0 :       if (NS_FAILED(rv)) {
    1299               0 :         return false;
    1300                 :       }
    1301                 : 
    1302               0 :       listener = corsproxy;
    1303                 :     }
    1304                 : 
    1305               0 :     request->mValidator = hvc;
    1306                 : 
    1307                 :     imgRequestProxy* proxy = static_cast<imgRequestProxy*>
    1308               0 :                                (static_cast<imgIRequest*>(req.get()));
    1309                 : 
    1310                 :     // We will send notifications from imgCacheValidator::OnStartRequest().
    1311                 :     // In the mean time, we must defer notifications because we are added to
    1312                 :     // the imgRequest's proxy list, and we can get extra notifications
    1313                 :     // resulting from methods such as RequestDecode(). See bug 579122.
    1314               0 :     proxy->SetNotificationsDeferred(true);
    1315                 : 
    1316                 :     // Add the proxy without notifying
    1317               0 :     hvc->AddProxy(proxy);
    1318                 : 
    1319               0 :     rv = newChannel->AsyncOpen(listener, nsnull);
    1320               0 :     if (NS_SUCCEEDED(rv))
    1321               0 :       NS_ADDREF(*aProxyRequest = req.get());
    1322                 : 
    1323               0 :     return NS_SUCCEEDED(rv);
    1324                 :   }
    1325                 : }
    1326                 : 
    1327               7 : bool imgLoader::ValidateEntry(imgCacheEntry *aEntry,
    1328                 :                                 nsIURI *aURI,
    1329                 :                                 nsIURI *aInitialDocumentURI,
    1330                 :                                 nsIURI *aReferrerURI,
    1331                 :                                 nsILoadGroup *aLoadGroup,
    1332                 :                                 imgIDecoderObserver *aObserver,
    1333                 :                                 nsISupports *aCX,
    1334                 :                                 nsLoadFlags aLoadFlags,
    1335                 :                                 bool aCanMakeNewChannel,
    1336                 :                                 imgIRequest *aExistingRequest,
    1337                 :                                 imgIRequest **aProxyRequest,
    1338                 :                                 nsIChannelPolicy *aPolicy,
    1339                 :                                 nsIPrincipal* aLoadingPrincipal,
    1340                 :                                 PRInt32 aCORSMode)
    1341                 : {
    1342              14 :   LOG_SCOPE(gImgLog, "imgLoader::ValidateEntry");
    1343                 : 
    1344                 :   bool hasExpired;
    1345               7 :   PRUint32 expirationTime = aEntry->GetExpiryTime();
    1346               7 :   if (expirationTime <= SecondsFromPRTime(PR_Now())) {
    1347               7 :     hasExpired = true;
    1348                 :   } else {
    1349               0 :     hasExpired = false;
    1350                 :   }
    1351                 : 
    1352                 :   nsresult rv;
    1353                 : 
    1354                 :   // Special treatment for file URLs - aEntry has expired if file has changed
    1355              14 :   nsCOMPtr<nsIFileURL> fileUrl(do_QueryInterface(aURI));
    1356               7 :   if (fileUrl) {
    1357               0 :     PRUint32 lastModTime = aEntry->GetTouchedTime();
    1358                 : 
    1359               0 :     nsCOMPtr<nsIFile> theFile;
    1360               0 :     rv = fileUrl->GetFile(getter_AddRefs(theFile));
    1361               0 :     if (NS_SUCCEEDED(rv)) {
    1362                 :       PRInt64 fileLastMod;
    1363               0 :       rv = theFile->GetLastModifiedTime(&fileLastMod);
    1364               0 :       if (NS_SUCCEEDED(rv)) {
    1365                 :         // nsIFile uses millisec, NSPR usec
    1366               0 :         fileLastMod *= 1000;
    1367               0 :         hasExpired = SecondsFromPRTime((PRTime)fileLastMod) > lastModTime;
    1368                 :       }
    1369                 :     }
    1370                 :   }
    1371                 : 
    1372              14 :   nsRefPtr<imgRequest> request(aEntry->GetRequest());
    1373                 : 
    1374               7 :   if (!request)
    1375               0 :     return false;
    1376                 : 
    1377              14 :   if (!ValidateCORSAndPrincipal(request, aEntry->ForcePrincipalCheck(),
    1378               7 :                                 aCORSMode, aLoadingPrincipal))
    1379               0 :     return false;
    1380                 : 
    1381               7 :   bool validateRequest = false;
    1382                 : 
    1383                 :   // If the request's loadId is the same as the aCX, then it is ok to use
    1384                 :   // this one because it has already been validated for this context.
    1385                 :   //
    1386                 :   // XXX: nsnull seems to be a 'special' key value that indicates that NO
    1387                 :   //      validation is required.
    1388                 :   //
    1389               7 :   void *key = (void *)aCX;
    1390               7 :   if (request->mLoadId != key) {
    1391                 :     // If we would need to revalidate this entry, but we're being told to
    1392                 :     // bypass the cache, we don't allow this entry to be used.
    1393               0 :     if (aLoadFlags & nsIRequest::LOAD_BYPASS_CACHE)
    1394               0 :       return false;
    1395                 : 
    1396                 :     // Determine whether the cache aEntry must be revalidated...
    1397               0 :     validateRequest = ShouldRevalidateEntry(aEntry, aLoadFlags, hasExpired);
    1398                 : 
    1399               0 :     PR_LOG(gImgLog, PR_LOG_DEBUG,
    1400                 :            ("imgLoader::ValidateEntry validating cache entry. " 
    1401                 :             "validateRequest = %d", validateRequest));
    1402                 :   }
    1403                 : #if defined(PR_LOGGING)
    1404               7 :   else if (!key) {
    1405              14 :     nsCAutoString spec;
    1406               7 :     aURI->GetSpec(spec);
    1407                 : 
    1408               7 :     PR_LOG(gImgLog, PR_LOG_DEBUG,
    1409                 :            ("imgLoader::ValidateEntry BYPASSING cache validation for %s " 
    1410                 :             "because of NULL LoadID", spec.get()));
    1411                 :   }
    1412                 : #endif
    1413                 : 
    1414                 :   // We can't use a cached request if it comes from a different
    1415                 :   // application cache than this load is expecting.
    1416              14 :   nsCOMPtr<nsIApplicationCacheContainer> appCacheContainer;
    1417              14 :   nsCOMPtr<nsIApplicationCache> requestAppCache;
    1418              14 :   nsCOMPtr<nsIApplicationCache> groupAppCache;
    1419               7 :   if ((appCacheContainer = do_GetInterface(request->mRequest)))
    1420               0 :     appCacheContainer->GetApplicationCache(getter_AddRefs(requestAppCache));
    1421               7 :   if ((appCacheContainer = do_QueryInterface(aLoadGroup)))
    1422               0 :     appCacheContainer->GetApplicationCache(getter_AddRefs(groupAppCache));
    1423                 : 
    1424               7 :   if (requestAppCache != groupAppCache) {
    1425               0 :     PR_LOG(gImgLog, PR_LOG_DEBUG,
    1426                 :            ("imgLoader::ValidateEntry - Unable to use cached imgRequest "
    1427                 :             "[request=%p] because of mismatched application caches\n",
    1428                 :             address_of(request)));
    1429               0 :     return false;
    1430                 :   }
    1431                 : 
    1432               7 :   if (validateRequest && aCanMakeNewChannel) {
    1433               0 :     LOG_SCOPE(gImgLog, "imgLoader::ValidateRequest |cache hit| must validate");
    1434                 : 
    1435                 :     return ValidateRequestWithNewChannel(request, aURI, aInitialDocumentURI,
    1436                 :                                          aReferrerURI, aLoadGroup, aObserver,
    1437                 :                                          aCX, aLoadFlags, aExistingRequest,
    1438                 :                                          aProxyRequest, aPolicy,
    1439               0 :                                          aLoadingPrincipal, aCORSMode);
    1440                 :   }
    1441                 : 
    1442               7 :   return !validateRequest;
    1443                 : }
    1444                 : 
    1445                 : 
    1446               0 : bool imgLoader::RemoveFromCache(nsIURI *aKey)
    1447                 : {
    1448               0 :   if (!aKey) return false;
    1449                 : 
    1450               0 :   imgCacheTable &cache = GetCache(aKey);
    1451               0 :   imgCacheQueue &queue = GetCacheQueue(aKey);
    1452                 : 
    1453               0 :   nsCAutoString spec;
    1454               0 :   aKey->GetSpec(spec);
    1455                 : 
    1456               0 :   LOG_STATIC_FUNC_WITH_PARAM(gImgLog, "imgLoader::RemoveFromCache", "uri", spec.get());
    1457                 : 
    1458               0 :   nsRefPtr<imgCacheEntry> entry;
    1459               0 :   if (cache.Get(spec, getter_AddRefs(entry)) && entry) {
    1460               0 :     cache.Remove(spec);
    1461                 : 
    1462               0 :     NS_ABORT_IF_FALSE(!entry->Evicted(), "Evicting an already-evicted cache entry!");
    1463                 : 
    1464                 :     // Entries with no proxies are in the tracker.
    1465               0 :     if (entry->HasNoProxies()) {
    1466               0 :       if (gCacheTracker)
    1467               0 :         gCacheTracker->RemoveObject(entry);
    1468               0 :       queue.Remove(entry);
    1469                 :     }
    1470                 : 
    1471               0 :     entry->SetEvicted(true);
    1472                 : 
    1473               0 :     nsRefPtr<imgRequest> request(getter_AddRefs(entry->GetRequest()));
    1474               0 :     request->SetIsInCache(false);
    1475                 : 
    1476               0 :     return true;
    1477                 :   }
    1478                 :   else
    1479               0 :     return false;
    1480                 : }
    1481                 : 
    1482               8 : bool imgLoader::RemoveFromCache(imgCacheEntry *entry)
    1483                 : {
    1484               8 :   LOG_STATIC_FUNC(gImgLog, "imgLoader::RemoveFromCache entry");
    1485                 : 
    1486              16 :   nsRefPtr<imgRequest> request(getter_AddRefs(entry->GetRequest()));
    1487               8 :   if (request) {
    1488              16 :     nsCOMPtr<nsIURI> key;
    1489               8 :     if (NS_SUCCEEDED(request->GetURI(getter_AddRefs(key))) && key) {
    1490               8 :       imgCacheTable &cache = GetCache(key);
    1491               8 :       imgCacheQueue &queue = GetCacheQueue(key);
    1492              16 :       nsCAutoString spec;
    1493               8 :       key->GetSpec(spec);
    1494                 : 
    1495               8 :       LOG_STATIC_FUNC_WITH_PARAM(gImgLog, "imgLoader::RemoveFromCache", "entry's uri", spec.get());
    1496                 : 
    1497               8 :       cache.Remove(spec);
    1498                 : 
    1499               8 :       if (entry->HasNoProxies()) {
    1500               2 :         LOG_STATIC_FUNC(gImgLog, "imgLoader::RemoveFromCache removing from tracker");
    1501               2 :         if (gCacheTracker)
    1502               2 :           gCacheTracker->RemoveObject(entry);
    1503               2 :         queue.Remove(entry);
    1504                 :       }
    1505                 : 
    1506               8 :       entry->SetEvicted(true);
    1507               8 :       request->SetIsInCache(false);
    1508                 : 
    1509               8 :       return true;
    1510                 :     }
    1511                 :   }
    1512                 : 
    1513               0 :   return false;
    1514                 : }
    1515                 : 
    1516               4 : static PLDHashOperator EnumEvictEntries(const nsACString&, 
    1517                 :                                         nsRefPtr<imgCacheEntry> &aData,
    1518                 :                                         void *data)
    1519                 : {
    1520                 :   nsTArray<nsRefPtr<imgCacheEntry> > *entries = 
    1521               4 :     reinterpret_cast<nsTArray<nsRefPtr<imgCacheEntry> > *>(data);
    1522                 : 
    1523               4 :   entries->AppendElement(aData);
    1524                 : 
    1525               4 :   return PL_DHASH_NEXT;
    1526                 : }
    1527                 : 
    1528             240 : nsresult imgLoader::EvictEntries(imgCacheTable &aCacheToClear)
    1529                 : {
    1530             240 :   LOG_STATIC_FUNC(gImgLog, "imgLoader::EvictEntries table");
    1531                 : 
    1532                 :   // We have to make a temporary, since RemoveFromCache removes the element
    1533                 :   // from the queue, invalidating iterators.
    1534             480 :   nsTArray<nsRefPtr<imgCacheEntry> > entries;
    1535             240 :   aCacheToClear.Enumerate(EnumEvictEntries, &entries);
    1536                 : 
    1537             244 :   for (PRUint32 i = 0; i < entries.Length(); ++i)
    1538               4 :     if (!RemoveFromCache(entries[i]))
    1539               0 :       return NS_ERROR_FAILURE;
    1540                 : 
    1541             240 :   return NS_OK;
    1542                 : }
    1543                 : 
    1544               0 : nsresult imgLoader::EvictEntries(imgCacheQueue &aQueueToClear)
    1545                 : {
    1546               0 :   LOG_STATIC_FUNC(gImgLog, "imgLoader::EvictEntries queue");
    1547                 : 
    1548                 :   // We have to make a temporary, since RemoveFromCache removes the element
    1549                 :   // from the queue, invalidating iterators.
    1550               0 :   nsTArray<nsRefPtr<imgCacheEntry> > entries(aQueueToClear.GetNumElements());
    1551               0 :   for (imgCacheQueue::const_iterator i = aQueueToClear.begin(); i != aQueueToClear.end(); ++i)
    1552               0 :     entries.AppendElement(*i);
    1553                 : 
    1554               0 :   for (PRUint32 i = 0; i < entries.Length(); ++i)
    1555               0 :     if (!RemoveFromCache(entries[i]))
    1556               0 :       return NS_ERROR_FAILURE;
    1557                 : 
    1558               0 :   return NS_OK;
    1559                 : }
    1560                 : 
    1561                 : #define LOAD_FLAGS_CACHE_MASK    (nsIRequest::LOAD_BYPASS_CACHE | \
    1562                 :                                   nsIRequest::LOAD_FROM_CACHE)
    1563                 : 
    1564                 : #define LOAD_FLAGS_VALIDATE_MASK (nsIRequest::VALIDATE_ALWAYS |   \
    1565                 :                                   nsIRequest::VALIDATE_NEVER |    \
    1566                 :                                   nsIRequest::VALIDATE_ONCE_PER_SESSION)
    1567                 : 
    1568                 : 
    1569                 : /* imgIRequest loadImage (in nsIURI aURI, in nsIURI initialDocumentURI, in nsIPrincipal loadingPrincipal, in nsILoadGroup aLoadGroup, in imgIDecoderObserver aObserver, in nsISupports aCX, in nsLoadFlags aLoadFlags, in nsISupports cacheKey, in imgIRequest aRequest); */
    1570                 : 
    1571               9 : NS_IMETHODIMP imgLoader::LoadImage(nsIURI *aURI, 
    1572                 :                                    nsIURI *aInitialDocumentURI,
    1573                 :                                    nsIURI *aReferrerURI,
    1574                 :                                    nsIPrincipal* aLoadingPrincipal,
    1575                 :                                    nsILoadGroup *aLoadGroup,
    1576                 :                                    imgIDecoderObserver *aObserver,
    1577                 :                                    nsISupports *aCX,
    1578                 :                                    nsLoadFlags aLoadFlags,
    1579                 :                                    nsISupports *aCacheKey,
    1580                 :                                    imgIRequest *aRequest,
    1581                 :                                    nsIChannelPolicy *aPolicy,
    1582                 :                                    imgIRequest **_retval)
    1583                 : {
    1584               9 :   VerifyCacheSizes();
    1585                 : 
    1586               9 :   NS_ASSERTION(aURI, "imgLoader::LoadImage -- NULL URI pointer");
    1587                 : 
    1588               9 :   if (!aURI)
    1589               0 :     return NS_ERROR_NULL_POINTER;
    1590                 : 
    1591              18 :   nsCAutoString spec;
    1592               9 :   aURI->GetSpec(spec);
    1593              18 :   LOG_SCOPE_WITH_PARAM(gImgLog, "imgLoader::LoadImage", "aURI", spec.get());
    1594                 : 
    1595               9 :   *_retval = nsnull;
    1596                 : 
    1597              18 :   nsRefPtr<imgRequest> request;
    1598                 : 
    1599                 :   nsresult rv;
    1600               9 :   nsLoadFlags requestFlags = nsIRequest::LOAD_NORMAL;
    1601                 : 
    1602                 :   // Get the default load flags from the loadgroup (if possible)...
    1603               9 :   if (aLoadGroup) {
    1604               0 :     aLoadGroup->GetLoadFlags(&requestFlags);
    1605                 :   }
    1606                 :   //
    1607                 :   // Merge the default load flags with those passed in via aLoadFlags.
    1608                 :   // Currently, *only* the caching, validation and background load flags
    1609                 :   // are merged...
    1610                 :   //
    1611                 :   // The flags in aLoadFlags take precedence over the default flags!
    1612                 :   //
    1613               9 :   if (aLoadFlags & LOAD_FLAGS_CACHE_MASK) {
    1614                 :     // Override the default caching flags...
    1615                 :     requestFlags = (requestFlags & ~LOAD_FLAGS_CACHE_MASK) |
    1616               0 :                    (aLoadFlags & LOAD_FLAGS_CACHE_MASK);
    1617                 :   }
    1618               9 :   if (aLoadFlags & LOAD_FLAGS_VALIDATE_MASK) {
    1619                 :     // Override the default validation flags...
    1620                 :     requestFlags = (requestFlags & ~LOAD_FLAGS_VALIDATE_MASK) |
    1621               0 :                    (aLoadFlags & LOAD_FLAGS_VALIDATE_MASK);
    1622                 :   }
    1623               9 :   if (aLoadFlags & nsIRequest::LOAD_BACKGROUND) {
    1624                 :     // Propagate background loading...
    1625               0 :     requestFlags |= nsIRequest::LOAD_BACKGROUND;
    1626                 :   }
    1627                 : 
    1628               9 :   PRInt32 corsmode = imgIRequest::CORS_NONE;
    1629               9 :   if (aLoadFlags & imgILoader::LOAD_CORS_ANONYMOUS) {
    1630               0 :     corsmode = imgIRequest::CORS_ANONYMOUS;
    1631               9 :   } else if (aLoadFlags & imgILoader::LOAD_CORS_USE_CREDENTIALS) {
    1632               0 :     corsmode = imgIRequest::CORS_USE_CREDENTIALS;
    1633                 :   }
    1634                 : 
    1635              18 :   nsRefPtr<imgCacheEntry> entry;
    1636                 : 
    1637                 :   // Look in the cache for our URI, and then validate it.
    1638                 :   // XXX For now ignore aCacheKey. We will need it in the future
    1639                 :   // for correctly dealing with image load requests that are a result
    1640                 :   // of post data.
    1641               9 :   imgCacheTable &cache = GetCache(aURI);
    1642                 : 
    1643               9 :   if (cache.Get(spec, getter_AddRefs(entry)) && entry) {
    1644               5 :     if (ValidateEntry(entry, aURI, aInitialDocumentURI, aReferrerURI,
    1645                 :                       aLoadGroup, aObserver, aCX, requestFlags, true,
    1646               5 :                       aRequest, _retval, aPolicy, aLoadingPrincipal, corsmode)) {
    1647               5 :       request = getter_AddRefs(entry->GetRequest());
    1648                 : 
    1649                 :       // If this entry has no proxies, its request has no reference to the entry.
    1650               5 :       if (entry->HasNoProxies()) {
    1651               0 :         LOG_FUNC_WITH_PARAM(gImgLog, "imgLoader::LoadImage() adding proxyless entry", "uri", spec.get());
    1652               0 :         NS_ABORT_IF_FALSE(!request->HasCacheEntry(), "Proxyless entry's request has cache entry!");
    1653               0 :         request->SetCacheEntry(entry);
    1654                 : 
    1655               0 :         if (gCacheTracker)
    1656               0 :           gCacheTracker->MarkUsed(entry);
    1657                 :       } 
    1658                 : 
    1659               5 :       entry->Touch();
    1660                 : 
    1661                 : #ifdef DEBUG_joe
    1662                 :       printf("CACHEGET: %d %s %d\n", time(NULL), spec.get(), entry->SizeOfData());
    1663                 : #endif
    1664                 :     }
    1665                 :     else {
    1666                 :       // We can't use this entry. We'll try to load it off the network, and if
    1667                 :       // successful, overwrite the old entry in the cache with a new one.
    1668               0 :       entry = nsnull;
    1669                 :     }
    1670                 :   }
    1671                 : 
    1672                 :   // Keep the channel in this scope, so we can adjust its notificationCallbacks
    1673                 :   // later when we create the proxy.
    1674              18 :   nsCOMPtr<nsIChannel> newChannel;
    1675                 :   // If we didn't get a cache hit, we need to load from the network.
    1676               9 :   if (!request) {
    1677               8 :     LOG_SCOPE(gImgLog, "imgLoader::LoadImage |cache miss|");
    1678                 : 
    1679                 :     bool forcePrincipalCheck;
    1680               4 :     rv = NewImageChannel(getter_AddRefs(newChannel),
    1681                 :                          &forcePrincipalCheck,
    1682                 :                          aURI,
    1683                 :                          aInitialDocumentURI,
    1684                 :                          aReferrerURI,
    1685                 :                          aLoadGroup,
    1686                 :                          mAcceptHeader,
    1687                 :                          requestFlags,
    1688                 :                          aPolicy,
    1689               4 :                          aLoadingPrincipal);
    1690               4 :     if (NS_FAILED(rv))
    1691               0 :       return NS_ERROR_FAILURE;
    1692                 : 
    1693                 :     NewRequestAndEntry(forcePrincipalCheck, getter_AddRefs(request),
    1694               4 :                        getter_AddRefs(entry));
    1695                 : 
    1696               4 :     PR_LOG(gImgLog, PR_LOG_DEBUG,
    1697                 :            ("[this=%p] imgLoader::LoadImage -- Created new imgRequest [request=%p]\n", this, request.get()));
    1698                 : 
    1699                 :     // Create a loadgroup for this new channel.  This way if the channel
    1700                 :     // is redirected, we'll have a way to cancel the resulting channel.
    1701                 :     nsCOMPtr<nsILoadGroup> loadGroup =
    1702               8 :         do_CreateInstance(NS_LOADGROUP_CONTRACTID);
    1703               4 :     newChannel->SetLoadGroup(loadGroup);
    1704                 : 
    1705                 :     request->Init(aURI, aURI, loadGroup, newChannel, entry, aCX,
    1706               4 :                   aLoadingPrincipal, corsmode);
    1707                 : 
    1708                 :     // Pass the inner window ID of the loading document, if possible.
    1709               8 :     nsCOMPtr<nsIDocument> doc = do_QueryInterface(aCX);
    1710               4 :     if (doc) {
    1711               0 :       request->SetInnerWindowID(doc->InnerWindowID());
    1712                 :     }
    1713                 : 
    1714                 :     // create the proxy listener
    1715              12 :     nsCOMPtr<nsIStreamListener> pl = new ProxyListener(request.get());
    1716                 : 
    1717                 :     // See if we need to insert a CORS proxy between the proxy listener and the
    1718                 :     // request.
    1719               8 :     nsCOMPtr<nsIStreamListener> listener = pl;
    1720               4 :     if (corsmode != imgIRequest::CORS_NONE) {
    1721               0 :       PR_LOG(gImgLog, PR_LOG_DEBUG,
    1722                 :              ("[this=%p] imgLoader::LoadImage -- Setting up a CORS load",
    1723                 :               this));
    1724               0 :       bool withCredentials = corsmode == imgIRequest::CORS_USE_CREDENTIALS;
    1725                 : 
    1726                 :       nsCOMPtr<nsIStreamListener> corsproxy =
    1727                 :         new nsCORSListenerProxy(pl, aLoadingPrincipal, newChannel,
    1728               0 :                                 withCredentials, &rv);
    1729               0 :       if (NS_FAILED(rv)) {
    1730               0 :         PR_LOG(gImgLog, PR_LOG_DEBUG,
    1731                 :                ("[this=%p] imgLoader::LoadImage -- nsCORSListenerProxy "
    1732                 :                 "creation failed: 0x%x\n", this, rv));
    1733               0 :         request->CancelAndAbort(rv);
    1734               0 :         return NS_ERROR_FAILURE;
    1735                 :       }
    1736                 : 
    1737               0 :       listener = corsproxy;
    1738                 :     }
    1739                 : 
    1740               4 :     PR_LOG(gImgLog, PR_LOG_DEBUG,
    1741                 :            ("[this=%p] imgLoader::LoadImage -- Calling channel->AsyncOpen()\n", this));
    1742                 : 
    1743               4 :     nsresult openRes = newChannel->AsyncOpen(listener, nsnull);
    1744                 : 
    1745               4 :     if (NS_FAILED(openRes)) {
    1746               0 :       PR_LOG(gImgLog, PR_LOG_DEBUG,
    1747                 :              ("[this=%p] imgLoader::LoadImage -- AsyncOpen() failed: 0x%x\n",
    1748                 :               this, openRes));
    1749               0 :       request->CancelAndAbort(openRes);
    1750               0 :       return openRes;
    1751                 :     }
    1752                 : 
    1753                 :     // Try to add the new request into the cache.
    1754               8 :     PutIntoCache(aURI, entry);
    1755                 :   } else {
    1756              10 :     LOG_MSG_WITH_PARAM(gImgLog, 
    1757               5 :                        "imgLoader::LoadImage |cache hit|", "request", request);
    1758                 :   }
    1759                 : 
    1760                 : 
    1761                 :   // If we didn't get a proxy when validating the cache entry, we need to create one.
    1762               9 :   if (!*_retval) {
    1763                 :     // ValidateEntry() has three return values: "Is valid," "might be valid --
    1764                 :     // validating over network", and "not valid." If we don't have a _retval,
    1765                 :     // we know ValidateEntry is not validating over the network, so it's safe
    1766                 :     // to SetLoadId here because we know this request is valid for this context.
    1767                 :     //
    1768                 :     // Note, however, that this doesn't guarantee the behaviour we want (one
    1769                 :     // URL maps to the same image on a page) if we load the same image in a
    1770                 :     // different tab (see bug 528003), because its load id will get re-set, and
    1771                 :     // that'll cause us to validate over the network.
    1772               9 :     request->SetLoadId(aCX);
    1773                 : 
    1774               9 :     LOG_MSG(gImgLog, "imgLoader::LoadImage", "creating proxy request.");
    1775                 :     rv = CreateNewProxyForRequest(request, aLoadGroup, aObserver,
    1776               9 :                                   requestFlags, aRequest, _retval);
    1777               9 :     if (NS_FAILED(rv)) {
    1778               0 :       return rv;
    1779                 :     }
    1780                 : 
    1781               9 :     imgRequestProxy *proxy = static_cast<imgRequestProxy *>(*_retval);
    1782                 : 
    1783                 :     // Make sure that OnStatus/OnProgress calls have the right request set, if
    1784                 :     // we did create a channel here.
    1785               9 :     if (newChannel) {
    1786                 :       nsCOMPtr<nsIInterfaceRequestor> requestor(
    1787              12 :           new nsProgressNotificationProxy(newChannel, proxy));
    1788               4 :       if (!requestor)
    1789               0 :         return NS_ERROR_OUT_OF_MEMORY;
    1790               8 :       newChannel->SetNotificationCallbacks(requestor);
    1791                 :     }
    1792                 : 
    1793                 :     // Note that it's OK to add here even if the request is done.  If it is,
    1794                 :     // it'll send a OnStopRequest() to the proxy in imgRequestProxy::Notify and
    1795                 :     // the proxy will be removed from the loadgroup.
    1796               9 :     proxy->AddToLoadGroup();
    1797                 : 
    1798                 :     // If we're loading off the network, explicitly don't notify our proxy,
    1799                 :     // because necko (or things called from necko, such as imgCacheValidator)
    1800                 :     // are going to call our notifications asynchronously, and we can't make it
    1801                 :     // further asynchronous because observers might rely on imagelib completing
    1802                 :     // its work between the channel's OnStartRequest and OnStopRequest.
    1803               9 :     if (!newChannel)
    1804               5 :       proxy->NotifyListener();
    1805                 : 
    1806               9 :     return rv;
    1807                 :   }
    1808                 : 
    1809               0 :   NS_ASSERTION(*_retval, "imgLoader::LoadImage -- no return value");
    1810                 : 
    1811               0 :   return NS_OK;
    1812                 : }
    1813                 : 
    1814                 : /* imgIRequest loadImageWithChannel(in nsIChannel channel, in imgIDecoderObserver aObserver, in nsISupports cx, out nsIStreamListener); */
    1815               6 : NS_IMETHODIMP imgLoader::LoadImageWithChannel(nsIChannel *channel, imgIDecoderObserver *aObserver, nsISupports *aCX, nsIStreamListener **listener, imgIRequest **_retval)
    1816                 : {
    1817               6 :   NS_ASSERTION(channel, "imgLoader::LoadImageWithChannel -- NULL channel pointer");
    1818                 : 
    1819              12 :   nsRefPtr<imgRequest> request;
    1820                 : 
    1821              12 :   nsCOMPtr<nsIURI> uri;
    1822               6 :   channel->GetURI(getter_AddRefs(uri));
    1823                 : 
    1824               6 :   nsLoadFlags requestFlags = nsIRequest::LOAD_NORMAL;
    1825               6 :   channel->GetLoadFlags(&requestFlags);
    1826                 : 
    1827              12 :   nsRefPtr<imgCacheEntry> entry;
    1828                 : 
    1829               6 :   if (requestFlags & nsIRequest::LOAD_BYPASS_CACHE) {
    1830               0 :     RemoveFromCache(uri);
    1831                 :   } else {
    1832                 :     // Look in the cache for our URI, and then validate it.
    1833                 :     // XXX For now ignore aCacheKey. We will need it in the future
    1834                 :     // for correctly dealing with image load requests that are a result
    1835                 :     // of post data.
    1836               6 :     imgCacheTable &cache = GetCache(uri);
    1837              12 :     nsCAutoString spec;
    1838                 : 
    1839               6 :     uri->GetSpec(spec);
    1840                 : 
    1841               6 :     if (cache.Get(spec, getter_AddRefs(entry)) && entry) {
    1842                 :       // We don't want to kick off another network load. So we ask
    1843                 :       // ValidateEntry to only do validation without creating a new proxy. If
    1844                 :       // it says that the entry isn't valid any more, we'll only use the entry
    1845                 :       // we're getting if the channel is loading from the cache anyways.
    1846                 :       //
    1847                 :       // XXX -- should this be changed? it's pretty much verbatim from the old
    1848                 :       // code, but seems nonsensical.
    1849               2 :       if (ValidateEntry(entry, uri, nsnull, nsnull, nsnull, aObserver, aCX,
    1850                 :                         requestFlags, false, nsnull, nsnull, nsnull,
    1851               2 :                         nsnull, imgIRequest::CORS_NONE)) {
    1852               2 :         request = getter_AddRefs(entry->GetRequest());
    1853                 :       } else {
    1854               0 :         nsCOMPtr<nsICachingChannel> cacheChan(do_QueryInterface(channel));
    1855                 :         bool bUseCacheCopy;
    1856                 : 
    1857               0 :         if (cacheChan)
    1858               0 :           cacheChan->IsFromCache(&bUseCacheCopy);
    1859                 :         else
    1860               0 :           bUseCacheCopy = false;
    1861                 : 
    1862               0 :         if (!bUseCacheCopy)
    1863               0 :           entry = nsnull;
    1864                 :         else {
    1865               0 :           request = getter_AddRefs(entry->GetRequest());
    1866                 :         }
    1867                 :       }
    1868                 : 
    1869               2 :       if (request && entry) {
    1870                 :         // If this entry has no proxies, its request has no reference to the entry.
    1871               2 :         if (entry->HasNoProxies()) {
    1872               0 :           LOG_FUNC_WITH_PARAM(gImgLog, "imgLoader::LoadImageWithChannel() adding proxyless entry", "uri", spec.get());
    1873               0 :           NS_ABORT_IF_FALSE(!request->HasCacheEntry(), "Proxyless entry's request has cache entry!");
    1874               0 :           request->SetCacheEntry(entry);
    1875                 : 
    1876               0 :           if (gCacheTracker)
    1877               0 :             gCacheTracker->MarkUsed(entry);
    1878                 :         } 
    1879                 :       }
    1880                 :     }
    1881                 :   }
    1882                 : 
    1883              12 :   nsCOMPtr<nsILoadGroup> loadGroup;
    1884               6 :   channel->GetLoadGroup(getter_AddRefs(loadGroup));
    1885                 : 
    1886                 :   // Filter out any load flags not from nsIRequest
    1887               6 :   requestFlags &= nsIRequest::LOAD_REQUESTMASK;
    1888                 : 
    1889               6 :   nsresult rv = NS_OK;
    1890               6 :   if (request) {
    1891                 :     // we have this in our cache already.. cancel the current (document) load
    1892                 : 
    1893               2 :     channel->Cancel(NS_ERROR_PARSED_DATA_CACHED); // this should fire an OnStopRequest
    1894                 : 
    1895               2 :     *listener = nsnull; // give them back a null nsIStreamListener
    1896                 : 
    1897                 :     rv = CreateNewProxyForRequest(request, loadGroup, aObserver,
    1898               2 :                                   requestFlags, nsnull, _retval);
    1899               2 :     static_cast<imgRequestProxy*>(*_retval)->NotifyListener();
    1900                 :   } else {
    1901                 :     // Default to doing a principal check because we don't know who
    1902                 :     // started that load and whether their principal ended up being
    1903                 :     // inherited on the channel.
    1904               4 :     NewRequestAndEntry(true, getter_AddRefs(request), getter_AddRefs(entry));
    1905                 : 
    1906                 :     // We use originalURI here to fulfil the imgIRequest contract on GetURI.
    1907               8 :     nsCOMPtr<nsIURI> originalURI;
    1908               4 :     channel->GetOriginalURI(getter_AddRefs(originalURI));
    1909                 : 
    1910                 :     // No principal specified here, because we're not passed one.
    1911                 :     request->Init(originalURI, uri, channel, channel, entry,
    1912               4 :                   aCX, nsnull, imgIRequest::CORS_NONE);
    1913                 : 
    1914               8 :     ProxyListener *pl = new ProxyListener(static_cast<nsIStreamListener *>(request.get()));
    1915               4 :     NS_ADDREF(pl);
    1916                 : 
    1917               4 :     *listener = static_cast<nsIStreamListener*>(pl);
    1918               4 :     NS_ADDREF(*listener);
    1919                 : 
    1920               4 :     NS_RELEASE(pl);
    1921                 : 
    1922                 :     // Try to add the new request into the cache.
    1923               4 :     PutIntoCache(originalURI, entry);
    1924                 : 
    1925                 :     rv = CreateNewProxyForRequest(request, loadGroup, aObserver,
    1926               4 :                                   requestFlags, nsnull, _retval);
    1927                 : 
    1928                 :     // Explicitly don't notify our proxy, because we're loading off the
    1929                 :     // network, and necko (or things called from necko, such as
    1930                 :     // imgCacheValidator) are going to call our notifications asynchronously,
    1931                 :     // and we can't make it further asynchronous because observers might rely
    1932                 :     // on imagelib completing its work between the channel's OnStartRequest and
    1933                 :     // OnStopRequest.
    1934                 :   }
    1935                 : 
    1936               6 :   return rv;
    1937                 : }
    1938                 : 
    1939               0 : NS_IMETHODIMP imgLoader::SupportImageWithMimeType(const char* aMimeType, bool *_retval)
    1940                 : {
    1941               0 :   *_retval = false;
    1942               0 :   nsCAutoString mimeType(aMimeType);
    1943               0 :   ToLowerCase(mimeType);
    1944               0 :   *_retval = (Image::GetDecoderType(mimeType.get()) == Image::eDecoderType_unknown)
    1945               0 :     ? false : true;
    1946               0 :   return NS_OK;
    1947                 : }
    1948                 : 
    1949             914 : NS_IMETHODIMP imgLoader::GetMIMETypeFromContent(nsIRequest* aRequest,
    1950                 :                                                 const PRUint8* aContents,
    1951                 :                                                 PRUint32 aLength,
    1952                 :                                                 nsACString& aContentType)
    1953                 : {
    1954             914 :   return GetMimeTypeFromContent((const char*)aContents, aLength, aContentType);
    1955                 : }
    1956                 : 
    1957                 : /* static */
    1958             922 : nsresult imgLoader::GetMimeTypeFromContent(const char* aContents, PRUint32 aLength, nsACString& aContentType)
    1959                 : {
    1960                 :   /* Is it a GIF? */
    1961            1585 :   if (aLength >= 6 && (!nsCRT::strncmp(aContents, "GIF87a", 6) ||
    1962             663 :                        !nsCRT::strncmp(aContents, "GIF89a", 6)))
    1963                 :   {
    1964               2 :     aContentType.AssignLiteral("image/gif");
    1965                 :   }
    1966                 : 
    1967                 :   /* or a PNG? */
    1968            1046 :   else if (aLength >= 8 && ((unsigned char)aContents[0]==0x89 &&
    1969              18 :                    (unsigned char)aContents[1]==0x50 &&
    1970              18 :                    (unsigned char)aContents[2]==0x4E &&
    1971              18 :                    (unsigned char)aContents[3]==0x47 &&
    1972              18 :                    (unsigned char)aContents[4]==0x0D &&
    1973              18 :                    (unsigned char)aContents[5]==0x0A &&
    1974              18 :                    (unsigned char)aContents[6]==0x1A &&
    1975              18 :                    (unsigned char)aContents[7]==0x0A))
    1976                 :   { 
    1977              18 :     aContentType.AssignLiteral("image/png");
    1978                 :   }
    1979                 : 
    1980                 :   /* maybe a JPEG (JFIF)? */
    1981                 :   /* JFIF files start with SOI APP0 but older files can start with SOI DQT
    1982                 :    * so we test for SOI followed by any marker, i.e. FF D8 FF
    1983                 :    * this will also work for SPIFF JPEG files if they appear in the future.
    1984                 :    *
    1985                 :    * (JFIF is 0XFF 0XD8 0XFF 0XE0 <skip 2> 0X4A 0X46 0X49 0X46 0X00)
    1986                 :    */
    1987            1556 :   else if (aLength >= 3 &&
    1988             654 :      ((unsigned char)aContents[0])==0xFF &&
    1989               0 :      ((unsigned char)aContents[1])==0xD8 &&
    1990               0 :      ((unsigned char)aContents[2])==0xFF)
    1991                 :   {
    1992               0 :     aContentType.AssignLiteral("image/jpeg");
    1993                 :   }
    1994                 : 
    1995                 :   /* or how about ART? */
    1996                 :   /* ART begins with JG (4A 47). Major version offset 2.
    1997                 :    * Minor version offset 3. Offset 4 must be NULL.
    1998                 :    */
    1999            1547 :   else if (aLength >= 5 &&
    2000             645 :    ((unsigned char) aContents[0])==0x4a &&
    2001               0 :    ((unsigned char) aContents[1])==0x47 &&
    2002               0 :    ((unsigned char) aContents[4])==0x00 )
    2003                 :   {
    2004               0 :     aContentType.AssignLiteral("image/x-jg");
    2005                 :   }
    2006                 : 
    2007             902 :   else if (aLength >= 2 && !nsCRT::strncmp(aContents, "BM", 2)) {
    2008               0 :     aContentType.AssignLiteral("image/bmp");
    2009                 :   }
    2010                 : 
    2011                 :   // ICOs always begin with a 2-byte 0 followed by a 2-byte 1.
    2012                 :   // CURs begin with 2-byte 0 followed by 2-byte 2.
    2013            1556 :   else if (aLength >= 4 && (!memcmp(aContents, "\000\000\001\000", 4) ||
    2014             654 :                             !memcmp(aContents, "\000\000\002\000", 4))) {
    2015               0 :     aContentType.AssignLiteral("image/x-icon");
    2016                 :   }
    2017                 : 
    2018                 :   else {
    2019                 :     /* none of the above?  I give up */
    2020             902 :     return NS_ERROR_NOT_AVAILABLE;
    2021                 :   }
    2022                 : 
    2023              20 :   return NS_OK;
    2024                 : }
    2025                 : 
    2026                 : /**
    2027                 :  * proxy stream listener class used to handle multipart/x-mixed-replace
    2028                 :  */
    2029                 : 
    2030                 : #include "nsIRequest.h"
    2031                 : #include "nsIStreamConverterService.h"
    2032                 : #include "nsXPIDLString.h"
    2033                 : 
    2034             130 : NS_IMPL_ISUPPORTS2(ProxyListener, nsIStreamListener, nsIRequestObserver)
    2035                 : 
    2036               8 : ProxyListener::ProxyListener(nsIStreamListener *dest) :
    2037               8 :   mDestListener(dest)
    2038                 : {
    2039                 :   /* member initializers and constructor code */
    2040               8 : }
    2041                 : 
    2042              16 : ProxyListener::~ProxyListener()
    2043                 : {
    2044                 :   /* destructor code */
    2045              32 : }
    2046                 : 
    2047                 : 
    2048                 : /** nsIRequestObserver methods **/
    2049                 : 
    2050                 : /* void onStartRequest (in nsIRequest request, in nsISupports ctxt); */
    2051               8 : NS_IMETHODIMP ProxyListener::OnStartRequest(nsIRequest *aRequest, nsISupports *ctxt)
    2052                 : {
    2053               8 :   if (!mDestListener)
    2054               0 :     return NS_ERROR_FAILURE;
    2055                 : 
    2056              16 :   nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
    2057               8 :   if (channel) {
    2058              16 :     nsCAutoString contentType;
    2059               8 :     nsresult rv = channel->GetContentType(contentType);
    2060                 : 
    2061               8 :     if (!contentType.IsEmpty()) {
    2062                 :      /* If multipart/x-mixed-replace content, we'll insert a MIME decoder
    2063                 :         in the pipeline to handle the content and pass it along to our
    2064                 :         original listener.
    2065                 :       */
    2066               8 :       if (NS_LITERAL_CSTRING("multipart/x-mixed-replace").Equals(contentType)) {
    2067                 : 
    2068               0 :         nsCOMPtr<nsIStreamConverterService> convServ(do_GetService("@mozilla.org/streamConverters;1", &rv));
    2069               0 :         if (NS_SUCCEEDED(rv)) {
    2070               0 :           nsCOMPtr<nsIStreamListener> toListener(mDestListener);
    2071               0 :           nsCOMPtr<nsIStreamListener> fromListener;
    2072                 : 
    2073               0 :           rv = convServ->AsyncConvertData("multipart/x-mixed-replace",
    2074                 :                                           "*/*",
    2075                 :                                           toListener,
    2076                 :                                           nsnull,
    2077               0 :                                           getter_AddRefs(fromListener));
    2078               0 :           if (NS_SUCCEEDED(rv))
    2079               0 :             mDestListener = fromListener;
    2080                 :         }
    2081                 :       }
    2082                 :     }
    2083                 :   }
    2084                 : 
    2085               8 :   return mDestListener->OnStartRequest(aRequest, ctxt);
    2086                 : }
    2087                 : 
    2088                 : /* void onStopRequest (in nsIRequest request, in nsISupports ctxt, in nsresult status); */
    2089               8 : NS_IMETHODIMP ProxyListener::OnStopRequest(nsIRequest *aRequest, nsISupports *ctxt, nsresult status)
    2090                 : {
    2091               8 :   if (!mDestListener)
    2092               0 :     return NS_ERROR_FAILURE;
    2093                 : 
    2094               8 :   return mDestListener->OnStopRequest(aRequest, ctxt, status);
    2095                 : }
    2096                 : 
    2097                 : /** nsIStreamListener methods **/
    2098                 : 
    2099                 : /* void onDataAvailable (in nsIRequest request, in nsISupports ctxt, in nsIInputStream inStr, in unsigned long sourceOffset, in unsigned long count); */
    2100               8 : NS_IMETHODIMP ProxyListener::OnDataAvailable(nsIRequest *aRequest, nsISupports *ctxt, nsIInputStream *inStr, PRUint32 sourceOffset, PRUint32 count)
    2101                 : {
    2102               8 :   if (!mDestListener)
    2103               0 :     return NS_ERROR_FAILURE;
    2104                 : 
    2105               8 :   return mDestListener->OnDataAvailable(aRequest, ctxt, inStr, sourceOffset, count);
    2106                 : }
    2107                 : 
    2108                 : /**
    2109                 :  * http validate class.  check a channel for a 304
    2110                 :  */
    2111                 : 
    2112               0 : NS_IMPL_ISUPPORTS5(imgCacheValidator, nsIStreamListener, nsIRequestObserver,
    2113                 :                    nsIChannelEventSink, nsIInterfaceRequestor,
    2114                 :                    nsIAsyncVerifyRedirectCallback)
    2115                 : 
    2116            1464 : imgLoader imgCacheValidator::sImgLoader;
    2117                 : 
    2118               0 : imgCacheValidator::imgCacheValidator(nsProgressNotificationProxy* progress,
    2119                 :                                      imgRequest *request, void *aContext,
    2120                 :                                      bool forcePrincipalCheckForCacheEntry)
    2121                 :  : mProgressProxy(progress),
    2122                 :    mRequest(request),
    2123               0 :    mContext(aContext)
    2124                 : {
    2125                 :   NewRequestAndEntry(forcePrincipalCheckForCacheEntry,
    2126               0 :                      getter_AddRefs(mNewRequest), getter_AddRefs(mNewEntry));
    2127               0 : }
    2128                 : 
    2129               0 : imgCacheValidator::~imgCacheValidator()
    2130                 : {
    2131               0 :   if (mRequest) {
    2132               0 :     mRequest->mValidator = nsnull;
    2133                 :   }
    2134               0 : }
    2135                 : 
    2136               0 : void imgCacheValidator::AddProxy(imgRequestProxy *aProxy)
    2137                 : {
    2138                 :   // aProxy needs to be in the loadgroup since we're validating from
    2139                 :   // the network.
    2140               0 :   aProxy->AddToLoadGroup();
    2141                 : 
    2142               0 :   mProxies.AppendObject(aProxy);
    2143               0 : }
    2144                 : 
    2145                 : /** nsIRequestObserver methods **/
    2146                 : 
    2147                 : /* void onStartRequest (in nsIRequest request, in nsISupports ctxt); */
    2148               0 : NS_IMETHODIMP imgCacheValidator::OnStartRequest(nsIRequest *aRequest, nsISupports *ctxt)
    2149                 : {
    2150                 :   // If this request is coming from cache and has the same URI as our
    2151                 :   // imgRequest, the request all our proxies are pointing at is valid, and all
    2152                 :   // we have to do is tell them to notify their listeners.
    2153               0 :   nsCOMPtr<nsICachingChannel> cacheChan(do_QueryInterface(aRequest));
    2154               0 :   nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
    2155               0 :   if (cacheChan && channel) {
    2156               0 :     bool isFromCache = false;
    2157               0 :     cacheChan->IsFromCache(&isFromCache);
    2158                 : 
    2159               0 :     nsCOMPtr<nsIURI> channelURI;
    2160               0 :     bool sameURI = false;
    2161               0 :     channel->GetURI(getter_AddRefs(channelURI));
    2162               0 :     if (channelURI)
    2163               0 :       channelURI->Equals(mRequest->mCurrentURI, &sameURI);
    2164                 : 
    2165               0 :     if (isFromCache && sameURI) {
    2166               0 :       PRUint32 count = mProxies.Count();
    2167               0 :       for (PRInt32 i = count-1; i>=0; i--) {
    2168               0 :         imgRequestProxy *proxy = static_cast<imgRequestProxy *>(mProxies[i]);
    2169                 : 
    2170                 :         // Proxies waiting on cache validation should be deferring notifications.
    2171                 :         // Undefer them.
    2172               0 :         NS_ABORT_IF_FALSE(proxy->NotificationsDeferred(),
    2173                 :                           "Proxies waiting on cache validation should be "
    2174                 :                           "deferring notifications!");
    2175               0 :         proxy->SetNotificationsDeferred(false);
    2176                 : 
    2177                 :         // Notify synchronously, because we're already in OnStartRequest, an
    2178                 :         // asynchronously-called function.
    2179               0 :         proxy->SyncNotifyListener();
    2180                 :       }
    2181                 : 
    2182                 :       // We don't need to load this any more.
    2183               0 :       aRequest->Cancel(NS_BINDING_ABORTED);
    2184                 : 
    2185               0 :       mRequest->SetLoadId(mContext);
    2186               0 :       mRequest->mValidator = nsnull;
    2187                 : 
    2188               0 :       mRequest = nsnull;
    2189                 : 
    2190               0 :       mNewRequest = nsnull;
    2191               0 :       mNewEntry = nsnull;
    2192                 : 
    2193               0 :       return NS_OK;
    2194                 :     }
    2195                 :   }
    2196                 : 
    2197                 :   // We can't load out of cache. We have to create a whole new request for the
    2198                 :   // data that's coming in off the channel.
    2199               0 :   nsCOMPtr<nsIURI> uri;
    2200               0 :   mRequest->GetURI(getter_AddRefs(uri));
    2201                 : 
    2202                 : #if defined(PR_LOGGING)
    2203               0 :   nsCAutoString spec;
    2204               0 :   uri->GetSpec(spec);
    2205               0 :   LOG_MSG_WITH_PARAM(gImgLog, "imgCacheValidator::OnStartRequest creating new request", "uri", spec.get());
    2206                 : #endif
    2207                 : 
    2208               0 :   PRInt32 corsmode = mRequest->GetCORSMode();
    2209               0 :   nsCOMPtr<nsIPrincipal> loadingPrincipal = mRequest->GetLoadingPrincipal();
    2210                 : 
    2211                 :   // Doom the old request's cache entry
    2212               0 :   mRequest->RemoveFromCache();
    2213                 : 
    2214               0 :   mRequest->mValidator = nsnull;
    2215               0 :   mRequest = nsnull;
    2216                 : 
    2217                 :   // We use originalURI here to fulfil the imgIRequest contract on GetURI.
    2218               0 :   nsCOMPtr<nsIURI> originalURI;
    2219               0 :   channel->GetOriginalURI(getter_AddRefs(originalURI));
    2220                 :   mNewRequest->Init(originalURI, uri, aRequest, channel, mNewEntry,
    2221                 :                     mContext, loadingPrincipal,
    2222               0 :                     corsmode);
    2223                 : 
    2224               0 :   mDestListener = new ProxyListener(mNewRequest);
    2225                 : 
    2226                 :   // Try to add the new request into the cache. Note that the entry must be in
    2227                 :   // the cache before the proxies' ownership changes, because adding a proxy
    2228                 :   // changes the caching behaviour for imgRequests.
    2229               0 :   sImgLoader.PutIntoCache(originalURI, mNewEntry);
    2230                 : 
    2231               0 :   PRUint32 count = mProxies.Count();
    2232               0 :   for (PRInt32 i = count-1; i>=0; i--) {
    2233               0 :     imgRequestProxy *proxy = static_cast<imgRequestProxy *>(mProxies[i]);
    2234               0 :     proxy->ChangeOwner(mNewRequest);
    2235                 : 
    2236                 :     // Proxies waiting on cache validation should be deferring notifications.
    2237                 :     // Undefer them.
    2238               0 :     NS_ABORT_IF_FALSE(proxy->NotificationsDeferred(),
    2239                 :                       "Proxies waiting on cache validation should be "
    2240                 :                       "deferring notifications!");
    2241               0 :     proxy->SetNotificationsDeferred(false);
    2242                 : 
    2243                 :     // Notify synchronously, because we're already in OnStartRequest, an
    2244                 :     // asynchronously-called function.
    2245               0 :     proxy->SyncNotifyListener();
    2246                 :   }
    2247                 : 
    2248               0 :   mNewRequest = nsnull;
    2249               0 :   mNewEntry = nsnull;
    2250                 : 
    2251               0 :   return mDestListener->OnStartRequest(aRequest, ctxt);
    2252                 : }
    2253                 : 
    2254                 : /* void onStopRequest (in nsIRequest request, in nsISupports ctxt, in nsresult status); */
    2255               0 : NS_IMETHODIMP imgCacheValidator::OnStopRequest(nsIRequest *aRequest, nsISupports *ctxt, nsresult status)
    2256                 : {
    2257               0 :   if (!mDestListener)
    2258               0 :     return NS_OK;
    2259                 : 
    2260               0 :   return mDestListener->OnStopRequest(aRequest, ctxt, status);
    2261                 : }
    2262                 : 
    2263                 : /** nsIStreamListener methods **/
    2264                 : 
    2265                 : 
    2266                 : /* void onDataAvailable (in nsIRequest request, in nsISupports ctxt, in nsIInputStream inStr, in unsigned long sourceOffset, in unsigned long count); */
    2267               0 : NS_IMETHODIMP imgCacheValidator::OnDataAvailable(nsIRequest *aRequest, nsISupports *ctxt, nsIInputStream *inStr, PRUint32 sourceOffset, PRUint32 count)
    2268                 : {
    2269               0 :   if (!mDestListener) {
    2270                 :     // XXX see bug 113959
    2271                 :     PRUint32 _retval;
    2272               0 :     inStr->ReadSegments(NS_DiscardSegment, nsnull, count, &_retval);
    2273               0 :     return NS_OK;
    2274                 :   }
    2275                 : 
    2276               0 :   return mDestListener->OnDataAvailable(aRequest, ctxt, inStr, sourceOffset, count);
    2277                 : }
    2278                 : 
    2279                 : /** nsIInterfaceRequestor methods **/
    2280                 : 
    2281               0 : NS_IMETHODIMP imgCacheValidator::GetInterface(const nsIID & aIID, void **aResult)
    2282                 : {
    2283               0 :   if (aIID.Equals(NS_GET_IID(nsIChannelEventSink)))
    2284               0 :     return QueryInterface(aIID, aResult);
    2285                 : 
    2286               0 :   return mProgressProxy->GetInterface(aIID, aResult);
    2287                 : }
    2288                 : 
    2289                 : // These functions are materially the same as the same functions in imgRequest.
    2290                 : // We duplicate them because we're verifying whether cache loads are necessary,
    2291                 : // not unconditionally loading.
    2292                 : 
    2293                 : /** nsIChannelEventSink methods **/
    2294               0 : NS_IMETHODIMP imgCacheValidator::AsyncOnChannelRedirect(nsIChannel *oldChannel,
    2295                 :                                                         nsIChannel *newChannel, PRUint32 flags,
    2296                 :                                                         nsIAsyncVerifyRedirectCallback *callback)
    2297                 : {
    2298                 :   // Note all cache information we get from the old channel.
    2299               0 :   mNewRequest->SetCacheValidation(mNewEntry, oldChannel);
    2300                 : 
    2301                 :   // Prepare for callback
    2302               0 :   mRedirectCallback = callback;
    2303               0 :   mRedirectChannel = newChannel;
    2304                 : 
    2305               0 :   return mProgressProxy->AsyncOnChannelRedirect(oldChannel, newChannel, flags, this);
    2306                 : }
    2307                 : 
    2308               0 : NS_IMETHODIMP imgCacheValidator::OnRedirectVerifyCallback(nsresult aResult)
    2309                 : {
    2310                 :   // If we've already been told to abort, just do so.
    2311               0 :   if (NS_FAILED(aResult)) {
    2312               0 :       mRedirectCallback->OnRedirectVerifyCallback(aResult);
    2313               0 :       mRedirectCallback = nsnull;
    2314               0 :       mRedirectChannel = nsnull;
    2315               0 :       return NS_OK;
    2316                 :   }
    2317                 : 
    2318                 :   // make sure we have a protocol that returns data rather than opens
    2319                 :   // an external application, e.g. mailto:
    2320               0 :   nsCOMPtr<nsIURI> uri;
    2321               0 :   mRedirectChannel->GetURI(getter_AddRefs(uri));
    2322               0 :   bool doesNotReturnData = false;
    2323                 :   NS_URIChainHasFlags(uri, nsIProtocolHandler::URI_DOES_NOT_RETURN_DATA,
    2324               0 :                       &doesNotReturnData);
    2325                 : 
    2326               0 :   nsresult result = NS_OK;
    2327                 : 
    2328               0 :   if (doesNotReturnData) {
    2329               0 :     result = NS_ERROR_ABORT;
    2330                 :   }
    2331                 : 
    2332               0 :   mRedirectCallback->OnRedirectVerifyCallback(result);
    2333               0 :   mRedirectCallback = nsnull;
    2334               0 :   mRedirectChannel = nsnull;
    2335               0 :   return NS_OK;
    2336            4392 : }

Generated by: LCOV version 1.7