2007-03-22 10:30:00 -07:00
/* vim:set ts=2 sw=2 sts=2 et cin: */
/* ***** BEGIN LICENSE BLOCK *****
* 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 .
*
* The Initial Developer of the Original Code is IBM Corporation .
* Portions created by IBM Corporation are Copyright ( C ) 2004
* IBM Corporation . All Rights Reserved .
*
* Contributor ( s ) :
* Darin Fisher < darin @ meer . net >
2007-05-14 13:09:20 -07:00
* Dave Camp < dcamp @ mozilla . 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 * * * * * */
# include "nsCache.h"
# include "nsDiskCache.h"
# include "nsDiskCacheDeviceSQL.h"
2007-05-14 13:09:20 -07:00
# include "nsCacheService.h"
2007-03-22 10:30:00 -07:00
# include "nsNetUtil.h"
# include "nsAutoPtr.h"
# include "nsString.h"
# include "nsPrintfCString.h"
# include "nsCRT.h"
# include "mozIStorageService.h"
# include "mozIStorageStatement.h"
2007-05-14 13:09:20 -07:00
# include "mozIStorageFunction.h"
# include "mozStorageHelper.h"
2007-03-22 10:30:00 -07:00
# include "nsICacheVisitor.h"
# include "nsISeekableStream.h"
2007-05-14 13:09:20 -07:00
static const char OFFLINE_CACHE_DEVICE_ID [ ] = { " offline " } ;
2007-03-22 10:30:00 -07:00
# define LOG(args) CACHE_LOG_DEBUG(args)
/*****************************************************************************
* helpers
*/
static nsresult
EnsureDir ( nsIFile * dir )
{
PRBool exists ;
nsresult rv = dir - > Exists ( & exists ) ;
if ( NS_SUCCEEDED ( rv ) & & ! exists )
rv = dir - > Create ( nsIFile : : DIRECTORY_TYPE , 0700 ) ;
return rv ;
}
static PRBool
DecomposeCacheEntryKey ( const nsCString * fullKey ,
const char * * cid ,
const char * * key ,
nsCString & buf )
{
buf = * fullKey ;
PRInt32 colon = buf . FindChar ( ' : ' ) ;
if ( colon = = kNotFound )
{
NS_ERROR ( " Invalid key " ) ;
return PR_FALSE ;
}
buf . SetCharAt ( ' \0 ' , colon ) ;
* cid = buf . get ( ) ;
* key = buf . get ( ) + colon + 1 ;
return PR_TRUE ;
}
class AutoResetStatement
{
public :
AutoResetStatement ( mozIStorageStatement * s )
: mStatement ( s ) { }
~ AutoResetStatement ( ) { mStatement - > Reset ( ) ; }
mozIStorageStatement * operator - > ( ) { return mStatement ; }
private :
mozIStorageStatement * mStatement ;
} ;
2007-05-14 13:09:20 -07:00
class EvictionObserver
{
public :
EvictionObserver ( mozIStorageConnection * db )
: mDB ( db )
{
mDB - > ExecuteSimpleSQL (
NS_LITERAL_CSTRING ( " CREATE TEMP TRIGGER cache_on_delete AFTER DELETE "
" ON moz_cache FOR EACH ROW BEGIN SELECT "
" cache_eviction_observer( "
" OLD.clientID, OLD.key, OLD.generation); "
" END; " ) ) ;
}
~ EvictionObserver ( )
{
mDB - > ExecuteSimpleSQL (
NS_LITERAL_CSTRING ( " DROP TRIGGER cache_on_delete; " ) ) ;
}
private :
mozIStorageConnection * mDB ;
} ;
2007-03-22 10:30:00 -07:00
# define DCACHE_HASH_MAX LL_MAXINT
# define DCACHE_HASH_BITS 64
/**
2007-05-14 13:09:20 -07:00
* nsOfflineCache : : Hash ( const char * key )
2007-03-22 10:30:00 -07:00
*
2007-05-14 13:09:20 -07:00
* This algorithm of this method implies nsOfflineCacheRecords will be stored
2007-03-22 10:30:00 -07:00
* in a certain order on disk . If the algorithm changes , existing cache
* map files may become invalid , and therefore the kCurrentVersion needs
* to be revised .
*/
static PRUint64
DCacheHash ( const char * key )
{
PRUint64 h = 0 ;
for ( const PRUint8 * s = ( PRUint8 * ) key ; * s ! = ' \0 ' ; + + s )
h = ( h > > ( DCACHE_HASH_BITS - 4 ) ) ^ ( h < < 4 ) ^ * s ;
return ( h = = 0 ? DCACHE_HASH_MAX : h ) ;
}
2007-05-14 13:09:20 -07:00
/******************************************************************************
* nsOfflineCacheEvictionFunction
*/
2007-03-22 10:30:00 -07:00
2007-05-14 13:09:20 -07:00
class nsOfflineCacheEvictionFunction : public mozIStorageFunction {
public :
NS_DECL_ISUPPORTS
NS_DECL_MOZISTORAGEFUNCTION
nsOfflineCacheEvictionFunction ( nsOfflineCacheDevice * device )
: mDevice ( device )
{ }
private :
nsOfflineCacheDevice * mDevice ;
} ;
NS_IMPL_ISUPPORTS1 ( nsOfflineCacheEvictionFunction , mozIStorageFunction )
// helper function for directly exposing the same data file binding
// path algorithm used in nsOfflineCacheBinding::Create
static nsresult
GetCacheDataFile ( nsIFile * cacheDir , const char * cid , const char * key ,
int generation , nsCOMPtr < nsIFile > & file )
2007-03-22 10:30:00 -07:00
{
2007-05-14 13:09:20 -07:00
cacheDir - > Clone ( getter_AddRefs ( file ) ) ;
if ( ! file )
return NS_ERROR_OUT_OF_MEMORY ;
2007-03-22 10:30:00 -07:00
2007-05-14 13:09:20 -07:00
nsCAutoString fullKey ;
fullKey . Append ( cid ) ;
fullKey . Append ( ' : ' ) ;
fullKey . Append ( key ) ;
2007-03-22 10:30:00 -07:00
2007-05-14 13:09:20 -07:00
PRUint64 hash = DCacheHash ( fullKey . get ( ) ) ;
PRUint32 dir1 = ( PRUint32 ) ( hash & 0x0F ) ;
PRUint32 dir2 = ( PRUint32 ) ( ( hash & 0xF0 ) > > 4 ) ;
hash > > = 8 ;
file - > AppendNative ( nsPrintfCString ( " %X " , dir1 ) ) ;
file - > AppendNative ( nsPrintfCString ( " %X " , dir2 ) ) ;
char leaf [ 64 ] ;
PR_snprintf ( leaf , sizeof ( leaf ) , " %014llX-%X " , hash , generation ) ;
return file - > AppendNative ( nsDependentCString ( leaf ) ) ;
}
NS_IMETHODIMP
nsOfflineCacheEvictionFunction : : OnFunctionCall ( mozIStorageValueArray * values )
{
LOG ( ( " nsOfflineCacheDevice::EvictionObserver \n " ) ) ;
PRUint32 numEntries ;
nsresult rv = values - > GetNumEntries ( & numEntries ) ;
NS_ENSURE_SUCCESS ( rv , rv ) ;
NS_ASSERTION ( numEntries = = 3 , " unexpected number of arguments " ) ;
2007-03-22 10:30:00 -07:00
2007-05-14 13:09:20 -07:00
PRUint32 valueLen ;
const char * cid = values - > AsSharedUTF8String ( 0 , & valueLen ) ;
const char * key = values - > AsSharedUTF8String ( 1 , & valueLen ) ;
int generation = values - > AsInt32 ( 2 ) ;
nsCOMPtr < nsIFile > file ;
rv = GetCacheDataFile ( mDevice - > CacheDirectory ( ) , cid , key ,
generation , file ) ;
if ( NS_FAILED ( rv ) )
{
LOG ( ( " GetCacheDataFile [cid=%s key=%s generation=%d] failed [rv=%x]! \n " ,
cid , key , generation , rv ) ) ;
return rv ;
2007-03-22 10:30:00 -07:00
}
2007-05-14 13:09:20 -07:00
# if defined(PR_LOGGING)
nsCAutoString path ;
file - > GetNativePath ( path ) ;
LOG ( ( " removing %s \n " , path . get ( ) ) ) ;
2007-03-22 10:30:00 -07:00
# endif
2007-05-14 13:09:20 -07:00
file - > Remove ( PR_FALSE ) ;
2007-03-22 10:30:00 -07:00
return NS_OK ;
}
/******************************************************************************
2007-05-14 13:09:20 -07:00
* nsOfflineCacheDeviceInfo
2007-03-22 10:30:00 -07:00
*/
2007-05-14 13:09:20 -07:00
class nsOfflineCacheDeviceInfo : public nsICacheDeviceInfo
2007-03-22 10:30:00 -07:00
{
public :
NS_DECL_ISUPPORTS
NS_DECL_NSICACHEDEVICEINFO
2007-05-14 13:09:20 -07:00
nsOfflineCacheDeviceInfo ( nsOfflineCacheDevice * device )
2007-03-22 10:30:00 -07:00
: mDevice ( device )
{ }
2007-05-14 13:09:20 -07:00
2007-03-22 10:30:00 -07:00
private :
2007-05-14 13:09:20 -07:00
nsOfflineCacheDevice * mDevice ;
2007-03-22 10:30:00 -07:00
} ;
2007-05-14 13:09:20 -07:00
NS_IMPL_ISUPPORTS1 ( nsOfflineCacheDeviceInfo , nsICacheDeviceInfo )
2007-03-22 10:30:00 -07:00
NS_IMETHODIMP
2007-05-14 13:09:20 -07:00
nsOfflineCacheDeviceInfo : : GetDescription ( char * * aDescription )
2007-03-22 10:30:00 -07:00
{
2007-05-14 13:09:20 -07:00
* aDescription = NS_strdup ( " Offline cache device " ) ;
2007-03-22 10:30:00 -07:00
return * aDescription ? NS_OK : NS_ERROR_OUT_OF_MEMORY ;
}
NS_IMETHODIMP
2007-05-14 13:09:20 -07:00
nsOfflineCacheDeviceInfo : : GetUsageReport ( char * * usageReport )
2007-03-22 10:30:00 -07:00
{
nsCAutoString buffer ;
buffer . AppendLiteral ( " \n <tr> \n <td><b>Cache Directory:</b></td> \n <td><tt> " ) ;
nsILocalFile * cacheDir = mDevice - > CacheDirectory ( ) ;
if ( ! cacheDir )
return NS_OK ;
nsAutoString path ;
nsresult rv = cacheDir - > GetPath ( path ) ;
if ( NS_SUCCEEDED ( rv ) )
AppendUTF16toUTF8 ( path , buffer ) ;
else
buffer . AppendLiteral ( " directory unavailable " ) ;
buffer . AppendLiteral ( " </tt></td> \n </tr> \n " ) ;
* usageReport = ToNewCString ( buffer ) ;
if ( ! * usageReport )
return NS_ERROR_OUT_OF_MEMORY ;
return NS_OK ;
}
NS_IMETHODIMP
2007-05-14 13:09:20 -07:00
nsOfflineCacheDeviceInfo : : GetEntryCount ( PRUint32 * aEntryCount )
2007-03-22 10:30:00 -07:00
{
* aEntryCount = mDevice - > EntryCount ( ) ;
return NS_OK ;
}
NS_IMETHODIMP
2007-05-14 13:09:20 -07:00
nsOfflineCacheDeviceInfo : : GetTotalSize ( PRUint32 * aTotalSize )
2007-03-22 10:30:00 -07:00
{
* aTotalSize = mDevice - > CacheSize ( ) ;
return NS_OK ;
}
NS_IMETHODIMP
2007-05-14 13:09:20 -07:00
nsOfflineCacheDeviceInfo : : GetMaximumSize ( PRUint32 * aMaximumSize )
2007-03-22 10:30:00 -07:00
{
* aMaximumSize = mDevice - > CacheCapacity ( ) ;
return NS_OK ;
}
/******************************************************************************
2007-05-14 13:09:20 -07:00
* nsOfflineCacheBinding
2007-03-22 10:30:00 -07:00
*/
2007-05-14 13:09:20 -07:00
class nsOfflineCacheBinding : public nsISupports
2007-03-22 10:30:00 -07:00
{
public :
NS_DECL_ISUPPORTS
2007-05-14 13:09:20 -07:00
static nsOfflineCacheBinding *
2007-03-22 10:30:00 -07:00
Create ( nsIFile * cacheDir , const char * key , int generation ) ;
nsCOMPtr < nsIFile > mDataFile ;
int mGeneration ;
} ;
2007-05-14 13:09:20 -07:00
NS_IMPL_THREADSAFE_ISUPPORTS0 ( nsOfflineCacheBinding )
2007-03-22 10:30:00 -07:00
2007-05-14 13:09:20 -07:00
nsOfflineCacheBinding *
nsOfflineCacheBinding : : Create ( nsIFile * cacheDir ,
const char * key ,
int generation )
2007-03-22 10:30:00 -07:00
{
nsCOMPtr < nsIFile > file ;
cacheDir - > Clone ( getter_AddRefs ( file ) ) ;
if ( ! file )
return nsnull ;
PRUint64 hash = DCacheHash ( key ) ;
PRUint32 dir1 = ( PRUint32 ) ( hash & 0x0F ) ;
PRUint32 dir2 = ( PRUint32 ) ( ( hash & 0xF0 ) > > 4 ) ;
hash > > = 8 ;
// XXX we might want to create these directories up-front
file - > AppendNative ( nsPrintfCString ( " %X " , dir1 ) ) ;
file - > Create ( nsIFile : : DIRECTORY_TYPE , 00700 ) ;
file - > AppendNative ( nsPrintfCString ( " %X " , dir2 ) ) ;
file - > Create ( nsIFile : : DIRECTORY_TYPE , 00700 ) ;
nsresult rv ;
char leaf [ 64 ] ;
if ( generation = = - 1 )
{
file - > AppendNative ( NS_LITERAL_CSTRING ( " placeholder " ) ) ;
for ( generation = 0 ; ; + + generation )
{
PR_snprintf ( leaf , sizeof ( leaf ) , " %014llX-%X " , hash , generation ) ;
rv = file - > SetNativeLeafName ( nsDependentCString ( leaf ) ) ;
if ( NS_FAILED ( rv ) )
return nsnull ;
rv = file - > Create ( nsIFile : : NORMAL_FILE_TYPE , 00600 ) ;
if ( NS_FAILED ( rv ) & & rv ! = NS_ERROR_FILE_ALREADY_EXISTS )
return nsnull ;
if ( NS_SUCCEEDED ( rv ) )
break ;
}
}
else
{
PR_snprintf ( leaf , sizeof ( leaf ) , " %014llX-%X " , hash , generation ) ;
rv = file - > AppendNative ( nsDependentCString ( leaf ) ) ;
if ( NS_FAILED ( rv ) )
return nsnull ;
}
2007-05-14 13:09:20 -07:00
nsOfflineCacheBinding * binding = new nsOfflineCacheBinding ;
2007-03-22 10:30:00 -07:00
if ( ! binding )
return nsnull ;
binding - > mDataFile . swap ( file ) ;
binding - > mGeneration = generation ;
return binding ;
}
/******************************************************************************
2007-05-14 13:09:20 -07:00
* nsOfflineCacheRecord
2007-03-22 10:30:00 -07:00
*/
2007-05-14 13:09:20 -07:00
struct nsOfflineCacheRecord
2007-03-22 10:30:00 -07:00
{
const char * clientID ;
const char * key ;
const PRUint8 * metaData ;
PRUint32 metaDataLen ;
PRInt32 generation ;
PRInt32 flags ;
PRInt32 dataSize ;
PRInt32 fetchCount ;
PRInt64 lastFetched ;
PRInt64 lastModified ;
PRInt64 expirationTime ;
} ;
static nsCacheEntry *
2007-05-14 13:09:20 -07:00
CreateCacheEntry ( nsOfflineCacheDevice * device ,
2007-03-22 10:30:00 -07:00
const nsCString * fullKey ,
2007-05-14 13:09:20 -07:00
const nsOfflineCacheRecord & rec )
2007-03-22 10:30:00 -07:00
{
if ( rec . flags ! = 0 )
{
LOG ( ( " refusing to load busy entry \n " ) ) ;
return nsnull ;
}
nsCacheEntry * entry ;
nsresult rv = nsCacheEntry : : Create ( fullKey - > get ( ) , // XXX enable sharing
nsICache : : STREAM_BASED ,
2007-05-14 13:09:20 -07:00
nsICache : : STORE_OFFLINE ,
2007-03-22 10:30:00 -07:00
device , & entry ) ;
if ( NS_FAILED ( rv ) )
return nsnull ;
entry - > SetFetchCount ( ( PRUint32 ) rec . fetchCount ) ;
entry - > SetLastFetched ( SecondsFromPRTime ( rec . lastFetched ) ) ;
entry - > SetLastModified ( SecondsFromPRTime ( rec . lastModified ) ) ;
entry - > SetExpirationTime ( SecondsFromPRTime ( rec . expirationTime ) ) ;
entry - > SetDataSize ( ( PRUint32 ) rec . dataSize ) ;
entry - > UnflattenMetaData ( ( const char * ) rec . metaData , rec . metaDataLen ) ;
// create a binding object for this entry
2007-05-14 13:09:20 -07:00
nsOfflineCacheBinding * binding =
nsOfflineCacheBinding : : Create ( device - > CacheDirectory ( ) ,
fullKey - > get ( ) ,
rec . generation ) ;
2007-03-22 10:30:00 -07:00
if ( ! binding )
{
delete entry ;
return nsnull ;
}
entry - > SetData ( binding ) ;
return entry ;
}
/******************************************************************************
2007-05-14 13:09:20 -07:00
* nsOfflineCacheEntryInfo
2007-03-22 10:30:00 -07:00
*/
2007-05-14 13:09:20 -07:00
class nsOfflineCacheEntryInfo : public nsICacheEntryInfo
2007-03-22 10:30:00 -07:00
{
public :
NS_DECL_ISUPPORTS
NS_DECL_NSICACHEENTRYINFO
2007-05-14 13:09:20 -07:00
nsOfflineCacheRecord * mRec ;
2007-03-22 10:30:00 -07:00
} ;
2007-05-14 13:09:20 -07:00
NS_IMPL_ISUPPORTS1 ( nsOfflineCacheEntryInfo , nsICacheEntryInfo )
2007-03-22 10:30:00 -07:00
NS_IMETHODIMP
2007-05-14 13:09:20 -07:00
nsOfflineCacheEntryInfo : : GetClientID ( char * * result )
2007-03-22 10:30:00 -07:00
{
2007-05-14 13:09:20 -07:00
* result = NS_strdup ( mRec - > clientID ) ;
2007-03-22 10:30:00 -07:00
return * result ? NS_OK : NS_ERROR_OUT_OF_MEMORY ;
}
NS_IMETHODIMP
2007-05-14 13:09:20 -07:00
nsOfflineCacheEntryInfo : : GetDeviceID ( char * * deviceID )
2007-03-22 10:30:00 -07:00
{
2007-05-14 13:09:20 -07:00
* deviceID = NS_strdup ( OFFLINE_CACHE_DEVICE_ID ) ;
2007-03-22 10:30:00 -07:00
return * deviceID ? NS_OK : NS_ERROR_OUT_OF_MEMORY ;
}
NS_IMETHODIMP
2007-05-14 13:09:20 -07:00
nsOfflineCacheEntryInfo : : GetKey ( nsACString & clientKey )
2007-03-22 10:30:00 -07:00
{
clientKey . Assign ( mRec - > key ) ;
return NS_OK ;
}
NS_IMETHODIMP
2007-05-14 13:09:20 -07:00
nsOfflineCacheEntryInfo : : GetFetchCount ( PRInt32 * aFetchCount )
2007-03-22 10:30:00 -07:00
{
* aFetchCount = mRec - > fetchCount ;
return NS_OK ;
}
NS_IMETHODIMP
2007-05-14 13:09:20 -07:00
nsOfflineCacheEntryInfo : : GetLastFetched ( PRUint32 * aLastFetched )
2007-03-22 10:30:00 -07:00
{
* aLastFetched = SecondsFromPRTime ( mRec - > lastFetched ) ;
return NS_OK ;
}
NS_IMETHODIMP
2007-05-14 13:09:20 -07:00
nsOfflineCacheEntryInfo : : GetLastModified ( PRUint32 * aLastModified )
2007-03-22 10:30:00 -07:00
{
* aLastModified = SecondsFromPRTime ( mRec - > lastModified ) ;
return NS_OK ;
}
NS_IMETHODIMP
2007-05-14 13:09:20 -07:00
nsOfflineCacheEntryInfo : : GetExpirationTime ( PRUint32 * aExpirationTime )
2007-03-22 10:30:00 -07:00
{
* aExpirationTime = SecondsFromPRTime ( mRec - > expirationTime ) ;
return NS_OK ;
}
NS_IMETHODIMP
2007-05-14 13:09:20 -07:00
nsOfflineCacheEntryInfo : : IsStreamBased ( PRBool * aStreamBased )
2007-03-22 10:30:00 -07:00
{
* aStreamBased = PR_TRUE ;
return NS_OK ;
}
2007-05-14 13:09:20 -07:00
NS_IMETHODIMP
nsOfflineCacheEntryInfo : : GetDataSize ( PRUint32 * aDataSize )
2007-03-22 10:30:00 -07:00
{
* aDataSize = mRec - > dataSize ;
return NS_OK ;
}
/******************************************************************************
2007-05-14 13:09:20 -07:00
* nsOfflineCacheDevice
2007-03-22 10:30:00 -07:00
*/
2007-05-14 13:09:20 -07:00
nsOfflineCacheDevice : : nsOfflineCacheDevice ( )
2007-03-22 10:30:00 -07:00
: mDB ( nsnull )
, mCacheCapacity ( 0 )
, mDeltaCounter ( 0 )
{
}
2007-05-14 13:09:20 -07:00
nsOfflineCacheDevice : : ~ nsOfflineCacheDevice ( )
2007-03-22 10:30:00 -07:00
{
Shutdown ( ) ;
}
PRUint32
2007-05-14 13:09:20 -07:00
nsOfflineCacheDevice : : CacheSize ( )
2007-03-22 10:30:00 -07:00
{
AutoResetStatement statement ( mStatement_CacheSize ) ;
PRBool hasRows ;
nsresult rv = statement - > ExecuteStep ( & hasRows ) ;
NS_ENSURE_TRUE ( NS_SUCCEEDED ( rv ) & & hasRows , 0 ) ;
return ( PRUint32 ) statement - > AsInt32 ( 0 ) ;
}
PRUint32
2007-05-14 13:09:20 -07:00
nsOfflineCacheDevice : : EntryCount ( )
2007-03-22 10:30:00 -07:00
{
AutoResetStatement statement ( mStatement_EntryCount ) ;
PRBool hasRows ;
nsresult rv = statement - > ExecuteStep ( & hasRows ) ;
NS_ENSURE_TRUE ( NS_SUCCEEDED ( rv ) & & hasRows , 0 ) ;
return ( PRUint32 ) statement - > AsInt32 ( 0 ) ;
}
nsresult
2007-05-14 13:09:20 -07:00
nsOfflineCacheDevice : : UpdateEntry ( nsCacheEntry * entry )
2007-03-22 10:30:00 -07:00
{
// Decompose the key into "ClientID" and "Key"
nsCAutoString keyBuf ;
const char * cid , * key ;
if ( ! DecomposeCacheEntryKey ( entry - > Key ( ) , & cid , & key , keyBuf ) )
return NS_ERROR_UNEXPECTED ;
nsCString metaDataBuf ;
PRUint32 mdSize = entry - > MetaDataSize ( ) ;
if ( ! EnsureStringLength ( metaDataBuf , mdSize ) )
return NS_ERROR_OUT_OF_MEMORY ;
char * md = metaDataBuf . BeginWriting ( ) ;
entry - > FlattenMetaData ( md , mdSize ) ;
2007-05-14 13:09:20 -07:00
nsOfflineCacheRecord rec ;
2007-03-22 10:30:00 -07:00
rec . metaData = ( const PRUint8 * ) md ;
rec . metaDataLen = mdSize ;
rec . flags = 0 ; // mark entry as inactive
rec . dataSize = entry - > DataSize ( ) ;
rec . fetchCount = entry - > FetchCount ( ) ;
rec . lastFetched = PRTimeFromSeconds ( entry - > LastFetched ( ) ) ;
rec . lastModified = PRTimeFromSeconds ( entry - > LastModified ( ) ) ;
rec . expirationTime = PRTimeFromSeconds ( entry - > ExpirationTime ( ) ) ;
AutoResetStatement statement ( mStatement_UpdateEntry ) ;
nsresult rv ;
2007-05-14 13:09:20 -07:00
rv = statement - > BindBlobParameter ( 0 , rec . metaData , rec . metaDataLen ) ;
2007-03-22 10:30:00 -07:00
rv | = statement - > BindInt32Parameter ( 1 , rec . flags ) ;
rv | = statement - > BindInt32Parameter ( 2 , rec . dataSize ) ;
rv | = statement - > BindInt32Parameter ( 3 , rec . fetchCount ) ;
rv | = statement - > BindInt64Parameter ( 4 , rec . lastFetched ) ;
rv | = statement - > BindInt64Parameter ( 5 , rec . lastModified ) ;
rv | = statement - > BindInt64Parameter ( 6 , rec . expirationTime ) ;
2007-05-14 13:09:20 -07:00
rv | = statement - > BindUTF8StringParameter ( 7 , nsDependentCString ( cid ) ) ;
rv | = statement - > BindUTF8StringParameter ( 8 , nsDependentCString ( key ) ) ;
2007-03-22 10:30:00 -07:00
NS_ENSURE_SUCCESS ( rv , rv ) ;
PRBool hasRows ;
rv = statement - > ExecuteStep ( & hasRows ) ;
NS_ENSURE_SUCCESS ( rv , rv ) ;
NS_ASSERTION ( ! hasRows , " UPDATE should not result in output " ) ;
return rv ;
}
nsresult
2007-05-14 13:09:20 -07:00
nsOfflineCacheDevice : : UpdateEntrySize ( nsCacheEntry * entry , PRUint32 newSize )
2007-03-22 10:30:00 -07:00
{
// Decompose the key into "ClientID" and "Key"
nsCAutoString keyBuf ;
const char * cid , * key ;
if ( ! DecomposeCacheEntryKey ( entry - > Key ( ) , & cid , & key , keyBuf ) )
return NS_ERROR_UNEXPECTED ;
AutoResetStatement statement ( mStatement_UpdateEntrySize ) ;
nsresult rv ;
rv = statement - > BindInt32Parameter ( 0 , newSize ) ;
2007-05-14 13:09:20 -07:00
rv | = statement - > BindUTF8StringParameter ( 1 , nsDependentCString ( cid ) ) ;
rv | = statement - > BindUTF8StringParameter ( 2 , nsDependentCString ( key ) ) ;
2007-03-22 10:30:00 -07:00
NS_ENSURE_SUCCESS ( rv , rv ) ;
PRBool hasRows ;
rv = statement - > ExecuteStep ( & hasRows ) ;
NS_ENSURE_SUCCESS ( rv , rv ) ;
NS_ASSERTION ( ! hasRows , " UPDATE should not result in output " ) ;
return rv ;
}
nsresult
2007-05-14 13:09:20 -07:00
nsOfflineCacheDevice : : DeleteEntry ( nsCacheEntry * entry , PRBool deleteData )
2007-03-22 10:30:00 -07:00
{
if ( deleteData )
{
nsresult rv = DeleteData ( entry ) ;
if ( NS_FAILED ( rv ) )
return rv ;
}
// Decompose the key into "ClientID" and "Key"
nsCAutoString keyBuf ;
const char * cid , * key ;
if ( ! DecomposeCacheEntryKey ( entry - > Key ( ) , & cid , & key , keyBuf ) )
return NS_ERROR_UNEXPECTED ;
AutoResetStatement statement ( mStatement_DeleteEntry ) ;
nsresult rv ;
2007-05-14 13:09:20 -07:00
rv = statement - > BindUTF8StringParameter ( 0 , nsDependentCString ( cid ) ) ;
rv | = statement - > BindUTF8StringParameter ( 1 , nsDependentCString ( key ) ) ;
2007-03-22 10:30:00 -07:00
NS_ENSURE_SUCCESS ( rv , rv ) ;
PRBool hasRows ;
rv = statement - > ExecuteStep ( & hasRows ) ;
NS_ENSURE_SUCCESS ( rv , rv ) ;
NS_ASSERTION ( ! hasRows , " DELETE should not result in output " ) ;
return rv ;
}
nsresult
2007-05-14 13:09:20 -07:00
nsOfflineCacheDevice : : DeleteData ( nsCacheEntry * entry )
2007-03-22 10:30:00 -07:00
{
2007-05-14 13:09:20 -07:00
nsOfflineCacheBinding * binding = ( nsOfflineCacheBinding * ) entry - > Data ( ) ;
2007-03-22 10:30:00 -07:00
NS_ENSURE_STATE ( binding ) ;
return binding - > mDataFile - > Remove ( PR_FALSE ) ;
}
/**
* nsCacheDevice implementation
*/
nsresult
2007-05-14 13:09:20 -07:00
nsOfflineCacheDevice : : Init ( )
2007-03-22 10:30:00 -07:00
{
NS_ENSURE_TRUE ( ! mDB , NS_ERROR_ALREADY_INITIALIZED ) ;
// SetCacheParentDirectory must have been called
NS_ENSURE_TRUE ( mCacheDirectory , NS_ERROR_UNEXPECTED ) ;
// make sure the cache directory exists
nsresult rv = EnsureDir ( mCacheDirectory ) ;
NS_ENSURE_SUCCESS ( rv , rv ) ;
// build path to index file
nsCOMPtr < nsIFile > indexFile ;
rv = mCacheDirectory - > Clone ( getter_AddRefs ( indexFile ) ) ;
NS_ENSURE_SUCCESS ( rv , rv ) ;
2007-05-14 13:09:20 -07:00
rv = indexFile - > AppendNative ( NS_LITERAL_CSTRING ( " index.sqlite " ) ) ;
2007-03-22 10:30:00 -07:00
NS_ENSURE_SUCCESS ( rv , rv ) ;
nsCOMPtr < mozIStorageService > ss =
2007-05-14 13:09:20 -07:00
do_GetService ( " @mozilla.org/storage/service;1 " , & rv ) ;
2007-03-22 10:30:00 -07:00
NS_ENSURE_SUCCESS ( rv , rv ) ;
rv = ss - > OpenDatabase ( indexFile , getter_AddRefs ( mDB ) ) ;
NS_ENSURE_SUCCESS ( rv , rv ) ;
mDB - > ExecuteSimpleSQL ( NS_LITERAL_CSTRING ( " PRAGMA synchronous = OFF; " ) ) ;
// XXX ... other initialization steps
// XXX in the future we may wish to verify the schema for moz_cache
// perhaps using "PRAGMA table_info" ?
// build the table
//
// "Generation" is the data file generation number.
// "Flags" is a bit-field indicating the state of the entry.
//
mDB - > ExecuteSimpleSQL (
NS_LITERAL_CSTRING ( " CREATE TABLE moz_cache ( \n "
" ClientID TEXT, \n "
" Key TEXT, \n "
" MetaData BLOB, \n "
" Generation INTEGER, \n "
" Flags INTEGER, \n "
" DataSize INTEGER, \n "
" FetchCount INTEGER, \n "
" LastFetched INTEGER, \n "
" LastModified INTEGER, \n "
" ExpirationTime INTEGER \n "
" ); \n " ) ) ;
// maybe the table already exists, so don't bother checking for errors.
2007-05-14 13:09:20 -07:00
// build the ownership table
mDB - > ExecuteSimpleSQL (
NS_LITERAL_CSTRING ( " CREATE TABLE moz_cache_owners ( \n "
" ClientID TEXT, \n "
" Domain TEXT, \n "
" URI TEXT, \n "
" Key TEXT \n "
" ); \n " ) ) ;
// maybe the table already exists, so don't bother checking for errors.
2007-03-22 10:30:00 -07:00
mDB - > ExecuteSimpleSQL (
NS_LITERAL_CSTRING ( " CREATE UNIQUE INDEX moz_cache_index "
" ON moz_cache (ClientID, Key); " ) ) ;
// maybe the index already exists, so don't bother checking for errors.
2007-05-14 13:09:20 -07:00
nsCOMPtr < mozIStorageFunction > evictionFunction =
new nsOfflineCacheEvictionFunction ( this ) ;
if ( ! evictionFunction ) return NS_ERROR_OUT_OF_MEMORY ;
rv = mDB - > CreateFunction ( " cache_eviction_observer " , 3 , evictionFunction ) ;
NS_ENSURE_SUCCESS ( rv , rv ) ;
2007-03-22 10:30:00 -07:00
// create all (most) of our statements up front
2007-05-15 00:53:42 -07:00
struct StatementSql {
2007-03-22 10:30:00 -07:00
nsCOMPtr < mozIStorageStatement > & statement ;
const char * sql ;
2007-05-15 00:53:42 -07:00
StatementSql ( nsCOMPtr < mozIStorageStatement > aStatement , const char * aSql ) :
statement ( aStatement ) , sql ( aSql ) { } ;
2007-03-22 10:30:00 -07:00
} prepared [ ] = {
2007-05-15 00:53:42 -07:00
StatementSql ( mStatement_CacheSize , " SELECT Sum(DataSize) from moz_cache; " ) ,
StatementSql ( mStatement_EntryCount , " SELECT count(*) from moz_cache; " ) ,
StatementSql ( mStatement_UpdateEntry , " UPDATE moz_cache SET MetaData = ?, Flags = ?, DataSize = ?, FetchCount = ?, LastFetched = ?, LastModified = ?, ExpirationTime = ? WHERE ClientID = ? AND Key = ?; " ) ,
StatementSql ( mStatement_UpdateEntrySize , " UPDATE moz_cache SET DataSize = ? WHERE ClientID = ? AND Key = ?; " ) ,
StatementSql ( mStatement_UpdateEntryFlags , " UPDATE moz_cache SET Flags = ? WHERE ClientID = ? AND Key = ?; " ) ,
StatementSql ( mStatement_DeleteEntry , " DELETE FROM moz_cache WHERE ClientID = ? AND Key = ?; " ) ,
StatementSql ( mStatement_FindEntry , " SELECT MetaData, Generation, Flags, DataSize, FetchCount, LastFetched, LastModified, ExpirationTime FROM moz_cache WHERE ClientID = ? AND Key = ?; " ) ,
StatementSql ( mStatement_BindEntry , " INSERT INTO moz_cache (ClientID, Key, MetaData, Generation, Flags, DataSize, FetchCount, LastFetched, LastModified, ExpirationTime) VALUES(?,?,?,?,?,?,?,?,?,?); " ) ,
StatementSql ( mStatement_ClearOwnership , " DELETE FROM moz_cache_owners WHERE ClientId = ? AND Domain = ? AND URI = ?; " ) ,
StatementSql ( mStatement_RemoveOwnership , " DELETE FROM moz_cache_owners WHERE ClientID = ? AND Domain = ? AND URI = ? AND Key = ?; " ) ,
StatementSql ( mStatement_ClearDomain , " DELETE FROM moz_cache_owners WHERE ClientID = ? AND Domain = ?; " ) ,
StatementSql ( mStatement_AddOwnership , " INSERT INTO moz_cache_owners (ClientID, Domain, URI, Key) VALUES (?, ?, ?, ?); " ) ,
StatementSql ( mStatement_CheckOwnership , " SELECT Key From moz_cache_owners WHERE ClientID = ? AND Domain = ? AND URI = ? AND Key = ?; " ) ,
StatementSql ( mStatement_ListOwned , " SELECT Key FROM moz_cache_owners WHERE ClientID = ? AND Domain = ? AND URI = ?; " ) ,
StatementSql ( mStatement_DeleteUnowned , " DELETE FROM moz_cache WHERE rowid IN (SELECT moz_cache.rowid FROM moz_cache LEFT OUTER JOIN moz_cache_owners ON (moz_cache.ClientID = moz_cache_owners.ClientID AND moz_cache.Key = moz_cache_owners.Key) WHERE moz_cache.ClientID = ? AND moz_cache_owners.Domain ISNULL); " ) ,
2007-03-22 10:30:00 -07:00
} ;
for ( PRUint32 i = 0 ; i < NS_ARRAY_LENGTH ( prepared ) ; + + i )
{
rv | = mDB - > CreateStatement ( nsDependentCString ( prepared [ i ] . sql ) ,
getter_AddRefs ( prepared [ i ] . statement ) ) ;
}
NS_ENSURE_SUCCESS ( rv , rv ) ;
2007-05-14 13:09:20 -07:00
// Clear up any dangling active flags
rv = mDB - > ExecuteSimpleSQL (
NS_LITERAL_CSTRING ( " UPDATE moz_cache "
" SET Flags=(Flags & ~1) "
" WHERE (Flags & 1); " ) ) ;
NS_ENSURE_SUCCESS ( rv , rv ) ;
2007-03-22 10:30:00 -07:00
return NS_OK ;
}
nsresult
2007-05-14 13:09:20 -07:00
nsOfflineCacheDevice : : Shutdown ( )
2007-03-22 10:30:00 -07:00
{
NS_ENSURE_TRUE ( mDB , NS_ERROR_NOT_INITIALIZED ) ;
mDB = 0 ;
return NS_OK ;
}
const char *
2007-05-14 13:09:20 -07:00
nsOfflineCacheDevice : : GetDeviceID ( )
2007-03-22 10:30:00 -07:00
{
2007-05-14 13:09:20 -07:00
return OFFLINE_CACHE_DEVICE_ID ;
2007-03-22 10:30:00 -07:00
}
nsCacheEntry *
2007-05-14 13:09:20 -07:00
nsOfflineCacheDevice : : FindEntry ( nsCString * fullKey , PRBool * collision )
2007-03-22 10:30:00 -07:00
{
2007-05-14 13:09:20 -07:00
LOG ( ( " nsOfflineCacheDevice::FindEntry [key=%s] \n " , fullKey - > get ( ) ) ) ;
2007-03-22 10:30:00 -07:00
// SELECT * FROM moz_cache WHERE key = ?
// Decompose the key into "ClientID" and "Key"
nsCAutoString keyBuf ;
const char * cid , * key ;
if ( ! DecomposeCacheEntryKey ( fullKey , & cid , & key , keyBuf ) )
return nsnull ;
AutoResetStatement statement ( mStatement_FindEntry ) ;
nsresult rv ;
2007-05-14 13:09:20 -07:00
rv = statement - > BindUTF8StringParameter ( 0 , nsDependentCString ( cid ) ) ;
rv | = statement - > BindUTF8StringParameter ( 1 , nsDependentCString ( key ) ) ;
2007-03-22 10:30:00 -07:00
NS_ENSURE_SUCCESS ( rv , nsnull ) ;
PRBool hasRows ;
rv = statement - > ExecuteStep ( & hasRows ) ;
if ( NS_FAILED ( rv ) | | ! hasRows )
return nsnull ; // entry not found
2007-05-14 13:09:20 -07:00
nsOfflineCacheRecord rec ;
statement - > GetSharedBlob ( 0 , & rec . metaDataLen ,
( const PRUint8 * * ) & rec . metaData ) ;
2007-03-22 10:30:00 -07:00
rec . generation = statement - > AsInt32 ( 1 ) ;
rec . flags = statement - > AsInt32 ( 2 ) ;
rec . dataSize = statement - > AsInt32 ( 3 ) ;
rec . fetchCount = statement - > AsInt32 ( 4 ) ;
rec . lastFetched = statement - > AsInt64 ( 5 ) ;
rec . lastModified = statement - > AsInt64 ( 6 ) ;
rec . expirationTime = statement - > AsInt64 ( 7 ) ;
LOG ( ( " entry: [%u %d %d %d %d %lld %lld %lld] \n " ,
rec . metaDataLen ,
rec . generation ,
rec . flags ,
rec . dataSize ,
rec . fetchCount ,
rec . lastFetched ,
rec . lastModified ,
rec . expirationTime ) ) ;
2007-05-14 13:09:20 -07:00
nsCacheEntry * entry = CreateCacheEntry ( this , fullKey , rec ) ;
2007-03-22 10:30:00 -07:00
2007-05-14 13:09:20 -07:00
if ( entry )
{
// make sure that the data file exists
nsOfflineCacheBinding * binding = ( nsOfflineCacheBinding * ) entry - > Data ( ) ;
PRBool isFile ;
rv = binding - > mDataFile - > IsFile ( & isFile ) ;
if ( NS_FAILED ( rv ) | | ! isFile )
{
DeleteEntry ( entry , PR_FALSE ) ;
delete entry ;
return nsnull ;
}
statement - > Reset ( ) ;
// mark as active
AutoResetStatement updateStatement ( mStatement_UpdateEntryFlags ) ;
rec . flags | = 0x1 ;
rv | = updateStatement - > BindInt32Parameter ( 0 , rec . flags ) ;
rv | = updateStatement - > BindUTF8StringParameter ( 1 , nsDependentCString ( cid ) ) ;
rv | = updateStatement - > BindUTF8StringParameter ( 2 , nsDependentCString ( key ) ) ;
if ( NS_FAILED ( rv ) )
{
delete entry ;
return nsnull ;
}
rv = updateStatement - > ExecuteStep ( & hasRows ) ;
if ( NS_FAILED ( rv ) )
{
delete entry ;
return nsnull ;
}
NS_ASSERTION ( ! hasRows , " UPDATE should not result in output " ) ;
}
return entry ;
2007-03-22 10:30:00 -07:00
}
nsresult
2007-05-14 13:09:20 -07:00
nsOfflineCacheDevice : : DeactivateEntry ( nsCacheEntry * entry )
2007-03-22 10:30:00 -07:00
{
2007-05-14 13:09:20 -07:00
LOG ( ( " nsOfflineCacheDevice::DeactivateEntry [key=%s] \n " ,
entry - > Key ( ) - > get ( ) ) ) ;
2007-03-22 10:30:00 -07:00
// This method is called to inform us that the nsCacheEntry object is going
// away. We should persist anything that needs to be persisted, or if the
// entry is doomed, we can go ahead and clear its storage.
if ( entry - > IsDoomed ( ) )
{
// remove corresponding row and file if they exist
// the row should have been removed in DoomEntry... we could assert that
// that happened. otherwise, all we have to do here is delete the file
// on disk.
DeleteData ( entry ) ;
}
else
{
// UPDATE the database row
// XXX Assumption: the row already exists because it was either created
// with a call to BindEntry or it was there when we called FindEntry.
UpdateEntry ( entry ) ;
}
delete entry ;
2007-05-14 13:09:20 -07:00
2007-03-22 10:30:00 -07:00
return NS_OK ;
}
nsresult
2007-05-14 13:09:20 -07:00
nsOfflineCacheDevice : : BindEntry ( nsCacheEntry * entry )
2007-03-22 10:30:00 -07:00
{
2007-05-14 13:09:20 -07:00
LOG ( ( " nsOfflineCacheDevice::BindEntry [key=%s] \n " , entry - > Key ( ) - > get ( ) ) ) ;
2007-03-22 10:30:00 -07:00
NS_ENSURE_STATE ( ! entry - > Data ( ) ) ;
// This method is called to inform us that we have a new entry. The entry
// may collide with an existing entry in our DB, but if that happens we can
// assume that the entry is not being used.
// INSERT the database row
// XXX Assumption: if the row already exists, then FindEntry would have
// returned it. if that entry was doomed, then DoomEntry would have removed
// it from the table. so, we should always have to insert at this point.
// Decompose the key into "ClientID" and "Key"
nsCAutoString keyBuf ;
const char * cid , * key ;
if ( ! DecomposeCacheEntryKey ( entry - > Key ( ) , & cid , & key , keyBuf ) )
return NS_ERROR_UNEXPECTED ;
// create binding, pick best generation number
2007-05-14 13:09:20 -07:00
nsRefPtr < nsOfflineCacheBinding > binding =
nsOfflineCacheBinding : : Create ( mCacheDirectory , entry - > Key ( ) - > get ( ) , - 1 ) ;
2007-03-22 10:30:00 -07:00
if ( ! binding )
return NS_ERROR_OUT_OF_MEMORY ;
2007-05-14 13:09:20 -07:00
nsOfflineCacheRecord rec ;
2007-03-22 10:30:00 -07:00
rec . clientID = cid ;
rec . key = key ;
rec . metaData = NULL ; // don't write any metadata now.
rec . metaDataLen = 0 ;
rec . generation = binding - > mGeneration ;
rec . flags = 0x1 ; // mark entry as active, we'll reset this in DeactivateEntry
rec . dataSize = 0 ;
rec . fetchCount = entry - > FetchCount ( ) ;
rec . lastFetched = PRTimeFromSeconds ( entry - > LastFetched ( ) ) ;
rec . lastModified = PRTimeFromSeconds ( entry - > LastModified ( ) ) ;
rec . expirationTime = PRTimeFromSeconds ( entry - > ExpirationTime ( ) ) ;
AutoResetStatement statement ( mStatement_BindEntry ) ;
nsresult rv ;
2007-05-14 13:09:20 -07:00
rv = statement - > BindUTF8StringParameter ( 0 , nsDependentCString ( rec . clientID ) ) ;
rv | = statement - > BindUTF8StringParameter ( 1 , nsDependentCString ( rec . key ) ) ;
rv | = statement - > BindBlobParameter ( 2 , rec . metaData , rec . metaDataLen ) ;
2007-03-22 10:30:00 -07:00
rv | = statement - > BindInt32Parameter ( 3 , rec . generation ) ;
rv | = statement - > BindInt32Parameter ( 4 , rec . flags ) ;
rv | = statement - > BindInt32Parameter ( 5 , rec . dataSize ) ;
rv | = statement - > BindInt32Parameter ( 6 , rec . fetchCount ) ;
rv | = statement - > BindInt64Parameter ( 7 , rec . lastFetched ) ;
rv | = statement - > BindInt64Parameter ( 8 , rec . lastModified ) ;
rv | = statement - > BindInt64Parameter ( 9 , rec . expirationTime ) ;
NS_ENSURE_SUCCESS ( rv , rv ) ;
PRBool hasRows ;
rv = statement - > ExecuteStep ( & hasRows ) ;
NS_ENSURE_SUCCESS ( rv , rv ) ;
NS_ASSERTION ( ! hasRows , " INSERT should not result in output " ) ;
entry - > SetData ( binding ) ;
return NS_OK ;
}
void
2007-05-14 13:09:20 -07:00
nsOfflineCacheDevice : : DoomEntry ( nsCacheEntry * entry )
2007-03-22 10:30:00 -07:00
{
2007-05-14 13:09:20 -07:00
LOG ( ( " nsOfflineCacheDevice::DoomEntry [key=%s] \n " , entry - > Key ( ) - > get ( ) ) ) ;
2007-03-22 10:30:00 -07:00
// This method is called to inform us that we should mark the entry to be
// deleted when it is no longer in use.
// We can go ahead and delete the corresponding row in our table,
// but we must not delete the file on disk until we are deactivated.
DeleteEntry ( entry , PR_FALSE ) ;
}
nsresult
2007-05-14 13:09:20 -07:00
nsOfflineCacheDevice : : OpenInputStreamForEntry ( nsCacheEntry * entry ,
nsCacheAccessMode mode ,
PRUint32 offset ,
nsIInputStream * * result )
2007-03-22 10:30:00 -07:00
{
2007-05-14 13:09:20 -07:00
LOG ( ( " nsOfflineCacheDevice::OpenInputStreamForEntry [key=%s] \n " ,
entry - > Key ( ) - > get ( ) ) ) ;
2007-03-22 10:30:00 -07:00
* result = nsnull ;
NS_ENSURE_TRUE ( offset < entry - > DataSize ( ) , NS_ERROR_INVALID_ARG ) ;
// return an input stream to the entry's data file. the stream
// may be read on a background thread.
2007-05-14 13:09:20 -07:00
nsOfflineCacheBinding * binding = ( nsOfflineCacheBinding * ) entry - > Data ( ) ;
2007-03-22 10:30:00 -07:00
NS_ENSURE_STATE ( binding ) ;
nsCOMPtr < nsIInputStream > in ;
NS_NewLocalFileInputStream ( getter_AddRefs ( in ) , binding - > mDataFile , PR_RDONLY ) ;
if ( ! in )
return NS_ERROR_UNEXPECTED ;
// respect |offset| param
if ( offset ! = 0 )
{
nsCOMPtr < nsISeekableStream > seekable = do_QueryInterface ( in ) ;
NS_ENSURE_TRUE ( seekable , NS_ERROR_UNEXPECTED ) ;
seekable - > Seek ( nsISeekableStream : : NS_SEEK_SET , offset ) ;
}
in . swap ( * result ) ;
return NS_OK ;
}
nsresult
2007-05-14 13:09:20 -07:00
nsOfflineCacheDevice : : OpenOutputStreamForEntry ( nsCacheEntry * entry ,
nsCacheAccessMode mode ,
PRUint32 offset ,
nsIOutputStream * * result )
2007-03-22 10:30:00 -07:00
{
2007-05-14 13:09:20 -07:00
LOG ( ( " nsOfflineCacheDevice::OpenOutputStreamForEntry [key=%s] \n " ,
entry - > Key ( ) - > get ( ) ) ) ;
2007-03-22 10:30:00 -07:00
* result = nsnull ;
NS_ENSURE_TRUE ( offset < = entry - > DataSize ( ) , NS_ERROR_INVALID_ARG ) ;
// return an output stream to the entry's data file. we can assume
// that the output stream will only be used on the main thread.
2007-05-14 13:09:20 -07:00
nsOfflineCacheBinding * binding = ( nsOfflineCacheBinding * ) entry - > Data ( ) ;
2007-03-22 10:30:00 -07:00
NS_ENSURE_STATE ( binding ) ;
nsCOMPtr < nsIOutputStream > out ;
NS_NewLocalFileOutputStream ( getter_AddRefs ( out ) , binding - > mDataFile ,
PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE ,
00600 ) ;
if ( ! out )
return NS_ERROR_UNEXPECTED ;
// respect |offset| param
nsCOMPtr < nsISeekableStream > seekable = do_QueryInterface ( out ) ;
NS_ENSURE_TRUE ( seekable , NS_ERROR_UNEXPECTED ) ;
if ( offset ! = 0 )
seekable - > Seek ( nsISeekableStream : : NS_SEEK_SET , offset ) ;
// truncate the file at the given offset
seekable - > SetEOF ( ) ;
nsCOMPtr < nsIOutputStream > bufferedOut ;
NS_NewBufferedOutputStream ( getter_AddRefs ( bufferedOut ) , out , 16 * 1024 ) ;
if ( ! bufferedOut )
return NS_ERROR_UNEXPECTED ;
bufferedOut . swap ( * result ) ;
return NS_OK ;
}
nsresult
2007-05-14 13:09:20 -07:00
nsOfflineCacheDevice : : GetFileForEntry ( nsCacheEntry * entry , nsIFile * * result )
2007-03-22 10:30:00 -07:00
{
2007-05-14 13:09:20 -07:00
LOG ( ( " nsOfflineCacheDevice::GetFileForEntry [key=%s] \n " ,
entry - > Key ( ) - > get ( ) ) ) ;
2007-03-22 10:30:00 -07:00
2007-05-14 13:09:20 -07:00
nsOfflineCacheBinding * binding = ( nsOfflineCacheBinding * ) entry - > Data ( ) ;
2007-03-22 10:30:00 -07:00
NS_ENSURE_STATE ( binding ) ;
NS_IF_ADDREF ( * result = binding - > mDataFile ) ;
return NS_OK ;
}
nsresult
2007-05-14 13:09:20 -07:00
nsOfflineCacheDevice : : OnDataSizeChange ( nsCacheEntry * entry , PRInt32 deltaSize )
2007-03-22 10:30:00 -07:00
{
2007-05-14 13:09:20 -07:00
LOG ( ( " nsOfflineCacheDevice::OnDataSizeChange [key=%s delta=%d] \n " ,
2007-03-22 10:30:00 -07:00
entry - > Key ( ) - > get ( ) , deltaSize ) ) ;
2007-05-14 13:09:20 -07:00
const PRInt32 DELTA_THRESHOLD = 1 < < 14 ; // 16k
2007-03-22 10:30:00 -07:00
// called to notify us of an impending change in the total size of the
2007-05-14 13:09:20 -07:00
// specified entry.
2007-03-22 10:30:00 -07:00
PRUint32 oldSize = entry - > DataSize ( ) ;
NS_ASSERTION ( deltaSize > = 0 | | PRInt32 ( oldSize ) + deltaSize > = 0 , " oops " ) ;
PRUint32 newSize = PRInt32 ( oldSize ) + deltaSize ;
UpdateEntrySize ( entry , newSize ) ;
mDeltaCounter + = deltaSize ; // this may go negative
if ( mDeltaCounter > = DELTA_THRESHOLD )
{
2007-05-14 13:09:20 -07:00
if ( CacheSize ( ) > mCacheCapacity ) {
// the entry will overrun the cache capacity, doom the entry
// and abort
nsresult rv = nsCacheService : : DoomEntry ( entry ) ;
NS_ASSERTION ( NS_SUCCEEDED ( rv ) , " DoomEntry() failed. " ) ;
return NS_ERROR_ABORT ;
}
2007-03-22 10:30:00 -07:00
mDeltaCounter = 0 ; // reset counter
}
return NS_OK ;
}
nsresult
2007-05-14 13:09:20 -07:00
nsOfflineCacheDevice : : Visit ( nsICacheVisitor * visitor )
2007-03-22 10:30:00 -07:00
{
NS_ENSURE_TRUE ( Initialized ( ) , NS_ERROR_NOT_INITIALIZED ) ;
2007-05-14 13:09:20 -07:00
// called to enumerate the offline cache.
2007-03-22 10:30:00 -07:00
nsCOMPtr < nsICacheDeviceInfo > deviceInfo =
2007-05-14 13:09:20 -07:00
new nsOfflineCacheDeviceInfo ( this ) ;
2007-03-22 10:30:00 -07:00
PRBool keepGoing ;
2007-05-14 13:09:20 -07:00
nsresult rv = visitor - > VisitDevice ( OFFLINE_CACHE_DEVICE_ID , deviceInfo ,
2007-03-22 10:30:00 -07:00
& keepGoing ) ;
if ( NS_FAILED ( rv ) )
return rv ;
if ( ! keepGoing )
return NS_OK ;
// SELECT * from moz_cache;
2007-05-14 13:09:20 -07:00
nsOfflineCacheRecord rec ;
nsRefPtr < nsOfflineCacheEntryInfo > info = new nsOfflineCacheEntryInfo ;
2007-03-22 10:30:00 -07:00
if ( ! info )
return NS_ERROR_OUT_OF_MEMORY ;
info - > mRec = & rec ;
// XXX may want to list columns explicitly
nsCOMPtr < mozIStorageStatement > statement ;
rv = mDB - > CreateStatement (
NS_LITERAL_CSTRING ( " SELECT * FROM moz_cache; " ) ,
getter_AddRefs ( statement ) ) ;
NS_ENSURE_SUCCESS ( rv , rv ) ;
PRBool hasRows ;
for ( ; ; )
{
rv = statement - > ExecuteStep ( & hasRows ) ;
if ( NS_FAILED ( rv ) | | ! hasRows )
break ;
2007-05-14 13:09:20 -07:00
statement - > GetSharedUTF8String ( 0 , NULL , & rec . clientID ) ;
statement - > GetSharedUTF8String ( 1 , NULL , & rec . key ) ;
statement - > GetSharedBlob ( 2 , & rec . metaDataLen ,
( const PRUint8 * * ) & rec . metaData ) ;
2007-03-22 10:30:00 -07:00
rec . generation = statement - > AsInt32 ( 3 ) ;
rec . flags = statement - > AsInt32 ( 4 ) ;
rec . dataSize = statement - > AsInt32 ( 5 ) ;
rec . fetchCount = statement - > AsInt32 ( 6 ) ;
rec . lastFetched = statement - > AsInt64 ( 7 ) ;
rec . lastModified = statement - > AsInt64 ( 8 ) ;
rec . expirationTime = statement - > AsInt64 ( 9 ) ;
PRBool keepGoing ;
2007-05-14 13:09:20 -07:00
rv = visitor - > VisitEntry ( OFFLINE_CACHE_DEVICE_ID , info , & keepGoing ) ;
2007-03-22 10:30:00 -07:00
if ( NS_FAILED ( rv ) | | ! keepGoing )
break ;
}
info - > mRec = nsnull ;
return NS_OK ;
}
nsresult
2007-05-14 13:09:20 -07:00
nsOfflineCacheDevice : : EvictEntries ( const char * clientID )
2007-03-22 10:30:00 -07:00
{
2007-05-14 13:09:20 -07:00
LOG ( ( " nsOfflineCacheDevice::EvictEntries [cid=%s] \n " ,
clientID ? clientID : " " ) ) ;
2007-03-22 10:30:00 -07:00
// called to evict all entries matching the given clientID.
// need trigger to fire user defined function after a row is deleted
// so we can delete the corresponding data file.
2007-05-14 13:09:20 -07:00
EvictionObserver evictionObserver ( mDB ) ;
2007-03-22 10:30:00 -07:00
const char * deleteCmd ;
if ( clientID )
{
deleteCmd =
PR_smprintf ( " DELETE FROM moz_cache WHERE ClientID=%q AND Flags=0; " ,
clientID ) ;
if ( ! deleteCmd )
return NS_ERROR_OUT_OF_MEMORY ;
}
else
{
deleteCmd = " DELETE FROM moz_cache WHERE Flags = 0; " ;
}
2007-05-14 13:09:20 -07:00
nsresult rv = mDB - > ExecuteSimpleSQL ( nsDependentCString ( deleteCmd ) ) ;
2007-03-22 10:30:00 -07:00
if ( clientID )
PR_smprintf_free ( ( char * ) deleteCmd ) ;
NS_ENSURE_SUCCESS ( rv , rv ) ;
return NS_OK ;
}
nsresult
2007-05-14 13:09:20 -07:00
nsOfflineCacheDevice : : SetOwnedKeys ( const char * clientID ,
const nsACString & ownerDomain ,
const nsACString & ownerURI ,
PRUint32 count ,
const char * * keys )
2007-03-22 10:30:00 -07:00
{
2007-05-14 13:09:20 -07:00
LOG ( ( " nsOfflineCacheDevice::SetOwnedKeys [cid=%s] \n " , clientID ) ) ;
mozStorageTransaction transaction ( mDB , PR_FALSE ) ;
2007-03-22 10:30:00 -07:00
2007-05-14 13:09:20 -07:00
nsDependentCString clientIDStr ( clientID ) ;
2007-03-22 10:30:00 -07:00
2007-05-14 13:09:20 -07:00
AutoResetStatement clearStatement ( mStatement_ClearOwnership ) ;
nsresult rv = clearStatement - > BindUTF8StringParameter (
0 , clientIDStr ) ;
rv | = clearStatement - > BindUTF8StringParameter ( 1 , ownerDomain ) ;
rv | = clearStatement - > BindUTF8StringParameter ( 2 , ownerURI ) ;
NS_ENSURE_SUCCESS ( rv , rv ) ;
2007-03-22 10:30:00 -07:00
2007-05-14 13:09:20 -07:00
rv = clearStatement - > Execute ( ) ;
2007-03-22 10:30:00 -07:00
NS_ENSURE_SUCCESS ( rv , rv ) ;
2007-05-14 13:09:20 -07:00
for ( PRUint32 i = 0 ; i < count ; i + + )
2007-03-22 10:30:00 -07:00
{
2007-05-14 13:09:20 -07:00
AutoResetStatement insertStatement ( mStatement_AddOwnership ) ;
rv = insertStatement - > BindUTF8StringParameter ( 0 , clientIDStr ) ;
rv | = insertStatement - > BindUTF8StringParameter ( 1 , ownerDomain ) ;
rv | = insertStatement - > BindUTF8StringParameter ( 2 , ownerURI ) ;
rv | = insertStatement - > BindUTF8StringParameter ( 3 , nsDependentCString ( keys [ i ] ) ) ;
NS_ENSURE_SUCCESS ( rv , rv ) ;
rv = insertStatement - > Execute ( ) ;
NS_ENSURE_SUCCESS ( rv , rv ) ;
}
2007-03-22 10:30:00 -07:00
2007-05-14 13:09:20 -07:00
return transaction . Commit ( ) ;
}
nsresult
nsOfflineCacheDevice : : GetOwnedKeys ( const char * clientID ,
const nsACString & ownerDomain ,
const nsACString & ownerURI ,
PRUint32 * count ,
char * * * keys )
{
LOG ( ( " nsOfflineCacheDevice::GetOwnedKeys [cid=%s] \n " , clientID ) ) ;
AutoResetStatement statement ( mStatement_ListOwned ) ;
nsresult rv = statement - > BindUTF8StringParameter (
0 , nsDependentCString ( clientID ) ) ;
rv | = statement - > BindUTF8StringParameter ( 1 , ownerDomain ) ;
rv | = statement - > BindUTF8StringParameter ( 2 , ownerURI ) ;
NS_ENSURE_SUCCESS ( rv , rv ) ;
PRBool hasRows ;
rv = statement - > ExecuteStep ( & hasRows ) ;
NS_ENSURE_SUCCESS ( rv , rv ) ;
nsTArray < nsCString > keyArray ;
while ( hasRows )
{
PRUint32 length ;
keyArray . AppendElement (
nsDependentCString ( statement - > AsSharedUTF8String ( 0 , & length ) ) ) ;
rv = statement - > ExecuteStep ( & hasRows ) ;
NS_ENSURE_SUCCESS ( rv , rv ) ;
}
2007-03-22 10:30:00 -07:00
2007-05-14 13:09:20 -07:00
* count = keyArray . Length ( ) ;
char * * ret = NS_STATIC_CAST ( char * * , NS_Alloc ( * count * sizeof ( char * ) ) ) ;
if ( ! ret ) return NS_ERROR_OUT_OF_MEMORY ;
for ( PRUint32 i = 0 ; i < * count ; i + + ) {
ret [ i ] = NS_strdup ( keyArray [ i ] . get ( ) ) ;
if ( ! ret [ i ] ) {
NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY ( i , ret ) ;
return NS_ERROR_OUT_OF_MEMORY ;
}
2007-03-22 10:30:00 -07:00
}
2007-05-14 13:09:20 -07:00
* keys = ret ;
2007-03-22 10:30:00 -07:00
return NS_OK ;
}
2007-05-14 13:09:20 -07:00
nsresult
nsOfflineCacheDevice : : AddOwnedKey ( const char * clientID ,
const nsACString & ownerDomain ,
const nsACString & ownerURI ,
const nsACString & key )
{
LOG ( ( " nsOfflineCacheDevice::AddOwnedKey [cid=%s] \n " , clientID ) ) ;
PRBool isOwned ;
nsresult rv = KeyIsOwned ( clientID , ownerDomain , ownerURI , key , & isOwned ) ;
NS_ENSURE_SUCCESS ( rv , rv ) ;
if ( isOwned ) return NS_OK ;
AutoResetStatement statement ( mStatement_AddOwnership ) ;
rv = statement - > BindUTF8StringParameter ( 0 , nsDependentCString ( clientID ) ) ;
rv | = statement - > BindUTF8StringParameter ( 1 , ownerDomain ) ;
rv | = statement - > BindUTF8StringParameter ( 2 , ownerURI ) ;
rv | = statement - > BindUTF8StringParameter ( 3 , key ) ;
NS_ENSURE_SUCCESS ( rv , rv ) ;
return statement - > Execute ( ) ;
}
nsresult
nsOfflineCacheDevice : : RemoveOwnedKey ( const char * clientID ,
const nsACString & ownerDomain ,
const nsACString & ownerURI ,
const nsACString & key )
{
LOG ( ( " nsOfflineCacheDevice::RemoveOwnedKey [cid=%s] \n " , clientID ) ) ;
PRBool isOwned ;
nsresult rv = KeyIsOwned ( clientID , ownerDomain , ownerURI , key , & isOwned ) ;
NS_ENSURE_SUCCESS ( rv , rv ) ;
if ( ! isOwned ) return NS_ERROR_NOT_AVAILABLE ;
AutoResetStatement statement ( mStatement_RemoveOwnership ) ;
rv = statement - > BindUTF8StringParameter ( 0 , nsDependentCString ( clientID ) ) ;
rv | = statement - > BindUTF8StringParameter ( 1 , ownerDomain ) ;
rv | = statement - > BindUTF8StringParameter ( 2 , ownerURI ) ;
rv | = statement - > BindUTF8StringParameter ( 3 , key ) ;
NS_ENSURE_SUCCESS ( rv , rv ) ;
return statement - > Execute ( ) ;
}
nsresult
nsOfflineCacheDevice : : KeyIsOwned ( const char * clientID ,
const nsACString & ownerDomain ,
const nsACString & ownerURI ,
const nsACString & key ,
PRBool * isOwned )
{
AutoResetStatement statement ( mStatement_CheckOwnership ) ;
nsresult rv = statement - > BindUTF8StringParameter (
0 , nsDependentCString ( clientID ) ) ;
rv | = statement - > BindUTF8StringParameter ( 1 , ownerDomain ) ;
rv | = statement - > BindUTF8StringParameter ( 2 , ownerURI ) ;
rv | = statement - > BindUTF8StringParameter ( 3 , key ) ;
NS_ENSURE_SUCCESS ( rv , rv ) ;
return statement - > ExecuteStep ( isOwned ) ;
}
nsresult
nsOfflineCacheDevice : : ClearKeysOwnedByDomain ( const char * clientID ,
const nsACString & domain )
{
LOG ( ( " nsOfflineCacheDevice::ClearKeysOwnedByDomain [cid=%s] \n " , clientID ) ) ;
AutoResetStatement statement ( mStatement_ClearDomain ) ;
nsresult rv = statement - > BindUTF8StringParameter (
0 , nsDependentCString ( clientID ) ) ;
rv | = statement - > BindUTF8StringParameter ( 1 , domain ) ;
NS_ENSURE_SUCCESS ( rv , rv ) ;
return statement - > Execute ( ) ;
}
nsresult
nsOfflineCacheDevice : : EvictUnownedEntries ( const char * clientID )
{
LOG ( ( " nsOfflineCacheDevice::EvictUnownedEntries [cid=%s] \n " , clientID ) ) ;
EvictionObserver evictionObserver ( mDB ) ;
AutoResetStatement statement ( mStatement_DeleteUnowned ) ;
nsresult rv = statement - > BindUTF8StringParameter (
0 , nsDependentCString ( clientID ) ) ;
NS_ENSURE_SUCCESS ( rv , rv ) ;
return statement - > Execute ( ) ;
}
2007-03-22 10:30:00 -07:00
/**
* Preference accessors
*/
void
2007-05-14 13:09:20 -07:00
nsOfflineCacheDevice : : SetCacheParentDirectory ( nsILocalFile * parentDir )
2007-03-22 10:30:00 -07:00
{
if ( Initialized ( ) )
{
NS_ERROR ( " cannot switch cache directory once initialized " ) ;
return ;
}
if ( ! parentDir )
{
mCacheDirectory = nsnull ;
return ;
}
// ensure parent directory exists
nsresult rv = EnsureDir ( parentDir ) ;
if ( NS_FAILED ( rv ) )
{
NS_WARNING ( " unable to create parent directory " ) ;
return ;
}
// cache dir may not exist, but that's ok
nsCOMPtr < nsIFile > dir ;
rv = parentDir - > Clone ( getter_AddRefs ( dir ) ) ;
if ( NS_FAILED ( rv ) )
return ;
2007-05-14 13:09:20 -07:00
rv = dir - > AppendNative ( NS_LITERAL_CSTRING ( " OfflineCache " ) ) ;
2007-03-22 10:30:00 -07:00
if ( NS_FAILED ( rv ) )
return ;
mCacheDirectory = do_QueryInterface ( dir ) ;
}
void
2007-05-14 13:09:20 -07:00
nsOfflineCacheDevice : : SetCapacity ( PRUint32 capacity )
2007-03-22 10:30:00 -07:00
{
mCacheCapacity = capacity * 1024 ;
}