Bug 442806: Use seperate, versioned caches for offline apps. r+sr=bz

This commit is contained in:
Dave Camp 2008-08-19 19:30:44 -07:00
parent 8758d3de43
commit db4eca26fd
38 changed files with 1537 additions and 1276 deletions

View File

@ -5408,14 +5408,8 @@ var OfflineApps = {
// XXX: duplicated in preferences/advanced.js // XXX: duplicated in preferences/advanced.js
_getOfflineAppUsage: function (host) _getOfflineAppUsage: function (host)
{ {
var cacheService = Components.classes["@mozilla.org/network/cache-service;1"]. // XXX Bug 442810: include offline cache usage.
getService(Components.interfaces.nsICacheService); var usage = 0;
var cacheSession = cacheService.createSession("HTTP-offline",
Components.interfaces.nsICache.STORE_OFFLINE,
true).
QueryInterface(Components.interfaces.nsIOfflineCacheSession);
var usage = cacheSession.getDomainUsage(host);
var storageManager = Components.classes["@mozilla.org/dom/storagemanager;1"]. var storageManager = Components.classes["@mozilla.org/dom/storagemanager;1"].
getService(Components.interfaces.nsIDOMStorageManager); getService(Components.interfaces.nsIDOMStorageManager);
usage += storageManager.getUsage(host); usage += storageManager.getUsage(host);

View File

@ -216,13 +216,8 @@ var gAdvancedPane = {
// XXX: duplicated in browser.js // XXX: duplicated in browser.js
_getOfflineAppUsage: function (host) _getOfflineAppUsage: function (host)
{ {
var cacheService = Components.classes["@mozilla.org/network/cache-service;1"]. // XXX Bug 442710: include offline cache usage.
getService(Components.interfaces.nsICacheService); var usage = 0;
var cacheSession = cacheService.createSession("HTTP-offline",
Components.interfaces.nsICache.STORE_OFFLINE,
true).
QueryInterface(Components.interfaces.nsIOfflineCacheSession);
var usage = cacheSession.getDomainUsage(host);
var storageManager = Components.classes["@mozilla.org/dom/storagemanager;1"]. var storageManager = Components.classes["@mozilla.org/dom/storagemanager;1"].
getService(Components.interfaces.nsIDOMStorageManager); getService(Components.interfaces.nsIDOMStorageManager);

View File

@ -71,6 +71,8 @@
#include "nsIScriptGlobalObject.h" #include "nsIScriptGlobalObject.h"
#include "nsNetCID.h" #include "nsNetCID.h"
#include "nsIOfflineCacheUpdate.h" #include "nsIOfflineCacheUpdate.h"
#include "nsIApplicationCache.h"
#include "nsIApplicationCacheContainer.h"
#include "nsIScriptSecurityManager.h" #include "nsIScriptSecurityManager.h"
#include "nsIDOMLoadStatus.h" #include "nsIDOMLoadStatus.h"
#include "nsICookieService.h" #include "nsICookieService.h"
@ -881,6 +883,24 @@ nsContentSink::ProcessOfflineManifest(nsIContent *aElement)
return; return;
} }
// XXX: at this point in the spec there is an algorithm for
// confirming whether the cache that was selected at load time was
// the proper application cache for this document. This will
// be implemented in a separate patch; For now just assume that we
// chose an acceptable application cache.
nsCOMPtr<nsIApplicationCacheContainer> channelContainer =
do_QueryInterface(mDocument->GetChannel());
nsCOMPtr<nsIApplicationCacheContainer> docContainer =
do_QueryInterface(mDocument);
if (channelContainer && docContainer) {
nsCOMPtr<nsIApplicationCache> appCache;
channelContainer->GetApplicationCache(getter_AddRefs(appCache));
docContainer->SetApplicationCache(appCache);
}
nsCOMPtr<nsIURI> manifestURI; nsCOMPtr<nsIURI> manifestURI;
nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(manifestURI), nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(manifestURI),
manifestSpec, mDocument, manifestSpec, mDocument,

View File

@ -804,41 +804,7 @@ nsContentUtils::GetOfflineAppManifest(nsIDOMWindow *aWindow, nsIURI **aURI)
PRBool PRBool
nsContentUtils::OfflineAppAllowed(nsIURI *aURI) nsContentUtils::OfflineAppAllowed(nsIURI *aURI)
{ {
nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(aURI); return NS_OfflineAppAllowed(aURI, sPrefBranch);
if (!innerURI)
return PR_FALSE;
// only http and https applications can use offline APIs.
PRBool match;
nsresult rv = innerURI->SchemeIs("http", &match);
NS_ENSURE_SUCCESS(rv, PR_FALSE);
if (!match) {
rv = innerURI->SchemeIs("https", &match);
NS_ENSURE_SUCCESS(rv, PR_FALSE);
if (!match) {
return PR_FALSE;
}
}
nsCOMPtr<nsIPermissionManager> permissionManager =
do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
if (!permissionManager) {
return PR_FALSE;
}
PRUint32 perm;
permissionManager->TestExactPermission(innerURI, "offline-app", &perm);
if (perm == nsIPermissionManager::UNKNOWN_ACTION) {
return GetBoolPref("offline-apps.allow_by_default");
}
if (perm == nsIPermissionManager::DENY_ACTION) {
return PR_FALSE;
}
return PR_TRUE;
} }
// static // static

View File

@ -1143,6 +1143,7 @@ NS_INTERFACE_TABLE_HEAD(nsDocument)
NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIRadioGroupContainer) NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIRadioGroupContainer)
NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIMutationObserver) NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIMutationObserver)
NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMNodeSelector) NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMNodeSelector)
NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIApplicationCacheContainer)
// nsNodeSH::PreCreate() depends on the identity pointer being the // nsNodeSH::PreCreate() depends on the identity pointer being the
// same as nsINode (which nsIDocument inherits), so if you change // same as nsINode (which nsIDocument inherits), so if you change
// the below line, make sure nsNodeSH::PreCreate() still does the // the below line, make sure nsNodeSH::PreCreate() still does the
@ -1953,6 +1954,22 @@ nsDocument::SetPrincipal(nsIPrincipal *aNewPrincipal)
mNodeInfoManager->SetDocumentPrincipal(aNewPrincipal); mNodeInfoManager->SetDocumentPrincipal(aNewPrincipal);
} }
NS_IMETHODIMP
nsDocument::GetApplicationCache(nsIApplicationCache **aApplicationCache)
{
NS_IF_ADDREF(*aApplicationCache = mApplicationCache);
return NS_OK;
}
NS_IMETHODIMP
nsDocument::SetApplicationCache(nsIApplicationCache *aApplicationCache)
{
mApplicationCache = aApplicationCache;
return NS_OK;
}
NS_IMETHODIMP NS_IMETHODIMP
nsDocument::GetContentType(nsAString& aContentType) nsDocument::GetContentType(nsAString& aContentType)
{ {

View File

@ -93,6 +93,8 @@
#include "nsCycleCollectionParticipant.h" #include "nsCycleCollectionParticipant.h"
#include "nsContentList.h" #include "nsContentList.h"
#include "nsGkAtoms.h" #include "nsGkAtoms.h"
#include "nsIApplicationCache.h"
#include "nsIApplicationCacheContainer.h"
// Put these here so all document impls get them automatically // Put these here so all document impls get them automatically
#include "nsHTMLStyleSheet.h" #include "nsHTMLStyleSheet.h"
@ -408,6 +410,7 @@ class nsDocument : public nsIDocument,
public nsIScriptObjectPrincipal, public nsIScriptObjectPrincipal,
public nsIRadioGroupContainer, public nsIRadioGroupContainer,
public nsIDOMNodeSelector, public nsIDOMNodeSelector,
public nsIApplicationCacheContainer,
public nsStubMutationObserver public nsStubMutationObserver
{ {
public: public:
@ -752,6 +755,9 @@ public:
// nsIScriptObjectPrincipal // nsIScriptObjectPrincipal
virtual nsIPrincipal* GetPrincipal(); virtual nsIPrincipal* GetPrincipal();
// nsIApplicationCacheContainer
NS_DECL_NSIAPPLICATIONCACHECONTAINER
virtual nsresult Init(); virtual nsresult Init();
virtual nsresult AddXMLEventsContent(nsIContent * aXMLEventsElement); virtual nsresult AddXMLEventsContent(nsIContent * aXMLEventsElement);
@ -1021,6 +1027,10 @@ protected:
// Our update nesting level // Our update nesting level
PRUint32 mUpdateNestLevel; PRUint32 mUpdateNestLevel;
// The application cache that this document is associated with, if
// any. This can change during the lifetime of the document.
nsCOMPtr<nsIApplicationCache> mApplicationCache;
private: private:
friend class nsUnblockOnloadEvent; friend class nsUnblockOnloadEvent;

View File

@ -143,6 +143,9 @@
#include "nsINestedURI.h" #include "nsINestedURI.h"
#include "nsITransportSecurityInfo.h" #include "nsITransportSecurityInfo.h"
#include "nsINSSErrorsService.h" #include "nsINSSErrorsService.h"
#include "nsIApplicationCache.h"
#include "nsIApplicationCacheContainer.h"
#include "nsIPermissionManager.h"
// Editor-related // Editor-related
#include "nsIEditingSession.h" #include "nsIEditingSession.h"
@ -458,6 +461,22 @@ NS_IMETHODIMP nsDocShell::GetInterface(const nsIID & aIID, void **aSink)
mContentViewer->GetDOMDocument((nsIDOMDocument **) aSink); mContentViewer->GetDOMDocument((nsIDOMDocument **) aSink);
return *aSink ? NS_OK : NS_NOINTERFACE; return *aSink ? NS_OK : NS_NOINTERFACE;
} }
else if (aIID.Equals(NS_GET_IID(nsIApplicationCacheContainer)) &&
NS_SUCCEEDED(EnsureContentViewer())) {
*aSink = nsnull;
// Return the toplevel document as an
// nsIApplicationCacheContainer.
nsCOMPtr<nsIDocShellTreeItem> rootItem;
GetSameTypeRootTreeItem(getter_AddRefs(rootItem));
nsCOMPtr<nsIDOMDocument> domDoc = do_GetInterface(rootItem);
NS_ASSERTION(domDoc, "Should have a document.");
if (!domDoc)
return NS_ERROR_NO_INTERFACE;
return domDoc->QueryInterface(aIID, aSink);
}
else if (aIID.Equals(NS_GET_IID(nsIPrompt)) && else if (aIID.Equals(NS_GET_IID(nsIPrompt)) &&
NS_SUCCEEDED(EnsureScriptEnvironment())) { NS_SUCCEEDED(EnsureScriptEnvironment())) {
nsresult rv; nsresult rv;
@ -7275,6 +7294,15 @@ nsDocShell::DoURILoad(nsIURI * aURI,
if (aFirstParty) { if (aFirstParty) {
// tag first party URL loads // tag first party URL loads
loadFlags |= nsIChannel::LOAD_INITIAL_DOCUMENT_URI; loadFlags |= nsIChannel::LOAD_INITIAL_DOCUMENT_URI;
// Toplevel document loads in domains with the offline-app
// permission should check for an associated application
// cache.
nsCOMPtr<nsIDocShellTreeItem> root;
GetSameTypeRootTreeItem(getter_AddRefs(root));
if (root == this && NS_OfflineAppAllowed(aURI)) {
loadFlags |= nsICachingChannel::LOAD_CHECK_OFFLINE_CACHE;
}
} }
if (mLoadType == LOAD_ERROR_PAGE) { if (mLoadType == LOAD_ERROR_PAGE) {

View File

@ -59,6 +59,7 @@ REQUIRES = xpcom \
nkcache \ nkcache \
pref \ pref \
prefetch \ prefetch \
docshell \
widget \ widget \
xpconnect \ xpconnect \
$(NULL) $(NULL)

View File

@ -45,7 +45,6 @@
#include "nsNetCID.h" #include "nsNetCID.h"
#include "nsICacheSession.h" #include "nsICacheSession.h"
#include "nsICacheService.h" #include "nsICacheService.h"
#include "nsIOfflineCacheSession.h"
#include "nsIOfflineCacheUpdate.h" #include "nsIOfflineCacheUpdate.h"
#include "nsIDOMLoadStatus.h" #include "nsIDOMLoadStatus.h"
#include "nsAutoPtr.h" #include "nsAutoPtr.h"
@ -55,6 +54,7 @@
#include "nsIPrivateDOMEvent.h" #include "nsIPrivateDOMEvent.h"
#include "nsIObserverService.h" #include "nsIObserverService.h"
#include "nsIScriptGlobalObject.h" #include "nsIScriptGlobalObject.h"
#include "nsIWebNavigation.h"
// Event names // Event names
@ -73,7 +73,7 @@ static const char kMaxEntriesPref[] = "offline.max_site_resources";
#define DEFAULT_MAX_ENTRIES 100 #define DEFAULT_MAX_ENTRIES 100
#define MAX_URI_LENGTH 2048 #define MAX_URI_LENGTH 2048
static nsCAutoString gCachedAsciiHost; static nsCAutoString gCachedManifestSpec;
static char **gCachedKeys = nsnull; static char **gCachedKeys = nsnull;
static PRUint32 gCachedKeysCount = 0; static PRUint32 gCachedKeysCount = 0;
@ -85,7 +85,6 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMOfflineResourceList)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMOfflineResourceList) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMOfflineResourceList)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mWindow) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mWindow)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mCacheSession)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mCacheUpdate) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mCacheUpdate)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mCheckingListeners) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mCheckingListeners)
@ -114,7 +113,6 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMOfflineResourceList) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMOfflineResourceList)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mWindow) NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mWindow)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mCacheSession)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mCacheUpdate) NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mCacheUpdate)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mCheckingListeners) NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mCheckingListeners)
@ -181,36 +179,20 @@ nsDOMOfflineResourceList::Init()
return NS_ERROR_DOM_INVALID_STATE_ERR; return NS_ERROR_DOM_INVALID_STATE_ERR;
} }
mManifestURI->GetAsciiSpec(mManifestSpec);
nsresult rv = nsContentUtils::GetSecurityManager()-> nsresult rv = nsContentUtils::GetSecurityManager()->
CheckSameOriginURI(mManifestURI, mDocumentURI, PR_TRUE); CheckSameOriginURI(mManifestURI, mDocumentURI, PR_TRUE);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
// Dynamically-managed resources are stored as a separate ownership list // Dynamically-managed resources are stored as a separate ownership list
// from the manifest. // from the manifest.
rv = mManifestURI->GetAsciiSpec(mDynamicOwnerSpec);
NS_ENSURE_SUCCESS(rv, rv);
mDynamicOwnerSpec.Append("#dynamic");
nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(mDocumentURI); nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(mDocumentURI);
if (!innerURI) if (!innerURI)
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
rv = innerURI->GetAsciiHost(mAsciiHost); mApplicationCacheService =
NS_ENSURE_SUCCESS(rv, rv); do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv);
nsCOMPtr<nsICacheService> serv = do_GetService(NS_CACHESERVICE_CONTRACTID,
&rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsICacheSession> session;
rv = serv->CreateSession("HTTP-offline",
nsICache::STORE_OFFLINE,
nsICache::STREAM_BASED,
getter_AddRefs(session));
NS_ENSURE_SUCCESS(rv, rv);
mCacheSession = do_QueryInterface(session, &rv);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
// Check for in-progress cache updates // Check for in-progress cache updates
@ -319,6 +301,11 @@ nsDOMOfflineResourceList::Add(const nsAString& aURI)
return NS_ERROR_DOM_SECURITY_ERR; return NS_ERROR_DOM_SECURITY_ERR;
} }
nsCOMPtr<nsIApplicationCache> appCache = GetDocumentAppCache();
if (!appCache) {
return NS_ERROR_DOM_INVALID_STATE_ERR;
}
if (aURI.Length() > MAX_URI_LENGTH) return NS_ERROR_DOM_BAD_URI; if (aURI.Length() > MAX_URI_LENGTH) return NS_ERROR_DOM_BAD_URI;
// this will fail if the URI is not absolute // this will fail if the URI is not absolute
@ -374,16 +361,24 @@ nsDOMOfflineResourceList::Remove(const nsAString& aURI)
return NS_ERROR_DOM_SECURITY_ERR; return NS_ERROR_DOM_SECURITY_ERR;
} }
nsCOMPtr<nsIApplicationCache> appCache = GetDocumentAppCache();
if (!appCache) {
return NS_ERROR_DOM_INVALID_STATE_ERR;
}
nsCAutoString key; nsCAutoString key;
rv = GetCacheKey(aURI, key); rv = GetCacheKey(aURI, key);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
ClearCachedKeys(); ClearCachedKeys();
rv = mCacheSession->RemoveOwnedKey(mAsciiHost, mDynamicOwnerSpec, key); // XXX: This is a race condition. remove() is specced to remove
NS_ENSURE_SUCCESS(rv, rv); // from the currently associated application cache, but if this
// happens during an update (or after an update, if we haven't
// swapped yet), that remove() will be lost when the next update is
// finished. Need to bring this issue up.
rv = mCacheSession->EvictUnownedEntries(); rv = appCache->UnmarkEntry(key, nsIApplicationCache::ITEM_DYNAMIC);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
return NS_OK; return NS_OK;
@ -394,29 +389,41 @@ nsDOMOfflineResourceList::GetStatus(PRUint16 *aStatus)
{ {
nsresult rv = Init(); nsresult rv = Init();
// It is OK to check the status without a manifest attribute (you'll // Init may fail with INVALID_STATE_ERR if there is no manifest URI.
// just get "uncached"). // The status attribute should not throw that exception, convert it
if (rv == NS_ERROR_DOM_INVALID_STATE_ERR && !mManifestURI) { // to an UNCACHED.
if (rv == NS_ERROR_DOM_INVALID_STATE_ERR ||
!nsContentUtils::OfflineAppAllowed(mDocumentURI)) {
*aStatus = nsIDOMOfflineResourceList::UNCACHED; *aStatus = nsIDOMOfflineResourceList::UNCACHED;
return NS_OK; return NS_OK;
} }
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
// If there is an update in process, use its status.
if (mCacheUpdate) { if (mCacheUpdate) {
return mCacheUpdate->GetStatus(aStatus); rv = mCacheUpdate->GetStatus(aStatus);
if (NS_SUCCEEDED(rv) && *aStatus != nsIDOMOfflineResourceList::IDLE) {
return NS_OK;
}
} }
// XXX: the spec allows either UNCACHED or IDLE, depending on whether // If this object is not associated with a cache, return UNCACHED
// the application is associated with a specific offline cache. Until nsCOMPtr<nsIApplicationCache> appCache = GetDocumentAppCache();
// we have versioned application caches, the best approximation is if (!appCache) {
// probably IDLE if the offline-app permission is set, and UNCACHED *aStatus = nsIDOMOfflineResourceList::UNCACHED;
// otherwise. return NS_OK;
}
if (nsContentUtils::OfflineAppAllowed(mDocumentURI)) { nsCOMPtr<nsIApplicationCache> activeCache;
rv = mApplicationCacheService->GetActiveCache(mManifestSpec,
getter_AddRefs(activeCache));
NS_ENSURE_SUCCESS(rv, rv);
if (appCache == activeCache) {
*aStatus = nsIDOMOfflineResourceList::IDLE; *aStatus = nsIDOMOfflineResourceList::IDLE;
} else { } else {
*aStatus = nsIDOMOfflineResourceList::UNCACHED; *aStatus = nsIDOMOfflineResourceList::UPDATEREADY;
} }
return NS_OK; return NS_OK;
@ -454,7 +461,35 @@ nsDOMOfflineResourceList::SwapCache()
return NS_ERROR_DOM_SECURITY_ERR; return NS_ERROR_DOM_SECURITY_ERR;
} }
return NS_ERROR_NOT_IMPLEMENTED; if (!mToplevel) {
return NS_ERROR_DOM_INVALID_STATE_ERR;
}
nsCOMPtr<nsIApplicationCacheService> serv =
do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIApplicationCache> currentAppCache = GetDocumentAppCache();
nsCOMPtr<nsIApplicationCache> newAppCache;
rv = serv->GetActiveCache(mManifestSpec, getter_AddRefs(newAppCache));
NS_ENSURE_SUCCESS(rv, rv);
if (!newAppCache || newAppCache == currentAppCache) {
return NS_ERROR_DOM_INVALID_STATE_ERR;
}
ClearCachedKeys();
nsCOMPtr<nsIApplicationCacheContainer> appCacheContainer =
GetDocumentAppCacheContainer();
if (appCacheContainer) {
rv = appCacheContainer->SetApplicationCache(newAppCache);
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
} }
// //
@ -944,20 +979,25 @@ nsDOMOfflineResourceList::UpdateCompleted(nsIOfflineCacheUpdate *aUpdate)
return NS_OK; return NS_OK;
} }
PRBool partial;
mCacheUpdate->GetPartial(&partial);
PRBool isUpgrade;
mCacheUpdate->GetIsUpgrade(&isUpgrade);
PRBool succeeded; PRBool succeeded;
nsresult rv = mCacheUpdate->GetSucceeded(&succeeded); nsresult rv = mCacheUpdate->GetSucceeded(&succeeded);
mCacheUpdate->RemoveObserver(this); mCacheUpdate->RemoveObserver(this);
mCacheUpdate = nsnull; mCacheUpdate = nsnull;
if (NS_SUCCEEDED(rv) && succeeded) { if (NS_SUCCEEDED(rv) && succeeded && !partial) {
// XXX: the spec requires a "cached" event to be sent if this is a if (isUpgrade) {
// first-time cache attempt, and "updateready" if this page was loaded SendEvent(NS_LITERAL_STRING(UPDATEREADY_STR),
// from an existing application cache. Since we don't have versioned mOnUpdateReadyListener, mUpdateReadyListeners);
// application caches yet, basically each update acts like a first-time } else {
// update, so we'll always fire "cached" for now. SendEvent(NS_LITERAL_STRING(CACHED_STR),
SendEvent(NS_LITERAL_STRING(CACHED_STR), mOnCachedListener, mCachedListeners);
mOnCachedListener, mCachedListeners); }
} }
return NS_OK; return NS_OK;
@ -981,19 +1021,61 @@ nsDOMOfflineResourceList::GetCacheKey(nsIURI *aURI, nsCString &aKey)
return NS_OK; return NS_OK;
} }
already_AddRefed<nsIApplicationCacheContainer>
nsDOMOfflineResourceList::GetDocumentAppCacheContainer()
{
nsCOMPtr<nsIDOMWindow> window = do_QueryReferent(mWindow);
if (!window) {
return nsnull;
}
nsCOMPtr<nsIWebNavigation> webnav = do_GetInterface(window);
if (!webnav) {
return nsnull;
}
nsCOMPtr<nsIApplicationCacheContainer> appCacheContainer =
do_GetInterface(webnav);
return appCacheContainer.forget();
}
already_AddRefed<nsIApplicationCache>
nsDOMOfflineResourceList::GetDocumentAppCache()
{
nsCOMPtr<nsIApplicationCacheContainer> appCacheContainer =
GetDocumentAppCacheContainer();
if (appCacheContainer) {
nsCOMPtr<nsIApplicationCache> applicationCache;
appCacheContainer->GetApplicationCache(
getter_AddRefs(applicationCache));
return applicationCache.forget();
}
return nsnull;
}
nsresult nsresult
nsDOMOfflineResourceList::CacheKeys() nsDOMOfflineResourceList::CacheKeys()
{ {
if (gCachedKeys && mAsciiHost == gCachedAsciiHost) if (gCachedKeys && mManifestSpec == gCachedManifestSpec)
return NS_OK; return NS_OK;
ClearCachedKeys(); ClearCachedKeys();
nsresult rv = mCacheSession->GetOwnedKeys(mAsciiHost, mDynamicOwnerSpec, nsCOMPtr<nsIApplicationCache> appCache;
&gCachedKeysCount, &gCachedKeys); mApplicationCacheService->GetActiveCache(mManifestSpec,
getter_AddRefs(appCache));
if (!appCache) {
return NS_ERROR_DOM_INVALID_STATE_ERR;
}
nsresult rv = appCache->GatherEntries(nsIApplicationCache::ITEM_DYNAMIC,
&gCachedKeysCount, &gCachedKeys);
if (NS_SUCCEEDED(rv)) if (NS_SUCCEEDED(rv))
gCachedAsciiHost = mAsciiHost; gCachedManifestSpec = mManifestSpec;
return rv; return rv;
} }
@ -1007,8 +1089,6 @@ nsDOMOfflineResourceList::ClearCachedKeys()
gCachedKeysCount = 0; gCachedKeysCount = 0;
} }
gCachedAsciiHost = ""; gCachedManifestSpec = "";
} }

View File

@ -41,7 +41,9 @@
#include "nscore.h" #include "nscore.h"
#include "nsIDOMOfflineResourceList.h" #include "nsIDOMOfflineResourceList.h"
#include "nsIOfflineCacheSession.h" #include "nsIApplicationCache.h"
#include "nsIApplicationCacheContainer.h"
#include "nsIApplicationCacheService.h"
#include "nsIOfflineCacheUpdate.h" #include "nsIOfflineCacheUpdate.h"
#include "nsTArray.h" #include "nsTArray.h"
#include "nsString.h" #include "nsString.h"
@ -97,6 +99,9 @@ private:
nsresult UpdateAdded(nsIOfflineCacheUpdate *aUpdate); nsresult UpdateAdded(nsIOfflineCacheUpdate *aUpdate);
nsresult UpdateCompleted(nsIOfflineCacheUpdate *aUpdate); nsresult UpdateCompleted(nsIOfflineCacheUpdate *aUpdate);
already_AddRefed<nsIApplicationCacheContainer> GetDocumentAppCacheContainer();
already_AddRefed<nsIApplicationCache> GetDocumentAppCache();
nsresult GetCacheKey(const nsAString &aURI, nsCString &aKey); nsresult GetCacheKey(const nsAString &aURI, nsCString &aKey);
nsresult GetCacheKey(nsIURI *aURI, nsCString &aKey); nsresult GetCacheKey(nsIURI *aURI, nsCString &aKey);
@ -105,13 +110,15 @@ private:
PRBool mInitialized; PRBool mInitialized;
PRBool mToplevel; PRBool mToplevel;
nsCOMPtr<nsIURI> mManifestURI; nsCOMPtr<nsIURI> mManifestURI;
// AsciiSpec of mManifestURI
nsCString mManifestSpec;
nsCOMPtr<nsIURI> mDocumentURI; nsCOMPtr<nsIURI> mDocumentURI;
nsCOMPtr<nsIWeakReference> mWindow; nsCOMPtr<nsIWeakReference> mWindow;
nsCOMPtr<nsIOfflineCacheSession> mCacheSession; nsCOMPtr<nsIApplicationCacheService> mApplicationCacheService;
nsCOMPtr<nsIOfflineCacheUpdate> mCacheUpdate; nsCOMPtr<nsIOfflineCacheUpdate> mCacheUpdate;
nsCAutoString mAsciiHost;
nsCAutoString mDynamicOwnerSpec;
nsCOMArray<nsIDOMEventListener> mCheckingListeners; nsCOMArray<nsIDOMEventListener> mCheckingListeners;
nsCOMArray<nsIDOMEventListener> mErrorListeners; nsCOMArray<nsIDOMEventListener> mErrorListeners;

View File

@ -51,14 +51,9 @@ fetch: function(callback)
var url = this.urls.shift(); var url = this.urls.shift();
var self = this; var self = this;
var cacheService = Cc["@mozilla.org/network/cache-service;1"] var cacheSession = OfflineTest.getActiveSession();
.getService(Ci.nsICacheService);
var cacheSession = cacheService.createSession("HTTP-offline",
Ci.nsICache.STORE_OFFLINE,
true);
cacheSession.asyncOpenCacheEntry(url, Ci.nsICache.ACCESS_READ, this); cacheSession.asyncOpenCacheEntry(url, Ci.nsICache.ACCESS_READ, this);
} }
}; };
var OfflineTest = { var OfflineTest = {
@ -167,29 +162,13 @@ isnot: function(a, b, name)
clear: function() clear: function()
{ {
// Clear the ownership list // XXX: maybe we should just wipe out the entire disk cache.
var cacheService = Cc["@mozilla.org/network/cache-service;1"] var appCacheService = Cc["@mozilla.org/network/application-cache-service;1"]
.getService(Ci.nsICacheService); .getService(Ci.nsIApplicationCacheService);
var cacheSession = cacheService.createSession("HTTP-offline", var applicationCache = this.getActiveCache();
Ci.nsICache.STORE_OFFLINE, if (applicationCache) {
true) applicationCache.discard();
.QueryInterface(Ci.nsIOfflineCacheSession); }
// Get the asciiHost from the page URL
var locationURI = Cc["@mozilla.org/network/standard-url;1"]
.createInstance(Ci.nsIURI);
locationURI.spec = window.location.href;
var asciiHost = locationURI.asciiHost;
// Clear manifest-owned urls
cacheSession.setOwnedKeys(asciiHost,
this.getManifestUrl() + "#manifest", 0, []);
// Clear dynamically-owned urls
cacheSession.setOwnedKeys(asciiHost,
this.getManifestUrl() + "#dynamic", 0, []);
cacheSession.evictUnownedEntries();
}, },
failEvent: function(e) failEvent: function(e)
@ -203,11 +182,7 @@ waitForAdd: function(url, onFinished) {
// Check every half second for ten seconds. // Check every half second for ten seconds.
var numChecks = 20; var numChecks = 20;
var waitFunc = function() { var waitFunc = function() {
var cacheService = Cc["@mozilla.org/network/cache-service;1"] var cacheSession = OfflineTest.getActiveSession();
.getService(Ci.nsICacheService);
var cacheSession = cacheService.createSession("HTTP-offline",
Ci.nsICache.STORE_OFFLINE,
true);
var entry; var entry;
try { try {
var entry = cacheSession.openCacheEntry(url, Ci.nsICache.ACCESS_READ, true); var entry = cacheSession.openCacheEntry(url, Ci.nsICache.ACCESS_READ, true);
@ -236,6 +211,27 @@ getManifestUrl: function()
return window.top.document.documentElement.getAttribute("manifest"); return window.top.document.documentElement.getAttribute("manifest");
}, },
getActiveCache: function()
{
// Note that this is the current active cache in the cache stack, not the
// one associated with this window.
var serv = Cc["@mozilla.org/network/application-cache-service;1"]
.getService(Ci.nsIApplicationCacheService);
return serv.getActiveCache(this.getManifestUrl());
},
getActiveSession: function()
{
var cache = this.getActiveCache();
if (!cache) return null;
var cacheService = Cc["@mozilla.org/network/cache-service;1"]
.getService(Ci.nsICacheService);
return cacheService.createSession(cache.clientID,
Ci.nsICache.STORE_OFFLINE,
true);
},
priv: function(func) priv: function(func)
{ {
var self = this; var self = this;
@ -247,11 +243,16 @@ priv: function(func)
checkCache: function(url, expectEntry) checkCache: function(url, expectEntry)
{ {
var cacheService = Cc["@mozilla.org/network/cache-service;1"] var cacheSession = this.getActiveSession();
.getService(Ci.nsICacheService); if (!cacheSession) {
var cacheSession = cacheService.createSession("HTTP-offline", if (expectEntry) {
Ci.nsICache.STORE_OFFLINE, this.ok(false, url + " should exist in the offline cache");
true); } else {
this.ok(true, url + " should not exist in the offline cache");
}
return;
}
try { try {
var entry = cacheSession.openCacheEntry(url, Ci.nsICache.ACCESS_READ, false); var entry = cacheSession.openCacheEntry(url, Ci.nsICache.ACCESS_READ, false);
if (expectEntry) { if (expectEntry) {
@ -267,7 +268,7 @@ checkCache: function(url, expectEntry)
} else { } else {
this.ok(true, url + " should not exist in the offline cache"); this.ok(true, url + " should not exist in the offline cache");
} }
} else if (e.result == NS_ERROR_CACHE_WAIT_FOR_VALIDATION) { } else if (e.result == NS_ERROR_CACHE_KEY_WAIT_FOR_VALIDATION) {
// There was a cache key that we couldn't access yet, that's good enough. // There was a cache key that we couldn't access yet, that's good enough.
if (expectEntry) { if (expectEntry) {
this.ok(true, url + " should exist in the offline cache"); this.ok(true, url + " should exist in the offline cache");

View File

@ -51,8 +51,9 @@ function manifestUpdated()
gCacheContents = contents; gCacheContents = contents;
// Now make sure applicationCache.update() does what we expect. // Now make sure applicationCache.update() does what we expect.
applicationCache.oncached = OfflineTest.priv(manifestUpdatedAgain); applicationCache.onupdateready = OfflineTest.priv(manifestUpdatedAgain);
applicationCache.onnoupdate = failAndFinish; applicationCache.onnoupdate = failAndFinish;
applicationCache.oncached = failAndFinish;
gGotChecking = false; gGotChecking = false;
gGotDownloading = false; gGotDownloading = false;

View File

@ -36,26 +36,6 @@ function addFinished()
OfflineTest.finish(); OfflineTest.finish();
} }
function secondUpdate()
{
OfflineTest.ok(gGotChecking, "Should get a checking event");
OfflineTest.ok(gGotDownloading, "Should get a downloading event");
// The document that requested the manifest should be in the cache
OfflineTest.checkCache(window.location.href, true);
OfflineTest.checkCache("http://localhost:8888/tests/SimpleTest/SimpleTest.js", true);
OfflineTest.checkCache("http://localhost:8888/MochiKit/packed.js", true);
OfflineTest.checkCache("http://localhost:8888/tests/dom/tests/mochitest/ajax/offline/offlineTests.js", true);
// Now add a file using the applicationCache API
applicationCache.add("http://localhost:8888/tests/SimpleTest/EventUtils.js");
// Wait for the add() to be downloaded
OfflineTest.waitForAdd("http://localhost:8888/tests/SimpleTest/EventUtils.js",
OfflineTest.priv(addFinished));
}
function manifestUpdated() function manifestUpdated()
{ {
OfflineTest.ok(gGotChecking, "Should get a checking event"); OfflineTest.ok(gGotChecking, "Should get a checking event");
@ -76,26 +56,18 @@ function manifestUpdated()
OfflineTest.checkCache("https://localhost:8888/MochiKit/packed.js", false); OfflineTest.checkCache("https://localhost:8888/MochiKit/packed.js", false);
OfflineTest.checkCache("bad:/uri/invalid", false); OfflineTest.checkCache("bad:/uri/invalid", false);
// Remove items from the cache. applicationCache.swapCache();
OfflineTest.clear();
// Make sure OfflineTest.clear() properly removed the items // XXX: make sure that the previous version went away after the swapCache().
OfflineTest.checkCache("http://localhost:8888/tests/SimpleTest/SimpleTest.js", false); // Now add a file using the applicationCache API
OfflineTest.checkCache("http://localhost:8888/MochiKit/packed.js", false); applicationCache.add("http://localhost:8888/tests/SimpleTest/EventUtils.js");
OfflineTest.checkCache("http://localhost:8888/tests/dom/tests/mochitest/ajax/offline/offlineTests.js", false);
OfflineTest.checkCache("http://localhost:8888/tests/SimpleTest/EventUtils.js",
false);
// Now make sure applicationCache.update() does what we expect. // Wait for the add() to be downloaded
applicationCache.oncached = OfflineTest.priv(secondUpdate); OfflineTest.waitForAdd("http://localhost:8888/tests/SimpleTest/EventUtils.js",
OfflineTest.priv(addFinished));
gGotChecking = false;
gGotDownloading = false;
applicationCache.update();
} }
if (OfflineTest.setup()) { if (OfflineTest.setup()) {
OfflineTest.ok(applicationCache instanceof EventTarget, OfflineTest.ok(applicationCache instanceof EventTarget,
"applicationCache should be an event target"); "applicationCache should be an event target");

View File

@ -61,6 +61,9 @@ SDK_XPIDLSRCS = \
$(NULL) $(NULL)
XPIDLSRCS = \ XPIDLSRCS = \
nsIApplicationCache.idl \
nsIApplicationCacheContainer.idl \
nsIApplicationCacheService.idl \
nsIAuthInformation.idl \ nsIAuthInformation.idl \
nsIAuthPrompt.idl \ nsIAuthPrompt.idl \
nsIAuthPrompt2.idl \ nsIAuthPrompt2.idl \

View File

@ -0,0 +1,143 @@
/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* ***** 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 nsIApplicationCache.idl.
*
* The Initial Developer of the Original Code is
* Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Dave Camp <dcamp@mozilla.com>
*
* 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 "nsISupports.idl"
/**
* Application caches store resources for offline use. Each
* application cache has a unique client ID for use with
* nsICacheService::openSession() to access the cache's entries.
*
* Each entry in the application cache can be marked with a set of
* types, as discussed in the WHAT-WG offline applications
* specification.
*
* All application caches with the same group ID belong to a cache
* group. Each group has one "active" cache that will service future
* loads. Inactive caches will be removed from the cache when they are
* no longer referenced.
*/
[scriptable, uuid(a9cfbeef-8f8d-49c3-b899-303390383ae9)]
interface nsIApplicationCache : nsISupports
{
/**
* Entries in an application cache can be marked as one or more of
* the following types.
*/
/* This item is the application manifest. */
const unsigned long ITEM_MANIFEST = 1 << 0;
/* This item was explicitly listed in the application manifest. */
const unsigned long ITEM_EXPLICIT = 1 << 1;
/* This item was navigated in a toplevel browsing context, and
* named this cache's group as its manifest. */
const unsigned long ITEM_IMPLICIT = 1 << 2;
/* This item was added by the dynamic scripting API */
const unsigned long ITEM_DYNAMIC = 1 << 3;
/* This item was listed in the application manifest, but named a
* different cache group as its manifest. */
const unsigned long ITEM_FOREIGN = 1 << 4;
/* This item was listed as a fallback entry. */
const unsigned long ITEM_FALLBACK = 1 << 5;
/* This item matched an opportunistic cache namespace and was
* cached accordingly. */
const unsigned long ITEM_OPPORTUNISTIC = 1 << 6;
/**
* The group ID for this cache group. This is the URI of the
* manifest file.
**/
readonly attribute ACString groupID;
/**
* The client ID for this application cache. Clients can open a
* session with nsICacheService::createSession() using this client
* ID and a storage policy of STORE_OFFLINE to access this cache.
*/
readonly attribute ACString clientID;
/**
* TRUE if the cache is the active cache for this group.
*/
readonly attribute boolean active;
/**
* Makes this cache the active application cache for this group.
* Future loads associated with this group will come from this
* cache. Other caches from this cache group will be deactivated.
*/
void activate();
/**
* Discard this application cache. Removes all cached resources
* for this cache. If this is the active application cache for the
* group, the group will be removed.
*/
void discard();
/**
* Adds item types to a given entry.
*/
void markEntry(in ACString key, in unsigned long typeBits);
/**
* Removes types from a given entry. If the resulting entry has
* no types left, the entry is removed.
*/
void unmarkEntry(in ACString key, in unsigned long typeBits);
/**
* Gets the types for a given entry.
*/
unsigned long getTypes(in ACString key);
/**
* Returns any entries in the application cache whose type matches
* one or more of the bits in typeBits.
*/
void gatherEntries(in PRUint32 typeBits,
out unsigned long count,
[array, size_is(count)] out string keys);
};

View File

@ -0,0 +1,52 @@
/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* ***** 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 nsIApplicationCache.idl.
*
* The Initial Developer of the Original Code is
* Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Dave Camp <dcamp@mozilla.com>
*
* 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 "nsISupports.idl"
interface nsIApplicationCache;
/**
* Interface used by objects that can be associated with an
* application cache.
*/
[scriptable, uuid(bbb80700-1f7f-4258-aff4-1743cc5a7d23)]
interface nsIApplicationCacheContainer : nsISupports
{
attribute nsIApplicationCache applicationCache;
};

View File

@ -0,0 +1,71 @@
/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* ***** 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 nsIApplicationCache.idl.
*
* The Initial Developer of the Original Code is
* Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Dave Camp <dcamp@mozilla.com>
*
* 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 "nsISupports.idl"
interface nsIApplicationCache;
/**
* The application cache service manages the set of application cache
* groups.
*/
[scriptable, uuid(3f411c68-0d17-420d-839a-6355eea877b9)]
interface nsIApplicationCacheService : nsISupports
{
/**
* Create a new, empty application cache for the given cache
* group.
*/
nsIApplicationCache createApplicationCache(in ACString group);
/**
* Get an application cache object for the given client ID.
*/
nsIApplicationCache getApplicationCache(in ACString clientID);
/**
* Get the currently active cache object for a cache group.
*/
nsIApplicationCache getActiveCache(in ACString group);
/**
* Try to find the best application cache to serve a resource.
*/
nsIApplicationCache chooseApplicationCache(in ACString key);
};

View File

@ -41,6 +41,7 @@
#include "nsISupports.idl" #include "nsISupports.idl"
interface nsIURI; interface nsIURI;
interface nsIPrefBranch;
/** /**
* nsINetUtil provides various network-related utility methods. * nsINetUtil provides various network-related utility methods.
@ -220,3 +221,25 @@ interface nsINetUtil : nsISupports
out long aCharsetStart, out long aCharsetStart,
out long aCharsetEnd); out long aCharsetEnd);
}; };
/**
* nsINetUtil methods added in mozilla 1.9.1.
*
* XXX bug 451255: Merge this up in to nsINetUtil as soon as possible.
*/
[scriptable, uuid(da76ab60-2ec5-4649-84c4-4954a08f6d37)]
interface nsINetUtil_MOZILLA_1_9_1 : nsISupports
{
/**
* Checks whether a document at the given URI should have access
* to the offline cache.
* @param aURI
* The URI to check
* @param aPrefBranch
* The pref branch to use to check the
* offline-apps.allow_by_default pref. If not specified,
* the pref service will be used.
*/
boolean OfflineAppAllowed(in nsIURI aURI,
in nsIPrefBranch aPrefBranch);
};

View File

@ -1441,4 +1441,28 @@ NS_GetFinalChannelURI(nsIChannel* channel, nsIURI** uri)
return channel->GetOriginalURI(uri); return channel->GetOriginalURI(uri);
} }
/**
* Checks whether a document at the given URI should have access
* to the offline cache.
* @param uri
* The URI to check
* @param prefBranch
* The pref branch to use to check the
* offline-apps.allow_by_default pref. If not specified,
* the pref service will be used.
*/
inline PRBool
NS_OfflineAppAllowed(nsIURI *aURI, nsIPrefBranch *aPrefBranch = nsnull)
{
nsresult rv;
nsCOMPtr<nsINetUtil_MOZILLA_1_9_1> util = do_GetIOService(&rv);
NS_ENSURE_SUCCESS(rv, PR_FALSE);
PRBool allowed;
rv = util->OfflineAppAllowed(aURI, aPrefBranch, &allowed);
NS_ENSURE_SUCCESS(rv, PR_FALSE);
return allowed;
}
#endif // !nsNetUtil_h__ #endif // !nsNetUtil_h__

View File

@ -70,6 +70,7 @@
#include "nsINestedURI.h" #include "nsINestedURI.h"
#include "nsNetUtil.h" #include "nsNetUtil.h"
#include "nsThreadUtils.h" #include "nsThreadUtils.h"
#include "nsIPermissionManager.h"
#if defined(XP_WIN) #if defined(XP_WIN)
#include "nsNativeConnectionHelper.h" #include "nsNativeConnectionHelper.h"
@ -278,12 +279,13 @@ nsIOService::GetInstance() {
return gIOService; return gIOService;
} }
NS_IMPL_THREADSAFE_ISUPPORTS5(nsIOService, NS_IMPL_THREADSAFE_ISUPPORTS6(nsIOService,
nsIIOService, nsIIOService,
nsIIOService2, nsIIOService2,
nsINetUtil, nsINetUtil,
nsIObserver, nsIObserver,
nsISupportsWeakReference) nsISupportsWeakReference,
nsINetUtil_MOZILLA_1_9_1)
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -983,3 +985,58 @@ nsIOService::ExtractCharsetFromContentType(const nsACString &aTypeHeader,
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP
nsIOService::OfflineAppAllowed(nsIURI *aURI,
nsIPrefBranch *aPrefBranch,
PRBool *aAllowed)
{
*aAllowed = PR_FALSE;
nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(aURI);
if (!innerURI)
return NS_OK;
// only http and https applications can use offline APIs.
PRBool match;
nsresult rv = innerURI->SchemeIs("http", &match);
NS_ENSURE_SUCCESS(rv, rv);
if (!match) {
rv = innerURI->SchemeIs("https", &match);
NS_ENSURE_SUCCESS(rv, rv);
if (!match) {
return NS_OK;
}
}
nsCOMPtr<nsIPermissionManager> permissionManager =
do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
if (!permissionManager) {
return NS_OK;
}
PRUint32 perm;
permissionManager->TestExactPermission(innerURI, "offline-app", &perm);
if (perm == nsIPermissionManager::UNKNOWN_ACTION) {
nsCOMPtr<nsIPrefBranch> branch = aPrefBranch;
if (!branch) {
branch = do_GetService(NS_PREFSERVICE_CONTRACTID);
}
if (branch) {
rv = branch->GetBoolPref("offline-apps.allow_by_default", aAllowed);
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}
if (perm == nsIPermissionManager::DENY_ACTION) {
return NS_OK;
}
*aAllowed = PR_TRUE;
return NS_OK;
}

View File

@ -77,6 +77,7 @@ class nsIPrefBranch2;
class nsIOService : public nsIIOService2 class nsIOService : public nsIIOService2
, public nsIObserver , public nsIObserver
, public nsINetUtil , public nsINetUtil
, public nsINetUtil_MOZILLA_1_9_1
, public nsSupportsWeakReference , public nsSupportsWeakReference
{ {
public: public:
@ -85,6 +86,7 @@ public:
NS_DECL_NSIIOSERVICE2 NS_DECL_NSIIOSERVICE2
NS_DECL_NSIOBSERVER NS_DECL_NSIOBSERVER
NS_DECL_NSINETUTIL NS_DECL_NSINETUTIL
NS_DECL_NSINETUTIL_MOZILLA_1_9_1
// Gets the singleton instance of the IO Service, creating it as needed // Gets the singleton instance of the IO Service, creating it as needed
// Returns nsnull on out of memory or failure to initialize. // Returns nsnull on out of memory or failure to initialize.

View File

@ -113,6 +113,11 @@ SHARED_LIBRARY_LIBS += \
LOCAL_INCLUDES += -I$(srcdir)/../cookie/src LOCAL_INCLUDES += -I$(srcdir)/../cookie/src
endif endif
ifdef MOZ_STORAGE
REQUIRES += storage
DEFINES += -DNECKO_OFFLINE_CACHE
endif
EXTRA_DSO_LDOPTS = \ EXTRA_DSO_LDOPTS = \
$(LIBS_DIR) \ $(LIBS_DIR) \
$(EXTRA_DSO_LIBS) \ $(EXTRA_DSO_LIBS) \

View File

@ -465,6 +465,18 @@
{0x8f, 0xbc, 0xbe, 0xe8, 0xf9, 0x22, 0xea, 0x67} \ {0x8f, 0xbc, 0xbe, 0xe8, 0xf9, 0x22, 0xea, 0x67} \
} }
// service implementing nsIApplicationCacheService.
#define NS_APPLICATIONCACHESERVICE_CLASSNAME \
"nsApplicationCacheService"
#define NS_APPLICATIONCACHESERVICE_CONTRACTID \
"@mozilla.org/network/application-cache-service;1"
#define NS_APPLICATIONCACHESERVICE_CID \
{ /* 02bf7a2a-39d8-4a23-a50c-2cbb085ab7a5 */ \
0x02bf7a2a, \
0x39d8, \
0x4a23, \
{0xa5, 0x0c, 0x2c, 0xbb, 0x08, 0x5a, 0xb7, 0xa5} \
}
/****************************************************************************** /******************************************************************************
* netwerk/protocol/http/ classes * netwerk/protocol/http/ classes

View File

@ -56,6 +56,7 @@
#include "nsMIMEInputStream.h" #include "nsMIMEInputStream.h"
#include "nsSOCKSSocketProvider.h" #include "nsSOCKSSocketProvider.h"
#include "nsCacheService.h" #include "nsCacheService.h"
#include "nsDiskCacheDeviceSQL.h"
#include "nsMimeTypes.h" #include "nsMimeTypes.h"
#include "nsNetStrings.h" #include "nsNetStrings.h"
@ -187,7 +188,11 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsSafeAboutProtocolHandler)
#include "nsAboutCacheEntry.h" #include "nsAboutCacheEntry.h"
NS_GENERIC_FACTORY_CONSTRUCTOR(nsAboutCacheEntry) NS_GENERIC_FACTORY_CONSTRUCTOR(nsAboutCacheEntry)
#endif #endif
#ifdef NECKO_OFFLINE_CACHE
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsOfflineCacheDevice, nsOfflineCacheDevice::GetInstance)
#endif
#ifdef NECKO_PROTOCOL_file #ifdef NECKO_PROTOCOL_file
// file // file
#include "nsFileProtocolHandler.h" #include "nsFileProtocolHandler.h"
@ -1045,6 +1050,14 @@ static const nsModuleComponentInfo gNetModuleInfo[] = {
nsCacheService::Create nsCacheService::Create
}, },
#ifdef NECKO_OFFLINE_CACHE
{ NS_APPLICATIONCACHESERVICE_CLASSNAME,
NS_APPLICATIONCACHESERVICE_CID,
NS_APPLICATIONCACHESERVICE_CONTRACTID,
nsOfflineCacheDeviceConstructor
},
#endif
#ifdef NECKO_COOKIES #ifdef NECKO_COOKIES
{ NS_COOKIEMANAGER_CLASSNAME, { NS_COOKIEMANAGER_CLASSNAME,
NS_COOKIEMANAGER_CID, NS_COOKIEMANAGER_CID,

View File

@ -53,7 +53,6 @@ XPIDLSRCS = \
nsICacheService.idl \ nsICacheService.idl \
nsICacheSession.idl \ nsICacheSession.idl \
nsICacheVisitor.idl \ nsICacheVisitor.idl \
nsIOfflineCacheSession.idl \
$(NULL) $(NULL)
include $(topsrcdir)/config/rules.mk include $(topsrcdir)/config/rules.mk

View File

@ -86,11 +86,7 @@ interface nsICacheService : nsISupports
void evictEntries(in nsCacheStoragePolicy storagePolicy); void evictEntries(in nsCacheStoragePolicy storagePolicy);
/** /**
* Return a unique, temporary cache client ID. * This method is deprecated and will throw NS_ERROR_NOT_IMPLEMENTED.
*
* 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); ACString createTemporaryClientID(in nsCacheStoragePolicy storagePolicy);
}; };

View File

@ -1,233 +0,0 @@
/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* ***** 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 nsIOfflineCacheSession.idl.
*
* The Initial Developer of the Original Code is
* Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2007
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Dave Camp <dcamp@mozilla.com>
*
* 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 "nsISupports.idl"
#include "nsICache.idl"
/**
* 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(3a33e268-4175-4440-a933-89d461c86c5f)]
interface nsIOfflineCacheSession : nsISupports
{
/**
* 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
*/
void getOwnerDomains(out unsigned long count,
[array, size_is(count)]out string domains);
/**
* Gets the list of owner URIs associated with a domain.
*
* @param ownerAsciiDomain
* The domain to query
* !! IMPORTANT !! : This must be ascii encoded host - nsIURI.asciiHost
* @param count
* The number of uris returned
* @param uris
* The uris in this domain that own resources
*/
void getOwnerURIs(in ACString ownerAsciiDomain,
out unsigned long count,
[array, size_is(count)]out string uris);
/**
* Sets the resources owned by a given domain/URI pair.
*
* Setting a list will remove any resources previously owned by this
* domain/URI pair.
*
* A key can be added while there is no associated entry. When
* an entry is created with this key, it will be owned by the
* domain/URI pair.
*
* @param ownerAsciiDomain
* The domain that owns the resources
* !! IMPORTANT !! : This must be ascii encoded host - nsIURI.asciiHost
* @param ownerAsciiKey
* The specific key that owns the resources. You may use
* ascii encoded URI spec of the owner - nsIURI.asciiSpec.
* This can be empty if none 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 ownerAsciiDomain,
in ACString ownerAsciiKey,
in unsigned long count,
[array, size_is(count)]in string keys);
/**
* Gets the list of resources owned by a given domain/URI pair.
*
* @param ownerAsciiDomain
* The domain that owns the resources
* !! IMPORTANT !! : This must be ascii encoded host - nsIURI.asciiHost
* @param ownerAsciiKey
* The specific key that owns the resources. You may use
* ascii encoded URI spec of the owner - nsIURI.asciiSpec.
* This can be empty if none 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 ownerAsciiDomain,
in ACString ownerAsciiKey,
out unsigned long count,
[array, size_is(count)]out string keys);
/**
* Adds an owned key to a domain/URI pair.
*
* A key can be added while there is no associated entry. When
* an entry is created with this key, it will be owned by the
* domain/URI pair.
*
* @param ownerAsciiDomain
* The domain that owns the resources
* !! IMPORTANT !! : This must be ascii encoded host - nsIURI.asciiHost
* @param ownerAsciiKey
* The specific key that owns the resources. You may use
* ascii encoded URI spec of the owner - nsIURI.asciiSpec.
* This can be empty if none specifically owns the resources.
* @param key
* The key to add.
*/
void addOwnedKey(in ACString ownerAsciiDomain,
in ACString ownerAsciiKey,
in ACString key);
/**
* Removes an owned key from a domain/URI pair.
*
* If the key does not exist, an NS_ERROR_NOT_AVAILABLE exception
* will be thrown.
*
* @param ownerAsciiDomain
* The domain that owns the resources
* !! IMPORTANT !! : This must be ascii encoded host - nsIURI.asciiHost
* @param ownerAsciiKey
* The specific key that owns the resources. You may use
* ascii encoded URI spec of the owner - nsIURI.asciiSpec.
* This can be empty if none specifically owns the resources.
* @param key The key to remove.
*/
void removeOwnedKey(in ACString ownerAsciiDomain,
in ACString ownerAsciiKey,
in ACString key);
/**
* Checks whether a key is owned by a given domain/URI pair.
*
* @param ownerAsciiDomain
* The domain that owns the resources
* !! IMPORTANT !! : This must be ascii encoded host - nsIURI.asciiHost
* @param ownerAsciiKey
* The specific key that owns the resources. You may use
* ascii encoded URI spec of the owner - nsIURI.asciiSpec.
* This can be empty if none specifically owns the resources.
* @param key The key to check
*/
boolean keyIsOwned(in ACString ownerAsciiDomain,
in ACString ownerAsciiKey,
in ACString key);
/**
* Remove all keys owned by a domain, including keys owned by
* a specific URI.
*
* @param ownerAsciiDomain
* The domain for which keys should be removed
* !! IMPORTANT !! : This must be ascii encoded host - nsIURI.asciiHost
*/
void clearKeysOwnedByDomain(in ACString ownerAsciiDomain);
/**
* Get the number of bytes used in the cache by a domain.
*
* @param domain The domain to check.
*/
unsigned long getDomainUsage(in ACString ownerDomain);
/**
* 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

@ -663,8 +663,7 @@ nsCacheService::Shutdown()
#endif // !NECKO_DISK_CACHE #endif // !NECKO_DISK_CACHE
#ifdef NECKO_OFFLINE_CACHE #ifdef NECKO_OFFLINE_CACHE
delete mOfflineDevice; NS_IF_RELEASE(mOfflineDevice);
mOfflineDevice = nsnull;
#endif // !NECKO_OFFLINE_CACHE #endif // !NECKO_OFFLINE_CACHE
#if defined(NECKO_DISK_CACHE) && defined(PR_LOGGING) #if defined(NECKO_DISK_CACHE) && defined(PR_LOGGING)
@ -829,245 +828,6 @@ nsCacheService::IsStorageEnabledForPolicy_Locked(nsCacheStoragePolicy storagePo
return PR_FALSE; return PR_FALSE;
} }
nsresult nsCacheService::GetOfflineOwnerDomains(nsCacheSession * session,
PRUint32 * count,
char *** domains)
{
#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->GetOwnerDomains(session->ClientID()->get(),
count, domains);
#else // !NECKO_OFFLINE_CACHE
return NS_ERROR_NOT_IMPLEMENTED;
#endif
}
nsresult nsCacheService::GetOfflineOwnerURIs(nsCacheSession * session,
const nsACString & ownerDomain,
PRUint32 * count,
char *** uris)
{
#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->GetOwnerURIs(session->ClientID()->get(),
ownerDomain, count, uris);
#else // !NECKO_OFFLINE_CACHE
return NS_ERROR_NOT_IMPLEMENTED;
#endif
}
nsresult
nsCacheService::SetOfflineOwnedKeys(nsCacheSession * session,
const nsACString & ownerDomain,
const nsACString & ownerURI,
PRUint32 count,
const char ** keys)
{
#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->SetOwnedKeys(session->ClientID()->get(),
ownerDomain,
ownerURI,
count,
keys);
#else // !NECKO_OFFLINE_CACHE
return NS_ERROR_NOT_IMPLEMENTED;
#endif
}
nsresult nsCacheService::GetOfflineOwnedKeys(nsCacheSession * session,
const nsACString & ownerDomain,
const nsACString & ownerURI,
PRUint32 * count,
char *** keys)
{
#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->GetOwnedKeys(session->ClientID()->get(),
ownerDomain,
ownerURI,
count,
keys);
#else // !NECKO_OFFLINE_CACHE
return NS_ERROR_NOT_IMPLEMENTED;
#endif
}
nsresult nsCacheService::AddOfflineOwnedKey(nsCacheSession * session,
const nsACString & ownerDomain,
const nsACString & ownerURI,
const nsACString & key)
{
#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->AddOwnedKey(session->ClientID()->get(),
ownerDomain,
ownerURI,
key);
#else // !NECKO_OFFLINE_CACHE
return NS_ERROR_NOT_IMPLEMENTED;
#endif
}
nsresult nsCacheService::RemoveOfflineOwnedKey(nsCacheSession * session,
const nsACString & ownerDomain,
const nsACString & ownerURI,
const nsACString & key)
{
#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->RemoveOwnedKey(session->ClientID()->get(),
ownerDomain,
ownerURI,
key);
#else // !NECKO_OFFLINE_CACHE
return NS_ERROR_NOT_IMPLEMENTED;
#endif
}
nsresult nsCacheService::OfflineKeyIsOwned(nsCacheSession * session,
const nsACString & ownerDomain,
const nsACString & ownerURI,
const nsACString & key,
PRBool *isOwned)
{
#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->KeyIsOwned(session->ClientID()->get(),
ownerDomain,
ownerURI,
key,
isOwned);
#else // !NECKO_OFFLINE_CACHE
return NS_ERROR_NOT_IMPLEMENTED;
#endif
}
nsresult nsCacheService::ClearOfflineKeysOwnedByDomain(nsCacheSession * session,
const nsACString & domain)
{
#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->ClearKeysOwnedByDomain(session->ClientID()->get(),
domain);
#else // !NECKO_OFFLINE_CACHE
return NS_ERROR_NOT_IMPLEMENTED;
#endif
}
nsresult nsCacheService::GetOfflineDomainUsage(nsCacheSession * session,
const nsACString & domain,
PRUint32 * usage)
{
#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->GetDomainUsage(session->ClientID()->get(),
domain,
usage);
#else // !NECKO_OFFLINE_CACHE
return NS_ERROR_NOT_IMPLEMENTED;
#endif
}
nsresult nsCacheService::EvictUnownedOfflineEntries(nsCacheSession * session)
{
#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->EvictUnownedEntries(session->ClientID()->get());
#else // !NECKO_OFFLINE_CACHE
return NS_ERROR_NOT_IMPLEMENTED;
#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_IMETHODIMP nsCacheService::VisitEntries(nsICacheVisitor *visitor)
{ {
NS_ENSURE_ARG_POINTER(visitor); NS_ENSURE_ARG_POINTER(visitor);
@ -1124,20 +884,7 @@ NS_IMETHODIMP nsCacheService::EvictEntries(nsCacheStoragePolicy storagePolicy)
NS_IMETHODIMP nsCacheService::CreateTemporaryClientID(nsCacheStoragePolicy storagePolicy, NS_IMETHODIMP nsCacheService::CreateTemporaryClientID(nsCacheStoragePolicy storagePolicy,
nsACString &clientID) 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; return NS_ERROR_NOT_IMPLEMENTED;
#endif
} }
/** /**
@ -1188,6 +935,8 @@ nsCacheService::CreateOfflineDevice()
mOfflineDevice = new nsOfflineCacheDevice; mOfflineDevice = new nsOfflineCacheDevice;
if (!mOfflineDevice) return NS_ERROR_OUT_OF_MEMORY; if (!mOfflineDevice) return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(mOfflineDevice);
// set the preferences // set the preferences
mOfflineDevice->SetCacheParentDirectory( mOfflineDevice->SetCacheParentDirectory(
mObserver->OfflineCacheParentDirectory()); mObserver->OfflineCacheParentDirectory());
@ -1199,8 +948,7 @@ nsCacheService::CreateOfflineDevice()
CACHE_LOG_DEBUG((" - disabling offline cache for this session.\n")); CACHE_LOG_DEBUG((" - disabling offline cache for this session.\n"));
mEnableOfflineDevice = PR_FALSE; mEnableOfflineDevice = PR_FALSE;
delete mOfflineDevice; NS_RELEASE(mOfflineDevice);
mOfflineDevice = nsnull;
} }
return rv; return rv;
#else // !NECKO_DISK_CACHE #else // !NECKO_DISK_CACHE

View File

@ -97,54 +97,6 @@ public:
static nsresult IsStorageEnabledForPolicy(nsCacheStoragePolicy storagePolicy, static nsresult IsStorageEnabledForPolicy(nsCacheStoragePolicy storagePolicy,
PRBool * result); PRBool * result);
static nsresult GetOfflineOwnerDomains(nsCacheSession * session,
PRUint32 * count,
char *** domains);
static nsresult GetOfflineOwnerURIs(nsCacheSession * session,
const nsACString & ownerDomain,
PRUint32 * count,
char *** uris);
static nsresult SetOfflineOwnedKeys(nsCacheSession * session,
const nsACString & ownerDomain,
const nsACString & ownerUri,
PRUint32 count,
const char ** keys);
static nsresult GetOfflineOwnedKeys(nsCacheSession * session,
const nsACString & ownerDomain,
const nsACString & ownerURI,
PRUint32 * count,
char *** keys);
static nsresult AddOfflineOwnedKey(nsCacheSession * session,
const nsACString & ownerDomain,
const nsACString & ownerURI,
const nsACString & key);
static nsresult RemoveOfflineOwnedKey(nsCacheSession * session,
const nsACString & ownerDomain,
const nsACString & ownerURI,
const nsACString & key);
static nsresult OfflineKeyIsOwned(nsCacheSession * session,
const nsACString & ownerDomain,
const nsACString & ownerURI,
const nsACString & key,
PRBool * isOwned);
static nsresult ClearOfflineKeysOwnedByDomain(nsCacheSession * session,
const nsACString & domain);
static nsresult GetOfflineDomainUsage(nsCacheSession * session,
const nsACString & domain,
PRUint32 * usage);
static nsresult EvictUnownedOfflineEntries(nsCacheSession * session);
static nsresult MergeTemporaryClientID(nsCacheSession * session,
const nsACString & fromClientID);
/** /**
* Methods called by nsCacheEntryDescriptor * Methods called by nsCacheEntryDescriptor
*/ */
@ -208,6 +160,7 @@ public:
void Shutdown(); void Shutdown();
private: private:
friend class nsCacheServiceAutoLock; friend class nsCacheServiceAutoLock;
friend class nsOfflineCacheDevice;
/** /**
* Internal Methods * Internal Methods

View File

@ -44,15 +44,7 @@
#include "nsCacheService.h" #include "nsCacheService.h"
#include "nsCRT.h" #include "nsCRT.h"
NS_IMPL_ADDREF(nsCacheSession) NS_IMPL_ISUPPORTS1(nsCacheSession, nsICacheSession)
NS_IMPL_RELEASE(nsCacheSession)
NS_INTERFACE_MAP_BEGIN(nsCacheSession)
NS_INTERFACE_MAP_ENTRY(nsICacheSession)
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(
nsIOfflineCacheSession, (StoragePolicy() == nsICache::STORE_OFFLINE))
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsICacheSession)
NS_INTERFACE_MAP_END
nsCacheSession::nsCacheSession(const char * clientID, nsCacheSession::nsCacheSession(const char * clientID,
nsCacheStoragePolicy storagePolicy, nsCacheStoragePolicy storagePolicy,
@ -135,75 +127,3 @@ NS_IMETHODIMP nsCacheSession::IsStorageEnabled(PRBool *result)
return nsCacheService::IsStorageEnabledForPolicy(StoragePolicy(), result); return nsCacheService::IsStorageEnabledForPolicy(StoragePolicy(), result);
} }
NS_IMETHODIMP nsCacheSession::GetOwnerDomains(PRUint32 * count,
char *** domains)
{
return nsCacheService::GetOfflineOwnerDomains(this, count, domains);
}
NS_IMETHODIMP nsCacheSession::GetOwnerURIs(const nsACString & domain,
PRUint32 * count,
char *** uris)
{
return nsCacheService::GetOfflineOwnerURIs(this, domain, count, uris);
}
NS_IMETHODIMP nsCacheSession::SetOwnedKeys(const nsACString & domain,
const nsACString & uri,
PRUint32 count,
const char ** keys)
{
return nsCacheService::SetOfflineOwnedKeys(this, domain, uri, count, keys);
}
NS_IMETHODIMP nsCacheSession::GetOwnedKeys(const nsACString & domain,
const nsACString & uri,
PRUint32 * count,
char *** keys)
{
return nsCacheService::GetOfflineOwnedKeys(this, domain, uri, count, keys);
}
NS_IMETHODIMP nsCacheSession::AddOwnedKey(const nsACString & domain,
const nsACString & uri,
const nsACString & key)
{
return nsCacheService::AddOfflineOwnedKey(this, domain, uri, key);
}
NS_IMETHODIMP nsCacheSession::RemoveOwnedKey(const nsACString & domain,
const nsACString & uri,
const nsACString & key)
{
return nsCacheService::RemoveOfflineOwnedKey(this, domain, uri, key);
}
NS_IMETHODIMP nsCacheSession::KeyIsOwned(const nsACString & domain,
const nsACString & uri,
const nsACString & key,
PRBool * isOwned)
{
return nsCacheService::OfflineKeyIsOwned(this, domain, uri, key, isOwned);
}
NS_IMETHODIMP nsCacheSession::ClearKeysOwnedByDomain(const nsACString & domain)
{
return nsCacheService::ClearOfflineKeysOwnedByDomain(this, domain);
}
NS_IMETHODIMP nsCacheSession::GetDomainUsage(const nsACString & domain,
PRUint32 *usage)
{
return nsCacheService::GetOfflineDomainUsage(this, domain, usage);
}
NS_IMETHODIMP nsCacheSession::EvictUnownedEntries()
{
return nsCacheService::EvictUnownedOfflineEntries(this);
}
NS_IMETHODIMP nsCacheSession::MergeTemporaryClientID(const nsACString& fromClientID)
{
return nsCacheService::MergeTemporaryClientID(this, fromClientID);
}

View File

@ -46,16 +46,13 @@
#include "nspr.h" #include "nspr.h"
#include "nsError.h" #include "nsError.h"
#include "nsICacheSession.h" #include "nsICacheSession.h"
#include "nsIOfflineCacheSession.h"
#include "nsString.h" #include "nsString.h"
class nsCacheSession : public nsICacheSession class nsCacheSession : public nsICacheSession
, public nsIOfflineCacheSession
{ {
public: public:
NS_DECL_ISUPPORTS NS_DECL_ISUPPORTS
NS_DECL_NSICACHESESSION NS_DECL_NSICACHESESSION
NS_DECL_NSIOFFLINECACHESESSION
nsCacheSession(const char * clientID, nsCacheStoragePolicy storagePolicy, PRBool streamBased); nsCacheSession(const char * clientID, nsCacheStoragePolicy storagePolicy, PRBool streamBased);
virtual ~nsCacheSession(); virtual ~nsCacheSession();

View File

@ -43,6 +43,7 @@
#include "nsNetUtil.h" #include "nsNetUtil.h"
#include "nsAutoPtr.h" #include "nsAutoPtr.h"
#include "nsEscape.h"
#include "nsString.h" #include "nsString.h"
#include "nsPrintfCString.h" #include "nsPrintfCString.h"
#include "nsCRT.h" #include "nsCRT.h"
@ -57,6 +58,7 @@
#include "nsISeekableStream.h" #include "nsISeekableStream.h"
static const char OFFLINE_CACHE_DEVICE_ID[] = { "offline" }; static const char OFFLINE_CACHE_DEVICE_ID[] = { "offline" };
static NS_DEFINE_CID(kCacheServiceCID, NS_CACHESERVICE_CID);
#define LOG(args) CACHE_LOG_DEBUG(args) #define LOG(args) CACHE_LOG_DEBUG(args)
@ -540,10 +542,144 @@ nsOfflineCacheEntryInfo::GetDataSize(PRUint32 *aDataSize)
} }
/******************************************************************************
* nsApplicationCache
*/
class nsApplicationCache : public nsIApplicationCache
, public nsSupportsWeakReference
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIAPPLICATIONCACHE
nsApplicationCache(nsOfflineCacheDevice *device,
const nsACString &group,
const nsACString &clientID);
virtual ~nsApplicationCache();
void MarkInvalid() { mValid = PR_FALSE; }
private:
nsRefPtr<nsOfflineCacheDevice> mDevice;
nsCString mGroup;
nsCString mClientID;
PRBool mValid;
};
NS_IMPL_ISUPPORTS2(nsApplicationCache,
nsIApplicationCache,
nsISupportsWeakReference)
nsApplicationCache::nsApplicationCache(nsOfflineCacheDevice *device,
const nsACString &group,
const nsACString &clientID)
: mDevice(device)
, mGroup(group)
, mClientID(clientID)
, mValid(PR_TRUE)
{
}
nsApplicationCache::~nsApplicationCache()
{
mDevice->mCaches.Remove(mClientID);
// If this isn't an active cache anymore, it can be destroyed.
if (mValid && !mDevice->IsActiveCache(mGroup, mClientID))
Discard();
}
NS_IMETHODIMP
nsApplicationCache::GetGroupID(nsACString &out)
{
out = mGroup;
return NS_OK;
}
NS_IMETHODIMP
nsApplicationCache::GetClientID(nsACString &out)
{
out = mClientID;
return NS_OK;
}
NS_IMETHODIMP
nsApplicationCache::GetActive(PRBool *out)
{
*out = mDevice->IsActiveCache(mGroup, mClientID);
return NS_OK;
}
NS_IMETHODIMP
nsApplicationCache::Activate()
{
NS_ENSURE_TRUE(mValid, NS_ERROR_NOT_AVAILABLE);
mDevice->ActivateCache(mGroup, mClientID);
return NS_OK;
}
NS_IMETHODIMP
nsApplicationCache::Discard()
{
NS_ENSURE_TRUE(mValid, NS_ERROR_NOT_AVAILABLE);
mValid = PR_FALSE;
if (mDevice->IsActiveCache(mGroup, mClientID))
{
mDevice->DeactivateGroup(mGroup);
}
return mDevice->EvictEntries(mClientID.get());
}
NS_IMETHODIMP
nsApplicationCache::MarkEntry(const nsACString &key,
PRUint32 typeBits)
{
NS_ENSURE_TRUE(mValid, NS_ERROR_NOT_AVAILABLE);
return mDevice->MarkEntry(mClientID, key, typeBits);
}
NS_IMETHODIMP
nsApplicationCache::UnmarkEntry(const nsACString &key,
PRUint32 typeBits)
{
NS_ENSURE_TRUE(mValid, NS_ERROR_NOT_AVAILABLE);
return mDevice->UnmarkEntry(mClientID, key, typeBits);
}
NS_IMETHODIMP
nsApplicationCache::GetTypes(const nsACString &key,
PRUint32 *typeBits)
{
NS_ENSURE_TRUE(mValid, NS_ERROR_NOT_AVAILABLE);
return mDevice->GetTypes(mClientID, key, typeBits);
}
NS_IMETHODIMP
nsApplicationCache::GatherEntries(PRUint32 typeBits,
PRUint32 * count,
char *** keys)
{
NS_ENSURE_TRUE(mValid, NS_ERROR_NOT_AVAILABLE);
return mDevice->GatherEntries(mClientID, typeBits, count, keys);
}
/****************************************************************************** /******************************************************************************
* nsOfflineCacheDevice * nsOfflineCacheDevice
*/ */
NS_IMPL_ISUPPORTS1(nsOfflineCacheDevice, nsIApplicationCacheService)
nsOfflineCacheDevice::nsOfflineCacheDevice() nsOfflineCacheDevice::nsOfflineCacheDevice()
: mDB(nsnull) : mDB(nsnull)
, mCacheCapacity(0) , mCacheCapacity(0)
@ -697,6 +833,23 @@ nsOfflineCacheDevice::DeleteData(nsCacheEntry *entry)
* nsCacheDevice implementation * nsCacheDevice implementation
*/ */
/* static */
nsOfflineCacheDevice *
nsOfflineCacheDevice::GetInstance()
{
nsresult rv;
nsCOMPtr<nsICacheService> serv = do_GetService(kCacheServiceCID, &rv);
NS_ENSURE_SUCCESS(rv, nsnull);
nsICacheService *iservice = static_cast<nsICacheService*>(serv.get());
nsCacheService *cacheService = static_cast<nsCacheService*>(iservice);
rv = cacheService->CreateOfflineDevice();
NS_ENSURE_SUCCESS(rv, nsnull);
NS_IF_ADDREF(cacheService->mOfflineDevice);
return cacheService->mOfflineDevice;
}
nsresult nsresult
nsOfflineCacheDevice::Init() nsOfflineCacheDevice::Init()
{ {
@ -735,8 +888,8 @@ nsOfflineCacheDevice::Init()
// "Generation" is the data file generation number. // "Generation" is the data file generation number.
// "Flags" is a bit-field indicating the state of the entry. // "Flags" is a bit-field indicating the state of the entry.
// //
mDB->ExecuteSimpleSQL( rv = mDB->ExecuteSimpleSQL(
NS_LITERAL_CSTRING("CREATE TABLE moz_cache (\n" NS_LITERAL_CSTRING("CREATE TABLE IF NOT EXISTS moz_cache (\n"
" ClientID TEXT,\n" " ClientID TEXT,\n"
" Key TEXT,\n" " Key TEXT,\n"
" MetaData BLOB,\n" " MetaData BLOB,\n"
@ -746,24 +899,38 @@ nsOfflineCacheDevice::Init()
" FetchCount INTEGER,\n" " FetchCount INTEGER,\n"
" LastFetched INTEGER,\n" " LastFetched INTEGER,\n"
" LastModified INTEGER,\n" " LastModified INTEGER,\n"
" ExpirationTime INTEGER\n" " ExpirationTime INTEGER,\n"
" ItemType INTEGER DEFAULT 0\n"
");\n")); ");\n"));
// maybe the table already exists, so don't bother checking for errors. NS_ENSURE_SUCCESS(rv, rv);
// build the ownership table // Databases from 1.9.0 don't have the ItemType column. Add the column
// here, but don't worry about failures (the column probably already exists)
mDB->ExecuteSimpleSQL( mDB->ExecuteSimpleSQL(
NS_LITERAL_CSTRING("CREATE TABLE moz_cache_owners (\n" NS_LITERAL_CSTRING("ALTER TABLE moz_cache ADD ItemType INTEGER DEFAULT 0"));
" ClientID TEXT,\n"
" Domain TEXT,\n" // Create the table for storing cache groups. All actions on
" URI TEXT,\n" // moz_cache_groups use the GroupID, so use it as the primary key.
" Key TEXT\n" rv = mDB->ExecuteSimpleSQL(
NS_LITERAL_CSTRING("CREATE TABLE IF NOT EXISTS moz_cache_groups (\n"
" GroupID TEXT PRIMARY KEY,\n"
" ActiveClientID TEXT\n"
");\n")); ");\n"));
// maybe the table already exists, so don't bother checking for errors. NS_ENSURE_SUCCESS(rv, rv);
mDB->ExecuteSimpleSQL( // Databases from 1.9.0 have a moz_cache_index that should be dropped
NS_LITERAL_CSTRING("CREATE UNIQUE INDEX moz_cache_index" rv = mDB->ExecuteSimpleSQL(
" ON moz_cache (ClientID, Key);")); NS_LITERAL_CSTRING("DROP INDEX IF EXISTS moz_cache_index"));
// maybe the index already exists, so don't bother checking for errors. NS_ENSURE_SUCCESS(rv, rv);
// Key/ClientID pairs should be unique in the database. All queries
// against moz_cache use the Key (which is also the most unique), so
// use it as the primary key for this index.
rv = mDB->ExecuteSimpleSQL(
NS_LITERAL_CSTRING("CREATE UNIQUE INDEX IF NOT EXISTS "
" moz_cache_key_clientid_index"
" ON moz_cache (Key, ClientID);"));
NS_ENSURE_SUCCESS(rv, rv);
mEvictionFunction = new nsOfflineCacheEvictionFunction(this); mEvictionFunction = new nsOfflineCacheEvictionFunction(this);
if (!mEvictionFunction) return NS_ERROR_OUT_OF_MEMORY; if (!mEvictionFunction) return NS_ERROR_OUT_OF_MEMORY;
@ -779,7 +946,8 @@ nsOfflineCacheDevice::Init()
statement (aStatement), sql (aSql) {} statement (aStatement), sql (aSql) {}
} prepared[] = { } prepared[] = {
StatementSql ( mStatement_CacheSize, "SELECT Sum(DataSize) from moz_cache;" ), StatementSql ( mStatement_CacheSize, "SELECT Sum(DataSize) from moz_cache;" ),
StatementSql ( mStatement_DomainSize, "SELECT Sum(moz_cache.DataSize) FROM moz_cache INNER 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=?;" ), // XXX bug 442810: Restore the ability to monitor individual cache usage
StatementSql ( mStatement_DomainSize, "SELECT 0;"),
StatementSql ( mStatement_EntryCount, "SELECT count(*) 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_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_UpdateEntrySize, "UPDATE moz_cache SET DataSize = ? WHERE ClientID = ? AND Key = ?;" ),
@ -787,25 +955,25 @@ nsOfflineCacheDevice::Init()
StatementSql ( mStatement_DeleteEntry, "DELETE FROM moz_cache 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_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_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_MarkEntry, "UPDATE moz_cache SET ItemType = (ItemType | ?) WHERE ClientID = ? AND Key = ?;" ),
StatementSql ( mStatement_ClearDomain, "DELETE FROM moz_cache_owners WHERE ClientID = ? AND Domain = ?;" ), StatementSql ( mStatement_UnmarkEntry, "UPDATE moz_cache SET ItemType = (ItemType & ~?) WHERE ClientID = ? AND Key = ?;" ),
StatementSql ( mStatement_AddOwnership, "INSERT INTO moz_cache_owners (ClientID, Domain, URI, Key) VALUES (?, ?, ?, ?);" ), StatementSql ( mStatement_GetTypes, "SELECT ItemType FROM moz_cache WHERE ClientID = ? AND Key = ?;"),
StatementSql ( mStatement_CheckOwnership, "SELECT Key From moz_cache_owners WHERE ClientID = ? AND Domain = ? AND URI = ? AND Key = ?;" ), StatementSql ( mStatement_CleanupUnmarked, "DELETE FROM moz_cache WHERE ClientID = ? AND Key = ? AND ItemType = 0;" ),
StatementSql ( mStatement_ListOwned, "SELECT Key FROM moz_cache_owners WHERE ClientID = ? AND Domain = ? AND URI = ?;" ), StatementSql ( mStatement_GatherEntries, "SELECT Key FROM moz_cache WHERE ClientID = ? AND (ItemType & ?) > 0;" ),
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_ActivateClient, "INSERT OR REPLACE INTO moz_cache_groups (GroupID, ActiveClientID) VALUES (?, ?);" ),
StatementSql ( mStatement_ListOwnerURIs, "SELECT DISTINCT URI FROM moz_cache_owners WHERE ClientID = ? AND Domain = ?;"), StatementSql ( mStatement_DeactivateGroup, "DELETE FROM moz_cache_groups WHERE GroupID = ?;" ),
StatementSql ( mStatement_DeleteConflicts, "DELETE FROM moz_cache WHERE rowid IN (SELECT old.rowid FROM moz_cache AS old, moz_cache AS new WHERE old.Key = new.Key AND old.ClientID = ? AND new.ClientID = ?)"), StatementSql ( mStatement_FindClient, "SELECT ClientID FROM moz_cache WHERE Key = ? ORDER BY LastFetched DESC, LastModified DESC;")
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) for (PRUint32 i = 0; NS_SUCCEEDED(rv) && i < NS_ARRAY_LENGTH(prepared); ++i)
{ {
rv |= mDB->CreateStatement(nsDependentCString(prepared[i].sql), LOG(("Creating statement: %s\n", prepared[i].sql));
getter_AddRefs(prepared[i].statement));
rv = mDB->CreateStatement(nsDependentCString(prepared[i].sql),
getter_AddRefs(prepared[i].statement));
NS_ENSURE_SUCCESS(rv, rv);
} }
NS_ENSURE_SUCCESS(rv, rv);
// Clear up any dangling active flags // Clear up any dangling active flags
rv = mDB->ExecuteSimpleSQL( rv = mDB->ExecuteSimpleSQL(
@ -814,24 +982,83 @@ nsOfflineCacheDevice::Init()
" WHERE (Flags & 1);")); " WHERE (Flags & 1);"));
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
// Clear up dangling temporary sessions rv = InitActiveCaches();
EvictionObserver evictionObserver(mDB, mEvictionFunction);
rv = mDB->ExecuteSimpleSQL(
NS_LITERAL_CSTRING("DELETE FROM moz_cache"
" WHERE (ClientID GLOB \"TempClient*\")"));
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
evictionObserver.Apply(); return NS_OK;
}
nsresult
nsOfflineCacheDevice::InitActiveCaches()
{
NS_ENSURE_TRUE(mCaches.Init(), NS_ERROR_OUT_OF_MEMORY);
NS_ENSURE_TRUE(mActiveCachesByGroup.Init(), NS_ERROR_OUT_OF_MEMORY);
nsresult rv = mActiveCaches.Init(5);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<mozIStorageStatement> statement;
rv =
mDB->CreateStatement(NS_LITERAL_CSTRING("SELECT GroupID, ActiveClientID"
" FROM moz_cache_groups"),
getter_AddRefs(statement));
NS_ENSURE_SUCCESS(rv, rv);
PRBool hasRows;
rv = statement->ExecuteStep(&hasRows);
NS_ENSURE_SUCCESS(rv, rv);
while (hasRows)
{
nsCAutoString group;
statement->GetUTF8String(0, group);
nsCString clientID;
statement->GetUTF8String(1, clientID);
mActiveCaches.Put(clientID);
mActiveCachesByGroup.Put(group, new nsCString(clientID));
rv = statement->ExecuteStep(&hasRows);
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK; return NS_OK;
} }
/* static */
PLDHashOperator
nsOfflineCacheDevice::ShutdownApplicationCache(const nsACString &key,
nsIWeakReference *weakRef,
void *ctx)
{
nsCOMPtr<nsIApplicationCache> obj = do_QueryReferent(weakRef);
if (obj)
{
nsApplicationCache *appCache = static_cast<nsApplicationCache*>(obj.get());
appCache->MarkInvalid();
}
return PL_DHASH_NEXT;
}
nsresult nsresult
nsOfflineCacheDevice::Shutdown() nsOfflineCacheDevice::Shutdown()
{ {
NS_ENSURE_TRUE(mDB, NS_ERROR_NOT_INITIALIZED); NS_ENSURE_TRUE(mDB, NS_ERROR_NOT_INITIALIZED);
if (mCaches.IsInitialized())
mCaches.EnumerateRead(ShutdownApplicationCache, this);
EvictionObserver evictionObserver(mDB, mEvictionFunction);
// Delete all rows whose clientID is not an active clientID.
nsresult rv = mDB->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DELETE FROM moz_cache WHERE rowid IN (SELECT moz_cache.rowid FROM moz_cache LEFT OUTER JOIN moz_cache_groups ON (moz_cache.ClientID = moz_cache_groups.ActiveClientID) WHERE moz_cache_groups.GroupID ISNULL)"));
if (NS_FAILED(rv))
NS_WARNING("Failed to clean up unused application caches.");
else
evictionObserver.Apply();
mDB = 0; mDB = 0;
mEvictionFunction = 0; mEvictionFunction = 0;
@ -1026,7 +1253,6 @@ nsOfflineCacheDevice::BindEntry(nsCacheEntry *entry)
PRBool hasRows; PRBool hasRows;
rv = statement->ExecuteStep(&hasRows); rv = statement->ExecuteStep(&hasRows);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
NS_ASSERTION(!hasRows, "INSERT should not result in output"); NS_ASSERTION(!hasRows, "INSERT should not result in output");
entry->SetData(binding); entry->SetData(binding);
@ -1250,23 +1476,25 @@ nsOfflineCacheDevice::EvictEntries(const char *clientID)
// so we can delete the corresponding data file. // so we can delete the corresponding data file.
EvictionObserver evictionObserver(mDB, mEvictionFunction); EvictionObserver evictionObserver(mDB, mEvictionFunction);
const char *deleteCmd; nsCOMPtr<mozIStorageStatement> statement;
nsresult rv;
if (clientID) if (clientID)
{ {
deleteCmd = rv = mDB->CreateStatement(NS_LITERAL_CSTRING("DELETE FROM moz_cache WHERE ClientID=? AND Flags = 0;"),
PR_smprintf("DELETE FROM moz_cache WHERE ClientID=%q AND Flags=0;", getter_AddRefs(statement));
clientID); NS_ENSURE_SUCCESS(rv, rv);
if (!deleteCmd)
return NS_ERROR_OUT_OF_MEMORY; rv = statement->BindUTF8StringParameter(0, nsDependentCString(clientID));
NS_ENSURE_SUCCESS(rv, rv);
} }
else else
{ {
deleteCmd = "DELETE FROM moz_cache WHERE Flags = 0;"; rv = mDB->CreateStatement(NS_LITERAL_CSTRING("DELETE FROM moz_cache WHERE Flags = 0;"),
getter_AddRefs(statement));
NS_ENSURE_SUCCESS(rv, rv);
} }
nsresult rv = mDB->ExecuteSimpleSQL(nsDependentCString(deleteCmd)); rv = statement->Execute();
if (clientID)
PR_smprintf_free((char *) deleteCmd);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
evictionObserver.Apply(); evictionObserver.Apply();
@ -1274,6 +1502,110 @@ nsOfflineCacheDevice::EvictEntries(const char *clientID)
return NS_OK; return NS_OK;
} }
nsresult
nsOfflineCacheDevice::MarkEntry(const nsCString &clientID,
const nsACString &key,
PRUint32 typeBits)
{
LOG(("nsOfflineCacheDevice::MarkEntry [cid=%s, key=%s, typeBits=%d]\n",
clientID.get(), PromiseFlatCString(key).get(), typeBits));
AutoResetStatement statement(mStatement_MarkEntry);
nsresult rv = statement->BindInt32Parameter(0, typeBits);
NS_ENSURE_SUCCESS(rv, rv);
rv = statement->BindUTF8StringParameter(1, clientID);
NS_ENSURE_SUCCESS(rv, rv);
rv = statement->BindUTF8StringParameter(2, key);
NS_ENSURE_SUCCESS(rv, rv);
rv = statement->Execute();
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nsresult
nsOfflineCacheDevice::UnmarkEntry(const nsCString &clientID,
const nsACString &key,
PRUint32 typeBits)
{
LOG(("nsOfflineCacheDevice::UnmarkEntry [cid=%s, key=%s, typeBits=%d]\n",
clientID.get(), PromiseFlatCString(key).get(), typeBits));
AutoResetStatement statement(mStatement_UnmarkEntry);
nsresult rv = statement->BindInt32Parameter(0, typeBits);
NS_ENSURE_SUCCESS(rv, rv);
rv = statement->BindUTF8StringParameter(1, clientID);
NS_ENSURE_SUCCESS(rv, rv);
rv = statement->BindUTF8StringParameter(2, key);
NS_ENSURE_SUCCESS(rv, rv);
rv = statement->Execute();
NS_ENSURE_SUCCESS(rv, rv);
// Remove the entry if it is now empty.
EvictionObserver evictionObserver(mDB, mEvictionFunction);
AutoResetStatement cleanupStatement(mStatement_CleanupUnmarked);
rv = cleanupStatement->BindUTF8StringParameter(0, clientID);
NS_ENSURE_SUCCESS(rv, rv);
rv = cleanupStatement->BindUTF8StringParameter(1, key);
NS_ENSURE_SUCCESS(rv, rv);
rv = cleanupStatement->Execute();
NS_ENSURE_SUCCESS(rv, rv);
evictionObserver.Apply();
return NS_OK;
}
nsresult
nsOfflineCacheDevice::GetTypes(const nsCString &clientID,
const nsACString &key,
PRUint32 *typeBits)
{
LOG(("nsOfflineCacheDevice::GetTypes [cid=%s, key=%s]\n",
clientID.get(), PromiseFlatCString(key).get()));
AutoResetStatement statement(mStatement_GetTypes);
nsresult rv = statement->BindUTF8StringParameter(0, clientID);
NS_ENSURE_SUCCESS(rv, rv);
rv = statement->BindUTF8StringParameter(1, key);
NS_ENSURE_SUCCESS(rv, rv);
PRBool hasRows;
rv = statement->ExecuteStep(&hasRows);
NS_ENSURE_SUCCESS(rv, rv);
if (!hasRows)
return NS_ERROR_CACHE_KEY_NOT_FOUND;
*typeBits = statement->AsInt32(0);
return NS_OK;
}
nsresult
nsOfflineCacheDevice::GatherEntries(const nsCString &clientID,
PRUint32 typeBits,
PRUint32 *count,
char ***keys)
{
LOG(("nsOfflineCacheDevice::GatherEntries [cid=%s, typeBits=%X]\n",
clientID.get(), typeBits));
AutoResetStatement statement(mStatement_GatherEntries);
nsresult rv = statement->BindUTF8StringParameter(0, clientID);
NS_ENSURE_SUCCESS(rv, rv);
rv = statement->BindInt32Parameter(1, typeBits);
NS_ENSURE_SUCCESS(rv, rv);
return RunSimpleQuery(mStatement_GatherEntries, 0, count, keys);
}
nsresult nsresult
nsOfflineCacheDevice::RunSimpleQuery(mozIStorageStatement * statement, nsOfflineCacheDevice::RunSimpleQuery(mozIStorageStatement * statement,
PRUint32 resultIndex, PRUint32 resultIndex,
@ -1312,312 +1644,190 @@ nsOfflineCacheDevice::RunSimpleQuery(mozIStorageStatement * statement,
return NS_OK; return NS_OK;
} }
nsresult NS_IMETHODIMP
nsOfflineCacheDevice::GetOwnerDomains(const char * clientID, nsOfflineCacheDevice::CreateApplicationCache(const nsACString &group,
PRUint32 * count, nsIApplicationCache **out)
char *** domains)
{ {
LOG(("nsOfflineCacheDevice::GetOwnerDomains [cid=%s]\n", clientID)); *out = nsnull;
AutoResetStatement statement(mStatement_ListOwnerDomains); nsCString clientID;
nsresult rv = statement->BindUTF8StringParameter( // Some characters are special in the clientID. Escape the groupID
0, nsDependentCString(clientID)); // before putting it in to the client key.
NS_ENSURE_SUCCESS(rv, rv); if (!NS_Escape(nsCString(group), clientID, url_Path)) {
return NS_ERROR_OUT_OF_MEMORY;
return RunSimpleQuery(mStatement_ListOwnerDomains, 0, count, domains);
}
nsresult
nsOfflineCacheDevice::GetOwnerURIs(const char * clientID,
const nsACString & ownerDomain,
PRUint32 * count,
char *** domains)
{
LOG(("nsOfflineCacheDevice::GetOwnerURIs [cid=%s]\n", clientID));
AutoResetStatement statement(mStatement_ListOwnerURIs);
nsresult rv = statement->BindUTF8StringParameter(
0, nsDependentCString(clientID));
rv = statement->BindUTF8StringParameter(
1, ownerDomain);
NS_ENSURE_SUCCESS(rv, rv);
return RunSimpleQuery(mStatement_ListOwnerURIs, 0, count, domains);
}
nsresult
nsOfflineCacheDevice::SetOwnedKeys(const char * clientID,
const nsACString & ownerDomain,
const nsACString & ownerURI,
PRUint32 count,
const char ** keys)
{
LOG(("nsOfflineCacheDevice::SetOwnedKeys [cid=%s]\n", clientID));
mozStorageTransaction transaction(mDB, PR_FALSE);
nsDependentCString clientIDStr(clientID);
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);
rv = clearStatement->Execute();
NS_ENSURE_SUCCESS(rv, rv);
for (PRUint32 i = 0; i < count; i++)
{
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);
} }
return transaction.Commit(); PRTime now = PR_Now();
// Include the timestamp to guarantee uniqueness across runs, and
// the gNextTemporaryClientID for uniqueness within a second.
clientID.Append(nsPrintfCString(64, "|%016lld|%d",
now / PR_USEC_PER_SEC,
gNextTemporaryClientID++));
nsCOMPtr<nsIApplicationCache> cache = new nsApplicationCache(this,
group,
clientID);
if (!cache)
return NS_ERROR_OUT_OF_MEMORY;
nsCOMPtr<nsIWeakReference> weak = do_GetWeakReference(cache);
if (!weak)
return NS_ERROR_OUT_OF_MEMORY;
mCaches.Put(clientID, weak);
cache.swap(*out);
return NS_OK;
} }
nsresult NS_IMETHODIMP
nsOfflineCacheDevice::GetOwnedKeys(const char * clientID, nsOfflineCacheDevice::GetApplicationCache(const nsACString &clientID,
const nsACString & ownerDomain, nsIApplicationCache **out)
const nsACString & ownerURI,
PRUint32 * count,
char *** keys)
{ {
LOG(("nsOfflineCacheDevice::GetOwnedKeys [cid=%s]\n", clientID)); *out = nsnull;
AutoResetStatement statement(mStatement_ListOwned); nsCOMPtr<nsIApplicationCache> cache;
nsWeakPtr weak;
if (mCaches.Get(clientID, getter_AddRefs(weak)))
cache = do_QueryReferent(weak);
if (!cache)
{
nsCString group;
nsresult rv = GetGroupForCache(clientID, group);
NS_ENSURE_SUCCESS(rv, rv);
if (group.IsEmpty()) {
return NS_OK;
}
cache = new nsApplicationCache(this, group, clientID);
weak = do_GetWeakReference(cache);
if (!weak)
return NS_ERROR_OUT_OF_MEMORY;
mCaches.Put(clientID, weak);
}
cache.swap(*out);
return NS_OK;
}
NS_IMETHODIMP
nsOfflineCacheDevice::GetActiveCache(const nsACString &group,
nsIApplicationCache **out)
{
*out = nsnull;
nsCString *clientID;
if (mActiveCachesByGroup.Get(group, &clientID))
return GetApplicationCache(*clientID, out);
return NS_OK;
}
NS_IMETHODIMP
nsOfflineCacheDevice::ChooseApplicationCache(const nsACString &key,
nsIApplicationCache **out)
{
*out = nsnull;
AutoResetStatement statement(mStatement_FindClient);
nsresult rv = statement->BindUTF8StringParameter( nsresult rv = statement->BindUTF8StringParameter(
0, nsDependentCString(clientID)); 0, key);
rv |= statement->BindUTF8StringParameter(1, ownerDomain);
rv |= statement->BindUTF8StringParameter(2, ownerURI);
NS_ENSURE_SUCCESS(rv, rv);
return RunSimpleQuery(mStatement_ListOwned, 0, count, keys);
}
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::GetDomainUsage(const char *clientID,
const nsACString &domain,
PRUint32 *usage)
{
LOG(("nsOfflineCacheDevice::GetDomainUsage [cid=%s]\n", clientID));
*usage = 0;
AutoResetStatement statement(mStatement_DomainSize);
nsresult rv;
rv = statement->BindUTF8StringParameter(0, nsDependentCString(clientID));
NS_ENSURE_SUCCESS(rv, rv);
rv = statement->BindUTF8StringParameter(1, domain);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
PRBool hasRows; PRBool hasRows;
rv = statement->ExecuteStep(&hasRows); rv = statement->ExecuteStep(&hasRows);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
if (!hasRows) while (hasRows) {
return NS_OK; nsCString clientID;
rv = statement->GetUTF8String(0, clientID);
NS_ENSURE_SUCCESS(rv, rv);
*usage = statement->AsInt32(0); if (mActiveCaches.Contains(clientID))
return GetApplicationCache(clientID, out);
rv = statement->ExecuteStep(&hasRows);
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK; return NS_OK;
} }
nsresult nsresult
nsOfflineCacheDevice::EvictUnownedEntries(const char *clientID) nsOfflineCacheDevice::ActivateCache(const nsCSubstring &group,
const nsCSubstring &clientID)
{ {
LOG(("nsOfflineCacheDevice::EvictUnownedEntries [cid=%s]\n", clientID)); AutoResetStatement statement(mStatement_ActivateClient);
EvictionObserver evictionObserver(mDB, mEvictionFunction); nsresult rv = statement->BindUTF8StringParameter(0, group);
NS_ENSURE_SUCCESS(rv, rv);
AutoResetStatement statement(mStatement_DeleteUnowned); rv = statement->BindUTF8StringParameter(1, clientID);
nsresult rv = statement->BindUTF8StringParameter(
0, nsDependentCString(clientID));
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
rv = statement->Execute(); rv = statement->Execute();
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
evictionObserver.Apply(); nsCString *active;
return NS_OK; if (mActiveCachesByGroup.Get(group, &active))
}
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; mActiveCaches.Remove(*active);
domainArray.AppendElement( mActiveCachesByGroup.Remove(group);
nsDependentCString(listOwnersStatement->AsSharedUTF8String(0, &length))); active = nsnull;
uriArray.AppendElement(
nsDependentCString(listOwnersStatement->AsSharedUTF8String(1, &length)));
rv = listOwnersStatement->ExecuteStep(&hasRows);
NS_ENSURE_SUCCESS(rv, rv);
} }
// Now move over each ownership set if (!clientID.IsEmpty())
for (PRUint32 i = 0; i < domainArray.Length(); i++) { {
PRUint32 count; mActiveCaches.Put(clientID);
char **keys; mActiveCachesByGroup.Put(group, new nsCString(clientID));
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, mEvictionFunction); return NS_OK;
}
AutoResetStatement deleteStatement(mStatement_DeleteConflicts); PRBool
rv = deleteStatement->BindUTF8StringParameter( nsOfflineCacheDevice::IsActiveCache(const nsCSubstring &group,
0, nsDependentCString(clientID)); const nsCSubstring &clientID)
rv |= deleteStatement->BindUTF8StringParameter( {
1, nsDependentCString(fromClientID)); nsCString *active = nsnull;
return mActiveCachesByGroup.Get(group, &active) && *active == clientID;
}
nsresult
nsOfflineCacheDevice::DeactivateGroup(const nsCSubstring &group)
{
nsCString *active = nsnull;
AutoResetStatement statement(mStatement_DeactivateGroup);
nsresult rv = statement->BindUTF8StringParameter(
0, group);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
rv = deleteStatement->Execute(); rv = statement->Execute();
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
AutoResetStatement swapStatement(mStatement_SwapClientID); if (mActiveCachesByGroup.Get(group, &active))
rv = swapStatement->BindUTF8StringParameter( {
0, nsDependentCString(clientID)); mActiveCaches.Remove(*active);
rv |= swapStatement->BindUTF8StringParameter( mActiveCachesByGroup.Remove(group);
1, nsDependentCString(fromClientID)); active = nsnull;
NS_ENSURE_SUCCESS(rv, rv); }
rv = swapStatement->Execute(); return NS_OK;
NS_ENSURE_SUCCESS(rv, rv); }
rv = transaction.Commit(); nsresult
NS_ENSURE_SUCCESS(rv, rv); nsOfflineCacheDevice::GetGroupForCache(const nsACString &clientID,
nsCString &out)
evictionObserver.Apply(); {
out.Assign(clientID);
out.Truncate(out.FindChar('|'));
NS_UnescapeURL(out);
return NS_OK; return NS_OK;
} }

View File

@ -39,6 +39,8 @@
#define nsOfflineCacheDevice_h__ #define nsOfflineCacheDevice_h__
#include "nsCacheDevice.h" #include "nsCacheDevice.h"
#include "nsIApplicationCache.h"
#include "nsIApplicationCacheService.h"
#include "nsILocalFile.h" #include "nsILocalFile.h"
#include "nsIObserver.h" #include "nsIObserver.h"
#include "mozIStorageConnection.h" #include "mozIStorageConnection.h"
@ -48,6 +50,10 @@
#include "nsCOMPtr.h" #include "nsCOMPtr.h"
#include "nsCOMArray.h" #include "nsCOMArray.h"
#include "nsVoidArray.h" #include "nsVoidArray.h"
#include "nsInterfaceHashtable.h"
#include "nsClassHashtable.h"
#include "nsHashSets.h"
#include "nsWeakReference.h"
class nsOfflineCacheDevice; class nsOfflineCacheDevice;
@ -70,16 +76,22 @@ private:
}; };
class nsOfflineCacheDevice : public nsCacheDevice class nsOfflineCacheDevice : public nsCacheDevice
, public nsIApplicationCacheService
{ {
public: public:
nsOfflineCacheDevice(); nsOfflineCacheDevice();
NS_DECL_ISUPPORTS
NS_DECL_NSIAPPLICATIONCACHESERVICE
/** /**
* nsCacheDevice methods * nsCacheDevice methods
*/ */
virtual ~nsOfflineCacheDevice(); virtual ~nsOfflineCacheDevice();
static nsOfflineCacheDevice *GetInstance();
virtual nsresult Init(); virtual nsresult Init();
virtual nsresult Shutdown(); virtual nsresult Shutdown();
@ -143,15 +155,15 @@ public:
nsresult ClearKeysOwnedByDomain(const char *clientID, nsresult ClearKeysOwnedByDomain(const char *clientID,
const nsACString &ownerDomain); const nsACString &ownerDomain);
nsresult GetDomainUsage(const char *clientID,
const nsACString &ownerDomain,
PRUint32 *usage);
nsresult EvictUnownedEntries(const char *clientID); nsresult EvictUnownedEntries(const char *clientID);
nsresult CreateTemporaryClientID(nsACString &clientID); nsresult ActivateCache(const nsCSubstring &group,
nsresult MergeTemporaryClientID(const char *clientID, const nsCSubstring &clientID);
const char *fromClientID); PRBool IsActiveCache(const nsCSubstring &group,
const nsCSubstring &clientID);
nsresult DeactivateGroup(const nsCSubstring &group);
nsresult GetGroupForCache(const nsCSubstring &clientID,
nsCString &out);
/** /**
* Preference accessors * Preference accessors
@ -166,13 +178,36 @@ public:
PRUint32 EntryCount(); PRUint32 EntryCount();
private: private:
friend class nsApplicationCache;
static PLDHashOperator ShutdownApplicationCache(const nsACString &key,
nsIWeakReference *weakRef,
void *ctx);
PRBool Initialized() { return mDB != nsnull; } PRBool Initialized() { return mDB != nsnull; }
nsresult InitActiveCaches();
nsresult UpdateEntry(nsCacheEntry *entry); nsresult UpdateEntry(nsCacheEntry *entry);
nsresult UpdateEntrySize(nsCacheEntry *entry, PRUint32 newSize); nsresult UpdateEntrySize(nsCacheEntry *entry, PRUint32 newSize);
nsresult DeleteEntry(nsCacheEntry *entry, PRBool deleteData); nsresult DeleteEntry(nsCacheEntry *entry, PRBool deleteData);
nsresult DeleteData(nsCacheEntry *entry); nsresult DeleteData(nsCacheEntry *entry);
nsresult EnableEvictionObserver(); nsresult EnableEvictionObserver();
nsresult DisableEvictionObserver(); nsresult DisableEvictionObserver();
nsresult MarkEntry(const nsCString &clientID,
const nsACString &key,
PRUint32 typeBits);
nsresult UnmarkEntry(const nsCString &clientID,
const nsACString &key,
PRUint32 typeBits);
nsresult GetTypes(const nsCString &clientID,
const nsACString &key,
PRUint32 *typeBits);
nsresult GatherEntries(const nsCString &clientID,
PRUint32 typeBits,
PRUint32 *count,
char *** values);
nsresult RunSimpleQuery(mozIStorageStatement *statment, nsresult RunSimpleQuery(mozIStorageStatement *statment,
PRUint32 resultIndex, PRUint32 resultIndex,
PRUint32 * count, PRUint32 * count,
@ -190,22 +225,23 @@ private:
nsCOMPtr<mozIStorageStatement> mStatement_DeleteEntry; nsCOMPtr<mozIStorageStatement> mStatement_DeleteEntry;
nsCOMPtr<mozIStorageStatement> mStatement_FindEntry; nsCOMPtr<mozIStorageStatement> mStatement_FindEntry;
nsCOMPtr<mozIStorageStatement> mStatement_BindEntry; nsCOMPtr<mozIStorageStatement> mStatement_BindEntry;
nsCOMPtr<mozIStorageStatement> mStatement_ClearOwnership;
nsCOMPtr<mozIStorageStatement> mStatement_RemoveOwnership;
nsCOMPtr<mozIStorageStatement> mStatement_ClearDomain; nsCOMPtr<mozIStorageStatement> mStatement_ClearDomain;
nsCOMPtr<mozIStorageStatement> mStatement_AddOwnership; nsCOMPtr<mozIStorageStatement> mStatement_MarkEntry;
nsCOMPtr<mozIStorageStatement> mStatement_CheckOwnership; nsCOMPtr<mozIStorageStatement> mStatement_UnmarkEntry;
nsCOMPtr<mozIStorageStatement> mStatement_DeleteConflicts; nsCOMPtr<mozIStorageStatement> mStatement_GetTypes;
nsCOMPtr<mozIStorageStatement> mStatement_DeleteUnowned; nsCOMPtr<mozIStorageStatement> mStatement_CleanupUnmarked;
nsCOMPtr<mozIStorageStatement> mStatement_ListOwned; nsCOMPtr<mozIStorageStatement> mStatement_GatherEntries;
nsCOMPtr<mozIStorageStatement> mStatement_ListOwners; nsCOMPtr<mozIStorageStatement> mStatement_ActivateClient;
nsCOMPtr<mozIStorageStatement> mStatement_ListOwnerDomains; nsCOMPtr<mozIStorageStatement> mStatement_DeactivateGroup;
nsCOMPtr<mozIStorageStatement> mStatement_ListOwnerURIs; nsCOMPtr<mozIStorageStatement> mStatement_FindClient;
nsCOMPtr<mozIStorageStatement> mStatement_SwapClientID;
nsCOMPtr<nsILocalFile> mCacheDirectory; nsCOMPtr<nsILocalFile> mCacheDirectory;
PRUint32 mCacheCapacity; PRUint32 mCacheCapacity;
PRInt32 mDeltaCounter; PRInt32 mDeltaCounter;
nsInterfaceHashtable<nsCStringHashKey, nsIWeakReference> mCaches;
nsClassHashtable<nsCStringHashKey, nsCString> mActiveCachesByGroup;
nsCStringHashSet mActiveCaches;
}; };
#endif // nsOfflineCacheDevice_h__ #endif // nsOfflineCacheDevice_h__

View File

@ -47,6 +47,7 @@
#include "nsHttpResponseHead.h" #include "nsHttpResponseHead.h"
#include "nsHttp.h" #include "nsHttp.h"
#include "nsIHttpAuthenticator.h" #include "nsIHttpAuthenticator.h"
#include "nsIApplicationCacheService.h"
#include "nsIAuthInformation.h" #include "nsIAuthInformation.h"
#include "nsIAuthPrompt2.h" #include "nsIAuthPrompt2.h"
#include "nsIAuthPromptProvider.h" #include "nsIAuthPromptProvider.h"
@ -1371,26 +1372,61 @@ nsHttpChannel::OpenCacheEntry(PRBool offline, PRBool *delayed)
else else
accessRequested = nsICache::ACCESS_READ_WRITE; // normal browsing accessRequested = nsICache::ACCESS_READ_WRITE; // normal browsing
nsCOMPtr<nsICacheSession> session; if (!mApplicationCache) {
if (mLoadFlags & LOAD_CHECK_OFFLINE_CACHE) { // Pick up an application cache from the load group if available
// when LOAD_CHECK_OFFLINE_CACHE set prefer the offline cache nsCOMPtr<nsIApplicationCacheContainer> appCacheContainer;
rv = gHttpHandler->GetCacheSession(nsICache::STORE_OFFLINE, GetCallback(appCacheContainer);
getter_AddRefs(session));
if (NS_FAILED(rv)) return rv;
// we'll try to synchronously open the cache entry... however, it may be if (appCacheContainer) {
// in use and not yet validated, in which case we'll try asynchronously appCacheContainer->GetApplicationCache(getter_AddRefs(mApplicationCache));
// opening the cache entry. }
if ((mLoadFlags & LOAD_CHECK_OFFLINE_CACHE) && !mApplicationCache) {
// We're supposed to load from an application cache, but
// one was not supplied by the load group. Ask the
// application cache service to choose one for us.
nsCOMPtr<nsIApplicationCacheService> appCacheService =
do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID);
if (appCacheService) {
nsresult rv = appCacheService->ChooseApplicationCache
(cacheKey, getter_AddRefs(mApplicationCache));
NS_ENSURE_SUCCESS(rv, rv);
}
}
}
nsCOMPtr<nsICacheSession> session;
// If we have an application cache, we check it first.
if (mApplicationCache) {
nsCAutoString appCacheClientID;
mApplicationCache->GetClientID(appCacheClientID);
nsCOMPtr<nsICacheService> serv =
do_GetService(NS_CACHESERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = serv->CreateSession(appCacheClientID.get(),
nsICache::STORE_OFFLINE,
nsICache::STREAM_BASED,
getter_AddRefs(session));
NS_ENSURE_SUCCESS(rv, rv);
// we'll try to synchronously open the cache entry... however,
// it may be in use and not yet validated, in which case we'll
// try asynchronously opening the cache entry.
// //
// we need open in ACCESS_READ only because we don't want to overwrite // we need open in ACCESS_READ only because we don't want to
// the offline cache entry non-atomically so ACCESS_READ // overwrite the offline cache entry non-atomically so
// will prevent us from writing to the HTTP-offline cache as a // ACCESS_READ will prevent us from writing to the
// normal cache entry. // HTTP-offline cache as a normal cache entry. XXX: this
rv = session->OpenCacheEntry(cacheKey, nsICache::ACCESS_READ, PR_FALSE, // needs to be checked.
rv = session->OpenCacheEntry(cacheKey,
nsICache::ACCESS_READ, PR_FALSE,
getter_AddRefs(mCacheEntry)); getter_AddRefs(mCacheEntry));
} }
if (!(mLoadFlags & LOAD_CHECK_OFFLINE_CACHE) || if (!mApplicationCache ||
(NS_FAILED(rv) && rv != NS_ERROR_CACHE_WAIT_FOR_VALIDATION)) (NS_FAILED(rv) && rv != NS_ERROR_CACHE_WAIT_FOR_VALIDATION))
{ {
rv = gHttpHandler->GetCacheSession(storagePolicy, rv = gHttpHandler->GetCacheSession(storagePolicy,
@ -1457,20 +1493,18 @@ nsHttpChannel::OpenOfflineCacheEntryForWriting()
nsCAutoString cacheKey; nsCAutoString cacheKey;
GenerateCacheKey(cacheKey); GenerateCacheKey(cacheKey);
nsCOMPtr<nsICacheSession> session; NS_ENSURE_TRUE(!mOfflineCacheClientID.IsEmpty(),
if (!mOfflineCacheClientID.IsEmpty()) { NS_ERROR_NOT_AVAILABLE);
nsCOMPtr<nsICacheService> serv =
do_GetService(NS_CACHESERVICE_CONTRACTID, &rv);
if (NS_FAILED(rv)) return rv;
rv = serv->CreateSession(mOfflineCacheClientID.get(), nsCOMPtr<nsICacheSession> session;
nsICache::STORE_OFFLINE, nsCOMPtr<nsICacheService> serv =
nsICache::STREAM_BASED, do_GetService(NS_CACHESERVICE_CONTRACTID, &rv);
getter_AddRefs(session)); if (NS_FAILED(rv)) return rv;
} else {
rv = gHttpHandler->GetCacheSession(nsICache::STORE_OFFLINE, rv = serv->CreateSession(mOfflineCacheClientID.get(),
getter_AddRefs(session)); nsICache::STORE_OFFLINE,
} nsICache::STREAM_BASED,
getter_AddRefs(session));
if (NS_FAILED(rv)) return rv; if (NS_FAILED(rv)) return rv;
rv = session->OpenCacheEntry(cacheKey, nsICache::ACCESS_READ_WRITE, rv = session->OpenCacheEntry(cacheKey, nsICache::ACCESS_READ_WRITE,
@ -3387,6 +3421,7 @@ NS_INTERFACE_MAP_BEGIN(nsHttpChannel)
NS_INTERFACE_MAP_ENTRY(nsISupportsPriority) NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
NS_INTERFACE_MAP_ENTRY(nsIProtocolProxyCallback) NS_INTERFACE_MAP_ENTRY(nsIProtocolProxyCallback)
NS_INTERFACE_MAP_ENTRY(nsIProxiedChannel) NS_INTERFACE_MAP_ENTRY(nsIProxiedChannel)
NS_INTERFACE_MAP_ENTRY(nsIApplicationCacheContainer)
NS_INTERFACE_MAP_END_INHERITING(nsHashPropertyBag) NS_INTERFACE_MAP_END_INHERITING(nsHashPropertyBag)
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -4876,6 +4911,24 @@ nsHttpChannel::DoAuthRetry(nsAHttpConnection *conn)
return mTransactionPump->AsyncRead(this, nsnull); return mTransactionPump->AsyncRead(this, nsnull);
} }
//-----------------------------------------------------------------------------
// nsHttpChannel::nsIApplicationCacheContainer
//-----------------------------------------------------------------------------
NS_IMETHODIMP
nsHttpChannel::GetApplicationCache(nsIApplicationCache **out)
{
NS_IF_ADDREF(*out = mApplicationCache);
return NS_OK;
}
NS_IMETHODIMP
nsHttpChannel::SetApplicationCache(nsIApplicationCache *appCache)
{
mApplicationCache = appCache;
return NS_OK;
}
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// nsHttpChannel::nsContentEncodings <public> // nsHttpChannel::nsContentEncodings <public>
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------

View File

@ -66,8 +66,11 @@
#include "nsIInputStream.h" #include "nsIInputStream.h"
#include "nsIProgressEventSink.h" #include "nsIProgressEventSink.h"
#include "nsICachingChannel.h" #include "nsICachingChannel.h"
#include "nsICacheSession.h"
#include "nsICacheEntryDescriptor.h" #include "nsICacheEntryDescriptor.h"
#include "nsICacheListener.h" #include "nsICacheListener.h"
#include "nsIApplicationCache.h"
#include "nsIApplicationCacheContainer.h"
#include "nsIEncodedChannel.h" #include "nsIEncodedChannel.h"
#include "nsITransport.h" #include "nsITransport.h"
#include "nsIUploadChannel.h" #include "nsIUploadChannel.h"
@ -103,6 +106,7 @@ class nsHttpChannel : public nsHashPropertyBag
, public nsISupportsPriority , public nsISupportsPriority
, public nsIProtocolProxyCallback , public nsIProtocolProxyCallback
, public nsIProxiedChannel , public nsIProxiedChannel
, public nsIApplicationCacheContainer
{ {
public: public:
NS_DECL_ISUPPORTS_INHERITED NS_DECL_ISUPPORTS_INHERITED
@ -121,6 +125,7 @@ public:
NS_DECL_NSISUPPORTSPRIORITY NS_DECL_NSISUPPORTSPRIORITY
NS_DECL_NSIPROTOCOLPROXYCALLBACK NS_DECL_NSIPROTOCOLPROXYCALLBACK
NS_DECL_NSIPROXIEDCHANNEL NS_DECL_NSIPROXIEDCHANNEL
NS_DECL_NSIAPPLICATIONCACHECONTAINER
nsHttpChannel(); nsHttpChannel();
virtual ~nsHttpChannel(); virtual ~nsHttpChannel();
@ -263,6 +268,8 @@ private:
nsCacheAccessMode mOfflineCacheAccess; nsCacheAccessMode mOfflineCacheAccess;
nsCString mOfflineCacheClientID; nsCString mOfflineCacheClientID;
nsCOMPtr<nsIApplicationCache> mApplicationCache;
// auth specific data // auth specific data
nsISupports *mProxyAuthContinuationState; nsISupports *mProxyAuthContinuationState;
nsCString mProxyAuthType; nsCString mProxyAuthType;

View File

@ -115,7 +115,7 @@ interface nsIOfflineCacheUpdateObserver : nsISupports {
* load its items one by one, sending itemCompleted() to any registered * load its items one by one, sending itemCompleted() to any registered
* observers. * observers.
*/ */
[scriptable, uuid(7dc06ede-1098-4384-b95e-65525ab254c9)] [scriptable, uuid(4b206247-82ee-46cf-a8b7-f7284e753bc2)]
interface nsIOfflineCacheUpdate : nsISupports { interface nsIOfflineCacheUpdate : nsISupports {
/** /**
* Fetch the status of the running update. This will return a value * Fetch the status of the running update. This will return a value
@ -129,6 +129,12 @@ interface nsIOfflineCacheUpdate : nsISupports {
*/ */
readonly attribute boolean partial; readonly attribute boolean partial;
/**
* TRUE if this is an upgrade attempt, FALSE if it is a new cache
* attempt.
*/
readonly attribute boolean isUpgrade;
/** /**
* The domain being updated, and the domain that will own any URIs added * The domain being updated, and the domain that will own any URIs added
* with this update. * with this update.

View File

@ -40,6 +40,8 @@
#include "nsCPrefetchService.h" #include "nsCPrefetchService.h"
#include "nsCURILoader.h" #include "nsCURILoader.h"
#include "nsIApplicationCacheContainer.h"
#include "nsIApplicationCacheService.h"
#include "nsICache.h" #include "nsICache.h"
#include "nsICacheService.h" #include "nsICacheService.h"
#include "nsICacheSession.h" #include "nsICacheSession.h"
@ -47,7 +49,7 @@
#include "nsIDOMWindow.h" #include "nsIDOMWindow.h"
#include "nsIDOMOfflineResourceList.h" #include "nsIDOMOfflineResourceList.h"
#include "nsIObserverService.h" #include "nsIObserverService.h"
#include "nsIOfflineCacheSession.h" #include "nsIURL.h"
#include "nsIWebProgress.h" #include "nsIWebProgress.h"
#include "nsICryptoHash.h" #include "nsICryptoHash.h"
#include "nsICacheEntryDescriptor.h" #include "nsICacheEntryDescriptor.h"
@ -104,10 +106,14 @@ NS_IMPL_ISUPPORTS6(nsOfflineCacheUpdateItem,
nsOfflineCacheUpdateItem::nsOfflineCacheUpdateItem(nsOfflineCacheUpdate *aUpdate, nsOfflineCacheUpdateItem::nsOfflineCacheUpdateItem(nsOfflineCacheUpdate *aUpdate,
nsIURI *aURI, nsIURI *aURI,
nsIURI *aReferrerURI, nsIURI *aReferrerURI,
const nsACString &aClientID) nsIApplicationCache *aPreviousApplicationCache,
const nsACString &aClientID,
PRUint32 type)
: mURI(aURI) : mURI(aURI)
, mReferrerURI(aReferrerURI) , mReferrerURI(aReferrerURI)
, mPreviousApplicationCache(aPreviousApplicationCache)
, mClientID(aClientID) , mClientID(aClientID)
, mItemType(type)
, mUpdate(aUpdate) , mUpdate(aUpdate)
, mChannel(nsnull) , mChannel(nsnull)
, mState(nsIDOMLoadStatus::UNINITIALIZED) , mState(nsIDOMLoadStatus::UNINITIALIZED)
@ -122,12 +128,33 @@ nsOfflineCacheUpdateItem::~nsOfflineCacheUpdateItem()
nsresult nsresult
nsOfflineCacheUpdateItem::OpenChannel() nsOfflineCacheUpdateItem::OpenChannel()
{ {
nsresult rv = NS_NewChannel(getter_AddRefs(mChannel), #if defined(PR_LOGGING)
mURI, if (LOG_ENABLED()) {
nsnull, nsnull, this, nsCAutoString spec;
nsIRequest::LOAD_BACKGROUND | mURI->GetSpec(spec);
nsICachingChannel::LOAD_ONLY_IF_MODIFIED | LOG(("%p: Opening channel for %s", this, spec.get()));
nsICachingChannel::LOAD_CHECK_OFFLINE_CACHE); }
#endif
nsresult rv = nsOfflineCacheUpdate::GetCacheKey(mURI, mCacheKey);
NS_ENSURE_SUCCESS(rv, rv);
rv = NS_NewChannel(getter_AddRefs(mChannel),
mURI,
nsnull, nsnull, this,
nsIRequest::LOAD_BACKGROUND |
nsICachingChannel::LOAD_ONLY_IF_MODIFIED |
nsICachingChannel::LOAD_CHECK_OFFLINE_CACHE);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIApplicationCacheContainer> appCacheContainer =
do_QueryInterface(mChannel, &rv);
// Support for nsIApplicationCacheContainer is required.
NS_ENSURE_SUCCESS(rv, rv);
// Use the existing application cache as the cache to check.
rv = appCacheContainer->SetApplicationCache(mPreviousApplicationCache);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
// configure HTTP specific stuff // configure HTTP specific stuff
@ -392,8 +419,11 @@ nsOfflineCacheUpdateItem::GetStatus(PRUint16 *aStatus)
nsOfflineManifestItem::nsOfflineManifestItem(nsOfflineCacheUpdate *aUpdate, nsOfflineManifestItem::nsOfflineManifestItem(nsOfflineCacheUpdate *aUpdate,
nsIURI *aURI, nsIURI *aURI,
nsIURI *aReferrerURI, nsIURI *aReferrerURI,
nsIApplicationCache *aPreviousApplicationCache,
const nsACString &aClientID) const nsACString &aClientID)
: nsOfflineCacheUpdateItem(aUpdate, aURI, aReferrerURI, aClientID) : nsOfflineCacheUpdateItem(aUpdate, aURI, aReferrerURI,
aPreviousApplicationCache, aClientID,
nsIApplicationCache::ITEM_MANIFEST)
, mParserState(PARSE_INIT) , mParserState(PARSE_INIT)
, mNeedsUpdate(PR_TRUE) , mNeedsUpdate(PR_TRUE)
, mManifestHashInitialized(PR_FALSE) , mManifestHashInitialized(PR_FALSE)
@ -757,6 +787,28 @@ nsOfflineCacheUpdate::~nsOfflineCacheUpdate()
LOG(("nsOfflineCacheUpdate::~nsOfflineCacheUpdate [%p]", this)); LOG(("nsOfflineCacheUpdate::~nsOfflineCacheUpdate [%p]", this));
} }
/* static */
nsresult
nsOfflineCacheUpdate::GetCacheKey(nsIURI *aURI, nsACString &aKey)
{
aKey.Truncate();
nsCOMPtr<nsIURI> newURI;
nsresult rv = aURI->Clone(getter_AddRefs(newURI));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIURL> newURL;
newURL = do_QueryInterface(newURI);
if (newURL) {
newURL->SetRef(EmptyCString());
}
rv = newURI->GetAsciiSpec(aKey);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nsresult nsresult
nsOfflineCacheUpdate::Init(PRBool aPartialUpdate, nsOfflineCacheUpdate::Init(PRBool aPartialUpdate,
nsIURI *aManifestURI, nsIURI *aManifestURI,
@ -770,7 +822,7 @@ nsOfflineCacheUpdate::Init(PRBool aPartialUpdate,
if (!service) if (!service)
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
LOG(("nsOfflineCacheUpdate::Init [%p]", this)); LOG(("nsOfflineCacheUpdate::Init [%p, %d]", this, aPartialUpdate));
mPartialUpdate = aPartialUpdate; mPartialUpdate = aPartialUpdate;
@ -793,58 +845,31 @@ nsOfflineCacheUpdate::Init(PRBool aPartialUpdate,
nsCAutoString manifestSpec; nsCAutoString manifestSpec;
rv = mManifestURI->GetAsciiSpec(manifestSpec); rv = GetCacheKey(mManifestURI, manifestSpec);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
PRInt32 ref = manifestSpec.FindChar('#');
if (ref != kNotFound)
manifestSpec.Truncate(ref);
mManifestOwnerSpec = manifestSpec;
mManifestOwnerSpec.AppendLiteral("#manifest");
mDynamicOwnerSpec = manifestSpec;
mDynamicOwnerSpec.AppendLiteral("#dynamic");
mDocumentURI = aDocumentURI; mDocumentURI = aDocumentURI;
nsCOMPtr<nsICacheService> cacheService = nsCOMPtr<nsIApplicationCacheService> cacheService =
do_GetService(NS_CACHESERVICE_CONTRACTID, &rv); do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsICacheSession> session; rv = cacheService->GetActiveCache(manifestSpec,
rv = cacheService->CreateSession("HTTP-offline", getter_AddRefs(mPreviousApplicationCache));
nsICache::STORE_OFFLINE,
nsICache::STREAM_BASED,
getter_AddRefs(session));
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
mMainCacheSession = do_QueryInterface(session, &rv); // Partial updates to existing application caches don't need a new cache.
NS_ENSURE_SUCCESS(rv, rv); if (aPartialUpdate && mPreviousApplicationCache) {
mApplicationCache = mPreviousApplicationCache;
// Partial updates don't use temporary cache sessions
if (aPartialUpdate) {
mCacheSession = mMainCacheSession;
} else { } else {
rv = cacheService->CreateTemporaryClientID(nsICache::STORE_OFFLINE, rv = cacheService->CreateApplicationCache(manifestSpec,
mClientID); getter_AddRefs(mApplicationCache));
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);
// The manifest implicitly owns itself.
rv = mCacheSession->AddOwnedKey(mUpdateDomain, mManifestOwnerSpec,
manifestSpec);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
} }
rv = mApplicationCache->GetClientID(mClientID);
NS_ENSURE_SUCCESS(rv, rv);
mState = STATE_INITIALIZED; mState = STATE_INITIALIZED;
return NS_OK; return NS_OK;
@ -871,17 +896,21 @@ nsOfflineCacheUpdate::HandleManifest(PRBool *aDoUpdate)
// Add items requested by the manifest. // Add items requested by the manifest.
const nsCOMArray<nsIURI> &manifestURIs = mManifestItem->GetExplicitURIs(); const nsCOMArray<nsIURI> &manifestURIs = mManifestItem->GetExplicitURIs();
for (PRInt32 i = 0; i < manifestURIs.Count(); i++) { for (PRInt32 i = 0; i < manifestURIs.Count(); i++) {
rv = AddURI(manifestURIs[i], mManifestOwnerSpec); rv = AddURI(manifestURIs[i], nsIApplicationCache::ITEM_EXPLICIT);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
} }
// The document that requested the manifest is implicitly included // The document that requested the manifest is implicitly included
// as part of that manifest update. // as part of that manifest update.
rv = AddURI(mDocumentURI, mManifestOwnerSpec); rv = AddURI(mDocumentURI, nsIApplicationCache::ITEM_IMPLICIT);
NS_ENSURE_SUCCESS(rv, rv);
// Add items previously cached implicitly
rv = AddExistingItems(nsIApplicationCache::ITEM_IMPLICIT);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
// Add items requested by the script API // Add items requested by the script API
rv = AddOwnedItems(mDynamicOwnerSpec); rv = AddExistingItems(nsIApplicationCache::ITEM_DYNAMIC);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
*aDoUpdate = PR_TRUE; *aDoUpdate = PR_TRUE;
@ -922,6 +951,15 @@ nsOfflineCacheUpdate::LoadCompleted()
return; return;
} }
rv = mApplicationCache->MarkEntry(mManifestItem->mCacheKey,
mManifestItem->mItemType);
if (NS_FAILED(rv)) {
mSucceeded = PR_FALSE;
NotifyError();
Finish();
return;
}
mState = STATE_DOWNLOADING; mState = STATE_DOWNLOADING;
NotifyDownloading(); NotifyDownloading();
@ -947,6 +985,14 @@ nsOfflineCacheUpdate::LoadCompleted()
return; return;
} }
rv = mApplicationCache->MarkEntry(item->mCacheKey, item->mItemType);
if (NS_FAILED(rv)) {
mSucceeded = PR_FALSE;
NotifyError();
Finish();
return;
}
rv = NotifyCompleted(item); rv = NotifyCompleted(item);
if (NS_FAILED(rv)) return; if (NS_FAILED(rv)) return;
@ -971,7 +1017,9 @@ nsOfflineCacheUpdate::Begin()
nsCOMPtr<nsIURI> uri; nsCOMPtr<nsIURI> uri;
mManifestItem = new nsOfflineManifestItem(this, mManifestURI, mManifestItem = new nsOfflineManifestItem(this, mManifestURI,
mDocumentURI, mClientID); mDocumentURI,
mPreviousApplicationCache,
mClientID);
if (!mManifestItem) { if (!mManifestItem) {
return NS_ERROR_OUT_OF_MEMORY; return NS_ERROR_OUT_OF_MEMORY;
} }
@ -1009,12 +1057,16 @@ nsOfflineCacheUpdate::Cancel()
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
nsresult nsresult
nsOfflineCacheUpdate::AddOwnedItems(const nsACString &aOwnerURI) nsOfflineCacheUpdate::AddExistingItems(PRUint32 aType)
{ {
PRUint32 count; if (!mPreviousApplicationCache) {
char **keys; return NS_OK;
nsresult rv = mMainCacheSession->GetOwnedKeys(mUpdateDomain, aOwnerURI, }
&count, &keys);
PRUint32 count = 0;
char **keys = nsnull;
nsresult rv = mPreviousApplicationCache->GatherEntries(aType,
&count, &keys);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
AutoFreeArray autoFree(count, keys); AutoFreeArray autoFree(count, keys);
@ -1022,12 +1074,8 @@ nsOfflineCacheUpdate::AddOwnedItems(const nsACString &aOwnerURI)
for (PRUint32 i = 0; i < count; i++) { for (PRUint32 i = 0; i < count; i++) {
nsCOMPtr<nsIURI> uri; nsCOMPtr<nsIURI> uri;
if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(uri), keys[i]))) { if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(uri), keys[i]))) {
nsRefPtr<nsOfflineCacheUpdateItem> item = rv = AddURI(uri, aType);
new nsOfflineCacheUpdateItem(this, uri, mDocumentURI, NS_ENSURE_SUCCESS(rv, rv);
mClientID);
if (!item) return NS_ERROR_OUT_OF_MEMORY;
mItems.AppendElement(item);
} }
} }
@ -1090,6 +1138,8 @@ nsOfflineCacheUpdate::NotifyError()
{ {
LOG(("nsOfflineCacheUpdate::NotifyError [%p]", this)); LOG(("nsOfflineCacheUpdate::NotifyError [%p]", this));
mState = STATE_FINISHED;
nsCOMArray<nsIOfflineCacheUpdateObserver> observers; nsCOMArray<nsIOfflineCacheUpdateObserver> observers;
nsresult rv = GatherObservers(observers); nsresult rv = GatherObservers(observers);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
@ -1122,6 +1172,8 @@ nsOfflineCacheUpdate::NotifyNoUpdate()
{ {
LOG(("nsOfflineCacheUpdate::NotifyNoUpdate [%p]", this)); LOG(("nsOfflineCacheUpdate::NotifyNoUpdate [%p]", this));
mState = STATE_FINISHED;
nsCOMArray<nsIOfflineCacheUpdateObserver> observers; nsCOMArray<nsIOfflineCacheUpdateObserver> observers;
nsresult rv = GatherObservers(observers); nsresult rv = GatherObservers(observers);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
@ -1196,7 +1248,7 @@ nsOfflineCacheUpdate::Finish()
if (!mPartialUpdate) { if (!mPartialUpdate) {
if (mSucceeded) { if (mSucceeded) {
nsresult rv = mMainCacheSession->MergeTemporaryClientID(mClientID); nsresult rv = mApplicationCache->Activate();
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
NotifyError(); NotifyError();
mSucceeded = PR_FALSE; mSucceeded = PR_FALSE;
@ -1208,6 +1260,8 @@ nsOfflineCacheUpdate::Finish()
for (PRUint32 i = 0; i < mItems.Length(); i++) { for (PRUint32 i = 0; i < mItems.Length(); i++) {
mItems[i]->Cancel(); mItems[i]->Cancel();
} }
mApplicationCache->Discard();
} }
} }
@ -1271,15 +1325,25 @@ nsOfflineCacheUpdate::GetSucceeded(PRBool *aSucceeded)
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP
nsOfflineCacheUpdate::GetIsUpgrade(PRBool *aIsUpgrade)
{
NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
*aIsUpgrade = (mPreviousApplicationCache != nsnull);
return NS_OK;
}
nsresult nsresult
nsOfflineCacheUpdate::AddURI(nsIURI *aURI, const nsACString &aOwnerSpec) nsOfflineCacheUpdate::AddURI(nsIURI *aURI, PRUint32 aType)
{ {
NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED); NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
if (mState >= STATE_DOWNLOADING) if (mState >= STATE_DOWNLOADING)
return NS_ERROR_NOT_AVAILABLE; return NS_ERROR_NOT_AVAILABLE;
// Manifest URIs must have the same scheme as the manifest. // Resource URIs must have the same scheme as the manifest.
nsCAutoString scheme; nsCAutoString scheme;
aURI->GetScheme(scheme); aURI->GetScheme(scheme);
@ -1287,35 +1351,20 @@ nsOfflineCacheUpdate::AddURI(nsIURI *aURI, const nsACString &aOwnerSpec)
if (NS_FAILED(mManifestURI->SchemeIs(scheme.get(), &match)) || !match) if (NS_FAILED(mManifestURI->SchemeIs(scheme.get(), &match)) || !match)
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
// Save the cache key as an owned URI
nsCAutoString spec;
nsresult rv = aURI->GetSpec(spec);
NS_ENSURE_SUCCESS(rv, rv);
// url fragments aren't used in cache keys
nsCAutoString::const_iterator specStart, specEnd;
spec.BeginReading(specStart);
spec.EndReading(specEnd);
if (FindCharInReadable('#', specStart, specEnd)) {
spec.BeginReading(specEnd);
rv = mCacheSession->AddOwnedKey(mUpdateDomain, aOwnerSpec,
Substring(specEnd, specStart));
NS_ENSURE_SUCCESS(rv, rv);
} else {
rv = mCacheSession->AddOwnedKey(mUpdateDomain, aOwnerSpec, spec);
NS_ENSURE_SUCCESS(rv, rv);
}
// Don't fetch the same URI twice. // Don't fetch the same URI twice.
for (PRUint32 i = 0; i < mItems.Length(); i++) { for (PRUint32 i = 0; i < mItems.Length(); i++) {
PRBool equals; PRBool equals;
if (NS_SUCCEEDED(mItems[i]->mURI->Equals(aURI, &equals)) && equals) { if (NS_SUCCEEDED(mItems[i]->mURI->Equals(aURI, &equals)) && equals) {
// retain both types.
mItems[i]->mItemType |= aType;
return NS_OK; return NS_OK;
} }
} }
nsRefPtr<nsOfflineCacheUpdateItem> item = nsRefPtr<nsOfflineCacheUpdateItem> item =
new nsOfflineCacheUpdateItem(this, aURI, mDocumentURI, mClientID); new nsOfflineCacheUpdateItem(this, aURI, mDocumentURI,
mPreviousApplicationCache, mClientID,
aType);
if (!item) return NS_ERROR_OUT_OF_MEMORY; if (!item) return NS_ERROR_OUT_OF_MEMORY;
mItems.AppendElement(item); mItems.AppendElement(item);
@ -1327,7 +1376,24 @@ nsOfflineCacheUpdate::AddURI(nsIURI *aURI, const nsACString &aOwnerSpec)
NS_IMETHODIMP NS_IMETHODIMP
nsOfflineCacheUpdate::AddDynamicURI(nsIURI *aURI) nsOfflineCacheUpdate::AddDynamicURI(nsIURI *aURI)
{ {
return AddURI(aURI, mDynamicOwnerSpec); // If this is a partial update and the resource is already in the
// cache, we should only mark the entry, not fetch it again.
if (mPartialUpdate) {
nsCAutoString key;
GetCacheKey(aURI, key);
PRUint32 types;
nsresult rv = mApplicationCache->GetTypes(key, &types);
if (NS_SUCCEEDED(rv)) {
if (!(types & nsIApplicationCache::ITEM_DYNAMIC)) {
mApplicationCache->MarkEntry
(key, nsIApplicationCache::ITEM_DYNAMIC);
}
return NS_OK;
}
}
return AddURI(aURI, nsIApplicationCache::ITEM_DYNAMIC);
} }
NS_IMETHODIMP NS_IMETHODIMP

View File

@ -52,8 +52,7 @@
#include "nsIInterfaceRequestor.h" #include "nsIInterfaceRequestor.h"
#include "nsIObserver.h" #include "nsIObserver.h"
#include "nsIObserverService.h" #include "nsIObserverService.h"
#include "nsIOfflineCacheSession.h" #include "nsIApplicationCache.h"
#include "nsIPrefetchService.h"
#include "nsIRequestObserver.h" #include "nsIRequestObserver.h"
#include "nsIRunnable.h" #include "nsIRunnable.h"
#include "nsIStreamListener.h" #include "nsIStreamListener.h"
@ -87,12 +86,17 @@ public:
nsOfflineCacheUpdateItem(nsOfflineCacheUpdate *aUpdate, nsOfflineCacheUpdateItem(nsOfflineCacheUpdate *aUpdate,
nsIURI *aURI, nsIURI *aURI,
nsIURI *aReferrerURI, nsIURI *aReferrerURI,
const nsACString &aClientID); nsIApplicationCache *aPreviousApplicationCache,
const nsACString &aClientID,
PRUint32 aType);
virtual ~nsOfflineCacheUpdateItem(); virtual ~nsOfflineCacheUpdateItem();
nsCOMPtr<nsIURI> mURI; nsCOMPtr<nsIURI> mURI;
nsCOMPtr<nsIURI> mReferrerURI; nsCOMPtr<nsIURI> mReferrerURI;
nsCOMPtr<nsIApplicationCache> mPreviousApplicationCache;
nsCString mClientID; nsCString mClientID;
nsCString mCacheKey;
PRUint32 mItemType;
nsresult OpenChannel(); nsresult OpenChannel();
nsresult Cancel(); nsresult Cancel();
@ -116,6 +120,7 @@ public:
nsOfflineManifestItem(nsOfflineCacheUpdate *aUpdate, nsOfflineManifestItem(nsOfflineCacheUpdate *aUpdate,
nsIURI *aURI, nsIURI *aURI,
nsIURI *aReferrerURI, nsIURI *aReferrerURI,
nsIApplicationCache *aPreviousApplicationCache,
const nsACString &aClientID); const nsACString &aClientID);
virtual ~nsOfflineManifestItem(); virtual ~nsOfflineManifestItem();
@ -177,19 +182,22 @@ public:
nsOfflineCacheUpdate(); nsOfflineCacheUpdate();
~nsOfflineCacheUpdate(); ~nsOfflineCacheUpdate();
static nsresult GetCacheKey(nsIURI *aURI, nsACString &aKey);
nsresult Init(); nsresult Init();
nsresult Begin(); nsresult Begin();
nsresult Cancel(); nsresult Cancel();
void LoadCompleted(); void LoadCompleted();
private: private:
nsresult HandleManifest(PRBool *aDoUpdate); nsresult HandleManifest(PRBool *aDoUpdate);
nsresult AddURI(nsIURI *aURI, const nsACString &aOwnerSpec); nsresult AddURI(nsIURI *aURI, PRUint32 aItemType);
nsresult ProcessNextURI(); nsresult ProcessNextURI();
nsresult AddOwnedItems(const nsACString &aOwnerURI); nsresult AddExistingItems(PRUint32 aType);
nsresult GatherObservers(nsCOMArray<nsIOfflineCacheUpdateObserver> &aObservers); nsresult GatherObservers(nsCOMArray<nsIOfflineCacheUpdateObserver> &aObservers);
nsresult NotifyError(); nsresult NotifyError();
@ -214,14 +222,12 @@ private:
PRBool mSucceeded; PRBool mSucceeded;
nsCString mUpdateDomain; nsCString mUpdateDomain;
nsCOMPtr<nsIURI> mManifestURI; nsCOMPtr<nsIURI> mManifestURI;
nsCString mManifestOwnerSpec;
nsCString mDynamicOwnerSpec;
nsCOMPtr<nsIURI> mDocumentURI; nsCOMPtr<nsIURI> mDocumentURI;
nsCString mClientID; nsCString mClientID;
nsCOMPtr<nsIOfflineCacheSession> mCacheSession; nsCOMPtr<nsIApplicationCache> mApplicationCache;
nsCOMPtr<nsIOfflineCacheSession> mMainCacheSession; nsCOMPtr<nsIApplicationCache> mPreviousApplicationCache;
nsCOMPtr<nsIObserverService> mObserverService; nsCOMPtr<nsIObserverService> mObserverService;