Bug 461071: Allow application caches to be obsoleted. r=honzab, r+sr=bz
--- a/dom/public/idl/offline/nsIDOMOfflineResourceList.idl
+++ b/dom/public/idl/offline/nsIDOMOfflineResourceList.idl
@@ -33,17 +33,17 @@
* 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 "domstubs.idl"
-[scriptable, uuid(8449bce2-0d8c-4c74-ab79-b41b8d81f1c4)]
+[scriptable, uuid(c9732cb7-e104-4cf1-9936-a8592fd46c5a)]
interface nsIDOMOfflineResourceList : nsISupports
{
/**
* Enumerate the list of dynamically-managed entries.
*/
readonly attribute unsigned long length;
DOMString item(in unsigned long index);
@@ -77,40 +77,37 @@ interface nsIDOMOfflineResourceList : ns
const unsigned short IDLE = 1;
/* The manifest is being fetched and checked for updates */
const unsigned short CHECKING = 2;
/* Resources are being downloaded to be added to the cache */
const unsigned short DOWNLOADING = 3;
- /**
- * There is a new version of the application cache available
- *
- * Versioned application caches are not currently implemented, so this
- * value will not yet be returned
- */
+ /* There is a new version of the application cache available */
const unsigned short UPDATEREADY = 4;
+ /* The application cache group is now obsolete. */
+ const unsigned short OBSOLETE = 5;
+
readonly attribute unsigned short status;
/**
* Begin the application update process on the associated application cache.
*/
void update();
/**
- * Swap in the newest version of the application cache.
- *
- * Versioned application caches are not currently implemented, so this
- * method will throw an exception.
+ * Swap in the newest version of the application cache, or disassociate
+ * from the cache if the cache group is obsolete.
*/
void swapCache();
/* Events */
attribute nsIDOMEventListener onchecking;
attribute nsIDOMEventListener onerror;
attribute nsIDOMEventListener onnoupdate;
attribute nsIDOMEventListener ondownloading;
attribute nsIDOMEventListener onprogress;
attribute nsIDOMEventListener onupdateready;
attribute nsIDOMEventListener oncached;
+ attribute nsIDOMEventListener onobsolete;
};
--- a/dom/src/offline/nsDOMOfflineResourceList.cpp
+++ b/dom/src/offline/nsDOMOfflineResourceList.cpp
@@ -60,16 +60,17 @@
#define CHECKING_STR "checking"
#define ERROR_STR "error"
#define NOUPDATE_STR "noupdate"
#define DOWNLOADING_STR "downloading"
#define PROGRESS_STR "progress"
#define CACHED_STR "cached"
#define UPDATEREADY_STR "updateready"
+#define OBSOLETE_STR "obsolete"
// To prevent abuse of the resource list for data storage, the number
// of offline urls and their length are limited.
static const char kMaxEntriesPref[] = "offline.max_site_resources";
#define DEFAULT_MAX_ENTRIES 100
#define MAX_URI_LENGTH 2048
@@ -85,24 +86,26 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mCheckingListeners)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mErrorListeners)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mNoUpdateListeners)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mDownloadingListeners)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mProgressListeners)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mCachedListeners)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mUpdateReadyListeners)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mObsoleteListeners)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnCheckingListener)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnErrorListener)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnNoUpdateListener)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnDownloadingListener)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnProgressListener)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnCachedListener)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnUpdateReadyListener)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnObsoleteListener)
for (PRUint32 i = 0; i < tmp->mPendingEvents.Length(); i++) {
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mPendingEvents[i].event);
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mPendingEvents[i].listener);
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mPendingEvents[i].listeners);
}
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
@@ -113,24 +116,26 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mCheckingListeners)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mErrorListeners)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mNoUpdateListeners)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mDownloadingListeners)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mProgressListeners)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mCachedListeners)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mUpdateReadyListeners)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mObsoleteListeners)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnCheckingListener)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnErrorListener)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnNoUpdateListener)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnDownloadingListener)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnProgressListener)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnCachedListener)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnUpdateReadyListener)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnObsoleteListener)
for (PRUint32 i = 0; i < tmp->mPendingEvents.Length(); i++) {
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mPendingEvents[i].event);
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mPendingEvents[i].listener);
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mPendingEvents[i].listeners);
}
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
@@ -232,24 +237,26 @@ nsDOMOfflineResourceList::Disconnect()
{
mCheckingListeners.Clear();
mErrorListeners.Clear();
mNoUpdateListeners.Clear();
mDownloadingListeners.Clear();
mProgressListeners.Clear();
mCachedListeners.Clear();
mUpdateReadyListeners.Clear();
+ mObsoleteListeners.Clear();
mOnCheckingListener = nsnull;
mOnErrorListener = nsnull;
mOnNoUpdateListener = nsnull;
mOnDownloadingListener = nsnull;
mOnProgressListener = nsnull;
mOnCachedListener = nsnull;
mOnUpdateReadyListener = nsnull;
+ mOnObsoleteListener = nsnull;
mPendingEvents.Clear();
}
//
// nsDOMOfflineResourceList::nsIDOMOfflineResourceList
//
@@ -418,17 +425,19 @@ nsDOMOfflineResourceList::GetStatus(PRUi
return NS_OK;
}
nsCOMPtr<nsIApplicationCache> activeCache;
rv = mApplicationCacheService->GetActiveCache(mManifestSpec,
getter_AddRefs(activeCache));
NS_ENSURE_SUCCESS(rv, rv);
- if (appCache == activeCache) {
+ if (activeCache == nsnull) {
+ *aStatus = nsIDOMOfflineResourceList::OBSOLETE;
+ } else if (appCache == activeCache) {
*aStatus = nsIDOMOfflineResourceList::IDLE;
} else {
*aStatus = nsIDOMOfflineResourceList::UPDATEREADY;
}
return NS_OK;
}
@@ -473,17 +482,20 @@ nsDOMOfflineResourceList::SwapCache()
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) {
+ // In the case of an obsolete cache group, newAppCache might be null.
+ // We will disassociate from the cache in that case.
+
+ if (newAppCache == currentAppCache) {
return NS_ERROR_DOM_INVALID_STATE_ERR;
}
ClearCachedKeys();
nsCOMPtr<nsIApplicationCacheContainer> appCacheContainer =
GetDocumentAppCacheContainer();
@@ -643,16 +655,37 @@ nsDOMOfflineResourceList::SetOnupdaterea
nsresult rv = Init();
NS_ENSURE_SUCCESS(rv, rv);
mOnUpdateReadyListener = aOnupdateready;
return NS_OK;
}
NS_IMETHODIMP
+nsDOMOfflineResourceList::GetOnobsolete(nsIDOMEventListener **aOnobsolete)
+{
+ nsresult rv = Init();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ NS_ENSURE_ARG_POINTER(aOnobsolete);
+ NS_IF_ADDREF(*aOnobsolete = mOnObsoleteListener);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMOfflineResourceList::SetOnobsolete(nsIDOMEventListener *aOnobsolete)
+{
+ nsresult rv = Init();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mOnObsoleteListener = aOnobsolete;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
nsDOMOfflineResourceList::AddEventListener(const nsAString& aType,
nsIDOMEventListener *aListener,
PRBool aUseCapture)
{
nsresult rv = Init();
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_ARG(aListener);
@@ -666,16 +699,17 @@ nsDOMOfflineResourceList::AddEventListen
IMPL_ADD_LISTENER(CHECKING_STR, mCheckingListeners)
IMPL_ADD_LISTENER(ERROR_STR, mErrorListeners)
IMPL_ADD_LISTENER(NOUPDATE_STR, mNoUpdateListeners)
IMPL_ADD_LISTENER(DOWNLOADING_STR, mDownloadingListeners)
IMPL_ADD_LISTENER(PROGRESS_STR, mProgressListeners)
IMPL_ADD_LISTENER(CACHED_STR, mCachedListeners)
IMPL_ADD_LISTENER(UPDATEREADY_STR, mUpdateReadyListeners)
+ IMPL_ADD_LISTENER(OBSOLETE_STR, mObsoleteListeners)
{
return NS_ERROR_INVALID_ARG;
}
array->AppendObject(aListener);
#undef IMPL_ADD_LISTENER
return NS_OK;
@@ -700,16 +734,17 @@ nsDOMOfflineResourceList::RemoveEventLis
IMPL_REMOVE_LISTENER(CHECKING_STR, mCheckingListeners)
IMPL_REMOVE_LISTENER(ERROR_STR, mErrorListeners)
IMPL_REMOVE_LISTENER(NOUPDATE_STR, mNoUpdateListeners)
IMPL_REMOVE_LISTENER(DOWNLOADING_STR, mDownloadingListeners)
IMPL_REMOVE_LISTENER(PROGRESS_STR, mProgressListeners)
IMPL_REMOVE_LISTENER(CACHED_STR, mCachedListeners)
IMPL_REMOVE_LISTENER(UPDATEREADY_STR, mUpdateReadyListeners)
+ IMPL_REMOVE_LISTENER(OBSOLETE_STR, mObsoleteListeners)
{
return NS_ERROR_INVALID_ARG;
}
// Allow a caller to remove O(N^2) behavior by removing end-to-start.
for (PRUint32 i = array->Count() - 1; i != PRUint32(-1); --i) {
if (array->ObjectAt(i) == aListener) {
array->RemoveObjectAt(i);
@@ -921,16 +956,24 @@ nsDOMOfflineResourceList::ItemStarted(ns
NS_IMETHODIMP
nsDOMOfflineResourceList::ItemCompleted(nsIOfflineCacheUpdate *aUpdate,
nsIDOMLoadStatus *aItem)
{
return NS_OK;
}
+NS_IMETHODIMP
+nsDOMOfflineResourceList::Obsolete(nsIOfflineCacheUpdate *aUpdate)
+{
+ SendEvent(NS_LITERAL_STRING(OBSOLETE_STR),
+ mOnObsoleteListener, mObsoleteListeners);
+ return NS_OK;
+}
+
nsresult
nsDOMOfflineResourceList::GetCacheKey(const nsAString &aURI, nsCString &aKey)
{
nsCOMPtr<nsIURI> requestedURI;
nsresult rv = NS_NewURI(getter_AddRefs(requestedURI), aURI);
NS_ENSURE_SUCCESS(rv, rv);
return GetCacheKey(requestedURI, aKey);
--- a/dom/src/offline/nsDOMOfflineResourceList.h
+++ b/dom/src/offline/nsDOMOfflineResourceList.h
@@ -126,24 +126,26 @@ private:
nsCOMArray<nsIDOMEventListener> mCheckingListeners;
nsCOMArray<nsIDOMEventListener> mErrorListeners;
nsCOMArray<nsIDOMEventListener> mNoUpdateListeners;
nsCOMArray<nsIDOMEventListener> mDownloadingListeners;
nsCOMArray<nsIDOMEventListener> mProgressListeners;
nsCOMArray<nsIDOMEventListener> mCachedListeners;
nsCOMArray<nsIDOMEventListener> mUpdateReadyListeners;
+ nsCOMArray<nsIDOMEventListener> mObsoleteListeners;
nsCOMPtr<nsIDOMEventListener> mOnCheckingListener;
nsCOMPtr<nsIDOMEventListener> mOnErrorListener;
nsCOMPtr<nsIDOMEventListener> mOnNoUpdateListener;
nsCOMPtr<nsIDOMEventListener> mOnDownloadingListener;
nsCOMPtr<nsIDOMEventListener> mOnProgressListener;
nsCOMPtr<nsIDOMEventListener> mOnCachedListener;
nsCOMPtr<nsIDOMEventListener> mOnUpdateReadyListener;
+ nsCOMPtr<nsIDOMEventListener> mOnObsoleteListener;
struct PendingEvent {
nsCOMPtr<nsIDOMEvent> event;
nsCOMPtr<nsIDOMEventListener> listener;
nsCOMArray<nsIDOMEventListener> listeners;
};
nsTArray<PendingEvent> mPendingEvents;
--- a/dom/tests/mochitest/ajax/offline/Makefile.in
+++ b/dom/tests/mochitest/ajax/offline/Makefile.in
@@ -55,16 +55,18 @@ include $(topsrcdir)/config/rules.mk
test_identicalManifest.html \
test_changingManifest.html \
test_offlineIFrame.html \
test_bug445544.html \
445544_part1.html \
445544_part2.html \
445544.cacheManifest \
445544.cacheManifest^headers^ \
+ test_obsolete.html \
+ obsolete.html \
badManifestMagic.cacheManifest \
badManifestMagic.cacheManifest^headers^ \
missingFile.cacheManifest \
missingFile.cacheManifest^headers^ \
simpleManifest.cacheManifest \
simpleManifest.cacheManifest^headers^ \
simpleManifest.notmanifest \
changing1Sec.sjs \
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/ajax/offline/obsolete.html
@@ -0,0 +1,62 @@
+<html manifest="obsolete.cacheManifest">
+<head>
+<title>obsolete test</title>
+<script type="text/javascript">
+
+function obsolete(evt)
+{
+ window.opener.ok(true, "Got an 'obsolete' event");
+
+ // The cache status is switched immediately AFTER sending the event,
+ // make sure that it isn't OBSOLETE yet...
+ window.opener.isnot(applicationCache.status, 5,
+ "Status should not yet be 5 (obsolete)");
+
+ // But check that it is after the event is fired.
+ setTimeout(function(){
+ window.opener.is(applicationCache.status, 5,
+ "Status should be 5 (obsolete)");
+
+ // Now swapCache(), and our new status should be UNCACHED.
+ applicationCache.swapCache();
+ window.opener.is(applicationCache.status, 0,
+ "Status should be 0 (UNCACHED)");
+ window.opener.finish();
+ }, 0);
+}
+
+function fail(evt)
+{
+ window.opener.ok(false, "Got an unexpected event: " + evt.type)
+ window.opener.finish();
+}
+
+applicationCache.oncached = function() {
+ // ok, we've successfully loaded from the initial cache.
+ try {
+ applicationCache.swapCache();
+ window.opener.todo(false, "We shouldn't have to swapCache in the oncached handler (bug 443023)");
+ } catch(e) {
+ }
+
+ // Now delete the manifest and refresh, we should get an "obsolete" message.
+ applicationCache.oncached = fail;
+ applicationCache.onupdateready = fail;
+ applicationCache.onobsolete = obsolete;
+
+ var req = new XMLHttpRequest();
+ req.open("DELETE", "obsolete.cacheManifest");
+ req.send("");
+ req.onreadystatechange = function() {
+ if (req.readyState == 4) {
+ applicationCache.update();
+ }
+ }
+}
+
+</script>
+</head>
+
+<body>
+<h1></h1>
+</body> </html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/ajax/offline/test_obsolete.html
@@ -0,0 +1,56 @@
+<html>
+<head>
+<title>Test obsolete application caches</title>
+
+<script type="text/javascript" src="/MochiKit/packed.js"></script>
+<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+
+<script type="text/javascript">
+
+var gTestWin;
+
+netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+
+// Enable the offline-app permission before loading the new window
+var pm = Cc["@mozilla.org/permissionmanager;1"]
+ .getService(Ci.nsIPermissionManager);
+var uri = Cc["@mozilla.org/network/io-service;1"]
+ .getService(Ci.nsIIOService)
+ .newURI(window.location.href, null, null);
+pm.add(uri, "offline-app", Ci.nsIPermissionManager.ALLOW_ACTION);
+
+// Now add the manifest that the test frame will use.
+var manifest =
+"CACHE MANIFEST\n" +
+"obsolete.html\n";
+
+var req = new XMLHttpRequest();
+req.open("PUT", "obsolete.cacheManifest");
+req.setRequestHeader("Content-Type", "text/cache-manifest");
+req.send(manifest);
+req.onreadystatechange = function() {
+ if (req.readyState == 4) {
+ // now this will properly load the manifest.
+ gTestWin = window.open("obsolete.html");
+ }
+}
+
+function finish()
+{
+ gTestWin.close();
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+
+</head>
+
+<body>
+
+</body>
+</html>
--- a/netwerk/base/public/nsIApplicationCacheService.idl
+++ b/netwerk/base/public/nsIApplicationCacheService.idl
@@ -40,17 +40,17 @@
#include "nsISupports.idl"
interface nsIApplicationCache;
/**
* The application cache service manages the set of application cache
* groups.
*/
-[scriptable, uuid(23b37fbd-5c55-43a5-b592-abf31c05e967)]
+[scriptable, uuid(36595ec8-e849-49f8-9eb4-e895a3cd39fe)]
interface nsIApplicationCacheService : nsISupports
{
/**
* Create a new, empty application cache for the given cache
* group.
*/
nsIApplicationCache createApplicationCache(in ACString group);
@@ -60,16 +60,21 @@ interface nsIApplicationCacheService : n
nsIApplicationCache getApplicationCache(in ACString clientID);
/**
* Get the currently active cache object for a cache group.
*/
nsIApplicationCache getActiveCache(in ACString group);
/**
+ * Deactivate the currently-active cache object for a cache group.
+ */
+ void deactivateGroup(in ACString group);
+
+ /**
* Try to find the best application cache to serve a resource.
*/
nsIApplicationCache chooseApplicationCache(in ACString key);
/**
* Flags the key as being opportunistically cached.
*
* This method should also propagate the entry to other
--- a/netwerk/cache/src/nsDiskCacheDeviceSQL.cpp
+++ b/netwerk/cache/src/nsDiskCacheDeviceSQL.cpp
@@ -2024,16 +2024,38 @@ nsOfflineCacheDevice::GetActiveCache(con
nsCString *clientID;
if (mActiveCachesByGroup.Get(group, &clientID))
return GetApplicationCache(*clientID, out);
return NS_OK;
}
+NS_IMETHODIMP
+nsOfflineCacheDevice::DeactivateGroup(const nsACString &group)
+{
+ nsCString *active = nsnull;
+
+ AutoResetStatement statement(mStatement_DeactivateGroup);
+ nsresult rv = statement->BindUTF8StringParameter(0, group);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = statement->Execute();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (mActiveCachesByGroup.Get(group, &active))
+ {
+ mActiveCaches.Remove(*active);
+ mActiveCachesByGroup.Remove(group);
+ active = nsnull;
+ }
+
+ return NS_OK;
+}
+
PRBool
nsOfflineCacheDevice::CanUseCache(nsIURI *keyURI, const nsCString &clientID)
{
if (mActiveCaches.Contains(clientID)) {
nsCAutoString groupID;
nsresult rv = GetGroupForCache(clientID, groupID);
NS_ENSURE_SUCCESS(rv, PR_FALSE);
@@ -2180,39 +2202,16 @@ 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 = statement->Execute();
- NS_ENSURE_SUCCESS(rv, rv);
-
- if (mActiveCachesByGroup.Get(group, &active))
- {
- mActiveCaches.Remove(*active);
- mActiveCachesByGroup.Remove(group);
- active = nsnull;
- }
-
- return NS_OK;
-}
-
-nsresult
nsOfflineCacheDevice::GetGroupForCache(const nsACString &clientID,
nsCString &out)
{
out.Assign(clientID);
out.Truncate(out.FindChar('|'));
NS_UnescapeURL(out);
return NS_OK;
--- a/netwerk/cache/src/nsDiskCacheDeviceSQL.h
+++ b/netwerk/cache/src/nsDiskCacheDeviceSQL.h
@@ -171,17 +171,16 @@ public:
nsresult ClearKeysOwnedByDomain(const char *clientID,
const nsACString &ownerDomain);
nsresult EvictUnownedEntries(const char *clientID);
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
*/
void SetCacheParentDirectory(nsILocalFile * parentDir);
--- a/uriloader/prefetch/nsIOfflineCacheUpdate.idl
+++ b/uriloader/prefetch/nsIOfflineCacheUpdate.idl
@@ -41,17 +41,17 @@
interface nsIURI;
interface nsIDOMNode;
interface nsIDOMDocument;
interface nsIDOMLoadStatus;
interface nsIOfflineCacheUpdate;
interface nsIPrincipal;
interface nsIPrefBranch;
-[scriptable, uuid(0aa38757-999c-44d6-bdb4-7dd32634fa83)]
+[scriptable, uuid(a28abeaf-a0b4-4440-b2fe-bc78249710ea)]
interface nsIOfflineCacheUpdateObserver : nsISupports {
/**
* There was an error updating the cache.
*
* @param aUpdate
* The nsIOfflineCacheUpdate being processed.
*/
void error(in nsIOfflineCacheUpdate aUpdate);
@@ -68,16 +68,24 @@ interface nsIOfflineCacheUpdateObserver
* No update was necessary.
*
* @param aUpdate
* The nsIOfflineCacheUpdate being processed.
*/
void noUpdate(in nsIOfflineCacheUpdate aUpdate);
/**
+ * The cache group is now obsolete.
+ *
+ * @param aUpdate
+ * The nsIOfflineCacheUpdate being processed.
+ */
+ void obsolete(in nsIOfflineCacheUpdate aUpdate);
+
+ /**
* Starting to download resources
*
* @param aUpdate
* The nsIOfflineCacheUpdate being processed.
*/
void downloading(in nsIOfflineCacheUpdate aUpdate);
/**
--- a/uriloader/prefetch/nsOfflineCacheUpdate.cpp
+++ b/uriloader/prefetch/nsOfflineCacheUpdate.cpp
@@ -899,16 +899,17 @@ NS_IMPL_ISUPPORTS1(nsOfflineCacheUpdate,
// nsOfflineCacheUpdate <public>
//-----------------------------------------------------------------------------
nsOfflineCacheUpdate::nsOfflineCacheUpdate()
: mState(STATE_UNINITIALIZED)
, mAddedItems(PR_FALSE)
, mPartialUpdate(PR_FALSE)
, mSucceeded(PR_TRUE)
+ , mObsolete(PR_FALSE)
, mCurrentItem(-1)
{
}
nsOfflineCacheUpdate::~nsOfflineCacheUpdate()
{
LOG(("nsOfflineCacheUpdate::~nsOfflineCacheUpdate [%p]", this));
}
@@ -1114,16 +1115,29 @@ nsOfflineCacheUpdate::LoadCompleted()
}
if (mState == STATE_CHECKING) {
// Manifest load finished.
NS_ASSERTION(mManifestItem,
"Must have a manifest item in STATE_CHECKING.");
+ // A 404 or 410 is interpreted as an intentional removal of
+ // the manifest file, rather than a transient server error.
+ // Obsolete this cache group if one of these is returned.
+ PRUint16 status;
+ rv = mManifestItem->GetStatus(&status);
+ if (status == 404 || status == 410) {
+ mSucceeded = PR_FALSE;
+ mObsolete = PR_TRUE;
+ NotifyObsolete();
+ Finish();
+ return;
+ }
+
PRBool doUpdate;
if (NS_FAILED(HandleManifest(&doUpdate))) {
mSucceeded = PR_FALSE;
NotifyError();
Finish();
return;
}
@@ -1387,16 +1401,34 @@ nsOfflineCacheUpdate::NotifyNoUpdate()
for (PRInt32 i = 0; i < observers.Count(); i++) {
observers[i]->NoUpdate(this);
}
return NS_OK;
}
nsresult
+nsOfflineCacheUpdate::NotifyObsolete()
+{
+ LOG(("nsOfflineCacheUpdate::NotifyObsolete [%p]", this));
+
+ mState = STATE_FINISHED;
+
+ nsCOMArray<nsIOfflineCacheUpdateObserver> observers;
+ nsresult rv = GatherObservers(observers);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ for (PRInt32 i = 0; i < observers.Count(); i++) {
+ observers[i]->Obsolete(this);
+ }
+
+ return NS_OK;
+}
+
+nsresult
nsOfflineCacheUpdate::NotifyDownloading()
{
LOG(("nsOfflineCacheUpdate::NotifyDownloading [%p]", this));
nsCOMArray<nsIOfflineCacheUpdateObserver> observers;
nsresult rv = GatherObservers(observers);
NS_ENSURE_SUCCESS(rv, rv);
@@ -1490,16 +1522,26 @@ nsOfflineCacheUpdate::Finish()
mSucceeded = PR_FALSE;
}
for (PRInt32 i = 0; i < mDocuments.Count(); i++) {
AssociateDocument(mDocuments[i]);
}
}
+ if (mObsolete) {
+ nsCOMPtr<nsIApplicationCacheService> appCacheService =
+ do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID);
+ if (appCacheService) {
+ nsCAutoString groupID;
+ mApplicationCache->GetGroupID(groupID);
+ appCacheService->DeactivateGroup(groupID);
+ }
+ }
+
if (!mSucceeded) {
// Update was not merged, mark all the loads as failures
for (PRUint32 i = 0; i < mItems.Length(); i++) {
mItems[i]->Cancel();
}
mApplicationCache->Discard();
}
--- a/uriloader/prefetch/nsOfflineCacheUpdate.h
+++ b/uriloader/prefetch/nsOfflineCacheUpdate.h
@@ -232,34 +232,37 @@ private:
// specified namespaces will be added.
nsresult AddExistingItems(PRUint32 aType,
nsTArray<nsCString>* namespaceFilter = nsnull);
nsresult GatherObservers(nsCOMArray<nsIOfflineCacheUpdateObserver> &aObservers);
nsresult NotifyError();
nsresult NotifyChecking();
nsresult NotifyNoUpdate();
+ nsresult NotifyObsolete();
nsresult NotifyDownloading();
nsresult NotifyStarted(nsOfflineCacheUpdateItem *aItem);
nsresult NotifyCompleted(nsOfflineCacheUpdateItem *aItem);
nsresult AssociateDocument(nsIDOMDocument *aDocument);
nsresult Finish();
enum {
STATE_UNINITIALIZED,
STATE_INITIALIZED,
STATE_CHECKING,
STATE_DOWNLOADING,
STATE_CANCELLED,
STATE_FINISHED
} mState;
- PRBool mAddedItems;
- PRBool mPartialUpdate;
- PRBool mSucceeded;
+ PRPackedBool mAddedItems;
+ PRPackedBool mPartialUpdate;
+ PRPackedBool mSucceeded;
+ PRPackedBool mObsolete;
+
nsCString mUpdateDomain;
nsCOMPtr<nsIURI> mManifestURI;
nsCOMPtr<nsIURI> mDocumentURI;
nsCString mClientID;
nsCOMPtr<nsIApplicationCache> mApplicationCache;
nsCOMPtr<nsIApplicationCache> mPreviousApplicationCache;