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
_getOfflineAppUsage: function (host)
{
var cacheService = Components.classes["@mozilla.org/network/cache-service;1"].
getService(Components.interfaces.nsICacheService);
var cacheSession = cacheService.createSession("HTTP-offline",
Components.interfaces.nsICache.STORE_OFFLINE,
true).
QueryInterface(Components.interfaces.nsIOfflineCacheSession);
var usage = cacheSession.getDomainUsage(host);
// XXX Bug 442810: include offline cache usage.
var usage = 0;
var storageManager = Components.classes["@mozilla.org/dom/storagemanager;1"].
getService(Components.interfaces.nsIDOMStorageManager);
usage += storageManager.getUsage(host);

View File

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

View File

@ -71,6 +71,8 @@
#include "nsIScriptGlobalObject.h"
#include "nsNetCID.h"
#include "nsIOfflineCacheUpdate.h"
#include "nsIApplicationCache.h"
#include "nsIApplicationCacheContainer.h"
#include "nsIScriptSecurityManager.h"
#include "nsIDOMLoadStatus.h"
#include "nsICookieService.h"
@ -881,6 +883,24 @@ nsContentSink::ProcessOfflineManifest(nsIContent *aElement)
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;
nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(manifestURI),
manifestSpec, mDocument,

View File

@ -804,41 +804,7 @@ nsContentUtils::GetOfflineAppManifest(nsIDOMWindow *aWindow, nsIURI **aURI)
PRBool
nsContentUtils::OfflineAppAllowed(nsIURI *aURI)
{
nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(aURI);
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;
return NS_OfflineAppAllowed(aURI, sPrefBranch);
}
// static

View File

@ -1143,6 +1143,7 @@ NS_INTERFACE_TABLE_HEAD(nsDocument)
NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIRadioGroupContainer)
NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIMutationObserver)
NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMNodeSelector)
NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIApplicationCacheContainer)
// nsNodeSH::PreCreate() depends on the identity pointer being the
// same as nsINode (which nsIDocument inherits), so if you change
// the below line, make sure nsNodeSH::PreCreate() still does the
@ -1953,6 +1954,22 @@ nsDocument::SetPrincipal(nsIPrincipal *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
nsDocument::GetContentType(nsAString& aContentType)
{

View File

@ -93,6 +93,8 @@
#include "nsCycleCollectionParticipant.h"
#include "nsContentList.h"
#include "nsGkAtoms.h"
#include "nsIApplicationCache.h"
#include "nsIApplicationCacheContainer.h"
// Put these here so all document impls get them automatically
#include "nsHTMLStyleSheet.h"
@ -408,6 +410,7 @@ class nsDocument : public nsIDocument,
public nsIScriptObjectPrincipal,
public nsIRadioGroupContainer,
public nsIDOMNodeSelector,
public nsIApplicationCacheContainer,
public nsStubMutationObserver
{
public:
@ -752,6 +755,9 @@ public:
// nsIScriptObjectPrincipal
virtual nsIPrincipal* GetPrincipal();
// nsIApplicationCacheContainer
NS_DECL_NSIAPPLICATIONCACHECONTAINER
virtual nsresult Init();
virtual nsresult AddXMLEventsContent(nsIContent * aXMLEventsElement);
@ -1021,6 +1027,10 @@ protected:
// Our update nesting level
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:
friend class nsUnblockOnloadEvent;

View File

@ -143,6 +143,9 @@
#include "nsINestedURI.h"
#include "nsITransportSecurityInfo.h"
#include "nsINSSErrorsService.h"
#include "nsIApplicationCache.h"
#include "nsIApplicationCacheContainer.h"
#include "nsIPermissionManager.h"
// Editor-related
#include "nsIEditingSession.h"
@ -458,6 +461,22 @@ NS_IMETHODIMP nsDocShell::GetInterface(const nsIID & aIID, void **aSink)
mContentViewer->GetDOMDocument((nsIDOMDocument **) aSink);
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)) &&
NS_SUCCEEDED(EnsureScriptEnvironment())) {
nsresult rv;
@ -7275,6 +7294,15 @@ nsDocShell::DoURILoad(nsIURI * aURI,
if (aFirstParty) {
// tag first party URL loads
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) {

View File

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

View File

@ -45,7 +45,6 @@
#include "nsNetCID.h"
#include "nsICacheSession.h"
#include "nsICacheService.h"
#include "nsIOfflineCacheSession.h"
#include "nsIOfflineCacheUpdate.h"
#include "nsIDOMLoadStatus.h"
#include "nsAutoPtr.h"
@ -55,6 +54,7 @@
#include "nsIPrivateDOMEvent.h"
#include "nsIObserverService.h"
#include "nsIScriptGlobalObject.h"
#include "nsIWebNavigation.h"
// Event names
@ -73,7 +73,7 @@ static const char kMaxEntriesPref[] = "offline.max_site_resources";
#define DEFAULT_MAX_ENTRIES 100
#define MAX_URI_LENGTH 2048
static nsCAutoString gCachedAsciiHost;
static nsCAutoString gCachedManifestSpec;
static char **gCachedKeys = nsnull;
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_NSCOMPTR(mWindow)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mCacheSession)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mCacheUpdate)
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_NSCOMPTR(mWindow)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mCacheSession)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mCacheUpdate)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mCheckingListeners)
@ -181,36 +179,20 @@ nsDOMOfflineResourceList::Init()
return NS_ERROR_DOM_INVALID_STATE_ERR;
}
mManifestURI->GetAsciiSpec(mManifestSpec);
nsresult rv = nsContentUtils::GetSecurityManager()->
CheckSameOriginURI(mManifestURI, mDocumentURI, PR_TRUE);
NS_ENSURE_SUCCESS(rv, rv);
// Dynamically-managed resources are stored as a separate ownership list
// from the manifest.
rv = mManifestURI->GetAsciiSpec(mDynamicOwnerSpec);
NS_ENSURE_SUCCESS(rv, rv);
mDynamicOwnerSpec.Append("#dynamic");
nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(mDocumentURI);
if (!innerURI)
return NS_ERROR_FAILURE;
rv = innerURI->GetAsciiHost(mAsciiHost);
NS_ENSURE_SUCCESS(rv, 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);
mApplicationCacheService =
do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
// Check for in-progress cache updates
@ -319,6 +301,11 @@ nsDOMOfflineResourceList::Add(const nsAString& aURI)
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;
// this will fail if the URI is not absolute
@ -374,16 +361,24 @@ nsDOMOfflineResourceList::Remove(const nsAString& aURI)
return NS_ERROR_DOM_SECURITY_ERR;
}
nsCOMPtr<nsIApplicationCache> appCache = GetDocumentAppCache();
if (!appCache) {
return NS_ERROR_DOM_INVALID_STATE_ERR;
}
nsCAutoString key;
rv = GetCacheKey(aURI, key);
NS_ENSURE_SUCCESS(rv, rv);
ClearCachedKeys();
rv = mCacheSession->RemoveOwnedKey(mAsciiHost, mDynamicOwnerSpec, key);
NS_ENSURE_SUCCESS(rv, rv);
// XXX: This is a race condition. remove() is specced to remove
// 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);
return NS_OK;
@ -394,29 +389,41 @@ nsDOMOfflineResourceList::GetStatus(PRUint16 *aStatus)
{
nsresult rv = Init();
// It is OK to check the status without a manifest attribute (you'll
// just get "uncached").
if (rv == NS_ERROR_DOM_INVALID_STATE_ERR && !mManifestURI) {
// Init may fail with INVALID_STATE_ERR if there is no manifest URI.
// The status attribute should not throw that exception, convert it
// to an UNCACHED.
if (rv == NS_ERROR_DOM_INVALID_STATE_ERR ||
!nsContentUtils::OfflineAppAllowed(mDocumentURI)) {
*aStatus = nsIDOMOfflineResourceList::UNCACHED;
return NS_OK;
}
NS_ENSURE_SUCCESS(rv, rv);
// If there is an update in process, use its status.
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
// the application is associated with a specific offline cache. Until
// we have versioned application caches, the best approximation is
// probably IDLE if the offline-app permission is set, and UNCACHED
// otherwise.
// If this object is not associated with a cache, return UNCACHED
nsCOMPtr<nsIApplicationCache> appCache = GetDocumentAppCache();
if (!appCache) {
*aStatus = nsIDOMOfflineResourceList::UNCACHED;
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;
} else {
*aStatus = nsIDOMOfflineResourceList::UNCACHED;
*aStatus = nsIDOMOfflineResourceList::UPDATEREADY;
}
return NS_OK;
@ -454,7 +461,35 @@ nsDOMOfflineResourceList::SwapCache()
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;
}
PRBool partial;
mCacheUpdate->GetPartial(&partial);
PRBool isUpgrade;
mCacheUpdate->GetIsUpgrade(&isUpgrade);
PRBool succeeded;
nsresult rv = mCacheUpdate->GetSucceeded(&succeeded);
mCacheUpdate->RemoveObserver(this);
mCacheUpdate = nsnull;
if (NS_SUCCEEDED(rv) && succeeded) {
// XXX: the spec requires a "cached" event to be sent if this is a
// first-time cache attempt, and "updateready" if this page was loaded
// from an existing application cache. Since we don't have versioned
// application caches yet, basically each update acts like a first-time
// update, so we'll always fire "cached" for now.
SendEvent(NS_LITERAL_STRING(CACHED_STR),
mOnCachedListener, mCachedListeners);
if (NS_SUCCEEDED(rv) && succeeded && !partial) {
if (isUpgrade) {
SendEvent(NS_LITERAL_STRING(UPDATEREADY_STR),
mOnUpdateReadyListener, mUpdateReadyListeners);
} else {
SendEvent(NS_LITERAL_STRING(CACHED_STR),
mOnCachedListener, mCachedListeners);
}
}
return NS_OK;
@ -981,19 +1021,61 @@ nsDOMOfflineResourceList::GetCacheKey(nsIURI *aURI, nsCString &aKey)
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
nsDOMOfflineResourceList::CacheKeys()
{
if (gCachedKeys && mAsciiHost == gCachedAsciiHost)
if (gCachedKeys && mManifestSpec == gCachedManifestSpec)
return NS_OK;
ClearCachedKeys();
nsresult rv = mCacheSession->GetOwnedKeys(mAsciiHost, mDynamicOwnerSpec,
&gCachedKeysCount, &gCachedKeys);
nsCOMPtr<nsIApplicationCache> appCache;
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))
gCachedAsciiHost = mAsciiHost;
gCachedManifestSpec = mManifestSpec;
return rv;
}
@ -1007,8 +1089,6 @@ nsDOMOfflineResourceList::ClearCachedKeys()
gCachedKeysCount = 0;
}
gCachedAsciiHost = "";
gCachedManifestSpec = "";
}

View File

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

View File

@ -51,14 +51,9 @@ fetch: function(callback)
var url = this.urls.shift();
var self = this;
var cacheService = Cc["@mozilla.org/network/cache-service;1"]
.getService(Ci.nsICacheService);
var cacheSession = cacheService.createSession("HTTP-offline",
Ci.nsICache.STORE_OFFLINE,
true);
var cacheSession = OfflineTest.getActiveSession();
cacheSession.asyncOpenCacheEntry(url, Ci.nsICache.ACCESS_READ, this);
}
};
var OfflineTest = {
@ -167,29 +162,13 @@ isnot: function(a, b, name)
clear: function()
{
// Clear the ownership list
var cacheService = Cc["@mozilla.org/network/cache-service;1"]
.getService(Ci.nsICacheService);
var cacheSession = cacheService.createSession("HTTP-offline",
Ci.nsICache.STORE_OFFLINE,
true)
.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();
// XXX: maybe we should just wipe out the entire disk cache.
var appCacheService = Cc["@mozilla.org/network/application-cache-service;1"]
.getService(Ci.nsIApplicationCacheService);
var applicationCache = this.getActiveCache();
if (applicationCache) {
applicationCache.discard();
}
},
failEvent: function(e)
@ -203,11 +182,7 @@ waitForAdd: function(url, onFinished) {
// Check every half second for ten seconds.
var numChecks = 20;
var waitFunc = function() {
var cacheService = Cc["@mozilla.org/network/cache-service;1"]
.getService(Ci.nsICacheService);
var cacheSession = cacheService.createSession("HTTP-offline",
Ci.nsICache.STORE_OFFLINE,
true);
var cacheSession = OfflineTest.getActiveSession();
var entry;
try {
var entry = cacheSession.openCacheEntry(url, Ci.nsICache.ACCESS_READ, true);
@ -236,6 +211,27 @@ getManifestUrl: function()
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)
{
var self = this;
@ -247,11 +243,16 @@ priv: function(func)
checkCache: function(url, expectEntry)
{
var cacheService = Cc["@mozilla.org/network/cache-service;1"]
.getService(Ci.nsICacheService);
var cacheSession = cacheService.createSession("HTTP-offline",
Ci.nsICache.STORE_OFFLINE,
true);
var cacheSession = this.getActiveSession();
if (!cacheSession) {
if (expectEntry) {
this.ok(false, url + " should exist in the offline cache");
} else {
this.ok(true, url + " should not exist in the offline cache");
}
return;
}
try {
var entry = cacheSession.openCacheEntry(url, Ci.nsICache.ACCESS_READ, false);
if (expectEntry) {
@ -267,7 +268,7 @@ checkCache: function(url, expectEntry)
} else {
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.
if (expectEntry) {
this.ok(true, url + " should exist in the offline cache");

View File

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

View File

@ -36,26 +36,6 @@ function addFinished()
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()
{
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("bad:/uri/invalid", false);
// Remove items from the cache.
OfflineTest.clear();
applicationCache.swapCache();
// 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);
OfflineTest.checkCache("http://localhost:8888/MochiKit/packed.js", false);
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 add a file using the applicationCache API
applicationCache.add("http://localhost:8888/tests/SimpleTest/EventUtils.js");
// Now make sure applicationCache.update() does what we expect.
applicationCache.oncached = OfflineTest.priv(secondUpdate);
gGotChecking = false;
gGotDownloading = false;
applicationCache.update();
// Wait for the add() to be downloaded
OfflineTest.waitForAdd("http://localhost:8888/tests/SimpleTest/EventUtils.js",
OfflineTest.priv(addFinished));
}
if (OfflineTest.setup()) {
OfflineTest.ok(applicationCache instanceof EventTarget,
"applicationCache should be an event target");

View File

@ -61,6 +61,9 @@ SDK_XPIDLSRCS = \
$(NULL)
XPIDLSRCS = \
nsIApplicationCache.idl \
nsIApplicationCacheContainer.idl \
nsIApplicationCacheService.idl \
nsIAuthInformation.idl \
nsIAuthPrompt.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"
interface nsIURI;
interface nsIPrefBranch;
/**
* nsINetUtil provides various network-related utility methods.
@ -220,3 +221,25 @@ interface nsINetUtil : nsISupports
out long aCharsetStart,
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);
}
/**
* 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__

View File

@ -70,6 +70,7 @@
#include "nsINestedURI.h"
#include "nsNetUtil.h"
#include "nsThreadUtils.h"
#include "nsIPermissionManager.h"
#if defined(XP_WIN)
#include "nsNativeConnectionHelper.h"
@ -278,12 +279,13 @@ nsIOService::GetInstance() {
return gIOService;
}
NS_IMPL_THREADSAFE_ISUPPORTS5(nsIOService,
NS_IMPL_THREADSAFE_ISUPPORTS6(nsIOService,
nsIIOService,
nsIIOService2,
nsINetUtil,
nsIObserver,
nsISupportsWeakReference)
nsISupportsWeakReference,
nsINetUtil_MOZILLA_1_9_1)
////////////////////////////////////////////////////////////////////////////////
@ -983,3 +985,58 @@ nsIOService::ExtractCharsetFromContentType(const nsACString &aTypeHeader,
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
, public nsIObserver
, public nsINetUtil
, public nsINetUtil_MOZILLA_1_9_1
, public nsSupportsWeakReference
{
public:
@ -85,6 +86,7 @@ public:
NS_DECL_NSIIOSERVICE2
NS_DECL_NSIOBSERVER
NS_DECL_NSINETUTIL
NS_DECL_NSINETUTIL_MOZILLA_1_9_1
// Gets the singleton instance of the IO Service, creating it as needed
// 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
endif
ifdef MOZ_STORAGE
REQUIRES += storage
DEFINES += -DNECKO_OFFLINE_CACHE
endif
EXTRA_DSO_LDOPTS = \
$(LIBS_DIR) \
$(EXTRA_DSO_LIBS) \

View File

@ -465,6 +465,18 @@
{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

View File

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

View File

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

View File

@ -86,11 +86,7 @@ interface nsICacheService : nsISupports
void evictEntries(in nsCacheStoragePolicy storagePolicy);
/**
* Return a unique, temporary cache client ID.
*
* This is used by the offline cache. The offline cache lets clients
* accumulate entries in a temporary client and merge them in as a group
* using nsIOfflineCacheSession.mergeTemporaryClient().
* This method is deprecated and will throw NS_ERROR_NOT_IMPLEMENTED.
*/
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
#ifdef NECKO_OFFLINE_CACHE
delete mOfflineDevice;
mOfflineDevice = nsnull;
NS_IF_RELEASE(mOfflineDevice);
#endif // !NECKO_OFFLINE_CACHE
#if defined(NECKO_DISK_CACHE) && defined(PR_LOGGING)
@ -829,245 +828,6 @@ nsCacheService::IsStorageEnabledForPolicy_Locked(nsCacheStoragePolicy storagePo
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_ENSURE_ARG_POINTER(visitor);
@ -1124,20 +884,7 @@ NS_IMETHODIMP nsCacheService::EvictEntries(nsCacheStoragePolicy storagePolicy)
NS_IMETHODIMP nsCacheService::CreateTemporaryClientID(nsCacheStoragePolicy storagePolicy,
nsACString &clientID)
{
#ifdef NECKO_OFFLINE_CACHE
// Only the offline cache device supports temporary clients
if (storagePolicy != nsICache::STORE_OFFLINE)
return NS_ERROR_NOT_AVAILABLE;
if (!gService->mOfflineDevice) {
nsresult rv = gService->CreateOfflineDevice();
if (NS_FAILED(rv)) return rv;
}
return gService->mOfflineDevice->CreateTemporaryClientID(clientID);
#else // !NECKO_OFFLINE_CACHE
return NS_ERROR_NOT_IMPLEMENTED;
#endif
}
/**
@ -1188,6 +935,8 @@ nsCacheService::CreateOfflineDevice()
mOfflineDevice = new nsOfflineCacheDevice;
if (!mOfflineDevice) return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(mOfflineDevice);
// set the preferences
mOfflineDevice->SetCacheParentDirectory(
mObserver->OfflineCacheParentDirectory());
@ -1199,8 +948,7 @@ nsCacheService::CreateOfflineDevice()
CACHE_LOG_DEBUG((" - disabling offline cache for this session.\n"));
mEnableOfflineDevice = PR_FALSE;
delete mOfflineDevice;
mOfflineDevice = nsnull;
NS_RELEASE(mOfflineDevice);
}
return rv;
#else // !NECKO_DISK_CACHE

View File

@ -97,54 +97,6 @@ public:
static nsresult IsStorageEnabledForPolicy(nsCacheStoragePolicy storagePolicy,
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
*/
@ -208,6 +160,7 @@ public:
void Shutdown();
private:
friend class nsCacheServiceAutoLock;
friend class nsOfflineCacheDevice;
/**
* Internal Methods

View File

@ -44,15 +44,7 @@
#include "nsCacheService.h"
#include "nsCRT.h"
NS_IMPL_ADDREF(nsCacheSession)
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
NS_IMPL_ISUPPORTS1(nsCacheSession, nsICacheSession)
nsCacheSession::nsCacheSession(const char * clientID,
nsCacheStoragePolicy storagePolicy,
@ -135,75 +127,3 @@ NS_IMETHODIMP nsCacheSession::IsStorageEnabled(PRBool *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 "nsError.h"
#include "nsICacheSession.h"
#include "nsIOfflineCacheSession.h"
#include "nsString.h"
class nsCacheSession : public nsICacheSession
, public nsIOfflineCacheSession
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSICACHESESSION
NS_DECL_NSIOFFLINECACHESESSION
nsCacheSession(const char * clientID, nsCacheStoragePolicy storagePolicy, PRBool streamBased);
virtual ~nsCacheSession();

View File

@ -43,6 +43,7 @@
#include "nsNetUtil.h"
#include "nsAutoPtr.h"
#include "nsEscape.h"
#include "nsString.h"
#include "nsPrintfCString.h"
#include "nsCRT.h"
@ -57,6 +58,7 @@
#include "nsISeekableStream.h"
static const char OFFLINE_CACHE_DEVICE_ID[] = { "offline" };
static NS_DEFINE_CID(kCacheServiceCID, NS_CACHESERVICE_CID);
#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
*/
NS_IMPL_ISUPPORTS1(nsOfflineCacheDevice, nsIApplicationCacheService)
nsOfflineCacheDevice::nsOfflineCacheDevice()
: mDB(nsnull)
, mCacheCapacity(0)
@ -697,6 +833,23 @@ nsOfflineCacheDevice::DeleteData(nsCacheEntry *entry)
* 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
nsOfflineCacheDevice::Init()
{
@ -735,8 +888,8 @@ nsOfflineCacheDevice::Init()
// "Generation" is the data file generation number.
// "Flags" is a bit-field indicating the state of the entry.
//
mDB->ExecuteSimpleSQL(
NS_LITERAL_CSTRING("CREATE TABLE moz_cache (\n"
rv = mDB->ExecuteSimpleSQL(
NS_LITERAL_CSTRING("CREATE TABLE IF NOT EXISTS moz_cache (\n"
" ClientID TEXT,\n"
" Key TEXT,\n"
" MetaData BLOB,\n"
@ -746,24 +899,38 @@ nsOfflineCacheDevice::Init()
" FetchCount INTEGER,\n"
" LastFetched INTEGER,\n"
" LastModified INTEGER,\n"
" ExpirationTime INTEGER\n"
" ExpirationTime INTEGER,\n"
" ItemType INTEGER DEFAULT 0\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(
NS_LITERAL_CSTRING("CREATE TABLE moz_cache_owners (\n"
" ClientID TEXT,\n"
" Domain TEXT,\n"
" URI TEXT,\n"
" Key TEXT\n"
NS_LITERAL_CSTRING("ALTER TABLE moz_cache ADD ItemType INTEGER DEFAULT 0"));
// Create the table for storing cache groups. All actions on
// moz_cache_groups use the GroupID, so use it as the primary key.
rv = mDB->ExecuteSimpleSQL(
NS_LITERAL_CSTRING("CREATE TABLE IF NOT EXISTS moz_cache_groups (\n"
" GroupID TEXT PRIMARY KEY,\n"
" ActiveClientID TEXT\n"
");\n"));
// maybe the table already exists, so don't bother checking for errors.
NS_ENSURE_SUCCESS(rv, rv);
mDB->ExecuteSimpleSQL(
NS_LITERAL_CSTRING("CREATE UNIQUE INDEX moz_cache_index"
" ON moz_cache (ClientID, Key);"));
// maybe the index already exists, so don't bother checking for errors.
// Databases from 1.9.0 have a moz_cache_index that should be dropped
rv = mDB->ExecuteSimpleSQL(
NS_LITERAL_CSTRING("DROP INDEX IF EXISTS moz_cache_index"));
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);
if (!mEvictionFunction) return NS_ERROR_OUT_OF_MEMORY;
@ -779,7 +946,8 @@ nsOfflineCacheDevice::Init()
statement (aStatement), sql (aSql) {}
} prepared[] = {
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_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 = ?;" ),
@ -787,25 +955,25 @@ nsOfflineCacheDevice::Init()
StatementSql ( mStatement_DeleteEntry, "DELETE FROM moz_cache WHERE ClientID = ? AND Key = ?;" ),
StatementSql ( mStatement_FindEntry, "SELECT MetaData, Generation, Flags, DataSize, FetchCount, LastFetched, LastModified, ExpirationTime FROM moz_cache WHERE ClientID = ? AND Key = ?;" ),
StatementSql ( mStatement_BindEntry, "INSERT INTO moz_cache (ClientID, Key, MetaData, Generation, Flags, DataSize, FetchCount, LastFetched, LastModified, ExpirationTime) VALUES(?,?,?,?,?,?,?,?,?,?);" ),
StatementSql ( mStatement_ClearOwnership, "DELETE FROM moz_cache_owners WHERE ClientId = ? AND Domain = ? AND URI = ?;" ),
StatementSql ( mStatement_RemoveOwnership, "DELETE FROM moz_cache_owners WHERE ClientID = ? AND Domain = ? AND URI = ? AND Key = ?;" ),
StatementSql ( mStatement_ClearDomain, "DELETE FROM moz_cache_owners WHERE ClientID = ? AND Domain = ?;" ),
StatementSql ( mStatement_AddOwnership, "INSERT INTO moz_cache_owners (ClientID, Domain, URI, Key) VALUES (?, ?, ?, ?);" ),
StatementSql ( mStatement_CheckOwnership, "SELECT Key From moz_cache_owners WHERE ClientID = ? AND Domain = ? AND URI = ? AND Key = ?;" ),
StatementSql ( mStatement_ListOwned, "SELECT Key FROM moz_cache_owners WHERE ClientID = ? AND Domain = ? AND URI = ?;" ),
StatementSql ( mStatement_ListOwners, "SELECT DISTINCT Domain, URI FROM moz_cache_owners WHERE ClientID = ?;"),
StatementSql ( mStatement_ListOwnerDomains, "SELECT DISTINCT Domain FROM moz_cache_owners WHERE ClientID = ?;"),
StatementSql ( mStatement_ListOwnerURIs, "SELECT DISTINCT URI FROM moz_cache_owners WHERE ClientID = ? AND Domain = ?;"),
StatementSql ( mStatement_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_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 = ?;")
StatementSql ( mStatement_MarkEntry, "UPDATE moz_cache SET ItemType = (ItemType | ?) WHERE ClientID = ? AND Key = ?;" ),
StatementSql ( mStatement_UnmarkEntry, "UPDATE moz_cache SET ItemType = (ItemType & ~?) WHERE ClientID = ? AND Key = ?;" ),
StatementSql ( mStatement_GetTypes, "SELECT ItemType FROM moz_cache WHERE ClientID = ? AND Key = ?;"),
StatementSql ( mStatement_CleanupUnmarked, "DELETE FROM moz_cache WHERE ClientID = ? AND Key = ? AND ItemType = 0;" ),
StatementSql ( mStatement_GatherEntries, "SELECT Key FROM moz_cache WHERE ClientID = ? AND (ItemType & ?) > 0;" ),
StatementSql ( mStatement_ActivateClient, "INSERT OR REPLACE INTO moz_cache_groups (GroupID, ActiveClientID) VALUES (?, ?);" ),
StatementSql ( mStatement_DeactivateGroup, "DELETE FROM moz_cache_groups WHERE GroupID = ?;" ),
StatementSql ( mStatement_FindClient, "SELECT ClientID FROM moz_cache WHERE Key = ? ORDER BY LastFetched DESC, LastModified DESC;")
};
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),
getter_AddRefs(prepared[i].statement));
LOG(("Creating statement: %s\n", prepared[i].sql));
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
rv = mDB->ExecuteSimpleSQL(
@ -814,24 +982,83 @@ nsOfflineCacheDevice::Init()
" WHERE (Flags & 1);"));
NS_ENSURE_SUCCESS(rv, rv);
// Clear up dangling temporary sessions
EvictionObserver evictionObserver(mDB, mEvictionFunction);
rv = mDB->ExecuteSimpleSQL(
NS_LITERAL_CSTRING("DELETE FROM moz_cache"
" WHERE (ClientID GLOB \"TempClient*\")"));
rv = InitActiveCaches();
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;
}
/* 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
nsOfflineCacheDevice::Shutdown()
{
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;
mEvictionFunction = 0;
@ -1026,7 +1253,6 @@ nsOfflineCacheDevice::BindEntry(nsCacheEntry *entry)
PRBool hasRows;
rv = statement->ExecuteStep(&hasRows);
NS_ENSURE_SUCCESS(rv, rv);
NS_ASSERTION(!hasRows, "INSERT should not result in output");
entry->SetData(binding);
@ -1250,23 +1476,25 @@ nsOfflineCacheDevice::EvictEntries(const char *clientID)
// so we can delete the corresponding data file.
EvictionObserver evictionObserver(mDB, mEvictionFunction);
const char *deleteCmd;
nsCOMPtr<mozIStorageStatement> statement;
nsresult rv;
if (clientID)
{
deleteCmd =
PR_smprintf("DELETE FROM moz_cache WHERE ClientID=%q AND Flags=0;",
clientID);
if (!deleteCmd)
return NS_ERROR_OUT_OF_MEMORY;
rv = mDB->CreateStatement(NS_LITERAL_CSTRING("DELETE FROM moz_cache WHERE ClientID=? AND Flags = 0;"),
getter_AddRefs(statement));
NS_ENSURE_SUCCESS(rv, rv);
rv = statement->BindUTF8StringParameter(0, nsDependentCString(clientID));
NS_ENSURE_SUCCESS(rv, rv);
}
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));
if (clientID)
PR_smprintf_free((char *) deleteCmd);
rv = statement->Execute();
NS_ENSURE_SUCCESS(rv, rv);
evictionObserver.Apply();
@ -1274,6 +1502,110 @@ nsOfflineCacheDevice::EvictEntries(const char *clientID)
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
nsOfflineCacheDevice::RunSimpleQuery(mozIStorageStatement * statement,
PRUint32 resultIndex,
@ -1312,312 +1644,190 @@ nsOfflineCacheDevice::RunSimpleQuery(mozIStorageStatement * statement,
return NS_OK;
}
nsresult
nsOfflineCacheDevice::GetOwnerDomains(const char * clientID,
PRUint32 * count,
char *** domains)
NS_IMETHODIMP
nsOfflineCacheDevice::CreateApplicationCache(const nsACString &group,
nsIApplicationCache **out)
{
LOG(("nsOfflineCacheDevice::GetOwnerDomains [cid=%s]\n", clientID));
*out = nsnull;
AutoResetStatement statement(mStatement_ListOwnerDomains);
nsresult rv = statement->BindUTF8StringParameter(
0, nsDependentCString(clientID));
NS_ENSURE_SUCCESS(rv, rv);
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);
nsCString clientID;
// Some characters are special in the clientID. Escape the groupID
// before putting it in to the client key.
if (!NS_Escape(nsCString(group), clientID, url_Path)) {
return NS_ERROR_OUT_OF_MEMORY;
}
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
nsOfflineCacheDevice::GetOwnedKeys(const char * clientID,
const nsACString & ownerDomain,
const nsACString & ownerURI,
PRUint32 * count,
char *** keys)
NS_IMETHODIMP
nsOfflineCacheDevice::GetApplicationCache(const nsACString &clientID,
nsIApplicationCache **out)
{
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(
0, nsDependentCString(clientID));
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);
0, key);
NS_ENSURE_SUCCESS(rv, rv);
PRBool hasRows;
rv = statement->ExecuteStep(&hasRows);
NS_ENSURE_SUCCESS(rv, rv);
if (!hasRows)
return NS_OK;
while (hasRows) {
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;
}
nsresult
nsOfflineCacheDevice::EvictUnownedEntries(const char *clientID)
nsOfflineCacheDevice::ActivateCache(const nsCSubstring &group,
const nsCSubstring &clientID)
{
LOG(("nsOfflineCacheDevice::EvictUnownedEntries [cid=%s]\n", clientID));
EvictionObserver evictionObserver(mDB, mEvictionFunction);
AutoResetStatement statement(mStatement_DeleteUnowned);
nsresult rv = statement->BindUTF8StringParameter(
0, nsDependentCString(clientID));
AutoResetStatement statement(mStatement_ActivateClient);
nsresult rv = statement->BindUTF8StringParameter(0, group);
NS_ENSURE_SUCCESS(rv, rv);
rv = statement->BindUTF8StringParameter(1, clientID);
NS_ENSURE_SUCCESS(rv, rv);
rv = statement->Execute();
NS_ENSURE_SUCCESS(rv, rv);
evictionObserver.Apply();
return NS_OK;
}
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)
nsCString *active;
if (mActiveCachesByGroup.Get(group, &active))
{
PRUint32 length;
domainArray.AppendElement(
nsDependentCString(listOwnersStatement->AsSharedUTF8String(0, &length)));
uriArray.AppendElement(
nsDependentCString(listOwnersStatement->AsSharedUTF8String(1, &length)));
rv = listOwnersStatement->ExecuteStep(&hasRows);
NS_ENSURE_SUCCESS(rv, rv);
mActiveCaches.Remove(*active);
mActiveCachesByGroup.Remove(group);
active = nsnull;
}
// Now move over each ownership set
for (PRUint32 i = 0; i < domainArray.Length(); i++) {
PRUint32 count;
char **keys;
rv = GetOwnedKeys(fromClientID, domainArray[i], uriArray[i],
&count, &keys);
NS_ENSURE_SUCCESS(rv, rv);
rv = SetOwnedKeys(clientID, domainArray[i], uriArray[i],
count, const_cast<const char **>(keys));
NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(count, keys);
NS_ENSURE_SUCCESS(rv, rv);
// Now clear out the temporary session's copy
rv = SetOwnedKeys(fromClientID, domainArray[i], uriArray[i], 0, 0);
NS_ENSURE_SUCCESS(rv, rv);
if (!clientID.IsEmpty())
{
mActiveCaches.Put(clientID);
mActiveCachesByGroup.Put(group, new nsCString(clientID));
}
EvictionObserver evictionObserver(mDB, mEvictionFunction);
return NS_OK;
}
AutoResetStatement deleteStatement(mStatement_DeleteConflicts);
rv = deleteStatement->BindUTF8StringParameter(
0, nsDependentCString(clientID));
rv |= deleteStatement->BindUTF8StringParameter(
1, nsDependentCString(fromClientID));
PRBool
nsOfflineCacheDevice::IsActiveCache(const nsCSubstring &group,
const nsCSubstring &clientID)
{
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);
rv = deleteStatement->Execute();
rv = statement->Execute();
NS_ENSURE_SUCCESS(rv, rv);
AutoResetStatement swapStatement(mStatement_SwapClientID);
rv = swapStatement->BindUTF8StringParameter(
0, nsDependentCString(clientID));
rv |= swapStatement->BindUTF8StringParameter(
1, nsDependentCString(fromClientID));
NS_ENSURE_SUCCESS(rv, rv);
if (mActiveCachesByGroup.Get(group, &active))
{
mActiveCaches.Remove(*active);
mActiveCachesByGroup.Remove(group);
active = nsnull;
}
rv = swapStatement->Execute();
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
rv = transaction.Commit();
NS_ENSURE_SUCCESS(rv, rv);
evictionObserver.Apply();
nsresult
nsOfflineCacheDevice::GetGroupForCache(const nsACString &clientID,
nsCString &out)
{
out.Assign(clientID);
out.Truncate(out.FindChar('|'));
NS_UnescapeURL(out);
return NS_OK;
}

View File

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

View File

@ -47,6 +47,7 @@
#include "nsHttpResponseHead.h"
#include "nsHttp.h"
#include "nsIHttpAuthenticator.h"
#include "nsIApplicationCacheService.h"
#include "nsIAuthInformation.h"
#include "nsIAuthPrompt2.h"
#include "nsIAuthPromptProvider.h"
@ -1371,26 +1372,61 @@ nsHttpChannel::OpenCacheEntry(PRBool offline, PRBool *delayed)
else
accessRequested = nsICache::ACCESS_READ_WRITE; // normal browsing
nsCOMPtr<nsICacheSession> session;
if (mLoadFlags & LOAD_CHECK_OFFLINE_CACHE) {
// when LOAD_CHECK_OFFLINE_CACHE set prefer the offline cache
rv = gHttpHandler->GetCacheSession(nsICache::STORE_OFFLINE,
getter_AddRefs(session));
if (NS_FAILED(rv)) return rv;
if (!mApplicationCache) {
// Pick up an application cache from the load group if available
nsCOMPtr<nsIApplicationCacheContainer> appCacheContainer;
GetCallback(appCacheContainer);
// 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.
if (appCacheContainer) {
appCacheContainer->GetApplicationCache(getter_AddRefs(mApplicationCache));
}
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
// the offline cache entry non-atomically so ACCESS_READ
// will prevent us from writing to the HTTP-offline cache as a
// normal cache entry.
rv = session->OpenCacheEntry(cacheKey, nsICache::ACCESS_READ, PR_FALSE,
// we need open in ACCESS_READ only because we don't want to
// overwrite the offline cache entry non-atomically so
// ACCESS_READ will prevent us from writing to the
// HTTP-offline cache as a normal cache entry. XXX: this
// needs to be checked.
rv = session->OpenCacheEntry(cacheKey,
nsICache::ACCESS_READ, PR_FALSE,
getter_AddRefs(mCacheEntry));
}
if (!(mLoadFlags & LOAD_CHECK_OFFLINE_CACHE) ||
if (!mApplicationCache ||
(NS_FAILED(rv) && rv != NS_ERROR_CACHE_WAIT_FOR_VALIDATION))
{
rv = gHttpHandler->GetCacheSession(storagePolicy,
@ -1457,20 +1493,18 @@ nsHttpChannel::OpenOfflineCacheEntryForWriting()
nsCAutoString cacheKey;
GenerateCacheKey(cacheKey);
nsCOMPtr<nsICacheSession> session;
if (!mOfflineCacheClientID.IsEmpty()) {
nsCOMPtr<nsICacheService> serv =
do_GetService(NS_CACHESERVICE_CONTRACTID, &rv);
if (NS_FAILED(rv)) return rv;
NS_ENSURE_TRUE(!mOfflineCacheClientID.IsEmpty(),
NS_ERROR_NOT_AVAILABLE);
rv = serv->CreateSession(mOfflineCacheClientID.get(),
nsICache::STORE_OFFLINE,
nsICache::STREAM_BASED,
getter_AddRefs(session));
} else {
rv = gHttpHandler->GetCacheSession(nsICache::STORE_OFFLINE,
getter_AddRefs(session));
}
nsCOMPtr<nsICacheSession> session;
nsCOMPtr<nsICacheService> serv =
do_GetService(NS_CACHESERVICE_CONTRACTID, &rv);
if (NS_FAILED(rv)) return rv;
rv = serv->CreateSession(mOfflineCacheClientID.get(),
nsICache::STORE_OFFLINE,
nsICache::STREAM_BASED,
getter_AddRefs(session));
if (NS_FAILED(rv)) return rv;
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(nsIProtocolProxyCallback)
NS_INTERFACE_MAP_ENTRY(nsIProxiedChannel)
NS_INTERFACE_MAP_ENTRY(nsIApplicationCacheContainer)
NS_INTERFACE_MAP_END_INHERITING(nsHashPropertyBag)
//-----------------------------------------------------------------------------
@ -4876,6 +4911,24 @@ nsHttpChannel::DoAuthRetry(nsAHttpConnection *conn)
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>
//-----------------------------------------------------------------------------

View File

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

View File

@ -115,7 +115,7 @@ interface nsIOfflineCacheUpdateObserver : nsISupports {
* load its items one by one, sending itemCompleted() to any registered
* observers.
*/
[scriptable, uuid(7dc06ede-1098-4384-b95e-65525ab254c9)]
[scriptable, uuid(4b206247-82ee-46cf-a8b7-f7284e753bc2)]
interface nsIOfflineCacheUpdate : nsISupports {
/**
* Fetch the status of the running update. This will return a value
@ -129,6 +129,12 @@ interface nsIOfflineCacheUpdate : nsISupports {
*/
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
* with this update.

View File

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

View File

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