2011-04-07 21:55:25 -07:00
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
2007-03-22 10:30:00 -07:00
* Version : MPL 1.1 / GPL 2.0 / LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 ( the " License " ) ; you may not use this file except in compliance with
* the License . You may obtain a copy of the License at
* http : //www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an " AS IS " basis ,
* WITHOUT WARRANTY OF ANY KIND , either express or implied . See the License
* for the specific language governing rights and limitations under the
* License .
*
* The Original Code is mozilla . org code .
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation .
* Portions created by the Initial Developer are Copyright ( C ) 2001
* the Initial Developer . All Rights Reserved .
*
* Contributor ( s ) :
* Stuart Parmenter < pavlov @ netscape . com >
2009-07-15 00:22:40 -07:00
* Ehsan Akhgari < ehsan . akhgari @ gmail . com >
2007-03-22 10:30:00 -07:00
*
* Alternatively , the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later ( the " GPL " ) , or
* the GNU Lesser General Public License Version 2.1 or later ( the " LGPL " ) ,
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above . If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL , and not to allow others to
* use your version of this file under the terms of the MPL , indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL . If you do not delete
* the provisions above , a recipient may use your version of this file under
* the terms of any one of the MPL , the GPL or the LGPL .
*
* * * * * * END LICENSE BLOCK * * * * * */
2012-01-02 12:23:41 -08:00
# include "mozilla/Attributes.h"
# include "mozilla/FunctionTimer.h"
# include "mozilla/Preferences.h"
2011-07-08 10:55:03 -07:00
# include "ImageLogging.h"
2007-03-22 10:30:00 -07:00
# include "imgLoader.h"
2010-08-13 21:09:48 -07:00
# include "imgRequestProxy.h"
2010-05-21 21:10:14 -07:00
2010-08-13 21:09:49 -07:00
# include "RasterImage.h"
2010-08-13 21:09:48 -07:00
/* We end up pulling in windows.h because we eventually hit gfxWindowsSurface;
* windows . h defines LoadImage , so we have to # undef it or imgLoader : : LoadImage
* gets changed .
* This # undef needs to be in multiple places because we don ' t always pull
* headers in in the same order .
*/
# undef LoadImage
2007-03-22 10:30:00 -07:00
# include "nsCOMPtr.h"
2011-07-14 11:47:34 -07:00
# include "nsContentUtils.h"
# include "nsCrossSiteListenerProxy.h"
2007-03-22 10:30:00 -07:00
# include "nsNetUtil.h"
2011-06-24 07:22:33 -07:00
# include "nsStreamUtils.h"
2007-03-22 10:30:00 -07:00
# include "nsIHttpChannel.h"
# include "nsICachingChannel.h"
2009-07-28 09:13:48 -07:00
# include "nsIInterfaceRequestor.h"
# include "nsIProgressEventSink.h"
2009-08-13 04:20:41 -07:00
# include "nsIChannelEventSink.h"
2010-08-04 19:15:55 -07:00
# include "nsIAsyncVerifyRedirectCallback.h"
2007-03-22 10:30:00 -07:00
# include "nsIServiceManager.h"
2008-09-04 16:00:42 -07:00
# include "nsIFileURL.h"
2007-03-22 10:30:00 -07:00
# include "nsThreadUtils.h"
# include "nsXPIDLString.h"
# include "nsCRT.h"
2010-12-20 08:21:59 -08:00
# include "nsIDocument.h"
# include "nsPIDOMWindow.h"
2007-03-22 10:30:00 -07:00
# include "netCore.h"
2009-02-16 03:27:22 -08:00
# include "nsURILoader.h"
2007-03-22 10:30:00 -07:00
# include "nsIComponentRegistrar.h"
2009-01-04 21:52:22 -08:00
# include "nsIApplicationCache.h"
# include "nsIApplicationCacheContainer.h"
2010-05-21 21:10:14 -07:00
# include "nsIMemoryReporter.h"
2010-07-11 18:01:53 -07:00
# include "nsIPrivateBrowsingService.h"
2010-05-21 21:10:14 -07:00
2007-03-22 10:30:00 -07:00
// we want to explore making the document own the load group
// so we can associate the document URI with the load group.
// until this point, we have an evil hack:
# include "nsIHttpChannelInternal.h"
2010-05-20 13:08:02 -07:00
# include "nsIContentSecurityPolicy.h"
# include "nsIChannelPolicy.h"
2007-03-22 10:30:00 -07:00
2011-09-20 14:00:42 -07:00
# include "nsContentUtils.h"
2011-06-11 19:30:15 -07:00
using namespace mozilla ;
2012-01-06 08:02:27 -08:00
using namespace mozilla : : image ;
2010-08-13 21:09:49 -07:00
2007-03-22 10:30:00 -07:00
# if defined(DEBUG_pavlov) || defined(DEBUG_timeless)
# include "nsISimpleEnumerator.h"
# include "nsXPCOM.h"
# include "nsISupportsPrimitives.h"
# include "nsXPIDLString.h"
# include "nsComponentManagerUtils.h"
2010-08-13 21:09:49 -07:00
2007-03-22 10:30:00 -07:00
static void PrintImageDecoders ( )
{
nsCOMPtr < nsIComponentRegistrar > compMgr ;
if ( NS_FAILED ( NS_GetComponentRegistrar ( getter_AddRefs ( compMgr ) ) ) | | ! compMgr )
return ;
nsCOMPtr < nsISimpleEnumerator > enumer ;
if ( NS_FAILED ( compMgr - > EnumerateContractIDs ( getter_AddRefs ( enumer ) ) ) | | ! enumer )
return ;
nsCString str ;
nsCOMPtr < nsISupports > s ;
2011-09-28 23:19:26 -07:00
bool more = false ;
2007-03-22 10:30:00 -07:00
while ( NS_SUCCEEDED ( enumer - > HasMoreElements ( & more ) ) & & more ) {
enumer - > GetNext ( getter_AddRefs ( s ) ) ;
if ( s ) {
nsCOMPtr < nsISupportsCString > ss ( do_QueryInterface ( s ) ) ;
nsCAutoString xcs ;
ss - > GetData ( xcs ) ;
2009-09-12 15:44:18 -07:00
NS_NAMED_LITERAL_CSTRING ( decoderContract , " @mozilla.org/image/decoder;3?type= " ) ;
2007-03-22 10:30:00 -07:00
if ( StringBeginsWith ( xcs , decoderContract ) ) {
printf ( " Have decoder for mime type: %s \n " , xcs . get ( ) + decoderContract . Length ( ) ) ;
}
}
}
}
# endif
2010-05-21 21:10:14 -07:00
2012-01-02 12:23:41 -08:00
class imgMemoryReporter MOZ_FINAL :
2010-05-21 21:10:14 -07:00
public nsIMemoryReporter
{
public :
enum ReporterType {
CHROME_BIT = PR_BIT ( 0 ) ,
USED_BIT = PR_BIT ( 1 ) ,
RAW_BIT = PR_BIT ( 2 ) ,
2011-07-18 06:20:27 -07:00
HEAP_BIT = PR_BIT ( 3 ) ,
ChromeUsedRaw = CHROME_BIT | USED_BIT | RAW_BIT | HEAP_BIT ,
ChromeUsedUncompressedHeap = CHROME_BIT | USED_BIT | HEAP_BIT ,
ChromeUsedUncompressedNonheap = CHROME_BIT | USED_BIT ,
ChromeUnusedRaw = CHROME_BIT | RAW_BIT | HEAP_BIT ,
ChromeUnusedUncompressedHeap = CHROME_BIT | HEAP_BIT ,
ChromeUnusedUncompressedNonheap = CHROME_BIT ,
ContentUsedRaw = USED_BIT | RAW_BIT | HEAP_BIT ,
ContentUsedUncompressedHeap = USED_BIT | HEAP_BIT ,
ContentUsedUncompressedNonheap = USED_BIT ,
ContentUnusedRaw = RAW_BIT | HEAP_BIT ,
ContentUnusedUncompressedHeap = HEAP_BIT ,
ContentUnusedUncompressedNonheap = 0
2010-05-21 21:10:14 -07:00
} ;
imgMemoryReporter ( ReporterType aType )
: mType ( aType )
2011-07-18 06:20:27 -07:00
{
// If the RAW bit is set, HEAP should also be set, because we don't
// currently understand storing compressed image data off the heap.
NS_ASSERTION ( ! ( aType & RAW_BIT ) | | ( aType & HEAP_BIT ) ,
" RAW bit should imply HEAP bit. " ) ;
}
2010-05-21 21:10:14 -07:00
NS_DECL_ISUPPORTS
2011-07-07 19:45:16 -07:00
NS_IMETHOD GetProcess ( nsACString & process )
2011-06-05 18:22:45 -07:00
{
2011-07-07 19:45:16 -07:00
process . Truncate ( ) ;
2011-06-05 18:22:45 -07:00
return NS_OK ;
}
2011-07-07 19:45:16 -07:00
NS_IMETHOD GetPath ( nsACString & path )
2010-05-21 21:10:14 -07:00
{
if ( mType = = ChromeUsedRaw ) {
2011-07-07 19:45:16 -07:00
path . AssignLiteral ( " explicit/images/chrome/used/raw " ) ;
2011-07-18 06:20:27 -07:00
} else if ( mType = = ChromeUsedUncompressedHeap ) {
path . AssignLiteral ( " explicit/images/chrome/used/uncompressed-heap " ) ;
} else if ( mType = = ChromeUsedUncompressedNonheap ) {
path . AssignLiteral ( " explicit/images/chrome/used/uncompressed-nonheap " ) ;
2010-05-21 21:10:14 -07:00
} else if ( mType = = ChromeUnusedRaw ) {
2011-07-07 19:45:16 -07:00
path . AssignLiteral ( " explicit/images/chrome/unused/raw " ) ;
2011-07-18 06:20:27 -07:00
} else if ( mType = = ChromeUnusedUncompressedHeap ) {
path . AssignLiteral ( " explicit/images/chrome/unused/uncompressed-heap " ) ;
} else if ( mType = = ChromeUnusedUncompressedNonheap ) {
path . AssignLiteral ( " explicit/images/chrome/unused/uncompressed-nonheap " ) ;
2010-05-21 21:10:14 -07:00
} else if ( mType = = ContentUsedRaw ) {
2011-07-07 19:45:16 -07:00
path . AssignLiteral ( " explicit/images/content/used/raw " ) ;
2011-07-18 06:20:27 -07:00
} else if ( mType = = ContentUsedUncompressedHeap ) {
path . AssignLiteral ( " explicit/images/content/used/uncompressed-heap " ) ;
} else if ( mType = = ContentUsedUncompressedNonheap ) {
path . AssignLiteral ( " explicit/images/content/used/uncompressed-nonheap " ) ;
2010-05-21 21:10:14 -07:00
} else if ( mType = = ContentUnusedRaw ) {
2011-07-07 19:45:16 -07:00
path . AssignLiteral ( " explicit/images/content/unused/raw " ) ;
2011-07-18 06:20:27 -07:00
} else if ( mType = = ContentUnusedUncompressedHeap ) {
path . AssignLiteral ( " explicit/images/content/unused/uncompressed-heap " ) ;
} else if ( mType = = ContentUnusedUncompressedNonheap ) {
path . AssignLiteral ( " explicit/images/content/unused/uncompressed-nonheap " ) ;
2010-05-21 21:10:14 -07:00
}
return NS_OK ;
}
2011-05-22 19:49:56 -07:00
NS_IMETHOD GetKind ( PRInt32 * kind )
{
2011-07-18 06:20:27 -07:00
if ( mType & HEAP_BIT ) {
* kind = KIND_HEAP ;
}
else {
* kind = KIND_MAPPED ;
}
2011-05-22 19:49:56 -07:00
return NS_OK ;
}
2011-06-16 11:34:09 -07:00
NS_IMETHOD GetUnits ( PRInt32 * units )
2010-05-21 21:10:14 -07:00
{
2011-06-16 11:34:09 -07:00
* units = UNITS_BYTES ;
2010-05-21 21:10:14 -07:00
return NS_OK ;
}
struct EnumArg {
EnumArg ( ReporterType aType )
: rtype ( aType ) , value ( 0 )
{ }
ReporterType rtype ;
PRInt32 value ;
} ;
static PLDHashOperator EnumEntries ( const nsACString & ,
imgCacheEntry * entry ,
void * userArg )
{
EnumArg * arg = static_cast < EnumArg * > ( userArg ) ;
ReporterType rtype = arg - > rtype ;
if ( rtype & USED_BIT ) {
if ( entry - > HasNoProxies ( ) )
return PL_DHASH_NEXT ;
} else {
if ( ! entry - > HasNoProxies ( ) )
return PL_DHASH_NEXT ;
}
nsRefPtr < imgRequest > req = entry - > GetRequest ( ) ;
2010-10-06 08:37:12 -07:00
Image * image = static_cast < Image * > ( req - > mImage . get ( ) ) ;
2010-08-13 21:09:49 -07:00
if ( ! image )
2010-05-21 21:10:14 -07:00
return PL_DHASH_NEXT ;
if ( rtype & RAW_BIT ) {
2011-07-18 06:20:27 -07:00
arg - > value + = image - > GetSourceHeapSize ( ) ;
} else if ( rtype & HEAP_BIT ) {
arg - > value + = image - > GetDecodedHeapSize ( ) ;
2010-05-21 21:10:14 -07:00
} else {
2011-07-18 06:20:27 -07:00
arg - > value + = image - > GetDecodedNonheapSize ( ) ;
2010-05-21 21:10:14 -07:00
}
return PL_DHASH_NEXT ;
}
2011-06-16 11:34:09 -07:00
NS_IMETHOD GetAmount ( PRInt64 * amount )
2010-05-21 21:10:14 -07:00
{
EnumArg arg ( mType ) ;
if ( mType & CHROME_BIT ) {
imgLoader : : sChromeCache . EnumerateRead ( EnumEntries , & arg ) ;
} else {
imgLoader : : sCache . EnumerateRead ( EnumEntries , & arg ) ;
}
2011-06-16 11:34:09 -07:00
* amount = arg . value ;
return NS_OK ;
}
2011-07-07 19:45:16 -07:00
NS_IMETHOD GetDescription ( nsACString & desc )
2011-06-16 11:34:09 -07:00
{
if ( mType = = ChromeUsedRaw ) {
2011-07-07 19:45:16 -07:00
desc . AssignLiteral ( " Memory used by in-use chrome images (compressed data). " ) ;
2011-07-18 06:20:27 -07:00
} else if ( mType = = ChromeUsedUncompressedHeap ) {
desc . AssignLiteral ( " Memory used by in-use chrome images (uncompressed data). " ) ;
} else if ( mType = = ChromeUsedUncompressedNonheap ) {
2011-07-07 19:45:16 -07:00
desc . AssignLiteral ( " Memory used by in-use chrome images (uncompressed data). " ) ;
2011-06-16 11:34:09 -07:00
} else if ( mType = = ChromeUnusedRaw ) {
2011-07-07 19:45:16 -07:00
desc . AssignLiteral ( " Memory used by not in-use chrome images (compressed data). " ) ;
2011-07-18 06:20:27 -07:00
} else if ( mType = = ChromeUnusedUncompressedHeap ) {
desc . AssignLiteral ( " Memory used by not in-use chrome images (uncompressed data). " ) ;
} else if ( mType = = ChromeUnusedUncompressedNonheap ) {
2011-07-07 19:45:16 -07:00
desc . AssignLiteral ( " Memory used by not in-use chrome images (uncompressed data). " ) ;
2011-06-16 11:34:09 -07:00
} else if ( mType = = ContentUsedRaw ) {
2011-07-07 19:45:16 -07:00
desc . AssignLiteral ( " Memory used by in-use content images (compressed data). " ) ;
2011-07-18 06:20:27 -07:00
} else if ( mType = = ContentUsedUncompressedHeap ) {
desc . AssignLiteral ( " Memory used by in-use content images (uncompressed data). " ) ;
} else if ( mType = = ContentUsedUncompressedNonheap ) {
2011-07-07 19:45:16 -07:00
desc . AssignLiteral ( " Memory used by in-use content images (uncompressed data). " ) ;
2011-06-16 11:34:09 -07:00
} else if ( mType = = ContentUnusedRaw ) {
2011-07-07 19:45:16 -07:00
desc . AssignLiteral ( " Memory used by not in-use content images (compressed data). " ) ;
2011-07-18 06:20:27 -07:00
} else if ( mType = = ContentUnusedUncompressedHeap ) {
desc . AssignLiteral ( " Memory used by not in-use content images (uncompressed data). " ) ;
} else if ( mType = = ContentUnusedUncompressedNonheap ) {
2011-07-07 19:45:16 -07:00
desc . AssignLiteral ( " Memory used by not in-use content images (uncompressed data). " ) ;
2011-06-16 11:34:09 -07:00
}
2010-05-21 21:10:14 -07:00
return NS_OK ;
}
ReporterType mType ;
} ;
NS_IMPL_ISUPPORTS1 ( imgMemoryReporter , nsIMemoryReporter )
2009-08-13 04:20:41 -07:00
NS_IMPL_ISUPPORTS3 ( nsProgressNotificationProxy ,
2009-07-28 09:13:48 -07:00
nsIProgressEventSink ,
2009-08-13 04:20:41 -07:00
nsIChannelEventSink ,
2009-07-28 09:13:48 -07:00
nsIInterfaceRequestor )
NS_IMETHODIMP
nsProgressNotificationProxy : : OnProgress ( nsIRequest * request ,
nsISupports * ctxt ,
PRUint64 progress ,
2011-07-01 10:03:30 -07:00
PRUint64 progressMax )
{
2009-07-28 09:13:48 -07:00
nsCOMPtr < nsILoadGroup > loadGroup ;
2010-11-21 11:21:57 -08:00
request - > GetLoadGroup ( getter_AddRefs ( loadGroup ) ) ;
2009-07-28 09:13:48 -07:00
nsCOMPtr < nsIProgressEventSink > target ;
NS_QueryNotificationCallbacks ( mOriginalCallbacks ,
loadGroup ,
NS_GET_IID ( nsIProgressEventSink ) ,
getter_AddRefs ( target ) ) ;
if ( ! target )
return NS_OK ;
return target - > OnProgress ( mImageRequest , ctxt , progress , progressMax ) ;
}
NS_IMETHODIMP
nsProgressNotificationProxy : : OnStatus ( nsIRequest * request ,
nsISupports * ctxt ,
nsresult status ,
2011-07-01 10:03:30 -07:00
const PRUnichar * statusArg )
{
2009-07-28 09:13:48 -07:00
nsCOMPtr < nsILoadGroup > loadGroup ;
2010-11-21 11:21:57 -08:00
request - > GetLoadGroup ( getter_AddRefs ( loadGroup ) ) ;
2009-07-28 09:13:48 -07:00
nsCOMPtr < nsIProgressEventSink > target ;
NS_QueryNotificationCallbacks ( mOriginalCallbacks ,
loadGroup ,
NS_GET_IID ( nsIProgressEventSink ) ,
getter_AddRefs ( target ) ) ;
if ( ! target )
return NS_OK ;
return target - > OnStatus ( mImageRequest , ctxt , status , statusArg ) ;
}
2009-08-13 04:20:41 -07:00
NS_IMETHODIMP
2010-08-04 19:15:55 -07:00
nsProgressNotificationProxy : : AsyncOnChannelRedirect ( nsIChannel * oldChannel ,
nsIChannel * newChannel ,
PRUint32 flags ,
2011-07-01 10:03:30 -07:00
nsIAsyncVerifyRedirectCallback * cb )
{
2009-08-13 04:20:41 -07:00
// Tell the original original callbacks about it too
nsCOMPtr < nsILoadGroup > loadGroup ;
2010-11-21 11:21:57 -08:00
newChannel - > GetLoadGroup ( getter_AddRefs ( loadGroup ) ) ;
2009-08-13 04:20:41 -07:00
nsCOMPtr < nsIChannelEventSink > target ;
NS_QueryNotificationCallbacks ( mOriginalCallbacks ,
loadGroup ,
NS_GET_IID ( nsIChannelEventSink ) ,
getter_AddRefs ( target ) ) ;
2010-08-04 19:15:55 -07:00
if ( ! target ) {
cb - > OnRedirectVerifyCallback ( NS_OK ) ;
return NS_OK ;
}
// Delegate to |target| if set, reusing |cb|
return target - > AsyncOnChannelRedirect ( oldChannel , newChannel , flags , cb ) ;
2009-08-13 04:20:41 -07:00
}
2009-07-28 09:13:48 -07:00
NS_IMETHODIMP
nsProgressNotificationProxy : : GetInterface ( const nsIID & iid ,
2011-07-01 10:03:30 -07:00
void * * result )
{
2009-07-28 09:13:48 -07:00
if ( iid . Equals ( NS_GET_IID ( nsIProgressEventSink ) ) ) {
* result = static_cast < nsIProgressEventSink * > ( this ) ;
2009-07-28 10:46:04 -07:00
NS_ADDREF_THIS ( ) ;
2009-07-28 09:13:48 -07:00
return NS_OK ;
}
2009-08-13 04:20:41 -07:00
if ( iid . Equals ( NS_GET_IID ( nsIChannelEventSink ) ) ) {
* result = static_cast < nsIChannelEventSink * > ( this ) ;
NS_ADDREF_THIS ( ) ;
return NS_OK ;
}
2009-07-28 09:13:48 -07:00
if ( mOriginalCallbacks )
return mOriginalCallbacks - > GetInterface ( iid , result ) ;
return NS_NOINTERFACE ;
}
2011-10-01 09:14:40 -07:00
static void NewRequestAndEntry ( bool aForcePrincipalCheckForCacheEntry ,
imgRequest * * aRequest , imgCacheEntry * * aEntry )
2008-09-04 09:36:27 -07:00
{
2011-10-01 09:14:40 -07:00
nsRefPtr < imgRequest > request = new imgRequest ( ) ;
nsRefPtr < imgCacheEntry > entry = new imgCacheEntry ( request , aForcePrincipalCheckForCacheEntry ) ;
request . forget ( aRequest ) ;
entry . forget ( aEntry ) ;
2008-09-04 16:00:42 -07:00
}
2008-09-04 09:36:27 -07:00
2011-09-28 23:19:26 -07:00
static bool ShouldRevalidateEntry ( imgCacheEntry * aEntry ,
2007-03-22 10:30:00 -07:00
nsLoadFlags aFlags ,
2011-09-28 23:19:26 -07:00
bool aHasExpired )
2007-03-22 10:30:00 -07:00
{
2011-09-28 23:19:26 -07:00
bool bValidateEntry = false ;
2007-03-22 10:30:00 -07:00
2008-09-04 16:00:42 -07:00
if ( aFlags & nsIRequest : : LOAD_BYPASS_CACHE )
2011-10-17 07:59:28 -07:00
return false ;
2007-03-22 10:30:00 -07:00
if ( aFlags & nsIRequest : : VALIDATE_ALWAYS ) {
2011-10-17 07:59:28 -07:00
bValidateEntry = true ;
2007-03-22 10:30:00 -07:00
}
2011-07-01 10:03:35 -07:00
else if ( aEntry - > GetMustValidate ( ) ) {
2011-10-17 07:59:28 -07:00
bValidateEntry = true ;
2011-07-01 10:03:35 -07:00
}
2007-03-22 10:30:00 -07:00
//
// The cache entry has expired... Determine whether the stale cache
// entry can be used without validation...
//
else if ( aHasExpired ) {
//
// VALIDATE_NEVER and VALIDATE_ONCE_PER_SESSION allow stale cache
// entries to be used unless they have been explicitly marked to
// indicate that revalidation is necessary.
//
if ( aFlags & ( nsIRequest : : VALIDATE_NEVER |
nsIRequest : : VALIDATE_ONCE_PER_SESSION ) )
{
2011-10-17 07:59:28 -07:00
bValidateEntry = false ;
2007-03-22 10:30:00 -07:00
}
//
// LOAD_FROM_CACHE allows a stale cache entry to be used... Otherwise,
// the entry must be revalidated.
//
else if ( ! ( aFlags & nsIRequest : : LOAD_FROM_CACHE ) ) {
2011-10-17 07:59:28 -07:00
bValidateEntry = true ;
2007-03-22 10:30:00 -07:00
}
}
return bValidateEntry ;
}
2011-07-14 11:47:34 -07:00
// Returns true if this request is compatible with the given CORS mode on the
// given loading principal, and false if the request may not be reused due
// to CORS.
static bool
2011-09-20 14:00:42 -07:00
ValidateCORSAndPrincipal ( imgRequest * request , bool forcePrincipalCheck ,
PRInt32 corsmode , nsIPrincipal * loadingPrincipal )
2011-07-14 11:47:34 -07:00
{
// If the entry's CORS mode doesn't match, or the CORS mode matches but the
// document principal isn't the same, we can't use this request.
if ( request - > GetCORSMode ( ) ! = corsmode ) {
return false ;
2011-09-20 14:00:42 -07:00
} else if ( request - > GetCORSMode ( ) ! = imgIRequest : : CORS_NONE | |
forcePrincipalCheck ) {
2011-07-14 11:47:34 -07:00
nsCOMPtr < nsIPrincipal > otherprincipal = request - > GetLoadingPrincipal ( ) ;
// If we previously had a principal, but we don't now, we can't use this
// request.
if ( otherprincipal & & ! loadingPrincipal ) {
return false ;
}
if ( otherprincipal & & loadingPrincipal ) {
2011-09-28 23:19:26 -07:00
bool equals = false ;
2011-07-14 11:47:34 -07:00
otherprincipal - > Equals ( loadingPrincipal , & equals ) ;
2011-07-17 10:40:24 -07:00
return equals ;
2011-07-14 11:47:34 -07:00
}
}
return true ;
}
2007-03-22 10:30:00 -07:00
static nsresult NewImageChannel ( nsIChannel * * aResult ,
2011-09-20 14:00:42 -07:00
// If aForcePrincipalCheckForCacheEntry is
// true, then we will force a principal check
// even when not using CORS before assuming we
// have a cache hit on a cache entry that we
// create for this channel. This is an out
// param that should be set to true if this
// channel ends up depending on
// aLoadingPrincipal and false otherwise.
bool * aForcePrincipalCheckForCacheEntry ,
2007-03-22 10:30:00 -07:00
nsIURI * aURI ,
nsIURI * aInitialDocumentURI ,
nsIURI * aReferringURI ,
nsILoadGroup * aLoadGroup ,
2009-07-15 00:22:40 -07:00
const nsCString & aAcceptHeader ,
2010-05-20 13:08:02 -07:00
nsLoadFlags aLoadFlags ,
2011-09-20 14:00:42 -07:00
nsIChannelPolicy * aPolicy ,
nsIPrincipal * aLoadingPrincipal )
2007-03-22 10:30:00 -07:00
{
nsresult rv ;
nsCOMPtr < nsIChannel > newChannel ;
nsCOMPtr < nsIHttpChannel > newHttpChannel ;
nsCOMPtr < nsIInterfaceRequestor > callbacks ;
if ( aLoadGroup ) {
// Get the notification callbacks from the load group for the new channel.
//
// XXX: This is not exactly correct, because the network request could be
// referenced by multiple windows... However, the new channel needs
// something. So, using the 'first' notification callbacks is better
// than nothing...
//
aLoadGroup - > GetNotificationCallbacks ( getter_AddRefs ( callbacks ) ) ;
}
// Pass in a NULL loadgroup because this is the underlying network request.
// This request may be referenced by several proxy image requests (psossibly
// in different documents).
// If all of the proxy requests are canceled then this request should be
// canceled too.
//
rv = NS_NewChannel ( aResult ,
aURI , // URI
nsnull , // Cached IOService
nsnull , // LoadGroup
callbacks , // Notification Callbacks
2010-05-20 13:08:02 -07:00
aLoadFlags ,
aPolicy ) ;
2007-03-22 10:30:00 -07:00
if ( NS_FAILED ( rv ) )
return rv ;
2011-09-20 14:00:42 -07:00
* aForcePrincipalCheckForCacheEntry = false ;
2007-03-22 10:30:00 -07:00
// Initialize HTTP-specific attributes
newHttpChannel = do_QueryInterface ( * aResult ) ;
if ( newHttpChannel ) {
newHttpChannel - > SetRequestHeader ( NS_LITERAL_CSTRING ( " Accept " ) ,
2009-07-15 00:22:40 -07:00
aAcceptHeader ,
2011-10-17 07:59:28 -07:00
false ) ;
2007-03-22 10:30:00 -07:00
nsCOMPtr < nsIHttpChannelInternal > httpChannelInternal = do_QueryInterface ( newHttpChannel ) ;
NS_ENSURE_TRUE ( httpChannelInternal , NS_ERROR_UNEXPECTED ) ;
httpChannelInternal - > SetDocumentURI ( aInitialDocumentURI ) ;
newHttpChannel - > SetReferrer ( aReferringURI ) ;
}
// Image channels are loaded by default with reduced priority.
nsCOMPtr < nsISupportsPriority > p = do_QueryInterface ( * aResult ) ;
if ( p ) {
PRUint32 priority = nsISupportsPriority : : PRIORITY_LOW ;
if ( aLoadFlags & nsIRequest : : LOAD_BACKGROUND )
+ + priority ; // further reduce priority for background loads
p - > AdjustPriority ( priority ) ;
}
2011-09-28 23:19:26 -07:00
bool setOwner = nsContentUtils : : SetUpChannelOwner ( aLoadingPrincipal ,
2011-10-17 07:59:28 -07:00
* aResult , aURI , false ) ;
2011-09-20 14:00:42 -07:00
* aForcePrincipalCheckForCacheEntry = setOwner ;
2007-03-22 10:30:00 -07:00
return NS_OK ;
}
2008-09-04 16:00:42 -07:00
static PRUint32 SecondsFromPRTime ( PRTime prTime )
{
return PRUint32 ( PRInt64 ( prTime ) / PRInt64 ( PR_USEC_PER_SEC ) ) ;
}
2011-09-20 14:00:42 -07:00
imgCacheEntry : : imgCacheEntry ( imgRequest * request , bool forcePrincipalCheck )
2008-09-04 16:00:42 -07:00
: mRequest ( request ) ,
mDataSize ( 0 ) ,
mTouchedTime ( SecondsFromPRTime ( PR_Now ( ) ) ) ,
mExpiryTime ( 0 ) ,
2011-10-17 07:59:28 -07:00
mMustValidate ( false ) ,
2011-07-01 10:03:38 -07:00
// We start off as evicted so we don't try to update the cache. PutIntoCache
// will set this to false.
2011-10-17 07:59:28 -07:00
mEvicted ( true ) ,
mHasNoProxies ( true ) ,
2011-09-20 14:00:42 -07:00
mForcePrincipalCheck ( forcePrincipalCheck )
2008-09-04 16:00:42 -07:00
{ }
2009-01-30 18:17:47 -08:00
imgCacheEntry : : ~ imgCacheEntry ( )
{
LOG_FUNC ( gImgLog , " imgCacheEntry::~imgCacheEntry() " ) ;
}
2011-09-28 23:19:26 -07:00
void imgCacheEntry : : Touch ( bool updateTime /* = true */ )
2008-09-04 16:00:42 -07:00
{
LOG_SCOPE ( gImgLog , " imgCacheEntry::Touch " ) ;
if ( updateTime )
mTouchedTime = SecondsFromPRTime ( PR_Now ( ) ) ;
2009-09-12 15:44:18 -07:00
UpdateCache ( ) ;
}
void imgCacheEntry : : UpdateCache ( PRInt32 diff /* = 0 */ )
{
2009-01-30 18:17:47 -08:00
// Don't update the cache if we've been removed from it or it doesn't care
// about our size or usage.
if ( ! Evicted ( ) & & HasNoProxies ( ) ) {
2008-09-04 16:00:42 -07:00
nsCOMPtr < nsIURI > uri ;
2011-09-07 16:35:54 -07:00
mRequest - > GetURI ( getter_AddRefs ( uri ) ) ;
2009-09-12 15:44:18 -07:00
imgLoader : : CacheEntriesChanged ( uri , diff ) ;
2008-09-04 16:00:42 -07:00
}
}
2011-09-28 23:19:26 -07:00
void imgCacheEntry : : SetHasNoProxies ( bool hasNoProxies )
2009-01-30 18:17:47 -08:00
{
# if defined(PR_LOGGING)
nsCOMPtr < nsIURI > uri ;
2011-09-07 16:35:54 -07:00
mRequest - > GetURI ( getter_AddRefs ( uri ) ) ;
2009-01-30 18:17:47 -08:00
nsCAutoString spec ;
if ( uri )
uri - > GetSpec ( spec ) ;
if ( hasNoProxies )
LOG_FUNC_WITH_PARAM ( gImgLog , " imgCacheEntry::SetHasNoProxies true " , " uri " , spec . get ( ) ) ;
else
LOG_FUNC_WITH_PARAM ( gImgLog , " imgCacheEntry::SetHasNoProxies false " , " uri " , spec . get ( ) ) ;
# endif
mHasNoProxies = hasNoProxies ;
}
2008-09-04 16:00:42 -07:00
imgCacheQueue : : imgCacheQueue ( )
2011-10-17 07:59:28 -07:00
: mDirty ( false ) ,
2008-09-04 16:00:42 -07:00
mSize ( 0 )
{ }
void imgCacheQueue : : UpdateSize ( PRInt32 diff )
{
mSize + = diff ;
}
PRUint32 imgCacheQueue : : GetSize ( ) const
{
return mSize ;
}
# include <algorithm>
2008-09-11 04:34:17 -07:00
using namespace std ;
2008-09-04 16:00:42 -07:00
void imgCacheQueue : : Remove ( imgCacheEntry * entry )
{
queueContainer : : iterator it = find ( mQueue . begin ( ) , mQueue . end ( ) , entry ) ;
if ( it ! = mQueue . end ( ) ) {
mSize - = ( * it ) - > GetDataSize ( ) ;
mQueue . erase ( it ) ;
MarkDirty ( ) ;
}
}
void imgCacheQueue : : Push ( imgCacheEntry * entry )
{
mSize + = entry - > GetDataSize ( ) ;
nsRefPtr < imgCacheEntry > refptr ( entry ) ;
mQueue . push_back ( refptr ) ;
MarkDirty ( ) ;
}
already_AddRefed < imgCacheEntry > imgCacheQueue : : Pop ( )
{
if ( mQueue . empty ( ) )
return nsnull ;
if ( IsDirty ( ) )
Refresh ( ) ;
nsRefPtr < imgCacheEntry > entry = mQueue [ 0 ] ;
std : : pop_heap ( mQueue . begin ( ) , mQueue . end ( ) , imgLoader : : CompareCacheEntries ) ;
mQueue . pop_back ( ) ;
mSize - = entry - > GetDataSize ( ) ;
imgCacheEntry * ret = entry ;
NS_ADDREF ( ret ) ;
return ret ;
}
void imgCacheQueue : : Refresh ( )
{
std : : make_heap ( mQueue . begin ( ) , mQueue . end ( ) , imgLoader : : CompareCacheEntries ) ;
2011-10-17 07:59:28 -07:00
mDirty = false ;
2008-09-04 16:00:42 -07:00
}
void imgCacheQueue : : MarkDirty ( )
{
2011-10-17 07:59:28 -07:00
mDirty = true ;
2008-09-04 16:00:42 -07:00
}
2011-09-28 23:19:26 -07:00
bool imgCacheQueue : : IsDirty ( )
2008-09-04 16:00:42 -07:00
{
return mDirty ;
}
PRUint32 imgCacheQueue : : GetNumElements ( ) const
{
return mQueue . size ( ) ;
}
imgCacheQueue : : iterator imgCacheQueue : : begin ( )
{
return mQueue . begin ( ) ;
}
imgCacheQueue : : const_iterator imgCacheQueue : : begin ( ) const
{
return mQueue . begin ( ) ;
}
imgCacheQueue : : iterator imgCacheQueue : : end ( )
{
return mQueue . end ( ) ;
}
imgCacheQueue : : const_iterator imgCacheQueue : : end ( ) const
{
return mQueue . end ( ) ;
}
nsresult imgLoader : : CreateNewProxyForRequest ( imgRequest * aRequest , nsILoadGroup * aLoadGroup ,
imgIDecoderObserver * aObserver ,
nsLoadFlags aLoadFlags , imgIRequest * aProxyRequest ,
imgIRequest * * _retval )
{
LOG_SCOPE_WITH_PARAM ( gImgLog , " imgLoader::CreateNewProxyForRequest " , " imgRequest " , aRequest ) ;
/* XXX If we move decoding onto separate threads, we should save off the
calling thread here and pass it off to | proxyRequest | so that it call
proxy calls to | aObserver | .
*/
imgRequestProxy * proxyRequest ;
if ( aProxyRequest ) {
proxyRequest = static_cast < imgRequestProxy * > ( aProxyRequest ) ;
} else {
2010-07-05 02:42:18 -07:00
proxyRequest = new imgRequestProxy ( ) ;
2008-09-04 16:00:42 -07:00
}
NS_ADDREF ( proxyRequest ) ;
/* It is important to call |SetLoadFlags()| before calling |Init()| because
| Init ( ) | adds the request to the loadgroup .
*/
proxyRequest - > SetLoadFlags ( aLoadFlags ) ;
2010-05-10 20:27:41 -07:00
nsCOMPtr < nsIURI > uri ;
aRequest - > GetURI ( getter_AddRefs ( uri ) ) ;
2008-09-04 16:00:42 -07:00
// init adds itself to imgRequest's list of observers
2010-05-14 13:47:59 -07:00
nsresult rv = proxyRequest - > Init ( aRequest , aLoadGroup , aRequest - > mImage , uri , aObserver ) ;
2008-09-04 16:00:42 -07:00
if ( NS_FAILED ( rv ) ) {
NS_RELEASE ( proxyRequest ) ;
return rv ;
}
// transfer reference to caller
* _retval = static_cast < imgIRequest * > ( proxyRequest ) ;
return NS_OK ;
}
2012-01-02 12:23:41 -08:00
class imgCacheObserver MOZ_FINAL : public nsIObserver
2008-09-04 16:00:42 -07:00
{
public :
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
private :
imgLoader mLoader ;
} ;
NS_IMPL_ISUPPORTS1 ( imgCacheObserver , nsIObserver )
NS_IMETHODIMP
imgCacheObserver : : Observe ( nsISupports * aSubject , const char * aTopic , const PRUnichar * aSomeData )
{
if ( strcmp ( aTopic , " memory-pressure " ) = = 0 ) {
2011-06-30 14:13:56 -07:00
DiscardTracker : : DiscardAll ( ) ;
2009-05-07 13:55:06 -07:00
mLoader . MinimizeCaches ( ) ;
2008-09-04 16:00:42 -07:00
} else if ( strcmp ( aTopic , " chrome-flush-skin-caches " ) = = 0 | |
strcmp ( aTopic , " chrome-flush-caches " ) = = 0 ) {
2009-05-07 13:55:06 -07:00
mLoader . ClearChromeImageCache ( ) ;
2008-09-04 16:00:42 -07:00
}
return NS_OK ;
}
2012-01-02 12:23:41 -08:00
class imgCacheExpirationTracker MOZ_FINAL
: public nsExpirationTracker < imgCacheEntry , 3 >
2008-09-04 16:00:42 -07:00
{
enum { TIMEOUT_SECONDS = 10 } ;
public :
imgCacheExpirationTracker ( ) ;
protected :
void NotifyExpired ( imgCacheEntry * entry ) ;
} ;
imgCacheExpirationTracker : : imgCacheExpirationTracker ( )
: nsExpirationTracker < imgCacheEntry , 3 > ( TIMEOUT_SECONDS * 1000 )
{ }
void imgCacheExpirationTracker : : NotifyExpired ( imgCacheEntry * entry )
{
2009-02-25 11:09:44 -08:00
// Hold on to a reference to this entry, because the expiration tracker
// mechanism doesn't.
nsRefPtr < imgCacheEntry > kungFuDeathGrip ( entry ) ;
2009-01-30 18:17:47 -08:00
# if defined(PR_LOGGING)
nsRefPtr < imgRequest > req ( entry - > GetRequest ( ) ) ;
if ( req ) {
nsCOMPtr < nsIURI > uri ;
2011-09-07 16:35:54 -07:00
req - > GetURI ( getter_AddRefs ( uri ) ) ;
2009-01-30 18:17:47 -08:00
nsCAutoString spec ;
uri - > GetSpec ( spec ) ;
LOG_FUNC_WITH_PARAM ( gImgLog , " imgCacheExpirationTracker::NotifyExpired " , " entry " , spec . get ( ) ) ;
}
# endif
2008-09-04 16:00:42 -07:00
// We can be called multiple times on the same entry. Don't do work multiple
// times.
if ( ! entry - > Evicted ( ) )
imgLoader : : RemoveFromCache ( entry ) ;
imgLoader : : VerifyCacheSizes ( ) ;
}
imgCacheObserver * gCacheObserver ;
imgCacheExpirationTracker * gCacheTracker ;
imgLoader : : imgCacheTable imgLoader : : sCache ;
imgCacheQueue imgLoader : : sCacheQueue ;
imgLoader : : imgCacheTable imgLoader : : sChromeCache ;
imgCacheQueue imgLoader : : sChromeCacheQueue ;
PRFloat64 imgLoader : : sCacheTimeWeight ;
PRUint32 imgLoader : : sCacheMaxSize ;
2009-07-15 00:22:40 -07:00
NS_IMPL_ISUPPORTS5 ( imgLoader , imgILoader , nsIContentSniffer , imgICache , nsISupportsWeakReference , nsIObserver )
2008-09-04 16:00:42 -07:00
imgLoader : : imgLoader ( )
{
/* member initializers and constructor code */
}
imgLoader : : ~ imgLoader ( )
{
/* destructor code */
}
void imgLoader : : VerifyCacheSizes ( )
{
2010-04-28 23:27:00 -07:00
# ifdef DEBUG
2008-09-04 16:00:42 -07:00
if ( ! gCacheTracker )
return ;
2009-02-13 15:24:22 -08:00
PRUint32 cachesize = sCache . Count ( ) + sChromeCache . Count ( ) ;
2009-01-30 18:17:47 -08:00
PRUint32 queuesize = sCacheQueue . GetNumElements ( ) + sChromeCacheQueue . GetNumElements ( ) ;
2008-09-04 16:00:42 -07:00
PRUint32 trackersize = 0 ;
for ( nsExpirationTracker < imgCacheEntry , 3 > : : Iterator it ( gCacheTracker ) ; it . Next ( ) ; )
trackersize + + ;
2009-01-30 18:17:47 -08:00
NS_ABORT_IF_FALSE ( queuesize = = trackersize , " Queue and tracker sizes out of sync! " ) ;
NS_ABORT_IF_FALSE ( queuesize < = cachesize , " Queue has more elements than cache! " ) ;
2010-04-28 23:27:00 -07:00
# endif
2008-09-04 16:00:42 -07:00
}
imgLoader : : imgCacheTable & imgLoader : : GetCache ( nsIURI * aURI )
{
2011-09-28 23:19:26 -07:00
bool chrome = false ;
2008-09-04 16:00:42 -07:00
aURI - > SchemeIs ( " chrome " , & chrome ) ;
if ( chrome )
return sChromeCache ;
else
return sCache ;
}
imgCacheQueue & imgLoader : : GetCacheQueue ( nsIURI * aURI )
{
2011-09-28 23:19:26 -07:00
bool chrome = false ;
2008-09-04 16:00:42 -07:00
aURI - > SchemeIs ( " chrome " , & chrome ) ;
if ( chrome )
return sChromeCacheQueue ;
else
return sCacheQueue ;
}
nsresult imgLoader : : InitCache ( )
{
2010-05-19 16:22:19 -07:00
NS_TIME_FUNCTION ;
2008-09-04 16:00:42 -07:00
nsresult rv ;
Bug 560095 - Use mozilla::services::GetObserverService(). r=biesi,dveditz,gavin,josh,jst,mrbkap,roc,sdwilsh,shaver,sicking,smontagu,surkov
2010-04-29 09:59:13 -07:00
nsCOMPtr < nsIObserverService > os = mozilla : : services : : GetObserverService ( ) ;
if ( ! os )
return NS_ERROR_FAILURE ;
2008-09-04 16:00:42 -07:00
gCacheObserver = new imgCacheObserver ( ) ;
NS_ADDREF ( gCacheObserver ) ;
2011-10-17 07:59:28 -07:00
os - > AddObserver ( gCacheObserver , " memory-pressure " , false ) ;
os - > AddObserver ( gCacheObserver , " chrome-flush-skin-caches " , false ) ;
os - > AddObserver ( gCacheObserver , " chrome-flush-caches " , false ) ;
2008-09-04 16:00:42 -07:00
gCacheTracker = new imgCacheExpirationTracker ( ) ;
if ( ! sCache . Init ( ) )
return NS_ERROR_OUT_OF_MEMORY ;
if ( ! sChromeCache . Init ( ) )
return NS_ERROR_OUT_OF_MEMORY ;
PRInt32 timeweight ;
2011-06-11 19:30:15 -07:00
rv = Preferences : : GetInt ( " image.cache.timeweight " , & timeweight ) ;
2008-09-04 16:00:42 -07:00
if ( NS_SUCCEEDED ( rv ) )
sCacheTimeWeight = timeweight / 1000.0 ;
else
sCacheTimeWeight = 0.5 ;
PRInt32 cachesize ;
2011-06-11 19:30:15 -07:00
rv = Preferences : : GetInt ( " image.cache.size " , & cachesize ) ;
2008-09-04 16:00:42 -07:00
if ( NS_SUCCEEDED ( rv ) )
sCacheMaxSize = cachesize ;
else
sCacheMaxSize = 5 * 1024 * 1024 ;
2009-07-15 00:22:40 -07:00
2010-05-21 21:10:14 -07:00
NS_RegisterMemoryReporter ( new imgMemoryReporter ( imgMemoryReporter : : ChromeUsedRaw ) ) ;
2011-07-18 06:20:27 -07:00
NS_RegisterMemoryReporter ( new imgMemoryReporter ( imgMemoryReporter : : ChromeUsedUncompressedHeap ) ) ;
NS_RegisterMemoryReporter ( new imgMemoryReporter ( imgMemoryReporter : : ChromeUsedUncompressedNonheap ) ) ;
2010-05-21 21:10:14 -07:00
NS_RegisterMemoryReporter ( new imgMemoryReporter ( imgMemoryReporter : : ChromeUnusedRaw ) ) ;
2011-07-18 06:20:27 -07:00
NS_RegisterMemoryReporter ( new imgMemoryReporter ( imgMemoryReporter : : ChromeUnusedUncompressedHeap ) ) ;
NS_RegisterMemoryReporter ( new imgMemoryReporter ( imgMemoryReporter : : ChromeUnusedUncompressedNonheap ) ) ;
2010-05-21 21:10:14 -07:00
NS_RegisterMemoryReporter ( new imgMemoryReporter ( imgMemoryReporter : : ContentUsedRaw ) ) ;
2011-07-18 06:20:27 -07:00
NS_RegisterMemoryReporter ( new imgMemoryReporter ( imgMemoryReporter : : ContentUsedUncompressedHeap ) ) ;
NS_RegisterMemoryReporter ( new imgMemoryReporter ( imgMemoryReporter : : ContentUsedUncompressedNonheap ) ) ;
2010-05-21 21:10:14 -07:00
NS_RegisterMemoryReporter ( new imgMemoryReporter ( imgMemoryReporter : : ContentUnusedRaw ) ) ;
2011-07-18 06:20:27 -07:00
NS_RegisterMemoryReporter ( new imgMemoryReporter ( imgMemoryReporter : : ContentUnusedUncompressedHeap ) ) ;
NS_RegisterMemoryReporter ( new imgMemoryReporter ( imgMemoryReporter : : ContentUnusedUncompressedNonheap ) ) ;
2010-05-21 21:10:14 -07:00
2008-09-04 16:00:42 -07:00
return NS_OK ;
}
2009-07-15 00:22:40 -07:00
nsresult imgLoader : : Init ( )
{
2011-06-11 19:30:15 -07:00
ReadAcceptHeaderPref ( ) ;
2009-07-15 00:22:40 -07:00
2011-06-11 19:30:15 -07:00
Preferences : : AddWeakObserver ( this , " image.http.accept " ) ;
2009-07-15 00:22:40 -07:00
2010-07-11 18:01:53 -07:00
// Listen for when we leave private browsing mode
nsCOMPtr < nsIObserverService > obService = mozilla : : services : : GetObserverService ( ) ;
if ( obService )
2011-10-17 07:59:28 -07:00
obService - > AddObserver ( this , NS_PRIVATE_BROWSING_SWITCH_TOPIC , true ) ;
2010-07-11 18:01:53 -07:00
2009-07-15 00:22:40 -07:00
return NS_OK ;
}
NS_IMETHODIMP
imgLoader : : Observe ( nsISupports * aSubject , const char * aTopic , const PRUnichar * aData )
{
2010-07-11 18:01:53 -07:00
// We listen for pref change notifications...
if ( ! strcmp ( aTopic , NS_PREFBRANCH_PREFCHANGE_TOPIC_ID ) ) {
if ( ! strcmp ( NS_ConvertUTF16toUTF8 ( aData ) . get ( ) , " image.http.accept " ) ) {
2011-06-11 19:30:15 -07:00
ReadAcceptHeaderPref ( ) ;
2010-07-11 18:01:53 -07:00
}
}
2009-07-15 00:22:40 -07:00
2010-07-11 18:01:53 -07:00
// ...and exits from private browsing.
else if ( ! strcmp ( aTopic , NS_PRIVATE_BROWSING_SWITCH_TOPIC ) ) {
if ( NS_LITERAL_STRING ( NS_PRIVATE_BROWSING_LEAVE ) . Equals ( aData ) )
ClearImageCache ( ) ;
2009-07-15 00:22:40 -07:00
}
2010-07-11 18:01:53 -07:00
// (Nothing else should bring us here)
else {
NS_ABORT_IF_FALSE ( 0 , " Invalid topic received " ) ;
}
2009-07-15 00:22:40 -07:00
return NS_OK ;
}
2011-06-11 19:30:15 -07:00
void imgLoader : : ReadAcceptHeaderPref ( )
2009-07-15 00:22:40 -07:00
{
2011-06-11 19:30:15 -07:00
nsAdoptingCString accept = Preferences : : GetCString ( " image.http.accept " ) ;
if ( accept )
2009-07-15 00:22:40 -07:00
mAcceptHeader = accept ;
else
mAcceptHeader = " image/png,image/*;q=0.8,*/*;q=0.5 " ;
}
2008-09-04 16:00:42 -07:00
/* void clearCache (in boolean chrome); */
2011-09-28 23:19:26 -07:00
NS_IMETHODIMP imgLoader : : ClearCache ( bool chrome )
2008-09-04 16:00:42 -07:00
{
if ( chrome )
return ClearChromeImageCache ( ) ;
else
return ClearImageCache ( ) ;
}
/* void removeEntry(in nsIURI uri); */
NS_IMETHODIMP imgLoader : : RemoveEntry ( nsIURI * uri )
{
if ( RemoveFromCache ( uri ) )
return NS_OK ;
return NS_ERROR_NOT_AVAILABLE ;
}
/* imgIRequest findEntry(in nsIURI uri); */
NS_IMETHODIMP imgLoader : : FindEntryProperties ( nsIURI * uri , nsIProperties * * _retval )
{
nsRefPtr < imgCacheEntry > entry ;
nsCAutoString spec ;
imgCacheTable & cache = GetCache ( uri ) ;
uri - > GetSpec ( spec ) ;
* _retval = nsnull ;
if ( cache . Get ( spec , getter_AddRefs ( entry ) ) & & entry ) {
2009-01-30 18:17:47 -08:00
if ( gCacheTracker & & entry - > HasNoProxies ( ) )
2008-09-04 16:00:42 -07:00
gCacheTracker - > MarkUsed ( entry ) ;
2009-01-30 18:17:47 -08:00
2008-09-04 16:00:42 -07:00
nsRefPtr < imgRequest > request = getter_AddRefs ( entry - > GetRequest ( ) ) ;
if ( request ) {
* _retval = request - > Properties ( ) ;
NS_ADDREF ( * _retval ) ;
}
}
return NS_OK ;
}
void imgLoader : : Shutdown ( )
{
ClearChromeImageCache ( ) ;
ClearImageCache ( ) ;
NS_IF_RELEASE ( gCacheObserver ) ;
delete gCacheTracker ;
gCacheTracker = nsnull ;
}
nsresult imgLoader : : ClearChromeImageCache ( )
{
2009-05-07 13:55:06 -07:00
return EvictEntries ( sChromeCache ) ;
2008-09-04 16:00:42 -07:00
}
nsresult imgLoader : : ClearImageCache ( )
{
2009-05-07 13:55:06 -07:00
return EvictEntries ( sCache ) ;
}
void imgLoader : : MinimizeCaches ( )
{
EvictEntries ( sCacheQueue ) ;
EvictEntries ( sChromeCacheQueue ) ;
2008-09-04 16:00:42 -07:00
}
2011-09-28 23:19:26 -07:00
bool imgLoader : : PutIntoCache ( nsIURI * key , imgCacheEntry * entry )
2008-09-04 16:00:42 -07:00
{
imgCacheTable & cache = GetCache ( key ) ;
nsCAutoString spec ;
key - > GetSpec ( spec ) ;
2009-01-30 18:17:47 -08:00
LOG_STATIC_FUNC_WITH_PARAM ( gImgLog , " imgLoader::PutIntoCache " , " uri " , spec . get ( ) ) ;
2008-09-04 16:00:42 -07:00
// Check to see if this request already exists in the cache and is being
// loaded on a different thread. If so, don't allow this entry to be added to
// the cache.
nsRefPtr < imgCacheEntry > tmpCacheEntry ;
if ( cache . Get ( spec , getter_AddRefs ( tmpCacheEntry ) ) & & tmpCacheEntry ) {
PR_LOG ( gImgLog , PR_LOG_DEBUG ,
( " [this=%p] imgLoader::PutIntoCache -- Element already in the cache " , nsnull ) ) ;
nsRefPtr < imgRequest > tmpRequest = getter_AddRefs ( tmpCacheEntry - > GetRequest ( ) ) ;
2008-09-30 14:47:47 -07:00
// If it already exists, and we're putting the same key into the cache, we
// should remove the old version.
PR_LOG ( gImgLog , PR_LOG_DEBUG ,
( " [this=%p] imgLoader::PutIntoCache -- Replacing cached element " , nsnull ) ) ;
2008-09-04 16:00:42 -07:00
2008-09-30 14:47:47 -07:00
RemoveFromCache ( key ) ;
} else {
PR_LOG ( gImgLog , PR_LOG_DEBUG ,
( " [this=%p] imgLoader::PutIntoCache -- Element NOT already in the cache " , nsnull ) ) ;
2008-09-04 16:00:42 -07:00
}
if ( ! cache . Put ( spec , entry ) )
2011-10-17 07:59:28 -07:00
return false ;
2008-09-04 16:00:42 -07:00
2009-03-17 14:07:16 -07:00
// We can be called to resurrect an evicted entry.
if ( entry - > Evicted ( ) )
2011-10-17 07:59:28 -07:00
entry - > SetEvicted ( false ) ;
2009-03-17 14:07:16 -07:00
// If we're resurrecting an entry with no proxies, put it back in the
// tracker and queue.
if ( entry - > HasNoProxies ( ) ) {
nsresult addrv = NS_OK ;
if ( gCacheTracker )
addrv = gCacheTracker - > AddObject ( entry ) ;
if ( NS_SUCCEEDED ( addrv ) ) {
imgCacheQueue & queue = GetCacheQueue ( key ) ;
queue . Push ( entry ) ;
}
}
nsRefPtr < imgRequest > request ( getter_AddRefs ( entry - > GetRequest ( ) ) ) ;
2011-10-17 07:59:28 -07:00
request - > SetIsInCache ( true ) ;
2009-03-17 14:07:16 -07:00
2011-10-17 07:59:28 -07:00
return true ;
2009-01-30 18:17:47 -08:00
}
2011-09-28 23:19:26 -07:00
bool imgLoader : : SetHasNoProxies ( nsIURI * key , imgCacheEntry * entry )
2009-01-30 18:17:47 -08:00
{
# if defined(PR_LOGGING)
nsCAutoString spec ;
key - > GetSpec ( spec ) ;
LOG_STATIC_FUNC_WITH_PARAM ( gImgLog , " imgLoader::SetHasNoProxies " , " uri " , spec . get ( ) ) ;
# endif
if ( entry - > Evicted ( ) )
2011-10-17 07:59:28 -07:00
return false ;
2009-01-30 18:17:47 -08:00
2008-09-04 16:00:42 -07:00
imgCacheQueue & queue = GetCacheQueue ( key ) ;
2009-01-30 18:17:47 -08:00
nsresult addrv = NS_OK ;
2008-09-04 16:00:42 -07:00
if ( gCacheTracker )
2009-01-30 18:17:47 -08:00
addrv = gCacheTracker - > AddObject ( entry ) ;
if ( NS_SUCCEEDED ( addrv ) ) {
queue . Push ( entry ) ;
2011-10-17 07:59:28 -07:00
entry - > SetHasNoProxies ( true ) ;
2009-01-30 18:17:47 -08:00
}
2008-09-04 16:00:42 -07:00
2009-01-30 18:17:47 -08:00
imgCacheTable & cache = GetCache ( key ) ;
2008-09-04 16:00:42 -07:00
CheckCacheLimits ( cache , queue ) ;
2011-10-17 07:59:28 -07:00
return true ;
2008-09-04 16:00:42 -07:00
}
2011-09-28 23:19:26 -07:00
bool imgLoader : : SetHasProxies ( nsIURI * key )
2009-01-30 18:17:47 -08:00
{
VerifyCacheSizes ( ) ;
imgCacheTable & cache = GetCache ( key ) ;
nsCAutoString spec ;
key - > GetSpec ( spec ) ;
LOG_STATIC_FUNC_WITH_PARAM ( gImgLog , " imgLoader::SetHasProxies " , " uri " , spec . get ( ) ) ;
nsRefPtr < imgCacheEntry > entry ;
if ( cache . Get ( spec , getter_AddRefs ( entry ) ) & & entry & & entry - > HasNoProxies ( ) ) {
imgCacheQueue & queue = GetCacheQueue ( key ) ;
queue . Remove ( entry ) ;
if ( gCacheTracker )
gCacheTracker - > RemoveObject ( entry ) ;
2011-10-17 07:59:28 -07:00
entry - > SetHasNoProxies ( false ) ;
2009-01-30 18:17:47 -08:00
2011-10-17 07:59:28 -07:00
return true ;
2009-01-30 18:17:47 -08:00
}
2011-10-17 07:59:28 -07:00
return false ;
2009-01-30 18:17:47 -08:00
}
2008-09-04 16:00:42 -07:00
void imgLoader : : CacheEntriesChanged ( nsIURI * uri , PRInt32 sizediff /* = 0 */ )
{
imgCacheQueue & queue = GetCacheQueue ( uri ) ;
queue . MarkDirty ( ) ;
queue . UpdateSize ( sizediff ) ;
}
void imgLoader : : CheckCacheLimits ( imgCacheTable & cache , imgCacheQueue & queue )
{
if ( queue . GetNumElements ( ) = = 0 )
NS_ASSERTION ( queue . GetSize ( ) = = 0 ,
" imgLoader::CheckCacheLimits -- incorrect cache size " ) ;
// Remove entries from the cache until we're back under our desired size.
while ( queue . GetSize ( ) > = sCacheMaxSize ) {
// Remove the first entry in the queue.
nsRefPtr < imgCacheEntry > entry ( queue . Pop ( ) ) ;
NS_ASSERTION ( entry , " imgLoader::CheckCacheLimits -- NULL entry pointer " ) ;
2009-01-30 18:17:47 -08:00
# if defined(PR_LOGGING)
nsRefPtr < imgRequest > req ( entry - > GetRequest ( ) ) ;
if ( req ) {
nsCOMPtr < nsIURI > uri ;
2011-09-07 16:35:54 -07:00
req - > GetURI ( getter_AddRefs ( uri ) ) ;
2009-01-30 18:17:47 -08:00
nsCAutoString spec ;
uri - > GetSpec ( spec ) ;
LOG_STATIC_FUNC_WITH_PARAM ( gImgLog , " imgLoader::CheckCacheLimits " , " entry " , spec . get ( ) ) ;
}
# endif
2008-09-04 16:00:42 -07:00
if ( entry )
RemoveFromCache ( entry ) ;
}
}
2011-09-28 23:19:26 -07:00
bool imgLoader : : ValidateRequestWithNewChannel ( imgRequest * request ,
2008-09-04 16:00:42 -07:00
nsIURI * aURI ,
nsIURI * aInitialDocumentURI ,
nsIURI * aReferrerURI ,
nsILoadGroup * aLoadGroup ,
imgIDecoderObserver * aObserver ,
nsISupports * aCX ,
nsLoadFlags aLoadFlags ,
imgIRequest * aExistingRequest ,
2010-05-20 13:08:02 -07:00
imgIRequest * * aProxyRequest ,
2011-07-14 11:47:32 -07:00
nsIChannelPolicy * aPolicy ,
2011-07-14 11:47:34 -07:00
nsIPrincipal * aLoadingPrincipal ,
PRInt32 aCORSMode )
2008-09-04 16:00:42 -07:00
{
// now we need to insert a new channel request object inbetween the real
// request and the proxy that basically delays loading the image until it
// gets a 304 or figures out that this needs to be a new request
nsresult rv ;
2009-01-30 18:17:47 -08:00
// If we're currently in the middle of validating this request, just hand
// back a proxy to it; the required work will be done for us.
2008-09-04 16:00:42 -07:00
if ( request - > mValidator ) {
rv = CreateNewProxyForRequest ( request , aLoadGroup , aObserver ,
aLoadFlags , aExistingRequest ,
reinterpret_cast < imgIRequest * * > ( aProxyRequest ) ) ;
2010-05-25 07:34:04 -07:00
if ( NS_FAILED ( rv ) ) {
2011-10-17 07:59:28 -07:00
return false ;
2010-05-25 07:34:04 -07:00
}
2008-09-04 16:00:42 -07:00
2010-08-02 12:44:49 -07:00
if ( * aProxyRequest ) {
imgRequestProxy * proxy = static_cast < imgRequestProxy * > ( * aProxyRequest ) ;
// We will send notifications from imgCacheValidator::OnStartRequest().
// In the mean time, we must defer notifications because we are added to
// the imgRequest's proxy list, and we can get extra notifications
// resulting from methods such as RequestDecode(). See bug 579122.
2011-10-17 07:59:28 -07:00
proxy - > SetNotificationsDeferred ( true ) ;
2010-08-02 12:44:49 -07:00
// Attach the proxy without notifying
request - > mValidator - > AddProxy ( proxy ) ;
}
2008-09-04 16:00:42 -07:00
return NS_SUCCEEDED ( rv ) ;
} else {
2011-07-01 10:03:42 -07:00
// We will rely on Necko to cache this request when it's possible, and to
// tell imgCacheValidator::OnStartRequest whether the request came from its
// cache.
2008-09-04 16:00:42 -07:00
nsCOMPtr < nsIChannel > newChannel ;
2011-09-20 14:00:42 -07:00
bool forcePrincipalCheck ;
2008-09-04 16:00:42 -07:00
rv = NewImageChannel ( getter_AddRefs ( newChannel ) ,
2011-09-20 14:00:42 -07:00
& forcePrincipalCheck ,
2008-09-04 16:00:42 -07:00
aURI ,
aInitialDocumentURI ,
aReferrerURI ,
aLoadGroup ,
2009-07-15 00:22:40 -07:00
mAcceptHeader ,
2010-05-20 13:08:02 -07:00
aLoadFlags ,
2011-09-20 14:00:42 -07:00
aPolicy ,
aLoadingPrincipal ) ;
2008-09-04 16:00:42 -07:00
if ( NS_FAILED ( rv ) ) {
2011-10-17 07:59:28 -07:00
return false ;
2008-09-04 16:00:42 -07:00
}
nsCOMPtr < imgIRequest > req ;
rv = CreateNewProxyForRequest ( request , aLoadGroup , aObserver ,
aLoadFlags , aExistingRequest , getter_AddRefs ( req ) ) ;
if ( NS_FAILED ( rv ) ) {
2011-10-17 07:59:28 -07:00
return false ;
2008-09-04 16:00:42 -07:00
}
2009-07-28 09:13:48 -07:00
// Make sure that OnStatus/OnProgress calls have the right request set...
2011-07-01 10:03:38 -07:00
nsRefPtr < nsProgressNotificationProxy > progressproxy =
new nsProgressNotificationProxy ( newChannel , req ) ;
if ( ! progressproxy )
2011-10-17 07:59:28 -07:00
return false ;
2009-07-28 09:13:48 -07:00
2011-09-20 14:00:42 -07:00
nsRefPtr < imgCacheValidator > hvc =
new imgCacheValidator ( progressproxy , request , aCX , forcePrincipalCheck ) ;
2011-07-14 11:47:34 -07:00
nsCOMPtr < nsIStreamListener > listener = hvc . get ( ) ;
if ( aCORSMode ! = imgIRequest : : CORS_NONE ) {
2011-09-28 23:19:26 -07:00
bool withCredentials = aCORSMode = = imgIRequest : : CORS_USE_CREDENTIALS ;
2011-07-14 11:47:34 -07:00
nsCOMPtr < nsIStreamListener > corsproxy =
new nsCORSListenerProxy ( hvc , aLoadingPrincipal , newChannel , withCredentials , & rv ) ;
if ( NS_FAILED ( rv ) ) {
2011-10-17 07:59:28 -07:00
return false ;
2011-07-14 11:47:34 -07:00
}
listener = corsproxy ;
2008-09-04 16:00:42 -07:00
}
2011-07-01 10:03:38 -07:00
newChannel - > SetNotificationCallbacks ( hvc ) ;
2008-09-04 16:00:42 -07:00
request - > mValidator = hvc ;
2010-08-02 12:44:49 -07:00
imgRequestProxy * proxy = static_cast < imgRequestProxy * >
( static_cast < imgIRequest * > ( req . get ( ) ) ) ;
// We will send notifications from imgCacheValidator::OnStartRequest().
// In the mean time, we must defer notifications because we are added to
// the imgRequest's proxy list, and we can get extra notifications
// resulting from methods such as RequestDecode(). See bug 579122.
2011-10-17 07:59:28 -07:00
proxy - > SetNotificationsDeferred ( true ) ;
2010-08-02 12:44:49 -07:00
// Add the proxy without notifying
hvc - > AddProxy ( proxy ) ;
2008-09-04 16:00:42 -07:00
2011-07-14 11:47:34 -07:00
rv = newChannel - > AsyncOpen ( listener , nsnull ) ;
2008-09-04 16:00:42 -07:00
if ( NS_SUCCEEDED ( rv ) )
NS_ADDREF ( * aProxyRequest = req . get ( ) ) ;
return NS_SUCCEEDED ( rv ) ;
}
}
2011-09-28 23:19:26 -07:00
bool imgLoader : : ValidateEntry ( imgCacheEntry * aEntry ,
2008-09-04 16:00:42 -07:00
nsIURI * aURI ,
nsIURI * aInitialDocumentURI ,
nsIURI * aReferrerURI ,
nsILoadGroup * aLoadGroup ,
imgIDecoderObserver * aObserver ,
nsISupports * aCX ,
nsLoadFlags aLoadFlags ,
2011-09-28 23:19:26 -07:00
bool aCanMakeNewChannel ,
2008-09-04 16:00:42 -07:00
imgIRequest * aExistingRequest ,
2010-05-20 13:08:02 -07:00
imgIRequest * * aProxyRequest ,
2011-07-14 11:47:32 -07:00
nsIChannelPolicy * aPolicy ,
2011-07-14 11:47:34 -07:00
nsIPrincipal * aLoadingPrincipal ,
PRInt32 aCORSMode )
2008-09-04 16:00:42 -07:00
{
LOG_SCOPE ( gImgLog , " imgLoader::ValidateEntry " ) ;
2011-09-28 23:19:26 -07:00
bool hasExpired ;
2008-09-04 16:00:42 -07:00
PRUint32 expirationTime = aEntry - > GetExpiryTime ( ) ;
if ( expirationTime < = SecondsFromPRTime ( PR_Now ( ) ) ) {
2011-10-17 07:59:28 -07:00
hasExpired = true ;
2008-09-04 16:00:42 -07:00
} else {
2011-10-17 07:59:28 -07:00
hasExpired = false ;
2008-09-04 16:00:42 -07:00
}
nsresult rv ;
// Special treatment for file URLs - aEntry has expired if file has changed
nsCOMPtr < nsIFileURL > fileUrl ( do_QueryInterface ( aURI ) ) ;
if ( fileUrl ) {
PRUint32 lastModTime = aEntry - > GetTouchedTime ( ) ;
nsCOMPtr < nsIFile > theFile ;
rv = fileUrl - > GetFile ( getter_AddRefs ( theFile ) ) ;
if ( NS_SUCCEEDED ( rv ) ) {
PRInt64 fileLastMod ;
rv = theFile - > GetLastModifiedTime ( & fileLastMod ) ;
if ( NS_SUCCEEDED ( rv ) ) {
// nsIFile uses millisec, NSPR usec
fileLastMod * = 1000 ;
hasExpired = SecondsFromPRTime ( ( PRTime ) fileLastMod ) > lastModTime ;
}
}
}
nsRefPtr < imgRequest > request ( aEntry - > GetRequest ( ) ) ;
if ( ! request )
2011-10-17 07:59:28 -07:00
return false ;
2008-09-04 16:00:42 -07:00
2011-09-20 14:00:42 -07:00
if ( ! ValidateCORSAndPrincipal ( request , aEntry - > ForcePrincipalCheck ( ) ,
aCORSMode , aLoadingPrincipal ) )
2011-10-17 07:59:28 -07:00
return false ;
2011-07-14 11:47:34 -07:00
2011-09-28 23:19:26 -07:00
bool validateRequest = false ;
2008-09-04 16:00:42 -07:00
// If the request's loadId is the same as the aCX, then it is ok to use
// this one because it has already been validated for this context.
//
// XXX: nsnull seems to be a 'special' key value that indicates that NO
// validation is required.
//
void * key = ( void * ) aCX ;
if ( request - > mLoadId ! = key ) {
2009-11-12 15:00:30 -08:00
// If we would need to revalidate this entry, but we're being told to
// bypass the cache, we don't allow this entry to be used.
if ( aLoadFlags & nsIRequest : : LOAD_BYPASS_CACHE )
2011-10-17 07:59:28 -07:00
return false ;
2009-11-12 15:00:30 -08:00
2008-09-04 16:00:42 -07:00
// Determine whether the cache aEntry must be revalidated...
validateRequest = ShouldRevalidateEntry ( aEntry , aLoadFlags , hasExpired ) ;
PR_LOG ( gImgLog , PR_LOG_DEBUG ,
( " imgLoader::ValidateEntry validating cache entry. "
" validateRequest = %d " , validateRequest ) ) ;
}
# if defined(PR_LOGGING)
else if ( ! key ) {
nsCAutoString spec ;
aURI - > GetSpec ( spec ) ;
PR_LOG ( gImgLog , PR_LOG_DEBUG ,
( " imgLoader::ValidateEntry BYPASSING cache validation for %s "
" because of NULL LoadID " , spec . get ( ) ) ) ;
}
# endif
2009-01-04 21:52:22 -08:00
// We can't use a cached request if it comes from a different
// application cache than this load is expecting.
nsCOMPtr < nsIApplicationCacheContainer > appCacheContainer ;
nsCOMPtr < nsIApplicationCache > requestAppCache ;
nsCOMPtr < nsIApplicationCache > groupAppCache ;
if ( ( appCacheContainer = do_GetInterface ( request - > mRequest ) ) )
appCacheContainer - > GetApplicationCache ( getter_AddRefs ( requestAppCache ) ) ;
if ( ( appCacheContainer = do_QueryInterface ( aLoadGroup ) ) )
appCacheContainer - > GetApplicationCache ( getter_AddRefs ( groupAppCache ) ) ;
if ( requestAppCache ! = groupAppCache ) {
PR_LOG ( gImgLog , PR_LOG_DEBUG ,
( " imgLoader::ValidateEntry - Unable to use cached imgRequest "
" [request=%p] because of mismatched application caches \n " ,
address_of ( request ) ) ) ;
2011-10-17 07:59:28 -07:00
return false ;
2009-01-04 21:52:22 -08:00
}
2008-09-04 16:00:42 -07:00
if ( validateRequest & & aCanMakeNewChannel ) {
LOG_SCOPE ( gImgLog , " imgLoader::ValidateRequest |cache hit| must validate " ) ;
return ValidateRequestWithNewChannel ( request , aURI , aInitialDocumentURI ,
aReferrerURI , aLoadGroup , aObserver ,
aCX , aLoadFlags , aExistingRequest ,
2011-07-14 11:47:32 -07:00
aProxyRequest , aPolicy ,
2011-07-14 11:47:34 -07:00
aLoadingPrincipal , aCORSMode ) ;
}
2008-09-04 16:00:42 -07:00
return ! validateRequest ;
}
2011-09-28 23:19:26 -07:00
bool imgLoader : : RemoveFromCache ( nsIURI * aKey )
2008-09-04 16:00:42 -07:00
{
2011-10-17 07:59:28 -07:00
if ( ! aKey ) return false ;
2008-09-04 16:00:42 -07:00
imgCacheTable & cache = GetCache ( aKey ) ;
imgCacheQueue & queue = GetCacheQueue ( aKey ) ;
nsCAutoString spec ;
aKey - > GetSpec ( spec ) ;
2009-01-30 18:17:47 -08:00
LOG_STATIC_FUNC_WITH_PARAM ( gImgLog , " imgLoader::RemoveFromCache " , " uri " , spec . get ( ) ) ;
2008-09-04 16:00:42 -07:00
nsRefPtr < imgCacheEntry > entry ;
if ( cache . Get ( spec , getter_AddRefs ( entry ) ) & & entry ) {
cache . Remove ( spec ) ;
2009-01-30 18:17:47 -08:00
NS_ABORT_IF_FALSE ( ! entry - > Evicted ( ) , " Evicting an already-evicted cache entry! " ) ;
// Entries with no proxies are in the tracker.
if ( entry - > HasNoProxies ( ) ) {
if ( gCacheTracker )
gCacheTracker - > RemoveObject ( entry ) ;
queue . Remove ( entry ) ;
}
2011-10-17 07:59:28 -07:00
entry - > SetEvicted ( true ) ;
2009-01-30 18:17:47 -08:00
2009-03-17 14:07:16 -07:00
nsRefPtr < imgRequest > request ( getter_AddRefs ( entry - > GetRequest ( ) ) ) ;
2011-10-17 07:59:28 -07:00
request - > SetIsInCache ( false ) ;
2009-03-17 14:07:16 -07:00
2011-10-17 07:59:28 -07:00
return true ;
2008-09-04 16:00:42 -07:00
}
else
2011-10-17 07:59:28 -07:00
return false ;
2008-09-04 16:00:42 -07:00
}
2011-09-28 23:19:26 -07:00
bool imgLoader : : RemoveFromCache ( imgCacheEntry * entry )
2008-09-04 16:00:42 -07:00
{
LOG_STATIC_FUNC ( gImgLog , " imgLoader::RemoveFromCache entry " ) ;
2009-01-30 18:17:47 -08:00
2008-09-04 16:00:42 -07:00
nsRefPtr < imgRequest > request ( getter_AddRefs ( entry - > GetRequest ( ) ) ) ;
if ( request ) {
nsCOMPtr < nsIURI > key ;
2011-09-07 16:35:54 -07:00
if ( NS_SUCCEEDED ( request - > GetURI ( getter_AddRefs ( key ) ) ) & & key ) {
2009-01-30 18:17:47 -08:00
imgCacheTable & cache = GetCache ( key ) ;
imgCacheQueue & queue = GetCacheQueue ( key ) ;
nsCAutoString spec ;
key - > GetSpec ( spec ) ;
LOG_STATIC_FUNC_WITH_PARAM ( gImgLog , " imgLoader::RemoveFromCache " , " entry's uri " , spec . get ( ) ) ;
cache . Remove ( spec ) ;
if ( entry - > HasNoProxies ( ) ) {
LOG_STATIC_FUNC ( gImgLog , " imgLoader::RemoveFromCache removing from tracker " ) ;
if ( gCacheTracker )
gCacheTracker - > RemoveObject ( entry ) ;
queue . Remove ( entry ) ;
}
2011-10-17 07:59:28 -07:00
entry - > SetEvicted ( true ) ;
request - > SetIsInCache ( false ) ;
2009-01-30 18:17:47 -08:00
2011-10-17 07:59:28 -07:00
return true ;
2009-01-30 18:17:47 -08:00
}
2008-09-04 16:00:42 -07:00
}
2011-10-17 07:59:28 -07:00
return false ;
2009-01-30 18:17:47 -08:00
}
static PLDHashOperator EnumEvictEntries ( const nsACString & ,
nsRefPtr < imgCacheEntry > & aData ,
void * data )
{
nsTArray < nsRefPtr < imgCacheEntry > > * entries =
reinterpret_cast < nsTArray < nsRefPtr < imgCacheEntry > > * > ( data ) ;
entries - > AppendElement ( aData ) ;
return PL_DHASH_NEXT ;
2008-09-04 16:00:42 -07:00
}
2009-05-07 13:55:06 -07:00
nsresult imgLoader : : EvictEntries ( imgCacheTable & aCacheToClear )
2008-09-04 16:00:42 -07:00
{
2009-05-07 13:55:06 -07:00
LOG_STATIC_FUNC ( gImgLog , " imgLoader::EvictEntries table " ) ;
2008-09-04 16:00:42 -07:00
// We have to make a temporary, since RemoveFromCache removes the element
// from the queue, invalidating iterators.
nsTArray < nsRefPtr < imgCacheEntry > > entries ;
2009-01-30 18:17:47 -08:00
aCacheToClear . Enumerate ( EnumEvictEntries , & entries ) ;
for ( PRUint32 i = 0 ; i < entries . Length ( ) ; + + i )
2008-09-04 16:00:42 -07:00
if ( ! RemoveFromCache ( entries [ i ] ) )
return NS_ERROR_FAILURE ;
return NS_OK ;
}
2009-05-07 13:55:06 -07:00
nsresult imgLoader : : EvictEntries ( imgCacheQueue & aQueueToClear )
{
LOG_STATIC_FUNC ( gImgLog , " imgLoader::EvictEntries queue " ) ;
// We have to make a temporary, since RemoveFromCache removes the element
// from the queue, invalidating iterators.
nsTArray < nsRefPtr < imgCacheEntry > > entries ( aQueueToClear . GetNumElements ( ) ) ;
for ( imgCacheQueue : : const_iterator i = aQueueToClear . begin ( ) ; i ! = aQueueToClear . end ( ) ; + + i )
entries . AppendElement ( * i ) ;
for ( PRUint32 i = 0 ; i < entries . Length ( ) ; + + i )
if ( ! RemoveFromCache ( entries [ i ] ) )
return NS_ERROR_FAILURE ;
return NS_OK ;
}
2008-09-04 16:00:42 -07:00
# define LOAD_FLAGS_CACHE_MASK (nsIRequest::LOAD_BYPASS_CACHE | \
nsIRequest : : LOAD_FROM_CACHE )
# define LOAD_FLAGS_VALIDATE_MASK (nsIRequest::VALIDATE_ALWAYS | \
nsIRequest : : VALIDATE_NEVER | \
nsIRequest : : VALIDATE_ONCE_PER_SESSION )
2011-07-14 11:47:32 -07:00
/* 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); */
2007-03-22 10:30:00 -07:00
NS_IMETHODIMP imgLoader : : LoadImage ( nsIURI * aURI ,
nsIURI * aInitialDocumentURI ,
nsIURI * aReferrerURI ,
2011-07-14 11:47:32 -07:00
nsIPrincipal * aLoadingPrincipal ,
2007-03-22 10:30:00 -07:00
nsILoadGroup * aLoadGroup ,
imgIDecoderObserver * aObserver ,
nsISupports * aCX ,
nsLoadFlags aLoadFlags ,
2008-09-04 16:00:42 -07:00
nsISupports * aCacheKey ,
2007-03-22 10:30:00 -07:00
imgIRequest * aRequest ,
2010-05-20 13:08:02 -07:00
nsIChannelPolicy * aPolicy ,
2007-03-22 10:30:00 -07:00
imgIRequest * * _retval )
{
2008-09-04 16:00:42 -07:00
VerifyCacheSizes ( ) ;
2007-03-22 10:30:00 -07:00
2008-09-04 16:00:42 -07:00
NS_ASSERTION ( aURI , " imgLoader::LoadImage -- NULL URI pointer " ) ;
2008-09-04 09:36:27 -07:00
2007-03-22 10:30:00 -07:00
if ( ! aURI )
return NS_ERROR_NULL_POINTER ;
nsCAutoString spec ;
2008-09-04 16:00:42 -07:00
aURI - > GetSpec ( spec ) ;
2007-03-22 10:30:00 -07:00
LOG_SCOPE_WITH_PARAM ( gImgLog , " imgLoader::LoadImage " , " aURI " , spec . get ( ) ) ;
2008-09-04 16:00:42 -07:00
* _retval = nsnull ;
nsRefPtr < imgRequest > request ;
2007-03-22 10:30:00 -07:00
nsresult rv ;
nsLoadFlags requestFlags = nsIRequest : : LOAD_NORMAL ;
// Get the default load flags from the loadgroup (if possible)...
if ( aLoadGroup ) {
aLoadGroup - > GetLoadFlags ( & requestFlags ) ;
}
//
// Merge the default load flags with those passed in via aLoadFlags.
// Currently, *only* the caching, validation and background load flags
// are merged...
//
2008-09-04 16:00:42 -07:00
// The flags in aLoadFlags take precedence over the default flags!
2007-03-22 10:30:00 -07:00
//
if ( aLoadFlags & LOAD_FLAGS_CACHE_MASK ) {
// Override the default caching flags...
requestFlags = ( requestFlags & ~ LOAD_FLAGS_CACHE_MASK ) |
( aLoadFlags & LOAD_FLAGS_CACHE_MASK ) ;
}
if ( aLoadFlags & LOAD_FLAGS_VALIDATE_MASK ) {
// Override the default validation flags...
requestFlags = ( requestFlags & ~ LOAD_FLAGS_VALIDATE_MASK ) |
( aLoadFlags & LOAD_FLAGS_VALIDATE_MASK ) ;
}
if ( aLoadFlags & nsIRequest : : LOAD_BACKGROUND ) {
// Propagate background loading...
requestFlags | = nsIRequest : : LOAD_BACKGROUND ;
}
2011-07-14 11:47:34 -07:00
PRInt32 corsmode = imgIRequest : : CORS_NONE ;
if ( aLoadFlags & imgILoader : : LOAD_CORS_ANONYMOUS ) {
corsmode = imgIRequest : : CORS_ANONYMOUS ;
} else if ( aLoadFlags & imgILoader : : LOAD_CORS_USE_CREDENTIALS ) {
corsmode = imgIRequest : : CORS_USE_CREDENTIALS ;
}
2008-09-04 16:00:42 -07:00
nsRefPtr < imgCacheEntry > entry ;
2007-03-22 10:30:00 -07:00
2009-11-12 15:00:30 -08:00
// Look in the cache for our URI, and then validate it.
// XXX For now ignore aCacheKey. We will need it in the future
// for correctly dealing with image load requests that are a result
// of post data.
imgCacheTable & cache = GetCache ( aURI ) ;
2008-12-11 16:47:47 -08:00
2009-11-12 15:00:30 -08:00
if ( cache . Get ( spec , getter_AddRefs ( entry ) ) & & entry ) {
2010-05-20 13:08:02 -07:00
if ( ValidateEntry ( entry , aURI , aInitialDocumentURI , aReferrerURI ,
2011-10-17 07:59:28 -07:00
aLoadGroup , aObserver , aCX , requestFlags , true ,
2011-07-14 11:47:34 -07:00
aRequest , _retval , aPolicy , aLoadingPrincipal , corsmode ) ) {
2009-11-12 15:00:30 -08:00
request = getter_AddRefs ( entry - > GetRequest ( ) ) ;
2008-09-04 16:00:42 -07:00
2009-11-12 15:00:30 -08:00
// If this entry has no proxies, its request has no reference to the entry.
if ( entry - > HasNoProxies ( ) ) {
LOG_FUNC_WITH_PARAM ( gImgLog , " imgLoader::LoadImage() adding proxyless entry " , " uri " , spec . get ( ) ) ;
NS_ABORT_IF_FALSE ( ! request - > HasCacheEntry ( ) , " Proxyless entry's request has cache entry! " ) ;
request - > SetCacheEntry ( entry ) ;
2009-01-30 18:17:47 -08:00
2009-11-12 15:00:30 -08:00
if ( gCacheTracker )
gCacheTracker - > MarkUsed ( entry ) ;
}
2009-01-30 18:17:47 -08:00
2009-11-12 15:00:30 -08:00
entry - > Touch ( ) ;
2009-01-30 18:17:47 -08:00
2008-09-04 16:00:42 -07:00
# ifdef DEBUG_joe
2009-11-12 15:00:30 -08:00
printf ( " CACHEGET: %d %s %d \n " , time ( NULL ) , spec . get ( ) , entry - > GetDataSize ( ) ) ;
2008-09-04 16:00:42 -07:00
# endif
2009-11-12 15:00:30 -08:00
}
else {
// We can't use this entry. We'll try to load it off the network, and if
// successful, overwrite the old entry in the cache with a new one.
entry = nsnull ;
2007-03-22 10:30:00 -07:00
}
}
2009-07-28 09:13:48 -07:00
// Keep the channel in this scope, so we can adjust its notificationCallbacks
// later when we create the proxy.
nsCOMPtr < nsIChannel > newChannel ;
2008-09-04 16:00:42 -07:00
// If we didn't get a cache hit, we need to load from the network.
if ( ! request ) {
2007-03-22 10:30:00 -07:00
LOG_SCOPE ( gImgLog , " imgLoader::LoadImage |cache miss| " ) ;
2011-09-20 14:00:42 -07:00
bool forcePrincipalCheck ;
2007-03-22 10:30:00 -07:00
rv = NewImageChannel ( getter_AddRefs ( newChannel ) ,
2011-09-20 14:00:42 -07:00
& forcePrincipalCheck ,
2007-03-22 10:30:00 -07:00
aURI ,
aInitialDocumentURI ,
aReferrerURI ,
aLoadGroup ,
2009-07-15 00:22:40 -07:00
mAcceptHeader ,
2010-05-20 13:08:02 -07:00
requestFlags ,
2011-09-20 14:00:42 -07:00
aPolicy ,
aLoadingPrincipal ) ;
2007-03-22 10:30:00 -07:00
if ( NS_FAILED ( rv ) )
return NS_ERROR_FAILURE ;
2011-10-01 09:14:40 -07:00
NewRequestAndEntry ( forcePrincipalCheck , getter_AddRefs ( request ) ,
getter_AddRefs ( entry ) ) ;
2007-03-22 10:30:00 -07:00
PR_LOG ( gImgLog , PR_LOG_DEBUG ,
2008-09-04 16:00:42 -07:00
( " [this=%p] imgLoader::LoadImage -- Created new imgRequest [request=%p] \n " , this , request . get ( ) ) ) ;
2007-03-22 10:30:00 -07:00
// Create a loadgroup for this new channel. This way if the channel
// is redirected, we'll have a way to cancel the resulting channel.
nsCOMPtr < nsILoadGroup > loadGroup =
do_CreateInstance ( NS_LOADGROUP_CONTRACTID ) ;
newChannel - > SetLoadGroup ( loadGroup ) ;
2011-09-19 12:57:21 -07:00
request - > Init ( aURI , aURI , loadGroup , newChannel , entry , aCX ,
2011-07-14 11:47:34 -07:00
aLoadingPrincipal , corsmode ) ;
2007-03-22 10:30:00 -07:00
2011-08-24 13:44:35 -07:00
// Pass the inner window ID of the loading document, if possible.
2010-12-20 08:21:59 -08:00
nsCOMPtr < nsIDocument > doc = do_QueryInterface ( aCX ) ;
if ( doc ) {
2011-08-24 13:44:35 -07:00
request - > SetInnerWindowID ( doc - > InnerWindowID ( ) ) ;
2010-12-20 08:21:59 -08:00
}
2007-03-22 10:30:00 -07:00
// create the proxy listener
2011-07-14 11:47:34 -07:00
nsCOMPtr < nsIStreamListener > pl = new ProxyListener ( request . get ( ) ) ;
// See if we need to insert a CORS proxy between the proxy listener and the
// request.
nsCOMPtr < nsIStreamListener > listener = pl ;
if ( corsmode ! = imgIRequest : : CORS_NONE ) {
2011-09-28 23:19:26 -07:00
bool withCredentials = corsmode = = imgIRequest : : CORS_USE_CREDENTIALS ;
2011-07-14 11:47:34 -07:00
nsCOMPtr < nsIStreamListener > corsproxy =
new nsCORSListenerProxy ( pl , aLoadingPrincipal , newChannel ,
withCredentials , & rv ) ;
if ( NS_FAILED ( rv ) ) {
return NS_ERROR_FAILURE ;
}
2007-03-22 10:30:00 -07:00
2011-07-14 11:47:34 -07:00
listener = corsproxy ;
}
2007-03-22 10:30:00 -07:00
PR_LOG ( gImgLog , PR_LOG_DEBUG ,
( " [this=%p] imgLoader::LoadImage -- Calling channel->AsyncOpen() \n " , this ) ) ;
2011-07-14 11:47:34 -07:00
nsresult openRes = newChannel - > AsyncOpen ( listener , nsnull ) ;
2007-03-22 10:30:00 -07:00
if ( NS_FAILED ( openRes ) ) {
PR_LOG ( gImgLog , PR_LOG_DEBUG ,
( " [this=%p] imgLoader::LoadImage -- AsyncOpen() failed: 0x%x \n " ,
this , openRes ) ) ;
2008-12-22 14:20:46 -08:00
request - > CancelAndAbort ( openRes ) ;
2007-03-22 10:30:00 -07:00
return openRes ;
}
2008-09-04 16:00:42 -07:00
// Try to add the new request into the cache.
2009-03-17 14:07:16 -07:00
PutIntoCache ( aURI , entry ) ;
2007-03-22 10:30:00 -07:00
} else {
LOG_MSG_WITH_PARAM ( gImgLog ,
" imgLoader::LoadImage |cache hit| " , " request " , request ) ;
}
2009-11-12 15:00:30 -08:00
2008-09-04 16:00:42 -07:00
// If we didn't get a proxy when validating the cache entry, we need to create one.
if ( ! * _retval ) {
2009-11-12 15:00:30 -08:00
// ValidateEntry() has three return values: "Is valid," "might be valid --
// validating over network", and "not valid." If we don't have a _retval,
// we know ValidateEntry is not validating over the network, so it's safe
// to SetLoadId here because we know this request is valid for this context.
//
// Note, however, that this doesn't guarantee the behaviour we want (one
// URL maps to the same image on a page) if we load the same image in a
// different tab (see bug 528003), because its load id will get re-set, and
// that'll cause us to validate over the network.
request - > SetLoadId ( aCX ) ;
2007-03-22 10:30:00 -07:00
2009-11-12 15:00:30 -08:00
LOG_MSG ( gImgLog , " imgLoader::LoadImage " , " creating proxy request. " ) ;
2008-09-04 16:00:42 -07:00
rv = CreateNewProxyForRequest ( request , aLoadGroup , aObserver ,
requestFlags , aRequest , _retval ) ;
2010-05-25 07:34:04 -07:00
if ( NS_FAILED ( rv ) ) {
return rv ;
}
2008-09-04 16:00:42 -07:00
imgRequestProxy * proxy = static_cast < imgRequestProxy * > ( * _retval ) ;
2007-03-22 10:30:00 -07:00
2009-07-28 09:13:48 -07:00
// Make sure that OnStatus/OnProgress calls have the right request set, if
// we did create a channel here.
if ( newChannel ) {
nsCOMPtr < nsIInterfaceRequestor > requestor (
new nsProgressNotificationProxy ( newChannel , proxy ) ) ;
if ( ! requestor )
return NS_ERROR_OUT_OF_MEMORY ;
newChannel - > SetNotificationCallbacks ( requestor ) ;
}
2008-09-04 16:00:42 -07:00
// Note that it's OK to add here even if the request is done. If it is,
2010-05-10 20:27:41 -07:00
// it'll send a OnStopRequest() to the proxy in imgRequestProxy::Notify and
// the proxy will be removed from the loadgroup.
2008-09-04 16:00:42 -07:00
proxy - > AddToLoadGroup ( ) ;
2008-09-04 08:23:16 -07:00
2010-07-28 14:52:14 -07:00
// If we're loading off the network, explicitly don't notify our proxy,
// because necko (or things called from necko, such as imgCacheValidator)
// are going to call our notifications asynchronously, and we can't make it
// further asynchronous because observers might rely on imagelib completing
// its work between the channel's OnStartRequest and OnStopRequest.
if ( ! newChannel )
proxy - > NotifyListener ( ) ;
2008-09-04 16:00:42 -07:00
return rv ;
2007-03-22 10:30:00 -07:00
}
2008-09-04 16:00:42 -07:00
NS_ASSERTION ( * _retval , " imgLoader::LoadImage -- no return value " ) ;
2007-03-22 10:30:00 -07:00
2008-09-04 16:00:42 -07:00
return NS_OK ;
2007-03-22 10:30:00 -07:00
}
/* imgIRequest loadImageWithChannel(in nsIChannel channel, in imgIDecoderObserver aObserver, in nsISupports cx, out nsIStreamListener); */
NS_IMETHODIMP imgLoader : : LoadImageWithChannel ( nsIChannel * channel , imgIDecoderObserver * aObserver , nsISupports * aCX , nsIStreamListener * * listener , imgIRequest * * _retval )
{
NS_ASSERTION ( channel , " imgLoader::LoadImageWithChannel -- NULL channel pointer " ) ;
2008-09-04 16:00:42 -07:00
nsRefPtr < imgRequest > request ;
2007-03-22 10:30:00 -07:00
nsCOMPtr < nsIURI > uri ;
channel - > GetURI ( getter_AddRefs ( uri ) ) ;
nsLoadFlags requestFlags = nsIRequest : : LOAD_NORMAL ;
channel - > GetLoadFlags ( & requestFlags ) ;
2008-09-04 16:00:42 -07:00
nsRefPtr < imgCacheEntry > entry ;
2007-03-22 10:30:00 -07:00
2008-09-04 16:00:42 -07:00
if ( requestFlags & nsIRequest : : LOAD_BYPASS_CACHE ) {
RemoveFromCache ( uri ) ;
} else {
// Look in the cache for our URI, and then validate it.
// XXX For now ignore aCacheKey. We will need it in the future
// for correctly dealing with image load requests that are a result
// of post data.
imgCacheTable & cache = GetCache ( uri ) ;
nsCAutoString spec ;
uri - > GetSpec ( spec ) ;
if ( cache . Get ( spec , getter_AddRefs ( entry ) ) & & entry ) {
// We don't want to kick off another network load. So we ask
// ValidateEntry to only do validation without creating a new proxy. If
// it says that the entry isn't valid any more, we'll only use the entry
// we're getting if the channel is loading from the cache anyways.
//
// XXX -- should this be changed? it's pretty much verbatim from the old
// code, but seems nonsensical.
if ( ValidateEntry ( entry , uri , nsnull , nsnull , nsnull , aObserver , aCX ,
2011-10-17 07:59:28 -07:00
requestFlags , false , nsnull , nsnull , nsnull ,
2011-07-14 11:47:34 -07:00
nsnull , imgIRequest : : CORS_NONE ) ) {
2008-09-04 16:00:42 -07:00
request = getter_AddRefs ( entry - > GetRequest ( ) ) ;
2007-03-22 10:30:00 -07:00
} else {
2008-09-04 16:00:42 -07:00
nsCOMPtr < nsICachingChannel > cacheChan ( do_QueryInterface ( channel ) ) ;
2011-09-28 23:19:26 -07:00
bool bUseCacheCopy ;
2008-09-04 16:00:42 -07:00
if ( cacheChan )
cacheChan - > IsFromCache ( & bUseCacheCopy ) ;
else
2011-10-17 07:59:28 -07:00
bUseCacheCopy = false ;
2008-09-04 16:00:42 -07:00
if ( ! bUseCacheCopy )
entry = nsnull ;
else {
request = getter_AddRefs ( entry - > GetRequest ( ) ) ;
}
2007-03-22 10:30:00 -07:00
}
2009-01-30 18:17:47 -08:00
if ( request & & entry ) {
// If this entry has no proxies, its request has no reference to the entry.
if ( entry - > HasNoProxies ( ) ) {
LOG_FUNC_WITH_PARAM ( gImgLog , " imgLoader::LoadImageWithChannel() adding proxyless entry " , " uri " , spec . get ( ) ) ;
NS_ABORT_IF_FALSE ( ! request - > HasCacheEntry ( ) , " Proxyless entry's request has cache entry! " ) ;
request - > SetCacheEntry ( entry ) ;
if ( gCacheTracker )
gCacheTracker - > MarkUsed ( entry ) ;
}
}
2007-03-22 10:30:00 -07:00
}
}
nsCOMPtr < nsILoadGroup > loadGroup ;
channel - > GetLoadGroup ( getter_AddRefs ( loadGroup ) ) ;
2011-06-09 12:03:41 -07:00
// Filter out any load flags not from nsIRequest
requestFlags & = nsIRequest : : LOAD_REQUESTMASK ;
2010-07-28 14:52:14 -07:00
2011-04-07 21:55:25 -07:00
nsresult rv = NS_OK ;
2007-03-22 10:30:00 -07:00
if ( request ) {
// we have this in our cache already.. cancel the current (document) load
2009-02-16 03:27:22 -08:00
channel - > Cancel ( NS_ERROR_PARSED_DATA_CACHED ) ; // this should fire an OnStopRequest
2007-03-22 10:30:00 -07:00
* listener = nsnull ; // give them back a null nsIStreamListener
2010-07-28 14:52:14 -07:00
rv = CreateNewProxyForRequest ( request , loadGroup , aObserver ,
requestFlags , nsnull , _retval ) ;
static_cast < imgRequestProxy * > ( * _retval ) - > NotifyListener ( ) ;
2007-03-22 10:30:00 -07:00
} else {
2011-09-20 14:00:42 -07:00
// Default to doing a principal check because we don't know who
// started that load and whether their principal ended up being
// inherited on the channel.
2011-10-17 07:59:28 -07:00
NewRequestAndEntry ( true , getter_AddRefs ( request ) , getter_AddRefs ( entry ) ) ;
2007-03-22 10:30:00 -07:00
2008-12-22 14:20:46 -08:00
// We use originalURI here to fulfil the imgIRequest contract on GetURI.
2007-03-22 10:30:00 -07:00
nsCOMPtr < nsIURI > originalURI ;
channel - > GetOriginalURI ( getter_AddRefs ( originalURI ) ) ;
2011-07-14 11:47:32 -07:00
// No principal specified here, because we're not passed one.
request - > Init ( originalURI , uri , channel , channel , entry ,
2011-09-19 12:57:21 -07:00
aCX , nsnull , imgIRequest : : CORS_NONE ) ;
2007-03-22 10:30:00 -07:00
2008-09-04 16:00:42 -07:00
ProxyListener * pl = new ProxyListener ( static_cast < nsIStreamListener * > ( request . get ( ) ) ) ;
2007-03-22 10:30:00 -07:00
NS_ADDREF ( pl ) ;
2007-07-08 00:08:04 -07:00
* listener = static_cast < nsIStreamListener * > ( pl ) ;
2007-03-22 10:30:00 -07:00
NS_ADDREF ( * listener ) ;
NS_RELEASE ( pl ) ;
2008-09-04 16:00:42 -07:00
// Try to add the new request into the cache.
2011-07-01 10:03:32 -07:00
PutIntoCache ( originalURI , entry ) ;
2007-03-22 10:30:00 -07:00
2010-07-28 14:52:14 -07:00
rv = CreateNewProxyForRequest ( request , loadGroup , aObserver ,
requestFlags , nsnull , _retval ) ;
2007-03-22 10:30:00 -07:00
2010-07-28 14:52:14 -07:00
// Explicitly don't notify our proxy, because we're loading off the
// network, and necko (or things called from necko, such as
// imgCacheValidator) are going to call our notifications asynchronously,
// and we can't make it further asynchronous because observers might rely
// on imagelib completing its work between the channel's OnStartRequest and
// OnStopRequest.
}
2007-03-22 10:30:00 -07:00
return rv ;
}
2011-09-28 23:19:26 -07:00
NS_IMETHODIMP imgLoader : : SupportImageWithMimeType ( const char * aMimeType , bool * _retval )
2007-03-22 10:30:00 -07:00
{
2011-10-17 07:59:28 -07:00
* _retval = false ;
2007-03-22 10:30:00 -07:00
nsCAutoString mimeType ( aMimeType ) ;
ToLowerCase ( mimeType ) ;
2010-08-22 19:30:45 -07:00
* _retval = ( Image : : GetDecoderType ( mimeType . get ( ) ) = = Image : : eDecoderType_unknown )
2011-10-17 07:59:28 -07:00
? false : true ;
2010-08-22 19:30:45 -07:00
return NS_OK ;
2007-03-22 10:30:00 -07:00
}
NS_IMETHODIMP imgLoader : : GetMIMETypeFromContent ( nsIRequest * aRequest ,
const PRUint8 * aContents ,
PRUint32 aLength ,
nsACString & aContentType )
{
return GetMimeTypeFromContent ( ( const char * ) aContents , aLength , aContentType ) ;
}
/* static */
nsresult imgLoader : : GetMimeTypeFromContent ( const char * aContents , PRUint32 aLength , nsACString & aContentType )
{
/* Is it a GIF? */
2009-04-05 17:30:03 -07:00
if ( aLength > = 6 & & ( ! nsCRT : : strncmp ( aContents , " GIF87a " , 6 ) | |
! nsCRT : : strncmp ( aContents , " GIF89a " , 6 ) ) )
{
2007-03-22 10:30:00 -07:00
aContentType . AssignLiteral ( " image/gif " ) ;
}
/* or a PNG? */
2009-04-05 17:30:03 -07:00
else if ( aLength > = 8 & & ( ( unsigned char ) aContents [ 0 ] = = 0x89 & &
2007-03-22 10:30:00 -07:00
( unsigned char ) aContents [ 1 ] = = 0x50 & &
( unsigned char ) aContents [ 2 ] = = 0x4E & &
2009-04-05 17:30:03 -07:00
( unsigned char ) aContents [ 3 ] = = 0x47 & &
( unsigned char ) aContents [ 4 ] = = 0x0D & &
( unsigned char ) aContents [ 5 ] = = 0x0A & &
( unsigned char ) aContents [ 6 ] = = 0x1A & &
( unsigned char ) aContents [ 7 ] = = 0x0A ) )
2007-03-22 10:30:00 -07:00
{
aContentType . AssignLiteral ( " image/png " ) ;
}
/* maybe a JPEG (JFIF)? */
/* JFIF files start with SOI APP0 but older files can start with SOI DQT
* so we test for SOI followed by any marker , i . e . FF D8 FF
* this will also work for SPIFF JPEG files if they appear in the future .
*
* ( JFIF is 0XFF 0XD8 0XFF 0XE0 < skip 2 > 0X4A 0X46 0X49 0X46 0X00 )
*/
else if ( aLength > = 3 & &
( ( unsigned char ) aContents [ 0 ] ) = = 0xFF & &
( ( unsigned char ) aContents [ 1 ] ) = = 0xD8 & &
( ( unsigned char ) aContents [ 2 ] ) = = 0xFF )
{
aContentType . AssignLiteral ( " image/jpeg " ) ;
}
/* or how about ART? */
/* ART begins with JG (4A 47). Major version offset 2.
* Minor version offset 3. Offset 4 must be NULL .
*/
else if ( aLength > = 5 & &
( ( unsigned char ) aContents [ 0 ] ) = = 0x4a & &
( ( unsigned char ) aContents [ 1 ] ) = = 0x47 & &
( ( unsigned char ) aContents [ 4 ] ) = = 0x00 )
{
aContentType . AssignLiteral ( " image/x-jg " ) ;
}
else if ( aLength > = 2 & & ! nsCRT : : strncmp ( aContents , " BM " , 2 ) ) {
aContentType . AssignLiteral ( " image/bmp " ) ;
}
// ICOs always begin with a 2-byte 0 followed by a 2-byte 1.
// CURs begin with 2-byte 0 followed by 2-byte 2.
else if ( aLength > = 4 & & ( ! memcmp ( aContents , " \000 \000 \001 \000 " , 4 ) | |
! memcmp ( aContents , " \000 \000 \002 \000 " , 4 ) ) ) {
aContentType . AssignLiteral ( " image/x-icon " ) ;
}
else {
/* none of the above? I give up */
return NS_ERROR_NOT_AVAILABLE ;
}
return NS_OK ;
}
/**
* proxy stream listener class used to handle multipart / x - mixed - replace
*/
# include "nsIRequest.h"
# include "nsIStreamConverterService.h"
# include "nsXPIDLString.h"
NS_IMPL_ISUPPORTS2 ( ProxyListener , nsIStreamListener , nsIRequestObserver )
ProxyListener : : ProxyListener ( nsIStreamListener * dest ) :
mDestListener ( dest )
{
/* member initializers and constructor code */
}
ProxyListener : : ~ ProxyListener ( )
{
/* destructor code */
}
/** nsIRequestObserver methods **/
/* void onStartRequest (in nsIRequest request, in nsISupports ctxt); */
NS_IMETHODIMP ProxyListener : : OnStartRequest ( nsIRequest * aRequest , nsISupports * ctxt )
{
if ( ! mDestListener )
return NS_ERROR_FAILURE ;
nsCOMPtr < nsIChannel > channel ( do_QueryInterface ( aRequest ) ) ;
if ( channel ) {
nsCAutoString contentType ;
nsresult rv = channel - > GetContentType ( contentType ) ;
if ( ! contentType . IsEmpty ( ) ) {
/* If multipart/x-mixed-replace content, we'll insert a MIME decoder
in the pipeline to handle the content and pass it along to our
original listener .
*/
if ( NS_LITERAL_CSTRING ( " multipart/x-mixed-replace " ) . Equals ( contentType ) ) {
nsCOMPtr < nsIStreamConverterService > convServ ( do_GetService ( " @mozilla.org/streamConverters;1 " , & rv ) ) ;
if ( NS_SUCCEEDED ( rv ) ) {
nsCOMPtr < nsIStreamListener > toListener ( mDestListener ) ;
nsCOMPtr < nsIStreamListener > fromListener ;
rv = convServ - > AsyncConvertData ( " multipart/x-mixed-replace " ,
" */* " ,
toListener ,
nsnull ,
getter_AddRefs ( fromListener ) ) ;
if ( NS_SUCCEEDED ( rv ) )
mDestListener = fromListener ;
}
}
}
}
return mDestListener - > OnStartRequest ( aRequest , ctxt ) ;
}
/* void onStopRequest (in nsIRequest request, in nsISupports ctxt, in nsresult status); */
NS_IMETHODIMP ProxyListener : : OnStopRequest ( nsIRequest * aRequest , nsISupports * ctxt , nsresult status )
{
if ( ! mDestListener )
return NS_ERROR_FAILURE ;
return mDestListener - > OnStopRequest ( aRequest , ctxt , status ) ;
}
/** nsIStreamListener methods **/
/* void onDataAvailable (in nsIRequest request, in nsISupports ctxt, in nsIInputStream inStr, in unsigned long sourceOffset, in unsigned long count); */
NS_IMETHODIMP ProxyListener : : OnDataAvailable ( nsIRequest * aRequest , nsISupports * ctxt , nsIInputStream * inStr , PRUint32 sourceOffset , PRUint32 count )
{
if ( ! mDestListener )
return NS_ERROR_FAILURE ;
return mDestListener - > OnDataAvailable ( aRequest , ctxt , inStr , sourceOffset , count ) ;
}
/**
* http validate class . check a channel for a 304
*/
2011-07-01 10:03:38 -07:00
NS_IMPL_ISUPPORTS5 ( imgCacheValidator , nsIStreamListener , nsIRequestObserver ,
nsIChannelEventSink , nsIInterfaceRequestor ,
nsIAsyncVerifyRedirectCallback )
2007-03-22 10:30:00 -07:00
2008-09-04 16:00:42 -07:00
imgLoader imgCacheValidator : : sImgLoader ;
2011-07-01 10:03:38 -07:00
imgCacheValidator : : imgCacheValidator ( nsProgressNotificationProxy * progress ,
2011-09-20 14:00:42 -07:00
imgRequest * request , void * aContext ,
bool forcePrincipalCheckForCacheEntry )
2011-07-01 10:03:38 -07:00
: mProgressProxy ( progress ) ,
mRequest ( request ) ,
mContext ( aContext )
{
2011-09-20 14:00:42 -07:00
NewRequestAndEntry ( forcePrincipalCheckForCacheEntry ,
getter_AddRefs ( mNewRequest ) , getter_AddRefs ( mNewEntry ) ) ;
2011-07-01 10:03:38 -07:00
}
2007-03-22 10:30:00 -07:00
imgCacheValidator : : ~ imgCacheValidator ( )
{
if ( mRequest ) {
mRequest - > mValidator = nsnull ;
}
}
void imgCacheValidator : : AddProxy ( imgRequestProxy * aProxy )
{
// aProxy needs to be in the loadgroup since we're validating from
// the network.
aProxy - > AddToLoadGroup ( ) ;
mProxies . AppendObject ( aProxy ) ;
}
/** nsIRequestObserver methods **/
/* void onStartRequest (in nsIRequest request, in nsISupports ctxt); */
NS_IMETHODIMP imgCacheValidator : : OnStartRequest ( nsIRequest * aRequest , nsISupports * ctxt )
{
2011-07-01 10:03:42 -07:00
// If this request is coming from cache and has the same URI as our
// imgRequest, the request all our proxies are pointing at is valid, and all
// we have to do is tell them to notify their listeners.
2007-03-22 10:30:00 -07:00
nsCOMPtr < nsICachingChannel > cacheChan ( do_QueryInterface ( aRequest ) ) ;
2011-07-01 10:03:42 -07:00
nsCOMPtr < nsIChannel > channel ( do_QueryInterface ( aRequest ) ) ;
if ( cacheChan & & channel ) {
2011-09-28 23:19:26 -07:00
bool isFromCache = false ;
2011-07-01 10:03:42 -07:00
cacheChan - > IsFromCache ( & isFromCache ) ;
2011-06-30 18:58:34 -07:00
2011-07-01 10:03:42 -07:00
nsCOMPtr < nsIURI > channelURI ;
2011-09-28 23:19:26 -07:00
bool sameURI = false ;
2011-07-01 10:03:42 -07:00
channel - > GetURI ( getter_AddRefs ( channelURI ) ) ;
if ( channelURI )
channelURI - > Equals ( mRequest - > mCurrentURI , & sameURI ) ;
if ( isFromCache & & sameURI ) {
2007-03-22 10:30:00 -07:00
PRUint32 count = mProxies . Count ( ) ;
for ( PRInt32 i = count - 1 ; i > = 0 ; i - - ) {
2007-07-08 00:08:04 -07:00
imgRequestProxy * proxy = static_cast < imgRequestProxy * > ( mProxies [ i ] ) ;
2010-07-28 14:52:14 -07:00
2010-08-02 12:44:49 -07:00
// Proxies waiting on cache validation should be deferring notifications.
// Undefer them.
NS_ABORT_IF_FALSE ( proxy - > NotificationsDeferred ( ) ,
" Proxies waiting on cache validation should be "
" deferring notifications! " ) ;
2011-10-17 07:59:28 -07:00
proxy - > SetNotificationsDeferred ( false ) ;
2010-08-02 12:44:49 -07:00
2010-07-28 14:52:14 -07:00
// Notify synchronously, because we're already in OnStartRequest, an
// asynchronously-called function.
proxy - > SyncNotifyListener ( ) ;
2007-03-22 10:30:00 -07:00
}
2011-07-01 10:03:42 -07:00
// We don't need to load this any more.
aRequest - > Cancel ( NS_BINDING_ABORTED ) ;
2007-03-22 10:30:00 -07:00
mRequest - > SetLoadId ( mContext ) ;
mRequest - > mValidator = nsnull ;
2008-09-04 16:00:42 -07:00
mRequest = nsnull ;
2007-03-22 10:30:00 -07:00
2011-07-01 10:03:38 -07:00
mNewRequest = nsnull ;
mNewEntry = nsnull ;
2007-03-22 10:30:00 -07:00
return NS_OK ;
}
}
2008-09-04 16:00:42 -07:00
2009-01-30 18:17:47 -08:00
// We can't load out of cache. We have to create a whole new request for the
// data that's coming in off the channel.
2007-03-22 10:30:00 -07:00
nsCOMPtr < nsIURI > uri ;
2009-01-30 18:17:47 -08:00
mRequest - > GetURI ( getter_AddRefs ( uri ) ) ;
# if defined(PR_LOGGING)
nsCAutoString spec ;
uri - > GetSpec ( spec ) ;
LOG_MSG_WITH_PARAM ( gImgLog , " imgCacheValidator::OnStartRequest creating new request " , " uri " , spec . get ( ) ) ;
# endif
2011-07-14 11:47:34 -07:00
PRInt32 corsmode = mRequest - > GetCORSMode ( ) ;
2011-07-14 11:47:32 -07:00
nsCOMPtr < nsIPrincipal > loadingPrincipal = mRequest - > GetLoadingPrincipal ( ) ;
2007-03-22 10:30:00 -07:00
// Doom the old request's cache entry
mRequest - > RemoveFromCache ( ) ;
mRequest - > mValidator = nsnull ;
2008-09-04 16:00:42 -07:00
mRequest = nsnull ;
2007-03-22 10:30:00 -07:00
2008-12-22 14:20:46 -08:00
// We use originalURI here to fulfil the imgIRequest contract on GetURI.
2007-03-22 10:30:00 -07:00
nsCOMPtr < nsIURI > originalURI ;
channel - > GetOriginalURI ( getter_AddRefs ( originalURI ) ) ;
2011-07-14 11:47:32 -07:00
mNewRequest - > Init ( originalURI , uri , channel , channel , mNewEntry ,
2011-09-19 12:57:21 -07:00
mContext , loadingPrincipal ,
2011-07-14 11:47:34 -07:00
corsmode ) ;
2007-03-22 10:30:00 -07:00
2011-07-14 11:47:34 -07:00
mDestListener = new ProxyListener ( mNewRequest ) ;
2007-03-22 10:30:00 -07:00
2009-01-30 18:17:47 -08:00
// Try to add the new request into the cache. Note that the entry must be in
// the cache before the proxies' ownership changes, because adding a proxy
// changes the caching behaviour for imgRequests.
2011-07-01 10:03:38 -07:00
sImgLoader . PutIntoCache ( originalURI , mNewEntry ) ;
2009-01-30 18:17:47 -08:00
2007-03-22 10:30:00 -07:00
PRUint32 count = mProxies . Count ( ) ;
for ( PRInt32 i = count - 1 ; i > = 0 ; i - - ) {
2007-07-08 00:08:04 -07:00
imgRequestProxy * proxy = static_cast < imgRequestProxy * > ( mProxies [ i ] ) ;
2011-07-01 10:03:38 -07:00
proxy - > ChangeOwner ( mNewRequest ) ;
2010-07-28 14:52:14 -07:00
2010-08-02 12:44:49 -07:00
// Proxies waiting on cache validation should be deferring notifications.
// Undefer them.
NS_ABORT_IF_FALSE ( proxy - > NotificationsDeferred ( ) ,
" Proxies waiting on cache validation should be "
" deferring notifications! " ) ;
2011-10-17 07:59:28 -07:00
proxy - > SetNotificationsDeferred ( false ) ;
2010-08-02 12:44:49 -07:00
2010-07-28 14:52:14 -07:00
// Notify synchronously, because we're already in OnStartRequest, an
// asynchronously-called function.
proxy - > SyncNotifyListener ( ) ;
2007-03-22 10:30:00 -07:00
}
2011-07-01 10:03:38 -07:00
mNewRequest = nsnull ;
mNewEntry = nsnull ;
2007-03-22 10:30:00 -07:00
return mDestListener - > OnStartRequest ( aRequest , ctxt ) ;
}
/* void onStopRequest (in nsIRequest request, in nsISupports ctxt, in nsresult status); */
NS_IMETHODIMP imgCacheValidator : : OnStopRequest ( nsIRequest * aRequest , nsISupports * ctxt , nsresult status )
{
if ( ! mDestListener )
return NS_OK ;
return mDestListener - > OnStopRequest ( aRequest , ctxt , status ) ;
}
/** nsIStreamListener methods **/
/* void onDataAvailable (in nsIRequest request, in nsISupports ctxt, in nsIInputStream inStr, in unsigned long sourceOffset, in unsigned long count); */
NS_IMETHODIMP imgCacheValidator : : OnDataAvailable ( nsIRequest * aRequest , nsISupports * ctxt , nsIInputStream * inStr , PRUint32 sourceOffset , PRUint32 count )
{
if ( ! mDestListener ) {
// XXX see bug 113959
PRUint32 _retval ;
2011-06-24 07:22:33 -07:00
inStr - > ReadSegments ( NS_DiscardSegment , nsnull , count , & _retval ) ;
2007-03-22 10:30:00 -07:00
return NS_OK ;
}
return mDestListener - > OnDataAvailable ( aRequest , ctxt , inStr , sourceOffset , count ) ;
}
2011-07-01 10:03:38 -07:00
/** nsIInterfaceRequestor methods **/
NS_IMETHODIMP imgCacheValidator : : GetInterface ( const nsIID & aIID , void * * aResult )
{
if ( aIID . Equals ( NS_GET_IID ( nsIChannelEventSink ) ) )
return QueryInterface ( aIID , aResult ) ;
return mProgressProxy - > GetInterface ( aIID , aResult ) ;
}
// These functions are materially the same as the same functions in imgRequest.
// We duplicate them because we're verifying whether cache loads are necessary,
// not unconditionally loading.
/** nsIChannelEventSink methods **/
NS_IMETHODIMP imgCacheValidator : : AsyncOnChannelRedirect ( nsIChannel * oldChannel ,
nsIChannel * newChannel , PRUint32 flags ,
nsIAsyncVerifyRedirectCallback * callback )
{
// Note all cache information we get from the old channel.
mNewRequest - > SetCacheValidation ( mNewEntry , oldChannel ) ;
// Prepare for callback
mRedirectCallback = callback ;
mRedirectChannel = newChannel ;
return mProgressProxy - > AsyncOnChannelRedirect ( oldChannel , newChannel , flags , this ) ;
}
NS_IMETHODIMP imgCacheValidator : : OnRedirectVerifyCallback ( nsresult aResult )
{
// If we've already been told to abort, just do so.
if ( NS_FAILED ( aResult ) ) {
mRedirectCallback - > OnRedirectVerifyCallback ( aResult ) ;
mRedirectCallback = nsnull ;
mRedirectChannel = nsnull ;
return NS_OK ;
}
// make sure we have a protocol that returns data rather than opens
// an external application, e.g. mailto:
nsCOMPtr < nsIURI > uri ;
mRedirectChannel - > GetURI ( getter_AddRefs ( uri ) ) ;
2011-09-28 23:19:26 -07:00
bool doesNotReturnData = false ;
2011-07-01 10:03:38 -07:00
NS_URIChainHasFlags ( uri , nsIProtocolHandler : : URI_DOES_NOT_RETURN_DATA ,
& doesNotReturnData ) ;
nsresult result = NS_OK ;
if ( doesNotReturnData ) {
result = NS_ERROR_ABORT ;
}
mRedirectCallback - > OnRedirectVerifyCallback ( result ) ;
mRedirectCallback = nsnull ;
mRedirectChannel = nsnull ;
return NS_OK ;
}