1 /* vim:set ts=2 sw=2 sts=2 et cin: */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is Mozilla.
17 * The Initial Developer of the Original Code is IBM Corporation.
18 * Portions created by IBM Corporation are Copyright (C) 2004
19 * IBM Corporation. All Rights Reserved.
22 * Darin Fisher <darin@meer.net>
23 * Dave Camp <dcamp@mozilla.com>
24 * Honza Bambas <honzab@firemni.cz>
26 * Alternatively, the contents of this file may be used under the terms of
27 * either the GNU General Public License Version 2 or later (the "GPL"), or
28 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
41 #include "nsDiskCache.h"
42 #include "nsDiskCacheDeviceSQL.h"
43 #include "nsCacheService.h"
46 #include "nsNetUtil.h"
47 #include "nsAutoPtr.h"
49 #include "nsIPrefBranch.h"
50 #include "nsIPrefService.h"
52 #include "nsPrintfCString.h"
54 #include "nsArrayUtils.h"
56 #include "nsIVariant.h"
58 #include "mozIStorageService.h"
59 #include "mozIStorageStatement.h"
60 #include "mozIStorageFunction.h"
61 #include "mozStorageHelper.h"
63 #include "nsICacheVisitor.h"
64 #include "nsISeekableStream.h"
66 static const char OFFLINE_CACHE_DEVICE_ID
[] = { "offline" };
67 static NS_DEFINE_CID(kCacheServiceCID
, NS_CACHESERVICE_CID
);
69 #define LOG(args) CACHE_LOG_DEBUG(args)
71 static PRUint32 gNextTemporaryClientID
= 0;
73 /*****************************************************************************
78 EnsureDir(nsIFile
*dir
)
81 nsresult rv
= dir
->Exists(&exists
);
82 if (NS_SUCCEEDED(rv
) && !exists
)
83 rv
= dir
->Create(nsIFile::DIRECTORY_TYPE
, 0700);
88 DecomposeCacheEntryKey(const nsCString
*fullKey
,
95 PRInt32 colon
= buf
.FindChar(':');
96 if (colon
== kNotFound
)
98 NS_ERROR("Invalid key");
101 buf
.SetCharAt('\0', colon
);
104 *key
= buf
.get() + colon
+ 1;
109 class AutoResetStatement
112 AutoResetStatement(mozIStorageStatement
*s
)
114 ~AutoResetStatement() { mStatement
->Reset(); }
115 mozIStorageStatement
*operator->() { return mStatement
; }
117 mozIStorageStatement
*mStatement
;
120 class EvictionObserver
123 EvictionObserver(mozIStorageConnection
*db
,
124 nsOfflineCacheEvictionFunction
*evictionFunction
)
125 : mDB(db
), mEvictionFunction(evictionFunction
)
127 mDB
->ExecuteSimpleSQL(
128 NS_LITERAL_CSTRING("CREATE TEMP TRIGGER cache_on_delete AFTER DELETE"
129 " ON moz_cache FOR EACH ROW BEGIN SELECT"
130 " cache_eviction_observer("
131 " OLD.key, OLD.generation);"
133 mEvictionFunction
->Reset();
138 mDB
->ExecuteSimpleSQL(
139 NS_LITERAL_CSTRING("DROP TRIGGER cache_on_delete;"));
140 mEvictionFunction
->Reset();
143 void Apply() { return mEvictionFunction
->Apply(); }
146 mozIStorageConnection
*mDB
;
147 nsRefPtr
<nsOfflineCacheEvictionFunction
> mEvictionFunction
;
150 #define DCACHE_HASH_MAX LL_MAXINT
151 #define DCACHE_HASH_BITS 64
154 * nsOfflineCache::Hash(const char * key)
156 * This algorithm of this method implies nsOfflineCacheRecords will be stored
157 * in a certain order on disk. If the algorithm changes, existing cache
158 * map files may become invalid, and therefore the kCurrentVersion needs
162 DCacheHash(const char * key
)
165 for (const PRUint8
* s
= (PRUint8
*) key
; *s
!= '\0'; ++s
)
166 h
= (h
>> (DCACHE_HASH_BITS
- 4)) ^ (h
<< 4) ^ *s
;
167 return (h
== 0 ? DCACHE_HASH_MAX
: h
);
170 /******************************************************************************
171 * nsOfflineCacheEvictionFunction
174 NS_IMPL_ISUPPORTS1(nsOfflineCacheEvictionFunction
, mozIStorageFunction
)
176 // helper function for directly exposing the same data file binding
177 // path algorithm used in nsOfflineCacheBinding::Create
179 GetCacheDataFile(nsIFile
*cacheDir
, const char *key
,
180 int generation
, nsCOMPtr
<nsIFile
> &file
)
182 cacheDir
->Clone(getter_AddRefs(file
));
184 return NS_ERROR_OUT_OF_MEMORY
;
186 PRUint64 hash
= DCacheHash(key
);
188 PRUint32 dir1
= (PRUint32
) (hash
& 0x0F);
189 PRUint32 dir2
= (PRUint32
)((hash
& 0xF0) >> 4);
193 file
->AppendNative(nsPrintfCString("%X", dir1
));
194 file
->AppendNative(nsPrintfCString("%X", dir2
));
197 PR_snprintf(leaf
, sizeof(leaf
), "%014llX-%X", hash
, generation
);
198 return file
->AppendNative(nsDependentCString(leaf
));
202 nsOfflineCacheEvictionFunction::OnFunctionCall(mozIStorageValueArray
*values
, nsIVariant
**_retval
)
204 LOG(("nsOfflineCacheEvictionFunction::OnFunctionCall\n"));
209 nsresult rv
= values
->GetNumEntries(&numEntries
);
210 NS_ENSURE_SUCCESS(rv
, rv
);
211 NS_ASSERTION(numEntries
== 2, "unexpected number of arguments");
214 const char *key
= values
->AsSharedUTF8String(0, &valueLen
);
215 int generation
= values
->AsInt32(1);
217 nsCOMPtr
<nsIFile
> file
;
218 rv
= GetCacheDataFile(mDevice
->CacheDirectory(), key
,
222 LOG(("GetCacheDataFile [key=%s generation=%d] failed [rv=%x]!\n",
223 key
, generation
, rv
));
227 mItems
.AppendObject(file
);
233 nsOfflineCacheEvictionFunction::Apply()
235 LOG(("nsOfflineCacheEvictionFunction::Apply\n"));
237 for (PRInt32 i
= 0; i
< mItems
.Count(); i
++) {
238 #if defined(PR_LOGGING)
240 mItems
[i
]->GetNativePath(path
);
241 LOG((" removing %s\n", path
.get()));
244 mItems
[i
]->Remove(PR_FALSE
);
250 /******************************************************************************
251 * nsOfflineCacheDeviceInfo
254 class nsOfflineCacheDeviceInfo
: public nsICacheDeviceInfo
258 NS_DECL_NSICACHEDEVICEINFO
260 nsOfflineCacheDeviceInfo(nsOfflineCacheDevice
* device
)
265 nsOfflineCacheDevice
* mDevice
;
268 NS_IMPL_ISUPPORTS1(nsOfflineCacheDeviceInfo
, nsICacheDeviceInfo
)
271 nsOfflineCacheDeviceInfo::GetDescription(char **aDescription
)
273 *aDescription
= NS_strdup("Offline cache device");
274 return *aDescription
? NS_OK
: NS_ERROR_OUT_OF_MEMORY
;
278 nsOfflineCacheDeviceInfo::GetUsageReport(char ** usageReport
)
280 nsCAutoString buffer
;
281 buffer
.AppendLiteral("\n<tr>\n<td><b>Cache Directory:</b></td>\n<td><tt> ");
283 nsILocalFile
*cacheDir
= mDevice
->CacheDirectory();
288 nsresult rv
= cacheDir
->GetPath(path
);
289 if (NS_SUCCEEDED(rv
))
290 AppendUTF16toUTF8(path
, buffer
);
292 buffer
.AppendLiteral("directory unavailable");
293 buffer
.AppendLiteral("</tt></td>\n</tr>\n");
295 *usageReport
= ToNewCString(buffer
);
297 return NS_ERROR_OUT_OF_MEMORY
;
303 nsOfflineCacheDeviceInfo::GetEntryCount(PRUint32
*aEntryCount
)
305 *aEntryCount
= mDevice
->EntryCount();
310 nsOfflineCacheDeviceInfo::GetTotalSize(PRUint32
*aTotalSize
)
312 *aTotalSize
= mDevice
->CacheSize();
317 nsOfflineCacheDeviceInfo::GetMaximumSize(PRUint32
*aMaximumSize
)
319 *aMaximumSize
= mDevice
->CacheCapacity();
323 /******************************************************************************
324 * nsOfflineCacheBinding
327 class nsOfflineCacheBinding
: public nsISupports
332 static nsOfflineCacheBinding
*
333 Create(nsIFile
*cacheDir
, const nsCString
*key
, int generation
);
335 nsCOMPtr
<nsIFile
> mDataFile
;
339 NS_IMPL_THREADSAFE_ISUPPORTS0(nsOfflineCacheBinding
)
341 nsOfflineCacheBinding
*
342 nsOfflineCacheBinding::Create(nsIFile
*cacheDir
,
343 const nsCString
*fullKey
,
346 nsCOMPtr
<nsIFile
> file
;
347 cacheDir
->Clone(getter_AddRefs(file
));
351 nsCAutoString keyBuf
;
352 const char *cid
, *key
;
353 if (!DecomposeCacheEntryKey(fullKey
, &cid
, &key
, keyBuf
))
356 PRUint64 hash
= DCacheHash(key
);
358 PRUint32 dir1
= (PRUint32
) (hash
& 0x0F);
359 PRUint32 dir2
= (PRUint32
)((hash
& 0xF0) >> 4);
363 // XXX we might want to create these directories up-front
365 file
->AppendNative(nsPrintfCString("%X", dir1
));
366 file
->Create(nsIFile::DIRECTORY_TYPE
, 00700);
368 file
->AppendNative(nsPrintfCString("%X", dir2
));
369 file
->Create(nsIFile::DIRECTORY_TYPE
, 00700);
374 if (generation
== -1)
376 file
->AppendNative(NS_LITERAL_CSTRING("placeholder"));
378 for (generation
= 0; ; ++generation
)
380 PR_snprintf(leaf
, sizeof(leaf
), "%014llX-%X", hash
, generation
);
382 rv
= file
->SetNativeLeafName(nsDependentCString(leaf
));
385 rv
= file
->Create(nsIFile::NORMAL_FILE_TYPE
, 00600);
386 if (NS_FAILED(rv
) && rv
!= NS_ERROR_FILE_ALREADY_EXISTS
)
388 if (NS_SUCCEEDED(rv
))
394 PR_snprintf(leaf
, sizeof(leaf
), "%014llX-%X", hash
, generation
);
395 rv
= file
->AppendNative(nsDependentCString(leaf
));
400 nsOfflineCacheBinding
*binding
= new nsOfflineCacheBinding
;
404 binding
->mDataFile
.swap(file
);
405 binding
->mGeneration
= generation
;
409 /******************************************************************************
410 * nsOfflineCacheRecord
413 struct nsOfflineCacheRecord
415 const char *clientID
;
417 const PRUint8
*metaData
;
418 PRUint32 metaDataLen
;
424 PRInt64 lastModified
;
425 PRInt64 expirationTime
;
428 static nsCacheEntry
*
429 CreateCacheEntry(nsOfflineCacheDevice
*device
,
430 const nsCString
*fullKey
,
431 const nsOfflineCacheRecord
&rec
)
435 LOG(("refusing to load busy entry\n"));
441 nsresult rv
= nsCacheEntry::Create(fullKey
->get(), // XXX enable sharing
442 nsICache::STREAM_BASED
,
443 nsICache::STORE_OFFLINE
,
448 entry
->SetFetchCount((PRUint32
) rec
.fetchCount
);
449 entry
->SetLastFetched(SecondsFromPRTime(rec
.lastFetched
));
450 entry
->SetLastModified(SecondsFromPRTime(rec
.lastModified
));
451 entry
->SetExpirationTime(SecondsFromPRTime(rec
.expirationTime
));
452 entry
->SetDataSize((PRUint32
) rec
.dataSize
);
454 entry
->UnflattenMetaData((const char *) rec
.metaData
, rec
.metaDataLen
);
456 // create a binding object for this entry
457 nsOfflineCacheBinding
*binding
=
458 nsOfflineCacheBinding::Create(device
->CacheDirectory(),
466 entry
->SetData(binding
);
472 /******************************************************************************
473 * nsOfflineCacheEntryInfo
476 class nsOfflineCacheEntryInfo
: public nsICacheEntryInfo
480 NS_DECL_NSICACHEENTRYINFO
482 nsOfflineCacheRecord
*mRec
;
485 NS_IMPL_ISUPPORTS1(nsOfflineCacheEntryInfo
, nsICacheEntryInfo
)
488 nsOfflineCacheEntryInfo::GetClientID(char **result
)
490 *result
= NS_strdup(mRec
->clientID
);
491 return *result
? NS_OK
: NS_ERROR_OUT_OF_MEMORY
;
495 nsOfflineCacheEntryInfo::GetDeviceID(char ** deviceID
)
497 *deviceID
= NS_strdup(OFFLINE_CACHE_DEVICE_ID
);
498 return *deviceID
? NS_OK
: NS_ERROR_OUT_OF_MEMORY
;
502 nsOfflineCacheEntryInfo::GetKey(nsACString
&clientKey
)
504 clientKey
.Assign(mRec
->key
);
509 nsOfflineCacheEntryInfo::GetFetchCount(PRInt32
*aFetchCount
)
511 *aFetchCount
= mRec
->fetchCount
;
516 nsOfflineCacheEntryInfo::GetLastFetched(PRUint32
*aLastFetched
)
518 *aLastFetched
= SecondsFromPRTime(mRec
->lastFetched
);
523 nsOfflineCacheEntryInfo::GetLastModified(PRUint32
*aLastModified
)
525 *aLastModified
= SecondsFromPRTime(mRec
->lastModified
);
530 nsOfflineCacheEntryInfo::GetExpirationTime(PRUint32
*aExpirationTime
)
532 *aExpirationTime
= SecondsFromPRTime(mRec
->expirationTime
);
537 nsOfflineCacheEntryInfo::IsStreamBased(PRBool
*aStreamBased
)
539 *aStreamBased
= PR_TRUE
;
544 nsOfflineCacheEntryInfo::GetDataSize(PRUint32
*aDataSize
)
546 *aDataSize
= mRec
->dataSize
;
551 /******************************************************************************
552 * nsApplicationCacheNamespace
555 NS_IMPL_ISUPPORTS1(nsApplicationCacheNamespace
, nsIApplicationCacheNamespace
)
558 nsApplicationCacheNamespace::Init(PRUint32 itemType
,
559 const nsACString
&namespaceSpec
,
560 const nsACString
&data
)
562 mItemType
= itemType
;
563 mNamespaceSpec
= namespaceSpec
;
569 nsApplicationCacheNamespace::GetItemType(PRUint32
*out
)
576 nsApplicationCacheNamespace::GetNamespaceSpec(nsACString
&out
)
578 out
= mNamespaceSpec
;
583 nsApplicationCacheNamespace::GetData(nsACString
&out
)
589 /******************************************************************************
593 class nsApplicationCache
: public nsIApplicationCache
594 , public nsSupportsWeakReference
598 NS_DECL_NSIAPPLICATIONCACHE
600 nsApplicationCache(nsOfflineCacheDevice
*device
,
601 const nsACString
&group
,
602 const nsACString
&clientID
);
604 virtual ~nsApplicationCache();
606 void MarkInvalid() { mValid
= PR_FALSE
; }
609 nsRefPtr
<nsOfflineCacheDevice
> mDevice
;
615 NS_IMPL_ISUPPORTS2(nsApplicationCache
,
617 nsISupportsWeakReference
)
619 nsApplicationCache::nsApplicationCache(nsOfflineCacheDevice
*device
,
620 const nsACString
&group
,
621 const nsACString
&clientID
)
624 , mClientID(clientID
)
629 nsApplicationCache::~nsApplicationCache()
631 mDevice
->mCaches
.Remove(mClientID
);
633 // If this isn't an active cache anymore, it can be destroyed.
634 if (mValid
&& !mDevice
->IsActiveCache(mGroup
, mClientID
))
639 nsApplicationCache::GetGroupID(nsACString
&out
)
646 nsApplicationCache::GetClientID(nsACString
&out
)
653 nsApplicationCache::GetActive(PRBool
*out
)
655 *out
= mDevice
->IsActiveCache(mGroup
, mClientID
);
660 nsApplicationCache::Activate()
662 NS_ENSURE_TRUE(mValid
, NS_ERROR_NOT_AVAILABLE
);
664 mDevice
->ActivateCache(mGroup
, mClientID
);
669 nsApplicationCache::Discard()
671 NS_ENSURE_TRUE(mValid
, NS_ERROR_NOT_AVAILABLE
);
675 if (mDevice
->IsActiveCache(mGroup
, mClientID
))
677 mDevice
->DeactivateGroup(mGroup
);
680 return mDevice
->EvictEntries(mClientID
.get());
684 nsApplicationCache::MarkEntry(const nsACString
&key
,
687 NS_ENSURE_TRUE(mValid
, NS_ERROR_NOT_AVAILABLE
);
689 return mDevice
->MarkEntry(mClientID
, key
, typeBits
);
694 nsApplicationCache::UnmarkEntry(const nsACString
&key
,
697 NS_ENSURE_TRUE(mValid
, NS_ERROR_NOT_AVAILABLE
);
699 return mDevice
->UnmarkEntry(mClientID
, key
, typeBits
);
703 nsApplicationCache::GetTypes(const nsACString
&key
,
706 NS_ENSURE_TRUE(mValid
, NS_ERROR_NOT_AVAILABLE
);
708 return mDevice
->GetTypes(mClientID
, key
, typeBits
);
712 nsApplicationCache::GatherEntries(PRUint32 typeBits
,
716 NS_ENSURE_TRUE(mValid
, NS_ERROR_NOT_AVAILABLE
);
718 return mDevice
->GatherEntries(mClientID
, typeBits
, count
, keys
);
722 nsApplicationCache::AddNamespaces(nsIArray
*namespaces
)
724 NS_ENSURE_TRUE(mValid
, NS_ERROR_NOT_AVAILABLE
);
729 mozStorageTransaction
transaction(mDevice
->mDB
, PR_FALSE
);
732 nsresult rv
= namespaces
->GetLength(&length
);
733 NS_ENSURE_SUCCESS(rv
, rv
);
735 for (PRUint32 i
= 0; i
< length
; i
++) {
736 nsCOMPtr
<nsIApplicationCacheNamespace
> ns
=
737 do_QueryElementAt(namespaces
, i
);
739 rv
= mDevice
->AddNamespace(mClientID
, ns
);
740 NS_ENSURE_SUCCESS(rv
, rv
);
744 rv
= transaction
.Commit();
745 NS_ENSURE_SUCCESS(rv
, rv
);
751 nsApplicationCache::GetMatchingNamespace(const nsACString
&key
,
752 nsIApplicationCacheNamespace
**out
)
755 NS_ENSURE_TRUE(mValid
, NS_ERROR_NOT_AVAILABLE
);
757 return mDevice
->GetMatchingNamespace(mClientID
, key
, out
);
760 /******************************************************************************
761 * nsOfflineCacheDevice
764 NS_IMPL_ISUPPORTS1(nsOfflineCacheDevice
, nsIApplicationCacheService
)
766 nsOfflineCacheDevice::nsOfflineCacheDevice()
773 nsOfflineCacheDevice::~nsOfflineCacheDevice()
780 nsOfflineCacheDevice::GetStrictFileOriginPolicy()
782 nsCOMPtr
<nsIPrefBranch
> prefs
= do_GetService(NS_PREFSERVICE_CONTRACTID
);
785 if (prefs
&& NS_SUCCEEDED(prefs
->GetBoolPref("security.fileuri.strict_origin_policy", &retval
)))
788 // As default value use true (be more strict)
793 nsOfflineCacheDevice::CacheSize()
795 AutoResetStatement
statement(mStatement_CacheSize
);
798 nsresult rv
= statement
->ExecuteStep(&hasRows
);
799 NS_ENSURE_TRUE(NS_SUCCEEDED(rv
) && hasRows
, 0);
801 return (PRUint32
) statement
->AsInt32(0);
805 nsOfflineCacheDevice::EntryCount()
807 AutoResetStatement
statement(mStatement_EntryCount
);
810 nsresult rv
= statement
->ExecuteStep(&hasRows
);
811 NS_ENSURE_TRUE(NS_SUCCEEDED(rv
) && hasRows
, 0);
813 return (PRUint32
) statement
->AsInt32(0);
817 nsOfflineCacheDevice::UpdateEntry(nsCacheEntry
*entry
)
819 // Decompose the key into "ClientID" and "Key"
820 nsCAutoString keyBuf
;
821 const char *cid
, *key
;
822 if (!DecomposeCacheEntryKey(entry
->Key(), &cid
, &key
, keyBuf
))
823 return NS_ERROR_UNEXPECTED
;
825 nsCString metaDataBuf
;
826 PRUint32 mdSize
= entry
->MetaDataSize();
827 if (!EnsureStringLength(metaDataBuf
, mdSize
))
828 return NS_ERROR_OUT_OF_MEMORY
;
829 char *md
= metaDataBuf
.BeginWriting();
830 entry
->FlattenMetaData(md
, mdSize
);
832 nsOfflineCacheRecord rec
;
833 rec
.metaData
= (const PRUint8
*) md
;
834 rec
.metaDataLen
= mdSize
;
835 rec
.flags
= 0; // mark entry as inactive
836 rec
.dataSize
= entry
->DataSize();
837 rec
.fetchCount
= entry
->FetchCount();
838 rec
.lastFetched
= PRTimeFromSeconds(entry
->LastFetched());
839 rec
.lastModified
= PRTimeFromSeconds(entry
->LastModified());
840 rec
.expirationTime
= PRTimeFromSeconds(entry
->ExpirationTime());
842 AutoResetStatement
statement(mStatement_UpdateEntry
);
845 rv
= statement
->BindBlobParameter(0, rec
.metaData
, rec
.metaDataLen
);
846 rv
|= statement
->BindInt32Parameter(1, rec
.flags
);
847 rv
|= statement
->BindInt32Parameter(2, rec
.dataSize
);
848 rv
|= statement
->BindInt32Parameter(3, rec
.fetchCount
);
849 rv
|= statement
->BindInt64Parameter(4, rec
.lastFetched
);
850 rv
|= statement
->BindInt64Parameter(5, rec
.lastModified
);
851 rv
|= statement
->BindInt64Parameter(6, rec
.expirationTime
);
852 rv
|= statement
->BindUTF8StringParameter(7, nsDependentCString(cid
));
853 rv
|= statement
->BindUTF8StringParameter(8, nsDependentCString(key
));
854 NS_ENSURE_SUCCESS(rv
, rv
);
857 rv
= statement
->ExecuteStep(&hasRows
);
858 NS_ENSURE_SUCCESS(rv
, rv
);
860 NS_ASSERTION(!hasRows
, "UPDATE should not result in output");
865 nsOfflineCacheDevice::UpdateEntrySize(nsCacheEntry
*entry
, PRUint32 newSize
)
867 // Decompose the key into "ClientID" and "Key"
868 nsCAutoString keyBuf
;
869 const char *cid
, *key
;
870 if (!DecomposeCacheEntryKey(entry
->Key(), &cid
, &key
, keyBuf
))
871 return NS_ERROR_UNEXPECTED
;
873 AutoResetStatement
statement(mStatement_UpdateEntrySize
);
876 rv
= statement
->BindInt32Parameter(0, newSize
);
877 rv
|= statement
->BindUTF8StringParameter(1, nsDependentCString(cid
));
878 rv
|= statement
->BindUTF8StringParameter(2, nsDependentCString(key
));
879 NS_ENSURE_SUCCESS(rv
, rv
);
882 rv
= statement
->ExecuteStep(&hasRows
);
883 NS_ENSURE_SUCCESS(rv
, rv
);
885 NS_ASSERTION(!hasRows
, "UPDATE should not result in output");
890 nsOfflineCacheDevice::DeleteEntry(nsCacheEntry
*entry
, PRBool deleteData
)
894 nsresult rv
= DeleteData(entry
);
899 // Decompose the key into "ClientID" and "Key"
900 nsCAutoString keyBuf
;
901 const char *cid
, *key
;
902 if (!DecomposeCacheEntryKey(entry
->Key(), &cid
, &key
, keyBuf
))
903 return NS_ERROR_UNEXPECTED
;
905 AutoResetStatement
statement(mStatement_DeleteEntry
);
908 rv
= statement
->BindUTF8StringParameter(0, nsDependentCString(cid
));
909 rv
|= statement
->BindUTF8StringParameter(1, nsDependentCString(key
));
910 NS_ENSURE_SUCCESS(rv
, rv
);
913 rv
= statement
->ExecuteStep(&hasRows
);
914 NS_ENSURE_SUCCESS(rv
, rv
);
916 NS_ASSERTION(!hasRows
, "DELETE should not result in output");
921 nsOfflineCacheDevice::DeleteData(nsCacheEntry
*entry
)
923 nsOfflineCacheBinding
*binding
= (nsOfflineCacheBinding
*) entry
->Data();
924 NS_ENSURE_STATE(binding
);
926 return binding
->mDataFile
->Remove(PR_FALSE
);
930 * nsCacheDevice implementation
934 nsOfflineCacheDevice
*
935 nsOfflineCacheDevice::GetInstance()
938 nsCOMPtr
<nsICacheService
> serv
= do_GetService(kCacheServiceCID
, &rv
);
939 NS_ENSURE_SUCCESS(rv
, nsnull
);
941 nsICacheService
*iservice
= static_cast<nsICacheService
*>(serv
.get());
942 nsCacheService
*cacheService
= static_cast<nsCacheService
*>(iservice
);
943 rv
= cacheService
->CreateOfflineDevice();
944 NS_ENSURE_SUCCESS(rv
, nsnull
);
946 NS_IF_ADDREF(cacheService
->mOfflineDevice
);
947 return cacheService
->mOfflineDevice
;
951 nsOfflineCacheDevice::Init()
953 NS_ENSURE_TRUE(!mDB
, NS_ERROR_ALREADY_INITIALIZED
);
955 // SetCacheParentDirectory must have been called
956 NS_ENSURE_TRUE(mCacheDirectory
, NS_ERROR_UNEXPECTED
);
958 // make sure the cache directory exists
959 nsresult rv
= EnsureDir(mCacheDirectory
);
960 NS_ENSURE_SUCCESS(rv
, rv
);
962 // build path to index file
963 nsCOMPtr
<nsIFile
> indexFile
;
964 rv
= mCacheDirectory
->Clone(getter_AddRefs(indexFile
));
965 NS_ENSURE_SUCCESS(rv
, rv
);
966 rv
= indexFile
->AppendNative(NS_LITERAL_CSTRING("index.sqlite"));
967 NS_ENSURE_SUCCESS(rv
, rv
);
969 nsCOMPtr
<mozIStorageService
> ss
=
970 do_GetService("@mozilla.org/storage/service;1", &rv
);
971 NS_ENSURE_SUCCESS(rv
, rv
);
973 rv
= ss
->OpenDatabase(indexFile
, getter_AddRefs(mDB
));
974 NS_ENSURE_SUCCESS(rv
, rv
);
976 mDB
->ExecuteSimpleSQL(NS_LITERAL_CSTRING("PRAGMA synchronous = OFF;"));
978 // XXX ... other initialization steps
980 // XXX in the future we may wish to verify the schema for moz_cache
981 // perhaps using "PRAGMA table_info" ?
985 // "Generation" is the data file generation number.
986 // "Flags" is a bit-field indicating the state of the entry.
988 rv
= mDB
->ExecuteSimpleSQL(
989 NS_LITERAL_CSTRING("CREATE TABLE IF NOT EXISTS moz_cache (\n"
993 " Generation INTEGER,\n"
995 " DataSize INTEGER,\n"
996 " FetchCount INTEGER,\n"
997 " LastFetched INTEGER,\n"
998 " LastModified INTEGER,\n"
999 " ExpirationTime INTEGER,\n"
1000 " ItemType INTEGER DEFAULT 0\n"
1002 NS_ENSURE_SUCCESS(rv
, rv
);
1004 // Databases from 1.9.0 don't have the ItemType column. Add the column
1005 // here, but don't worry about failures (the column probably already exists)
1006 mDB
->ExecuteSimpleSQL(
1007 NS_LITERAL_CSTRING("ALTER TABLE moz_cache ADD ItemType INTEGER DEFAULT 0"));
1009 // Create the table for storing cache groups. All actions on
1010 // moz_cache_groups use the GroupID, so use it as the primary key.
1011 rv
= mDB
->ExecuteSimpleSQL(
1012 NS_LITERAL_CSTRING("CREATE TABLE IF NOT EXISTS moz_cache_groups (\n"
1013 " GroupID TEXT PRIMARY KEY,\n"
1014 " ActiveClientID TEXT\n"
1016 NS_ENSURE_SUCCESS(rv
, rv
);
1018 mDB
->ExecuteSimpleSQL(
1019 NS_LITERAL_CSTRING("ALTER TABLE moz_cache_groups "
1020 "ADD ActivateTimeStamp INTEGER DEFAULT 0"));
1022 // ClientID: clientID joining moz_cache and moz_cache_namespaces
1024 // Data: Data associated with this namespace (e.g. a fallback URI
1025 // for fallback entries).
1026 // ItemType: the type of namespace.
1027 rv
= mDB
->ExecuteSimpleSQL(
1028 NS_LITERAL_CSTRING("CREATE TABLE IF NOT EXISTS"
1029 " moz_cache_namespaces (\n"
1031 " NameSpace TEXT,\n"
1033 " ItemType INTEGER\n"
1035 NS_ENSURE_SUCCESS(rv
, rv
);
1037 // Databases from 1.9.0 have a moz_cache_index that should be dropped
1038 rv
= mDB
->ExecuteSimpleSQL(
1039 NS_LITERAL_CSTRING("DROP INDEX IF EXISTS moz_cache_index"));
1040 NS_ENSURE_SUCCESS(rv
, rv
);
1042 // Key/ClientID pairs should be unique in the database. All queries
1043 // against moz_cache use the Key (which is also the most unique), so
1044 // use it as the primary key for this index.
1045 rv
= mDB
->ExecuteSimpleSQL(
1046 NS_LITERAL_CSTRING("CREATE UNIQUE INDEX IF NOT EXISTS "
1047 " moz_cache_key_clientid_index"
1048 " ON moz_cache (Key, ClientID);"));
1049 NS_ENSURE_SUCCESS(rv
, rv
);
1051 // Used for ClientID lookups and to keep ClientID/NameSpace pairs unique.
1052 rv
= mDB
->ExecuteSimpleSQL(
1053 NS_LITERAL_CSTRING("CREATE UNIQUE INDEX IF NOT EXISTS"
1054 " moz_cache_namespaces_clientid_index"
1055 " ON moz_cache_namespaces (ClientID, NameSpace);"));
1056 NS_ENSURE_SUCCESS(rv
, rv
);
1058 // Used for namespace lookups.
1059 rv
= mDB
->ExecuteSimpleSQL(
1060 NS_LITERAL_CSTRING("CREATE INDEX IF NOT EXISTS"
1061 " moz_cache_namespaces_namespace_index"
1062 " ON moz_cache_namespaces (NameSpace);"));
1063 NS_ENSURE_SUCCESS(rv
, rv
);
1066 mEvictionFunction
= new nsOfflineCacheEvictionFunction(this);
1067 if (!mEvictionFunction
) return NS_ERROR_OUT_OF_MEMORY
;
1069 rv
= mDB
->CreateFunction(NS_LITERAL_CSTRING("cache_eviction_observer"), 2, mEvictionFunction
);
1070 NS_ENSURE_SUCCESS(rv
, rv
);
1072 // create all (most) of our statements up front
1073 struct StatementSql
{
1074 nsCOMPtr
<mozIStorageStatement
> &statement
;
1076 StatementSql (nsCOMPtr
<mozIStorageStatement
> &aStatement
, const char *aSql
):
1077 statement (aStatement
), sql (aSql
) {}
1079 StatementSql ( mStatement_CacheSize
, "SELECT Sum(DataSize) from moz_cache;" ),
1080 // XXX bug 442810: Restore the ability to monitor individual cache usage
1081 StatementSql ( mStatement_DomainSize
, "SELECT 0;"),
1082 StatementSql ( mStatement_EntryCount
, "SELECT count(*) from moz_cache;" ),
1083 StatementSql ( mStatement_UpdateEntry
, "UPDATE moz_cache SET MetaData = ?, Flags = ?, DataSize = ?, FetchCount = ?, LastFetched = ?, LastModified = ?, ExpirationTime = ? WHERE ClientID = ? AND Key = ?;" ),
1084 StatementSql ( mStatement_UpdateEntrySize
, "UPDATE moz_cache SET DataSize = ? WHERE ClientID = ? AND Key = ?;" ),
1085 StatementSql ( mStatement_UpdateEntryFlags
, "UPDATE moz_cache SET Flags = ? WHERE ClientID = ? AND Key = ?;" ),
1086 StatementSql ( mStatement_DeleteEntry
, "DELETE FROM moz_cache WHERE ClientID = ? AND Key = ?;" ),
1087 StatementSql ( mStatement_FindEntry
, "SELECT MetaData, Generation, Flags, DataSize, FetchCount, LastFetched, LastModified, ExpirationTime, ItemType FROM moz_cache WHERE ClientID = ? AND Key = ?;" ),
1088 StatementSql ( mStatement_BindEntry
, "INSERT INTO moz_cache (ClientID, Key, MetaData, Generation, Flags, DataSize, FetchCount, LastFetched, LastModified, ExpirationTime) VALUES(?,?,?,?,?,?,?,?,?,?);" ),
1090 StatementSql ( mStatement_MarkEntry
, "UPDATE moz_cache SET ItemType = (ItemType | ?) WHERE ClientID = ? AND Key = ?;" ),
1091 StatementSql ( mStatement_UnmarkEntry
, "UPDATE moz_cache SET ItemType = (ItemType & ~?) WHERE ClientID = ? AND Key = ?;" ),
1092 StatementSql ( mStatement_GetTypes
, "SELECT ItemType FROM moz_cache WHERE ClientID = ? AND Key = ?;"),
1093 StatementSql ( mStatement_CleanupUnmarked
, "DELETE FROM moz_cache WHERE ClientID = ? AND Key = ? AND ItemType = 0;" ),
1094 StatementSql ( mStatement_GatherEntries
, "SELECT Key FROM moz_cache WHERE ClientID = ? AND (ItemType & ?) > 0;" ),
1096 StatementSql ( mStatement_ActivateClient
, "INSERT OR REPLACE INTO moz_cache_groups (GroupID, ActiveClientID, ActivateTimeStamp) VALUES (?, ?, ?);" ),
1097 StatementSql ( mStatement_DeactivateGroup
, "DELETE FROM moz_cache_groups WHERE GroupID = ?;" ),
1098 StatementSql ( mStatement_FindClient
, "SELECT ClientID, ItemType FROM moz_cache WHERE Key = ? ORDER BY LastFetched DESC, LastModified DESC;" ),
1100 // Search for namespaces that match the URI. Use the <= operator
1101 // to ensure that we use the index on moz_cache_namespaces.
1102 StatementSql ( mStatement_FindClientByNamespace
, "SELECT ns.ClientID, ns.ItemType FROM"
1103 " moz_cache_namespaces AS ns JOIN moz_cache_groups AS groups"
1104 " ON ns.ClientID = groups.ActiveClientID"
1105 " WHERE ns.NameSpace <= ?1 AND ?1 GLOB ns.NameSpace || '*'"
1106 " ORDER BY ns.NameSpace DESC, groups.ActivateTimeStamp DESC;"),
1107 StatementSql ( mStatement_FindNamespaceEntry
, "SELECT ns.NameSpace, ns.Data, ns.ItemType FROM "
1108 " moz_cache_namespaces AS ns JOIN moz_cache_groups AS groups"
1109 " ON ns.ClientID = groups.ActiveClientID"
1110 " WHERE ClientID = ?1"
1111 " AND NameSpace <= ?2 AND ?2 GLOB NameSpace || '*'"
1112 " ORDER BY ns.NameSpace DESC, groups.ActivateTimeStamp DESC;"),
1113 StatementSql ( mStatement_InsertNamespaceEntry
, "INSERT INTO moz_cache_namespaces (ClientID, NameSpace, Data, ItemType) VALUES(?, ?, ?, ?);"),
1115 for (PRUint32 i
= 0; NS_SUCCEEDED(rv
) && i
< NS_ARRAY_LENGTH(prepared
); ++i
)
1117 LOG(("Creating statement: %s\n", prepared
[i
].sql
));
1119 rv
= mDB
->CreateStatement(nsDependentCString(prepared
[i
].sql
),
1120 getter_AddRefs(prepared
[i
].statement
));
1121 NS_ENSURE_SUCCESS(rv
, rv
);
1124 // Clear up any dangling active flags
1125 rv
= mDB
->ExecuteSimpleSQL(
1126 NS_LITERAL_CSTRING("UPDATE moz_cache"
1127 " SET Flags=(Flags & ~1)"
1128 " WHERE (Flags & 1);"));
1129 NS_ENSURE_SUCCESS(rv
, rv
);
1131 rv
= InitActiveCaches();
1132 NS_ENSURE_SUCCESS(rv
, rv
);
1138 nsOfflineCacheDevice::InitActiveCaches()
1140 NS_ENSURE_TRUE(mCaches
.Init(), NS_ERROR_OUT_OF_MEMORY
);
1141 NS_ENSURE_TRUE(mActiveCachesByGroup
.Init(), NS_ERROR_OUT_OF_MEMORY
);
1143 nsresult rv
= mActiveCaches
.Init(5);
1144 NS_ENSURE_SUCCESS(rv
, rv
);
1146 nsCOMPtr
<mozIStorageStatement
> statement
;
1148 mDB
->CreateStatement(NS_LITERAL_CSTRING("SELECT GroupID, ActiveClientID"
1149 " FROM moz_cache_groups"),
1150 getter_AddRefs(statement
));
1151 NS_ENSURE_SUCCESS(rv
, rv
);
1154 rv
= statement
->ExecuteStep(&hasRows
);
1155 NS_ENSURE_SUCCESS(rv
, rv
);
1159 nsCAutoString group
;
1160 statement
->GetUTF8String(0, group
);
1162 statement
->GetUTF8String(1, clientID
);
1164 mActiveCaches
.Put(clientID
);
1165 mActiveCachesByGroup
.Put(group
, new nsCString(clientID
));
1167 rv
= statement
->ExecuteStep(&hasRows
);
1168 NS_ENSURE_SUCCESS(rv
, rv
);
1176 nsOfflineCacheDevice::ShutdownApplicationCache(const nsACString
&key
,
1177 nsIWeakReference
*weakRef
,
1180 nsCOMPtr
<nsIApplicationCache
> obj
= do_QueryReferent(weakRef
);
1183 nsApplicationCache
*appCache
= static_cast<nsApplicationCache
*>(obj
.get());
1184 appCache
->MarkInvalid();
1187 return PL_DHASH_NEXT
;
1191 nsOfflineCacheDevice::Shutdown()
1193 NS_ENSURE_TRUE(mDB
, NS_ERROR_NOT_INITIALIZED
);
1195 if (mCaches
.IsInitialized())
1196 mCaches
.EnumerateRead(ShutdownApplicationCache
, this);
1198 EvictionObserver
evictionObserver(mDB
, mEvictionFunction
);
1200 // Delete all rows whose clientID is not an active clientID.
1201 nsresult rv
= mDB
->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1202 "DELETE FROM moz_cache WHERE rowid IN"
1203 " (SELECT moz_cache.rowid FROM"
1204 " moz_cache LEFT OUTER JOIN moz_cache_groups ON"
1205 " (moz_cache.ClientID = moz_cache_groups.ActiveClientID)"
1206 " WHERE moz_cache_groups.GroupID ISNULL)"));
1209 NS_WARNING("Failed to clean up unused application caches.");
1211 evictionObserver
.Apply();
1213 // Delete all namespaces whose clientID is not an active clientID.
1214 rv
= mDB
->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1215 "DELETE FROM moz_cache_namespaces WHERE rowid IN"
1216 " (SELECT moz_cache_namespaces.rowid FROM"
1217 " moz_cache_namespaces LEFT OUTER JOIN moz_cache_groups ON"
1218 " (moz_cache_namespaces.ClientID = moz_cache_groups.ActiveClientID)"
1219 " WHERE moz_cache_groups.GroupID ISNULL)"));
1222 NS_WARNING("Failed to clean up namespaces.");
1225 mEvictionFunction
= 0;
1231 nsOfflineCacheDevice::GetDeviceID()
1233 return OFFLINE_CACHE_DEVICE_ID
;
1237 nsOfflineCacheDevice::FindEntry(nsCString
*fullKey
, PRBool
*collision
)
1239 LOG(("nsOfflineCacheDevice::FindEntry [key=%s]\n", fullKey
->get()));
1241 // SELECT * FROM moz_cache WHERE key = ?
1243 // Decompose the key into "ClientID" and "Key"
1244 nsCAutoString keyBuf
;
1245 const char *cid
, *key
;
1246 if (!DecomposeCacheEntryKey(fullKey
, &cid
, &key
, keyBuf
))
1249 AutoResetStatement
statement(mStatement_FindEntry
);
1252 rv
= statement
->BindUTF8StringParameter(0, nsDependentCString(cid
));
1253 rv
|= statement
->BindUTF8StringParameter(1, nsDependentCString(key
));
1254 NS_ENSURE_SUCCESS(rv
, nsnull
);
1257 rv
= statement
->ExecuteStep(&hasRows
);
1258 if (NS_FAILED(rv
) || !hasRows
)
1259 return nsnull
; // entry not found
1261 nsOfflineCacheRecord rec
;
1262 statement
->GetSharedBlob(0, &rec
.metaDataLen
,
1263 (const PRUint8
**) &rec
.metaData
);
1264 rec
.generation
= statement
->AsInt32(1);
1265 rec
.flags
= statement
->AsInt32(2);
1266 rec
.dataSize
= statement
->AsInt32(3);
1267 rec
.fetchCount
= statement
->AsInt32(4);
1268 rec
.lastFetched
= statement
->AsInt64(5);
1269 rec
.lastModified
= statement
->AsInt64(6);
1270 rec
.expirationTime
= statement
->AsInt64(7);
1272 LOG(("entry: [%u %d %d %d %d %lld %lld %lld]\n",
1280 rec
.expirationTime
));
1282 nsCacheEntry
*entry
= CreateCacheEntry(this, fullKey
, rec
);
1286 // make sure that the data file exists
1287 nsOfflineCacheBinding
*binding
= (nsOfflineCacheBinding
*)entry
->Data();
1289 rv
= binding
->mDataFile
->IsFile(&isFile
);
1290 if (NS_FAILED(rv
) || !isFile
)
1292 DeleteEntry(entry
, PR_FALSE
);
1300 AutoResetStatement
updateStatement(mStatement_UpdateEntryFlags
);
1302 rv
|= updateStatement
->BindInt32Parameter(0, rec
.flags
);
1303 rv
|= updateStatement
->BindUTF8StringParameter(1, nsDependentCString(cid
));
1304 rv
|= updateStatement
->BindUTF8StringParameter(2, nsDependentCString(key
));
1311 rv
= updateStatement
->ExecuteStep(&hasRows
);
1318 NS_ASSERTION(!hasRows
, "UPDATE should not result in output");
1325 nsOfflineCacheDevice::DeactivateEntry(nsCacheEntry
*entry
)
1327 LOG(("nsOfflineCacheDevice::DeactivateEntry [key=%s]\n",
1328 entry
->Key()->get()));
1330 // This method is called to inform us that the nsCacheEntry object is going
1331 // away. We should persist anything that needs to be persisted, or if the
1332 // entry is doomed, we can go ahead and clear its storage.
1334 if (entry
->IsDoomed())
1336 // remove corresponding row and file if they exist
1338 // the row should have been removed in DoomEntry... we could assert that
1339 // that happened. otherwise, all we have to do here is delete the file
1345 // UPDATE the database row
1347 // XXX Assumption: the row already exists because it was either created
1348 // with a call to BindEntry or it was there when we called FindEntry.
1359 nsOfflineCacheDevice::BindEntry(nsCacheEntry
*entry
)
1361 LOG(("nsOfflineCacheDevice::BindEntry [key=%s]\n", entry
->Key()->get()));
1363 NS_ENSURE_STATE(!entry
->Data());
1365 // This method is called to inform us that we have a new entry. The entry
1366 // may collide with an existing entry in our DB, but if that happens we can
1367 // assume that the entry is not being used.
1369 // INSERT the database row
1371 // XXX Assumption: if the row already exists, then FindEntry would have
1372 // returned it. if that entry was doomed, then DoomEntry would have removed
1373 // it from the table. so, we should always have to insert at this point.
1375 // Decompose the key into "ClientID" and "Key"
1376 nsCAutoString keyBuf
;
1377 const char *cid
, *key
;
1378 if (!DecomposeCacheEntryKey(entry
->Key(), &cid
, &key
, keyBuf
))
1379 return NS_ERROR_UNEXPECTED
;
1381 // create binding, pick best generation number
1382 nsRefPtr
<nsOfflineCacheBinding
> binding
=
1383 nsOfflineCacheBinding::Create(mCacheDirectory
, entry
->Key(), -1);
1385 return NS_ERROR_OUT_OF_MEMORY
;
1387 nsOfflineCacheRecord rec
;
1390 rec
.metaData
= NULL
; // don't write any metadata now.
1391 rec
.metaDataLen
= 0;
1392 rec
.generation
= binding
->mGeneration
;
1393 rec
.flags
= 0x1; // mark entry as active, we'll reset this in DeactivateEntry
1395 rec
.fetchCount
= entry
->FetchCount();
1396 rec
.lastFetched
= PRTimeFromSeconds(entry
->LastFetched());
1397 rec
.lastModified
= PRTimeFromSeconds(entry
->LastModified());
1398 rec
.expirationTime
= PRTimeFromSeconds(entry
->ExpirationTime());
1400 AutoResetStatement
statement(mStatement_BindEntry
);
1403 rv
= statement
->BindUTF8StringParameter(0, nsDependentCString(rec
.clientID
));
1404 rv
|= statement
->BindUTF8StringParameter(1, nsDependentCString(rec
.key
));
1405 rv
|= statement
->BindBlobParameter(2, rec
.metaData
, rec
.metaDataLen
);
1406 rv
|= statement
->BindInt32Parameter(3, rec
.generation
);
1407 rv
|= statement
->BindInt32Parameter(4, rec
.flags
);
1408 rv
|= statement
->BindInt32Parameter(5, rec
.dataSize
);
1409 rv
|= statement
->BindInt32Parameter(6, rec
.fetchCount
);
1410 rv
|= statement
->BindInt64Parameter(7, rec
.lastFetched
);
1411 rv
|= statement
->BindInt64Parameter(8, rec
.lastModified
);
1412 rv
|= statement
->BindInt64Parameter(9, rec
.expirationTime
);
1413 NS_ENSURE_SUCCESS(rv
, rv
);
1416 rv
= statement
->ExecuteStep(&hasRows
);
1417 NS_ENSURE_SUCCESS(rv
, rv
);
1418 NS_ASSERTION(!hasRows
, "INSERT should not result in output");
1420 entry
->SetData(binding
);
1425 nsOfflineCacheDevice::DoomEntry(nsCacheEntry
*entry
)
1427 LOG(("nsOfflineCacheDevice::DoomEntry [key=%s]\n", entry
->Key()->get()));
1429 // This method is called to inform us that we should mark the entry to be
1430 // deleted when it is no longer in use.
1432 // We can go ahead and delete the corresponding row in our table,
1433 // but we must not delete the file on disk until we are deactivated.
1435 DeleteEntry(entry
, PR_FALSE
);
1439 nsOfflineCacheDevice::OpenInputStreamForEntry(nsCacheEntry
*entry
,
1440 nsCacheAccessMode mode
,
1442 nsIInputStream
**result
)
1444 LOG(("nsOfflineCacheDevice::OpenInputStreamForEntry [key=%s]\n",
1445 entry
->Key()->get()));
1449 NS_ENSURE_TRUE(offset
< entry
->DataSize(), NS_ERROR_INVALID_ARG
);
1451 // return an input stream to the entry's data file. the stream
1452 // may be read on a background thread.
1454 nsOfflineCacheBinding
*binding
= (nsOfflineCacheBinding
*) entry
->Data();
1455 NS_ENSURE_STATE(binding
);
1457 nsCOMPtr
<nsIInputStream
> in
;
1458 NS_NewLocalFileInputStream(getter_AddRefs(in
), binding
->mDataFile
, PR_RDONLY
);
1460 return NS_ERROR_UNEXPECTED
;
1462 // respect |offset| param
1465 nsCOMPtr
<nsISeekableStream
> seekable
= do_QueryInterface(in
);
1466 NS_ENSURE_TRUE(seekable
, NS_ERROR_UNEXPECTED
);
1468 seekable
->Seek(nsISeekableStream::NS_SEEK_SET
, offset
);
1476 nsOfflineCacheDevice::OpenOutputStreamForEntry(nsCacheEntry
*entry
,
1477 nsCacheAccessMode mode
,
1479 nsIOutputStream
**result
)
1481 LOG(("nsOfflineCacheDevice::OpenOutputStreamForEntry [key=%s]\n",
1482 entry
->Key()->get()));
1486 NS_ENSURE_TRUE(offset
<= entry
->DataSize(), NS_ERROR_INVALID_ARG
);
1488 // return an output stream to the entry's data file. we can assume
1489 // that the output stream will only be used on the main thread.
1491 nsOfflineCacheBinding
*binding
= (nsOfflineCacheBinding
*) entry
->Data();
1492 NS_ENSURE_STATE(binding
);
1494 nsCOMPtr
<nsIOutputStream
> out
;
1495 NS_NewLocalFileOutputStream(getter_AddRefs(out
), binding
->mDataFile
,
1496 PR_WRONLY
| PR_CREATE_FILE
| PR_TRUNCATE
,
1499 return NS_ERROR_UNEXPECTED
;
1501 // respect |offset| param
1502 nsCOMPtr
<nsISeekableStream
> seekable
= do_QueryInterface(out
);
1503 NS_ENSURE_TRUE(seekable
, NS_ERROR_UNEXPECTED
);
1505 seekable
->Seek(nsISeekableStream::NS_SEEK_SET
, offset
);
1507 // truncate the file at the given offset
1510 nsCOMPtr
<nsIOutputStream
> bufferedOut
;
1511 NS_NewBufferedOutputStream(getter_AddRefs(bufferedOut
), out
, 16 * 1024);
1513 return NS_ERROR_UNEXPECTED
;
1515 bufferedOut
.swap(*result
);
1520 nsOfflineCacheDevice::GetFileForEntry(nsCacheEntry
*entry
, nsIFile
**result
)
1522 LOG(("nsOfflineCacheDevice::GetFileForEntry [key=%s]\n",
1523 entry
->Key()->get()));
1525 nsOfflineCacheBinding
*binding
= (nsOfflineCacheBinding
*) entry
->Data();
1526 NS_ENSURE_STATE(binding
);
1528 NS_IF_ADDREF(*result
= binding
->mDataFile
);
1533 nsOfflineCacheDevice::OnDataSizeChange(nsCacheEntry
*entry
, PRInt32 deltaSize
)
1535 LOG(("nsOfflineCacheDevice::OnDataSizeChange [key=%s delta=%d]\n",
1536 entry
->Key()->get(), deltaSize
));
1538 const PRInt32 DELTA_THRESHOLD
= 1<<14; // 16k
1540 // called to notify us of an impending change in the total size of the
1543 PRUint32 oldSize
= entry
->DataSize();
1544 NS_ASSERTION(deltaSize
>= 0 || PRInt32(oldSize
) + deltaSize
>= 0, "oops");
1545 PRUint32 newSize
= PRInt32(oldSize
) + deltaSize
;
1546 UpdateEntrySize(entry
, newSize
);
1548 mDeltaCounter
+= deltaSize
; // this may go negative
1550 if (mDeltaCounter
>= DELTA_THRESHOLD
)
1552 if (CacheSize() > mCacheCapacity
) {
1553 // the entry will overrun the cache capacity, doom the entry
1558 nsCacheService::DoomEntry(entry
);
1559 NS_ASSERTION(NS_SUCCEEDED(rv
), "DoomEntry() failed.");
1560 return NS_ERROR_ABORT
;
1563 mDeltaCounter
= 0; // reset counter
1570 nsOfflineCacheDevice::Visit(nsICacheVisitor
*visitor
)
1572 NS_ENSURE_TRUE(Initialized(), NS_ERROR_NOT_INITIALIZED
);
1574 // called to enumerate the offline cache.
1576 nsCOMPtr
<nsICacheDeviceInfo
> deviceInfo
=
1577 new nsOfflineCacheDeviceInfo(this);
1580 nsresult rv
= visitor
->VisitDevice(OFFLINE_CACHE_DEVICE_ID
, deviceInfo
,
1588 // SELECT * from moz_cache;
1590 nsOfflineCacheRecord rec
;
1591 nsRefPtr
<nsOfflineCacheEntryInfo
> info
= new nsOfflineCacheEntryInfo
;
1593 return NS_ERROR_OUT_OF_MEMORY
;
1596 // XXX may want to list columns explicitly
1597 nsCOMPtr
<mozIStorageStatement
> statement
;
1598 rv
= mDB
->CreateStatement(
1599 NS_LITERAL_CSTRING("SELECT * FROM moz_cache;"),
1600 getter_AddRefs(statement
));
1601 NS_ENSURE_SUCCESS(rv
, rv
);
1606 rv
= statement
->ExecuteStep(&hasRows
);
1607 if (NS_FAILED(rv
) || !hasRows
)
1610 statement
->GetSharedUTF8String(0, NULL
, &rec
.clientID
);
1611 statement
->GetSharedUTF8String(1, NULL
, &rec
.key
);
1612 statement
->GetSharedBlob(2, &rec
.metaDataLen
,
1613 (const PRUint8
**) &rec
.metaData
);
1614 rec
.generation
= statement
->AsInt32(3);
1615 rec
.flags
= statement
->AsInt32(4);
1616 rec
.dataSize
= statement
->AsInt32(5);
1617 rec
.fetchCount
= statement
->AsInt32(6);
1618 rec
.lastFetched
= statement
->AsInt64(7);
1619 rec
.lastModified
= statement
->AsInt64(8);
1620 rec
.expirationTime
= statement
->AsInt64(9);
1623 rv
= visitor
->VisitEntry(OFFLINE_CACHE_DEVICE_ID
, info
, &keepGoing
);
1624 if (NS_FAILED(rv
) || !keepGoing
)
1628 info
->mRec
= nsnull
;
1633 nsOfflineCacheDevice::EvictEntries(const char *clientID
)
1635 LOG(("nsOfflineCacheDevice::EvictEntries [cid=%s]\n",
1636 clientID
? clientID
: ""));
1638 // called to evict all entries matching the given clientID.
1640 // need trigger to fire user defined function after a row is deleted
1641 // so we can delete the corresponding data file.
1642 EvictionObserver
evictionObserver(mDB
, mEvictionFunction
);
1644 nsCOMPtr
<mozIStorageStatement
> statement
;
1648 rv
= mDB
->CreateStatement(NS_LITERAL_CSTRING("DELETE FROM moz_cache WHERE ClientID=? AND Flags = 0;"),
1649 getter_AddRefs(statement
));
1650 NS_ENSURE_SUCCESS(rv
, rv
);
1652 rv
= statement
->BindUTF8StringParameter(0, nsDependentCString(clientID
));
1653 NS_ENSURE_SUCCESS(rv
, rv
);
1657 rv
= mDB
->CreateStatement(NS_LITERAL_CSTRING("DELETE FROM moz_cache WHERE Flags = 0;"),
1658 getter_AddRefs(statement
));
1659 NS_ENSURE_SUCCESS(rv
, rv
);
1662 rv
= statement
->Execute();
1663 NS_ENSURE_SUCCESS(rv
, rv
);
1665 evictionObserver
.Apply();
1668 // Also evict any namespaces associated with this clientID.
1671 rv
= mDB
->CreateStatement(NS_LITERAL_CSTRING("DELETE FROM moz_cache_namespaces WHERE ClientID=?"),
1672 getter_AddRefs(statement
));
1673 NS_ENSURE_SUCCESS(rv
, rv
);
1675 rv
= statement
->BindUTF8StringParameter(0, nsDependentCString(clientID
));
1676 NS_ENSURE_SUCCESS(rv
, rv
);
1680 rv
= mDB
->CreateStatement(NS_LITERAL_CSTRING("DELETE FROM moz_cache_namespaces;"),
1681 getter_AddRefs(statement
));
1682 NS_ENSURE_SUCCESS(rv
, rv
);
1685 rv
= statement
->Execute();
1686 NS_ENSURE_SUCCESS(rv
, rv
);
1692 nsOfflineCacheDevice::MarkEntry(const nsCString
&clientID
,
1693 const nsACString
&key
,
1696 LOG(("nsOfflineCacheDevice::MarkEntry [cid=%s, key=%s, typeBits=%d]\n",
1697 clientID
.get(), PromiseFlatCString(key
).get(), typeBits
));
1699 AutoResetStatement
statement(mStatement_MarkEntry
);
1700 nsresult rv
= statement
->BindInt32Parameter(0, typeBits
);
1701 NS_ENSURE_SUCCESS(rv
, rv
);
1702 rv
= statement
->BindUTF8StringParameter(1, clientID
);
1703 NS_ENSURE_SUCCESS(rv
, rv
);
1704 rv
= statement
->BindUTF8StringParameter(2, key
);
1705 NS_ENSURE_SUCCESS(rv
, rv
);
1707 rv
= statement
->Execute();
1708 NS_ENSURE_SUCCESS(rv
, rv
);
1714 nsOfflineCacheDevice::UnmarkEntry(const nsCString
&clientID
,
1715 const nsACString
&key
,
1718 LOG(("nsOfflineCacheDevice::UnmarkEntry [cid=%s, key=%s, typeBits=%d]\n",
1719 clientID
.get(), PromiseFlatCString(key
).get(), typeBits
));
1721 AutoResetStatement
statement(mStatement_UnmarkEntry
);
1722 nsresult rv
= statement
->BindInt32Parameter(0, typeBits
);
1723 NS_ENSURE_SUCCESS(rv
, rv
);
1724 rv
= statement
->BindUTF8StringParameter(1, clientID
);
1725 NS_ENSURE_SUCCESS(rv
, rv
);
1726 rv
= statement
->BindUTF8StringParameter(2, key
);
1727 NS_ENSURE_SUCCESS(rv
, rv
);
1729 rv
= statement
->Execute();
1730 NS_ENSURE_SUCCESS(rv
, rv
);
1732 // Remove the entry if it is now empty.
1734 EvictionObserver
evictionObserver(mDB
, mEvictionFunction
);
1736 AutoResetStatement
cleanupStatement(mStatement_CleanupUnmarked
);
1737 rv
= cleanupStatement
->BindUTF8StringParameter(0, clientID
);
1738 NS_ENSURE_SUCCESS(rv
, rv
);
1739 rv
= cleanupStatement
->BindUTF8StringParameter(1, key
);
1740 NS_ENSURE_SUCCESS(rv
, rv
);
1742 rv
= cleanupStatement
->Execute();
1743 NS_ENSURE_SUCCESS(rv
, rv
);
1745 evictionObserver
.Apply();
1751 nsOfflineCacheDevice::GetMatchingNamespace(const nsCString
&clientID
,
1752 const nsACString
&key
,
1753 nsIApplicationCacheNamespace
**out
)
1755 LOG(("nsOfflineCacheDevice::GetMatchingNamespace [cid=%s, key=%s]\n",
1756 clientID
.get(), PromiseFlatCString(key
).get()));
1760 AutoResetStatement
statement(mStatement_FindNamespaceEntry
);
1762 rv
= statement
->BindUTF8StringParameter(0, clientID
);
1763 NS_ENSURE_SUCCESS(rv
, rv
);
1764 rv
= statement
->BindUTF8StringParameter(1, key
);
1765 NS_ENSURE_SUCCESS(rv
, rv
);
1768 rv
= statement
->ExecuteStep(&hasRows
);
1769 NS_ENSURE_SUCCESS(rv
, rv
);
1775 nsCString namespaceSpec
;
1776 rv
= statement
->GetUTF8String(0, namespaceSpec
);
1777 NS_ENSURE_SUCCESS(rv
, rv
);
1780 rv
= statement
->GetUTF8String(1, data
);
1781 NS_ENSURE_SUCCESS(rv
, rv
);
1784 rv
= statement
->GetInt32(2, &itemType
);
1785 NS_ENSURE_SUCCESS(rv
, rv
);
1787 // XXX: There is currently a proposal on the table to extend matching
1788 // semantics for bypass entries to be a namespace (possibly with extra
1789 // filter data). When/if that happens, this logic will need to change.
1790 if ((itemType
& nsIApplicationCacheNamespace::NAMESPACE_BYPASS
) &&
1791 (namespaceSpec
!= key
)) {
1792 rv
= statement
->ExecuteStep(&hasRows
);
1793 NS_ENSURE_SUCCESS(rv
, rv
);
1798 nsCOMPtr
<nsIApplicationCacheNamespace
> ns
=
1799 new nsApplicationCacheNamespace();
1801 return NS_ERROR_OUT_OF_MEMORY
;
1803 rv
= ns
->Init(itemType
, namespaceSpec
, data
);
1804 NS_ENSURE_SUCCESS(rv
, rv
);
1814 nsOfflineCacheDevice::CacheOpportunistically(const nsCString
&clientID
,
1815 const nsACString
&key
)
1817 // XXX: We should also be propagating this cache entry to other matching
1818 // caches. See bug 444807.
1820 return MarkEntry(clientID
, key
, nsIApplicationCache::ITEM_OPPORTUNISTIC
);
1824 nsOfflineCacheDevice::GetTypes(const nsCString
&clientID
,
1825 const nsACString
&key
,
1828 LOG(("nsOfflineCacheDevice::GetTypes [cid=%s, key=%s]\n",
1829 clientID
.get(), PromiseFlatCString(key
).get()));
1831 AutoResetStatement
statement(mStatement_GetTypes
);
1832 nsresult rv
= statement
->BindUTF8StringParameter(0, clientID
);
1833 NS_ENSURE_SUCCESS(rv
, rv
);
1834 rv
= statement
->BindUTF8StringParameter(1, key
);
1835 NS_ENSURE_SUCCESS(rv
, rv
);
1838 rv
= statement
->ExecuteStep(&hasRows
);
1839 NS_ENSURE_SUCCESS(rv
, rv
);
1842 return NS_ERROR_CACHE_KEY_NOT_FOUND
;
1844 *typeBits
= statement
->AsInt32(0);
1850 nsOfflineCacheDevice::GatherEntries(const nsCString
&clientID
,
1855 LOG(("nsOfflineCacheDevice::GatherEntries [cid=%s, typeBits=%X]\n",
1856 clientID
.get(), typeBits
));
1858 AutoResetStatement
statement(mStatement_GatherEntries
);
1859 nsresult rv
= statement
->BindUTF8StringParameter(0, clientID
);
1860 NS_ENSURE_SUCCESS(rv
, rv
);
1862 rv
= statement
->BindInt32Parameter(1, typeBits
);
1863 NS_ENSURE_SUCCESS(rv
, rv
);
1865 return RunSimpleQuery(mStatement_GatherEntries
, 0, count
, keys
);
1869 nsOfflineCacheDevice::AddNamespace(const nsCString
&clientID
,
1870 nsIApplicationCacheNamespace
*ns
)
1872 nsCString namespaceSpec
;
1873 nsresult rv
= ns
->GetNamespaceSpec(namespaceSpec
);
1874 NS_ENSURE_SUCCESS(rv
, rv
);
1877 rv
= ns
->GetData(data
);
1878 NS_ENSURE_SUCCESS(rv
, rv
);
1881 rv
= ns
->GetItemType(&itemType
);
1882 NS_ENSURE_SUCCESS(rv
, rv
);
1884 LOG(("nsOfflineCacheDevice::AddNamespace [cid=%s, ns=%s, type=%d]",
1885 PromiseFlatCString(clientID
).get(),
1886 namespaceSpec
.get(), data
.get(), itemType
));
1888 AutoResetStatement
statement(mStatement_InsertNamespaceEntry
);
1890 rv
= statement
->BindUTF8StringParameter(0, clientID
);
1891 NS_ENSURE_SUCCESS(rv
, rv
);
1893 rv
= statement
->BindUTF8StringParameter(1, namespaceSpec
);
1894 NS_ENSURE_SUCCESS(rv
, rv
);
1896 rv
= statement
->BindUTF8StringParameter(2, data
);
1897 NS_ENSURE_SUCCESS(rv
, rv
);
1899 rv
= statement
->BindInt32Parameter(3, itemType
);
1900 NS_ENSURE_SUCCESS(rv
, rv
);
1902 rv
= statement
->Execute();
1903 NS_ENSURE_SUCCESS(rv
, rv
);
1909 nsOfflineCacheDevice::RunSimpleQuery(mozIStorageStatement
* statement
,
1910 PRUint32 resultIndex
,
1915 nsresult rv
= statement
->ExecuteStep(&hasRows
);
1916 NS_ENSURE_SUCCESS(rv
, rv
);
1918 nsTArray
<nsCString
> valArray
;
1922 valArray
.AppendElement(
1923 nsDependentCString(statement
->AsSharedUTF8String(resultIndex
, &length
)));
1925 rv
= statement
->ExecuteStep(&hasRows
);
1926 NS_ENSURE_SUCCESS(rv
, rv
);
1929 *count
= valArray
.Length();
1930 char **ret
= static_cast<char **>(NS_Alloc(*count
* sizeof(char*)));
1931 if (!ret
) return NS_ERROR_OUT_OF_MEMORY
;
1933 for (PRUint32 i
= 0; i
< *count
; i
++) {
1934 ret
[i
] = NS_strdup(valArray
[i
].get());
1936 NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(i
, ret
);
1937 return NS_ERROR_OUT_OF_MEMORY
;
1947 nsOfflineCacheDevice::CreateApplicationCache(const nsACString
&group
,
1948 nsIApplicationCache
**out
)
1953 // Some characters are special in the clientID. Escape the groupID
1954 // before putting it in to the client key.
1955 if (!NS_Escape(nsCString(group
), clientID
, url_Path
)) {
1956 return NS_ERROR_OUT_OF_MEMORY
;
1959 PRTime now
= PR_Now();
1961 // Include the timestamp to guarantee uniqueness across runs, and
1962 // the gNextTemporaryClientID for uniqueness within a second.
1963 clientID
.Append(nsPrintfCString(64, "|%016lld|%d",
1964 now
/ PR_USEC_PER_SEC
,
1965 gNextTemporaryClientID
++));
1967 nsCOMPtr
<nsIApplicationCache
> cache
= new nsApplicationCache(this,
1971 return NS_ERROR_OUT_OF_MEMORY
;
1973 nsCOMPtr
<nsIWeakReference
> weak
= do_GetWeakReference(cache
);
1975 return NS_ERROR_OUT_OF_MEMORY
;
1977 mCaches
.Put(clientID
, weak
);
1985 nsOfflineCacheDevice::GetApplicationCache(const nsACString
&clientID
,
1986 nsIApplicationCache
**out
)
1990 nsCOMPtr
<nsIApplicationCache
> cache
;
1993 if (mCaches
.Get(clientID
, getter_AddRefs(weak
)))
1994 cache
= do_QueryReferent(weak
);
1999 nsresult rv
= GetGroupForCache(clientID
, group
);
2000 NS_ENSURE_SUCCESS(rv
, rv
);
2002 if (group
.IsEmpty()) {
2006 cache
= new nsApplicationCache(this, group
, clientID
);
2007 weak
= do_GetWeakReference(cache
);
2009 return NS_ERROR_OUT_OF_MEMORY
;
2011 mCaches
.Put(clientID
, weak
);
2020 nsOfflineCacheDevice::GetActiveCache(const nsACString
&group
,
2021 nsIApplicationCache
**out
)
2025 nsCString
*clientID
;
2026 if (mActiveCachesByGroup
.Get(group
, &clientID
))
2027 return GetApplicationCache(*clientID
, out
);
2033 nsOfflineCacheDevice::CanUseCache(nsIURI
*keyURI
, const nsCString
&clientID
)
2035 if (mActiveCaches
.Contains(clientID
)) {
2036 nsCAutoString groupID
;
2037 nsresult rv
= GetGroupForCache(clientID
, groupID
);
2038 NS_ENSURE_SUCCESS(rv
, PR_FALSE
);
2040 nsCOMPtr
<nsIURI
> groupURI
;
2041 rv
= NS_NewURI(getter_AddRefs(groupURI
), groupID
);
2042 if (NS_SUCCEEDED(rv
)) {
2043 // When we are choosing an initial cache to load the top
2044 // level document from, the URL of that document must have
2045 // the same origin as the manifest, according to the spec.
2046 // The following check is here because explicit, fallback
2047 // and dynamic entries might have origin different from the
2049 if (NS_SecurityCompareURIs(keyURI
, groupURI
,
2050 GetStrictFileOriginPolicy()))
2060 nsOfflineCacheDevice::ChooseApplicationCache(const nsACString
&key
,
2061 nsIApplicationCache
**out
)
2065 nsCOMPtr
<nsIURI
> keyURI
;
2066 nsresult rv
= NS_NewURI(getter_AddRefs(keyURI
), key
);
2067 NS_ENSURE_SUCCESS(rv
, rv
);
2069 // First try to find a matching cache entry.
2070 AutoResetStatement
statement(mStatement_FindClient
);
2071 rv
= statement
->BindUTF8StringParameter(0, key
);
2072 NS_ENSURE_SUCCESS(rv
, rv
);
2075 rv
= statement
->ExecuteStep(&hasRows
);
2076 NS_ENSURE_SUCCESS(rv
, rv
);
2080 rv
= statement
->GetInt32(1, &itemType
);
2081 NS_ENSURE_SUCCESS(rv
, rv
);
2083 if (!(itemType
& nsIApplicationCache::ITEM_FOREIGN
)) {
2084 nsCAutoString clientID
;
2085 rv
= statement
->GetUTF8String(0, clientID
);
2086 NS_ENSURE_SUCCESS(rv
, rv
);
2088 if (CanUseCache(keyURI
, clientID
)) {
2089 return GetApplicationCache(clientID
, out
);
2093 rv
= statement
->ExecuteStep(&hasRows
);
2094 NS_ENSURE_SUCCESS(rv
, rv
);
2097 // OK, we didn't find an exact match. Search for a client with a
2098 // matching namespace.
2100 AutoResetStatement
nsstatement(mStatement_FindClientByNamespace
);
2102 rv
= nsstatement
->BindUTF8StringParameter(0, key
);
2103 NS_ENSURE_SUCCESS(rv
, rv
);
2105 rv
= nsstatement
->ExecuteStep(&hasRows
);
2106 NS_ENSURE_SUCCESS(rv
, rv
);
2111 rv
= nsstatement
->GetInt32(1, &itemType
);
2112 NS_ENSURE_SUCCESS(rv
, rv
);
2114 // Don't associate with a cache based solely on a whitelist entry
2115 if (!(itemType
& nsIApplicationCacheNamespace::NAMESPACE_BYPASS
)) {
2116 nsCAutoString clientID
;
2117 rv
= nsstatement
->GetUTF8String(0, clientID
);
2118 NS_ENSURE_SUCCESS(rv
, rv
);
2120 if (CanUseCache(keyURI
, clientID
)) {
2121 return GetApplicationCache(clientID
, out
);
2125 rv
= nsstatement
->ExecuteStep(&hasRows
);
2126 NS_ENSURE_SUCCESS(rv
, rv
);
2133 nsOfflineCacheDevice::CacheOpportunistically(nsIApplicationCache
* cache
,
2134 const nsACString
&key
)
2136 NS_ENSURE_ARG_POINTER(cache
);
2140 nsCAutoString clientID
;
2141 rv
= cache
->GetClientID(clientID
);
2142 NS_ENSURE_SUCCESS(rv
, rv
);
2144 return CacheOpportunistically(clientID
, key
);
2148 nsOfflineCacheDevice::ActivateCache(const nsCSubstring
&group
,
2149 const nsCSubstring
&clientID
)
2151 AutoResetStatement
statement(mStatement_ActivateClient
);
2152 nsresult rv
= statement
->BindUTF8StringParameter(0, group
);
2153 NS_ENSURE_SUCCESS(rv
, rv
);
2154 rv
= statement
->BindUTF8StringParameter(1, clientID
);
2155 NS_ENSURE_SUCCESS(rv
, rv
);
2156 rv
= statement
->BindInt32Parameter(2, SecondsFromPRTime(PR_Now()));
2157 NS_ENSURE_SUCCESS(rv
, rv
);
2159 rv
= statement
->Execute();
2160 NS_ENSURE_SUCCESS(rv
, rv
);
2163 if (mActiveCachesByGroup
.Get(group
, &active
))
2165 mActiveCaches
.Remove(*active
);
2166 mActiveCachesByGroup
.Remove(group
);
2170 if (!clientID
.IsEmpty())
2172 mActiveCaches
.Put(clientID
);
2173 mActiveCachesByGroup
.Put(group
, new nsCString(clientID
));
2180 nsOfflineCacheDevice::IsActiveCache(const nsCSubstring
&group
,
2181 const nsCSubstring
&clientID
)
2183 nsCString
*active
= nsnull
;
2184 return mActiveCachesByGroup
.Get(group
, &active
) && *active
== clientID
;
2188 nsOfflineCacheDevice::DeactivateGroup(const nsCSubstring
&group
)
2190 nsCString
*active
= nsnull
;
2192 AutoResetStatement
statement(mStatement_DeactivateGroup
);
2193 nsresult rv
= statement
->BindUTF8StringParameter(
2195 NS_ENSURE_SUCCESS(rv
, rv
);
2197 rv
= statement
->Execute();
2198 NS_ENSURE_SUCCESS(rv
, rv
);
2200 if (mActiveCachesByGroup
.Get(group
, &active
))
2202 mActiveCaches
.Remove(*active
);
2203 mActiveCachesByGroup
.Remove(group
);
2211 nsOfflineCacheDevice::GetGroupForCache(const nsACString
&clientID
,
2214 out
.Assign(clientID
);
2215 out
.Truncate(out
.FindChar('|'));
2216 NS_UnescapeURL(out
);
2222 * Preference accessors
2226 nsOfflineCacheDevice::SetCacheParentDirectory(nsILocalFile
*parentDir
)
2230 NS_ERROR("cannot switch cache directory once initialized");
2236 mCacheDirectory
= nsnull
;
2240 // ensure parent directory exists
2241 nsresult rv
= EnsureDir(parentDir
);
2244 NS_WARNING("unable to create parent directory");
2248 // cache dir may not exist, but that's ok
2249 nsCOMPtr
<nsIFile
> dir
;
2250 rv
= parentDir
->Clone(getter_AddRefs(dir
));
2253 rv
= dir
->AppendNative(NS_LITERAL_CSTRING("OfflineCache"));
2257 mCacheDirectory
= do_QueryInterface(dir
);
2261 nsOfflineCacheDevice::SetCapacity(PRUint32 capacity
)
2263 mCacheCapacity
= capacity
* 1024;