Bug 461071: Allow application caches to be obsoleted. r=honzab, r+sr=bz

This commit is contained in:
Dave Camp 2008-11-04 10:59:12 -08:00
Родитель b63345b178
Коммит 0c0880f78f
12 изменённых файлов: 260 добавлений и 42 удалений

Просмотреть файл

@ -38,7 +38,7 @@
#include "domstubs.idl"
[scriptable, uuid(8449bce2-0d8c-4c74-ab79-b41b8d81f1c4)]
[scriptable, uuid(c9732cb7-e104-4cf1-9936-a8592fd46c5a)]
interface nsIDOMOfflineResourceList : nsISupports
{
/**
@ -82,14 +82,12 @@ interface nsIDOMOfflineResourceList : nsISupports
/* 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;
/**
@ -98,10 +96,8 @@ interface nsIDOMOfflineResourceList : nsISupports
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();
@ -113,4 +109,5 @@ interface nsIDOMOfflineResourceList : nsISupports
attribute nsIDOMEventListener onprogress;
attribute nsIDOMEventListener onupdateready;
attribute nsIDOMEventListener oncached;
attribute nsIDOMEventListener onobsolete;
};

Просмотреть файл

@ -65,6 +65,7 @@
#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.
@ -90,6 +91,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMOfflineResourceList)
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)
@ -98,6 +100,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMOfflineResourceList)
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);
@ -118,6 +121,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMOfflineResourceList)
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)
@ -126,6 +130,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMOfflineResourceList)
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);
@ -237,6 +242,7 @@ nsDOMOfflineResourceList::Disconnect()
mProgressListeners.Clear();
mCachedListeners.Clear();
mUpdateReadyListeners.Clear();
mObsoleteListeners.Clear();
mOnCheckingListener = nsnull;
mOnErrorListener = nsnull;
@ -245,6 +251,7 @@ nsDOMOfflineResourceList::Disconnect()
mOnProgressListener = nsnull;
mOnCachedListener = nsnull;
mOnUpdateReadyListener = nsnull;
mOnObsoleteListener = nsnull;
mPendingEvents.Clear();
}
@ -423,7 +430,9 @@ nsDOMOfflineResourceList::GetStatus(PRUint16 *aStatus)
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;
@ -478,7 +487,10 @@ nsDOMOfflineResourceList::SwapCache()
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;
}
@ -647,6 +659,27 @@ nsDOMOfflineResourceList::SetOnupdateready(nsIDOMEventListener *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,
@ -671,6 +704,7 @@ nsDOMOfflineResourceList::AddEventListener(const nsAString& aType,
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;
}
@ -705,6 +739,7 @@ nsDOMOfflineResourceList::RemoveEventListener(const nsAString &aType,
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;
}
@ -926,6 +961,14 @@ nsDOMOfflineResourceList::ItemCompleted(nsIOfflineCacheUpdate *aUpdate,
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)
{

Просмотреть файл

@ -131,6 +131,7 @@ private:
nsCOMArray<nsIDOMEventListener> mProgressListeners;
nsCOMArray<nsIDOMEventListener> mCachedListeners;
nsCOMArray<nsIDOMEventListener> mUpdateReadyListeners;
nsCOMArray<nsIDOMEventListener> mObsoleteListeners;
nsCOMPtr<nsIDOMEventListener> mOnCheckingListener;
nsCOMPtr<nsIDOMEventListener> mOnErrorListener;
@ -139,6 +140,7 @@ private:
nsCOMPtr<nsIDOMEventListener> mOnProgressListener;
nsCOMPtr<nsIDOMEventListener> mOnCachedListener;
nsCOMPtr<nsIDOMEventListener> mOnUpdateReadyListener;
nsCOMPtr<nsIDOMEventListener> mOnObsoleteListener;
struct PendingEvent {
nsCOMPtr<nsIDOMEvent> event;

Просмотреть файл

@ -60,6 +60,8 @@ _TEST_FILES = \
445544_part2.html \
445544.cacheManifest \
445544.cacheManifest^headers^ \
test_obsolete.html \
obsolete.html \
badManifestMagic.cacheManifest \
badManifestMagic.cacheManifest^headers^ \
missingFile.cacheManifest \

Просмотреть файл

@ -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>

Просмотреть файл

@ -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>

Просмотреть файл

@ -45,7 +45,7 @@ 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
{
/**
@ -64,6 +64,11 @@ interface nsIApplicationCacheService : nsISupports
*/
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.
*/

45
netwerk/cache/src/nsDiskCacheDeviceSQL.cpp поставляемый
Просмотреть файл

@ -2029,6 +2029,28 @@ nsOfflineCacheDevice::GetActiveCache(const nsACString &group,
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)
{
@ -2184,29 +2206,6 @@ nsOfflineCacheDevice::IsActiveCache(const nsCSubstring &group,
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)

1
netwerk/cache/src/nsDiskCacheDeviceSQL.h поставляемый
Просмотреть файл

@ -176,7 +176,6 @@ public:
const nsCSubstring &clientID);
PRBool IsActiveCache(const nsCSubstring &group,
const nsCSubstring &clientID);
nsresult DeactivateGroup(const nsCSubstring &group);
nsresult GetGroupForCache(const nsCSubstring &clientID,
nsCString &out);

Просмотреть файл

@ -46,7 +46,7 @@ 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.
@ -72,6 +72,14 @@ interface nsIOfflineCacheUpdateObserver : nsISupports {
*/
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
*

Просмотреть файл

@ -904,6 +904,7 @@ nsOfflineCacheUpdate::nsOfflineCacheUpdate()
, mAddedItems(PR_FALSE)
, mPartialUpdate(PR_FALSE)
, mSucceeded(PR_TRUE)
, mObsolete(PR_FALSE)
, mCurrentItem(-1)
{
}
@ -1119,6 +1120,19 @@ nsOfflineCacheUpdate::LoadCompleted()
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;
@ -1391,6 +1405,24 @@ nsOfflineCacheUpdate::NotifyNoUpdate()
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()
{
@ -1495,6 +1527,16 @@ nsOfflineCacheUpdate::Finish()
}
}
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++) {

Просмотреть файл

@ -237,6 +237,7 @@ private:
nsresult NotifyError();
nsresult NotifyChecking();
nsresult NotifyNoUpdate();
nsresult NotifyObsolete();
nsresult NotifyDownloading();
nsresult NotifyStarted(nsOfflineCacheUpdateItem *aItem);
nsresult NotifyCompleted(nsOfflineCacheUpdateItem *aItem);
@ -252,9 +253,11 @@ private:
STATE_FINISHED
} mState;
PRBool mAddedItems;
PRBool mPartialUpdate;
PRBool mSucceeded;
PRPackedBool mAddedItems;
PRPackedBool mPartialUpdate;
PRPackedBool mSucceeded;
PRPackedBool mObsolete;
nsCString mUpdateDomain;
nsCOMPtr<nsIURI> mManifestURI;