Bug 461325: Cache implicit entries when the manifest is not changed. r=dcamp, sr=biesi
authorHonza Bambas <honzab@allpeers.com>
Wed, 05 Nov 2008 16:01:08 -0800
changeset 21367 038df99ac499a5ad4011fb33a9b425336d104bf0
parent 21366 bfaeea708b98cd7c9b727c3379c1b7a74c64a222
child 21368 3502bada8a5c926d441fd061bd5d5330dca1cb4d
push id3509
push userdcamp@mozilla.com
push dateThu, 06 Nov 2008 00:07:27 +0000
treeherdermozilla-central@ee04803822c9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdcamp, biesi
bugs461325
milestone1.9.1b2pre
Bug 461325: Cache implicit entries when the manifest is not changed. r=dcamp, sr=biesi
content/base/src/nsContentSink.cpp
dom/tests/mochitest/ajax/offline/offlineTests.js
dom/tests/mochitest/ajax/offline/test_offlineMode.html
dom/tests/mochitest/ajax/offline/test_updatingManifest.html
netwerk/base/public/nsIApplicationCacheChannel.idl
netwerk/protocol/http/src/nsHttpChannel.cpp
netwerk/protocol/http/src/nsHttpChannel.h
uriloader/prefetch/Makefile.in
uriloader/prefetch/nsOfflineCacheUpdate.cpp
uriloader/prefetch/nsOfflineCacheUpdate.h
--- a/content/base/src/nsContentSink.cpp
+++ b/content/base/src/nsContentSink.cpp
@@ -1017,21 +1017,30 @@ nsContentSink::ProcessOfflineManifest(ns
   aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::manifest, manifestSpec);
 
   // Grab the application cache the document was loaded from, if any.
   nsCOMPtr<nsIApplicationCache> applicationCache;
 
   nsCOMPtr<nsIApplicationCacheChannel> applicationCacheChannel =
     do_QueryInterface(mDocument->GetChannel());
   if (applicationCacheChannel) {
-    rv = applicationCacheChannel->GetApplicationCache(
-      getter_AddRefs(applicationCache));
+    PRBool loadedFromApplicationCache;
+    rv = applicationCacheChannel->GetLoadedFromApplicationCache(
+      &loadedFromApplicationCache);
     if (NS_FAILED(rv)) {
       return;
     }
+
+    if (loadedFromApplicationCache) {
+      rv = applicationCacheChannel->GetApplicationCache(
+        getter_AddRefs(applicationCache));
+      if (NS_FAILED(rv)) {
+        return;
+      }
+    }
   }
 
   if (manifestSpec.IsEmpty() && !applicationCache) {
     // Not loaded from an application cache, and no manifest
     // attribute.  Nothing to do here.
     return;
   }
 
--- a/dom/tests/mochitest/ajax/offline/offlineTests.js
+++ b/dom/tests/mochitest/ajax/offline/offlineTests.js
@@ -298,19 +298,19 @@ checkCache: function(url, expectEntry)
       if (expectEntry) {
         this.ok(false, url + " should exist in the offline cache");
       } else {
         this.ok(true, url + " should not exist in the offline cache");
       }
     } 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");
+        this.ok(!mustBeValid, url + " should exist in the offline cache");
       } else {
-        this.ok(false, url + " should not exist in the offline cache");
+        this.ok(mustBeValid, url + " should not exist in the offline cache");
       }
     } else {
       throw e;
     }
   }
 },
 
 putData: function(serverPath, contentType, data)
--- a/dom/tests/mochitest/ajax/offline/test_offlineMode.html
+++ b/dom/tests/mochitest/ajax/offline/test_offlineMode.html
@@ -71,18 +71,17 @@ function finalize()
 
   var ioserv = Cc["@mozilla.org/network/io-service;1"]
       .getService(Ci.nsIIOService);
 
   if (!ioserv.offline)
   {
     OfflineTest.checkCache("http://localhost:8888/tests/dom/tests/mochitest/ajax/offline/notonwhitelist.html", true);
     OfflineTest.checkCache("http://localhost:8888/tests/dom/tests/mochitest/ajax/offline/updatingIFrame.html", true);
-    OfflineTest.todo(false, "Bug 461325 - implicit entry should be in cache");
-    //OfflineTest.checkCache("http://localhost:8888/tests/dom/tests/mochitest/ajax/offline/updatingImplicit.html", true);
+    OfflineTest.checkCache("http://localhost:8888/tests/dom/tests/mochitest/ajax/offline/updatingImplicit.html", true);
 
     OfflineTest.is(gGotExplicitVersion, 1, "Explicit entry loaded");
     OfflineTest.is(gGotImplicitVersion, 1, "Implicit entry loaded");
     OfflineTest.is(gGotDynamicVersion, 1, "Dynamic entry loaded");
 
     gGotExplicitVersion = 0;
     gGotImplicitVersion = 0;
     gGotDynamicVersion = 0;
@@ -114,17 +113,17 @@ function finalize()
   }
   else
   {
     gImplicitWindow.close();
 
     ioserv.offline = false;
 
     OfflineTest.is(gGotExplicitVersion, 1, "Explicit entry loaded");
-    OfflineTest.todo(gGotImplicitVersion == 1, "Bug 461325 - Implicit entry loaded");
+    OfflineTest.is(gGotImplicitVersion == 1, "Bug 461325 - Implicit entry loaded");
     OfflineTest.is(gGotDynamicVersion, 1, "Dynamic entry loaded");
     OfflineTest.ok(gGotOnError, "Got onerror event invoked by implicit page load in offline mode");
 
     OfflineTest.teardown();
     OfflineTest.finish();
   }
 }
 
--- a/dom/tests/mochitest/ajax/offline/test_updatingManifest.html
+++ b/dom/tests/mochitest/ajax/offline/test_updatingManifest.html
@@ -193,18 +193,17 @@ function implicitCached()
   OfflineTest.checkCache("http://localhost:8888/tests/dom/tests/mochitest/ajax/offline/fallback.html", true);
   OfflineTest.checkCache("http://localhost:8888/tests/dom/tests/mochitest/ajax/offline/fallback2.html", false);
 
   // Whitelist entries
   OfflineTest.checkCache("http://localhost:8888/tests/dom/tests/mochitest/ajax/offline/onwhitelist.html", false);
   checkFallbackAndWhitelisting("http://localhost:8888/tests/dom/tests/mochitest/ajax/offline/onwhitelist.html", "", true);
 
   // Implicit entries
-  OfflineTest.todo(false, "Bug 461325");
-  //OfflineTest.checkCache("http://localhost:8888/tests/dom/tests/mochitest/ajax/offline/updatingImplicit.html", true);
+  OfflineTest.checkCache("http://localhost:8888/tests/dom/tests/mochitest/ajax/offline/updatingImplicit.html", true);
 
   // Dynamic entries
   OfflineTest.checkCache("http://localhost:8888/tests/SimpleTest/EventUtils.js", true);
 
   // Fallback URI selection check
   checkFallbackAndWhitelisting("http://localhost:8888/tests/dom/tests/mochitest/ajax/offline/namespace1/opp.html",
       "http://localhost:8888/tests/dom/tests/mochitest/ajax/offline/fallback.html", false);
   checkFallbackAndWhitelisting("http://localhost:8888/tests/dom/tests/mochitest/ajax/offline/namespace1/sub/opp.html",
@@ -243,18 +242,17 @@ function manifestUpdated()
     OfflineTest.checkCache("http://localhost:8888/tests/dom/tests/mochitest/ajax/offline/fallback.html", true);
     OfflineTest.checkCache("http://localhost:8888/tests/dom/tests/mochitest/ajax/offline/fallback2.html", true);
 
     // Whitelist entries
     OfflineTest.checkCache("http://localhost:8888/tests/dom/tests/mochitest/ajax/offline/onwhitelist.html", false);
     checkFallbackAndWhitelisting("http://localhost:8888/tests/dom/tests/mochitest/ajax/offline/onwhitelist.html", "", false);
 
     // Implicit entries
-    OfflineTest.todo(false, "Bug 461325");
-    //OfflineTest.checkCache("http://localhost:8888/tests/dom/tests/mochitest/ajax/offline/updatingImplicit.html", true);
+    OfflineTest.checkCache("http://localhost:8888/tests/dom/tests/mochitest/ajax/offline/updatingImplicit.html", true);
 
     // Dynamic entries
     OfflineTest.checkCache("http://localhost:8888/tests/SimpleTest/EventUtils.js", true);
 
     // Fallback URI selection check
     checkFallbackAndWhitelisting("http://localhost:8888/tests/dom/tests/mochitest/ajax/offline/namespace1/opp.html",
         "http://localhost:8888/tests/dom/tests/mochitest/ajax/offline/fallback.html", false);
     checkFallbackAndWhitelisting("http://localhost:8888/tests/dom/tests/mochitest/ajax/offline/namespace1/sub/opp.html",
@@ -289,18 +287,17 @@ function manifestUpdated()
     OfflineTest.checkCache("http://localhost:8888/tests/dom/tests/mochitest/ajax/offline/fallback.html", false);
     OfflineTest.checkCache("http://localhost:8888/tests/dom/tests/mochitest/ajax/offline/fallback2.html", true);
 
     // Whitelist entries
     OfflineTest.checkCache("http://localhost:8888/tests/dom/tests/mochitest/ajax/offline/onwhitelist.html", false);
     checkFallbackAndWhitelisting("http://localhost:8888/tests/dom/tests/mochitest/ajax/offline/onwhitelist.html", "", true);
 
     // Implicit entries
-    OfflineTest.todo(false, "Bug 461325");
-    //OfflineTest.checkCache("http://localhost:8888/tests/dom/tests/mochitest/ajax/offline/updatingImplicit.html", true);
+    OfflineTest.checkCache("http://localhost:8888/tests/dom/tests/mochitest/ajax/offline/updatingImplicit.html", true);
 
     // Dynamic entries
     OfflineTest.checkCache("http://localhost:8888/tests/SimpleTest/EventUtils.js", true);
 
     // Fallback URI selection check
     checkFallbackAndWhitelisting("http://localhost:8888/tests/dom/tests/mochitest/ajax/offline/namespace1/opp.html",
         "", false);
     checkFallbackAndWhitelisting("http://localhost:8888/tests/dom/tests/mochitest/ajax/offline/namespace1/sub/opp.html",
--- a/netwerk/base/public/nsIApplicationCacheChannel.idl
+++ b/netwerk/base/public/nsIApplicationCacheChannel.idl
@@ -38,20 +38,28 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsISupports.idl"
 #include "nsIApplicationCacheContainer.idl"
 
 /**
  * Interface implemented by channels that support application caches.
  */
-[scriptable, uuid(a27beec8-b40c-4919-ad68-f1576264713c)]
+[scriptable, uuid(9acfd21c-9c07-459f-8dae-ed2ffba23ddc)]
 interface nsIApplicationCacheChannel : nsIApplicationCacheContainer
 {
     /**
+     * TRUE when the resource came from the application cache. This
+     * might be false even there is assigned an application cache
+     * e.g. in case of fallback of load of an entry matching bypass
+     * namespace.
+     */
+    readonly attribute PRBool loadedFromApplicationCache;
+
+    /**
      * When true, the channel will ask its notification callbacks for
      * an application cache if one is not explicitly provided.  Default
      * value is true.
      *
      * NS_ERROR_ALREADY_OPENED will be thrown if set after AsyncOpen()
      * is called.
      */
     attribute boolean inheritApplicationCache;
--- a/netwerk/protocol/http/src/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/src/nsHttpChannel.cpp
@@ -127,16 +127,17 @@ nsHttpChannel::nsHttpChannel()
     , mSuppressDefensiveAuth(PR_FALSE)
     , mResuming(PR_FALSE)
     , mInitedCacheEntry(PR_FALSE)
     , mCacheForOfflineUse(PR_FALSE)
     , mCachingOpportunistically(PR_FALSE)
     , mFallbackChannel(PR_FALSE)
     , mInheritApplicationCache(PR_TRUE)
     , mChooseApplicationCache(PR_FALSE)
+    , mLoadedFromApplicationCache(PR_FALSE)
     , mTracingEnabled(PR_TRUE)
 {
     LOG(("Creating nsHttpChannel @%x\n", this));
 
     // grab a reference to the handler to ensure that it doesn't go away.
     nsHttpHandler *handler = gHttpHandler;
     NS_ADDREF(handler);
 }
@@ -1480,16 +1481,17 @@ nsHttpChannel::ProcessFallback(PRBool *f
 }
 
 nsresult
 nsHttpChannel::OpenCacheEntry(PRBool offline, PRBool *delayed)
 {
     nsresult rv;
 
     *delayed = PR_FALSE;
+    mLoadedFromApplicationCache = PR_FALSE;
 
     LOG(("nsHttpChannel::OpenCacheEntry [this=%x]", this));
 
     // make sure we're not abusing this function
     NS_PRECONDITION(!mCacheEntry, "cache entry already open");
 
     nsCAutoString cacheKey;
 
@@ -1642,16 +1644,21 @@ nsHttpChannel::OpenCacheEntry(PRBool off
                 nsCString clientID;
                 mApplicationCache->GetClientID(clientID);
 
                 mCacheForOfflineUse = !clientID.IsEmpty();
                 SetOfflineCacheClientID(clientID);
                 mCachingOpportunistically = PR_TRUE;
             }
         }
+        else if (NS_SUCCEEDED(rv)) {
+            // We successfully opened an offline cache session and the entry,
+            // now indiciate we load from the offline cache.
+            mLoadedFromApplicationCache = PR_TRUE;
+        }
     }
 
     if (!mCacheEntry && !waitingForValidation) {
         rv = gHttpHandler->GetCacheSession(storagePolicy,
                                            getter_AddRefs(session));
         if (NS_FAILED(rv)) return rv;
 
         rv = session->OpenCacheEntry(cacheKey, accessRequested, PR_FALSE,
@@ -5240,16 +5247,23 @@ nsHttpChannel::SetApplicationCache(nsIAp
 {
     NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
 
     mApplicationCache = appCache;
     return NS_OK;
 }
 
 NS_IMETHODIMP
+nsHttpChannel::GetLoadedFromApplicationCache(PRBool *aLoadedFromApplicationCache)
+{
+    *aLoadedFromApplicationCache = mLoadedFromApplicationCache;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
 nsHttpChannel::GetInheritApplicationCache(PRBool *aInherit)
 {
     *aInherit = mInheritApplicationCache;
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHttpChannel::SetInheritApplicationCache(PRBool aInherit)
--- a/netwerk/protocol/http/src/nsHttpChannel.h
+++ b/netwerk/protocol/http/src/nsHttpChannel.h
@@ -329,16 +329,17 @@ private:
     // True if mCacheForOfflineUse was set because we were caching
     // opportunistically.
     PRUint32                          mCachingOpportunistically : 1;
     // True if we are loading a fallback cache entry from the
     // application cache.
     PRUint32                          mFallbackChannel          : 1;
     PRUint32                          mInheritApplicationCache  : 1;
     PRUint32                          mChooseApplicationCache   : 1;
+    PRUint32                          mLoadedFromApplicationCache : 1;
     PRUint32                          mTracingEnabled           : 1;
 
     class nsContentEncodings : public nsIUTF8StringEnumerator
     {
     public:
         NS_DECL_ISUPPORTS
         NS_DECL_NSIUTF8STRINGENUMERATOR
 
--- a/uriloader/prefetch/Makefile.in
+++ b/uriloader/prefetch/Makefile.in
@@ -42,16 +42,19 @@ VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE		= prefetch
 LIBRARY_NAME	= prefetch_s
 LIBXUL_LIBRARY	= 1
 REQUIRES	= xpcom \
 		  dom \
+		  content \
+		  widget \
+		  layout \
 		  string \
 		  necko \
 		  uriloader \
 		  nkcache \
 		  chardet \
 		  pref \
 		  caps \
 		  $(NULL)
--- a/uriloader/prefetch/nsOfflineCacheUpdate.cpp
+++ b/uriloader/prefetch/nsOfflineCacheUpdate.cpp
@@ -42,19 +42,22 @@
 #include "nsCURILoader.h"
 #include "nsIApplicationCacheContainer.h"
 #include "nsIApplicationCacheChannel.h"
 #include "nsIApplicationCacheService.h"
 #include "nsICache.h"
 #include "nsICacheService.h"
 #include "nsICacheSession.h"
 #include "nsICachingChannel.h"
+#include "nsIContent.h"
 #include "nsIDocumentLoader.h"
+#include "nsIDOMElement.h"
 #include "nsIDOMWindow.h"
 #include "nsIDOMOfflineResourceList.h"
+#include "nsIDocument.h"
 #include "nsIObserverService.h"
 #include "nsIURL.h"
 #include "nsIWebProgress.h"
 #include "nsICryptoHash.h"
 #include "nsICacheEntryDescriptor.h"
 #include "nsIPermissionManager.h"
 #include "nsIPrincipal.h"
 #include "nsIPrefBranch.h"
@@ -1323,16 +1326,17 @@ nsOfflineCacheUpdate::LoadCompleted()
             Finish();
             return;
         }
 
         if (!doUpdate) {
             mSucceeded = PR_FALSE;
             NotifyNoUpdate();
             Finish();
+            ScheduleImplicit();
             return;
         }
 
         rv = mApplicationCache->MarkEntry(mManifestItem->mCacheKey,
                                           mManifestItem->mItemType);
         if (NS_FAILED(rv)) {
             mSucceeded = PR_FALSE;
             NotifyError();
@@ -1699,16 +1703,106 @@ nsOfflineCacheUpdate::NotifyCompleted(ns
 
     for (PRInt32 i = 0; i < observers.Count(); i++) {
         observers[i]->ItemCompleted(this, aItem);
     }
 
     return NS_OK;
 }
 
+void
+nsOfflineCacheUpdate::AddDocument(nsIDOMDocument *aDocument)
+{
+    // Add document only if it was not loaded from an offline cache.
+    // If it were loaded from an offline cache then it has already
+    // been associated with it and must not be again cached as
+    // implicit (which are the reasons we collect documents here).
+    nsCOMPtr<nsIDocument> document = do_QueryInterface(aDocument);
+    if (!document)
+        return;
+
+    nsIChannel* channel = document->GetChannel();
+    nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel =
+        do_QueryInterface(channel);
+    if (!appCacheChannel)
+        return;
+
+    PRBool loadedFromAppCache;
+    appCacheChannel->GetLoadedFromApplicationCache(&loadedFromAppCache);
+    if (loadedFromAppCache)
+        return;
+
+    mDocuments.AppendObject(aDocument);
+}
+
+nsresult
+nsOfflineCacheUpdate::ScheduleImplicit()
+{
+    if (mDocuments.Count() == 0)
+        return NS_OK;
+
+    nsresult rv;
+
+    nsRefPtr<nsOfflineCacheUpdate> update = new nsOfflineCacheUpdate();
+    NS_ENSURE_TRUE(update, NS_ERROR_OUT_OF_MEMORY);
+
+    nsCAutoString clientID;
+    if (mPreviousApplicationCache) {
+        rv = mPreviousApplicationCache->GetClientID(clientID);
+        NS_ENSURE_SUCCESS(rv, rv);
+    }
+    else {
+        clientID = mClientID;
+    }
+
+    rv = update->InitPartial(mManifestURI, clientID, mDocumentURI);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    PRBool added = PR_FALSE;
+    for (PRInt32 i = 0; i < mDocuments.Count(); i++) {
+        nsIDOMDocument* domDoc = mDocuments[i];
+        nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
+        if (!doc)
+            continue;
+
+        nsIURI* uri = doc->GetDocumentURI();
+        if (!uri)
+            continue;
+
+        nsIContent* content = doc->GetRootContent();
+        nsCOMPtr<nsIDOMElement> root = do_QueryInterface(content);
+        if (!root)
+            continue;
+
+        nsAutoString manifestSpec;
+        rv = root->GetAttribute(NS_LITERAL_STRING("manifest"), manifestSpec);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        nsCOMPtr<nsIURI> manifestURI;
+        NS_NewURI(getter_AddRefs(manifestURI), manifestSpec,
+                  doc->GetDocumentCharacterSet().get(),
+                  doc->GetDocumentURI());
+        if (!manifestURI)
+            continue;
+
+        rv = update->AddURI(uri, nsIApplicationCache::ITEM_IMPLICIT);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        added = PR_TRUE;
+    }
+
+    if (!added)
+      return NS_OK;
+
+    rv = update->Schedule();
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    return NS_OK;
+}
+
 nsresult
 nsOfflineCacheUpdate::AssociateDocument(nsIDOMDocument *aDocument)
 {
     // Check that the document that requested this update was
     // previously associated with an application cache.  If not, it
     // should be associated with the new one.
     nsCOMPtr<nsIApplicationCacheContainer> container =
         do_QueryInterface(aDocument);
--- a/uriloader/prefetch/nsOfflineCacheUpdate.h
+++ b/uriloader/prefetch/nsOfflineCacheUpdate.h
@@ -217,20 +217,17 @@ public:
     nsresult Init();
 
     nsresult Begin();
     nsresult Cancel();
 
     void LoadCompleted();
     void ManifestCheckCompleted(nsresult aStatus,
                                 const nsCString &aManifestHash);
-
-    void AddDocument(nsIDOMDocument *aDocument) {
-        mDocuments.AppendObject(aDocument);
-    };
+    void AddDocument(nsIDOMDocument *aDocument);
 
 private:
     nsresult HandleManifest(PRBool *aDoUpdate);
     nsresult AddURI(nsIURI *aURI, PRUint32 aItemType);
 
     nsresult ProcessNextURI();
 
     // Adds items from the previous cache witha type matching aType.
@@ -243,16 +240,17 @@ private:
     nsresult NotifyError();
     nsresult NotifyChecking();
     nsresult NotifyNoUpdate();
     nsresult NotifyObsolete();
     nsresult NotifyDownloading();
     nsresult NotifyStarted(nsOfflineCacheUpdateItem *aItem);
     nsresult NotifyCompleted(nsOfflineCacheUpdateItem *aItem);
     nsresult AssociateDocument(nsIDOMDocument *aDocument);
+    nsresult ScheduleImplicit();
     nsresult Finish();
 
     enum {
         STATE_UNINITIALIZED,
         STATE_INITIALIZED,
         STATE_CHECKING,
         STATE_DOWNLOADING,
         STATE_CANCELLED,