Back to index

lightning-sunbird  0.9+nobinonly
imgCache.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
00002  *
00003  * ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is mozilla.org code.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 2001
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Stuart Parmenter <pavlov@netscape.com>
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either the GNU General Public License Version 2 or later (the "GPL"), or
00028  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 #include "imgCache.h"
00041 
00042 #include "ImageLogging.h"
00043 
00044 #include "imgRequest.h"
00045 
00046 #include "nsXPIDLString.h"
00047 #include "nsCOMPtr.h"
00048 #include "nsIServiceManager.h"
00049 #include "nsIMemory.h"
00050 #include "nsIObserverService.h"
00051 
00052 #include "nsICache.h"
00053 #include "nsICacheService.h"
00054 #include "nsICacheSession.h"
00055 #include "nsICacheEntryDescriptor.h"
00056 
00057 #include "nsIFile.h"
00058 #include "nsIFileURL.h"
00059 
00060 NS_IMPL_ISUPPORTS3(imgCache, imgICache, nsIObserver, nsISupportsWeakReference)
00061 
00062 imgCache::imgCache()
00063 {
00064   /* member initializers and constructor code */
00065 }
00066 
00067 imgCache::~imgCache()
00068 {
00069   /* destructor code */
00070 }
00071 
00072 nsresult imgCache::Init()
00073 {
00074   imgCache* cache = new imgCache();
00075   if(!cache) return NS_ERROR_OUT_OF_MEMORY;
00076 
00077   nsCOMPtr<nsIObserverService> os = do_GetService("@mozilla.org/observer-service;1");
00078   if (os) {
00079     os->AddObserver(cache, "memory-pressure", PR_FALSE);
00080     os->AddObserver(cache, "chrome-flush-skin-caches", PR_FALSE);
00081     os->AddObserver(cache, "chrome-flush-caches", PR_FALSE);
00082   }
00083   
00084   return NS_OK;
00085 }
00086 
00087 /* void clearCache (in boolean chrome); */
00088 NS_IMETHODIMP imgCache::ClearCache(PRBool chrome)
00089 {
00090   if (chrome)
00091     return imgCache::ClearChromeImageCache();
00092   else
00093     return imgCache::ClearImageCache();
00094 }
00095 
00096 
00097 /* void removeEntry(in nsIURI uri); */
00098 NS_IMETHODIMP imgCache::RemoveEntry(nsIURI *uri)
00099 {
00100   if (imgCache::Remove(uri))
00101     return NS_OK;
00102 
00103   return NS_ERROR_NOT_AVAILABLE;
00104 }
00105 
00106 /* imgIRequest findEntry(in nsIURI uri); */
00107 NS_IMETHODIMP imgCache::FindEntryProperties(nsIURI *uri, nsIProperties **_retval)
00108 {
00109   PRBool expired;
00110   // This is an owning reference that must be released.
00111   imgRequest *request = nsnull;
00112   nsCOMPtr<nsICacheEntryDescriptor> entry;
00113 
00114   // addrefs request
00115   imgCache::Get(uri, &expired, &request, getter_AddRefs(entry));
00116 
00117   *_retval = nsnull;
00118 
00119   if (request) {
00120     *_retval = request->Properties();
00121     NS_ADDREF(*_retval);
00122   }
00123 
00124   NS_IF_RELEASE(request);
00125 
00126   return NS_OK;
00127 }
00128 
00129 
00130 static nsCOMPtr<nsICacheSession> gSession = nsnull;
00131 static nsCOMPtr<nsICacheSession> gChromeSession = nsnull;
00132 
00133 void GetCacheSession(nsIURI *aURI, nsICacheSession **_retval)
00134 {
00135   NS_ASSERTION(aURI, "Null URI!");
00136 
00137   PRBool isChrome = PR_FALSE;
00138   aURI->SchemeIs("chrome", &isChrome);
00139 
00140   if (gSession && !isChrome) {
00141     *_retval = gSession;
00142     NS_ADDREF(*_retval);
00143     return;
00144   }
00145 
00146   if (gChromeSession && isChrome) {
00147     *_retval = gChromeSession;
00148     NS_ADDREF(*_retval);
00149     return;
00150   }
00151 
00152   nsCOMPtr<nsICacheService> cacheService(do_GetService("@mozilla.org/network/cache-service;1"));
00153   if (!cacheService) {
00154     NS_WARNING("Unable to get the cache service");
00155     return;
00156   }
00157 
00158   nsCOMPtr<nsICacheSession> newSession;
00159   cacheService->CreateSession(isChrome ? "image-chrome" : "image",
00160                               nsICache::STORE_IN_MEMORY,
00161                               nsICache::NOT_STREAM_BASED,
00162                               getter_AddRefs(newSession));
00163 
00164   if (!newSession) {
00165     NS_WARNING("Unable to create a cache session");
00166     return;
00167   }
00168 
00169   if (isChrome)
00170     gChromeSession = newSession;
00171   else {
00172     gSession = newSession;
00173     gSession->SetDoomEntriesIfExpired(PR_FALSE);
00174   }
00175 
00176   *_retval = newSession;
00177   NS_ADDREF(*_retval);
00178 }
00179 
00180 
00181 void imgCache::Shutdown()
00182 {
00183   gSession = nsnull;
00184   gChromeSession = nsnull;
00185 }
00186 
00187 
00188 nsresult imgCache::ClearChromeImageCache()
00189 {
00190   if (!gChromeSession)
00191     return NS_OK;
00192 
00193   return gChromeSession->EvictEntries();
00194 }
00195 
00196 nsresult imgCache::ClearImageCache()
00197 {
00198   if (!gSession)
00199     return NS_OK;
00200 
00201   return gSession->EvictEntries();
00202 }
00203 
00204 
00205 
00206 PRBool imgCache::Put(nsIURI *aKey, imgRequest *request, nsICacheEntryDescriptor **aEntry)
00207 {
00208   LOG_STATIC_FUNC(gImgLog, "imgCache::Put");
00209 
00210   nsresult rv;
00211 
00212   nsCOMPtr<nsICacheSession> ses;
00213   GetCacheSession(aKey, getter_AddRefs(ses));
00214   if (!ses) return PR_FALSE;
00215 
00216   nsCAutoString spec;
00217   aKey->GetAsciiSpec(spec);
00218 
00219   nsCOMPtr<nsICacheEntryDescriptor> entry;
00220 
00221   rv = ses->OpenCacheEntry(spec, nsICache::ACCESS_WRITE, nsICache::BLOCKING, getter_AddRefs(entry));
00222 
00223   if (NS_FAILED(rv) || !entry)
00224     return PR_FALSE;
00225 
00226   nsCOMPtr<nsISupports> sup = NS_REINTERPRET_CAST(nsISupports*, request);
00227   entry->SetCacheElement(sup);
00228 
00229   entry->MarkValid();
00230 
00231   // If file, force revalidation on expiration
00232   PRBool isFile;
00233   aKey->SchemeIs("file", &isFile);
00234   if (isFile)
00235     entry->SetMetaDataElement("MustValidateIfExpired", "true");
00236 
00237   *aEntry = entry;
00238   NS_ADDREF(*aEntry);
00239 
00240   return PR_TRUE;
00241 }
00242 
00243 static PRUint32
00244 SecondsFromPRTime(PRTime prTime)
00245 {
00246   PRInt64 microSecondsPerSecond, intermediateResult;
00247   PRUint32 seconds;
00248   
00249   LL_I2L(microSecondsPerSecond, PR_USEC_PER_SEC);
00250   LL_DIV(intermediateResult, prTime, microSecondsPerSecond);
00251   LL_L2UI(seconds, intermediateResult);
00252   return seconds;
00253 }
00254 
00255 
00256 PRBool imgCache::Get(nsIURI *aKey, PRBool *aHasExpired, imgRequest **aRequest, nsICacheEntryDescriptor **aEntry)
00257 {
00258   LOG_STATIC_FUNC(gImgLog, "imgCache::Get");
00259 
00260   nsresult rv;
00261 
00262   nsCOMPtr<nsICacheSession> ses;
00263   GetCacheSession(aKey, getter_AddRefs(ses));
00264   if (!ses) return PR_FALSE;
00265 
00266   nsCAutoString spec;
00267   aKey->GetAsciiSpec(spec);
00268 
00269   nsCOMPtr<nsICacheEntryDescriptor> entry;
00270 
00271   rv = ses->OpenCacheEntry(spec, nsICache::ACCESS_READ, nsICache::BLOCKING, getter_AddRefs(entry));
00272 
00273   if (NS_FAILED(rv) || !entry)
00274     return PR_FALSE;
00275 
00276   if (aHasExpired) {
00277     PRUint32 expirationTime;
00278     rv = entry->GetExpirationTime(&expirationTime);
00279     if (NS_FAILED(rv) || (expirationTime <= SecondsFromPRTime(PR_Now()))) {
00280       *aHasExpired = PR_TRUE;
00281     } else {
00282       *aHasExpired = PR_FALSE;
00283     }
00284     // Special treatment for file URLs - entry has expired if file has changed
00285     nsCOMPtr<nsIFileURL> fileUrl(do_QueryInterface(aKey));
00286     if (fileUrl) {
00287       PRUint32 lastModTime;
00288       entry->GetLastModified(&lastModTime);
00289 
00290       nsCOMPtr<nsIFile> theFile;
00291       rv = fileUrl->GetFile(getter_AddRefs(theFile));
00292       if (NS_SUCCEEDED(rv)) {
00293         PRInt64 fileLastMod;
00294         rv = theFile->GetLastModifiedTime(&fileLastMod);
00295         if (NS_SUCCEEDED(rv)) {
00296           // nsIFile uses millisec, NSPR usec
00297           PRInt64 one_thousand = LL_INIT(0, 1000);
00298           LL_MUL(fileLastMod, fileLastMod, one_thousand);
00299           *aHasExpired = SecondsFromPRTime((PRTime)fileLastMod) > lastModTime;
00300         }
00301       }
00302     }
00303   }
00304 
00305   nsCOMPtr<nsISupports> sup;
00306   entry->GetCacheElement(getter_AddRefs(sup));
00307 
00308   *aRequest = NS_REINTERPRET_CAST(imgRequest*, sup.get());
00309   NS_IF_ADDREF(*aRequest);
00310 
00311   *aEntry = entry;
00312   NS_ADDREF(*aEntry);
00313 
00314   return PR_TRUE;
00315 }
00316 
00317 
00318 PRBool imgCache::Remove(nsIURI *aKey)
00319 {
00320   LOG_STATIC_FUNC(gImgLog, "imgCache::Remove");
00321   if (!aKey) return PR_FALSE;
00322 
00323   nsresult rv;
00324   nsCOMPtr<nsICacheSession> ses;
00325   GetCacheSession(aKey, getter_AddRefs(ses));
00326   if (!ses) return PR_FALSE;
00327 
00328   nsCAutoString spec;
00329   aKey->GetAsciiSpec(spec);
00330 
00331   nsCOMPtr<nsICacheEntryDescriptor> entry;
00332 
00333   rv = ses->OpenCacheEntry(spec, nsICache::ACCESS_READ, nsICache::BLOCKING, getter_AddRefs(entry));
00334 
00335   if (NS_FAILED(rv) || !entry)
00336     return PR_FALSE;
00337 
00338   entry->Doom();
00339 
00340   return PR_TRUE;
00341 }
00342 
00343 
00344 NS_IMETHODIMP
00345 imgCache::Observe(nsISupports* aSubject, const char* aTopic, const PRUnichar* aSomeData)
00346 {
00347   if (strcmp(aTopic, "memory-pressure") == 0 ||
00348       strcmp(aTopic, "chrome-flush-skin-caches") == 0 ||
00349       strcmp(aTopic, "chrome-flush-caches") == 0)
00350     ClearCache(PR_TRUE);
00351 
00352   return NS_OK;
00353 }