update the offline cache atomically. b=389223, r=biesi, sr=jst

This commit is contained in:
dcamp@mozilla.com 2007-07-24 23:31:27 -07:00
parent 1de862b2d3
commit d6d4f57562
14 changed files with 460 additions and 134 deletions

View File

@ -47,6 +47,7 @@
#include "nsNetCID.h"
#include "nsICacheService.h"
#include "nsICacheSession.h"
#include "nsIOfflineCacheUpdate.h"
#include "nsContentUtils.h"
#include "nsDOMError.h"
#include "nsNetUtil.h"
@ -154,7 +155,6 @@ NS_INTERFACE_MAP_BEGIN(nsDOMOfflineLoadStatusList)
NS_INTERFACE_MAP_ENTRY(nsIDOMLoadStatusList)
NS_INTERFACE_MAP_ENTRY(nsIDOMEventTarget)
NS_INTERFACE_MAP_ENTRY(nsIObserver)
NS_INTERFACE_MAP_ENTRY(nsIOfflineCacheUpdateObserver)
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(LoadStatusList)
NS_INTERFACE_MAP_END
@ -199,7 +199,7 @@ nsDOMOfflineLoadStatusList::Init()
rv = cacheUpdateService->GetUpdate(i, getter_AddRefs(cacheUpdate));
NS_ENSURE_SUCCESS(rv, rv);
rv = WatchUpdate(cacheUpdate);
UpdateAdded(cacheUpdate);
NS_ENSURE_SUCCESS(rv, rv);
}
@ -210,6 +210,8 @@ nsDOMOfflineLoadStatusList::Init()
rv = observerServ->AddObserver(this, "offline-cache-update-added", PR_TRUE);
NS_ENSURE_SUCCESS(rv, rv);
rv = observerServ->AddObserver(this, "offline-cache-update-completed", PR_TRUE);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
@ -231,7 +233,7 @@ nsDOMOfflineLoadStatusList::FindWrapper(nsIDOMLoadStatus *aStatus,
}
nsresult
nsDOMOfflineLoadStatusList::WatchUpdate(nsIOfflineCacheUpdate *aUpdate)
nsDOMOfflineLoadStatusList::UpdateAdded(nsIOfflineCacheUpdate *aUpdate)
{
nsCAutoString owner;
nsresult rv = aUpdate->GetUpdateDomain(owner);
@ -256,19 +258,50 @@ nsDOMOfflineLoadStatusList::WatchUpdate(nsIOfflineCacheUpdate *aUpdate)
mItems.AppendObject(wrapper);
rv = SendLoadEvent(NS_LITERAL_STRING(LOADREQUESTED_STR),
mLoadRequestedEventListeners,
wrapper);
NS_ENSURE_SUCCESS(rv, rv);
SendLoadEvent(NS_LITERAL_STRING(LOADREQUESTED_STR),
mLoadRequestedEventListeners,
wrapper);
}
NS_ENSURE_SUCCESS(rv, rv);
rv = aUpdate->AddObserver(this, PR_TRUE);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
NS_IMETHODIMP
nsDOMOfflineLoadStatusList::UpdateCompleted(nsIOfflineCacheUpdate *aUpdate)
{
nsCAutoString owner;
nsresult rv = aUpdate->GetUpdateDomain(owner);
NS_ENSURE_SUCCESS(rv, rv);
if (owner != mHostPort) {
// This update doesn't belong to us
return NS_OK;
}
PRUint32 numItems;
rv = aUpdate->GetCount(&numItems);
NS_ENSURE_SUCCESS(rv, rv);
for (PRUint32 i = 0; i < numItems; i++) {
nsCOMPtr<nsIDOMLoadStatus> status;
rv = aUpdate->Item(i, getter_AddRefs(status));
NS_ENSURE_SUCCESS(rv, rv);
PRUint32 index;
nsCOMPtr<nsIDOMLoadStatus> wrapper = FindWrapper(status, &index);
if (wrapper) {
mItems.RemoveObjectAt(index);
nsresult rv = SendLoadEvent(NS_LITERAL_STRING(LOADCOMPLETED_STR),
mLoadCompletedEventListeners,
wrapper);
NS_ENSURE_SUCCESS(rv, rv);
}
}
return NS_OK;
}
//
// nsDOMOfflineLoadStatusList::nsIDOMLoadStatusList
//
@ -468,29 +501,13 @@ nsDOMOfflineLoadStatusList::Observe(nsISupports *aSubject,
if (!strcmp(aTopic, "offline-cache-update-added")) {
nsCOMPtr<nsIOfflineCacheUpdate> update = do_QueryInterface(aSubject);
if (update) {
rv = WatchUpdate(update);
NS_ENSURE_SUCCESS(rv, rv);
UpdateAdded(update);
}
} else if (!strcmp(aTopic, "offline-cache-update-completed")) {
nsCOMPtr<nsIOfflineCacheUpdate> update = do_QueryInterface(aSubject);
if (update) {
UpdateCompleted(update);
}
}
return NS_OK;
}
//
// nsDOMLoadStatusList::nsIOfflineCacheUpdateObserver
//
NS_IMETHODIMP
nsDOMOfflineLoadStatusList::ItemCompleted(nsIDOMLoadStatus *aItem)
{
PRUint32 index;
nsCOMPtr<nsIDOMLoadStatus> wrapper = FindWrapper(aItem, &index);
if (wrapper) {
mItems.RemoveObjectAt(index);
nsresult rv = SendLoadEvent(NS_LITERAL_STRING(LOADCOMPLETED_STR),
mLoadCompletedEventListeners,
wrapper);
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;

View File

@ -59,7 +59,6 @@ class nsDOMOfflineLoadStatus;
class nsDOMOfflineLoadStatusList : public nsIDOMLoadStatusList,
public nsIDOMEventTarget,
public nsIObserver,
public nsIOfflineCacheUpdateObserver,
public nsSupportsWeakReference
{
public:
@ -67,7 +66,6 @@ public:
NS_DECL_NSIDOMLOADSTATUSLIST
NS_DECL_NSIDOMEVENTTARGET
NS_DECL_NSIOBSERVER
NS_DECL_NSIOFFLINECACHEUPDATEOBSERVER
nsDOMOfflineLoadStatusList(nsIURI *aURI);
virtual ~nsDOMOfflineLoadStatusList();
@ -75,7 +73,8 @@ public:
nsresult Init();
private :
nsresult WatchUpdate (nsIOfflineCacheUpdate *aUpdate);
nsresult UpdateAdded (nsIOfflineCacheUpdate *aUpdate);
nsresult UpdateCompleted (nsIOfflineCacheUpdate *aUpdate);
nsIDOMLoadStatus *FindWrapper (nsIDOMLoadStatus *aStatus,
PRUint32 *aIndex);
void NotifyEventListeners(const nsCOMArray<nsIDOMEventListener>& aListeners,
@ -95,6 +94,7 @@ private :
nsCOMArray<nsIDOMEventListener> mLoadRequestedEventListeners;
nsCOMArray<nsIDOMEventListener> mLoadCompletedEventListeners;
nsCOMArray<nsIDOMEventListener> mUpdateCompletedEventListeners;
};
class nsDOMLoadStatusEvent : public nsDOMEvent,

View File

@ -50,7 +50,7 @@ interface nsIFile;
* 3) Support for uniquely identifying cached data in cases when the URL
* is insufficient (e.g., HTTP form submission).
*/
[scriptable, uuid(afafb719-bdf5-49c8-a4a9-db39ec331c9b)]
[scriptable, uuid(09556ba7-b13d-47d2-b154-fe690b063899)]
interface nsICachingChannel : nsISupports
{
/**
@ -105,6 +105,12 @@ interface nsICachingChannel : nsISupports
*/
attribute boolean cacheForOfflineUse;
/**
* The session into which to cache offline data. If not specified,
* data will be placed in "HTTP-offline"
*/
attribute ACString offlineCacheClientID;
/**
* Get the "file" where the cached data can be found. This is valid for
* as long as a reference to the cache token is held. This may return

View File

@ -48,7 +48,7 @@ interface nsICacheListener;
interface nsICacheSession;
interface nsICacheVisitor;
[scriptable, uuid(de114eb4-29fc-4959-b2f7-2d03eb9bc771)]
[scriptable, uuid(98dd0187-aad4-4cab-82c5-1adddef3629d)]
interface nsICacheService : nsISupports
{
/**
@ -84,6 +84,15 @@ interface nsICacheService : nsISupports
* Evicts all entries in all devices implied by the storage policy.
*/
void evictEntries(in nsCacheStoragePolicy storagePolicy);
/**
* Return a unique, temporary cache client ID.
*
* This is used by the offline cache. The offline cache lets clients
* accumulate entries in a temporary client and merge them in as a group
* using nsIOfflineCacheSession.mergeTemporaryClient().
*/
ACString createTemporaryClientID(in nsCacheStoragePolicy storagePolicy);
};
%{C++

View File

@ -40,37 +40,39 @@
#include "nsISupports.idl"
#include "nsICache.idl"
[scriptable, uuid(0058c32b-0d93-4cf8-a561-e6f749c8a7b1)]
/**
* The offline cache is meant to reliably store resources for
* offline use. The expected semantics are:
*
* a) Once populated, the cache will not evict an application resource
* unless explicitly asked.
*
* b) Resources no longer in use by the application should be evicted.
*
* c) If the cache fills up, new entries should be rejected rather
* than throwing out old ones.
*
* The offline cache uses domains to concretely represent an
* application. It maintains a list of resources to be pinned for
* each domain. This list is separate from actual cache
* population - the caller is still responsible for placing items
* in the cache, and ownership can be declared without a
* corresponding entry.
*
* A key can optionally be associated with a specific URI within
* the domain.
*/
[scriptable, uuid(de7875e5-a7e2-4d8d-ad01-807ef55ef8c0)]
interface nsIOfflineCacheSession : nsISupports
{
/**
* The offline cache is meant to reliably store resources for
* offline use. The expected semantics are:
*
* a) Once populated, the cache will not evict an application resource
* unless explicitly asked.
*
* b) Resources no longer in use by the application should be evicted.
*
* c) If the cache fills up, new entries should be rejected rather
* than throwing out old ones.
*
* The offline cache uses domains to concretely represent an
* application. It maintains a list of resources to be pinned for
* each domain. This list is separate from actual cache
* population - the caller is still responsible for placing items
* in the cache, and ownership can be declared without a
* corresponding entry.
*
* A key can optionally be associated with a specific URI within
* the domain.
*/
/**
* Gets the list of owner domains in the cache.
*
* @param count The number of domains returned
* @param uris The domains that own resources in the cache
* @param count
* The number of domains returned
* @param uris
* The domains that own resources in the cache
*/
void getOwnerDomains(out unsigned long count,
[array, size_is(count)]out string domains);
@ -78,9 +80,12 @@ interface nsIOfflineCacheSession : nsISupports
/**
* Gets the list of owner URIs associated with a domain.
*
* @param ownerDomain The domain to query
* @param count The number of uris returned
* @param uris The uris in this domain that own resources
* @param ownerDomain
* The domain to query
* @param count
* The number of uris returned
* @param uris
* The uris in this domain that own resources
*/
void getOwnerURIs(in ACString ownerDomain,
out unsigned long count,
@ -96,12 +101,16 @@ interface nsIOfflineCacheSession : nsISupports
* an entry is created with this key, it will be owned by the
* domain/URI pair.
*
* @param ownerDomain The domain that owns the resources.
* @param ownerURI The specific URI that owns the resources. This can
* be empty if no URI specifically owns the resources.
* @param count The number of keys in keys.
* @param keys The keys that the domain/URI pair own. This can be empty
* to clear ownership for the domain/URI pair.
* @param ownerDomain
* The domain that owns the resources.
* @param ownerURI
* The specific URI that owns the resources. This can be empty if
* no URI specifically owns the resources.
* @param count
* The number of keys in keys.
* @param keys
* The keys that the domain/URI pair own. This can be empty to
* clear ownership for the domain/URI pair.
*/
void setOwnedKeys(in ACString ownerDomain,
in ACString ownerURI,
@ -111,11 +120,15 @@ interface nsIOfflineCacheSession : nsISupports
/**
* Gets the list of resources owned by a given domain/URI pair.
*
* @param ownerDomain The domain that owns the resources.
* @param ownerURI The specific URI that owns the resources. This can
* be empty if no URI specifically owns the resources.
* @param count The number of keys in keys.
* @param keys The keys that the domain/URI pair own.
* @param ownerDomain
* The domain that owns the resources.
* @param ownerURI
* The specific URI that owns the resources. This can be empty
* if no URI specifically owns the resources.
* @param count
* The number of keys in keys.
* @param keys
* The keys that the domain/URI pair own.
*/
void getOwnedKeys(in ACString ownerDomain,
in ACString ownerURI,
@ -129,10 +142,13 @@ interface nsIOfflineCacheSession : nsISupports
* an entry is created with this key, it will be owned by the
* domain/URI pair.
*
* @param ownerDomain The domain that owns the resources.
* @param ownerURI The specific URI that owns the resources. This can
* be empty if no URI specifically owns the resources.
* @param key The key to add.
* @param ownerDomain
* The domain that owns the resources.
* @param ownerURI
* The specific URI that owns the resources. This can be empty
* if no URI specifically owns the resources.
* @param key
* The key to add.
*/
void addOwnedKey(in ACString ownerDomain,
in ACString ownerURI,
@ -144,9 +160,11 @@ interface nsIOfflineCacheSession : nsISupports
* If the key does not exist, an NS_ERROR_NOT_AVAILABLE exception
* will be thrown.
*
* @param ownerDomain The domain that owns the resources.
* @param ownerURI The specific URI that owns the resources. This can
* be empty if no URI specifically owns the resources.
* @param ownerDomain
* The domain that owns the resources.
* @param ownerURI
* The specific URI that owns the resources. This can be empty
* if no URI specifically owns the resources.
* @param key The key to remove.
*/
void removeOwnedKey(in ACString ownerDomain,
@ -156,9 +174,11 @@ interface nsIOfflineCacheSession : nsISupports
/**
* Checks whether a key is owned by a given domain/URI pair.
*
* @param ownerDomain The domain that owns the resources.
* @param ownerURI The specific URI that owns the resources. This can
* be empty if no URI specifically owns the resources.
* @param ownerDomain
* The domain that owns the resources.
* @param ownerURI
* The specific URI that owns the resources. This can be empty
* if no URI specifically owns the resources.
* @param key The key to check
*/
boolean keyIsOwned(in ACString ownerDomain,
@ -177,4 +197,17 @@ interface nsIOfflineCacheSession : nsISupports
* Evict all entries that are not owned by a domain.
*/
void evictUnownedEntries();
/**
* Merge the items from a temporary clientID in to this client. This lets
* offline cache updates accumulate in a temporary client and be moved
* in all at once.
*
* Entries in the temporary client will replace any entries in this client
* with the same cache key.
*
* Ownership lists for a given domain/URI pair from the temporary client
* will replace ownership lists for the same domain/URI pair.
*/
void mergeTemporaryClientID(in ACString temporaryClientID);
};

View File

@ -1030,6 +1030,25 @@ nsresult nsCacheService::EvictUnownedOfflineEntries(nsCacheSession * session)
#endif
}
nsresult nsCacheService::MergeTemporaryClientID(nsCacheSession * session,
const nsACString & clientID)
{
#ifdef NECKO_OFFLINE_CACHE
if (session->StoragePolicy() != nsICache::STORE_OFFLINE)
return NS_ERROR_NOT_AVAILABLE;
if (!gService->mOfflineDevice) {
nsresult rv = gService->CreateOfflineDevice();
if (NS_FAILED(rv)) return rv;
}
return gService->mOfflineDevice->MergeTemporaryClientID
(session->ClientID()->get(), PromiseFlatCString(clientID).get());
#else // !NECKO_OFFLINE_CACHE
return NS_ERROR_NOT_IMPLEMENTED;
#endif
}
NS_IMETHODIMP nsCacheService::VisitEntries(nsICacheVisitor *visitor)
{
NS_ENSURE_ARG_POINTER(visitor);
@ -1083,6 +1102,24 @@ NS_IMETHODIMP nsCacheService::EvictEntries(nsCacheStoragePolicy storagePolicy)
return EvictEntriesForClient(nsnull, storagePolicy);
}
NS_IMETHODIMP nsCacheService::CreateTemporaryClientID(nsCacheStoragePolicy storagePolicy,
nsACString &clientID)
{
#ifdef NECKO_OFFLINE_CACHE
// Only the offline cache device supports temporary clients
if (storagePolicy != nsICache::STORE_OFFLINE)
return NS_ERROR_NOT_AVAILABLE;
if (!gService->mOfflineDevice) {
nsresult rv = gService->CreateOfflineDevice();
if (NS_FAILED(rv)) return rv;
}
return gService->mOfflineDevice->CreateTemporaryClientID(clientID);
#else // !NECKO_OFFLINE_CACHE
return NS_ERROR_NOT_IMPLEMENTED;
#endif
}
/**
* Internal Methods

View File

@ -139,6 +139,9 @@ public:
static nsresult EvictUnownedOfflineEntries(nsCacheSession * session);
static nsresult MergeTemporaryClientID(nsCacheSession * session,
const nsACString & fromClientID);
/**
* Methods called by nsCacheEntryDescriptor
*/

View File

@ -197,3 +197,7 @@ NS_IMETHODIMP nsCacheSession::EvictUnownedEntries()
return nsCacheService::EvictUnownedOfflineEntries(this);
}
NS_IMETHODIMP nsCacheSession::MergeTemporaryClientID(const nsACString& fromClientID)
{
return nsCacheService::MergeTemporaryClientID(this, fromClientID);
}

View File

@ -60,6 +60,7 @@ static const char OFFLINE_CACHE_DEVICE_ID[] = { "offline" };
#define LOG(args) CACHE_LOG_DEBUG(args)
static PRUint32 gNextTemporaryClientID = 0;
/*****************************************************************************
* helpers
@ -118,7 +119,7 @@ class EvictionObserver
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);"
" OLD.key, OLD.generation);"
" END;"));
}
@ -175,19 +176,14 @@ 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,
GetCacheDataFile(nsIFile *cacheDir, const char *key,
int generation, nsCOMPtr<nsIFile> &file)
{
cacheDir->Clone(getter_AddRefs(file));
if (!file)
return NS_ERROR_OUT_OF_MEMORY;
nsCAutoString fullKey;
fullKey.Append(cid);
fullKey.Append(':');
fullKey.Append(key);
PRUint64 hash = DCacheHash(fullKey.get());
PRUint64 hash = DCacheHash(key);
PRUint32 dir1 = (PRUint32) (hash & 0x0F);
PRUint32 dir2 = (PRUint32)((hash & 0xF0) >> 4);
@ -212,20 +208,19 @@ nsOfflineCacheEvictionFunction::OnFunctionCall(mozIStorageValueArray *values, ns
PRUint32 numEntries;
nsresult rv = values->GetNumEntries(&numEntries);
NS_ENSURE_SUCCESS(rv, rv);
NS_ASSERTION(numEntries == 3, "unexpected number of arguments");
NS_ASSERTION(numEntries == 2, "unexpected number of arguments");
PRUint32 valueLen;
const char *cid = values->AsSharedUTF8String(0, &valueLen);
const char *key = values->AsSharedUTF8String(1, &valueLen);
int generation = values->AsInt32(2);
const char *key = values->AsSharedUTF8String(0, &valueLen);
int generation = values->AsInt32(1);
nsCOMPtr<nsIFile> file;
rv = GetCacheDataFile(mDevice->CacheDirectory(), cid, key,
rv = GetCacheDataFile(mDevice->CacheDirectory(), key,
generation, file);
if (NS_FAILED(rv))
{
LOG(("GetCacheDataFile [cid=%s key=%s generation=%d] failed [rv=%x]!\n",
cid, key, generation, rv));
LOG(("GetCacheDataFile [key=%s generation=%d] failed [rv=%x]!\n",
key, generation, rv));
return rv;
}
@ -323,7 +318,7 @@ public:
NS_DECL_ISUPPORTS
static nsOfflineCacheBinding *
Create(nsIFile *cacheDir, const char *key, int generation);
Create(nsIFile *cacheDir, const nsCString *key, int generation);
nsCOMPtr<nsIFile> mDataFile;
int mGeneration;
@ -333,7 +328,7 @@ NS_IMPL_THREADSAFE_ISUPPORTS0(nsOfflineCacheBinding)
nsOfflineCacheBinding *
nsOfflineCacheBinding::Create(nsIFile *cacheDir,
const char *key,
const nsCString *fullKey,
int generation)
{
nsCOMPtr<nsIFile> file;
@ -341,6 +336,11 @@ nsOfflineCacheBinding::Create(nsIFile *cacheDir,
if (!file)
return nsnull;
nsCAutoString keyBuf;
const char *cid, *key;
if (!DecomposeCacheEntryKey(fullKey, &cid, &key, keyBuf))
return nsnull;
PRUint64 hash = DCacheHash(key);
PRUint32 dir1 = (PRUint32) (hash & 0x0F);
@ -444,7 +444,7 @@ CreateCacheEntry(nsOfflineCacheDevice *device,
// create a binding object for this entry
nsOfflineCacheBinding *binding =
nsOfflineCacheBinding::Create(device->CacheDirectory(),
fullKey->get(),
fullKey,
rec.generation);
if (!binding)
{
@ -765,7 +765,7 @@ nsOfflineCacheDevice::Init()
new nsOfflineCacheEvictionFunction(this);
if (!evictionFunction) return NS_ERROR_OUT_OF_MEMORY;
rv = mDB->CreateFunction(NS_LITERAL_CSTRING("cache_eviction_observer"), 3, evictionFunction);
rv = mDB->CreateFunction(NS_LITERAL_CSTRING("cache_eviction_observer"), 2, evictionFunction);
NS_ENSURE_SUCCESS(rv, rv);
// create all (most) of our statements up front
@ -789,9 +789,11 @@ nsOfflineCacheDevice::Init()
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_ListOwners, "SELECT DISTINCT Domain, URI FROM moz_cache_owners WHERE ClientID = ?;"),
StatementSql ( mStatement_ListOwnerDomains, "SELECT DISTINCT Domain FROM moz_cache_owners WHERE ClientID = ?;"),
StatementSql ( mStatement_ListOwnerURIs, "SELECT DISTINCT URI FROM moz_cache_owners WHERE ClientID = ? AND Domain = ?;"),
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);" )
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);" ),
StatementSql ( mStatement_SwapClientID, "UPDATE OR REPLACE moz_cache SET ClientID = ? WHERE ClientID = ?;")
};
for (PRUint32 i=0; i<NS_ARRAY_LENGTH(prepared); ++i)
{
@ -807,6 +809,14 @@ nsOfflineCacheDevice::Init()
" WHERE (Flags & 1);"));
NS_ENSURE_SUCCESS(rv, rv);
// Clear up dangling temporary sessions
EvictionObserver evictionObserver(mDB);
rv = mDB->ExecuteSimpleSQL(
NS_LITERAL_CSTRING("DELETE FROM moz_cache"
" WHERE (ClientID GLOB \"TempClient*\")"));
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
@ -972,7 +982,7 @@ nsOfflineCacheDevice::BindEntry(nsCacheEntry *entry)
// create binding, pick best generation number
nsRefPtr<nsOfflineCacheBinding> binding =
nsOfflineCacheBinding::Create(mCacheDirectory, entry->Key()->get(), -1);
nsOfflineCacheBinding::Create(mCacheDirectory, entry->Key(), -1);
if (!binding)
return NS_ERROR_OUT_OF_MEMORY;
@ -1474,6 +1484,84 @@ nsOfflineCacheDevice::EvictUnownedEntries(const char *clientID)
return statement->Execute();
}
nsresult
nsOfflineCacheDevice::CreateTemporaryClientID(nsACString &clientID)
{
nsCAutoString str;
str.AssignLiteral("TempClient");
str.AppendInt(gNextTemporaryClientID++);
clientID.Assign(str);
return NS_OK;
}
nsresult
nsOfflineCacheDevice::MergeTemporaryClientID(const char *clientID,
const char *fromClientID)
{
LOG(("nsOfflineCacheDevice::MergeTemporaryClientID [cid=%s, from=%s]\n",
clientID, fromClientID));
mozStorageTransaction transaction(mDB, PR_FALSE);
// Move over ownerships
AutoResetStatement listOwnersStatement(mStatement_ListOwners);
nsresult rv = listOwnersStatement->BindUTF8StringParameter(
0, nsDependentCString(fromClientID));
NS_ENSURE_SUCCESS(rv, rv);
// List all the owners in the new session
nsTArray<nsCString> domainArray;
nsTArray<nsCString> uriArray;
PRBool hasRows;
rv = listOwnersStatement->ExecuteStep(&hasRows);
NS_ENSURE_SUCCESS(rv, rv);
while (hasRows)
{
PRUint32 length;
domainArray.AppendElement(
nsDependentCString(listOwnersStatement->AsSharedUTF8String(0, &length)));
uriArray.AppendElement(
nsDependentCString(listOwnersStatement->AsSharedUTF8String(1, &length)));
rv = listOwnersStatement->ExecuteStep(&hasRows);
NS_ENSURE_SUCCESS(rv, rv);
}
// Now move over each ownership set
for (PRUint32 i = 0; i < domainArray.Length(); i++) {
PRUint32 count;
char **keys;
rv = GetOwnedKeys(fromClientID, domainArray[i], uriArray[i],
&count, &keys);
NS_ENSURE_SUCCESS(rv, rv);
rv = SetOwnedKeys(clientID, domainArray[i], uriArray[i],
count, const_cast<const char **>(keys));
NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(count, keys);
NS_ENSURE_SUCCESS(rv, rv);
// Now clear out the temporary session's copy
rv = SetOwnedKeys(fromClientID, domainArray[i], uriArray[i], 0, 0);
NS_ENSURE_SUCCESS(rv, rv);
}
EvictionObserver evictionObserver(mDB);
AutoResetStatement swapStatement(mStatement_SwapClientID);
rv = swapStatement->BindUTF8StringParameter(
0, nsDependentCString(clientID));
rv |= swapStatement->BindUTF8StringParameter(
1, nsDependentCString(fromClientID));
NS_ENSURE_SUCCESS(rv, rv);
rv = swapStatement->Execute();
NS_ENSURE_SUCCESS(rv, rv);
return transaction.Commit();
}
/**
* Preference accessors
*/

View File

@ -121,6 +121,10 @@ public:
const nsACString &ownerDomain);
nsresult EvictUnownedEntries(const char *clientID);
nsresult CreateTemporaryClientID(nsACString &clientID);
nsresult MergeTemporaryClientID(const char *clientID,
const char *fromClientID);
/**
* Preference accessors
@ -163,8 +167,10 @@ private:
nsCOMPtr<mozIStorageStatement> mStatement_CheckOwnership;
nsCOMPtr<mozIStorageStatement> mStatement_DeleteUnowned;
nsCOMPtr<mozIStorageStatement> mStatement_ListOwned;
nsCOMPtr<mozIStorageStatement> mStatement_ListOwners;
nsCOMPtr<mozIStorageStatement> mStatement_ListOwnerDomains;
nsCOMPtr<mozIStorageStatement> mStatement_ListOwnerURIs;
nsCOMPtr<mozIStorageStatement> mStatement_SwapClientID;
nsCOMPtr<nsILocalFile> mCacheDirectory;
PRUint32 mCacheCapacity;

View File

@ -75,6 +75,7 @@
#include "nsStreamUtils.h"
#include "nsIOService.h"
#include "nsAuthInformationHolder.h"
#include "nsICacheService.h"
// True if the local cache should be bypassed when processing a request.
#define BYPASS_LOCAL_CACHE(loadFlags) \
@ -1405,8 +1406,19 @@ nsHttpChannel::OpenOfflineCacheEntryForWriting()
GenerateCacheKey(cacheKey);
nsCOMPtr<nsICacheSession> session;
rv = gHttpHandler->GetCacheSession(nsICache::STORE_OFFLINE,
getter_AddRefs(session));
if (!mOfflineCacheClientID.IsEmpty()) {
nsCOMPtr<nsICacheService> serv =
do_GetService(NS_CACHESERVICE_CONTRACTID, &rv);
if (NS_FAILED(rv)) return rv;
rv = serv->CreateSession(mOfflineCacheClientID.get(),
nsICache::STORE_OFFLINE,
nsICache::STREAM_BASED,
getter_AddRefs(session));
} else {
rv = gHttpHandler->GetCacheSession(nsICache::STORE_OFFLINE,
getter_AddRefs(session));
}
if (NS_FAILED(rv)) return rv;
rv = session->OpenCacheEntry(cacheKey, nsICache::ACCESS_READ_WRITE,
@ -4550,6 +4562,22 @@ nsHttpChannel::SetCacheForOfflineUse(PRBool value)
return NS_OK;
}
NS_IMETHODIMP
nsHttpChannel::GetOfflineCacheClientID(nsACString &value)
{
value = mOfflineCacheClientID;
return NS_OK;
}
NS_IMETHODIMP
nsHttpChannel::SetOfflineCacheClientID(const nsACString &value)
{
mOfflineCacheClientID = value;
return NS_OK;
}
NS_IMETHODIMP
nsHttpChannel::GetCacheFile(nsIFile **cacheFile)
{

View File

@ -254,6 +254,7 @@ private:
nsCOMPtr<nsICacheEntryDescriptor> mOfflineCacheEntry;
nsCacheAccessMode mOfflineCacheAccess;
nsCString mOfflineCacheClientID;
// auth specific data
nsISupports *mProxyAuthContinuationState;

View File

@ -52,6 +52,7 @@
#include "nsNetUtil.h"
#include "nsServiceManagerUtils.h"
#include "nsStreamUtils.h"
#include "nsThreadUtils.h"
#include "prlog.h"
static nsOfflineCacheUpdateService *gOfflineCacheUpdateService = nsnull;
@ -85,10 +86,11 @@ private:
// nsOfflineCacheUpdateItem::nsISupports
//-----------------------------------------------------------------------------
NS_IMPL_ISUPPORTS5(nsOfflineCacheUpdateItem,
NS_IMPL_ISUPPORTS6(nsOfflineCacheUpdateItem,
nsIDOMLoadStatus,
nsIRequestObserver,
nsIStreamListener,
nsIRunnable,
nsIInterfaceRequestor,
nsIChannelEventSink)
@ -99,9 +101,11 @@ NS_IMPL_ISUPPORTS5(nsOfflineCacheUpdateItem,
nsOfflineCacheUpdateItem::nsOfflineCacheUpdateItem(nsOfflineCacheUpdate *aUpdate,
nsIURI *aURI,
nsIURI *aReferrerURI,
nsIDOMNode *aSource)
nsIDOMNode *aSource,
const nsACString &aClientID)
: mURI(aURI)
, mReferrerURI(aReferrerURI)
, mClientID(aClientID)
, mUpdate(aUpdate)
, mChannel(nsnull)
, mState(nsIDOMLoadStatus::UNINITIALIZED)
@ -139,6 +143,11 @@ nsOfflineCacheUpdateItem::OpenChannel()
if (cachingChannel) {
rv = cachingChannel->SetCacheForOfflineUse(PR_TRUE);
NS_ENSURE_SUCCESS(rv, rv);
if (!mClientID.IsEmpty()) {
rv = cachingChannel->SetOfflineCacheClientID(mClientID);
NS_ENSURE_SUCCESS(rv, rv);
}
}
rv = mChannel->AsyncOpen(this, nsnull);
@ -206,6 +215,20 @@ nsOfflineCacheUpdateItem::OnStopRequest(nsIRequest *aRequest,
mChannel->GetContentLength(&mBytesRead);
}
// We need to notify the update that the load is complete, but we
// want to give the channel a chance to close the cache entries.
NS_DispatchToCurrentThread(this);
return NS_OK;
}
//-----------------------------------------------------------------------------
// nsOfflineCacheUpdateItem::nsIRunnable
//-----------------------------------------------------------------------------
NS_IMETHODIMP
nsOfflineCacheUpdateItem::Run()
{
mUpdate->LoadCompleted();
return NS_OK;
@ -245,8 +268,15 @@ nsOfflineCacheUpdateItem::OnChannelRedirect(nsIChannel *aOldChannel,
do_QueryInterface(aOldChannel);
nsCOMPtr<nsICachingChannel> newCachingChannel =
do_QueryInterface(aOldChannel);
if (newCachingChannel)
newCachingChannel->SetCacheForOfflineUse(PR_TRUE);
if (newCachingChannel) {
rv = newCachingChannel->SetCacheForOfflineUse(PR_TRUE);
NS_ENSURE_SUCCESS(rv, rv);
if (!mClientID.IsEmpty()) {
rv = newCachingChannel->SetOfflineCacheClientID(mClientID);
NS_ENSURE_SUCCESS(rv, rv);
}
}
PRBool match;
rv = newURI->SchemeIs("http", &match);
@ -370,6 +400,8 @@ nsOfflineCacheUpdate::nsOfflineCacheUpdate()
: mState(STATE_UNINITIALIZED)
, mAddedItems(PR_FALSE)
, mPartialUpdate(PR_FALSE)
, mSucceeded(PR_TRUE)
, mCurrentItem(-1)
{
}
@ -409,9 +441,27 @@ nsOfflineCacheUpdate::Init(PRBool aPartialUpdate,
getter_AddRefs(session));
NS_ENSURE_SUCCESS(rv, rv);
mCacheSession = do_QueryInterface(session, &rv);
mMainCacheSession = do_QueryInterface(session, &rv);
NS_ENSURE_SUCCESS(rv, rv);
// Partial updates don't use temporary cache sessions
if (aPartialUpdate) {
mCacheSession = mMainCacheSession;
} else {
rv = cacheService->CreateTemporaryClientID(nsICache::STORE_OFFLINE,
mClientID);
NS_ENSURE_SUCCESS(rv, rv);
rv = cacheService->CreateSession(mClientID.get(),
nsICache::STORE_OFFLINE,
nsICache::STREAM_BASED,
getter_AddRefs(session));
NS_ENSURE_SUCCESS(rv, rv);
mCacheSession = do_QueryInterface(session, &rv);
NS_ENSURE_SUCCESS(rv, rv);
}
mState = STATE_INITIALIZED;
return NS_OK;
@ -424,10 +474,23 @@ nsOfflineCacheUpdate::LoadCompleted()
LOG(("nsOfflineCacheUpdate::LoadCompleted [%p]", this));
NS_ASSERTION(mItems.Length() >= 1, "Unknown load completed");
nsRefPtr<nsOfflineCacheUpdateItem> item = mItems[mCurrentItem];
mCurrentItem++;
nsRefPtr<nsOfflineCacheUpdateItem> item = mItems[0];
mItems.RemoveElementAt(0);
PRUint16 status;
rv = item->GetStatus(&status);
// Check for failures. Only connection or server errors (5XX) will cause
// the update to fail.
if (NS_FAILED(rv) || status == 0 || status >= 500) {
// Only fail updates from this domain. Outside-of-domain updates
// are not guaranteeed to be updated.
nsCAutoString domain;
item->mURI->GetHostPort(domain);
if (domain == mUpdateDomain) {
mSucceeded = PR_FALSE;
}
}
rv = NotifyCompleted(item);
if (NS_FAILED(rv)) return;
@ -449,6 +512,7 @@ nsOfflineCacheUpdate::Begin()
mState = STATE_RUNNING;
mCurrentItem = 0;
ProcessNextURI();
return NS_OK;
@ -460,10 +524,12 @@ nsOfflineCacheUpdate::Cancel()
LOG(("nsOfflineCacheUpdate::Cancel [%p]", this));
mState = STATE_CANCELLED;
mSucceeded = PR_FALSE;
if (mItems.Length() > 0) {
// First load might be running
mItems[0]->Cancel();
if (mCurrentItem >= 0 &&
mCurrentItem < static_cast<PRInt32>(mItems.Length())) {
// Load might be running
mItems[mCurrentItem]->Cancel();
}
return NS_OK;
@ -478,8 +544,8 @@ nsOfflineCacheUpdate::AddOwnedItems(const nsACString &aOwnerURI)
{
PRUint32 count;
char **keys;
nsresult rv = mCacheSession->GetOwnedKeys(mUpdateDomain, aOwnerURI,
&count, &keys);
nsresult rv = mMainCacheSession->GetOwnedKeys(mUpdateDomain, aOwnerURI,
&count, &keys);
NS_ENSURE_SUCCESS(rv, rv);
AutoFreeArray autoFree(count, keys);
@ -488,7 +554,8 @@ nsOfflineCacheUpdate::AddOwnedItems(const nsACString &aOwnerURI)
nsCOMPtr<nsIURI> uri;
if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(uri), keys[i]))) {
nsRefPtr<nsOfflineCacheUpdateItem> item =
new nsOfflineCacheUpdateItem(this, uri, mReferrerURI, nsnull);
new nsOfflineCacheUpdateItem(this, uri, mReferrerURI,
nsnull, mClientID);
if (!item) return NS_ERROR_OUT_OF_MEMORY;
mItems.AppendElement(item);
@ -504,7 +571,7 @@ nsOfflineCacheUpdate::AddDomainItems()
{
PRUint32 count;
char **uris;
nsresult rv = mCacheSession->GetOwnerURIs(mUpdateDomain, &count, &uris);
nsresult rv = mMainCacheSession->GetOwnerURIs(mUpdateDomain, &count, &uris);
NS_ENSURE_SUCCESS(rv, rv);
AutoFreeArray autoFree(count, uris);
@ -525,22 +592,23 @@ nsOfflineCacheUpdate::AddDomainItems()
nsresult
nsOfflineCacheUpdate::ProcessNextURI()
{
LOG(("nsOfflineCacheUpdate::ProcessNextURI [%p, numItems=%d]",
this, mItems.Length()));
LOG(("nsOfflineCacheUpdate::ProcessNextURI [%p, current=%d, numItems=%d]",
this, mCurrentItem, mItems.Length()));
if (mState == STATE_CANCELLED || mItems.Length() == 0) {
if (mState == STATE_CANCELLED ||
mCurrentItem >= static_cast<PRInt32>(mItems.Length())) {
return Finish();
}
#if defined(PR_LOGGING)
if (LOG_ENABLED()) {
nsCAutoString spec;
mItems[0]->mURI->GetSpec(spec);
mItems[mCurrentItem]->mURI->GetSpec(spec);
LOG(("%p: Opening channel for %s", this, spec.get()));
}
#endif
nsresult rv = mItems[0]->OpenChannel();
nsresult rv = mItems[mCurrentItem]->OpenChannel();
if (NS_FAILED(rv)) {
LoadCompleted();
return rv;
@ -584,6 +652,21 @@ nsOfflineCacheUpdate::Finish()
nsOfflineCacheUpdateService *service =
nsOfflineCacheUpdateService::GetInstance();
if (!mPartialUpdate) {
if (mSucceeded) {
nsresult rv = mMainCacheSession->MergeTemporaryClientID(mClientID);
if (NS_FAILED(rv))
mSucceeded = PR_FALSE;
}
if (!mSucceeded) {
// Update was not merged, mark all the loads as failures
for (PRUint32 i = 0; i < mItems.Length(); i++) {
mItems[i]->Cancel();
}
}
}
if (!service)
return NS_ERROR_FAILURE;
@ -651,8 +734,9 @@ nsOfflineCacheUpdate::AddURI(nsIURI *aURI, nsIDOMNode *aSource)
NS_ENSURE_SUCCESS(rv, rv);
}
nsRefPtr<nsOfflineCacheUpdateItem> item =
new nsOfflineCacheUpdateItem(this, aURI, mReferrerURI, aSource);
nsRefPtr<nsOfflineCacheUpdateItem> item =
new nsOfflineCacheUpdateItem(this, aURI, mReferrerURI,
aSource, mClientID);
if (!item) return NS_ERROR_OUT_OF_MEMORY;
mItems.AppendElement(item);

View File

@ -55,6 +55,7 @@
#include "nsIOfflineCacheSession.h"
#include "nsIPrefetchService.h"
#include "nsIRequestObserver.h"
#include "nsIRunnable.h"
#include "nsIStreamListener.h"
#include "nsIURI.h"
#include "nsIWebProgressListener.h"
@ -67,6 +68,7 @@ class nsOfflineCacheUpdate;
class nsOfflineCacheUpdateItem : public nsIDOMLoadStatus
, public nsIStreamListener
, public nsIRunnable
, public nsIInterfaceRequestor
, public nsIChannelEventSink
{
@ -75,18 +77,21 @@ public:
NS_DECL_NSIDOMLOADSTATUS
NS_DECL_NSIREQUESTOBSERVER
NS_DECL_NSISTREAMLISTENER
NS_DECL_NSIRUNNABLE
NS_DECL_NSIINTERFACEREQUESTOR
NS_DECL_NSICHANNELEVENTSINK
nsOfflineCacheUpdateItem(nsOfflineCacheUpdate *aUpdate,
nsIURI *aURI,
nsIURI *aReferrerURI,
nsIDOMNode *aSource);
nsIDOMNode *aSource,
const nsACString &aClientID);
~nsOfflineCacheUpdateItem();
nsCOMPtr<nsIURI> mURI;
nsCOMPtr<nsIURI> mReferrerURI;
nsCOMPtr<nsIWeakReference> mSource;
nsCString mClientID;
nsresult OpenChannel();
nsresult Cancel();
@ -131,14 +136,19 @@ private:
PRBool mAddedItems;
PRBool mPartialUpdate;
PRBool mSucceeded;
nsCString mUpdateDomain;
nsCString mOwnerURI;
nsCOMPtr<nsIURI> mReferrerURI;
nsCString mClientID;
nsCOMPtr<nsIOfflineCacheSession> mCacheSession;
nsCOMPtr<nsIOfflineCacheSession> mMainCacheSession;
nsCOMPtr<nsIObserverService> mObserverService;
/* Items being updated */
PRInt32 mCurrentItem;
nsTArray<nsRefPtr<nsOfflineCacheUpdateItem> > mItems;
/* Clients watching this update for changes */