Bug 536295 - e10s HTTP: offline application cache, r=dwitte, sr=cbiesinger, a=fennec-2.0b2+
authorHonza Bambas <honzab.moz@firemni.cz>
Wed, 20 Oct 2010 19:12:32 +0200
changeset 56242 c73c0da830fefb346b6372d3e25db73299f52d82
parent 56241 051b5bc464432d093c94a23e80fd0650fd154efb
child 56244 76cd9b4d30347e9e02dbbb4f448f091799ffaa2d
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdwitte, cbiesinger, fennec-2
bugs536295
milestone2.0b8pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 536295 - e10s HTTP: offline application cache, r=dwitte, sr=cbiesinger, a=fennec-2.0b2+
browser/base/content/browser.js
content/base/src/nsContentSink.cpp
docshell/base/nsDocShell.cpp
dom/base/nsGlobalWindow.cpp
dom/ipc/ContentChild.cpp
dom/ipc/PBrowser.ipdl
dom/ipc/TabChild.cpp
dom/ipc/TabChild.h
dom/ipc/TabParent.cpp
dom/ipc/TabParent.h
dom/src/offline/nsDOMOfflineResourceList.cpp
dom/src/offline/nsDOMOfflineResourceList.h
ipc/ipdl/Makefile.in
netwerk/base/public/nsIApplicationCache.idl
netwerk/base/public/nsIApplicationCacheChannel.idl
netwerk/build/nsNetCID.h
netwerk/build/nsNetModule.cpp
netwerk/cache/nsApplicationCache.h
netwerk/cache/nsDiskCacheDeviceSQL.cpp
netwerk/protocol/http/HttpChannelChild.cpp
netwerk/protocol/http/HttpChannelChild.h
netwerk/protocol/http/HttpChannelParent.cpp
netwerk/protocol/http/HttpChannelParent.h
netwerk/protocol/http/PHttpChannel.ipdl
netwerk/protocol/http/nsHttpChannel.cpp
uriloader/prefetch/Makefile.in
uriloader/prefetch/OfflineCacheUpdateChild.cpp
uriloader/prefetch/OfflineCacheUpdateChild.h
uriloader/prefetch/OfflineCacheUpdateGlue.cpp
uriloader/prefetch/OfflineCacheUpdateGlue.h
uriloader/prefetch/OfflineCacheUpdateParent.cpp
uriloader/prefetch/OfflineCacheUpdateParent.h
uriloader/prefetch/POfflineCacheUpdate.ipdl
uriloader/prefetch/ipdl.mk
uriloader/prefetch/nsIOfflineCacheUpdate.idl
uriloader/prefetch/nsOfflineCacheUpdate.cpp
uriloader/prefetch/nsOfflineCacheUpdate.h
uriloader/prefetch/nsOfflineCacheUpdateService.cpp
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -5730,17 +5730,17 @@ var OfflineApps = {
     if (!manifest)
       return;
 
     var manifestURI = makeURI(manifest, aDocument.characterSet,
                               aDocument.documentURIObject);
 
     var updateService = Cc["@mozilla.org/offlinecacheupdate-service;1"].
                         getService(Ci.nsIOfflineCacheUpdateService);
-    updateService.scheduleUpdate(manifestURI, aDocument.documentURIObject);
+    updateService.scheduleUpdate(manifestURI, aDocument.documentURIObject, window);
   },
 
   /////////////////////////////////////////////////////////////////////////////
   // nsIObserver
   observe: function (aSubject, aTopic, aState)
   {
     if (aTopic == "dom-storage-warn-quota-exceeded") {
       if (aSubject) {
--- a/content/base/src/nsContentSink.cpp
+++ b/content/base/src/nsContentSink.cpp
@@ -69,17 +69,16 @@
 #include "nsIDOMWindowInternal.h"
 #include "nsIPrincipal.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsNetCID.h"
 #include "nsIOfflineCacheUpdate.h"
 #include "nsIApplicationCache.h"
 #include "nsIApplicationCacheContainer.h"
 #include "nsIApplicationCacheChannel.h"
-#include "nsIApplicationCacheService.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIDOMLoadStatus.h"
 #include "nsICookieService.h"
 #include "nsIPrompt.h"
 #include "nsServiceManagerUtils.h"
 #include "nsContentUtils.h"
 #include "nsParserUtils.h"
 #include "nsCRT.h"
@@ -938,39 +937,16 @@ nsContentSink::PrefetchDNS(const nsAStri
   }
 
   if (!hostname.IsEmpty() && nsHTMLDNSPrefetch::IsAllowed(mDocument)) {
     nsHTMLDNSPrefetch::PrefetchLow(hostname);
   }
 }
 
 nsresult
-nsContentSink::GetChannelCacheKey(nsIChannel* aChannel, nsACString& aCacheKey)
-{
-  aCacheKey.Truncate();
-
-  nsresult rv;
-  nsCOMPtr<nsICachingChannel> cachingChannel = do_QueryInterface(aChannel, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCOMPtr<nsISupports> cacheKey;
-  rv = cachingChannel->GetCacheKey(getter_AddRefs(cacheKey));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCOMPtr<nsISupportsCString> cacheKeyString = 
-        do_QueryInterface(cacheKey, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = cacheKeyString->GetData(aCacheKey);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_OK;
-}
-
-nsresult
 nsContentSink::SelectDocAppCache(nsIApplicationCache *aLoadApplicationCache,
                                  nsIURI *aManifestURI,
                                  PRBool aFetchedWithHTTPGetOrEquiv,
                                  CacheSelectionAction *aAction)
 {
   nsresult rv;
 
   *aAction = CACHE_SELECTION_NONE;
@@ -989,27 +965,18 @@ nsContentSink::SelectDocAppCache(nsIAppl
     rv = NS_NewURI(getter_AddRefs(groupURI), groupID);
     NS_ENSURE_SUCCESS(rv, rv);
 
     PRBool equal = PR_FALSE;
     rv = groupURI->Equals(aManifestURI, &equal);
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (!equal) {
-      // This is a foreign entry, mark it as such and force a reload to avoid
-      // loading the foreign entry.  The next attempt will not choose this
-      // cache entry (because it has been marked foreign).
-
-      nsCAutoString cachekey;
-      rv = GetChannelCacheKey(mDocument->GetChannel(), cachekey);
-      NS_ENSURE_SUCCESS(rv, rv);
-
-      rv = aLoadApplicationCache->MarkEntry(cachekey,
-                                            nsIApplicationCache::ITEM_FOREIGN);
-      NS_ENSURE_SUCCESS(rv, rv);
+      // This is a foreign entry, force a reload to avoid loading the foreign
+      // entry. The entry will be marked as foreign to avoid loading it again.
 
       *aAction = CACHE_SELECTION_RELOAD;
     }
     else {
       // The http manifest attribute URI is equal to the manifest URI of
       // the cache the document was loaded from - associate the document with
       // that cache and invoke the cache update process.
 #ifdef NS_DEBUG
@@ -1214,16 +1181,23 @@ nsContentSink::ProcessOfflineManifest(co
       nsCOMPtr<nsIDOMDocument> domdoc = do_QueryInterface(mDocument);
       updateService->ScheduleOnDocumentStop(manifestURI, mDocumentURI, domdoc);
     }
     break;
   }
   case CACHE_SELECTION_RELOAD: {
     // This situation occurs only for toplevel documents, see bottom
     // of SelectDocAppCache method.
+    // The document has been loaded from a different offline cache group than
+    // the manifest it refers to, i.e. this is a foreign entry, mark it as such 
+    // and force a reload to avoid loading it.  The next attempt will not 
+    // choose it.
+
+    applicationCacheChannel->MarkOfflineCacheEntryAsForeign();
+
     nsCOMPtr<nsIWebNavigation> webNav = do_QueryInterface(mDocShell);
 
     webNav->Stop(nsIWebNavigation::STOP_ALL);
     webNav->Reload(nsIWebNavigation::LOAD_FLAGS_NONE);
     break;
   }
   default:
     NS_ASSERTION(PR_FALSE,
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -225,16 +225,20 @@ static NS_DEFINE_CID(kAppShellCID, NS_AP
 //#define DEBUG_DOCSHELL_FOCUS
 #define DEBUG_PAGE_CACHE
 #endif
 
 #include "nsContentErrors.h"
 #include "nsIChannelPolicy.h"
 #include "nsIContentSecurityPolicy.h"
 
+#ifdef MOZ_IPC
+#include "nsXULAppAPI.h"
+#endif
+
 using namespace mozilla;
 
 // Number of documents currently loading
 static PRInt32 gNumberOfDocumentsLoading = 0;
 
 // Global count of existing docshells.
 static PRInt32 gDocShellCount = 0;
 
@@ -5923,17 +5927,23 @@ nsDocShell::OnRedirectStateChange(nsICha
         // here.  OnNewURI will do that, so we will cache it.
         SaveLastVisit(aNewChannel, oldURI, aRedirectFlags);
     }
 
     // check if the new load should go through the application cache.
     nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel =
         do_QueryInterface(aNewChannel);
     if (appCacheChannel) {
-        appCacheChannel->SetChooseApplicationCache(ShouldCheckAppCache(newURI));
+#ifdef MOZ_IPC
+        // Permission will be checked in the parent process.
+        if (GeckoProcessType_Default != XRE_GetProcessType())
+            appCacheChannel->SetChooseApplicationCache(PR_TRUE);
+        else
+#endif
+            appCacheChannel->SetChooseApplicationCache(ShouldCheckAppCache(newURI));
     }
 
     if (!(aRedirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL) && 
         mLoadType & (LOAD_CMD_RELOAD | LOAD_CMD_HISTORY)) {
         mLoadType = LOAD_NORMAL_REPLACE;
         SetHistoryEntry(&mLSHE, nsnull);
     }
 }
@@ -8576,17 +8586,24 @@ nsDocShell::DoURILoad(nsIURI * aURI,
     nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel =
         do_QueryInterface(channel);
     if (appCacheChannel) {
         // Any document load should not inherit application cache.
         appCacheChannel->SetInheritApplicationCache(PR_FALSE);
 
         // Loads with the correct permissions should check for a matching
         // application cache.
-        appCacheChannel->SetChooseApplicationCache(ShouldCheckAppCache(aURI));
+#ifdef MOZ_IPC
+        // Permission will be checked in the parent process
+        if (GeckoProcessType_Default != XRE_GetProcessType())
+            appCacheChannel->SetChooseApplicationCache(PR_TRUE);
+        else
+#endif
+            appCacheChannel->SetChooseApplicationCache(
+                ShouldCheckAppCache(aURI));
     }
 
     // Make sure to give the caller a channel if we managed to create one
     // This is important for correct error page/session history interaction
     if (aRequest)
         NS_ADDREF(*aRequest = channel);
 
     channel->SetOriginalURI(aURI);
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -8159,16 +8159,32 @@ nsGlobalWindow::Observe(nsISupports* aSu
     }
 
     PRBool defaultActionEnabled;
     DispatchEvent((nsIDOMStorageEvent *)event, &defaultActionEnabled);
 
     return NS_OK;
   }
 
+  if (!nsCRT::strcmp(aTopic, "offline-cache-update-added")) {
+    if (mApplicationCache)
+      return NS_OK;
+
+    // Instantiate the application object now. It observes update belonging to
+    // this window's document and correctly updates the applicationCache object
+    // state.
+    nsCOMPtr<nsIDOMOfflineResourceList> applicationCache;
+    GetApplicationCache(getter_AddRefs(applicationCache));
+    nsCOMPtr<nsIObserver> observer = do_QueryInterface(applicationCache);
+    if (observer)
+      observer->Observe(aSubject, aTopic, aData);
+
+    return NS_OK;
+  }
+
   NS_WARNING("unrecognized topic in nsGlobalWindow::Observe");
   return NS_ERROR_FAILURE;
 }
 
 static PLDHashOperator
 FirePendingStorageEvents(const nsAString& aKey, PRBool aData, void *userArg)
 {
   nsGlobalWindow *win = static_cast<nsGlobalWindow *>(userArg);
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -80,16 +80,17 @@
 #ifdef MOZ_PERMISSIONS
 #include "nsPermission.h"
 #include "nsPermissionManager.h"
 #endif
 
 using namespace mozilla::ipc;
 using namespace mozilla::net;
 using namespace mozilla::places;
+using namespace mozilla::docshell;
 
 namespace mozilla {
 namespace dom {
 class AlertObserver
 {
 public:
 
     AlertObserver(nsIObserver *aObserver, const nsString& aData)
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -39,16 +39,17 @@
 
 include protocol PContent;
 include protocol PContentDialog;
 include protocol PDocumentRenderer;
 include protocol PDocumentRendererShmem;
 include protocol PDocumentRendererNativeID;
 include protocol PContentPermissionRequest;
 include protocol PRenderFrame;
+include protocol POfflineCacheUpdate;
 
 include "TabMessageUtils.h";
 include "gfxMatrix.h";
 include "mozilla/net/NeckoMessageUtils.h";
 include "IPC/nsGUIEventIPC.h";
 
 using gfxMatrix;
 using IPC::URI;
@@ -68,16 +69,17 @@ rpc protocol PBrowser
     manager PContent;
 
     manages PContentDialog;
     manages PDocumentRenderer;
     manages PDocumentRendererShmem;
     manages PDocumentRendererNativeID;
     manages PContentPermissionRequest;
     manages PRenderFrame;
+    manages POfflineCacheUpdate;
 
 both:
     AsyncMessage(nsString aMessage, nsString aJSON);
 
 parent:
     /**
      * When child sends this message, parent should move focus to
      * the next or previous focusable element.
@@ -182,16 +184,43 @@ parent:
                    PRInt32[] aIntParams, nsString[] aStringParams);
 
     /**
      * Create a layout frame (encapsulating a remote layer tree) for
      * the page that is currently loaded in the <browser>.
      */
     async PRenderFrame();
 
+    /** 
+     * Starts an offline application cache update.
+     * @param manifestURI
+     *   URI of the manifest to fetch, the application cache group ID
+     * @param documentURI
+     *   URI of the document that referred the manifest
+     * @param clientID
+     *   The group cache version identifier to use
+     * @param stickDocument
+     *   True if the update was initiated by a document load that referred
+     *   a manifest.
+     *   False if the update was initiated by applicationCache.update() call.
+     *
+     *   Tells the update to carry the documentURI to a potential separate 
+     *   update of implicit (master) items.
+     *
+     *   Why this argument? If the document was not found in an offline cache 
+     *   before load and refers a manifest and this manifest itself has not 
+     *   been changed since the last fetch, we will not do the application 
+     *   cache group update. But we must cache the document (identified by the
+     *   documentURI). This argument will ensure that a previously uncached 
+     *   document will get cached and that we don't re-cache a document that 
+     *   has already been cached (stickDocument=false).
+     */
+    POfflineCacheUpdate(URI manifestURI, URI documentURI, nsCString clientID,
+                        bool stickDocument);
+
     __delete__();
 
 child:
     /**
      * Notify the remote browser that it has been Show()n on this
      * side, with the given |visibleRect|.  This message is expected
      * to trigger creation of the remote browser's "widget".
      *
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -36,16 +36,17 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "TabChild.h"
 #include "mozilla/dom/PContentChild.h"
 #include "mozilla/dom/PContentDialogChild.h"
 #include "mozilla/layers/PLayersChild.h"
 #include "mozilla/layout/RenderFrameChild.h"
+#include "mozilla/docshell/OfflineCacheUpdateChild.h"
 
 #include "BasicLayers.h"
 #include "nsIWebBrowser.h"
 #include "nsIWebBrowserSetup.h"
 #include "nsEmbedCID.h"
 #include "nsComponentManagerUtils.h"
 #include "nsIBaseWindow.h"
 #include "nsIDOMWindow.h"
@@ -89,16 +90,17 @@
 #include "nsIFrame.h"
 #include "nsIView.h"
 #include "nsIEventListenerManager.h"
 #include "PCOMContentPermissionRequestChild.h"
 
 using namespace mozilla::dom;
 using namespace mozilla::layers;
 using namespace mozilla::layout;
+using namespace mozilla::docshell;
 
 NS_IMPL_ISUPPORTS1(ContentListener, nsIDOMEventListener)
 
 NS_IMETHODIMP
 ContentListener::HandleEvent(nsIDOMEvent* aEvent)
 {
   RemoteDOMEvent remoteEvent;
   remoteEvent.mEvent = do_QueryInterface(aEvent);
@@ -804,16 +806,34 @@ TabChild::RecvActivateFrameEvent(const n
     do_QueryInterface(window->GetChromeEventHandler());
   NS_ENSURE_TRUE(chromeHandler, true);
   nsRefPtr<ContentListener> listener = new ContentListener(this);
   NS_ENSURE_TRUE(listener, true);
   chromeHandler->AddEventListener(aType, listener, capture);
   return true;
 }
 
+POfflineCacheUpdateChild*
+TabChild::AllocPOfflineCacheUpdate(const URI& manifestURI,
+            const URI& documentURI,
+            const nsCString& clientID,
+            const bool& stickDocument)
+{
+  NS_RUNTIMEABORT("unused");
+  return nsnull;
+}
+
+bool
+TabChild::DeallocPOfflineCacheUpdate(POfflineCacheUpdateChild* actor)
+{
+  OfflineCacheUpdateChild* offlineCacheUpdate = static_cast<OfflineCacheUpdateChild*>(actor);
+  delete offlineCacheUpdate;
+  return true;
+}
+
 bool
 TabChild::RecvLoadRemoteScript(const nsString& aURL)
 {
   if (!mCx && !InitTabChildGlobal())
     return false;
 
   LoadFrameScriptInternal(aURL);
   return true;
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -272,16 +272,22 @@ public:
             const PRUint32& flags,
             const bool& flush,
             const gfxMatrix& aMatrix,
             const PRUint32& aNativeID);
 
     virtual PContentPermissionRequestChild* AllocPContentPermissionRequest(const nsCString& aType, const IPC::URI& uri);
     virtual bool DeallocPContentPermissionRequest(PContentPermissionRequestChild* actor);
 
+    virtual POfflineCacheUpdateChild* AllocPOfflineCacheUpdate(const URI& manifestURI,
+            const URI& documentURI,
+            const nsCString& clientID,
+            const bool& stickDocument);
+    virtual bool DeallocPOfflineCacheUpdate(POfflineCacheUpdateChild* offlineCacheUpdate);
+
     nsIWebNavigation* WebNavigation() { return mWebNav; }
 
     JSContext* GetJSContext() { return mCx; }
 
     nsIPrincipal* GetPrincipal() { return mPrincipal; }
 
 protected:
     NS_OVERRIDE
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -38,16 +38,17 @@
 
 #include "TabParent.h"
 
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/ipc/DocumentRendererParent.h"
 #include "mozilla/ipc/DocumentRendererShmemParent.h"
 #include "mozilla/ipc/DocumentRendererNativeIDParent.h"
 #include "mozilla/layout/RenderFrameParent.h"
+#include "mozilla/docshell/OfflineCacheUpdateParent.h"
 
 #include "nsIURI.h"
 #include "nsFocusManager.h"
 #include "nsCOMPtr.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIDOMElement.h"
 #include "nsEventDispatcher.h"
 #include "nsIDOMEventTarget.h"
@@ -698,16 +699,45 @@ TabParent::AllocPRenderFrame()
 
 bool
 TabParent::DeallocPRenderFrame(PRenderFrameParent* aFrame)
 {
   delete aFrame;
   return true;
 }
 
+mozilla::docshell::POfflineCacheUpdateParent*
+TabParent::AllocPOfflineCacheUpdate(const URI& aManifestURI,
+                                    const URI& aDocumentURI,
+                                    const nsCString& aClientID,
+                                    const bool& stickDocument)
+{
+  nsRefPtr<mozilla::docshell::OfflineCacheUpdateParent> update =
+    new mozilla::docshell::OfflineCacheUpdateParent();
+
+  nsresult rv = update->Schedule(aManifestURI, aDocumentURI, aClientID,
+                                 stickDocument);
+  if (NS_FAILED(rv))
+    return nsnull;
+
+  POfflineCacheUpdateParent* result = update.get();
+  update.forget();
+  return result;
+}
+
+bool
+TabParent::DeallocPOfflineCacheUpdate(mozilla::docshell::POfflineCacheUpdateParent* actor)
+{
+  mozilla::docshell::OfflineCacheUpdateParent* update =
+    static_cast<mozilla::docshell::OfflineCacheUpdateParent*>(actor);
+
+  update->Release();
+  return true;
+}
+
 PRBool
 TabParent::ShouldDelayDialogs()
 {
   nsRefPtr<nsFrameLoader> frameLoader = GetFrameLoader();
   NS_ENSURE_TRUE(frameLoader, PR_TRUE);
   PRBool delay = PR_FALSE;
   frameLoader->GetDelayRemoteDialogs(&delay);
   return delay;
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -165,16 +165,23 @@ public:
             const gfxMatrix& aMatrix,
             const PRUint32& nativeID);
     virtual bool DeallocPDocumentRendererNativeID(PDocumentRendererNativeIDParent* actor);
 
 
     virtual PContentPermissionRequestParent* AllocPContentPermissionRequest(const nsCString& aType, const IPC::URI& uri);
     virtual bool DeallocPContentPermissionRequest(PContentPermissionRequestParent* actor);
 
+    virtual POfflineCacheUpdateParent* AllocPOfflineCacheUpdate(
+            const URI& aManifestURI,
+            const URI& aDocumentURI,
+            const nsCString& aClientID,
+            const bool& stickDocument);
+    virtual bool DeallocPOfflineCacheUpdate(POfflineCacheUpdateParent* actor);
+
     JSBool GetGlobalJSObject(JSContext* cx, JSObject** globalp);
 
     NS_DECL_ISUPPORTS
     NS_DECL_NSIAUTHPROMPTPROVIDER
     NS_DECL_NSISECUREBROWSERUI
     NS_DECL_NSISSLSTATUSPROVIDER
 
     void HandleDelayedDialogs();
--- a/dom/src/offline/nsDOMOfflineResourceList.cpp
+++ b/dom/src/offline/nsDOMOfflineResourceList.cpp
@@ -52,16 +52,25 @@
 #include "nsContentUtils.h"
 #include "nsIJSContextStack.h"
 #include "nsEventDispatcher.h"
 #include "nsIPrivateDOMEvent.h"
 #include "nsIObserverService.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIWebNavigation.h"
 
+#ifdef MOZ_IPC
+#include "nsXULAppAPI.h"
+#define IS_CHILD_PROCESS() \
+    (GeckoProcessType_Default != XRE_GetProcessType())
+#else
+#define IS_CHILD_PROCESS() \
+    (false)
+#endif
+
 // Event names
 
 #define CHECKING_STR    "checking"
 #define ERROR_STR       "error"
 #define NOUPDATE_STR    "noupdate"
 #define DOWNLOADING_STR "downloading"
 #define PROGRESS_STR    "progress"
 #define CACHED_STR      "cached"
@@ -132,16 +141,18 @@ nsDOMOfflineResourceList::nsDOMOfflineRe
                                                    nsIURI *aDocumentURI,
                                                    nsPIDOMWindow *aWindow,
                                                    nsIScriptContext* aScriptContext)
   : mInitialized(PR_FALSE)
   , mManifestURI(aManifestURI)
   , mDocumentURI(aDocumentURI)
   , mCachedKeys(nsnull)
   , mCachedKeysCount(0)
+  , mExposeCacheUpdateStatus(true)
+  , mStatus(nsIDOMOfflineResourceList::IDLE)
 {
   mOwner = aWindow;
   mScriptContext = aScriptContext;
 }
 
 nsDOMOfflineResourceList::~nsDOMOfflineResourceList()
 {
   ClearCachedKeys();
@@ -165,36 +176,39 @@ nsDOMOfflineResourceList::Init()
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Dynamically-managed resources are stored as a separate ownership list
   // from the manifest.
   nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(mDocumentURI);
   if (!innerURI)
     return NS_ERROR_FAILURE;
 
-  mApplicationCacheService =
-    do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
+  if (!IS_CHILD_PROCESS())
+  {
+    mApplicationCacheService =
+      do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
 
-  // Check for in-progress cache updates
-  nsCOMPtr<nsIOfflineCacheUpdateService> cacheUpdateService =
-    do_GetService(NS_OFFLINECACHEUPDATESERVICE_CONTRACTID, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  PRUint32 numUpdates;
-  rv = cacheUpdateService->GetNumUpdates(&numUpdates);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  for (PRUint32 i = 0; i < numUpdates; i++) {
-    nsCOMPtr<nsIOfflineCacheUpdate> cacheUpdate;
-    rv = cacheUpdateService->GetUpdate(i, getter_AddRefs(cacheUpdate));
+    // Check for in-progress cache updates
+    nsCOMPtr<nsIOfflineCacheUpdateService> cacheUpdateService =
+      do_GetService(NS_OFFLINECACHEUPDATESERVICE_CONTRACTID, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    UpdateAdded(cacheUpdate);
+    PRUint32 numUpdates;
+    rv = cacheUpdateService->GetNumUpdates(&numUpdates);
     NS_ENSURE_SUCCESS(rv, rv);
+
+    for (PRUint32 i = 0; i < numUpdates; i++) {
+      nsCOMPtr<nsIOfflineCacheUpdate> cacheUpdate;
+      rv = cacheUpdateService->GetUpdate(i, getter_AddRefs(cacheUpdate));
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      UpdateAdded(cacheUpdate);
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
   }
 
   // watch for new offline cache updates
   nsCOMPtr<nsIObserverService> observerService =
     mozilla::services::GetObserverService();
   if (!observerService)
     return NS_ERROR_FAILURE;
 
@@ -230,16 +244,19 @@ nsDOMOfflineResourceList::Disconnect()
 
 //
 // nsDOMOfflineResourceList::nsIDOMOfflineResourceList
 //
 
 NS_IMETHODIMP
 nsDOMOfflineResourceList::GetMozItems(nsIDOMDOMStringList **aItems)
 {
+  if (IS_CHILD_PROCESS()) 
+    return NS_ERROR_NOT_IMPLEMENTED;
+
   *aItems = nsnull;
 
   nsRefPtr<nsDOMStringList> items = new nsDOMStringList();
   NS_ENSURE_TRUE(items, NS_ERROR_OUT_OF_MEMORY);
 
   // If we are not associated with an application cache, return an
   // empty list.
   nsCOMPtr<nsIApplicationCache> appCache = GetDocumentAppCache();
@@ -265,16 +282,19 @@ nsDOMOfflineResourceList::GetMozItems(ns
 
   NS_ADDREF(*aItems = items);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMOfflineResourceList::MozHasItem(const nsAString& aURI, PRBool* aExists)
 {
+  if (IS_CHILD_PROCESS()) 
+    return NS_ERROR_NOT_IMPLEMENTED;
+
   nsresult rv = Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIApplicationCache> appCache = GetDocumentAppCache();
   if (!appCache) {
     return NS_ERROR_DOM_INVALID_STATE_ERR;
   }
 
@@ -292,16 +312,19 @@ nsDOMOfflineResourceList::MozHasItem(con
 
   *aExists = ((types & nsIApplicationCache::ITEM_DYNAMIC) != 0);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMOfflineResourceList::GetMozLength(PRUint32 *aLength)
 {
+  if (IS_CHILD_PROCESS()) 
+    return NS_ERROR_NOT_IMPLEMENTED;
+
   if (!mManifestURI) {
     *aLength = 0;
     return NS_OK;
   }
 
   nsresult rv = Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -310,16 +333,19 @@ nsDOMOfflineResourceList::GetMozLength(P
 
   *aLength = mCachedKeysCount;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMOfflineResourceList::MozItem(PRUint32 aIndex, nsAString& aURI)
 {
+  if (IS_CHILD_PROCESS()) 
+    return NS_ERROR_NOT_IMPLEMENTED;
+
   nsresult rv = Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
   SetDOMStringToNull(aURI);
 
   rv = CacheKeys();
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -329,16 +355,19 @@ nsDOMOfflineResourceList::MozItem(PRUint
   CopyUTF8toUTF16(mCachedKeys[aIndex], aURI);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMOfflineResourceList::MozAdd(const nsAString& aURI)
 {
+  if (IS_CHILD_PROCESS()) 
+    return NS_ERROR_NOT_IMPLEMENTED;
+
   nsresult rv = Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (!nsContentUtils::OfflineAppAllowed(mDocumentURI)) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
   nsCOMPtr<nsIApplicationCache> appCache = GetDocumentAppCache();
@@ -393,16 +422,19 @@ nsDOMOfflineResourceList::MozAdd(const n
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMOfflineResourceList::MozRemove(const nsAString& aURI)
 {
+  if (IS_CHILD_PROCESS()) 
+    return NS_ERROR_NOT_IMPLEMENTED;
+
   nsresult rv = Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (!nsContentUtils::OfflineAppAllowed(mDocumentURI)) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
   nsCOMPtr<nsIApplicationCache> appCache = GetDocumentAppCache();
@@ -448,36 +480,24 @@ nsDOMOfflineResourceList::GetStatus(PRUi
   nsCOMPtr<nsIApplicationCache> appCache = GetDocumentAppCache();
   if (!appCache) {
     *aStatus = nsIDOMOfflineResourceList::UNCACHED;
     return NS_OK;
   }
 
 
   // If there is an update in process, use its status.
-  if (mCacheUpdate) {
+  if (mCacheUpdate && mExposeCacheUpdateStatus) {
     rv = mCacheUpdate->GetStatus(aStatus);
     if (NS_SUCCEEDED(rv) && *aStatus != nsIDOMOfflineResourceList::IDLE) {
       return NS_OK;
     }
   }
 
-  nsCOMPtr<nsIApplicationCache> activeCache;
-  rv = mApplicationCacheService->GetActiveCache(mManifestSpec,
-                                                getter_AddRefs(activeCache));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  if (activeCache == nsnull) {
-    *aStatus = nsIDOMOfflineResourceList::OBSOLETE;
-  } else if (appCache == activeCache) {
-    *aStatus = nsIDOMOfflineResourceList::IDLE;
-  } else {
-    *aStatus = nsIDOMOfflineResourceList::UPDATEREADY;
-  }
-
+  *aStatus = mStatus;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMOfflineResourceList::Update()
 {
   nsresult rv = Init();
   NS_ENSURE_SUCCESS(rv, rv);
@@ -485,61 +505,70 @@ nsDOMOfflineResourceList::Update()
   if (!nsContentUtils::OfflineAppAllowed(mDocumentURI)) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
   nsCOMPtr<nsIOfflineCacheUpdateService> updateService =
     do_GetService(NS_OFFLINECACHEUPDATESERVICE_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  nsCOMPtr<nsIDOMWindow> window = 
+    do_QueryInterface(mOwner);
+
   nsCOMPtr<nsIOfflineCacheUpdate> update;
   rv = updateService->ScheduleUpdate(mManifestURI, mDocumentURI,
-                                     getter_AddRefs(update));
+                                     window, getter_AddRefs(update));
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMOfflineResourceList::SwapCache()
 {
   nsresult rv = Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (!nsContentUtils::OfflineAppAllowed(mDocumentURI)) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
-  nsCOMPtr<nsIApplicationCacheService> serv =
-    do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-
   nsCOMPtr<nsIApplicationCache> currentAppCache = GetDocumentAppCache();
+  if (!currentAppCache) {
+    return NS_ERROR_DOM_INVALID_STATE_ERR;
+  }
 
-  nsCOMPtr<nsIApplicationCache> newAppCache;
-  rv = serv->GetActiveCache(mManifestSpec, getter_AddRefs(newAppCache));
-  NS_ENSURE_SUCCESS(rv, rv);
+  // Check the current and potentially newly available cache are not identical.
+  if (mAvailableApplicationCache == currentAppCache) {
+    return NS_ERROR_DOM_INVALID_STATE_ERR;
+  }
 
-  // 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;
+  if (mAvailableApplicationCache) {
+    nsCString currClientId, availClientId;
+    currentAppCache->GetClientID(currClientId);
+    mAvailableApplicationCache->GetClientID(availClientId);
+    if (availClientId == currClientId)
+      return NS_ERROR_DOM_INVALID_STATE_ERR;
   }
 
   ClearCachedKeys();
 
   nsCOMPtr<nsIApplicationCacheContainer> appCacheContainer =
     GetDocumentAppCacheContainer();
 
+  // In the case of an obsolete cache group, newAppCache might be null.
+  // We will disassociate from the cache in that case.
   if (appCacheContainer) {
-    rv = appCacheContainer->SetApplicationCache(newAppCache);
+    rv = appCacheContainer->SetApplicationCache(mAvailableApplicationCache);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
+  mAvailableApplicationCache = nsnull;
+  mStatus = nsIDOMOfflineResourceList::IDLE;
+
   return NS_OK;
 }
 
 //
 // nsDOMOfflineResourceList::nsIDOMEventTarget
 //
 
 NS_IMETHODIMP
@@ -722,63 +751,60 @@ nsDOMOfflineResourceList::Observe(nsISup
 
   return NS_OK;
 }
 
 //
 // nsDOMOfflineResourceList::nsIOfflineCacheUpdateObserver
 //
 NS_IMETHODIMP
-nsDOMOfflineResourceList::Error(nsIOfflineCacheUpdate *aUpdate)
+nsDOMOfflineResourceList::UpdateStateChanged(nsIOfflineCacheUpdate *aUpdate,
+                                     PRUint32 event)
 {
-  SendEvent(NS_LITERAL_STRING(ERROR_STR));
+  mExposeCacheUpdateStatus = 
+      (event == STATE_CHECKING) ||
+      (event == STATE_DOWNLOADING) ||
+      (event == STATE_ITEMSTARTED) ||
+      (event == STATE_ITEMCOMPLETED) ||
+      // During notification of "obsolete" we must expose state of the update
+      (event == STATE_OBSOLETE);
+
+  switch (event) {
+    case STATE_ERROR:
+      SendEvent(NS_LITERAL_STRING(ERROR_STR));
+      break;
+    case STATE_CHECKING:
+      SendEvent(NS_LITERAL_STRING(CHECKING_STR));
+      break;
+    case STATE_NOUPDATE:
+      SendEvent(NS_LITERAL_STRING(NOUPDATE_STR));
+      break;
+    case STATE_OBSOLETE:
+      mStatus = nsIDOMOfflineResourceList::OBSOLETE;
+      mAvailableApplicationCache = nsnull;
+      SendEvent(NS_LITERAL_STRING(OBSOLETE_STR));
+      break;
+    case STATE_DOWNLOADING:
+      SendEvent(NS_LITERAL_STRING(DOWNLOADING_STR));
+      break;
+    case STATE_ITEMSTARTED:
+      SendEvent(NS_LITERAL_STRING(PROGRESS_STR));
+      break;
+    case STATE_ITEMCOMPLETED:
+      // Nothing to do here...
+      break;
+  }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsDOMOfflineResourceList::Checking(nsIOfflineCacheUpdate *aUpdate)
-{
-  SendEvent(NS_LITERAL_STRING(CHECKING_STR));
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDOMOfflineResourceList::NoUpdate(nsIOfflineCacheUpdate *aUpdate)
-{
-  SendEvent(NS_LITERAL_STRING(NOUPDATE_STR));
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDOMOfflineResourceList::Downloading(nsIOfflineCacheUpdate *aUpdate)
+nsDOMOfflineResourceList::ApplicationCacheAvailable(nsIApplicationCache *aApplicationCache)
 {
-  SendEvent(NS_LITERAL_STRING(DOWNLOADING_STR));
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDOMOfflineResourceList::ItemStarted(nsIOfflineCacheUpdate *aUpdate,
-                                      nsIDOMLoadStatus *aItem)
-{
-  SendEvent(NS_LITERAL_STRING(PROGRESS_STR));
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDOMOfflineResourceList::ItemCompleted(nsIOfflineCacheUpdate *aUpdate,
-                                        nsIDOMLoadStatus *aItem)
-{
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDOMOfflineResourceList::Obsolete(nsIOfflineCacheUpdate *aUpdate)
-{
-  SendEvent(NS_LITERAL_STRING(OBSOLETE_STR));
+  mAvailableApplicationCache = aApplicationCache;
   return NS_OK;
 }
 
 nsresult
 nsDOMOfflineResourceList::GetCacheKey(const nsAString &aURI, nsCString &aKey)
 {
   nsCOMPtr<nsIURI> requestedURI;
   nsresult rv = NS_NewURI(getter_AddRefs(requestedURI), aURI);
@@ -870,18 +896,20 @@ nsDOMOfflineResourceList::UpdateComplete
   PRBool succeeded;
   nsresult rv = mCacheUpdate->GetSucceeded(&succeeded);
 
   mCacheUpdate->RemoveObserver(this);
   mCacheUpdate = nsnull;
 
   if (NS_SUCCEEDED(rv) && succeeded && !partial) {
     if (isUpgrade) {
+      mStatus = nsIDOMOfflineResourceList::UPDATEREADY;
       SendEvent(NS_LITERAL_STRING(UPDATEREADY_STR));
     } else {
+      mStatus = nsIDOMOfflineResourceList::IDLE;
       SendEvent(NS_LITERAL_STRING(CACHED_STR));
     }
   }
 
   return NS_OK;
 }
 
 nsresult
@@ -900,16 +928,19 @@ nsDOMOfflineResourceList::GetCacheKey(ns
   }
 
   return NS_OK;
 }
 
 nsresult
 nsDOMOfflineResourceList::CacheKeys()
 {
+  if (IS_CHILD_PROCESS()) 
+    return NS_ERROR_NOT_IMPLEMENTED;
+
   if (mCachedKeys)
     return NS_OK;
 
   nsCOMPtr<nsIApplicationCache> appCache;
   mApplicationCacheService->GetActiveCache(mManifestSpec,
                                            getter_AddRefs(appCache));
 
   if (!appCache) {
@@ -924,11 +955,8 @@ void
 nsDOMOfflineResourceList::ClearCachedKeys()
 {
   if (mCachedKeys) {
     NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(mCachedKeysCount, mCachedKeys);
     mCachedKeys = nsnull;
     mCachedKeysCount = 0;
   }
 }
-
-
-
--- a/dom/src/offline/nsDOMOfflineResourceList.h
+++ b/dom/src/offline/nsDOMOfflineResourceList.h
@@ -105,17 +105,20 @@ private:
   PRBool mInitialized;
 
   nsCOMPtr<nsIURI> mManifestURI;
   // AsciiSpec of mManifestURI
   nsCString mManifestSpec;
 
   nsCOMPtr<nsIURI> mDocumentURI;
   nsCOMPtr<nsIApplicationCacheService> mApplicationCacheService;
+  nsCOMPtr<nsIApplicationCache> mAvailableApplicationCache;
   nsCOMPtr<nsIOfflineCacheUpdate> mCacheUpdate;
+  bool mExposeCacheUpdateStatus;
+  PRUint16 mStatus;
 
   // The set of dynamic keys for this application cache object.
   char **mCachedKeys;
   PRUint32 mCachedKeysCount;
 
   nsRefPtr<nsDOMEventListenerWrapper> mOnCheckingListener;
   nsRefPtr<nsDOMEventListenerWrapper> mOnErrorListener;
   nsRefPtr<nsDOMEventListenerWrapper> mOnNoUpdateListener;
--- a/ipc/ipdl/Makefile.in
+++ b/ipc/ipdl/Makefile.in
@@ -62,16 +62,17 @@ IPDLDIRS =  \
   ipc/testshell  \
   js/ipc  \
   js/jetpack \
   layout/ipc \
   netwerk/ipc  \
   netwerk/protocol/http  \
   netwerk/protocol/wyciwyg \
   netwerk/cookie  \
+  uriloader/prefetch  \
   $(NULL)
 ##-----------------------------------------------------------------------------
 
 ifdef MOZ_IPDL_TESTS
 DIRS += test
 endif
 
 vpath %.ipdl $(topsrcdir)
--- a/netwerk/base/public/nsIApplicationCache.idl
+++ b/netwerk/base/public/nsIApplicationCache.idl
@@ -106,20 +106,27 @@ interface nsIApplicationCacheNamespace :
  * 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(663e2e2e-04a0-47b6-87b3-a122be46cb53)]
+[scriptable, uuid(32f83e3f-470c-4423-a86a-d35d1c215ccb)]
 interface nsIApplicationCache : nsISupports
 {
     /**
+     * Init this application cache instance to just hold the group ID and
+     * the client ID to work just as a handle to the real cache. Used on
+     * content process to simplify the application cache code.
+     */
+    void initAsHandle(in ACString groupId, in ACString clientId);
+
+    /**
      * 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. */
--- a/netwerk/base/public/nsIApplicationCacheChannel.idl
+++ b/netwerk/base/public/nsIApplicationCacheChannel.idl
@@ -38,17 +38,17 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsISupports.idl"
 #include "nsIApplicationCacheContainer.idl"
 
 /**
  * Interface implemented by channels that support application caches.
  */
-[scriptable, uuid(9acfd21c-9c07-459f-8dae-ed2ffba23ddc)]
+[scriptable, uuid(8d9024e6-ab01-442d-8119-2f55d55d91b0)]
 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.
      */
@@ -70,9 +70,16 @@ interface nsIApplicationCacheChannel : n
      * notification callbacks.  Default value is false.
      *
      * This attribute will not be transferred through a redirect.
      *
      * NS_ERROR_ALREADY_OPENED will be thrown if set after AsyncOpen()
      * is called.
      */
     attribute boolean chooseApplicationCache;
+
+    /**
+     * A shortcut method to mark the cache item of this channel as 'foreign'.
+     * See the 'cache selection algorithm' and CACHE_SELECTION_RELOAD
+     * action handling in nsContentSink.
+     */
+    void markOfflineCacheEntryAsForeign();
 };
--- a/netwerk/build/nsNetCID.h
+++ b/netwerk/build/nsNetCID.h
@@ -507,16 +507,29 @@
 #define NS_APPLICATIONCACHENAMESPACE_CID             \
 { /* b00ed78a-04e2-4f74-8e1c-d1af79dfd12f */         \
     0xb00ed78a,                                      \
     0x04e2,                                          \
     0x4f74,                                          \
    {0x8e, 0x1c, 0xd1, 0xaf, 0x79, 0xdf, 0xd1, 0x2f}  \
 }
 
+#define NS_APPLICATIONCACHE_CLASSNAME \
+    "nsApplicationCache"
+#define NS_APPLICATIONCACHE_CONTRACTID \
+    "@mozilla.org/network/application-cache;1"
+
+#define NS_APPLICATIONCACHE_CID             \
+{ /* 463440c5-baad-4f3c-9e50-0b107abe7183 */ \
+    0x463440c5, \
+    0xbaad, \
+    0x4f3c, \
+   {0x9e, 0x50, 0xb, 0x10, 0x7a, 0xbe, 0x71, 0x83 } \
+}
+
 /******************************************************************************
  * netwerk/protocol/http/ classes
  */
 
 #define NS_HTTPPROTOCOLHANDLER_CID \
 { /* 4f47e42e-4d23-4dd3-bfda-eb29255e9ea3 */         \
     0x4f47e42e,                                      \
     0x4d23,                                          \
--- a/netwerk/build/nsNetModule.cpp
+++ b/netwerk/build/nsNetModule.cpp
@@ -56,16 +56,17 @@
 #include "nsStreamLoader.h"
 #include "nsUnicharStreamLoader.h"
 #include "nsFileStreams.h"
 #include "nsBufferedStreams.h"
 #include "nsMIMEInputStream.h"
 #include "nsSOCKSSocketProvider.h"
 #include "nsCacheService.h"
 #include "nsDiskCacheDeviceSQL.h"
+#include "nsApplicationCache.h"
 #include "nsMimeTypes.h"
 #include "nsNetStrings.h"
 #include "nsDNSPrefetch.h"
 #include "nsAboutProtocolHandler.h"
 
 #include "nsNetCID.h"
 
 #if defined(XP_MACOSX)
@@ -206,16 +207,17 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsNestedA
 #include "nsAboutCache.h"
 #include "nsAboutCacheEntry.h"
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsAboutCacheEntry)
 #endif
 
 #ifdef NECKO_OFFLINE_CACHE
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsOfflineCacheDevice, nsOfflineCacheDevice::GetInstance)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsApplicationCacheNamespace)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsApplicationCache)
 #endif
 
 #ifdef NECKO_PROTOCOL_file
 // file
 #include "nsFileProtocolHandler.h"
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsFileProtocolHandler, Init)
 #endif
 
@@ -704,16 +706,17 @@ NS_DEFINE_NAMED_CID(NS_ABOUT_CACHE_ENTRY
 #endif
 NS_DEFINE_NAMED_CID(NS_SOCKSSOCKETPROVIDER_CID);
 NS_DEFINE_NAMED_CID(NS_SOCKS4SOCKETPROVIDER_CID);
 NS_DEFINE_NAMED_CID(NS_UDPSOCKETPROVIDER_CID);
 NS_DEFINE_NAMED_CID(NS_CACHESERVICE_CID);
 #ifdef NECKO_OFFLINE_CACHE
 NS_DEFINE_NAMED_CID(NS_APPLICATIONCACHESERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_APPLICATIONCACHENAMESPACE_CID);
+NS_DEFINE_NAMED_CID(NS_APPLICATIONCACHE_CID);
 #endif
 #ifdef NECKO_COOKIES
 NS_DEFINE_NAMED_CID(NS_COOKIEMANAGER_CID);
 NS_DEFINE_NAMED_CID(NS_COOKIESERVICE_CID);
 #endif
 #ifdef NECKO_WIFI
 NS_DEFINE_NAMED_CID(NS_WIFI_MONITOR_COMPONENT_CID);
 #endif
@@ -828,16 +831,17 @@ static const mozilla::Module::CIDEntry k
 #endif
     { &kNS_SOCKSSOCKETPROVIDER_CID, false, NULL, nsSOCKSSocketProvider::CreateV5 },
     { &kNS_SOCKS4SOCKETPROVIDER_CID, false, NULL, nsSOCKSSocketProvider::CreateV4 },
     { &kNS_UDPSOCKETPROVIDER_CID, false, NULL, nsUDPSocketProviderConstructor },
     { &kNS_CACHESERVICE_CID, false, NULL, nsCacheService::Create },
 #ifdef NECKO_OFFLINE_CACHE
     { &kNS_APPLICATIONCACHESERVICE_CID, false, NULL, nsOfflineCacheDeviceConstructor },
     { &kNS_APPLICATIONCACHENAMESPACE_CID, false, NULL, nsApplicationCacheNamespaceConstructor },
+    { &kNS_APPLICATIONCACHE_CID, false, NULL, nsApplicationCacheConstructor },
 #endif
 #ifdef NECKO_COOKIES
     { &kNS_COOKIEMANAGER_CID, false, NULL, nsICookieServiceConstructor },
     { &kNS_COOKIESERVICE_CID, false, NULL, nsICookieServiceConstructor },
 #endif
 #ifdef NECKO_WIFI
     { &kNS_WIFI_MONITOR_COMPONENT_CID, false, NULL, nsWifiMonitorConstructor },
 #endif
@@ -959,16 +963,17 @@ static const mozilla::Module::ContractID
 #endif
     { NS_NETWORK_SOCKET_CONTRACTID_PREFIX "socks", &kNS_SOCKSSOCKETPROVIDER_CID },
     { NS_NETWORK_SOCKET_CONTRACTID_PREFIX "socks4", &kNS_SOCKS4SOCKETPROVIDER_CID },
     { NS_NETWORK_SOCKET_CONTRACTID_PREFIX "udp", &kNS_UDPSOCKETPROVIDER_CID },
     { NS_CACHESERVICE_CONTRACTID, &kNS_CACHESERVICE_CID },
 #ifdef NECKO_OFFLINE_CACHE
     { NS_APPLICATIONCACHESERVICE_CONTRACTID, &kNS_APPLICATIONCACHESERVICE_CID },
     { NS_APPLICATIONCACHENAMESPACE_CONTRACTID, &kNS_APPLICATIONCACHENAMESPACE_CID },
+    { NS_APPLICATIONCACHE_CONTRACTID, &kNS_APPLICATIONCACHE_CID },
 #endif
 #ifdef NECKO_COOKIES
     { NS_COOKIEMANAGER_CONTRACTID, &kNS_COOKIEMANAGER_CID },
     { NS_COOKIESERVICE_CONTRACTID, &kNS_COOKIESERVICE_CID },
 #endif
 #ifdef NECKO_WIFI
     { NS_WIFI_MONITOR_CONTRACTID, &kNS_WIFI_MONITOR_COMPONENT_CID },
 #endif
new file mode 100644
--- /dev/null
+++ b/netwerk/cache/nsApplicationCache.h
@@ -0,0 +1,62 @@
+/* vim:set ts=2 sw=2 sts=2 et cin: */
+/* ***** 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 Mozilla.
+ *
+ * The Initial Developer of the Original Code is Mozilla Corporation.
+ * Portions created by Mozilla Corporation are Copyright (C) 2010
+ * Mozilla Corporation. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Dave Camp <dcamp@mozilla.com>
+ *   Honza Bambas <honzab@firemni.cz>
+ *
+ * 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 ***** */
+
+class nsApplicationCache : public nsIApplicationCache
+                         , public nsSupportsWeakReference
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIAPPLICATIONCACHE
+
+  nsApplicationCache(nsOfflineCacheDevice *device,
+                     const nsACString &group,
+                     const nsACString &clientID);
+
+  nsApplicationCache();
+
+  virtual ~nsApplicationCache();
+
+  void MarkInvalid();
+
+private:
+  nsRefPtr<nsOfflineCacheDevice> mDevice;
+  nsCString mGroup;
+  nsCString mClientID;
+  PRBool mValid;
+};
+
--- a/netwerk/cache/nsDiskCacheDeviceSQL.cpp
+++ b/netwerk/cache/nsDiskCacheDeviceSQL.cpp
@@ -36,16 +36,17 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsCache.h"
 #include "nsDiskCache.h"
 #include "nsDiskCacheDeviceSQL.h"
 #include "nsCacheService.h"
+#include "nsApplicationCache.h"
 
 #include "nsNetCID.h"
 #include "nsNetUtil.h"
 #include "nsAutoPtr.h"
 #include "nsEscape.h"
 #include "nsIPrefBranch.h"
 #include "nsIPrefService.h"
 #include "nsString.h"
@@ -589,61 +590,66 @@ nsApplicationCacheNamespace::GetData(nsA
   out = mData;
   return NS_OK;
 }
 
 /******************************************************************************
  * 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()
+  : mDevice(nsnull)
+  , mValid(PR_TRUE)
+{
+}
+
 nsApplicationCache::nsApplicationCache(nsOfflineCacheDevice *device,
                                        const nsACString &group,
                                        const nsACString &clientID)
   : mDevice(device)
   , mGroup(group)
   , mClientID(clientID)
   , mValid(PR_TRUE)
 {
 }
 
 nsApplicationCache::~nsApplicationCache()
 {
+  if (!mDevice)
+    return;
+
   mDevice->mCaches.Remove(mClientID);
 
   // If this isn't an active cache anymore, it can be destroyed.
   if (mValid && !mDevice->IsActiveCache(mGroup, mClientID))
     Discard();
 }
 
+void
+nsApplicationCache::MarkInvalid()
+{
+  mValid = PR_FALSE;
+}
+
+NS_IMETHODIMP
+nsApplicationCache::InitAsHandle(const nsACString &groupId,
+                                 const nsACString &clientId)
+{
+  NS_ENSURE_FALSE(mDevice, NS_ERROR_ALREADY_INITIALIZED);
+  NS_ENSURE_TRUE(mGroup.IsEmpty(), NS_ERROR_ALREADY_INITIALIZED);
+
+  mGroup = groupId;
+  mClientID = clientId;
+  return NS_OK;
+}
+
 NS_IMETHODIMP
 nsApplicationCache::GetGroupID(nsACString &out)
 {
   out = mGroup;
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -651,86 +657,95 @@ nsApplicationCache::GetClientID(nsACStri
 {
   out = mClientID;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsApplicationCache::GetActive(PRBool *out)
 {
+  NS_ENSURE_TRUE(mDevice, NS_ERROR_NOT_AVAILABLE);
+
   *out = mDevice->IsActiveCache(mGroup, mClientID);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsApplicationCache::Activate()
 {
   NS_ENSURE_TRUE(mValid, NS_ERROR_NOT_AVAILABLE);
+  NS_ENSURE_TRUE(mDevice, NS_ERROR_NOT_AVAILABLE);
 
   mDevice->ActivateCache(mGroup, mClientID);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsApplicationCache::Discard()
 {
   NS_ENSURE_TRUE(mValid, NS_ERROR_NOT_AVAILABLE);
+  NS_ENSURE_TRUE(mDevice, 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);
+  NS_ENSURE_TRUE(mDevice, 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);
+  NS_ENSURE_TRUE(mDevice, 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);
+  NS_ENSURE_TRUE(mDevice, 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);
+  NS_ENSURE_TRUE(mDevice, NS_ERROR_NOT_AVAILABLE);
 
   return mDevice->GatherEntries(mClientID, typeBits, count, keys);
 }
 
 NS_IMETHODIMP
 nsApplicationCache::AddNamespaces(nsIArray *namespaces)
 {
   NS_ENSURE_TRUE(mValid, NS_ERROR_NOT_AVAILABLE);
+  NS_ENSURE_TRUE(mDevice, NS_ERROR_NOT_AVAILABLE);
 
   if (!namespaces)
     return NS_OK;
 
   mozStorageTransaction transaction(mDevice->mDB, PR_FALSE);
 
   PRUint32 length;
   nsresult rv = namespaces->GetLength(&length);
@@ -752,24 +767,26 @@ nsApplicationCache::AddNamespaces(nsIArr
 }
 
 NS_IMETHODIMP
 nsApplicationCache::GetMatchingNamespace(const nsACString &key,
                                          nsIApplicationCacheNamespace **out)
 
 {
   NS_ENSURE_TRUE(mValid, NS_ERROR_NOT_AVAILABLE);
+  NS_ENSURE_TRUE(mDevice, NS_ERROR_NOT_AVAILABLE);
 
   return mDevice->GetMatchingNamespace(mClientID, key, out);
 }
 
 NS_IMETHODIMP
 nsApplicationCache::GetUsage(PRUint32 *usage)
 {
   NS_ENSURE_TRUE(mValid, NS_ERROR_NOT_AVAILABLE);
+  NS_ENSURE_TRUE(mDevice, NS_ERROR_NOT_AVAILABLE);
 
   return mDevice->GetUsage(mClientID, usage);
 }
 
 /******************************************************************************
  * nsCloseDBEvent
  *****************************************************************************/
 
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -179,16 +179,31 @@ class StartRequestEvent : public Channel
   PRPackedBool mUseResponseHead;
   PRPackedBool mIsFromCache;
   PRPackedBool mCacheEntryAvailable;
   PRUint32 mCacheExpirationTime;
   nsCString mCachedCharset;
   nsCString mSecurityInfoSerialization;
 };
 
+bool
+HttpChannelChild::RecvAssociateApplicationCache(const nsCString &groupID,
+                                                const nsCString &clientID)
+{
+  nsresult rv;
+  mApplicationCache = do_CreateInstance(
+    NS_APPLICATIONCACHE_CONTRACTID, &rv);
+  if (NS_FAILED(rv))
+    return true;
+
+  mLoadedFromApplicationCache = PR_TRUE;
+  mApplicationCache->InitAsHandle(groupID, clientID);
+  return true;
+}
+
 bool 
 HttpChannelChild::RecvOnStartRequest(const nsHttpResponseHead& responseHead,
                                      const PRBool& useResponseHead,
                                      const RequestHeaderTuples& requestHeaders,
                                      const PRBool& isFromCache,
                                      const PRBool& cacheEntryAvailable,
                                      const PRUint32& cacheExpirationTime,
                                      const nsCString& cachedCharset,
@@ -873,16 +888,32 @@ HttpChannelChild::AsyncOpen(nsIStreamLis
     // connection. See nsHttpChannel::AsyncOpen().
 
     // Clear mCanceled here, or we will bail out at top of OnCancel().
     mCanceled = false;
     OnCancel(mStatus);
     return NS_OK;
   }
 
+  nsCString appCacheClientId;
+  if (mInheritApplicationCache) {
+    // Pick up an application cache from the notification
+    // callbacks if available
+    nsCOMPtr<nsIApplicationCacheContainer> appCacheContainer;
+    GetCallback(appCacheContainer);
+
+    if (appCacheContainer) {
+      nsCOMPtr<nsIApplicationCache> appCache;
+      rv = appCacheContainer->GetApplicationCache(getter_AddRefs(appCache));
+      if (NS_SUCCEEDED(rv) && appCache) {
+        appCache->GetClientID(appCacheClientId);
+      }
+    }
+  }
+
   //
   // Send request to the chrome process...
   //
 
   // FIXME: bug 558623: Combine constructor and SendAsyncOpen into one IPC msg
 
   mozilla::dom::TabChild* tabChild = nsnull;
   nsCOMPtr<nsITabChild> iTabChild;
@@ -897,17 +928,18 @@ HttpChannelChild::AsyncOpen(nsIStreamLis
 
   gNeckoChild->SendPHttpChannelConstructor(this, tabChild);
 
   SendAsyncOpen(IPC::URI(mURI), IPC::URI(mOriginalURI),
                 IPC::URI(mDocumentURI), IPC::URI(mReferrer), mLoadFlags,
                 mRequestHeaders, mRequestHead.Method(), uploadStreamData,
                 uploadStreamInfo, mPriority, mRedirectionLimit,
                 mAllowPipelining, mForceAllowThirdPartyCookie, mSendResumeAt,
-                mStartPos, mEntityID);
+                mStartPos, mEntityID, mChooseApplicationCache, 
+                appCacheClientId);
 
   return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // HttpChannelChild::nsIHttpChannel
 //-----------------------------------------------------------------------------
 
@@ -1042,58 +1074,70 @@ HttpChannelChild::SetNewListener(nsIStre
 
 //-----------------------------------------------------------------------------
 // HttpChannelChild::nsIApplicationCacheContainer
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 HttpChannelChild::GetApplicationCache(nsIApplicationCache **aApplicationCache)
 {
-  DROP_DEAD();
+  NS_IF_ADDREF(*aApplicationCache = mApplicationCache);
+  return NS_OK;
 }
 NS_IMETHODIMP
 HttpChannelChild::SetApplicationCache(nsIApplicationCache *aApplicationCache)
 {
-  // FIXME: redirects call. so stub OK for now. Fix in bug 536295.  
-  return NS_ERROR_NOT_IMPLEMENTED;
+  NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
+
+  mApplicationCache = aApplicationCache;
+  return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // HttpChannelChild::nsIApplicationCacheChannel
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
-HttpChannelChild::GetLoadedFromApplicationCache(PRBool *retval)
+HttpChannelChild::GetLoadedFromApplicationCache(PRBool *aLoadedFromApplicationCache)
 {
-  // FIXME: stub for bug 536295
-  *retval = 0;
+  *aLoadedFromApplicationCache = mLoadedFromApplicationCache;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-HttpChannelChild::GetInheritApplicationCache(PRBool *aInheritApplicationCache)
+HttpChannelChild::GetInheritApplicationCache(PRBool *aInherit)
 {
-  DROP_DEAD();
+  *aInherit = mInheritApplicationCache;
+  return NS_OK;
 }
 NS_IMETHODIMP
-HttpChannelChild::SetInheritApplicationCache(PRBool aInheritApplicationCache)
+HttpChannelChild::SetInheritApplicationCache(PRBool aInherit)
 {
-  // FIXME: Browser calls this early, so stub OK for now. Fix in bug 536295.  
+  mInheritApplicationCache = aInherit;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-HttpChannelChild::GetChooseApplicationCache(PRBool *aChooseApplicationCache)
+HttpChannelChild::GetChooseApplicationCache(PRBool *aChoose)
 {
-  DROP_DEAD();
+  *aChoose = mChooseApplicationCache;
+  return NS_OK;
 }
+
 NS_IMETHODIMP
-HttpChannelChild::SetChooseApplicationCache(PRBool aChooseApplicationCache)
+HttpChannelChild::SetChooseApplicationCache(PRBool aChoose)
 {
-  // FIXME: Browser calls this early, so stub OK for now. Fix in bug 536295.  
+  mChooseApplicationCache = aChoose;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+HttpChannelChild::MarkOfflineCacheEntryAsForeign()
+{
+  SendMarkOfflineCacheEntryAsForeign();
   return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // HttpChannelChild::nsIAssociatedContentSecurity
 //-----------------------------------------------------------------------------
 
 bool
--- a/netwerk/protocol/http/HttpChannelChild.h
+++ b/netwerk/protocol/http/HttpChannelChild.h
@@ -136,16 +136,18 @@ protected:
   bool RecvOnProgress(const PRUint64& progress, const PRUint64& progressMax);
   bool RecvOnStatus(const nsresult& status, const nsString& statusArg);
   bool RecvCancelEarly(const nsresult& status);
   bool RecvRedirect1Begin(PHttpChannelChild* newChannel,
                           const URI& newURI,
                           const PRUint32& redirectFlags,
                           const nsHttpResponseHead& responseHead);
   bool RecvRedirect3Complete();
+  bool RecvAssociateApplicationCache(const nsCString& groupID,
+                                     const nsCString& clientID);
   bool RecvDeleteSelf();
 
   bool GetAssociatedContentSecurity(nsIAssociatedContentSecurity** res = nsnull);
 
 private:
   RequestHeaderTuples mRequestHeaders;
   nsRefPtr<HttpChannelChild> mRedirectChannelChild;
   nsCOMPtr<nsIURI> mRedirectOriginalURI;
--- a/netwerk/protocol/http/HttpChannelParent.cpp
+++ b/netwerk/protocol/http/HttpChannelParent.cpp
@@ -49,16 +49,18 @@
 #include "nsISupportsPriority.h"
 #include "nsIAuthPromptProvider.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsIBadCertListener2.h"
 #include "nsICacheEntryDescriptor.h"
 #include "nsSerializationHelper.h"
 #include "nsISerializable.h"
 #include "nsIAssociatedContentSecurity.h"
+#include "nsIApplicationCacheService.h"
+#include "nsIOfflineCacheUpdate.h"
 
 namespace mozilla {
 namespace net {
 
 HttpChannelParent::HttpChannelParent(PBrowserParent* iframeEmbedding)
 : mIPCClosed(false)
 {
   // Ensure gHttpHandler is initialized: we need the atom table up and running.
@@ -105,17 +107,19 @@ HttpChannelParent::RecvAsyncOpen(const I
                                  const nsCString&           uploadStreamData,
                                  const PRInt32&             uploadStreamInfo,
                                  const PRUint16&            priority,
                                  const PRUint8&             redirectionLimit,
                                  const PRBool&              allowPipelining,
                                  const PRBool&              forceAllowThirdPartyCookie,
                                  const bool&                doResumeAt,
                                  const PRUint64&            startPos,
-                                 const nsCString&           entityID)
+                                 const nsCString&           entityID,
+                                 const bool&                chooseApplicationCache,
+                                 const nsCString&           appCacheClientID)
 {
   nsCOMPtr<nsIURI> uri(aURI);
   nsCOMPtr<nsIURI> originalUri(aOriginalURI);
   nsCOMPtr<nsIURI> docUri(aDocURI);
   nsCOMPtr<nsIURI> referrerUri(aReferrerURI);
 
   nsCString uriSpec;
   uri->GetSpec(uriSpec);
@@ -171,16 +175,51 @@ HttpChannelParent::RecvAsyncOpen(const I
   }
 
   if (priority != nsISupportsPriority::PRIORITY_NORMAL)
     httpChan->SetPriority(priority);
   httpChan->SetRedirectionLimit(redirectionLimit);
   httpChan->SetAllowPipelining(allowPipelining);
   httpChan->SetForceAllowThirdPartyCookie(forceAllowThirdPartyCookie);
 
+  nsCOMPtr<nsIApplicationCacheChannel> appCacheChan =
+    do_QueryInterface(mChannel);
+  nsCOMPtr<nsIApplicationCacheService> appCacheService =
+    do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID);
+
+  PRBool setChooseApplicationCache = chooseApplicationCache;
+  if (appCacheChan && appCacheService) {
+    // We might potentially want to drop this flag (that is TRUE by default)
+    // after we succefully associate the channel with an application cache
+    // reported by the channel child.  Dropping it here may be too early.
+    appCacheChan->SetInheritApplicationCache(PR_FALSE);
+    if (!appCacheClientID.IsEmpty()) {
+      nsCOMPtr<nsIApplicationCache> appCache;
+      rv = appCacheService->GetApplicationCache(appCacheClientID,
+                                                getter_AddRefs(appCache));
+      if (NS_SUCCEEDED(rv)) {
+        appCacheChan->SetApplicationCache(appCache);
+        setChooseApplicationCache = PR_FALSE;
+      }
+    }
+
+    if (setChooseApplicationCache) {
+      nsCOMPtr<nsIOfflineCacheUpdateService> offlineUpdateService =
+        do_GetService("@mozilla.org/offlinecacheupdate-service;1", &rv);
+      if (NS_SUCCEEDED(rv)) {
+        rv = offlineUpdateService->OfflineAppAllowedForURI(uri,
+                                                           nsnull,
+                                                           &setChooseApplicationCache);
+
+        if (setChooseApplicationCache && NS_SUCCEEDED(rv))
+          appCacheChan->SetChooseApplicationCache(PR_TRUE);
+      }
+    }
+  }
+
   rv = httpChan->AsyncOpen(mChannelListener, nsnull);
   if (NS_FAILED(rv))
     return SendCancelEarly(rv);
 
   return true;
 }
 
 bool 
@@ -268,16 +307,24 @@ HttpChannelParent::RecvDocumentChannelCl
 {
   // We must clear the cache entry here, else we'll block other channels from
   // reading it if we've got it open for writing.  
   mCacheDescriptor = 0;
 
   return true;
 }
 
+bool 
+HttpChannelParent::RecvMarkOfflineCacheEntryAsForeign()
+{
+  nsHttpChannel *httpChan = static_cast<nsHttpChannel *>(mChannel.get());
+  httpChan->MarkOfflineCacheEntryAsForeign();
+  return true;
+}
+
 //-----------------------------------------------------------------------------
 // nsIRequestObserver and nsIStreamListener methods equivalents
 //-----------------------------------------------------------------------------
 
 nsresult
 HttpChannelParent::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext)
 {
   LOG(("HttpChannelParent::OnStartRequest [this=%x]\n", this));
@@ -291,16 +338,32 @@ HttpChannelParent::OnStartRequest(nsIReq
   nsHttpRequestHead  *requestHead = chan->GetRequestHead();
   PRBool isFromCache = false;
   chan->IsFromCache(&isFromCache);
   PRUint32 expirationTime = nsICache::NO_EXPIRATION_TIME;
   chan->GetCacheTokenExpirationTime(&expirationTime);
   nsCString cachedCharset;
   chan->GetCacheTokenCachedCharset(cachedCharset);
 
+  PRBool loadedFromApplicationCache;
+  chan->GetLoadedFromApplicationCache(&loadedFromApplicationCache);
+  if (loadedFromApplicationCache) {
+    nsCOMPtr<nsIApplicationCache> appCache;
+    chan->GetApplicationCache(getter_AddRefs(appCache));
+    nsCString appCacheGroupId;
+    nsCString appCacheClientId;
+    appCache->GetGroupID(appCacheGroupId);
+    appCache->GetClientID(appCacheClientId);
+    if (mIPCClosed || 
+        !SendAssociateApplicationCache(appCacheGroupId, appCacheClientId))
+    {
+      return NS_ERROR_UNEXPECTED;
+    }
+  }
+
   nsCOMPtr<nsIEncodedChannel> encodedChannel = do_QueryInterface(aRequest);
   if (encodedChannel)
     encodedChannel->SetApplyConversion(PR_FALSE);
 
   // Keep the cache entry for future use in RecvSetCacheTokenCachedCharset().
   // It could be already released by nsHttpChannel at that time.
   chan->GetCacheToken(getter_AddRefs(mCacheDescriptor));
 
--- a/netwerk/protocol/http/HttpChannelParent.h
+++ b/netwerk/protocol/http/HttpChannelParent.h
@@ -91,30 +91,33 @@ protected:
                              const nsCString&           uploadStreamData,
                              const PRInt32&             uploadStreamInfo,
                              const PRUint16&            priority,
                              const PRUint8&             redirectionLimit,
                              const PRBool&              allowPipelining,
                              const PRBool&              forceAllowThirdPartyCookie,
                              const bool&                doResumeAt,
                              const PRUint64&            startPos,
-                             const nsCString&           entityID);
+                             const nsCString&           entityID,
+                             const bool&                chooseApplicationCache,
+                             const nsCString&           appCacheClientID);
 
   virtual bool RecvSetPriority(const PRUint16& priority);
   virtual bool RecvSetCacheTokenCachedCharset(const nsCString& charset);
   virtual bool RecvSuspend();
   virtual bool RecvResume();
   virtual bool RecvCancel(const nsresult& status);
   virtual bool RecvRedirect2Verify(const nsresult& result,
                                    const RequestHeaderTuples& changedHeaders);
   virtual bool RecvUpdateAssociatedContentSecurity(const PRInt32& high,
                                                    const PRInt32& low,
                                                    const PRInt32& broken,
                                                    const PRInt32& no);
   virtual bool RecvDocumentChannelCleanup();
+  virtual bool RecvMarkOfflineCacheEntryAsForeign();
 
   virtual void ActorDestroy(ActorDestroyReason why);
 
 protected:
   friend class mozilla::net::HttpChannelParentListener;
   nsCOMPtr<nsITabParent> mTabParent;
 
 private:
--- a/netwerk/protocol/http/PHttpChannel.ipdl
+++ b/netwerk/protocol/http/PHttpChannel.ipdl
@@ -71,17 +71,19 @@ parent:
             nsCString           uploadStreamData,
             PRInt32             uploadStreamInfo,
             PRUint16            priority,
             PRUint8             redirectionLimit,
             PRBool              allowPipelining,
             PRBool              forceAllowThirdPartyCookie,
             bool                resumeAt,
             PRUint64            startPos,
-            nsCString           entityID);
+            nsCString           entityID,
+            bool                chooseApplicationCache,
+            nsCString           appCacheClientID);
 
   SetPriority(PRUint16 priority);
 
   SetCacheTokenCachedCharset(nsCString charset);
 
   UpdateAssociatedContentSecurity(PRInt32 high,
                                   PRInt32 low,
                                   PRInt32 broken,
@@ -94,16 +96,31 @@ parent:
   // Reports approval/veto of redirect by child process redirect observers
   Redirect2Verify(nsresult result, RequestHeaderTuples changedHeaders);
 
   // For document loads we keep this protocol open after child's
   // OnStopRequest, and send this msg (instead of __delete__) to allow
   // partial cleanup on parent. 
   DocumentChannelCleanup();
 
+  // This might have to be sync. If this fails we must fail the document load
+  // to avoid endless loop.
+  //
+  // Explanation: the document loaded was loaded from the offline cache. But
+  // the cache group id (the manifest URL) of the cache group it was loaded 
+  // from is different then the manifest the document refers to in the html 
+  // tag. If we detect this during the cache selection algorithm, we must not 
+  // load this document from the offline cache group it was just loaded from.
+  // Marking the cache entry as foreign in its cache group will prevent
+  // the document to load from the bad offline cache group. After it is marked,
+  // we reload the document to take the effect. If we fail to mark the entry 
+  // as foreign, we will end up in the same situation and reload again and
+  // again, indefinitely.
+  MarkOfflineCacheEntryAsForeign();
+
   __delete__();
 
 child:
   OnStartRequest(nsHttpResponseHead  responseHead,
                  PRBool              useResponseHead,
                  RequestHeaderTuples requestHeaders,
                  PRBool              isFromCache,
                  PRBool              cacheEntryAvailable,
@@ -130,16 +147,20 @@ child:
   Redirect1Begin(PHttpChannel       newChannel,
                  URI                newUri,
                  PRUint32           redirectFlags,
                  nsHttpResponseHead responseHead);
 
   // Called if redirect successful so that child can complete setup.
   Redirect3Complete();
 
+  // Associte the child with an application ids
+  AssociateApplicationCache(nsCString groupID,
+                            nsCString clientID);
+
   // Tell child to delete channel (all IPDL deletes must be done from child to
   // avoid races: see bug 591708).
   DeleteSelf();
 };
 
 
 } // namespace net
 } // namespace mozilla
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -4528,16 +4528,35 @@ NS_IMETHODIMP
 nsHttpChannel::SetChooseApplicationCache(PRBool aChoose)
 {
     NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
 
     mChooseApplicationCache = aChoose;
     return NS_OK;
 }
 
+NS_IMETHODIMP
+nsHttpChannel::MarkOfflineCacheEntryAsForeign()
+{
+    if (!mApplicationCache)
+        return NS_ERROR_NOT_AVAILABLE;
+
+    nsresult rv;
+
+    nsCAutoString cacheKey;
+    rv = GenerateCacheKey(mPostID, cacheKey);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = mApplicationCache->MarkEntry(cacheKey,
+                                      nsIApplicationCache::ITEM_FOREIGN);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    return NS_OK;
+}
+
 //-----------------------------------------------------------------------------
 // nsHttpChannel::nsIAsyncVerifyRedirectCallback
 //-----------------------------------------------------------------------------
 
 nsresult
 nsHttpChannel::WaitForRedirectCallback()
 {
     nsresult rv;
--- a/uriloader/prefetch/Makefile.in
+++ b/uriloader/prefetch/Makefile.in
@@ -41,27 +41,52 @@ srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE		= prefetch
 LIBRARY_NAME	= prefetch_s
 LIBXUL_LIBRARY	= 1
 
+ifdef MOZ_IPC
+EXPORTS_NAMESPACES = mozilla/docshell
+
+EXPORTS_mozilla/docshell += \
+  OfflineCacheUpdateParent.h \
+  OfflineCacheUpdateChild.h  \
+  $(NULL)
+endif
+
 CPPSRCS = \
 		nsPrefetchService.cpp \
 		nsOfflineCacheUpdate.cpp \
+		nsOfflineCacheUpdateService.cpp  \
+		OfflineCacheUpdateGlue.cpp \
 		$(NULL)
 
+ifdef MOZ_IPC
+CPPSRCS += \
+		OfflineCacheUpdateChild.cpp \
+		OfflineCacheUpdateParent.cpp
+endif
+
 XPIDLSRCS =	\
 		nsIPrefetchService.idl \
 		nsIOfflineCacheUpdate.idl \
 		$(NULL)
+
 EXPORTS = \
 		nsCPrefetchService.h \
 		$(NULL)
 
+LOCAL_INCLUDES = \
+		-I$(topsrcdir)/content/base/src \
+		-I$(topsrcdir)/content/events/src \
+		$(NULL)
+
 # we don't want the shared lib, but we want to force the creation of a static lib.
 FORCE_STATIC_LIB = 1
 
+include $(topsrcdir)/config/config.mk
+include $(topsrcdir)/ipc/chromium/chromium-config.mk
 include $(topsrcdir)/config/rules.mk
 
 # vim: ts=4 sw=4 noexpandtab
copy from uriloader/prefetch/nsOfflineCacheUpdate.cpp
copy to uriloader/prefetch/OfflineCacheUpdateChild.cpp
--- a/uriloader/prefetch/nsOfflineCacheUpdate.cpp
+++ b/uriloader/prefetch/OfflineCacheUpdateChild.cpp
@@ -31,1622 +31,103 @@
  * 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 "OfflineCacheUpdateChild.h"
 #include "nsOfflineCacheUpdate.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/TabChild.h"
 
-#include "nsCPrefetchService.h"
-#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 "mozilla/dom/Element.h"
-#include "nsIDocumentLoader.h"
-#include "nsIDOMElement.h"
+#include "nsIDocShell.h"
+#include "nsIDocShellTreeItem.h"
+#include "nsIDocShellTreeOwner.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"
-#include "nsIPrefService.h"
+#include "nsITabChild.h"
 #include "nsNetCID.h"
 #include "nsNetUtil.h"
 #include "nsServiceManagerUtils.h"
 #include "nsStreamUtils.h"
 #include "nsThreadUtils.h"
 #include "nsProxyRelease.h"
 #include "prlog.h"
 #include "nsIAsyncVerifyRedirectCallback.h"
 
 static nsOfflineCacheUpdateService *gOfflineCacheUpdateService = nsnull;
 
-static const PRUint32 kRescheduleLimit = 3;
-
 #if defined(PR_LOGGING)
 //
 // To enable logging (see prlog.h for full details):
 //
 //    set NSPR_LOG_MODULES=nsOfflineCacheUpdate:5
 //    set NSPR_LOG_FILE=offlineupdate.log
 //
 // this enables PR_LOG_ALWAYS level information and places all output in
 // the file offlineupdate.log
 //
-static PRLogModuleInfo *gOfflineCacheUpdateLog;
+extern PRLogModuleInfo *gOfflineCacheUpdateLog;
 #endif
 #define LOG(args) PR_LOG(gOfflineCacheUpdateLog, 4, args)
 #define LOG_ENABLED() PR_LOG_TEST(gOfflineCacheUpdateLog, 4)
 
-class AutoFreeArray {
-public:
-    AutoFreeArray(PRUint32 count, char **values)
-        : mCount(count), mValues(values) {};
-    ~AutoFreeArray() { NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(mCount, mValues); }
-private:
-    PRUint32 mCount;
-    char **mValues;
-};
-
-static nsresult
-DropReferenceFromURL(nsIURI * aURI)
-{
-    nsCOMPtr<nsIURL> url = do_QueryInterface(aURI);
-    if (url) {
-        nsresult rv = url->SetRef(EmptyCString());
-        NS_ENSURE_SUCCESS(rv, rv);
-    }
-
-    return NS_OK;
-}
-
-//-----------------------------------------------------------------------------
-// nsManifestCheck
-//-----------------------------------------------------------------------------
-
-class nsManifestCheck : public nsIStreamListener
-                      , public nsIChannelEventSink
-                      , public nsIInterfaceRequestor
-{
-public:
-    nsManifestCheck(nsOfflineCacheUpdate *aUpdate,
-                    nsIURI *aURI,
-                    nsIURI *aReferrerURI)
-        : mUpdate(aUpdate)
-        , mURI(aURI)
-        , mReferrerURI(aReferrerURI)
-        {}
-
-    NS_DECL_ISUPPORTS
-    NS_DECL_NSIREQUESTOBSERVER
-    NS_DECL_NSISTREAMLISTENER
-    NS_DECL_NSICHANNELEVENTSINK
-    NS_DECL_NSIINTERFACEREQUESTOR
-
-    nsresult Begin();
-
-private:
-
-    static NS_METHOD ReadManifest(nsIInputStream *aInputStream,
-                                  void *aClosure,
-                                  const char *aFromSegment,
-                                  PRUint32 aOffset,
-                                  PRUint32 aCount,
-                                  PRUint32 *aBytesConsumed);
-
-    nsRefPtr<nsOfflineCacheUpdate> mUpdate;
-    nsCOMPtr<nsIURI> mURI;
-    nsCOMPtr<nsIURI> mReferrerURI;
-    nsCOMPtr<nsICryptoHash> mManifestHash;
-    nsCOMPtr<nsIChannel> mChannel;
-};
+namespace mozilla {
+namespace docshell {
 
 //-----------------------------------------------------------------------------
-// nsManifestCheck::nsISupports
-//-----------------------------------------------------------------------------
-NS_IMPL_ISUPPORTS4(nsManifestCheck,
-                   nsIRequestObserver,
-                   nsIStreamListener,
-                   nsIChannelEventSink,
-                   nsIInterfaceRequestor)
-
-//-----------------------------------------------------------------------------
-// nsManifestCheck <public>
-//-----------------------------------------------------------------------------
-
-nsresult
-nsManifestCheck::Begin()
-{
-    nsresult rv;
-    mManifestHash = do_CreateInstance("@mozilla.org/security/hash;1", &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = mManifestHash->Init(nsICryptoHash::MD5);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = NS_NewChannel(getter_AddRefs(mChannel),
-                       mURI,
-                       nsnull, nsnull, nsnull,
-                       nsIRequest::LOAD_BYPASS_CACHE);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    // configure HTTP specific stuff
-    nsCOMPtr<nsIHttpChannel> httpChannel =
-        do_QueryInterface(mChannel);
-    if (httpChannel) {
-        httpChannel->SetReferrer(mReferrerURI);
-        httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("X-Moz"),
-                                      NS_LITERAL_CSTRING("offline-resource"),
-                                      PR_FALSE);
-    }
-
-    rv = mChannel->AsyncOpen(this, nsnull);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    return NS_OK;
-}
-
-//-----------------------------------------------------------------------------
-// nsManifestCheck <public>
-//-----------------------------------------------------------------------------
-
-/* static */
-NS_METHOD
-nsManifestCheck::ReadManifest(nsIInputStream *aInputStream,
-                              void *aClosure,
-                              const char *aFromSegment,
-                              PRUint32 aOffset,
-                              PRUint32 aCount,
-                              PRUint32 *aBytesConsumed)
-{
-    nsManifestCheck *manifestCheck =
-        static_cast<nsManifestCheck*>(aClosure);
-
-    nsresult rv;
-    *aBytesConsumed = aCount;
-
-    rv = manifestCheck->mManifestHash->Update(
-        reinterpret_cast<const PRUint8 *>(aFromSegment), aCount);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    return NS_OK;
-}
-
-//-----------------------------------------------------------------------------
-// nsManifestCheck::nsIStreamListener
+// OfflineCacheUpdateChild::nsISupports
 //-----------------------------------------------------------------------------
 
-NS_IMETHODIMP
-nsManifestCheck::OnStartRequest(nsIRequest *aRequest,
-                                nsISupports *aContext)
-{
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsManifestCheck::OnDataAvailable(nsIRequest *aRequest,
-                                 nsISupports *aContext,
-                                 nsIInputStream *aStream,
-                                 PRUint32 aOffset,
-                                 PRUint32 aCount)
-{
-    PRUint32 bytesRead;
-    aStream->ReadSegments(ReadManifest, this, aCount, &bytesRead);
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsManifestCheck::OnStopRequest(nsIRequest *aRequest,
-                               nsISupports *aContext,
-                               nsresult aStatus)
-{
-    nsCAutoString manifestHash;
-    if (NS_SUCCEEDED(aStatus)) {
-        mManifestHash->Finish(PR_TRUE, manifestHash);
-    }
-
-    mUpdate->ManifestCheckCompleted(aStatus, manifestHash);
-
-    return NS_OK;
-}
-
-//-----------------------------------------------------------------------------
-// nsManifestCheck::nsIInterfaceRequestor
-//-----------------------------------------------------------------------------
+NS_INTERFACE_MAP_BEGIN(OfflineCacheUpdateChild)
+  NS_INTERFACE_MAP_ENTRY(nsIOfflineCacheUpdate)
+NS_INTERFACE_MAP_END
 
-NS_IMETHODIMP
-nsManifestCheck::GetInterface(const nsIID &aIID, void **aResult)
-{
-    if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
-        NS_ADDREF_THIS();
-        *aResult = static_cast<nsIChannelEventSink *>(this);
-        return NS_OK;
-    }
-
-    return NS_ERROR_NO_INTERFACE;
-}
-
-//-----------------------------------------------------------------------------
-// nsManifestCheck::nsIChannelEventSink
-//-----------------------------------------------------------------------------
-
-NS_IMETHODIMP
-nsManifestCheck::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
-                                        nsIChannel *aNewChannel,
-                                        PRUint32 aFlags,
-                                        nsIAsyncVerifyRedirectCallback *callback)
-{
-    // Redirects should cause the load (and therefore the update) to fail.
-    if (aFlags & nsIChannelEventSink::REDIRECT_INTERNAL) {
-        callback->OnRedirectVerifyCallback(NS_OK);
-        return NS_OK;
-    }
-    aOldChannel->Cancel(NS_ERROR_ABORT);
-    return NS_ERROR_ABORT;
-}
-
-//-----------------------------------------------------------------------------
-// nsOfflineCacheUpdateItem::nsISupports
-//-----------------------------------------------------------------------------
-
-NS_IMPL_ISUPPORTS6(nsOfflineCacheUpdateItem,
-                   nsIDOMLoadStatus,
-                   nsIRequestObserver,
-                   nsIStreamListener,
-                   nsIRunnable,
-                   nsIInterfaceRequestor,
-                   nsIChannelEventSink)
-
-//-----------------------------------------------------------------------------
-// nsOfflineCacheUpdateItem <public>
-//-----------------------------------------------------------------------------
+NS_IMPL_ADDREF(OfflineCacheUpdateChild)
+NS_IMPL_RELEASE_WITH_DESTROY(OfflineCacheUpdateChild, RefcountHitZero())
 
-nsOfflineCacheUpdateItem::nsOfflineCacheUpdateItem(nsOfflineCacheUpdate *aUpdate,
-                                                   nsIURI *aURI,
-                                                   nsIURI *aReferrerURI,
-                                                   nsIApplicationCache *aPreviousApplicationCache,
-                                                   const nsACString &aClientID,
-                                                   PRUint32 type)
-    : mURI(aURI)
-    , mReferrerURI(aReferrerURI)
-    , mPreviousApplicationCache(aPreviousApplicationCache)
-    , mClientID(aClientID)
-    , mItemType(type)
-    , mUpdate(aUpdate)
-    , mChannel(nsnull)
-    , mState(nsIDOMLoadStatus::UNINITIALIZED)
-    , mBytesRead(0)
+void
+OfflineCacheUpdateChild::RefcountHitZero()
 {
-}
-
-nsOfflineCacheUpdateItem::~nsOfflineCacheUpdateItem()
-{
-}
-
-nsresult
-nsOfflineCacheUpdateItem::OpenChannel()
-{
-#if defined(PR_LOGGING)
-    if (LOG_ENABLED()) {
-        nsCAutoString spec;
-        mURI->GetSpec(spec);
-        LOG(("%p: Opening channel for %s", this, spec.get()));
+    if (mIPCActivated) {
+        // ContentChild::DeallocPOfflineCacheUpdate will delete this
+        OfflineCacheUpdateChild::Send__delete__(this);
+    } else {
+        delete this;    // we never opened IPDL channel
     }
-#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<nsIApplicationCacheChannel> appCacheChannel =
-        do_QueryInterface(mChannel, &rv);
-
-    // Support for nsIApplicationCacheChannel is required.
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    // Use the existing application cache as the cache to check.
-    rv = appCacheChannel->SetApplicationCache(mPreviousApplicationCache);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    // configure HTTP specific stuff
-    nsCOMPtr<nsIHttpChannel> httpChannel =
-        do_QueryInterface(mChannel);
-    if (httpChannel) {
-        httpChannel->SetReferrer(mReferrerURI);
-        httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("X-Moz"),
-                                      NS_LITERAL_CSTRING("offline-resource"),
-                                      PR_FALSE);
-    }
-
-    nsCOMPtr<nsICachingChannel> cachingChannel =
-        do_QueryInterface(mChannel);
-    if (cachingChannel) {
-        rv = cachingChannel->SetCacheForOfflineUse(PR_TRUE);
-        NS_ENSURE_SUCCESS(rv, rv);
-
-        if (!mClientID.IsEmpty()) {
-            rv = cachingChannel->SetOfflineCacheClientID(mClientID);
-            NS_ENSURE_SUCCESS(rv, rv);
-        }
-    }
-
-    rv = mChannel->AsyncOpen(this, nsnull);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    mState = nsIDOMLoadStatus::REQUESTED;
-
-    return NS_OK;
-}
-
-nsresult
-nsOfflineCacheUpdateItem::Cancel()
-{
-    if (mChannel) {
-        mChannel->Cancel(NS_ERROR_ABORT);
-        mChannel = nsnull;
-    }
-
-    mState = nsIDOMLoadStatus::UNINITIALIZED;
-
-    return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
-// nsOfflineCacheUpdateItem::nsIStreamListener
-//-----------------------------------------------------------------------------
-
-NS_IMETHODIMP
-nsOfflineCacheUpdateItem::OnStartRequest(nsIRequest *aRequest,
-                                         nsISupports *aContext)
-{
-    mState = nsIDOMLoadStatus::RECEIVING;
-
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsOfflineCacheUpdateItem::OnDataAvailable(nsIRequest *aRequest,
-                                          nsISupports *aContext,
-                                          nsIInputStream *aStream,
-                                          PRUint32 aOffset,
-                                          PRUint32 aCount)
-{
-    PRUint32 bytesRead = 0;
-    aStream->ReadSegments(NS_DiscardSegment, nsnull, aCount, &bytesRead);
-    mBytesRead += bytesRead;
-    LOG(("loaded %u bytes into offline cache [offset=%u]\n",
-         bytesRead, aOffset));
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsOfflineCacheUpdateItem::OnStopRequest(nsIRequest *aRequest,
-                                        nsISupports *aContext,
-                                        nsresult aStatus)
-{
-    LOG(("done fetching offline item [status=%x]\n", aStatus));
-
-    mState = nsIDOMLoadStatus::LOADED;
-
-    if (mBytesRead == 0 && aStatus == NS_OK) {
-        // we didn't need to read (because LOAD_ONLY_IF_MODIFIED was
-        // specified), but the object should report loadedSize as if it
-        // did.
-        mChannel->GetContentLength(&mBytesRead);
-    }
-
-    // We need to notify the update that the load is complete, but we
-    // want to give the channel a chance to close the cache entries.
-    NS_DispatchToCurrentThread(this);
-
-    return NS_OK;
-}
-
-
-//-----------------------------------------------------------------------------
-// nsOfflineCacheUpdateItem::nsIRunnable
-//-----------------------------------------------------------------------------
-NS_IMETHODIMP
-nsOfflineCacheUpdateItem::Run()
-{
-    mUpdate->LoadCompleted();
-
-    return NS_OK;
-}
-
-//-----------------------------------------------------------------------------
-// nsOfflineCacheUpdateItem::nsIInterfaceRequestor
-//-----------------------------------------------------------------------------
-
-NS_IMETHODIMP
-nsOfflineCacheUpdateItem::GetInterface(const nsIID &aIID, void **aResult)
-{
-    if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
-        NS_ADDREF_THIS();
-        *aResult = static_cast<nsIChannelEventSink *>(this);
-        return NS_OK;
-    }
-
-    return NS_ERROR_NO_INTERFACE;
-}
-
-//-----------------------------------------------------------------------------
-// nsOfflineCacheUpdateItem::nsIChannelEventSink
+// OfflineCacheUpdateChild <public>
 //-----------------------------------------------------------------------------
 
-NS_IMETHODIMP
-nsOfflineCacheUpdateItem::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
-                                                 nsIChannel *aNewChannel,
-                                                 PRUint32 aFlags,
-                                                 nsIAsyncVerifyRedirectCallback *cb)
-{
-    if (!(aFlags & nsIChannelEventSink::REDIRECT_INTERNAL)) {
-        // Don't allow redirect in case of non-internal redirect and cancel
-        // the channel to clean the cache entry.
-        aOldChannel->Cancel(NS_ERROR_ABORT);
-        return NS_ERROR_ABORT;
-    }
-
-    nsCOMPtr<nsIURI> newURI;
-    nsresult rv = aNewChannel->GetURI(getter_AddRefs(newURI));
-    if (NS_FAILED(rv))
-        return rv;
-
-    nsCOMPtr<nsICachingChannel> oldCachingChannel =
-        do_QueryInterface(aOldChannel);
-    nsCOMPtr<nsICachingChannel> newCachingChannel =
-        do_QueryInterface(aNewChannel);
-    if (newCachingChannel) {
-        rv = newCachingChannel->SetCacheForOfflineUse(PR_TRUE);
-        NS_ENSURE_SUCCESS(rv, rv);
-        if (!mClientID.IsEmpty()) {
-            rv = newCachingChannel->SetOfflineCacheClientID(mClientID);
-            NS_ENSURE_SUCCESS(rv, rv);
-        }
-    }
-
-    nsCAutoString oldScheme;
-    mURI->GetScheme(oldScheme);
-
-    PRBool match;
-    if (NS_FAILED(newURI->SchemeIs(oldScheme.get(), &match)) || !match) {
-        LOG(("rejected: redirected to a different scheme\n"));
-        return NS_ERROR_ABORT;
-    }
-
-    // HTTP request headers are not automatically forwarded to the new channel.
-    nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aNewChannel);
-    NS_ENSURE_STATE(httpChannel);
-
-    httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("X-Moz"),
-                                  NS_LITERAL_CSTRING("offline-resource"),
-                                  PR_FALSE);
-
-    mChannel = aNewChannel;
-
-    cb->OnRedirectVerifyCallback(NS_OK);
-    return NS_OK;
-}
-
-//-----------------------------------------------------------------------------
-// nsOfflineCacheUpdateItem::nsIDOMLoadStatus
-//-----------------------------------------------------------------------------
-
-NS_IMETHODIMP
-nsOfflineCacheUpdateItem::GetSource(nsIDOMNode **aSource)
-{
-    *aSource = nsnull;
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsOfflineCacheUpdateItem::GetUri(nsAString &aURI)
-{
-    nsCAutoString spec;
-    nsresult rv = mURI->GetSpec(spec);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    CopyUTF8toUTF16(spec, aURI);
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsOfflineCacheUpdateItem::GetTotalSize(PRInt32 *aTotalSize)
-{
-    if (mChannel) {
-        return mChannel->GetContentLength(aTotalSize);
-    }
-
-    *aTotalSize = -1;
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsOfflineCacheUpdateItem::GetLoadedSize(PRInt32 *aLoadedSize)
-{
-    *aLoadedSize = mBytesRead;
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsOfflineCacheUpdateItem::GetReadyState(PRUint16 *aReadyState)
-{
-    *aReadyState = mState;
-    return NS_OK;
-}
-
-nsresult
-nsOfflineCacheUpdateItem::GetRequestSucceeded(PRBool * succeeded)
-{
-    *succeeded = PR_FALSE;
-
-    if (!mChannel)
-        return NS_OK;
-
-    nsresult rv;
-    nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel, &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    PRBool reqSucceeded;
-    rv = httpChannel->GetRequestSucceeded(&reqSucceeded);
-    if (NS_ERROR_NOT_AVAILABLE == rv)
-        return NS_OK;
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    if (!reqSucceeded) {
-        LOG(("Request failed"));
-        return NS_OK;
-    }
-
-    nsresult channelStatus;
-    rv = httpChannel->GetStatus(&channelStatus);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    if (NS_FAILED(channelStatus)) {
-        LOG(("Channel status=0x%08x", channelStatus));
-        return NS_OK;
-    }
-
-    *succeeded = PR_TRUE;
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsOfflineCacheUpdateItem::GetStatus(PRUint16 *aStatus)
-{
-    if (!mChannel) {
-        *aStatus = 0;
-        return NS_OK;
-    }
-
-    nsresult rv;
-    nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel, &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    PRUint32 httpStatus;
-    rv = httpChannel->GetResponseStatus(&httpStatus);
-    if (rv == NS_ERROR_NOT_AVAILABLE) {
-        *aStatus = 0;
-        return NS_OK;
-    }
-
-    NS_ENSURE_SUCCESS(rv, rv);
-    *aStatus = PRUint16(httpStatus);
-    return NS_OK;
-}
-
-//-----------------------------------------------------------------------------
-// nsOfflineManifestItem
-//-----------------------------------------------------------------------------
-
-//-----------------------------------------------------------------------------
-// nsOfflineManifestItem <public>
-//-----------------------------------------------------------------------------
-
-nsOfflineManifestItem::nsOfflineManifestItem(nsOfflineCacheUpdate *aUpdate,
-                                             nsIURI *aURI,
-                                             nsIURI *aReferrerURI,
-                                             nsIApplicationCache *aPreviousApplicationCache,
-                                             const nsACString &aClientID)
-    : nsOfflineCacheUpdateItem(aUpdate, aURI, aReferrerURI,
-                               aPreviousApplicationCache, aClientID,
-                               nsIApplicationCache::ITEM_MANIFEST)
-    , mParserState(PARSE_INIT)
-    , mNeedsUpdate(PR_TRUE)
-    , mManifestHashInitialized(PR_FALSE)
-{
-    ReadStrictFileOriginPolicyPref();
-}
-
-nsOfflineManifestItem::~nsOfflineManifestItem()
+OfflineCacheUpdateChild::OfflineCacheUpdateChild(nsIDOMWindow* aWindow)
+    : mState(STATE_UNINITIALIZED)
+    , mIsUpgrade(PR_FALSE)
+    , mIPCActivated(PR_FALSE)
+    , mWindow(aWindow)
 {
 }
 
-//-----------------------------------------------------------------------------
-// nsOfflineManifestItem <private>
-//-----------------------------------------------------------------------------
-
-/* static */
-NS_METHOD
-nsOfflineManifestItem::ReadManifest(nsIInputStream *aInputStream,
-                                    void *aClosure,
-                                    const char *aFromSegment,
-                                    PRUint32 aOffset,
-                                    PRUint32 aCount,
-                                    PRUint32 *aBytesConsumed)
+OfflineCacheUpdateChild::~OfflineCacheUpdateChild()
 {
-    nsOfflineManifestItem *manifest =
-        static_cast<nsOfflineManifestItem*>(aClosure);
-
-    nsresult rv;
-
-    *aBytesConsumed = aCount;
-
-    if (manifest->mParserState == PARSE_ERROR) {
-        // parse already failed, ignore this
-        return NS_OK;
-    }
-
-    if (!manifest->mManifestHashInitialized) {
-        // Avoid re-creation of crypto hash when it fails from some reason the first time
-        manifest->mManifestHashInitialized = PR_TRUE;
-
-        manifest->mManifestHash = do_CreateInstance("@mozilla.org/security/hash;1", &rv);
-        if (NS_SUCCEEDED(rv)) {
-            rv = manifest->mManifestHash->Init(nsICryptoHash::MD5);
-            if (NS_FAILED(rv)) {
-                manifest->mManifestHash = nsnull;
-                LOG(("Could not initialize manifest hash for byte-to-byte check, rv=%08x", rv));
-            }
-        }
-    }
-
-    if (manifest->mManifestHash) {
-        rv = manifest->mManifestHash->Update(reinterpret_cast<const PRUint8 *>(aFromSegment), aCount);
-        if (NS_FAILED(rv)) {
-            manifest->mManifestHash = nsnull;
-            LOG(("Could not update manifest hash, rv=%08x", rv));
-        }
-    }
-
-    manifest->mReadBuf.Append(aFromSegment, aCount);
-
-    nsCString::const_iterator begin, iter, end;
-    manifest->mReadBuf.BeginReading(begin);
-    manifest->mReadBuf.EndReading(end);
-
-    for (iter = begin; iter != end; iter++) {
-        if (*iter == '\r' || *iter == '\n') {
-            nsresult rv = manifest->HandleManifestLine(begin, iter);
-            
-            if (NS_FAILED(rv)) {
-                LOG(("HandleManifestLine failed with 0x%08x", rv));
-                return NS_ERROR_ABORT;
-            }
-
-            begin = iter;
-            begin++;
-        }
-    }
-
-    // any leftovers are saved for next time
-    manifest->mReadBuf = Substring(begin, end);
-
-    return NS_OK;
-}
-
-nsresult
-nsOfflineManifestItem::AddNamespace(PRUint32 namespaceType,
-                                    const nsCString &namespaceSpec,
-                                    const nsCString &data)
-
-{
-    nsresult rv;
-    if (!mNamespaces) {
-        mNamespaces = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
-        NS_ENSURE_SUCCESS(rv, rv);
-    }
-
-    nsCOMPtr<nsIApplicationCacheNamespace> ns =
-        do_CreateInstance(NS_APPLICATIONCACHENAMESPACE_CONTRACTID, &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = ns->Init(namespaceType, namespaceSpec, data);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = mNamespaces->AppendElement(ns, PR_FALSE);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    return NS_OK;
+    LOG(("OfflineCacheUpdateChild::~OfflineCacheUpdateChild [%p]", this));
 }
 
 nsresult
-nsOfflineManifestItem::HandleManifestLine(const nsCString::const_iterator &aBegin,
-                                          const nsCString::const_iterator &aEnd)
-{
-    nsCString::const_iterator begin = aBegin;
-    nsCString::const_iterator end = aEnd;
-
-    // all lines ignore trailing spaces and tabs
-    nsCString::const_iterator last = end;
-    --last;
-    while (end != begin && (*last == ' ' || *last == '\t')) {
-        --end;
-        --last;
-    }
-
-    if (mParserState == PARSE_INIT) {
-        // Allow a UTF-8 BOM
-        if (begin != end && static_cast<unsigned char>(*begin) == 0xef) {
-            if (++begin == end || static_cast<unsigned char>(*begin) != 0xbb ||
-                ++begin == end || static_cast<unsigned char>(*begin) != 0xbf) {
-                mParserState = PARSE_ERROR;
-                return NS_OK;
-            }
-            ++begin;
-        }
-
-        const nsCSubstring &magic = Substring(begin, end);
-
-        if (!magic.EqualsLiteral("CACHE MANIFEST")) {
-            mParserState = PARSE_ERROR;
-            return NS_OK;
-        }
-
-        mParserState = PARSE_CACHE_ENTRIES;
-        return NS_OK;
-    }
-
-    // lines other than the first ignore leading spaces and tabs
-    while (begin != end && (*begin == ' ' || *begin == '\t'))
-        begin++;
-
-    // ignore blank lines and comments
-    if (begin == end || *begin == '#')
-        return NS_OK;
-
-    const nsCSubstring &line = Substring(begin, end);
-
-    if (line.EqualsLiteral("CACHE:")) {
-        mParserState = PARSE_CACHE_ENTRIES;
-        return NS_OK;
-    }
-
-    if (line.EqualsLiteral("FALLBACK:")) {
-        mParserState = PARSE_FALLBACK_ENTRIES;
-        return NS_OK;
-    }
-
-    if (line.EqualsLiteral("NETWORK:")) {
-        mParserState = PARSE_BYPASS_ENTRIES;
-        return NS_OK;
-    }
-
-    nsresult rv;
-
-    switch(mParserState) {
-    case PARSE_INIT:
-    case PARSE_ERROR: {
-        // this should have been dealt with earlier
-        return NS_ERROR_FAILURE;
-    }
-
-    case PARSE_CACHE_ENTRIES: {
-        nsCOMPtr<nsIURI> uri;
-        rv = NS_NewURI(getter_AddRefs(uri), line, nsnull, mURI);
-        if (NS_FAILED(rv))
-            break;
-        if (NS_FAILED(DropReferenceFromURL(uri)))
-            break;
-
-        nsCAutoString scheme;
-        uri->GetScheme(scheme);
-
-        // Manifest URIs must have the same scheme as the manifest.
-        PRBool match;
-        if (NS_FAILED(mURI->SchemeIs(scheme.get(), &match)) || !match)
-            break;
-
-        mExplicitURIs.AppendObject(uri);
-        break;
-    }
-
-    case PARSE_FALLBACK_ENTRIES: {
-        PRInt32 separator = line.FindChar(' ');
-        if (separator == kNotFound) {
-            separator = line.FindChar('\t');
-            if (separator == kNotFound)
-                break;
-        }
-
-        nsCString namespaceSpec(Substring(line, 0, separator));
-        nsCString fallbackSpec(Substring(line, separator + 1));
-        namespaceSpec.CompressWhitespace();
-        fallbackSpec.CompressWhitespace();
-
-        nsCOMPtr<nsIURI> namespaceURI;
-        rv = NS_NewURI(getter_AddRefs(namespaceURI), namespaceSpec, nsnull, mURI);
-        if (NS_FAILED(rv))
-            break;
-        if (NS_FAILED(DropReferenceFromURL(namespaceURI)))
-            break;
-        rv = namespaceURI->GetAsciiSpec(namespaceSpec);
-        if (NS_FAILED(rv))
-            break;
-
-
-        nsCOMPtr<nsIURI> fallbackURI;
-        rv = NS_NewURI(getter_AddRefs(fallbackURI), fallbackSpec, nsnull, mURI);
-        if (NS_FAILED(rv))
-            break;
-        if (NS_FAILED(DropReferenceFromURL(fallbackURI)))
-            break;
-        rv = fallbackURI->GetAsciiSpec(fallbackSpec);
-        if (NS_FAILED(rv))
-            break;
-
-        // Manifest and namespace must be same origin
-        if (!NS_SecurityCompareURIs(mURI, namespaceURI,
-                                    mStrictFileOriginPolicy))
-            break;
-
-        // Fallback and namespace must be same origin
-        if (!NS_SecurityCompareURIs(namespaceURI, fallbackURI,
-                                    mStrictFileOriginPolicy))
-            break;
-
-        mFallbackURIs.AppendObject(fallbackURI);
-
-        AddNamespace(nsIApplicationCacheNamespace::NAMESPACE_FALLBACK,
-                     namespaceSpec, fallbackSpec);
-        break;
-    }
-
-    case PARSE_BYPASS_ENTRIES: {
-        nsCOMPtr<nsIURI> bypassURI;
-        rv = NS_NewURI(getter_AddRefs(bypassURI), line, nsnull, mURI);
-        if (NS_FAILED(rv))
-            break;
-
-        nsCAutoString scheme;
-        bypassURI->GetScheme(scheme);
-        PRBool equals;
-        if (NS_FAILED(mURI->SchemeIs(scheme.get(), &equals)) || !equals)
-            break;
-        if (NS_FAILED(DropReferenceFromURL(bypassURI)))
-            break;
-        nsCString spec;
-        if (NS_FAILED(bypassURI->GetAsciiSpec(spec)))
-            break;
-
-        AddNamespace(nsIApplicationCacheNamespace::NAMESPACE_BYPASS,
-                     spec, EmptyCString());
-        break;
-    }
-    }
-
-    return NS_OK;
-}
-
-nsresult 
-nsOfflineManifestItem::GetOldManifestContentHash(nsIRequest *aRequest)
-{
-    nsresult rv;
-
-    nsCOMPtr<nsICachingChannel> cachingChannel = do_QueryInterface(aRequest, &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    // load the main cache token that is actually the old offline cache token and 
-    // read previous manifest content hash value
-    nsCOMPtr<nsISupports> cacheToken;
-    cachingChannel->GetCacheToken(getter_AddRefs(cacheToken));
-    if (cacheToken) {
-        nsCOMPtr<nsICacheEntryDescriptor> cacheDescriptor(do_QueryInterface(cacheToken, &rv));
-        NS_ENSURE_SUCCESS(rv, rv);
-    
-        rv = cacheDescriptor->GetMetaDataElement("offline-manifest-hash", getter_Copies(mOldManifestHashValue));
-        if (NS_FAILED(rv))
-            mOldManifestHashValue.Truncate();
-    }
-
-    return NS_OK;
-}
-
-nsresult 
-nsOfflineManifestItem::CheckNewManifestContentHash(nsIRequest *aRequest)
-{
-    nsresult rv;
-
-    if (!mManifestHash) {
-        // Nothing to compare against...
-        return NS_OK;
-    }
-
-    nsCString newManifestHashValue;
-    rv = mManifestHash->Finish(PR_TRUE, mManifestHashValue);
-    mManifestHash = nsnull;
-
-    if (NS_FAILED(rv)) {
-        LOG(("Could not finish manifest hash, rv=%08x", rv));
-        // This is not critical error
-        return NS_OK;
-    }
-
-    if (!ParseSucceeded()) {
-        // Parsing failed, the hash is not valid
-        return NS_OK;
-    }
-
-    if (mOldManifestHashValue == mManifestHashValue) {
-        LOG(("Update not needed, downloaded manifest content is byte-for-byte identical"));
-        mNeedsUpdate = PR_FALSE;
-    }
-
-    // Store the manifest content hash value to the new
-    // offline cache token
-    nsCOMPtr<nsICachingChannel> cachingChannel = do_QueryInterface(aRequest, &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    nsCOMPtr<nsISupports> cacheToken;
-    cachingChannel->GetOfflineCacheToken(getter_AddRefs(cacheToken));
-    if (cacheToken) {
-        nsCOMPtr<nsICacheEntryDescriptor> cacheDescriptor(do_QueryInterface(cacheToken, &rv));
-        NS_ENSURE_SUCCESS(rv, rv);
-    
-        rv = cacheDescriptor->SetMetaDataElement("offline-manifest-hash", mManifestHashValue.get());
-        NS_ENSURE_SUCCESS(rv, rv);
-    }
-
-    return NS_OK;
-}
-
-void
-nsOfflineManifestItem::ReadStrictFileOriginPolicyPref()
-{
-    nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
-    mStrictFileOriginPolicy =
-        (!prefs ||
-         NS_FAILED(prefs->GetBoolPref("security.fileuri.strict_origin_policy",
-                                      &mStrictFileOriginPolicy)));
-}
-
-NS_IMETHODIMP
-nsOfflineManifestItem::OnStartRequest(nsIRequest *aRequest,
-                                      nsISupports *aContext)
-{
-    nsresult rv;
-
-    nsCOMPtr<nsIHttpChannel> channel = do_QueryInterface(aRequest, &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    PRBool succeeded;
-    rv = channel->GetRequestSucceeded(&succeeded);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    if (!succeeded) {
-        LOG(("HTTP request failed"));
-        mParserState = PARSE_ERROR;
-        return NS_ERROR_ABORT;
-    }
-
-    nsCAutoString contentType;
-    rv = channel->GetContentType(contentType);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    if (!contentType.EqualsLiteral("text/cache-manifest")) {
-        LOG(("Rejected cache manifest with Content-Type %s (expecting text/cache-manifest)",
-             contentType.get()));
-        mParserState = PARSE_ERROR;
-        return NS_ERROR_ABORT;
-    }
-
-    rv = GetOldManifestContentHash(aRequest);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    return nsOfflineCacheUpdateItem::OnStartRequest(aRequest, aContext);
-}
-
-NS_IMETHODIMP
-nsOfflineManifestItem::OnDataAvailable(nsIRequest *aRequest,
-                                       nsISupports *aContext,
-                                       nsIInputStream *aStream,
-                                       PRUint32 aOffset,
-                                       PRUint32 aCount)
-{
-    PRUint32 bytesRead = 0;
-    aStream->ReadSegments(ReadManifest, this, aCount, &bytesRead);
-    mBytesRead += bytesRead;
-
-    if (mParserState == PARSE_ERROR) {
-        LOG(("OnDataAvailable is canceling the request due a parse error\n"));
-        return NS_ERROR_ABORT;
-    }
-
-    LOG(("loaded %u bytes into offline cache [offset=%u]\n",
-         bytesRead, aOffset));
-
-    // All the parent method does is read and discard, don't bother
-    // chaining up.
-
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsOfflineManifestItem::OnStopRequest(nsIRequest *aRequest,
-                                     nsISupports *aContext,
-                                     nsresult aStatus)
-{
-    // handle any leftover manifest data
-    nsCString::const_iterator begin, end;
-    mReadBuf.BeginReading(begin);
-    mReadBuf.EndReading(end);
-    nsresult rv = HandleManifestLine(begin, end);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    if (mBytesRead == 0) {
-        // we didn't need to read (because LOAD_ONLY_IF_MODIFIED was
-        // specified.)
-        mNeedsUpdate = PR_FALSE;
-    } else {
-        rv = CheckNewManifestContentHash(aRequest);
-        NS_ENSURE_SUCCESS(rv, rv);
-    }
-
-    return nsOfflineCacheUpdateItem::OnStopRequest(aRequest, aContext, aStatus);
-}
-
-//-----------------------------------------------------------------------------
-// nsOfflineCacheUpdate::nsISupports
-//-----------------------------------------------------------------------------
-
-NS_IMPL_ISUPPORTS1(nsOfflineCacheUpdate,
-                   nsIOfflineCacheUpdate)
-
-//-----------------------------------------------------------------------------
-// nsOfflineCacheUpdate <public>
-//-----------------------------------------------------------------------------
-
-nsOfflineCacheUpdate::nsOfflineCacheUpdate()
-    : mState(STATE_UNINITIALIZED)
-    , mOwner(nsnull)
-    , mAddedItems(PR_FALSE)
-    , mPartialUpdate(PR_FALSE)
-    , mSucceeded(PR_TRUE)
-    , mObsolete(PR_FALSE)
-    , mCurrentItem(-1)
-    , mRescheduleCount(0)
-{
-}
-
-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(nsIURI *aManifestURI,
-                           nsIURI *aDocumentURI)
-{
-    nsresult rv;
-
-    // Make sure the service has been initialized
-    nsOfflineCacheUpdateService* service =
-        nsOfflineCacheUpdateService::EnsureService();
-    if (!service)
-        return NS_ERROR_FAILURE;
-
-    LOG(("nsOfflineCacheUpdate::Init [%p]", this));
-
-    mPartialUpdate = PR_FALSE;
-
-    // Only http and https applications are supported.
-    PRBool match;
-    rv = aManifestURI->SchemeIs("http", &match);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    if (!match) {
-        rv = aManifestURI->SchemeIs("https", &match);
-        NS_ENSURE_SUCCESS(rv, rv);
-        if (!match)
-            return NS_ERROR_ABORT;
-    }
-
-    mManifestURI = aManifestURI;
-
-    rv = mManifestURI->GetAsciiHost(mUpdateDomain);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    nsCAutoString manifestSpec;
-
-    rv = GetCacheKey(mManifestURI, manifestSpec);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    mDocumentURI = aDocumentURI;
-
-    nsCOMPtr<nsIApplicationCacheService> cacheService =
-        do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = cacheService->GetActiveCache(manifestSpec,
-                                      getter_AddRefs(mPreviousApplicationCache));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    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;
-}
-
-nsresult
-nsOfflineCacheUpdate::InitPartial(nsIURI *aManifestURI,
-                                  const nsACString& clientID,
-                                  nsIURI *aDocumentURI)
-{
-    nsresult rv;
-
-    // Make sure the service has been initialized
-    nsOfflineCacheUpdateService* service =
-        nsOfflineCacheUpdateService::EnsureService();
-    if (!service)
-        return NS_ERROR_FAILURE;
-
-    LOG(("nsOfflineCacheUpdate::InitPartial [%p]", this));
-
-    mPartialUpdate = PR_TRUE;
-    mClientID = clientID;
-    mDocumentURI = aDocumentURI;
-
-    mManifestURI = aManifestURI;
-    rv = mManifestURI->GetAsciiHost(mUpdateDomain);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    nsCOMPtr<nsIApplicationCacheService> cacheService =
-        do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = cacheService->GetApplicationCache(mClientID,
-                                           getter_AddRefs(mApplicationCache));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    if (!mApplicationCache) {
-        nsCAutoString manifestSpec;
-        rv = GetCacheKey(mManifestURI, manifestSpec);
-        NS_ENSURE_SUCCESS(rv, rv);
-
-        rv = cacheService->CreateApplicationCache
-            (manifestSpec, getter_AddRefs(mApplicationCache));
-        NS_ENSURE_SUCCESS(rv, rv);
-    }
-
-    nsCAutoString groupID;
-    rv = mApplicationCache->GetGroupID(groupID);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = NS_NewURI(getter_AddRefs(mManifestURI), groupID);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    mState = STATE_INITIALIZED;
-    return NS_OK;
-}
-
-nsresult
-nsOfflineCacheUpdate::HandleManifest(PRBool *aDoUpdate)
-{
-    // Be pessimistic
-    *aDoUpdate = PR_FALSE;
-
-    PRBool succeeded;
-    nsresult rv = mManifestItem->GetRequestSucceeded(&succeeded);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    if (!succeeded || !mManifestItem->ParseSucceeded()) {
-        return NS_ERROR_FAILURE;
-    }
-
-    if (!mManifestItem->NeedsUpdate()) {
-        return NS_OK;
-    }
-
-    // Add items requested by the manifest.
-    const nsCOMArray<nsIURI> &manifestURIs = mManifestItem->GetExplicitURIs();
-    for (PRInt32 i = 0; i < manifestURIs.Count(); i++) {
-        rv = AddURI(manifestURIs[i], nsIApplicationCache::ITEM_EXPLICIT);
-        NS_ENSURE_SUCCESS(rv, rv);
-    }
-
-    const nsCOMArray<nsIURI> &fallbackURIs = mManifestItem->GetFallbackURIs();
-    for (PRInt32 i = 0; i < fallbackURIs.Count(); i++) {
-        rv = AddURI(fallbackURIs[i], nsIApplicationCache::ITEM_FALLBACK);
-        NS_ENSURE_SUCCESS(rv, rv);
-    }
-
-    // The document that requested the manifest is implicitly included
-    // as part of that manifest update.
-    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 = AddExistingItems(nsIApplicationCache::ITEM_DYNAMIC);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    // Add opportunistically cached items conforming current opportunistic
-    // namespace list
-    rv = AddExistingItems(nsIApplicationCache::ITEM_OPPORTUNISTIC,
-                          &mManifestItem->GetOpportunisticNamespaces());
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    *aDoUpdate = PR_TRUE;
-
-    return NS_OK;
-}
-
-void
-nsOfflineCacheUpdate::LoadCompleted()
-{
-    nsresult rv;
-
-    // Keep the object alive through a Finish() call.
-    nsCOMPtr<nsIOfflineCacheUpdate> kungFuDeathGrip(this);
-
-    LOG(("nsOfflineCacheUpdate::LoadCompleted [%p]", this));
-
-    if (mState == STATE_CANCELLED) {
-        Finish();
-        return;
-    }
-
-    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;
-            if (mPreviousApplicationCache) {
-                NotifyObsolete();
-            } else {
-                NotifyError();
-            }
-            Finish();
-            return;
-        }
-
-        PRBool doUpdate;
-        if (NS_FAILED(HandleManifest(&doUpdate))) {
-            mSucceeded = PR_FALSE;
-            NotifyError();
-            Finish();
-            return;
-        }
-
-        if (!doUpdate) {
-            mSucceeded = PR_FALSE;
-
-            for (PRInt32 i = 0; i < mDocuments.Count(); i++) {
-                AssociateDocument(mDocuments[i], mPreviousApplicationCache);
-            }
-
-            ScheduleImplicit();
-
-            // If we didn't need an implicit update, we can
-            // send noupdate and end the update now.
-            if (!mImplicitUpdate) {
-                NotifyNoUpdate();
-                Finish();
-            }
-            return;
-        }
-
-        rv = mApplicationCache->MarkEntry(mManifestItem->mCacheKey,
-                                          mManifestItem->mItemType);
-        if (NS_FAILED(rv)) {
-            mSucceeded = PR_FALSE;
-            NotifyError();
-            Finish();
-            return;
-        }
-
-        mState = STATE_DOWNLOADING;
-        NotifyDownloading();
-
-        // Start fetching resources.
-        ProcessNextURI();
-
-        return;
-    }
-
-    // Normal load finished.
-
-    nsRefPtr<nsOfflineCacheUpdateItem> item = mItems[mCurrentItem];
-    mCurrentItem++;
-
-    PRBool succeeded;
-    rv = item->GetRequestSucceeded(&succeeded);
-
-    // Check for failures.  3XX, 4XX and 5XX errors on items explicitly
-    // listed in the manifest will cause the update to fail.
-    if (NS_FAILED(rv) || !succeeded) {
-        if (item->mItemType &
-            (nsIApplicationCache::ITEM_EXPLICIT |
-             nsIApplicationCache::ITEM_FALLBACK)) {
-            mSucceeded = PR_FALSE;
-        }
-    } else {
-        rv = mApplicationCache->MarkEntry(item->mCacheKey, item->mItemType);
-        if (NS_FAILED(rv)) {
-            mSucceeded = PR_FALSE;
-        }
-    }
-
-    if (!mSucceeded) {
-        NotifyError();
-        Finish();
-        return;
-    }
-
-    rv = NotifyCompleted(item);
-    if (NS_FAILED(rv)) return;
-
-    ProcessNextURI();
-}
-
-void
-nsOfflineCacheUpdate::ManifestCheckCompleted(nsresult aStatus,
-                                             const nsCString &aManifestHash)
-{
-    // Keep the object alive through a Finish() call.
-    nsCOMPtr<nsIOfflineCacheUpdate> kungFuDeathGrip(this);
-
-    if (NS_SUCCEEDED(aStatus)) {
-        nsCAutoString firstManifestHash;
-        mManifestItem->GetManifestHash(firstManifestHash);
-        if (aManifestHash != firstManifestHash) {
-            aStatus = NS_ERROR_FAILURE;
-        }
-    }
-
-    if (NS_FAILED(aStatus)) {
-        mSucceeded = PR_FALSE;
-        NotifyError();
-    }
-
-    Finish();
-
-    if (NS_FAILED(aStatus) && mRescheduleCount < kRescheduleLimit) {
-        // Reschedule this update.
-        nsRefPtr<nsOfflineCacheUpdate> newUpdate =
-            new nsOfflineCacheUpdate();
-        newUpdate->Init(mManifestURI, mDocumentURI);
-
-        for (PRInt32 i = 0; i < mDocuments.Count(); i++) {
-            newUpdate->AddDocument(mDocuments[i]);
-        }
-
-        newUpdate->mRescheduleCount = mRescheduleCount + 1;
-        newUpdate->Schedule();
-    }
-}
-
-nsresult
-nsOfflineCacheUpdate::Begin()
-{
-    LOG(("nsOfflineCacheUpdate::Begin [%p]", this));
-
-    // Keep the object alive through a ProcessNextURI()/Finish() call.
-    nsCOMPtr<nsIOfflineCacheUpdate> kungFuDeathGrip(this);
-
-    mCurrentItem = 0;
-
-    if (mPartialUpdate) {
-        mState = STATE_DOWNLOADING;
-        NotifyDownloading();
-        ProcessNextURI();
-        return NS_OK;
-    }
-
-    // Start checking the manifest.
-    nsCOMPtr<nsIURI> uri;
-
-    mManifestItem = new nsOfflineManifestItem(this, mManifestURI,
-                                              mDocumentURI,
-                                              mPreviousApplicationCache,
-                                              mClientID);
-    if (!mManifestItem) {
-        return NS_ERROR_OUT_OF_MEMORY;
-    }
-
-    mState = STATE_CHECKING;
-    NotifyChecking();
-
-    nsresult rv = mManifestItem->OpenChannel();
-    if (NS_FAILED(rv)) {
-        LoadCompleted();
-    }
-
-    return NS_OK;
-}
-
-nsresult
-nsOfflineCacheUpdate::Cancel()
-{
-    LOG(("nsOfflineCacheUpdate::Cancel [%p]", this));
-
-    mState = STATE_CANCELLED;
-    mSucceeded = PR_FALSE;
-
-    if (mCurrentItem >= 0 &&
-        mCurrentItem < static_cast<PRInt32>(mItems.Length())) {
-        // Load might be running
-        mItems[mCurrentItem]->Cancel();
-    }
-
-    return NS_OK;
-}
-
-//-----------------------------------------------------------------------------
-// nsOfflineCacheUpdate <private>
-//-----------------------------------------------------------------------------
-
-nsresult
-nsOfflineCacheUpdate::AddExistingItems(PRUint32 aType,
-                                       nsTArray<nsCString>* namespaceFilter)
-{
-    if (!mPreviousApplicationCache) {
-        return NS_OK;
-    }
-
-    if (namespaceFilter && namespaceFilter->Length() == 0) {
-        // Don't bother to walk entries when there are no namespaces
-        // defined.
-        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);
-
-    for (PRUint32 i = 0; i < count; i++) {
-        if (namespaceFilter) {
-            PRBool found = PR_FALSE;
-            for (PRUint32 j = 0; j < namespaceFilter->Length() && !found; j++) {
-                found = StringBeginsWith(nsDependentCString(keys[i]),
-                                         namespaceFilter->ElementAt(j));
-            }
-
-            if (!found)
-                continue;
-        }
-
-        nsCOMPtr<nsIURI> uri;
-        if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(uri), keys[i]))) {
-            rv = AddURI(uri, aType);
-            NS_ENSURE_SUCCESS(rv, rv);
-        }
-    }
-
-    return NS_OK;
-}
-
-nsresult
-nsOfflineCacheUpdate::ProcessNextURI()
-{
-    // Keep the object alive through a Finish() call.
-    nsCOMPtr<nsIOfflineCacheUpdate> kungFuDeathGrip(this);
-
-    LOG(("nsOfflineCacheUpdate::ProcessNextURI [%p, current=%d, numItems=%d]",
-         this, mCurrentItem, mItems.Length()));
-
-    NS_ASSERTION(mState == STATE_DOWNLOADING,
-                 "ProcessNextURI should only be called from the DOWNLOADING state");
-
-    if (mCurrentItem >= static_cast<PRInt32>(mItems.Length())) {
-        if (mPartialUpdate) {
-            return Finish();
-        } else {
-            // Verify that the manifest wasn't changed during the
-            // update, to prevent capturing a cache while the server
-            // is being updated.  The check will call
-            // ManifestCheckCompleted() when it's done.
-            nsRefPtr<nsManifestCheck> manifestCheck =
-                new nsManifestCheck(this, mManifestURI, mDocumentURI);
-            if (NS_FAILED(manifestCheck->Begin())) {
-                mSucceeded = PR_FALSE;
-                NotifyError();
-                return Finish();
-            }
-
-            return NS_OK;
-        }
-    }
-
-#if defined(PR_LOGGING)
-    if (LOG_ENABLED()) {
-        nsCAutoString spec;
-        mItems[mCurrentItem]->mURI->GetSpec(spec);
-        LOG(("%p: Opening channel for %s", this, spec.get()));
-    }
-#endif
-
-    NotifyStarted(mItems[mCurrentItem]);
-
-    nsresult rv = mItems[mCurrentItem]->OpenChannel();
-    if (NS_FAILED(rv)) {
-        LoadCompleted();
-        return rv;
-    }
-
-    return NS_OK;
-}
-
-nsresult
-nsOfflineCacheUpdate::GatherObservers(nsCOMArray<nsIOfflineCacheUpdateObserver> &aObservers)
+OfflineCacheUpdateChild::GatherObservers(nsCOMArray<nsIOfflineCacheUpdateObserver> &aObservers)
 {
     for (PRInt32 i = 0; i < mWeakObservers.Count(); i++) {
         nsCOMPtr<nsIOfflineCacheUpdateObserver> observer =
             do_QueryReferent(mWeakObservers[i]);
         if (observer)
             aObservers.AppendObject(observer);
         else
             mWeakObservers.RemoveObjectAt(i--);
@@ -1654,137 +135,24 @@ nsOfflineCacheUpdate::GatherObservers(ns
 
     for (PRInt32 i = 0; i < mObservers.Count(); i++) {
         aObservers.AppendObject(mObservers[i]);
     }
 
     return NS_OK;
 }
 
-nsresult
-nsOfflineCacheUpdate::NotifyError()
-{
-    LOG(("nsOfflineCacheUpdate::NotifyError [%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]->Error(this);
-    }
-
-    return NS_OK;
-}
-
-nsresult
-nsOfflineCacheUpdate::NotifyChecking()
+void
+OfflineCacheUpdateChild::SetDocument(nsIDOMDocument *aDocument)
 {
-    LOG(("nsOfflineCacheUpdate::NotifyChecking [%p]", this));
-
-    nsCOMArray<nsIOfflineCacheUpdateObserver> observers;
-    nsresult rv = GatherObservers(observers);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    for (PRInt32 i = 0; i < observers.Count(); i++) {
-        observers[i]->Checking(this);
-    }
-
-    return NS_OK;
-}
-
-nsresult
-nsOfflineCacheUpdate::NotifyNoUpdate()
-{
-    LOG(("nsOfflineCacheUpdate::NotifyNoUpdate [%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]->NoUpdate(this);
-    }
-
-    return NS_OK;
-}
-
-nsresult
-nsOfflineCacheUpdate::NotifyObsolete()
-{
-    LOG(("nsOfflineCacheUpdate::NotifyObsolete [%p]", this));
-
-    mState = STATE_FINISHED;
+    // The design is one document for one cache update on the content process.
+    NS_ASSERTION(!mDocument, "Setting more then a single document on a child offline cache update");
 
-    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);
-
-    for (PRInt32 i = 0; i < observers.Count(); i++) {
-        observers[i]->Downloading(this);
-    }
-
-    return NS_OK;
-}
+    LOG(("Document %p added to update child %p", aDocument, this));
 
-nsresult
-nsOfflineCacheUpdate::NotifyStarted(nsOfflineCacheUpdateItem *aItem)
-{
-    LOG(("nsOfflineCacheUpdate::NotifyStarted [%p, %p]", this, aItem));
-
-    nsCOMArray<nsIOfflineCacheUpdateObserver> observers;
-    nsresult rv = GatherObservers(observers);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    for (PRInt32 i = 0; i < observers.Count(); i++) {
-        observers[i]->ItemStarted(this, aItem);
-    }
-
-    return NS_OK;
-}
-
-nsresult
-nsOfflineCacheUpdate::NotifyCompleted(nsOfflineCacheUpdateItem *aItem)
-{
-    LOG(("nsOfflineCacheUpdate::NotifyCompleted [%p, %p]", this, aItem));
-
-    nsCOMArray<nsIOfflineCacheUpdateObserver> observers;
-    nsresult rv = GatherObservers(observers);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    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;
 
@@ -1794,109 +162,21 @@ nsOfflineCacheUpdate::AddDocument(nsIDOM
     if (!appCacheChannel)
         return;
 
     PRBool loadedFromAppCache;
     appCacheChannel->GetLoadedFromApplicationCache(&loadedFromAppCache);
     if (loadedFromAppCache)
         return;
 
-    mDocuments.AppendObject(aDocument);
-}
-
-void
-nsOfflineCacheUpdate::SetOwner(nsOfflineCacheUpdateOwner *aOwner)
-{
-    NS_ASSERTION(!mOwner, "Tried to set cache update owner twice.");
-    mOwner = aOwner;
-}
-
-nsresult
-nsOfflineCacheUpdate::UpdateFinished(nsOfflineCacheUpdate *aUpdate)
-{
-    // Keep the object alive through a Finish() call.
-    nsCOMPtr<nsIOfflineCacheUpdate> kungFuDeathGrip(this);
-
-    mImplicitUpdate = nsnull;
-
-    NotifyNoUpdate();
-    Finish();
-
-    return NS_OK;
+    mDocument = 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;
-
-        nsCOMPtr<nsIDOMElement> root = do_QueryInterface(doc->GetRootElement());
-        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;
-
-    update->SetOwner(this);
-    rv = update->Begin();
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    mImplicitUpdate = update;
-
-    return NS_OK;
-}
-
-nsresult
-nsOfflineCacheUpdate::AssociateDocument(nsIDOMDocument *aDocument,
+OfflineCacheUpdateChild::AssociateDocument(nsIDOMDocument *aDocument,
                                         nsIApplicationCache *aApplicationCache)
 {
     // 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);
     if (!container)
@@ -1920,88 +200,84 @@ nsOfflineCacheUpdate::AssociateDocument(
 
         rv = container->SetApplicationCache(aApplicationCache);
         NS_ENSURE_SUCCESS(rv, rv);
     }
 
     return NS_OK;
 }
 
-nsresult
-nsOfflineCacheUpdate::Finish()
-{
-    LOG(("nsOfflineCacheUpdate::Finish [%p]", this));
-
-    mState = STATE_FINISHED;
-
-    if (!mPartialUpdate) {
-        if (mSucceeded) {
-            nsIArray *namespaces = mManifestItem->GetNamespaces();
-            nsresult rv = mApplicationCache->AddNamespaces(namespaces);
-            if (NS_FAILED(rv)) {
-                NotifyError();
-                mSucceeded = PR_FALSE;
-            }
-
-            rv = mApplicationCache->Activate();
-            if (NS_FAILED(rv)) {
-                NotifyError();
-                mSucceeded = PR_FALSE;
-            }
-
-            for (PRInt32 i = 0; i < mDocuments.Count(); i++) {
-                AssociateDocument(mDocuments[i], mApplicationCache);
-            }
-        }
-
-        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();
-        }
-    }
-
-    nsresult rv = NS_OK;
-
-    if (mOwner) {
-        rv = mOwner->UpdateFinished(this);
-        mOwner = nsnull;
-    }
-
-    return rv;
-}
-
 //-----------------------------------------------------------------------------
-// nsOfflineCacheUpdate::nsIOfflineCacheUpdate
+// OfflineCacheUpdateChild::nsIOfflineCacheUpdate
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
-nsOfflineCacheUpdate::GetUpdateDomain(nsACString &aUpdateDomain)
+OfflineCacheUpdateChild::Init(nsIURI *aManifestURI,
+                           nsIURI *aDocumentURI,
+                           nsIDOMDocument *aDocument)
+{
+    nsresult rv;
+
+    // Make sure the service has been initialized
+    nsOfflineCacheUpdateService* service =
+        nsOfflineCacheUpdateService::EnsureService();
+    if (!service)
+        return NS_ERROR_FAILURE;
+
+    LOG(("OfflineCacheUpdateChild::Init [%p]", this));
+
+    // Only http and https applications are supported.
+    PRBool match;
+    rv = aManifestURI->SchemeIs("http", &match);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (!match) {
+        rv = aManifestURI->SchemeIs("https", &match);
+        NS_ENSURE_SUCCESS(rv, rv);
+        if (!match)
+            return NS_ERROR_ABORT;
+    }
+
+    mManifestURI = aManifestURI;
+
+    rv = mManifestURI->GetAsciiHost(mUpdateDomain);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    mDocumentURI = aDocumentURI;
+
+    mState = STATE_INITIALIZED;
+
+    if (aDocument)
+        SetDocument(aDocument);
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+OfflineCacheUpdateChild::InitPartial(nsIURI *aManifestURI,
+                                  const nsACString& clientID,
+                                  nsIURI *aDocumentURI)
+{
+    NS_NOTREACHED("Not expected to do partial offline cache updates"
+                  " on the child process");
+    // For now leaving this method, we may discover we need it.
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+OfflineCacheUpdateChild::GetUpdateDomain(nsACString &aUpdateDomain)
 {
     NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
 
     aUpdateDomain = mUpdateDomain;
     return NS_OK;
 }
 
 NS_IMETHODIMP
-nsOfflineCacheUpdate::GetStatus(PRUint16 *aStatus)
+OfflineCacheUpdateChild::GetStatus(PRUint16 *aStatus)
 {
     switch (mState) {
     case STATE_CHECKING :
         *aStatus = nsIDOMOfflineResourceList::CHECKING;
         return NS_OK;
     case STATE_DOWNLOADING :
         *aStatus = nsIDOMOfflineResourceList::DOWNLOADING;
         return NS_OK;
@@ -2009,161 +285,79 @@ nsOfflineCacheUpdate::GetStatus(PRUint16
         *aStatus = nsIDOMOfflineResourceList::IDLE;
         return NS_OK;
     }
 
     return NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
-nsOfflineCacheUpdate::GetPartial(PRBool *aPartial)
+OfflineCacheUpdateChild::GetPartial(PRBool *aPartial)
 {
-    *aPartial = mPartialUpdate;
+    *aPartial = PR_FALSE;
     return NS_OK;
 }
 
 NS_IMETHODIMP
-nsOfflineCacheUpdate::GetManifestURI(nsIURI **aManifestURI)
+OfflineCacheUpdateChild::GetManifestURI(nsIURI **aManifestURI)
 {
     NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
 
     NS_IF_ADDREF(*aManifestURI = mManifestURI);
     return NS_OK;
 }
 
 NS_IMETHODIMP
-nsOfflineCacheUpdate::GetSucceeded(PRBool *aSucceeded)
+OfflineCacheUpdateChild::GetSucceeded(PRBool *aSucceeded)
 {
     NS_ENSURE_TRUE(mState == STATE_FINISHED, NS_ERROR_NOT_AVAILABLE);
 
     *aSucceeded = mSucceeded;
 
     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, PRUint32 aType)
+OfflineCacheUpdateChild::GetIsUpgrade(PRBool *aIsUpgrade)
 {
     NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
 
-    if (mState >= STATE_DOWNLOADING)
-        return NS_ERROR_NOT_AVAILABLE;
-
-    // Resource URIs must have the same scheme as the manifest.
-    nsCAutoString scheme;
-    aURI->GetScheme(scheme);
-
-    PRBool match;
-    if (NS_FAILED(mManifestURI->SchemeIs(scheme.get(), &match)) || !match)
-        return NS_ERROR_FAILURE;
-
-    // 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,
-                                     mPreviousApplicationCache, mClientID,
-                                     aType);
-    if (!item) return NS_ERROR_OUT_OF_MEMORY;
-
-    mItems.AppendElement(item);
-    mAddedItems = PR_TRUE;
+    *aIsUpgrade = mIsUpgrade;
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
-nsOfflineCacheUpdate::AddDynamicURI(nsIURI *aURI)
+OfflineCacheUpdateChild::AddDynamicURI(nsIURI *aURI)
 {
-    // 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);
+    return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
-nsOfflineCacheUpdate::GetCount(PRUint32 *aNumItems)
-{
-    LOG(("nsOfflineCacheUpdate::GetNumItems [%p, num=%d]",
-         this, mItems.Length()));
-
-    NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
-
-    *aNumItems = mItems.Length();
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsOfflineCacheUpdate::Item(PRUint32 aIndex, nsIDOMLoadStatus **aItem)
-{
-    LOG(("nsOfflineCacheUpdate::GetItems [%p, index=%d]", this, aIndex));
-
-    NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
-
-    if (aIndex < mItems.Length())
-        NS_IF_ADDREF(*aItem = mItems.ElementAt(aIndex));
-    else
-        *aItem = nsnull;
-
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsOfflineCacheUpdate::AddObserver(nsIOfflineCacheUpdateObserver *aObserver,
+OfflineCacheUpdateChild::AddObserver(nsIOfflineCacheUpdateObserver *aObserver,
                                   PRBool aHoldWeak)
 {
-    LOG(("nsOfflineCacheUpdate::AddObserver [%p]", this));
+    LOG(("OfflineCacheUpdateChild::AddObserver [%p]", this));
 
     NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
 
     if (aHoldWeak) {
         nsCOMPtr<nsIWeakReference> weakRef = do_GetWeakReference(aObserver);
         mWeakObservers.AppendObject(weakRef);
     } else {
         mObservers.AppendObject(aObserver);
     }
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
-nsOfflineCacheUpdate::RemoveObserver(nsIOfflineCacheUpdateObserver *aObserver)
+OfflineCacheUpdateChild::RemoveObserver(nsIOfflineCacheUpdateObserver *aObserver)
 {
-    LOG(("nsOfflineCacheUpdate::RemoveObserver [%p]", this));
+    LOG(("OfflineCacheUpdateChild::RemoveObserver [%p]", this));
 
     NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
 
     for (PRInt32 i = 0; i < mWeakObservers.Count(); i++) {
         nsCOMPtr<nsIOfflineCacheUpdateObserver> observer =
             do_QueryReferent(mWeakObservers[i]);
         if (observer == aObserver) {
             mWeakObservers.RemoveObjectAt(i);
@@ -2176,522 +370,164 @@ nsOfflineCacheUpdate::RemoveObserver(nsI
             mObservers.RemoveObjectAt(i);
             return NS_OK;
         }
     }
 
     return NS_OK;
 }
 
+NS_IMETHODIMP
+OfflineCacheUpdateChild::Schedule()
+{
+    LOG(("OfflineCacheUpdateChild::Schedule [%p]", this));
 
-NS_IMETHODIMP
-nsOfflineCacheUpdate::Schedule()
-{
-    LOG(("nsOfflineCacheUpdate::Schedule [%p]", this));
+#ifdef MOZ_IPC
+    NS_ASSERTION(mWindow, "Window must be provided to the offline cache update child");
+#endif
 
-    nsOfflineCacheUpdateService* service =
-        nsOfflineCacheUpdateService::EnsureService();
+    nsCOMPtr<nsPIDOMWindow> piWindow = 
+        do_QueryInterface(mWindow);
+    mWindow = nsnull;
 
-    if (!service) {
-        return NS_ERROR_FAILURE;
+    nsIDocShell *docshell = piWindow->GetDocShell();
+
+    nsCOMPtr<nsIDocShellTreeItem> item = do_QueryInterface(docshell);
+    if (!item) {
+      NS_WARNING("doc shell tree item is null");
+      return NS_ERROR_FAILURE;
     }
 
-    return service->Schedule(this);
-}
-
-//-----------------------------------------------------------------------------
-// nsOfflineCachePendingUpdate
-//-----------------------------------------------------------------------------
-
-class nsOfflineCachePendingUpdate : public nsIWebProgressListener
-                                  , public nsSupportsWeakReference
-{
-public:
-    NS_DECL_ISUPPORTS
-    NS_DECL_NSIWEBPROGRESSLISTENER
-
-    nsOfflineCachePendingUpdate(nsOfflineCacheUpdateService *aService,
-                                nsIURI *aManifestURI,
-                                nsIURI *aDocumentURI,
-                                nsIDOMDocument *aDocument)
-        : mService(aService)
-        , mManifestURI(aManifestURI)
-        , mDocumentURI(aDocumentURI)
-        {
-            mDocument = do_GetWeakReference(aDocument);
-        }
-
-private:
-    nsRefPtr<nsOfflineCacheUpdateService> mService;
-    nsCOMPtr<nsIURI> mManifestURI;
-    nsCOMPtr<nsIURI> mDocumentURI;
-    nsCOMPtr<nsIWeakReference> mDocument;
-};
-
-NS_IMPL_ISUPPORTS2(nsOfflineCachePendingUpdate,
-                   nsIWebProgressListener,
-                   nsISupportsWeakReference)
-
-//-----------------------------------------------------------------------------
-// nsOfflineCacheUpdateService::nsIWebProgressListener
-//-----------------------------------------------------------------------------
-
-NS_IMETHODIMP
-nsOfflineCachePendingUpdate::OnProgressChange(nsIWebProgress *aProgress,
-                                              nsIRequest *aRequest,
-                                              PRInt32 curSelfProgress,
-                                              PRInt32 maxSelfProgress,
-                                              PRInt32 curTotalProgress,
-                                              PRInt32 maxTotalProgress)
-{
-    NS_NOTREACHED("notification excluded in AddProgressListener(...)");
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsOfflineCachePendingUpdate::OnStateChange(nsIWebProgress* aWebProgress,
-                                           nsIRequest *aRequest,
-                                           PRUint32 progressStateFlags,
-                                           nsresult aStatus)
-{
-    nsCOMPtr<nsIDOMDocument> updateDoc = do_QueryReferent(mDocument);
-    if (!updateDoc) {
-        // The document that scheduled this update has gone away,
-        // we don't need to listen anymore.
-        aWebProgress->RemoveProgressListener(this);
-        NS_RELEASE_THIS();
-        return NS_OK;
-    }
-
-    if (!(progressStateFlags & STATE_STOP)) {
-        return NS_OK;
-    }
-
-    nsCOMPtr<nsIDOMWindow> window;
-    aWebProgress->GetDOMWindow(getter_AddRefs(window));
-    if (!window) return NS_OK;
-
-    nsCOMPtr<nsIDOMDocument> progressDoc;
-    window->GetDocument(getter_AddRefs(progressDoc));
-    if (!progressDoc) return NS_OK;
-
-    if (!SameCOMIdentity(progressDoc, updateDoc)) {
-        return NS_OK;
-    }
-
-    LOG(("nsOfflineCachePendingUpdate::OnStateChange [%p, doc=%p]",
-         this, progressDoc.get()));
-
-    // Only schedule the update if the document loaded successfully
-    if (NS_SUCCEEDED(aStatus)) {
-        nsCOMPtr<nsIOfflineCacheUpdate> update;
-        mService->Schedule(mManifestURI, mDocumentURI,
-                           updateDoc, getter_AddRefs(update));
+    nsCOMPtr<nsIDocShellTreeOwner> owner;
+    item->GetTreeOwner(getter_AddRefs(owner));
+    
+    nsCOMPtr<nsITabChild> tabchild = do_GetInterface(owner);
+    if (!tabchild) {
+      NS_WARNING("tab is null");
+      return NS_ERROR_FAILURE;
     }
 
-    aWebProgress->RemoveProgressListener(this);
-    NS_RELEASE_THIS();
-
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsOfflineCachePendingUpdate::OnLocationChange(nsIWebProgress* aWebProgress,
-                                              nsIRequest* aRequest,
-                                              nsIURI *location)
-{
-    NS_NOTREACHED("notification excluded in AddProgressListener(...)");
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsOfflineCachePendingUpdate::OnStatusChange(nsIWebProgress* aWebProgress,
-                                            nsIRequest* aRequest,
-                                            nsresult aStatus,
-                                            const PRUnichar* aMessage)
-{
-    NS_NOTREACHED("notification excluded in AddProgressListener(...)");
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsOfflineCachePendingUpdate::OnSecurityChange(nsIWebProgress *aWebProgress,
-                                              nsIRequest *aRequest,
-                                              PRUint32 state)
-{
-    NS_NOTREACHED("notification excluded in AddProgressListener(...)");
-    return NS_OK;
-}
-
-//-----------------------------------------------------------------------------
-// nsOfflineCacheUpdateService::nsISupports
-//-----------------------------------------------------------------------------
-
-NS_IMPL_ISUPPORTS3(nsOfflineCacheUpdateService,
-                   nsIOfflineCacheUpdateService,
-                   nsIObserver,
-                   nsISupportsWeakReference)
-
-//-----------------------------------------------------------------------------
-// nsOfflineCacheUpdateService <public>
-//-----------------------------------------------------------------------------
-
-nsOfflineCacheUpdateService::nsOfflineCacheUpdateService()
-    : mDisabled(PR_FALSE)
-    , mUpdateRunning(PR_FALSE)
-{
-}
-
-nsOfflineCacheUpdateService::~nsOfflineCacheUpdateService()
-{
-    gOfflineCacheUpdateService = nsnull;
-}
-
-nsresult
-nsOfflineCacheUpdateService::Init()
-{
-#if defined(PR_LOGGING)
-    if (!gOfflineCacheUpdateLog)
-        gOfflineCacheUpdateLog = PR_NewLogModule("nsOfflineCacheUpdate");
-#endif
-
-    // Observe xpcom-shutdown event
+    // because owner implements nsITabChild, we can assume that it is
+    // the one and only TabChild.
+    mozilla::dom::TabChild* child = static_cast<mozilla::dom::TabChild*>(tabchild.get());
+    
     nsCOMPtr<nsIObserverService> observerService =
       mozilla::services::GetObserverService();
-    if (!observerService)
-      return NS_ERROR_FAILURE;
-
-    nsresult rv = observerService->AddObserver(this,
-                                               NS_XPCOM_SHUTDOWN_OBSERVER_ID,
-                                               PR_TRUE);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    gOfflineCacheUpdateService = this;
-
-    return NS_OK;
-}
-
-/* static */
-nsOfflineCacheUpdateService *
-nsOfflineCacheUpdateService::GetInstance()
-{
-    if (!gOfflineCacheUpdateService) {
-        gOfflineCacheUpdateService = new nsOfflineCacheUpdateService();
-        if (!gOfflineCacheUpdateService)
-            return nsnull;
-        NS_ADDREF(gOfflineCacheUpdateService);
-        nsresult rv = gOfflineCacheUpdateService->Init();
-        if (NS_FAILED(rv)) {
-            NS_RELEASE(gOfflineCacheUpdateService);
-            return nsnull;
-        }
-        return gOfflineCacheUpdateService;
+    if (observerService) {
+      LOG(("Calling offline-cache-update-added"));
+      observerService->NotifyObservers(static_cast<nsIOfflineCacheUpdate*>(this),
+                                       "offline-cache-update-added",
+                                       nsnull);
+      LOG(("Done offline-cache-update-added"));
     }
 
-    NS_ADDREF(gOfflineCacheUpdateService);
-
-    return gOfflineCacheUpdateService;
-}
-
-/* static */
-nsOfflineCacheUpdateService *
-nsOfflineCacheUpdateService::EnsureService()
-{
-    if (!gOfflineCacheUpdateService) {
-        // Make the service manager hold a long-lived reference to the service
-        nsCOMPtr<nsIOfflineCacheUpdateService> service =
-            do_GetService(NS_OFFLINECACHEUPDATESERVICE_CONTRACTID);
-    }
-
-    return gOfflineCacheUpdateService;
-}
+    // mDocument is non-null if both:
+    // 1. this update was initiated by a document that referred a manifest
+    // 2. the document has not already been loaded from the application cache
+    // This tells the update to cache this document even in case the manifest
+    // has not been changed since the last fetch.
+    // See also nsOfflineCacheUpdate::ScheduleImplicit.
+    bool stickDocument = mDocument != nsnull; 
 
-nsresult
-nsOfflineCacheUpdateService::Schedule(nsOfflineCacheUpdate *aUpdate)
-{
-    LOG(("nsOfflineCacheUpdateService::Schedule [%p, update=%p]",
-         this, aUpdate));
-
-    aUpdate->SetOwner(this);
+    // Need to addref ourself here, because the IPC stack doesn't hold
+    // a reference to us. Will be released in RecvFinish() that identifies 
+    // the work has been done.
+    child->SendPOfflineCacheUpdateConstructor(this,
+                                              IPC::URI(mManifestURI),
+                                              IPC::URI(mDocumentURI),
+                                              mClientID,
+                                              stickDocument);
 
-    nsCOMPtr<nsIObserverService> observerService =
-      mozilla::services::GetObserverService();
-    if (!observerService)
-      return NS_ERROR_FAILURE;
-
-    observerService->NotifyObservers(static_cast<nsIOfflineCacheUpdate*>(aUpdate),
-                                     "offline-cache-update-added",
-                                     nsnull);
-
-    mUpdates.AppendElement(aUpdate);
-
-    ProcessNextUpdate();
+    mIPCActivated = PR_TRUE;
+    this->AddRef();
 
     return NS_OK;
 }
 
-NS_IMETHODIMP
-nsOfflineCacheUpdateService::ScheduleOnDocumentStop(nsIURI *aManifestURI,
-                                                    nsIURI *aDocumentURI,
-                                                    nsIDOMDocument *aDocument)
+bool
+OfflineCacheUpdateChild::RecvAssociateDocuments(const nsCString &cacheGroupId,
+                                                  const nsCString &cacheClientId)
 {
-    LOG(("nsOfflineCacheUpdateService::ScheduleOnDocumentStop [%p, manifestURI=%p, documentURI=%p doc=%p]",
-         this, aManifestURI, aDocumentURI, aDocument));
+    LOG(("OfflineCacheUpdateChild::RecvAssociateDocuments [%p, cache=%s]", this, cacheClientId.get()));
+
+    nsresult rv;
 
-    nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDocument);
-    nsCOMPtr<nsISupports> container = doc->GetContainer();
-    nsCOMPtr<nsIWebProgress> progress = do_QueryInterface(container);
-    NS_ENSURE_TRUE(progress, NS_ERROR_INVALID_ARG);
+    nsCOMPtr<nsIApplicationCache> cache =
+        do_CreateInstance(NS_APPLICATIONCACHE_CONTRACTID, &rv);
+    if (NS_FAILED(rv))
+      return true;
+
+    cache->InitAsHandle(cacheGroupId, cacheClientId);
 
-    // Proceed with cache update
-    nsRefPtr<nsOfflineCachePendingUpdate> update =
-        new nsOfflineCachePendingUpdate(this, aManifestURI,
-                                        aDocumentURI, aDocument);
-    NS_ENSURE_TRUE(update, NS_ERROR_OUT_OF_MEMORY);
+    if (mDocument) {
+        AssociateDocument(mDocument, cache);
+    }
 
-    nsresult rv = progress->AddProgressListener
-        (update, nsIWebProgress::NOTIFY_STATE_DOCUMENT);
+    nsCOMArray<nsIOfflineCacheUpdateObserver> observers;
+    rv = GatherObservers(observers);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    // The update will release when it has scheduled itself.
-    update.forget();
+    for (PRInt32 i = 0; i < observers.Count(); i++)
+        observers[i]->ApplicationCacheAvailable(cache);
 
-    return NS_OK;
+    return true;
 }
 
-nsresult
-nsOfflineCacheUpdateService::UpdateFinished(nsOfflineCacheUpdate *aUpdate)
+bool
+OfflineCacheUpdateChild::RecvNotifyStateEvent(const PRUint32 &event)
 {
-    LOG(("nsOfflineCacheUpdateService::UpdateFinished [%p, update=%p]",
-         this, aUpdate));
+    LOG(("OfflineCacheUpdateChild::RecvNotifyStateEvent [%p]", this));
+
+    // Convert the public observer state to our internal state
+    switch (event) {
+        case nsIOfflineCacheUpdateObserver::STATE_CHECKING:
+            mState = STATE_CHECKING;
+            break;
+
+        case nsIOfflineCacheUpdateObserver::STATE_DOWNLOADING:
+            mState = STATE_DOWNLOADING;
+            break;
+
+        default:
+            break;
+    }
 
-    NS_ASSERTION(mUpdates.Length() > 0 &&
-                 mUpdates[0] == aUpdate, "Unknown update completed");
+    nsCOMArray<nsIOfflineCacheUpdateObserver> observers;
+    nsresult rv = GatherObservers(observers);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    for (PRInt32 i = 0; i < observers.Count(); i++)
+        observers[i]->UpdateStateChanged(this, event);
+
+    return true;
+}
 
-    // keep this item alive until we're done notifying observers
-    nsRefPtr<nsOfflineCacheUpdate> update = mUpdates[0];
-    mUpdates.RemoveElementAt(0);
-    mUpdateRunning = PR_FALSE;
+bool
+OfflineCacheUpdateChild::RecvFinish(const bool &succeeded,
+                                    const bool &isUpgrade)
+{
+    LOG(("OfflineCacheUpdateChild::RecvFinish [%p]", this));
+
+    nsRefPtr<OfflineCacheUpdateChild> kungFuDeathGrip(this);
+
+    mState = STATE_FINISHED;
+    mSucceeded = succeeded;
+    mIsUpgrade = isUpgrade;
 
     nsCOMPtr<nsIObserverService> observerService =
       mozilla::services::GetObserverService();
-    if (!observerService)
-      return NS_ERROR_FAILURE;
-
-    observerService->NotifyObservers(static_cast<nsIOfflineCacheUpdate*>(aUpdate),
-                                     "offline-cache-update-completed",
-                                     nsnull);
-
-    ProcessNextUpdate();
-
-    return NS_OK;
-}
-
-//-----------------------------------------------------------------------------
-// nsOfflineCacheUpdateService <private>
-//-----------------------------------------------------------------------------
-
-nsresult
-nsOfflineCacheUpdateService::ProcessNextUpdate()
-{
-    LOG(("nsOfflineCacheUpdateService::ProcessNextUpdate [%p, num=%d]",
-         this, mUpdates.Length()));
-
-    if (mDisabled)
-        return NS_ERROR_ABORT;
-
-    if (mUpdateRunning)
-        return NS_OK;
-
-    if (mUpdates.Length() > 0) {
-        mUpdateRunning = PR_TRUE;
-        return mUpdates[0]->Begin();
-    }
-
-    return NS_OK;
-}
-
-//-----------------------------------------------------------------------------
-// nsOfflineCacheUpdateService::nsIOfflineCacheUpdateService
-//-----------------------------------------------------------------------------
-
-NS_IMETHODIMP
-nsOfflineCacheUpdateService::GetNumUpdates(PRUint32 *aNumUpdates)
-{
-    LOG(("nsOfflineCacheUpdateService::GetNumUpdates [%p]", this));
-
-    *aNumUpdates = mUpdates.Length();
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsOfflineCacheUpdateService::GetUpdate(PRUint32 aIndex,
-                                       nsIOfflineCacheUpdate **aUpdate)
-{
-    LOG(("nsOfflineCacheUpdateService::GetUpdate [%p, %d]", this, aIndex));
-
-    if (aIndex < mUpdates.Length()) {
-        NS_ADDREF(*aUpdate = mUpdates[aIndex]);
-    } else {
-        *aUpdate = nsnull;
-    }
-
-    return NS_OK;
-}
-
-nsresult
-nsOfflineCacheUpdateService::Schedule(nsIURI *aManifestURI,
-                                      nsIURI *aDocumentURI,
-                                      nsIDOMDocument *aDocument,
-                                      nsIOfflineCacheUpdate **aUpdate)
-{
-    // Check for existing updates
-    nsresult rv;
-    for (PRUint32 i = 0; i < mUpdates.Length(); i++) {
-        nsRefPtr<nsOfflineCacheUpdate> update = mUpdates[i];
-
-        PRBool partial;
-        rv = update->GetPartial(&partial);
-        NS_ENSURE_SUCCESS(rv, rv);
-
-        if (partial) {
-            // Partial updates aren't considered
-            continue;
-        }
-
-        nsCOMPtr<nsIURI> manifestURI;
-        update->GetManifestURI(getter_AddRefs(manifestURI));
-        if (manifestURI) {
-            PRBool equals;
-            rv = manifestURI->Equals(aManifestURI, &equals);
-            if (equals) {
-                if (aDocument) {
-                    LOG(("Document %p added to update %p", aDocument, update.get()));
-                    update->AddDocument(aDocument);
-                }
-                NS_ADDREF(*aUpdate = update);
-                return NS_OK;
-            }
-        }
+    if (observerService) {
+        LOG(("Calling offline-cache-update-completed"));
+        observerService->NotifyObservers(static_cast<nsIOfflineCacheUpdate*>(this),
+                                         "offline-cache-update-completed",
+                                         nsnull);
+        LOG(("Done offline-cache-update-completed"));
     }
 
-    // There is no existing update, start one.
-
-    nsRefPtr<nsOfflineCacheUpdate> update = new nsOfflineCacheUpdate();
-    if (!update)
-        return NS_ERROR_OUT_OF_MEMORY;
-
-    rv = update->Init(aManifestURI, aDocumentURI);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    if (aDocument) {
-        LOG(("First document %p added to update %p", aDocument, update.get()));
-        update->AddDocument(aDocument);
-    }
-
-    rv = update->Schedule();
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    NS_ADDREF(*aUpdate = update);
-
-    return NS_OK;
-}
+    // This is by contract the last notification from the parent, release
+    // us now. This is corresponding to AddRef in Schedule().
+    this->Release();
 
-NS_IMETHODIMP
-nsOfflineCacheUpdateService::ScheduleUpdate(nsIURI *aManifestURI,
-                                            nsIURI *aDocumentURI,
-                                            nsIOfflineCacheUpdate **aUpdate)
-{
-    return Schedule(aManifestURI, aDocumentURI, nsnull, aUpdate);
-}
-
-//-----------------------------------------------------------------------------
-// nsOfflineCacheUpdateService::nsIObserver
-//-----------------------------------------------------------------------------
-
-NS_IMETHODIMP
-nsOfflineCacheUpdateService::Observe(nsISupports     *aSubject,
-                                     const char      *aTopic,
-                                     const PRUnichar *aData)
-{
-    if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
-        if (mUpdates.Length() > 0)
-            mUpdates[0]->Cancel();
-        mDisabled = PR_TRUE;
-    }
-
-    return NS_OK;
+    return true;
 }
 
-//-----------------------------------------------------------------------------
-// nsOfflineCacheUpdateService::nsIOfflineCacheUpdateService
-//-----------------------------------------------------------------------------
-
-NS_IMETHODIMP
-nsOfflineCacheUpdateService::OfflineAppAllowed(nsIPrincipal *aPrincipal,
-                                               nsIPrefBranch *aPrefBranch,
-                                               PRBool *aAllowed)
-{
-    nsCOMPtr<nsIURI> codebaseURI;
-    nsresult rv = aPrincipal->GetURI(getter_AddRefs(codebaseURI));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    return OfflineAppAllowedForURI(codebaseURI, aPrefBranch, aAllowed);
 }
-
-NS_IMETHODIMP
-nsOfflineCacheUpdateService::OfflineAppAllowedForURI(nsIURI *aURI,
-                                                     nsIPrefBranch *aPrefBranch,
-                                                     PRBool *aAllowed)
-{
-    *aAllowed = PR_FALSE;
-    if (!aURI)
-        return NS_OK;
-
-    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);
-            if (NS_FAILED(rv)) {
-                *aAllowed = PR_FALSE;
-            }
-        }
-
-        return NS_OK;
-    }
-
-    if (perm == nsIPermissionManager::DENY_ACTION) {
-        return NS_OK;
-    }
-
-    *aAllowed = PR_TRUE;
-
-    return NS_OK;
-}
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/uriloader/prefetch/OfflineCacheUpdateChild.h
@@ -0,0 +1,126 @@
+/* -*- Mode: C++; 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 mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Honza Bambas <honzab@firemni.cz>
+ *
+ * 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 ***** */
+
+#ifndef nsOfflineCacheUpdateChild_h
+#define nsOfflineCacheUpdateChild_h
+
+#include "mozilla/docshell/POfflineCacheUpdateChild.h"
+#include "nsIOfflineCacheUpdate.h"
+
+#include "nsCOMArray.h"
+#include "nsCOMPtr.h"
+#include "nsICacheService.h"
+#include "nsIDOMDocument.h"
+#include "nsIObserver.h"
+#include "nsIObserverService.h"
+#include "nsIURI.h"
+#include "nsString.h"
+#include "nsWeakReference.h"
+
+namespace mozilla {
+namespace docshell {
+
+class OfflineCacheUpdateChild : public nsIOfflineCacheUpdate
+                                , public POfflineCacheUpdateChild
+{
+public:
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSIOFFLINECACHEUPDATE
+
+    virtual bool
+    RecvNotifyStateEvent(const PRUint32& stateEvent);
+
+    virtual bool
+    RecvAssociateDocuments(
+            const nsCString& cacheGroupId,
+            const nsCString& cacheClientId);
+
+    virtual bool
+    RecvFinish(const bool& succeded,
+               const bool& isUpgrade);
+
+    OfflineCacheUpdateChild(nsIDOMWindow* aWindow);
+    ~OfflineCacheUpdateChild();
+
+    void SetDocument(nsIDOMDocument *aDocument);
+
+private:
+    nsresult AssociateDocument(nsIDOMDocument *aDocument,
+                               nsIApplicationCache *aApplicationCache);
+    nsresult GatherObservers(nsCOMArray<nsIOfflineCacheUpdateObserver> &aObservers);
+    nsresult Finish();
+
+    void RefcountHitZero();
+
+    enum {
+        STATE_UNINITIALIZED,
+        STATE_INITIALIZED,
+        STATE_CHECKING,
+        STATE_DOWNLOADING,
+        STATE_CANCELLED,
+        STATE_FINISHED
+    } mState;
+
+    PRPackedBool mIsUpgrade;
+    PRPackedBool mSucceeded;
+    PRPackedBool mIPCActivated;
+
+    nsCString mUpdateDomain;
+    nsCOMPtr<nsIURI> mManifestURI;
+    nsCOMPtr<nsIURI> mDocumentURI;
+
+    nsCString mClientID;
+
+    nsCOMPtr<nsIObserverService> mObserverService;
+
+    /* Clients watching this update for changes */
+    nsCOMArray<nsIWeakReference> mWeakObservers;
+    nsCOMArray<nsIOfflineCacheUpdateObserver> mObservers;
+
+    /* Document that requested this update */
+    nsCOMPtr<nsIDOMDocument> mDocument;
+
+    /* Keep reference to the window that owns this update to call the
+       parent offline cache update construcor */
+    nsCOMPtr<nsIDOMWindow> mWindow;
+};
+
+}
+}
+
+#endif
new file mode 100644
--- /dev/null
+++ b/uriloader/prefetch/OfflineCacheUpdateGlue.cpp
@@ -0,0 +1,234 @@
+/* -*- mode: C++; 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 mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Honza Bambas <honzab@firemni.cz>
+ *
+ * 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 "OfflineCacheUpdateGlue.h"
+#include "nsOfflineCacheUpdate.h"
+#include "mozilla/Services.h"
+
+#include "nsIApplicationCache.h"
+#include "nsIApplicationCacheChannel.h"
+#include "nsIApplicationCacheContainer.h"
+#include "nsIChannel.h"
+#include "nsIDocument.h"
+#include "prlog.h"
+
+#if defined(PR_LOGGING)
+//
+// To enable logging (see prlog.h for full details):
+//
+//    set NSPR_LOG_MODULES=nsOfflineCacheUpdate:5
+//    set NSPR_LOG_FILE=offlineupdate.log
+//
+// this enables PR_LOG_ALWAYS level information and places all output in
+// the file offlineupdate.log
+//
+extern PRLogModuleInfo *gOfflineCacheUpdateLog;
+#endif
+#define LOG(args) PR_LOG(gOfflineCacheUpdateLog, 4, args)
+#define LOG_ENABLED() PR_LOG_TEST(gOfflineCacheUpdateLog, 4)
+
+namespace mozilla {
+namespace docshell {
+
+//-----------------------------------------------------------------------------
+// OfflineCacheUpdateGlue::nsISupports
+//-----------------------------------------------------------------------------
+
+NS_IMPL_ISUPPORTS3(OfflineCacheUpdateGlue,
+                   nsIOfflineCacheUpdate,
+                   nsIOfflineCacheUpdateObserver,
+                   nsISupportsWeakReference)
+
+//-----------------------------------------------------------------------------
+// OfflineCacheUpdateGlue <public>
+//-----------------------------------------------------------------------------
+
+OfflineCacheUpdateGlue::OfflineCacheUpdateGlue()
+{
+    LOG(("OfflineCacheUpdateGlue::OfflineCacheUpdateGlue [%p]", this));
+}
+
+OfflineCacheUpdateGlue::~OfflineCacheUpdateGlue()
+{
+    LOG(("OfflineCacheUpdateGlue::~OfflineCacheUpdateGlue [%p]", this));
+}
+
+nsIOfflineCacheUpdate*
+OfflineCacheUpdateGlue::EnsureUpdate()
+{
+    if (!mUpdate) {
+        mUpdate = new nsOfflineCacheUpdate();
+        LOG(("OfflineCacheUpdateGlue [%p] is using update [%p]", this, mUpdate.get()));
+    }
+
+    return mUpdate;
+}
+
+NS_IMETHODIMP
+OfflineCacheUpdateGlue::Schedule()
+{
+    nsCOMPtr<nsIObserverService> observerService =
+        mozilla::services::GetObserverService();
+    if (observerService) {
+        LOG(("Calling offline-cache-update-added"));
+        observerService->NotifyObservers(static_cast<nsIOfflineCacheUpdate*>(this),
+                                         "offline-cache-update-added",
+                                         nsnull);
+        LOG(("Done offline-cache-update-added"));
+    }
+
+    if (!EnsureUpdate())
+        return NS_ERROR_NULL_POINTER;
+
+    // Do not use weak reference, we must survive!
+    mUpdate->AddObserver(this, PR_FALSE);
+
+    return mUpdate->Schedule();
+}
+
+NS_IMETHODIMP
+OfflineCacheUpdateGlue::Init(nsIURI *aManifestURI, 
+                             nsIURI *aDocumentURI,
+                             nsIDOMDocument *aDocument)
+{
+    if (!EnsureUpdate())
+        return NS_ERROR_NULL_POINTER;
+
+    mDocumentURI = aDocumentURI;
+
+    if (aDocument)
+        SetDocument(aDocument);
+
+    return mUpdate->Init(aManifestURI, aDocumentURI, nsnull);
+}
+
+void
+OfflineCacheUpdateGlue::SetDocument(nsIDOMDocument *aDocument)
+{
+    // The design is one document for one cache update on the content process.
+    NS_ASSERTION(!mDocument, 
+                 "Setting more then a single document on an instance of OfflineCacheUpdateGlue");
+
+    LOG(("Document %p added to update glue %p", aDocument, this));
+
+    // 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;
+
+    if (EnsureUpdate()) {
+        mUpdate->StickDocument(mDocumentURI);
+    }
+
+    mDocument = aDocument;
+}
+
+NS_IMETHODIMP
+OfflineCacheUpdateGlue::UpdateStateChanged(nsIOfflineCacheUpdate *aUpdate, PRUint32 state)
+{
+    if (state == nsIOfflineCacheUpdateObserver::STATE_FINISHED) {
+        LOG(("OfflineCacheUpdateGlue got STATE_FINISHED [%p]", this));
+
+        nsCOMPtr<nsIObserverService> observerService =
+          mozilla::services::GetObserverService();
+        if (observerService) {
+            LOG(("Calling offline-cache-update-completed"));
+            observerService->NotifyObservers(static_cast<nsIOfflineCacheUpdate*>(this),
+                                             "offline-cache-update-completed",
+                                             nsnull);
+            LOG(("Done offline-cache-update-completed"));
+        }
+
+        aUpdate->RemoveObserver(this);
+    }
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+OfflineCacheUpdateGlue::ApplicationCacheAvailable(nsIApplicationCache *aApplicationCache)
+{
+    NS_ENSURE_ARG(aApplicationCache);
+
+    // 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(mDocument);
+    if (!container)
+        return NS_OK;
+
+    nsCOMPtr<nsIApplicationCache> existingCache;
+    nsresult rv = container->GetApplicationCache(getter_AddRefs(existingCache));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (!existingCache) {
+#if defined(PR_LOGGING)
+        if (LOG_ENABLED()) {
+            nsCAutoString clientID;
+            if (aApplicationCache) {
+                aApplicationCache->GetClientID(clientID);
+            }
+            LOG(("Update %p: associating app cache %s to document %p",
+                 this, clientID.get(), mDocument));
+        }
+#endif
+
+        rv = container->SetApplicationCache(aApplicationCache);
+        NS_ENSURE_SUCCESS(rv, rv);
+    }
+
+    return NS_OK;
+}
+
+}
+}
new file mode 100644
--- /dev/null
+++ b/uriloader/prefetch/OfflineCacheUpdateGlue.h
@@ -0,0 +1,104 @@
+/* -*- Mode: C++; 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 mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Honza Bambas <honzab@firemni.cz>
+ *
+ * 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 ***** */
+
+#ifndef nsOfflineCacheUpdateGlue_h
+#define nsOfflineCacheUpdateGlue_h
+
+#include "nsIOfflineCacheUpdate.h"
+
+#include "nsCOMPtr.h"
+#include "nsAutoPtr.h"
+#include "nsString.h"
+#include "nsWeakReference.h"
+
+class nsOfflineCacheUpdate;
+
+namespace mozilla {
+namespace docshell {
+
+// Like FORWARD_SAFE except methods:
+//    Schedule
+//    Init
+#define NS_ADJUSTED_FORWARD_NSIOFFLINECACHEUPDATE(_to) \
+  NS_SCRIPTABLE NS_IMETHOD GetStatus(PRUint16 *aStatus) { return !_to ? NS_ERROR_NULL_POINTER : _to->GetStatus(aStatus); } \
+  NS_SCRIPTABLE NS_IMETHOD GetPartial(PRBool *aPartial) { return !_to ? NS_ERROR_NULL_POINTER : _to->GetPartial(aPartial); } \
+  NS_SCRIPTABLE NS_IMETHOD GetIsUpgrade(PRBool *aIsUpgrade) { return !_to ? NS_ERROR_NULL_POINTER : _to->GetIsUpgrade(aIsUpgrade); } \
+  NS_SCRIPTABLE NS_IMETHOD GetUpdateDomain(nsACString & aUpdateDomain) { return !_to ? NS_ERROR_NULL_POINTER : _to->GetUpdateDomain(aUpdateDomain); } \
+  NS_SCRIPTABLE NS_IMETHOD GetManifestURI(nsIURI **aManifestURI) { return !_to ? NS_ERROR_NULL_POINTER : _to->GetManifestURI(aManifestURI); } \
+  NS_SCRIPTABLE NS_IMETHOD GetSucceeded(PRBool *aSucceeded) { return !_to ? NS_ERROR_NULL_POINTER : _to->GetSucceeded(aSucceeded); } \
+  NS_SCRIPTABLE NS_IMETHOD InitPartial(nsIURI *aManifestURI, const nsACString & aClientID, nsIURI *aDocumentURI) { return !_to ? NS_ERROR_NULL_POINTER : _to->InitPartial(aManifestURI, aClientID, aDocumentURI); } \
+  NS_SCRIPTABLE NS_IMETHOD AddDynamicURI(nsIURI *aURI) { return !_to ? NS_ERROR_NULL_POINTER : _to->AddDynamicURI(aURI); } \
+  NS_SCRIPTABLE NS_IMETHOD AddObserver(nsIOfflineCacheUpdateObserver *aObserver, PRBool aHoldWeak) { return !_to ? NS_ERROR_NULL_POINTER : _to->AddObserver(aObserver, aHoldWeak); } \
+  NS_SCRIPTABLE NS_IMETHOD RemoveObserver(nsIOfflineCacheUpdateObserver *aObserver) { return !_to ? NS_ERROR_NULL_POINTER : _to->RemoveObserver(aObserver); } 
+
+class OfflineCacheUpdateGlue : public nsSupportsWeakReference
+                               , public nsIOfflineCacheUpdate
+                               , public nsIOfflineCacheUpdateObserver
+{
+public:
+    NS_DECL_ISUPPORTS
+
+private:
+    nsIOfflineCacheUpdate* EnsureUpdate();
+
+public:
+    NS_ADJUSTED_FORWARD_NSIOFFLINECACHEUPDATE(EnsureUpdate())
+    NS_SCRIPTABLE NS_IMETHOD Schedule(void);
+    NS_SCRIPTABLE NS_IMETHOD Init(nsIURI *aManifestURI, 
+                                  nsIURI *aDocumentURI, 
+                                  nsIDOMDocument *aDocument);
+
+    NS_DECL_NSIOFFLINECACHEUPDATEOBSERVER
+
+    OfflineCacheUpdateGlue();
+    ~OfflineCacheUpdateGlue();
+
+    void SetDocument(nsIDOMDocument *aDocument);
+
+private:
+    nsRefPtr<nsOfflineCacheUpdate> mUpdate;
+
+    /* Document that requested this update */
+    nsCOMPtr<nsIDOMDocument> mDocument;
+    nsCOMPtr<nsIURI> mDocumentURI;
+};
+
+}
+}
+
+#endif
new file mode 100644
--- /dev/null
+++ b/uriloader/prefetch/OfflineCacheUpdateParent.cpp
@@ -0,0 +1,169 @@
+/* -*- mode: C++; 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 mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Honza Bambas <honzab@firemni.cz>
+ *
+ * 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 "OfflineCacheUpdateParent.h"
+#include "nsOfflineCacheUpdate.h"
+#include "nsIApplicationCache.h"
+
+static nsOfflineCacheUpdateService *gOfflineCacheUpdateService = nsnull;
+
+#if defined(PR_LOGGING)
+//
+// To enable logging (see prlog.h for full details):
+//
+//    set NSPR_LOG_MODULES=nsOfflineCacheUpdate:5
+//    set NSPR_LOG_FILE=offlineupdate.log
+//
+// this enables PR_LOG_ALWAYS level information and places all output in
+// the file offlineupdate.log
+//
+extern PRLogModuleInfo *gOfflineCacheUpdateLog;
+#endif
+#define LOG(args) PR_LOG(gOfflineCacheUpdateLog, 4, args)
+#define LOG_ENABLED() PR_LOG_TEST(gOfflineCacheUpdateLog, 4)
+
+namespace mozilla {
+namespace docshell {
+
+//-----------------------------------------------------------------------------
+// OfflineCacheUpdateParent::nsISupports
+//-----------------------------------------------------------------------------
+
+NS_IMPL_ISUPPORTS1(OfflineCacheUpdateParent,
+                   nsIOfflineCacheUpdateObserver)
+
+//-----------------------------------------------------------------------------
+// OfflineCacheUpdateParent <public>
+//-----------------------------------------------------------------------------
+
+OfflineCacheUpdateParent::OfflineCacheUpdateParent()
+{
+    // Make sure the service has been initialized
+    nsOfflineCacheUpdateService* service =
+        nsOfflineCacheUpdateService::EnsureService();
+    if (!service)
+        return;
+
+    LOG(("OfflineCacheUpdateParent::OfflineCacheUpdateParent [%p]", this));
+}
+
+OfflineCacheUpdateParent::~OfflineCacheUpdateParent()
+{
+    LOG(("OfflineCacheUpdateParent::~OfflineCacheUpdateParent [%p]", this));
+}
+
+nsresult
+OfflineCacheUpdateParent::Schedule(const URI& aManifestURI,
+                                   const URI& aDocumentURI,
+                                   const nsCString& aClientID,
+                                   const bool& stickDocument)
+{
+    LOG(("OfflineCacheUpdateParent::RecvSchedule [%p]", this));
+
+    nsRefPtr<nsOfflineCacheUpdate> update;
+    nsCOMPtr<nsIURI> manifestURI(aManifestURI);
+    nsCOMPtr<nsIURI> documentURI(aDocumentURI);
+
+    nsOfflineCacheUpdateService* service =
+        nsOfflineCacheUpdateService::EnsureService();
+    if (!service)
+        return NS_ERROR_FAILURE;
+
+    service->FindUpdate(manifestURI, documentURI, getter_AddRefs(update));
+    if (!update) {
+        update = new nsOfflineCacheUpdate();
+
+        nsresult rv;
+        // Leave aDocument argument null. Only glues and children keep 
+        // document instances.
+        rv = update->Init(manifestURI, documentURI, nsnull);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        rv = update->Schedule();
+        NS_ENSURE_SUCCESS(rv, rv);
+    }
+
+    update->AddObserver(this, PR_FALSE);
+
+    if (stickDocument) {
+      nsCOMPtr<nsIURI> stickURI;
+      documentURI->Clone(getter_AddRefs(stickURI));
+      update->StickDocument(stickURI);
+    }
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+OfflineCacheUpdateParent::UpdateStateChanged(nsIOfflineCacheUpdate *aUpdate, PRUint32 state)
+{
+    LOG(("OfflineCacheUpdateParent::StateEvent [%p]", this));
+
+    SendNotifyStateEvent(state);
+
+    if (state == nsIOfflineCacheUpdateObserver::STATE_FINISHED) {
+        // Tell the child the particulars after the update has finished.
+        // Sending the Finish event will release the child side of the protocol
+        // and notify "offline-cache-update-completed" on the child process.
+        PRBool isUpgrade;
+        aUpdate->GetIsUpgrade(&isUpgrade);
+        PRBool succeeded;
+        aUpdate->GetSucceeded(&succeeded);
+
+        SendFinish(succeeded, isUpgrade);
+    }
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+OfflineCacheUpdateParent::ApplicationCacheAvailable(nsIApplicationCache *aApplicationCache)
+{
+    NS_ENSURE_ARG(aApplicationCache);
+
+    nsCString cacheClientId;
+    aApplicationCache->GetClientID(cacheClientId);
+    nsCString cacheGroupId;
+    aApplicationCache->GetGroupID(cacheGroupId);
+
+    SendAssociateDocuments(cacheGroupId, cacheClientId);
+    return NS_OK;
+}
+
+} // docshell
+} // mozilla
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/uriloader/prefetch/OfflineCacheUpdateParent.h
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; 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 mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Honza Bambas <honzab@firemni.cz>
+ *
+ * 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 ***** */
+
+#ifndef nsOfflineCacheUpdateParent_h
+#define nsOfflineCacheUpdateParent_h
+
+#include "mozilla/docshell/POfflineCacheUpdateParent.h"
+#include "nsIOfflineCacheUpdate.h"
+
+#include "nsString.h"
+
+namespace mozilla {
+namespace docshell {
+
+class OfflineCacheUpdateParent : public POfflineCacheUpdateParent
+                               , public nsIOfflineCacheUpdateObserver
+{
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSIOFFLINECACHEUPDATEOBSERVER
+
+    nsresult
+    Schedule(const URI& manifestURI,
+             const URI& documentURI,
+             const nsCString& clientID,
+             const bool& stickDocument);
+
+    OfflineCacheUpdateParent();
+    ~OfflineCacheUpdateParent();
+
+private:
+    void RefcountHitZero();
+};
+
+}
+}
+
+#endif
new file mode 100644
--- /dev/null
+++ b/uriloader/prefetch/POfflineCacheUpdate.ipdl
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+
+/* ***** 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 mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ *  The Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Honza Bambas <honzab@firemni.cz>
+ *
+ * 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 protocol PBrowser;
+
+include "mozilla/net/NeckoMessageUtils.h";
+
+using IPC::URI;
+
+namespace mozilla {
+namespace docshell {
+
+//-------------------------------------------------------------------
+protocol POfflineCacheUpdate
+{
+  manager PBrowser;
+
+parent:
+  __delete__();
+
+child:
+  NotifyStateEvent(PRUint32 stateEvent);
+  AssociateDocuments(nsCString cacheGroupId, nsCString cacheClientId);
+  Finish(bool succeded, bool isUpgrate);
+};
+
+}
+}
new file mode 100644
--- /dev/null
+++ b/uriloader/prefetch/ipdl.mk
@@ -0,0 +1,40 @@
+# ***** 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 Mozilla Firefox.
+#
+# The Initial Developer of the Original Code is
+# The Mozilla Foundation <http://www.mozilla.org/>.
+# Portions created by the Initial Developer are Copyright (C) 2009
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):  Jason Duell
+#
+# 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 *****
+
+IPDLSRCS =          \
+  POfflineCacheUpdate.ipdl \
+  $(NULL)
+
--- a/uriloader/prefetch/nsIOfflineCacheUpdate.idl
+++ b/uriloader/prefetch/nsIOfflineCacheUpdate.idl
@@ -34,103 +34,71 @@
  * 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 nsIURI;
+interface nsIDOMWindow;
 interface nsIDOMNode;
 interface nsIDOMDocument;
 interface nsIDOMLoadStatus;
 interface nsIOfflineCacheUpdate;
 interface nsIPrincipal;
 interface nsIPrefBranch;
+interface nsIApplicationCache;
 
-[scriptable, uuid(a28abeaf-a0b4-4440-b2fe-bc78249710ea)]
+[scriptable, uuid(47360d57-8ef4-4a5d-8865-1a27a739ad1a)]
 interface nsIOfflineCacheUpdateObserver : nsISupports {
-  /**
-   * There was an error updating the cache.
-   *
-   * @param aUpdate
-   *        The nsIOfflineCacheUpdate being processed.
-   */
-  void error(in nsIOfflineCacheUpdate aUpdate);
+  const unsigned long STATE_ERROR = 1;
+  const unsigned long STATE_CHECKING = 2;
+  const unsigned long STATE_NOUPDATE = 3;
+  const unsigned long STATE_OBSOLETE = 4;
+  const unsigned long STATE_DOWNLOADING = 5;
+  const unsigned long STATE_ITEMSTARTED = 6;
+  const unsigned long STATE_ITEMCOMPLETED = 7;
+  const unsigned long STATE_FINISHED = 10;
 
   /**
-   * The manifest is being checked for updates
-   *
-   * @param aUpdate
-   *        The nsIOfflineCacheUpdate being processed.
-   */
-  void checking(in nsIOfflineCacheUpdate aUpdate);
-
-  /**
-   * No update was necessary.
-   *
-   * @param aUpdate
-   *        The nsIOfflineCacheUpdate being processed.
-   */
-  void noUpdate(in nsIOfflineCacheUpdate aUpdate);
-
-  /**
-   * The cache group is now obsolete.
+   * aUpdate has changed its state.
    *
    * @param aUpdate
    *        The nsIOfflineCacheUpdate being processed.
+   * @param event
+   *        See enumeration above
    */
-  void obsolete(in nsIOfflineCacheUpdate aUpdate);
-
-  /**
-   * Starting to download resources
-   *
-   * @param aUpdate
-   *        The nsIOfflineCacheUpdate being processed.
-   */
-  void downloading(in nsIOfflineCacheUpdate aUpdate);
+  void updateStateChanged(in nsIOfflineCacheUpdate aUpdate, in PRUint32 state);
 
   /**
-   * An item has started downloading.
+   * Informs the observer about an application being available to associate.
    *
-   * @param aUpdate
-   *        The nsIOfflineCacheUpdate being processed.
-   * @param aItem
-   *        load status for the item that is being downloaded.
+   * @param applicationCache
+   *        The application cache instance that has been created or found by the 
+   *        update to associate with
    */
-  void itemStarted(in nsIOfflineCacheUpdate aUpdate,
-                   in nsIDOMLoadStatus aItem);
-
-  /**
-   * An item has finished loading.
-   *
-   * @param aUpdate
-   *        The nsIOfflineCacheUpdate being processed.
-   * @param aItem
-   *         load status for the item that completed.
-   */
-  void itemCompleted(in nsIOfflineCacheUpdate aUpdate,
-                     in nsIDOMLoadStatus aItem);
+  void applicationCacheAvailable(in nsIApplicationCache applicationCache);
 };
 
 /**
  * An nsIOfflineCacheUpdate is used to update an application's offline
  * resources.
  *
  * It can be used to perform partial or complete updates.
  *
  * Each update object maintains a list of nsIDOMLoadStatus items for the
  * resources it is updating.  The list of these items will be available
  * after the object is scheduled.
  *
  * One update object will be updating at a time.  The active object will
  * load its items one by one, sending itemCompleted() to any registered
  * observers.
  */
-[scriptable, uuid(877261bb-b952-4d27-847e-859bdd47c0ec)]
+[scriptable, uuid(24605d81-8cf9-4021-8575-7f39aacbf31a)]
 interface nsIOfflineCacheUpdate : nsISupports {
   /**
    * Fetch the status of the running update.  This will return a value
    * defined in nsIDOMOfflineResourceList.
    */
   readonly attribute unsigned short status;
 
   /**
@@ -164,17 +132,17 @@ interface nsIOfflineCacheUpdate : nsISup
   /**
    * Initialize the update.
    *
    * @param aManifestURI
    *        The manifest URI to be checked.
    * @param aDocumentURI
    *        The page that is requesting the update.
    */
-  void init(in nsIURI aManifestURI, in nsIURI aDocumentURI);
+  void init(in nsIURI aManifestURI, in nsIURI aDocumentURI, in nsIDOMDocument aDocument);
 
   /**
    * Initialize the update for partial processing. 
    *
    * @param aManifestURI
    *        The manifest URI of the related cache.
    * @param aClientID
    *        Client  ID of the cache to store resource to. This ClientID
@@ -196,22 +164,16 @@ interface nsIOfflineCacheUpdate : nsISup
 
   /**
    * Add the update to the offline update queue.  An offline-cache-update-added
    * event will be sent to the observer service.
    */
   void schedule();
 
   /**
-   * Access to the list of items in the update.
-   */
-  readonly attribute unsigned long count;
-  nsIDOMLoadStatus item(in unsigned long index);
-
-  /**
    * Observe loads that are added to the update.
    *
    * @param aObserver
    *        object that notifications will be sent to.
    * @param aHoldWeak
    *        TRUE if you want the update to hold a weak reference to the
    *        observer, FALSE for a strong reference.
    */
@@ -249,17 +211,18 @@ interface nsIOfflineCacheUpdateService :
     nsIOfflineCacheUpdate getUpdate(in unsigned long index);
 
     /**
      * Schedule a cache update for a given offline manifest.  If an
      * existing update is scheduled or running, that update will be returned.
      * Otherwise a new update will be scheduled.
      */
     nsIOfflineCacheUpdate scheduleUpdate(in nsIURI aManifestURI,
-                                         in nsIURI aDocumentURI);
+                                         in nsIURI aDocumentURI,
+                                         in nsIDOMWindow aWindow);
 
     /**
      * Schedule a cache update for a manifest when the document finishes
      * loading.
      */
     void scheduleOnDocumentStop(in nsIURI aManifestURI,
                                 in nsIURI aDocumentURI,
                                 in nsIDOMDocument aDocument);
--- a/uriloader/prefetch/nsOfflineCacheUpdate.cpp
+++ b/uriloader/prefetch/nsOfflineCacheUpdate.cpp
@@ -81,17 +81,17 @@ static const PRUint32 kRescheduleLimit =
 // To enable logging (see prlog.h for full details):
 //
 //    set NSPR_LOG_MODULES=nsOfflineCacheUpdate:5
 //    set NSPR_LOG_FILE=offlineupdate.log
 //
 // this enables PR_LOG_ALWAYS level information and places all output in
 // the file offlineupdate.log
 //
-static PRLogModuleInfo *gOfflineCacheUpdateLog;
+extern PRLogModuleInfo *gOfflineCacheUpdateLog;
 #endif
 #define LOG(args) PR_LOG(gOfflineCacheUpdateLog, 4, args)
 #define LOG_ENABLED() PR_LOG_TEST(gOfflineCacheUpdateLog, 4)
 
 class AutoFreeArray {
 public:
     AutoFreeArray(PRUint32 count, char **values)
         : mCount(count), mValues(values) {};
@@ -1114,17 +1114,18 @@ nsOfflineManifestItem::OnStopRequest(nsI
 
     return nsOfflineCacheUpdateItem::OnStopRequest(aRequest, aContext, aStatus);
 }
 
 //-----------------------------------------------------------------------------
 // nsOfflineCacheUpdate::nsISupports
 //-----------------------------------------------------------------------------
 
-NS_IMPL_ISUPPORTS1(nsOfflineCacheUpdate,
+NS_IMPL_ISUPPORTS2(nsOfflineCacheUpdate,
+                   nsIOfflineCacheUpdateObserver,
                    nsIOfflineCacheUpdate)
 
 //-----------------------------------------------------------------------------
 // nsOfflineCacheUpdate <public>
 //-----------------------------------------------------------------------------
 
 nsOfflineCacheUpdate::nsOfflineCacheUpdate()
     : mState(STATE_UNINITIALIZED)
@@ -1162,17 +1163,18 @@ nsOfflineCacheUpdate::GetCacheKey(nsIURI
     rv = newURI->GetAsciiSpec(aKey);
     NS_ENSURE_SUCCESS(rv, rv);
 
     return NS_OK;
 }
 
 nsresult
 nsOfflineCacheUpdate::Init(nsIURI *aManifestURI,
-                           nsIURI *aDocumentURI)
+                           nsIURI *aDocumentURI,
+                           nsIDOMDocument *aDocument)
 {
     nsresult rv;
 
     // Make sure the service has been initialized
     nsOfflineCacheUpdateService* service =
         nsOfflineCacheUpdateService::EnsureService();
     if (!service)
         return NS_ERROR_FAILURE;
@@ -1356,61 +1358,59 @@ nsOfflineCacheUpdate::LoadCompleted()
         // 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;
             if (mPreviousApplicationCache) {
-                NotifyObsolete();
+                NotifyState(nsIOfflineCacheUpdateObserver::STATE_OBSOLETE);
             } else {
-                NotifyError();
+                NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR);
             }
             Finish();
             return;
         }
 
         PRBool doUpdate;
         if (NS_FAILED(HandleManifest(&doUpdate))) {
             mSucceeded = PR_FALSE;
-            NotifyError();
+            NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR);
             Finish();
             return;
         }
 
         if (!doUpdate) {
             mSucceeded = PR_FALSE;
 
-            for (PRInt32 i = 0; i < mDocuments.Count(); i++) {
-                AssociateDocument(mDocuments[i], mPreviousApplicationCache);
-            }
+            AssociateDocuments(mPreviousApplicationCache);
 
             ScheduleImplicit();
 
             // If we didn't need an implicit update, we can
             // send noupdate and end the update now.
             if (!mImplicitUpdate) {
-                NotifyNoUpdate();
+                NotifyState(nsIOfflineCacheUpdateObserver::STATE_NOUPDATE);
                 Finish();
             }
             return;
         }
 
         rv = mApplicationCache->MarkEntry(mManifestItem->mCacheKey,
                                           mManifestItem->mItemType);
         if (NS_FAILED(rv)) {
             mSucceeded = PR_FALSE;
-            NotifyError();
+            NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR);
             Finish();
             return;
         }
 
         mState = STATE_DOWNLOADING;
-        NotifyDownloading();
+        NotifyState(nsIOfflineCacheUpdateObserver::STATE_DOWNLOADING);
 
         // Start fetching resources.
         ProcessNextURI();
 
         return;
     }
 
     // Normal load finished.
@@ -1432,94 +1432,108 @@ nsOfflineCacheUpdate::LoadCompleted()
     } else {
         rv = mApplicationCache->MarkEntry(item->mCacheKey, item->mItemType);
         if (NS_FAILED(rv)) {
             mSucceeded = PR_FALSE;
         }
     }
 
     if (!mSucceeded) {
-        NotifyError();
+        NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR);
         Finish();
         return;
     }
 
-    rv = NotifyCompleted(item);
+    rv = NotifyState(nsIOfflineCacheUpdateObserver::STATE_ITEMCOMPLETED);
     if (NS_FAILED(rv)) return;
 
     ProcessNextURI();
 }
 
 void
 nsOfflineCacheUpdate::ManifestCheckCompleted(nsresult aStatus,
                                              const nsCString &aManifestHash)
 {
     // Keep the object alive through a Finish() call.
     nsCOMPtr<nsIOfflineCacheUpdate> kungFuDeathGrip(this);
 
     if (NS_SUCCEEDED(aStatus)) {
         nsCAutoString firstManifestHash;
         mManifestItem->GetManifestHash(firstManifestHash);
         if (aManifestHash != firstManifestHash) {
+            LOG(("Manifest has changed during cache items download [%p]", this));
             aStatus = NS_ERROR_FAILURE;
         }
     }
 
     if (NS_FAILED(aStatus)) {
         mSucceeded = PR_FALSE;
-        NotifyError();
+        NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR);
     }
 
-    Finish();
-
     if (NS_FAILED(aStatus) && mRescheduleCount < kRescheduleLimit) {
-        // Reschedule this update.
+        // Do the final stuff but prevent notification of STATE_FINISHED.  
+        // That would disconnect listeners that are responsible for document
+        // association after a successful update. Forwarding notifications
+        // from a new update through this dead update to them is absolutely
+        // correct.
+        FinishNoNotify();
+
         nsRefPtr<nsOfflineCacheUpdate> newUpdate =
             new nsOfflineCacheUpdate();
-        newUpdate->Init(mManifestURI, mDocumentURI);
+        // Leave aDocument argument null. Only glues and children keep 
+        // document instances.
+        newUpdate->Init(mManifestURI, mDocumentURI, nsnull);
 
-        for (PRInt32 i = 0; i < mDocuments.Count(); i++) {
-            newUpdate->AddDocument(mDocuments[i]);
+        // In a rare case the manifest will not be modified on the next refetch
+        // transfer all master document URIs to the new update to ensure that
+        // all documents refering it will be properly cached.
+        for (PRUint32 i = 0; i < mDocumentURIs.Count(); i++) {
+            newUpdate->StickDocument(mDocumentURIs[i]);
         }
 
         newUpdate->mRescheduleCount = mRescheduleCount + 1;
+        newUpdate->AddObserver(this, PR_FALSE);
         newUpdate->Schedule();
     }
+    else {
+        Finish();
+    }
 }
 
 nsresult
 nsOfflineCacheUpdate::Begin()
 {
     LOG(("nsOfflineCacheUpdate::Begin [%p]", this));
 
     // Keep the object alive through a ProcessNextURI()/Finish() call.
     nsCOMPtr<nsIOfflineCacheUpdate> kungFuDeathGrip(this);
 
     mCurrentItem = 0;
 
     if (mPartialUpdate) {
         mState = STATE_DOWNLOADING;
-        NotifyDownloading();
+        NotifyState(nsIOfflineCacheUpdateObserver::STATE_DOWNLOADING);
         ProcessNextURI();
         return NS_OK;
     }
 
     // Start checking the manifest.
     nsCOMPtr<nsIURI> uri;
 
     mManifestItem = new nsOfflineManifestItem(this, mManifestURI,
                                               mDocumentURI,
                                               mPreviousApplicationCache,
                                               mClientID);
     if (!mManifestItem) {
         return NS_ERROR_OUT_OF_MEMORY;
     }
 
     mState = STATE_CHECKING;
-    NotifyChecking();
+    NotifyState(nsIOfflineCacheUpdateObserver::STATE_CHECKING);
 
     nsresult rv = mManifestItem->OpenChannel();
     if (NS_FAILED(rv)) {
         LoadCompleted();
     }
 
     return NS_OK;
 }
@@ -1608,33 +1622,33 @@ nsOfflineCacheUpdate::ProcessNextURI()
             // Verify that the manifest wasn't changed during the
             // update, to prevent capturing a cache while the server
             // is being updated.  The check will call
             // ManifestCheckCompleted() when it's done.
             nsRefPtr<nsManifestCheck> manifestCheck =
                 new nsManifestCheck(this, mManifestURI, mDocumentURI);
             if (NS_FAILED(manifestCheck->Begin())) {
                 mSucceeded = PR_FALSE;
-                NotifyError();
+                NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR);
                 return Finish();
             }
 
             return NS_OK;
         }
     }
 
 #if defined(PR_LOGGING)
     if (LOG_ENABLED()) {
         nsCAutoString spec;
         mItems[mCurrentItem]->mURI->GetSpec(spec);
         LOG(("%p: Opening channel for %s", this, spec.get()));
     }
 #endif
 
-    NotifyStarted(mItems[mCurrentItem]);
+    NotifyState(nsIOfflineCacheUpdateObserver::STATE_ITEMSTARTED);
 
     nsresult rv = mItems[mCurrentItem]->OpenChannel();
     if (NS_FAILED(rv)) {
         LoadCompleted();
         return rv;
     }
 
     return NS_OK;
@@ -1655,156 +1669,52 @@ nsOfflineCacheUpdate::GatherObservers(ns
     for (PRInt32 i = 0; i < mObservers.Count(); i++) {
         aObservers.AppendObject(mObservers[i]);
     }
 
     return NS_OK;
 }
 
 nsresult
-nsOfflineCacheUpdate::NotifyError()
+nsOfflineCacheUpdate::NotifyState(PRUint32 state)
 {
-    LOG(("nsOfflineCacheUpdate::NotifyError [%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]->Error(this);
-    }
-
-    return NS_OK;
-}
-
-nsresult
-nsOfflineCacheUpdate::NotifyChecking()
-{
-    LOG(("nsOfflineCacheUpdate::NotifyChecking [%p]", this));
+    LOG(("nsOfflineCacheUpdate::NotifyState [%p, %d]", this, state));
 
     nsCOMArray<nsIOfflineCacheUpdateObserver> observers;
     nsresult rv = GatherObservers(observers);
     NS_ENSURE_SUCCESS(rv, rv);
 
     for (PRInt32 i = 0; i < observers.Count(); i++) {
-        observers[i]->Checking(this);
-    }
-
-    return NS_OK;
-}
-
-nsresult
-nsOfflineCacheUpdate::NotifyNoUpdate()
-{
-    LOG(("nsOfflineCacheUpdate::NotifyNoUpdate [%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]->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);
+        observers[i]->UpdateStateChanged(this, state);
     }
 
     return NS_OK;
 }
 
 nsresult
-nsOfflineCacheUpdate::NotifyDownloading()
+nsOfflineCacheUpdate::AssociateDocuments(nsIApplicationCache* cache)
 {
-    LOG(("nsOfflineCacheUpdate::NotifyDownloading [%p]", this));
-
     nsCOMArray<nsIOfflineCacheUpdateObserver> observers;
     nsresult rv = GatherObservers(observers);
     NS_ENSURE_SUCCESS(rv, rv);
 
     for (PRInt32 i = 0; i < observers.Count(); i++) {
-        observers[i]->Downloading(this);
-    }
-
-    return NS_OK;
-}
-
-nsresult
-nsOfflineCacheUpdate::NotifyStarted(nsOfflineCacheUpdateItem *aItem)
-{
-    LOG(("nsOfflineCacheUpdate::NotifyStarted [%p, %p]", this, aItem));
-
-    nsCOMArray<nsIOfflineCacheUpdateObserver> observers;
-    nsresult rv = GatherObservers(observers);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    for (PRInt32 i = 0; i < observers.Count(); i++) {
-        observers[i]->ItemStarted(this, aItem);
-    }
-
-    return NS_OK;
-}
-
-nsresult
-nsOfflineCacheUpdate::NotifyCompleted(nsOfflineCacheUpdateItem *aItem)
-{
-    LOG(("nsOfflineCacheUpdate::NotifyCompleted [%p, %p]", this, aItem));
-
-    nsCOMArray<nsIOfflineCacheUpdateObserver> observers;
-    nsresult rv = GatherObservers(observers);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    for (PRInt32 i = 0; i < observers.Count(); i++) {
-        observers[i]->ItemCompleted(this, aItem);
+        observers[i]->ApplicationCacheAvailable(cache);
     }
 
     return NS_OK;
 }
 
 void
-nsOfflineCacheUpdate::AddDocument(nsIDOMDocument *aDocument)
+nsOfflineCacheUpdate::StickDocument(nsIURI *aDocumentURI)
 {
-    // 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;
+    if (!aDocumentURI)
+      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);
+    mDocumentURIs.AppendObject(aDocumentURI);
 }
 
 void
 nsOfflineCacheUpdate::SetOwner(nsOfflineCacheUpdateOwner *aOwner)
 {
     NS_ASSERTION(!mOwner, "Tried to set cache update owner twice.");
     mOwner = aOwner;
 }
@@ -1812,26 +1722,26 @@ nsOfflineCacheUpdate::SetOwner(nsOffline
 nsresult
 nsOfflineCacheUpdate::UpdateFinished(nsOfflineCacheUpdate *aUpdate)
 {
     // Keep the object alive through a Finish() call.
     nsCOMPtr<nsIOfflineCacheUpdate> kungFuDeathGrip(this);
 
     mImplicitUpdate = nsnull;
 
-    NotifyNoUpdate();
+    NotifyState(nsIOfflineCacheUpdateObserver::STATE_NOUPDATE);
     Finish();
 
     return NS_OK;
 }
 
 nsresult
 nsOfflineCacheUpdate::ScheduleImplicit()
 {
-    if (mDocuments.Count() == 0)
+    if (mDocumentURIs.Count() == 0)
         return NS_OK;
 
     nsresult rv;
 
     nsRefPtr<nsOfflineCacheUpdate> update = new nsOfflineCacheUpdate();
     NS_ENSURE_TRUE(update, NS_ERROR_OUT_OF_MEMORY);
 
     nsCAutoString clientID;
@@ -1841,120 +1751,54 @@ nsOfflineCacheUpdate::ScheduleImplicit()
     }
     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;
-
-        nsCOMPtr<nsIDOMElement> root = do_QueryInterface(doc->GetRootElement());
-        if (!root)
-            continue;
-
-        nsAutoString manifestSpec;
-        rv = root->GetAttribute(NS_LITERAL_STRING("manifest"), manifestSpec);
+    for (PRUint32 i = 0; i < mDocumentURIs.Count(); i++) {
+        rv = update->AddURI(mDocumentURIs[i], 
+              nsIApplicationCache::ITEM_IMPLICIT);
         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;
-
     update->SetOwner(this);
     rv = update->Begin();
     NS_ENSURE_SUCCESS(rv, rv);
 
     mImplicitUpdate = update;
 
     return NS_OK;
 }
 
 nsresult
-nsOfflineCacheUpdate::AssociateDocument(nsIDOMDocument *aDocument,
-                                        nsIApplicationCache *aApplicationCache)
-{
-    // 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);
-    if (!container)
-        return NS_OK;
-
-    nsCOMPtr<nsIApplicationCache> existingCache;
-    nsresult rv = container->GetApplicationCache(getter_AddRefs(existingCache));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    if (!existingCache) {
-#if defined(PR_LOGGING)
-        if (LOG_ENABLED()) {
-            nsCAutoString clientID;
-            if (aApplicationCache) {
-                aApplicationCache->GetClientID(clientID);
-            }
-            LOG(("Update %p: associating app cache %s to document %p",
-                 this, clientID.get(), aDocument));
-        }
-#endif
-
-        rv = container->SetApplicationCache(aApplicationCache);
-        NS_ENSURE_SUCCESS(rv, rv);
-    }
-
-    return NS_OK;
-}
-
-nsresult
-nsOfflineCacheUpdate::Finish()
+nsOfflineCacheUpdate::FinishNoNotify()
 {
     LOG(("nsOfflineCacheUpdate::Finish [%p]", this));
 
     mState = STATE_FINISHED;
 
     if (!mPartialUpdate) {
         if (mSucceeded) {
             nsIArray *namespaces = mManifestItem->GetNamespaces();
             nsresult rv = mApplicationCache->AddNamespaces(namespaces);
             if (NS_FAILED(rv)) {
-                NotifyError();
+                NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR);
                 mSucceeded = PR_FALSE;
             }
 
             rv = mApplicationCache->Activate();
             if (NS_FAILED(rv)) {
-                NotifyError();
+                NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR);
                 mSucceeded = PR_FALSE;
             }
 
-            for (PRInt32 i = 0; i < mDocuments.Count(); i++) {
-                AssociateDocument(mDocuments[i], mApplicationCache);
-            }
+            AssociateDocuments(mApplicationCache);
         }
 
         if (mObsolete) {
             nsCOMPtr<nsIApplicationCacheService> appCacheService =
                 do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID);
             if (appCacheService) {
                 nsCAutoString groupID;
                 mApplicationCache->GetGroupID(groupID);
@@ -1977,16 +1821,26 @@ nsOfflineCacheUpdate::Finish()
     if (mOwner) {
         rv = mOwner->UpdateFinished(this);
         mOwner = nsnull;
     }
 
     return rv;
 }
 
+nsresult
+nsOfflineCacheUpdate::Finish()
+{
+    nsresult rv = FinishNoNotify();
+
+    NotifyState(nsIOfflineCacheUpdateObserver::STATE_FINISHED);
+
+    return rv;
+}
+
 //-----------------------------------------------------------------------------
 // nsOfflineCacheUpdate::nsIOfflineCacheUpdate
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 nsOfflineCacheUpdate::GetUpdateDomain(nsACString &aUpdateDomain)
 {
     NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
@@ -2085,16 +1939,21 @@ nsOfflineCacheUpdate::AddURI(nsIURI *aUR
     mAddedItems = PR_TRUE;
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsOfflineCacheUpdate::AddDynamicURI(nsIURI *aURI)
 {
+#if !defined(MOZ_IPC)
+    if (GeckoProcessType_Default != XRE_GetProcessType()) 
+        return NS_ERROR_NOT_IMPLEMENTED;
+#endif
+
     // 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);
@@ -2106,64 +1965,37 @@ nsOfflineCacheUpdate::AddDynamicURI(nsIU
             return NS_OK;
         }
     }
 
     return AddURI(aURI, nsIApplicationCache::ITEM_DYNAMIC);
 }
 
 NS_IMETHODIMP
-nsOfflineCacheUpdate::GetCount(PRUint32 *aNumItems)
-{
-    LOG(("nsOfflineCacheUpdate::GetNumItems [%p, num=%d]",
-         this, mItems.Length()));
-
-    NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
-
-    *aNumItems = mItems.Length();
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsOfflineCacheUpdate::Item(PRUint32 aIndex, nsIDOMLoadStatus **aItem)
-{
-    LOG(("nsOfflineCacheUpdate::GetItems [%p, index=%d]", this, aIndex));
-
-    NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
-
-    if (aIndex < mItems.Length())
-        NS_IF_ADDREF(*aItem = mItems.ElementAt(aIndex));
-    else
-        *aItem = nsnull;
-
-    return NS_OK;
-}
-
-NS_IMETHODIMP
 nsOfflineCacheUpdate::AddObserver(nsIOfflineCacheUpdateObserver *aObserver,
                                   PRBool aHoldWeak)
 {
-    LOG(("nsOfflineCacheUpdate::AddObserver [%p]", this));
+    LOG(("nsOfflineCacheUpdate::AddObserver [%p] to update [%p]", aObserver, this));
 
     NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
 
     if (aHoldWeak) {
         nsCOMPtr<nsIWeakReference> weakRef = do_GetWeakReference(aObserver);
         mWeakObservers.AppendObject(weakRef);
     } else {
         mObservers.AppendObject(aObserver);
     }
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsOfflineCacheUpdate::RemoveObserver(nsIOfflineCacheUpdateObserver *aObserver)
 {
-    LOG(("nsOfflineCacheUpdate::RemoveObserver [%p]", this));
+    LOG(("nsOfflineCacheUpdate::RemoveObserver [%p] from update [%p]", aObserver, this));
 
     NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
 
     for (PRInt32 i = 0; i < mWeakObservers.Count(); i++) {
         nsCOMPtr<nsIOfflineCacheUpdateObserver> observer =
             do_QueryReferent(mWeakObservers[i]);
         if (observer == aObserver) {
             mWeakObservers.RemoveObjectAt(i);
@@ -2189,509 +2021,36 @@ nsOfflineCacheUpdate::Schedule()
 
     nsOfflineCacheUpdateService* service =
         nsOfflineCacheUpdateService::EnsureService();
 
     if (!service) {
         return NS_ERROR_FAILURE;
     }
 
-    return service->Schedule(this);
-}
-
-//-----------------------------------------------------------------------------
-// nsOfflineCachePendingUpdate
-//-----------------------------------------------------------------------------
-
-class nsOfflineCachePendingUpdate : public nsIWebProgressListener
-                                  , public nsSupportsWeakReference
-{
-public:
-    NS_DECL_ISUPPORTS
-    NS_DECL_NSIWEBPROGRESSLISTENER
-
-    nsOfflineCachePendingUpdate(nsOfflineCacheUpdateService *aService,
-                                nsIURI *aManifestURI,
-                                nsIURI *aDocumentURI,
-                                nsIDOMDocument *aDocument)
-        : mService(aService)
-        , mManifestURI(aManifestURI)
-        , mDocumentURI(aDocumentURI)
-        {
-            mDocument = do_GetWeakReference(aDocument);
-        }
-
-private:
-    nsRefPtr<nsOfflineCacheUpdateService> mService;
-    nsCOMPtr<nsIURI> mManifestURI;
-    nsCOMPtr<nsIURI> mDocumentURI;
-    nsCOMPtr<nsIWeakReference> mDocument;
-};
-
-NS_IMPL_ISUPPORTS2(nsOfflineCachePendingUpdate,
-                   nsIWebProgressListener,
-                   nsISupportsWeakReference)
-
-//-----------------------------------------------------------------------------
-// nsOfflineCacheUpdateService::nsIWebProgressListener
-//-----------------------------------------------------------------------------
-
-NS_IMETHODIMP
-nsOfflineCachePendingUpdate::OnProgressChange(nsIWebProgress *aProgress,
-                                              nsIRequest *aRequest,
-                                              PRInt32 curSelfProgress,
-                                              PRInt32 maxSelfProgress,
-                                              PRInt32 curTotalProgress,
-                                              PRInt32 maxTotalProgress)
-{
-    NS_NOTREACHED("notification excluded in AddProgressListener(...)");
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsOfflineCachePendingUpdate::OnStateChange(nsIWebProgress* aWebProgress,
-                                           nsIRequest *aRequest,
-                                           PRUint32 progressStateFlags,
-                                           nsresult aStatus)
-{
-    nsCOMPtr<nsIDOMDocument> updateDoc = do_QueryReferent(mDocument);
-    if (!updateDoc) {
-        // The document that scheduled this update has gone away,
-        // we don't need to listen anymore.
-        aWebProgress->RemoveProgressListener(this);
-        NS_RELEASE_THIS();
-        return NS_OK;
-    }
-
-    if (!(progressStateFlags & STATE_STOP)) {
-        return NS_OK;
-    }
-
-    nsCOMPtr<nsIDOMWindow> window;
-    aWebProgress->GetDOMWindow(getter_AddRefs(window));
-    if (!window) return NS_OK;
-
-    nsCOMPtr<nsIDOMDocument> progressDoc;
-    window->GetDocument(getter_AddRefs(progressDoc));
-    if (!progressDoc) return NS_OK;
-
-    if (!SameCOMIdentity(progressDoc, updateDoc)) {
-        return NS_OK;
-    }
-
-    LOG(("nsOfflineCachePendingUpdate::OnStateChange [%p, doc=%p]",
-         this, progressDoc.get()));
-
-    // Only schedule the update if the document loaded successfully
-    if (NS_SUCCEEDED(aStatus)) {
-        nsCOMPtr<nsIOfflineCacheUpdate> update;
-        mService->Schedule(mManifestURI, mDocumentURI,
-                           updateDoc, getter_AddRefs(update));
-    }
-
-    aWebProgress->RemoveProgressListener(this);
-    NS_RELEASE_THIS();
-
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsOfflineCachePendingUpdate::OnLocationChange(nsIWebProgress* aWebProgress,
-                                              nsIRequest* aRequest,
-                                              nsIURI *location)
-{
-    NS_NOTREACHED("notification excluded in AddProgressListener(...)");
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsOfflineCachePendingUpdate::OnStatusChange(nsIWebProgress* aWebProgress,
-                                            nsIRequest* aRequest,
-                                            nsresult aStatus,
-                                            const PRUnichar* aMessage)
-{
-    NS_NOTREACHED("notification excluded in AddProgressListener(...)");
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsOfflineCachePendingUpdate::OnSecurityChange(nsIWebProgress *aWebProgress,
-                                              nsIRequest *aRequest,
-                                              PRUint32 state)
-{
-    NS_NOTREACHED("notification excluded in AddProgressListener(...)");
-    return NS_OK;
-}
-
-//-----------------------------------------------------------------------------
-// nsOfflineCacheUpdateService::nsISupports
-//-----------------------------------------------------------------------------
-
-NS_IMPL_ISUPPORTS3(nsOfflineCacheUpdateService,
-                   nsIOfflineCacheUpdateService,
-                   nsIObserver,
-                   nsISupportsWeakReference)
-
-//-----------------------------------------------------------------------------
-// nsOfflineCacheUpdateService <public>
-//-----------------------------------------------------------------------------
-
-nsOfflineCacheUpdateService::nsOfflineCacheUpdateService()
-    : mDisabled(PR_FALSE)
-    , mUpdateRunning(PR_FALSE)
-{
-}
-
-nsOfflineCacheUpdateService::~nsOfflineCacheUpdateService()
-{
-    gOfflineCacheUpdateService = nsnull;
-}
-
-nsresult
-nsOfflineCacheUpdateService::Init()
-{
-#if defined(PR_LOGGING)
-    if (!gOfflineCacheUpdateLog)
-        gOfflineCacheUpdateLog = PR_NewLogModule("nsOfflineCacheUpdate");
-#endif
-
-    // Observe xpcom-shutdown event
-    nsCOMPtr<nsIObserverService> observerService =
-      mozilla::services::GetObserverService();
-    if (!observerService)
-      return NS_ERROR_FAILURE;
-
-    nsresult rv = observerService->AddObserver(this,
-                                               NS_XPCOM_SHUTDOWN_OBSERVER_ID,
-                                               PR_TRUE);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    gOfflineCacheUpdateService = this;
-
-    return NS_OK;
-}
-
-/* static */
-nsOfflineCacheUpdateService *
-nsOfflineCacheUpdateService::GetInstance()
-{
-    if (!gOfflineCacheUpdateService) {
-        gOfflineCacheUpdateService = new nsOfflineCacheUpdateService();
-        if (!gOfflineCacheUpdateService)
-            return nsnull;
-        NS_ADDREF(gOfflineCacheUpdateService);
-        nsresult rv = gOfflineCacheUpdateService->Init();
-        if (NS_FAILED(rv)) {
-            NS_RELEASE(gOfflineCacheUpdateService);
-            return nsnull;
-        }
-        return gOfflineCacheUpdateService;
-    }
-
-    NS_ADDREF(gOfflineCacheUpdateService);
-
-    return gOfflineCacheUpdateService;
-}
-
-/* static */
-nsOfflineCacheUpdateService *
-nsOfflineCacheUpdateService::EnsureService()
-{
-    if (!gOfflineCacheUpdateService) {
-        // Make the service manager hold a long-lived reference to the service
-        nsCOMPtr<nsIOfflineCacheUpdateService> service =
-            do_GetService(NS_OFFLINECACHEUPDATESERVICE_CONTRACTID);
-    }
-
-    return gOfflineCacheUpdateService;
-}
-
-nsresult
-nsOfflineCacheUpdateService::Schedule(nsOfflineCacheUpdate *aUpdate)
-{
-    LOG(("nsOfflineCacheUpdateService::Schedule [%p, update=%p]",
-         this, aUpdate));
-
-    aUpdate->SetOwner(this);
-
-    nsCOMPtr<nsIObserverService> observerService =
-      mozilla::services::GetObserverService();
-    if (!observerService)
-      return NS_ERROR_FAILURE;
-
-    observerService->NotifyObservers(static_cast<nsIOfflineCacheUpdate*>(aUpdate),
-                                     "offline-cache-update-added",
-                                     nsnull);
-
-    mUpdates.AppendElement(aUpdate);
-
-    ProcessNextUpdate();
-
-    return NS_OK;
+    return service->ScheduleUpdate(this);
 }
 
 NS_IMETHODIMP
-nsOfflineCacheUpdateService::ScheduleOnDocumentStop(nsIURI *aManifestURI,
-                                                    nsIURI *aDocumentURI,
-                                                    nsIDOMDocument *aDocument)
-{
-    LOG(("nsOfflineCacheUpdateService::ScheduleOnDocumentStop [%p, manifestURI=%p, documentURI=%p doc=%p]",
-         this, aManifestURI, aDocumentURI, aDocument));
-
-    nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDocument);
-    nsCOMPtr<nsISupports> container = doc->GetContainer();
-    nsCOMPtr<nsIWebProgress> progress = do_QueryInterface(container);
-    NS_ENSURE_TRUE(progress, NS_ERROR_INVALID_ARG);
-
-    // Proceed with cache update
-    nsRefPtr<nsOfflineCachePendingUpdate> update =
-        new nsOfflineCachePendingUpdate(this, aManifestURI,
-                                        aDocumentURI, aDocument);
-    NS_ENSURE_TRUE(update, NS_ERROR_OUT_OF_MEMORY);
-
-    nsresult rv = progress->AddProgressListener
-        (update, nsIWebProgress::NOTIFY_STATE_DOCUMENT);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    // The update will release when it has scheduled itself.
-    update.forget();
-
-    return NS_OK;
-}
-
-nsresult
-nsOfflineCacheUpdateService::UpdateFinished(nsOfflineCacheUpdate *aUpdate)
+nsOfflineCacheUpdate::UpdateStateChanged(nsIOfflineCacheUpdate *aUpdate,
+                                         PRUint32 aState)
 {
-    LOG(("nsOfflineCacheUpdateService::UpdateFinished [%p, update=%p]",
-         this, aUpdate));
-
-    NS_ASSERTION(mUpdates.Length() > 0 &&
-                 mUpdates[0] == aUpdate, "Unknown update completed");
-
-    // keep this item alive until we're done notifying observers
-    nsRefPtr<nsOfflineCacheUpdate> update = mUpdates[0];
-    mUpdates.RemoveElementAt(0);
-    mUpdateRunning = PR_FALSE;
-
-    nsCOMPtr<nsIObserverService> observerService =
-      mozilla::services::GetObserverService();
-    if (!observerService)
-      return NS_ERROR_FAILURE;
-
-    observerService->NotifyObservers(static_cast<nsIOfflineCacheUpdate*>(aUpdate),
-                                     "offline-cache-update-completed",
-                                     nsnull);
-
-    ProcessNextUpdate();
-
-    return NS_OK;
-}
-
-//-----------------------------------------------------------------------------
-// nsOfflineCacheUpdateService <private>
-//-----------------------------------------------------------------------------
-
-nsresult
-nsOfflineCacheUpdateService::ProcessNextUpdate()
-{
-    LOG(("nsOfflineCacheUpdateService::ProcessNextUpdate [%p, num=%d]",
-         this, mUpdates.Length()));
-
-    if (mDisabled)
-        return NS_ERROR_ABORT;
-
-    if (mUpdateRunning)
-        return NS_OK;
-
-    if (mUpdates.Length() > 0) {
-        mUpdateRunning = PR_TRUE;
-        return mUpdates[0]->Begin();
+    if (aState == nsIOfflineCacheUpdateObserver::STATE_FINISHED) {
+        // Take the mSucceeded flag from the underlying update, we will be
+        // queried for it soon. mSucceeded of this update is false (manifest 
+        // check failed) but the subsequent re-fetch update might succeed
+        PRBool succeeded;
+        aUpdate->GetSucceeded(&succeeded);
+        mSucceeded = succeeded;
     }
 
-    return NS_OK;
-}
-
-//-----------------------------------------------------------------------------
-// nsOfflineCacheUpdateService::nsIOfflineCacheUpdateService
-//-----------------------------------------------------------------------------
+    nsresult rv = NotifyState(aState);
+    if (aState == nsIOfflineCacheUpdateObserver::STATE_FINISHED)
+        aUpdate->RemoveObserver(this);
 
-NS_IMETHODIMP
-nsOfflineCacheUpdateService::GetNumUpdates(PRUint32 *aNumUpdates)
-{
-    LOG(("nsOfflineCacheUpdateService::GetNumUpdates [%p]", this));
-
-    *aNumUpdates = mUpdates.Length();
-    return NS_OK;
+    return rv;
 }
 
 NS_IMETHODIMP
-nsOfflineCacheUpdateService::GetUpdate(PRUint32 aIndex,
-                                       nsIOfflineCacheUpdate **aUpdate)
-{
-    LOG(("nsOfflineCacheUpdateService::GetUpdate [%p, %d]", this, aIndex));
-
-    if (aIndex < mUpdates.Length()) {
-        NS_ADDREF(*aUpdate = mUpdates[aIndex]);
-    } else {
-        *aUpdate = nsnull;
-    }
-
-    return NS_OK;
-}
-
-nsresult
-nsOfflineCacheUpdateService::Schedule(nsIURI *aManifestURI,
-                                      nsIURI *aDocumentURI,
-                                      nsIDOMDocument *aDocument,
-                                      nsIOfflineCacheUpdate **aUpdate)
+nsOfflineCacheUpdate::ApplicationCacheAvailable(nsIApplicationCache *applicationCache)
 {
-    // Check for existing updates
-    nsresult rv;
-    for (PRUint32 i = 0; i < mUpdates.Length(); i++) {
-        nsRefPtr<nsOfflineCacheUpdate> update = mUpdates[i];
-
-        PRBool partial;
-        rv = update->GetPartial(&partial);
-        NS_ENSURE_SUCCESS(rv, rv);
-
-        if (partial) {
-            // Partial updates aren't considered
-            continue;
-        }
-
-        nsCOMPtr<nsIURI> manifestURI;
-        update->GetManifestURI(getter_AddRefs(manifestURI));
-        if (manifestURI) {
-            PRBool equals;
-            rv = manifestURI->Equals(aManifestURI, &equals);
-            if (equals) {
-                if (aDocument) {
-                    LOG(("Document %p added to update %p", aDocument, update.get()));
-                    update->AddDocument(aDocument);
-                }
-                NS_ADDREF(*aUpdate = update);
-                return NS_OK;
-            }
-        }
-    }
-
-    // There is no existing update, start one.
-
-    nsRefPtr<nsOfflineCacheUpdate> update = new nsOfflineCacheUpdate();
-    if (!update)
-        return NS_ERROR_OUT_OF_MEMORY;
-
-    rv = update->Init(aManifestURI, aDocumentURI);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    if (aDocument) {
-        LOG(("First document %p added to update %p", aDocument, update.get()));
-        update->AddDocument(aDocument);
-    }
-
-    rv = update->Schedule();
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    NS_ADDREF(*aUpdate = update);
-
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsOfflineCacheUpdateService::ScheduleUpdate(nsIURI *aManifestURI,
-                                            nsIURI *aDocumentURI,
-                                            nsIOfflineCacheUpdate **aUpdate)
-{
-    return Schedule(aManifestURI, aDocumentURI, nsnull, aUpdate);
+    return AssociateDocuments(applicationCache);
 }
-
-//-----------------------------------------------------------------------------
-// nsOfflineCacheUpdateService::nsIObserver
-//-----------------------------------------------------------------------------
-
-NS_IMETHODIMP
-nsOfflineCacheUpdateService::Observe(nsISupports     *aSubject,
-                                     const char      *aTopic,
-                                     const PRUnichar *aData)
-{
-    if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
-        if (mUpdates.Length() > 0)
-            mUpdates[0]->Cancel();
-        mDisabled = PR_TRUE;
-    }
-
-    return NS_OK;
-}
-
-//-----------------------------------------------------------------------------
-// nsOfflineCacheUpdateService::nsIOfflineCacheUpdateService
-//-----------------------------------------------------------------------------
-
-NS_IMETHODIMP
-nsOfflineCacheUpdateService::OfflineAppAllowed(nsIPrincipal *aPrincipal,
-                                               nsIPrefBranch *aPrefBranch,
-                                               PRBool *aAllowed)
-{
-    nsCOMPtr<nsIURI> codebaseURI;
-    nsresult rv = aPrincipal->GetURI(getter_AddRefs(codebaseURI));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    return OfflineAppAllowedForURI(codebaseURI, aPrefBranch, aAllowed);
-}
-
-NS_IMETHODIMP
-nsOfflineCacheUpdateService::OfflineAppAllowedForURI(nsIURI *aURI,
-                                                     nsIPrefBranch *aPrefBranch,
-                                                     PRBool *aAllowed)
-{
-    *aAllowed = PR_FALSE;
-    if (!aURI)
-        return NS_OK;
-
-    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);
-            if (NS_FAILED(rv)) {
-                *aAllowed = PR_FALSE;
-            }
-        }
-
-        return NS_OK;
-    }
-
-    if (perm == nsIPermissionManager::DENY_ACTION) {
-        return NS_OK;
-    }
-
-    *aAllowed = PR_TRUE;
-
-    return NS_OK;
-}
--- a/uriloader/prefetch/nsOfflineCacheUpdate.h
+++ b/uriloader/prefetch/nsOfflineCacheUpdate.h
@@ -206,65 +206,61 @@ private:
 
 class nsOfflineCacheUpdateOwner
 {
 public:
     virtual nsresult UpdateFinished(nsOfflineCacheUpdate *aUpdate) = 0;
 };
 
 class nsOfflineCacheUpdate : public nsIOfflineCacheUpdate
+                           , public nsIOfflineCacheUpdateObserver
                            , public nsOfflineCacheUpdateOwner
 {
 public:
     NS_DECL_ISUPPORTS
     NS_DECL_NSIOFFLINECACHEUPDATE
+    NS_DECL_NSIOFFLINECACHEUPDATEOBSERVER
 
     nsOfflineCacheUpdate();
     ~nsOfflineCacheUpdate();
 
     static nsresult GetCacheKey(nsIURI *aURI, nsACString &aKey);
 
     nsresult Init();
 
     nsresult Begin();
     nsresult Cancel();
 
     void LoadCompleted();
     void ManifestCheckCompleted(nsresult aStatus,
                                 const nsCString &aManifestHash);
-    void AddDocument(nsIDOMDocument *aDocument);
+    void StickDocument(nsIURI *aDocumentURI);
 
     void SetOwner(nsOfflineCacheUpdateOwner *aOwner);
 
     virtual nsresult UpdateFinished(nsOfflineCacheUpdate *aUpdate);
 
 private:
     nsresult HandleManifest(PRBool *aDoUpdate);
     nsresult AddURI(nsIURI *aURI, PRUint32 aItemType);
 
     nsresult ProcessNextURI();
 
     // Adds items from the previous cache witha type matching aType.
     // If namespaceFilter is non-null, only items matching the
     // specified namespaces will be added.
     nsresult AddExistingItems(PRUint32 aType,
                               nsTArray<nsCString>* namespaceFilter = nsnull);
+    nsresult ScheduleImplicit();
+    nsresult AssociateDocuments(nsIApplicationCache* cache);
 
     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,
-                               nsIApplicationCache *aApplicationCache);
-    nsresult ScheduleImplicit();
+    nsresult NotifyState(PRUint32 state);
     nsresult Finish();
+    nsresult FinishNoNotify();
 
     enum {
         STATE_UNINITIALIZED,
         STATE_INITIALIZED,
         STATE_CHECKING,
         STATE_DOWNLOADING,
         STATE_CANCELLED,
         STATE_FINISHED
@@ -274,17 +270,16 @@ private:
 
     PRPackedBool mAddedItems;
     PRPackedBool mPartialUpdate;
     PRPackedBool mSucceeded;
     PRPackedBool mObsolete;
 
     nsCString mUpdateDomain;
     nsCOMPtr<nsIURI> mManifestURI;
-
     nsCOMPtr<nsIURI> mDocumentURI;
 
     nsCString mClientID;
     nsCOMPtr<nsIApplicationCache> mApplicationCache;
     nsCOMPtr<nsIApplicationCache> mPreviousApplicationCache;
 
     nsCOMPtr<nsIObserverService> mObserverService;
 
@@ -294,17 +289,17 @@ private:
     PRInt32 mCurrentItem;
     nsTArray<nsRefPtr<nsOfflineCacheUpdateItem> > mItems;
 
     /* Clients watching this update for changes */
     nsCOMArray<nsIWeakReference> mWeakObservers;
     nsCOMArray<nsIOfflineCacheUpdateObserver> mObservers;
 
     /* Documents that requested this update */
-    nsCOMArray<nsIDOMDocument> mDocuments;
+    nsCOMArray<nsIURI> mDocumentURIs;
 
     /* Reschedule count.  When an update is rescheduled due to
      * mismatched manifests, the reschedule count will be increased. */
     PRUint32 mRescheduleCount;
 
     nsRefPtr<nsOfflineCacheUpdate> mImplicitUpdate;
 };
 
@@ -318,20 +313,25 @@ public:
     NS_DECL_NSIOFFLINECACHEUPDATESERVICE
     NS_DECL_NSIOBSERVER
 
     nsOfflineCacheUpdateService();
     ~nsOfflineCacheUpdateService();
 
     nsresult Init();
 
-    nsresult Schedule(nsOfflineCacheUpdate *aUpdate);
+    nsresult ScheduleUpdate(nsOfflineCacheUpdate *aUpdate);
+    nsresult FindUpdate(nsIURI *aManifestURI,
+                        nsIURI *aDocumentURI,
+                        nsOfflineCacheUpdate **aUpdate);
+
     nsresult Schedule(nsIURI *aManifestURI,
                       nsIURI *aDocumentURI,
                       nsIDOMDocument *aDocument,
+                      nsIDOMWindow* aWindow,
                       nsIOfflineCacheUpdate **aUpdate);
 
     virtual nsresult UpdateFinished(nsOfflineCacheUpdate *aUpdate);
 
     /**
      * Returns the singleton nsOfflineCacheUpdateService without an addref, or
      * nsnull if the service couldn't be created.
      */
copy from uriloader/prefetch/nsOfflineCacheUpdate.cpp
copy to uriloader/prefetch/nsOfflineCacheUpdateService.cpp
--- a/uriloader/prefetch/nsOfflineCacheUpdate.cpp
+++ b/uriloader/prefetch/nsOfflineCacheUpdateService.cpp
@@ -31,29 +31,34 @@
  * 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 ***** */
 
+#ifdef MOZ_IPC
+#include "OfflineCacheUpdateChild.h"
+#include "OfflineCacheUpdateParent.h"
+#include "nsXULAppAPI.h"
+#endif
+#include "OfflineCacheUpdateGlue.h"
 #include "nsOfflineCacheUpdate.h"
 
 #include "nsCPrefetchService.h"
 #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 "mozilla/dom/Element.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"
@@ -69,29 +74,33 @@
 #include "nsStreamUtils.h"
 #include "nsThreadUtils.h"
 #include "nsProxyRelease.h"
 #include "prlog.h"
 #include "nsIAsyncVerifyRedirectCallback.h"
 
 static nsOfflineCacheUpdateService *gOfflineCacheUpdateService = nsnull;
 
-static const PRUint32 kRescheduleLimit = 3;
+#ifdef MOZ_IPC
+typedef mozilla::docshell::OfflineCacheUpdateParent OfflineCacheUpdateParent;
+typedef mozilla::docshell::OfflineCacheUpdateChild OfflineCacheUpdateChild;
+#endif
+typedef mozilla::docshell::OfflineCacheUpdateGlue OfflineCacheUpdateGlue;
 
 #if defined(PR_LOGGING)
 //
 // To enable logging (see prlog.h for full details):
 //
 //    set NSPR_LOG_MODULES=nsOfflineCacheUpdate:5
 //    set NSPR_LOG_FILE=offlineupdate.log
 //
 // this enables PR_LOG_ALWAYS level information and places all output in
 // the file offlineupdate.log
 //
-static PRLogModuleInfo *gOfflineCacheUpdateLog;
+PRLogModuleInfo *gOfflineCacheUpdateLog;
 #endif
 #define LOG(args) PR_LOG(gOfflineCacheUpdateLog, 4, args)
 #define LOG_ENABLED() PR_LOG_TEST(gOfflineCacheUpdateLog, 4)
 
 class AutoFreeArray {
 public:
     AutoFreeArray(PRUint32 count, char **values)
         : mCount(count), mValues(values) {};
@@ -109,2100 +118,16 @@ DropReferenceFromURL(nsIURI * aURI)
         nsresult rv = url->SetRef(EmptyCString());
         NS_ENSURE_SUCCESS(rv, rv);
     }
 
     return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
-// nsManifestCheck
-//-----------------------------------------------------------------------------
-
-class nsManifestCheck : public nsIStreamListener
-                      , public nsIChannelEventSink
-                      , public nsIInterfaceRequestor
-{
-public:
-    nsManifestCheck(nsOfflineCacheUpdate *aUpdate,
-                    nsIURI *aURI,
-                    nsIURI *aReferrerURI)
-        : mUpdate(aUpdate)
-        , mURI(aURI)
-        , mReferrerURI(aReferrerURI)
-        {}
-
-    NS_DECL_ISUPPORTS
-    NS_DECL_NSIREQUESTOBSERVER
-    NS_DECL_NSISTREAMLISTENER
-    NS_DECL_NSICHANNELEVENTSINK
-    NS_DECL_NSIINTERFACEREQUESTOR
-
-    nsresult Begin();
-
-private:
-
-    static NS_METHOD ReadManifest(nsIInputStream *aInputStream,
-                                  void *aClosure,
-                                  const char *aFromSegment,
-                                  PRUint32 aOffset,
-                                  PRUint32 aCount,
-                                  PRUint32 *aBytesConsumed);
-
-    nsRefPtr<nsOfflineCacheUpdate> mUpdate;
-    nsCOMPtr<nsIURI> mURI;
-    nsCOMPtr<nsIURI> mReferrerURI;
-    nsCOMPtr<nsICryptoHash> mManifestHash;
-    nsCOMPtr<nsIChannel> mChannel;
-};
-
-//-----------------------------------------------------------------------------
-// nsManifestCheck::nsISupports
-//-----------------------------------------------------------------------------
-NS_IMPL_ISUPPORTS4(nsManifestCheck,
-                   nsIRequestObserver,
-                   nsIStreamListener,
-                   nsIChannelEventSink,
-                   nsIInterfaceRequestor)
-
-//-----------------------------------------------------------------------------
-// nsManifestCheck <public>
-//-----------------------------------------------------------------------------
-
-nsresult
-nsManifestCheck::Begin()
-{
-    nsresult rv;
-    mManifestHash = do_CreateInstance("@mozilla.org/security/hash;1", &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = mManifestHash->Init(nsICryptoHash::MD5);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = NS_NewChannel(getter_AddRefs(mChannel),
-                       mURI,
-                       nsnull, nsnull, nsnull,
-                       nsIRequest::LOAD_BYPASS_CACHE);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    // configure HTTP specific stuff
-    nsCOMPtr<nsIHttpChannel> httpChannel =
-        do_QueryInterface(mChannel);
-    if (httpChannel) {
-        httpChannel->SetReferrer(mReferrerURI);
-        httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("X-Moz"),
-                                      NS_LITERAL_CSTRING("offline-resource"),
-                                      PR_FALSE);
-    }
-
-    rv = mChannel->AsyncOpen(this, nsnull);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    return NS_OK;
-}
-
-//-----------------------------------------------------------------------------
-// nsManifestCheck <public>
-//-----------------------------------------------------------------------------
-
-/* static */
-NS_METHOD
-nsManifestCheck::ReadManifest(nsIInputStream *aInputStream,
-                              void *aClosure,
-                              const char *aFromSegment,
-                              PRUint32 aOffset,
-                              PRUint32 aCount,
-                              PRUint32 *aBytesConsumed)
-{
-    nsManifestCheck *manifestCheck =
-        static_cast<nsManifestCheck*>(aClosure);
-
-    nsresult rv;
-    *aBytesConsumed = aCount;
-
-    rv = manifestCheck->mManifestHash->Update(
-        reinterpret_cast<const PRUint8 *>(aFromSegment), aCount);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    return NS_OK;
-}
-
-//-----------------------------------------------------------------------------
-// nsManifestCheck::nsIStreamListener
-//-----------------------------------------------------------------------------
-
-NS_IMETHODIMP
-nsManifestCheck::OnStartRequest(nsIRequest *aRequest,
-                                nsISupports *aContext)
-{
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsManifestCheck::OnDataAvailable(nsIRequest *aRequest,
-                                 nsISupports *aContext,
-                                 nsIInputStream *aStream,
-                                 PRUint32 aOffset,
-                                 PRUint32 aCount)
-{
-    PRUint32 bytesRead;
-    aStream->ReadSegments(ReadManifest, this, aCount, &bytesRead);
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsManifestCheck::OnStopRequest(nsIRequest *aRequest,
-                               nsISupports *aContext,
-                               nsresult aStatus)
-{
-    nsCAutoString manifestHash;
-    if (NS_SUCCEEDED(aStatus)) {
-        mManifestHash->Finish(PR_TRUE, manifestHash);
-    }
-
-    mUpdate->ManifestCheckCompleted(aStatus, manifestHash);
-
-    return NS_OK;
-}
-
-//-----------------------------------------------------------------------------
-// nsManifestCheck::nsIInterfaceRequestor
-//-----------------------------------------------------------------------------
-
-NS_IMETHODIMP
-nsManifestCheck::GetInterface(const nsIID &aIID, void **aResult)
-{
-    if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
-        NS_ADDREF_THIS();
-        *aResult = static_cast<nsIChannelEventSink *>(this);
-        return NS_OK;
-    }
-
-    return NS_ERROR_NO_INTERFACE;
-}
-
-//-----------------------------------------------------------------------------
-// nsManifestCheck::nsIChannelEventSink
-//-----------------------------------------------------------------------------
-
-NS_IMETHODIMP
-nsManifestCheck::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
-                                        nsIChannel *aNewChannel,
-                                        PRUint32 aFlags,
-                                        nsIAsyncVerifyRedirectCallback *callback)
-{
-    // Redirects should cause the load (and therefore the update) to fail.
-    if (aFlags & nsIChannelEventSink::REDIRECT_INTERNAL) {
-        callback->OnRedirectVerifyCallback(NS_OK);
-        return NS_OK;
-    }
-    aOldChannel->Cancel(NS_ERROR_ABORT);
-    return NS_ERROR_ABORT;
-}
-
-//-----------------------------------------------------------------------------
-// nsOfflineCacheUpdateItem::nsISupports
-//-----------------------------------------------------------------------------
-
-NS_IMPL_ISUPPORTS6(nsOfflineCacheUpdateItem,
-                   nsIDOMLoadStatus,
-                   nsIRequestObserver,
-                   nsIStreamListener,
-                   nsIRunnable,
-                   nsIInterfaceRequestor,
-                   nsIChannelEventSink)
-
-//-----------------------------------------------------------------------------
-// nsOfflineCacheUpdateItem <public>
-//-----------------------------------------------------------------------------
-
-nsOfflineCacheUpdateItem::nsOfflineCacheUpdateItem(nsOfflineCacheUpdate *aUpdate,
-                                                   nsIURI *aURI,
-                                                   nsIURI *aReferrerURI,
-                                                   nsIApplicationCache *aPreviousApplicationCache,
-                                                   const nsACString &aClientID,
-                                                   PRUint32 type)
-    : mURI(aURI)
-    , mReferrerURI(aReferrerURI)
-    , mPreviousApplicationCache(aPreviousApplicationCache)
-    , mClientID(aClientID)
-    , mItemType(type)
-    , mUpdate(aUpdate)
-    , mChannel(nsnull)
-    , mState(nsIDOMLoadStatus::UNINITIALIZED)
-    , mBytesRead(0)
-{
-}
-
-nsOfflineCacheUpdateItem::~nsOfflineCacheUpdateItem()
-{
-}
-
-nsresult
-nsOfflineCacheUpdateItem::OpenChannel()
-{
-#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<nsIApplicationCacheChannel> appCacheChannel =
-        do_QueryInterface(mChannel, &rv);
-
-    // Support for nsIApplicationCacheChannel is required.
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    // Use the existing application cache as the cache to check.
-    rv = appCacheChannel->SetApplicationCache(mPreviousApplicationCache);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    // configure HTTP specific stuff
-    nsCOMPtr<nsIHttpChannel> httpChannel =
-        do_QueryInterface(mChannel);
-    if (httpChannel) {
-        httpChannel->SetReferrer(mReferrerURI);
-        httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("X-Moz"),
-                                      NS_LITERAL_CSTRING("offline-resource"),
-                                      PR_FALSE);
-    }
-
-    nsCOMPtr<nsICachingChannel> cachingChannel =
-        do_QueryInterface(mChannel);
-    if (cachingChannel) {
-        rv = cachingChannel->SetCacheForOfflineUse(PR_TRUE);
-        NS_ENSURE_SUCCESS(rv, rv);
-
-        if (!mClientID.IsEmpty()) {
-            rv = cachingChannel->SetOfflineCacheClientID(mClientID);
-            NS_ENSURE_SUCCESS(rv, rv);
-        }
-    }
-
-    rv = mChannel->AsyncOpen(this, nsnull);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    mState = nsIDOMLoadStatus::REQUESTED;
-
-    return NS_OK;
-}
-
-nsresult
-nsOfflineCacheUpdateItem::Cancel()
-{
-    if (mChannel) {
-        mChannel->Cancel(NS_ERROR_ABORT);
-        mChannel = nsnull;
-    }
-
-    mState = nsIDOMLoadStatus::UNINITIALIZED;
-
-    return NS_OK;
-}
-
-//-----------------------------------------------------------------------------
-// nsOfflineCacheUpdateItem::nsIStreamListener
-//-----------------------------------------------------------------------------
-
-NS_IMETHODIMP
-nsOfflineCacheUpdateItem::OnStartRequest(nsIRequest *aRequest,
-                                         nsISupports *aContext)
-{
-    mState = nsIDOMLoadStatus::RECEIVING;
-
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsOfflineCacheUpdateItem::OnDataAvailable(nsIRequest *aRequest,
-                                          nsISupports *aContext,
-                                          nsIInputStream *aStream,
-                                          PRUint32 aOffset,
-                                          PRUint32 aCount)
-{
-    PRUint32 bytesRead = 0;
-    aStream->ReadSegments(NS_DiscardSegment, nsnull, aCount, &bytesRead);
-    mBytesRead += bytesRead;
-    LOG(("loaded %u bytes into offline cache [offset=%u]\n",
-         bytesRead, aOffset));
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsOfflineCacheUpdateItem::OnStopRequest(nsIRequest *aRequest,
-                                        nsISupports *aContext,
-                                        nsresult aStatus)
-{
-    LOG(("done fetching offline item [status=%x]\n", aStatus));
-
-    mState = nsIDOMLoadStatus::LOADED;
-
-    if (mBytesRead == 0 && aStatus == NS_OK) {
-        // we didn't need to read (because LOAD_ONLY_IF_MODIFIED was
-        // specified), but the object should report loadedSize as if it
-        // did.
-        mChannel->GetContentLength(&mBytesRead);
-    }
-
-    // We need to notify the update that the load is complete, but we
-    // want to give the channel a chance to close the cache entries.
-    NS_DispatchToCurrentThread(this);
-
-    return NS_OK;
-}
-
-
-//-----------------------------------------------------------------------------
-// nsOfflineCacheUpdateItem::nsIRunnable
-//-----------------------------------------------------------------------------
-NS_IMETHODIMP
-nsOfflineCacheUpdateItem::Run()
-{
-    mUpdate->LoadCompleted();
-
-    return NS_OK;
-}
-
-//-----------------------------------------------------------------------------
-// nsOfflineCacheUpdateItem::nsIInterfaceRequestor
-//-----------------------------------------------------------------------------
-
-NS_IMETHODIMP
-nsOfflineCacheUpdateItem::GetInterface(const nsIID &aIID, void **aResult)
-{
-    if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
-        NS_ADDREF_THIS();
-        *aResult = static_cast<nsIChannelEventSink *>(this);
-        return NS_OK;
-    }
-
-    return NS_ERROR_NO_INTERFACE;
-}
-
-//-----------------------------------------------------------------------------
-// nsOfflineCacheUpdateItem::nsIChannelEventSink
-//-----------------------------------------------------------------------------
-
-NS_IMETHODIMP
-nsOfflineCacheUpdateItem::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
-                                                 nsIChannel *aNewChannel,
-                                                 PRUint32 aFlags,
-                                                 nsIAsyncVerifyRedirectCallback *cb)
-{
-    if (!(aFlags & nsIChannelEventSink::REDIRECT_INTERNAL)) {
-        // Don't allow redirect in case of non-internal redirect and cancel
-        // the channel to clean the cache entry.
-        aOldChannel->Cancel(NS_ERROR_ABORT);
-        return NS_ERROR_ABORT;
-    }
-
-    nsCOMPtr<nsIURI> newURI;
-    nsresult rv = aNewChannel->GetURI(getter_AddRefs(newURI));
-    if (NS_FAILED(rv))
-        return rv;
-
-    nsCOMPtr<nsICachingChannel> oldCachingChannel =
-        do_QueryInterface(aOldChannel);
-    nsCOMPtr<nsICachingChannel> newCachingChannel =
-        do_QueryInterface(aNewChannel);
-    if (newCachingChannel) {
-        rv = newCachingChannel->SetCacheForOfflineUse(PR_TRUE);
-        NS_ENSURE_SUCCESS(rv, rv);
-        if (!mClientID.IsEmpty()) {
-            rv = newCachingChannel->SetOfflineCacheClientID(mClientID);
-            NS_ENSURE_SUCCESS(rv, rv);
-        }
-    }
-
-    nsCAutoString oldScheme;
-    mURI->GetScheme(oldScheme);
-
-    PRBool match;
-    if (NS_FAILED(newURI->SchemeIs(oldScheme.get(), &match)) || !match) {
-        LOG(("rejected: redirected to a different scheme\n"));
-        return NS_ERROR_ABORT;
-    }
-
-    // HTTP request headers are not automatically forwarded to the new channel.
-    nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aNewChannel);
-    NS_ENSURE_STATE(httpChannel);
-
-    httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("X-Moz"),
-                                  NS_LITERAL_CSTRING("offline-resource"),
-                                  PR_FALSE);
-
-    mChannel = aNewChannel;
-
-    cb->OnRedirectVerifyCallback(NS_OK);
-    return NS_OK;
-}
-
-//-----------------------------------------------------------------------------
-// nsOfflineCacheUpdateItem::nsIDOMLoadStatus
-//-----------------------------------------------------------------------------
-
-NS_IMETHODIMP
-nsOfflineCacheUpdateItem::GetSource(nsIDOMNode **aSource)
-{
-    *aSource = nsnull;
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsOfflineCacheUpdateItem::GetUri(nsAString &aURI)
-{
-    nsCAutoString spec;
-    nsresult rv = mURI->GetSpec(spec);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    CopyUTF8toUTF16(spec, aURI);
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsOfflineCacheUpdateItem::GetTotalSize(PRInt32 *aTotalSize)
-{
-    if (mChannel) {
-        return mChannel->GetContentLength(aTotalSize);
-    }
-
-    *aTotalSize = -1;
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsOfflineCacheUpdateItem::GetLoadedSize(PRInt32 *aLoadedSize)
-{
-    *aLoadedSize = mBytesRead;
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsOfflineCacheUpdateItem::GetReadyState(PRUint16 *aReadyState)
-{
-    *aReadyState = mState;
-    return NS_OK;
-}
-
-nsresult
-nsOfflineCacheUpdateItem::GetRequestSucceeded(PRBool * succeeded)
-{
-    *succeeded = PR_FALSE;
-
-    if (!mChannel)
-        return NS_OK;
-
-    nsresult rv;
-    nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel, &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    PRBool reqSucceeded;
-    rv = httpChannel->GetRequestSucceeded(&reqSucceeded);
-    if (NS_ERROR_NOT_AVAILABLE == rv)
-        return NS_OK;
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    if (!reqSucceeded) {
-        LOG(("Request failed"));
-        return NS_OK;
-    }
-
-    nsresult channelStatus;
-    rv = httpChannel->GetStatus(&channelStatus);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    if (NS_FAILED(channelStatus)) {
-        LOG(("Channel status=0x%08x", channelStatus));
-        return NS_OK;
-    }
-
-    *succeeded = PR_TRUE;
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsOfflineCacheUpdateItem::GetStatus(PRUint16 *aStatus)
-{
-    if (!mChannel) {
-        *aStatus = 0;
-        return NS_OK;
-    }
-
-    nsresult rv;
-    nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel, &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    PRUint32 httpStatus;
-    rv = httpChannel->GetResponseStatus(&httpStatus);
-    if (rv == NS_ERROR_NOT_AVAILABLE) {
-        *aStatus = 0;
-        return NS_OK;
-    }
-
-    NS_ENSURE_SUCCESS(rv, rv);
-    *aStatus = PRUint16(httpStatus);
-    return NS_OK;
-}
-
-//-----------------------------------------------------------------------------
-// nsOfflineManifestItem
-//-----------------------------------------------------------------------------
-
-//-----------------------------------------------------------------------------
-// nsOfflineManifestItem <public>
-//-----------------------------------------------------------------------------
-
-nsOfflineManifestItem::nsOfflineManifestItem(nsOfflineCacheUpdate *aUpdate,
-                                             nsIURI *aURI,
-                                             nsIURI *aReferrerURI,
-                                             nsIApplicationCache *aPreviousApplicationCache,
-                                             const nsACString &aClientID)
-    : nsOfflineCacheUpdateItem(aUpdate, aURI, aReferrerURI,
-                               aPreviousApplicationCache, aClientID,
-                               nsIApplicationCache::ITEM_MANIFEST)
-    , mParserState(PARSE_INIT)
-    , mNeedsUpdate(PR_TRUE)
-    , mManifestHashInitialized(PR_FALSE)
-{
-    ReadStrictFileOriginPolicyPref();
-}
-
-nsOfflineManifestItem::~nsOfflineManifestItem()
-{
-}
-
-//-----------------------------------------------------------------------------
-// nsOfflineManifestItem <private>
-//-----------------------------------------------------------------------------
-
-/* static */
-NS_METHOD
-nsOfflineManifestItem::ReadManifest(nsIInputStream *aInputStream,
-                                    void *aClosure,
-                                    const char *aFromSegment,
-                                    PRUint32 aOffset,
-                                    PRUint32 aCount,
-                                    PRUint32 *aBytesConsumed)
-{
-    nsOfflineManifestItem *manifest =
-        static_cast<nsOfflineManifestItem*>(aClosure);
-
-    nsresult rv;
-
-    *aBytesConsumed = aCount;
-
-    if (manifest->mParserState == PARSE_ERROR) {
-        // parse already failed, ignore this
-        return NS_OK;
-    }
-
-    if (!manifest->mManifestHashInitialized) {
-        // Avoid re-creation of crypto hash when it fails from some reason the first time
-        manifest->mManifestHashInitialized = PR_TRUE;
-
-        manifest->mManifestHash = do_CreateInstance("@mozilla.org/security/hash;1", &rv);
-        if (NS_SUCCEEDED(rv)) {
-            rv = manifest->mManifestHash->Init(nsICryptoHash::MD5);
-            if (NS_FAILED(rv)) {
-                manifest->mManifestHash = nsnull;
-                LOG(("Could not initialize manifest hash for byte-to-byte check, rv=%08x", rv));
-            }
-        }
-    }
-
-    if (manifest->mManifestHash) {
-        rv = manifest->mManifestHash->Update(reinterpret_cast<const PRUint8 *>(aFromSegment), aCount);
-        if (NS_FAILED(rv)) {
-            manifest->mManifestHash = nsnull;
-            LOG(("Could not update manifest hash, rv=%08x", rv));
-        }
-    }
-
-    manifest->mReadBuf.Append(aFromSegment, aCount);
-
-    nsCString::const_iterator begin, iter, end;
-    manifest->mReadBuf.BeginReading(begin);
-    manifest->mReadBuf.EndReading(end);
-
-    for (iter = begin; iter != end; iter++) {
-        if (*iter == '\r' || *iter == '\n') {
-            nsresult rv = manifest->HandleManifestLine(begin, iter);
-            
-            if (NS_FAILED(rv)) {
-                LOG(("HandleManifestLine failed with 0x%08x", rv));
-                return NS_ERROR_ABORT;
-            }
-
-            begin = iter;
-            begin++;
-        }
-    }
-
-    // any leftovers are saved for next time
-    manifest->mReadBuf = Substring(begin, end);
-
-    return NS_OK;
-}
-
-nsresult
-nsOfflineManifestItem::AddNamespace(PRUint32 namespaceType,
-                                    const nsCString &namespaceSpec,
-                                    const nsCString &data)
-
-{
-    nsresult rv;
-    if (!mNamespaces) {
-        mNamespaces = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
-        NS_ENSURE_SUCCESS(rv, rv);
-    }
-
-    nsCOMPtr<nsIApplicationCacheNamespace> ns =
-        do_CreateInstance(NS_APPLICATIONCACHENAMESPACE_CONTRACTID, &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = ns->Init(namespaceType, namespaceSpec, data);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = mNamespaces->AppendElement(ns, PR_FALSE);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    return NS_OK;
-}
-
-nsresult
-nsOfflineManifestItem::HandleManifestLine(const nsCString::const_iterator &aBegin,
-                                          const nsCString::const_iterator &aEnd)
-{
-    nsCString::const_iterator begin = aBegin;
-    nsCString::const_iterator end = aEnd;
-
-    // all lines ignore trailing spaces and tabs
-    nsCString::const_iterator last = end;
-    --last;
-    while (end != begin && (*last == ' ' || *last == '\t')) {
-        --end;
-        --last;
-    }
-
-    if (mParserState == PARSE_INIT) {
-        // Allow a UTF-8 BOM
-        if (begin != end && static_cast<unsigned char>(*begin) == 0xef) {
-            if (++begin == end || static_cast<unsigned char>(*begin) != 0xbb ||
-                ++begin == end || static_cast<unsigned char>(*begin) != 0xbf) {
-                mParserState = PARSE_ERROR;
-                return NS_OK;
-            }
-            ++begin;
-        }
-
-        const nsCSubstring &magic = Substring(begin, end);
-
-        if (!magic.EqualsLiteral("CACHE MANIFEST")) {
-            mParserState = PARSE_ERROR;
-            return NS_OK;
-        }
-
-        mParserState = PARSE_CACHE_ENTRIES;
-        return NS_OK;
-    }
-
-    // lines other than the first ignore leading spaces and tabs
-    while (begin != end && (*begin == ' ' || *begin == '\t'))
-        begin++;
-
-    // ignore blank lines and comments
-    if (begin == end || *begin == '#')
-        return NS_OK;
-
-    const nsCSubstring &line = Substring(begin, end);
-
-    if (line.EqualsLiteral("CACHE:")) {
-        mParserState = PARSE_CACHE_ENTRIES;
-        return NS_OK;
-    }
-
-    if (line.EqualsLiteral("FALLBACK:")) {
-        mParserState = PARSE_FALLBACK_ENTRIES;
-        return NS_OK;
-    }
-
-    if (line.EqualsLiteral("NETWORK:")) {
-        mParserState = PARSE_BYPASS_ENTRIES;
-        return NS_OK;
-    }
-
-    nsresult rv;
-
-    switch(mParserState) {
-    case PARSE_INIT:
-    case PARSE_ERROR: {
-        // this should have been dealt with earlier
-        return NS_ERROR_FAILURE;
-    }
-
-    case PARSE_CACHE_ENTRIES: {
-        nsCOMPtr<nsIURI> uri;
-        rv = NS_NewURI(getter_AddRefs(uri), line, nsnull, mURI);
-        if (NS_FAILED(rv))
-            break;
-        if (NS_FAILED(DropReferenceFromURL(uri)))
-            break;
-
-        nsCAutoString scheme;
-        uri->GetScheme(scheme);
-
-        // Manifest URIs must have the same scheme as the manifest.
-        PRBool match;
-        if (NS_FAILED(mURI->SchemeIs(scheme.get(), &match)) || !match)
-            break;
-
-        mExplicitURIs.AppendObject(uri);
-        break;
-    }
-
-    case PARSE_FALLBACK_ENTRIES: {
-        PRInt32 separator = line.FindChar(' ');
-        if (separator == kNotFound) {
-            separator = line.FindChar('\t');
-            if (separator == kNotFound)
-                break;
-        }
-
-        nsCString namespaceSpec(Substring(line, 0, separator));
-        nsCString fallbackSpec(Substring(line, separator + 1));
-        namespaceSpec.CompressWhitespace();
-        fallbackSpec.CompressWhitespace();
-
-        nsCOMPtr<nsIURI> namespaceURI;
-        rv = NS_NewURI(getter_AddRefs(namespaceURI), namespaceSpec, nsnull, mURI);
-        if (NS_FAILED(rv))
-            break;
-        if (NS_FAILED(DropReferenceFromURL(namespaceURI)))
-            break;
-        rv = namespaceURI->GetAsciiSpec(namespaceSpec);
-        if (NS_FAILED(rv))
-            break;
-
-
-        nsCOMPtr<nsIURI> fallbackURI;
-        rv = NS_NewURI(getter_AddRefs(fallbackURI), fallbackSpec, nsnull, mURI);
-        if (NS_FAILED(rv))
-            break;
-        if (NS_FAILED(DropReferenceFromURL(fallbackURI)))
-            break;
-        rv = fallbackURI->GetAsciiSpec(fallbackSpec);
-        if (NS_FAILED(rv))
-            break;
-
-        // Manifest and namespace must be same origin
-        if (!NS_SecurityCompareURIs(mURI, namespaceURI,
-                                    mStrictFileOriginPolicy))
-            break;
-
-        // Fallback and namespace must be same origin
-        if (!NS_SecurityCompareURIs(namespaceURI, fallbackURI,
-                                    mStrictFileOriginPolicy))
-            break;
-
-        mFallbackURIs.AppendObject(fallbackURI);
-
-        AddNamespace(nsIApplicationCacheNamespace::NAMESPACE_FALLBACK,
-                     namespaceSpec, fallbackSpec);
-        break;
-    }
-
-    case PARSE_BYPASS_ENTRIES: {
-        nsCOMPtr<nsIURI> bypassURI;
-        rv = NS_NewURI(getter_AddRefs(bypassURI), line, nsnull, mURI);
-        if (NS_FAILED(rv))
-            break;
-
-        nsCAutoString scheme;
-        bypassURI->GetScheme(scheme);
-        PRBool equals;
-        if (NS_FAILED(mURI->SchemeIs(scheme.get(), &equals)) || !equals)
-            break;
-        if (NS_FAILED(DropReferenceFromURL(bypassURI)))
-            break;
-        nsCString spec;
-        if (NS_FAILED(bypassURI->GetAsciiSpec(spec)))
-            break;
-
-        AddNamespace(nsIApplicationCacheNamespace::NAMESPACE_BYPASS,
-                     spec, EmptyCString());
-        break;
-    }
-    }
-
-    return NS_OK;
-}
-
-nsresult 
-nsOfflineManifestItem::GetOldManifestContentHash(nsIRequest *aRequest)
-{
-    nsresult rv;
-
-    nsCOMPtr<nsICachingChannel> cachingChannel = do_QueryInterface(aRequest, &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    // load the main cache token that is actually the old offline cache token and 
-    // read previous manifest content hash value
-    nsCOMPtr<nsISupports> cacheToken;
-    cachingChannel->GetCacheToken(getter_AddRefs(cacheToken));
-    if (cacheToken) {
-        nsCOMPtr<nsICacheEntryDescriptor> cacheDescriptor(do_QueryInterface(cacheToken, &rv));
-        NS_ENSURE_SUCCESS(rv, rv);
-    
-        rv = cacheDescriptor->GetMetaDataElement("offline-manifest-hash", getter_Copies(mOldManifestHashValue));
-        if (NS_FAILED(rv))
-            mOldManifestHashValue.Truncate();
-    }
-
-    return NS_OK;
-}
-
-nsresult 
-nsOfflineManifestItem::CheckNewManifestContentHash(nsIRequest *aRequest)
-{
-    nsresult rv;
-
-    if (!mManifestHash) {
-        // Nothing to compare against...
-        return NS_OK;
-    }
-
-    nsCString newManifestHashValue;
-    rv = mManifestHash->Finish(PR_TRUE, mManifestHashValue);
-    mManifestHash = nsnull;
-
-    if (NS_FAILED(rv)) {
-        LOG(("Could not finish manifest hash, rv=%08x", rv));
-        // This is not critical error
-        return NS_OK;
-    }
-
-    if (!ParseSucceeded()) {
-        // Parsing failed, the hash is not valid
-        return NS_OK;
-    }
-
-    if (mOldManifestHashValue == mManifestHashValue) {
-        LOG(("Update not needed, downloaded manifest content is byte-for-byte identical"));
-        mNeedsUpdate = PR_FALSE;
-    }
-
-    // Store the manifest content hash value to the new
-    // offline cache token
-    nsCOMPtr<nsICachingChannel> cachingChannel = do_QueryInterface(aRequest, &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    nsCOMPtr<nsISupports> cacheToken;
-    cachingChannel->GetOfflineCacheToken(getter_AddRefs(cacheToken));
-    if (cacheToken) {
-        nsCOMPtr<nsICacheEntryDescriptor> cacheDescriptor(do_QueryInterface(cacheToken, &rv));
-        NS_ENSURE_SUCCESS(rv, rv);
-    
-        rv = cacheDescriptor->SetMetaDataElement("offline-manifest-hash", mManifestHashValue.get());
-        NS_ENSURE_SUCCESS(rv, rv);
-    }
-
-    return NS_OK;
-}
-
-void
-nsOfflineManifestItem::ReadStrictFileOriginPolicyPref()
-{
-    nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
-    mStrictFileOriginPolicy =
-        (!prefs ||
-         NS_FAILED(prefs->GetBoolPref("security.fileuri.strict_origin_policy",
-                                      &mStrictFileOriginPolicy)));
-}
-
-NS_IMETHODIMP
-nsOfflineManifestItem::OnStartRequest(nsIRequest *aRequest,
-                                      nsISupports *aContext)
-{
-    nsresult rv;
-
-    nsCOMPtr<nsIHttpChannel> channel = do_QueryInterface(aRequest, &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    PRBool succeeded;
-    rv = channel->GetRequestSucceeded(&succeeded);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    if (!succeeded) {
-        LOG(("HTTP request failed"));
-        mParserState = PARSE_ERROR;
-        return NS_ERROR_ABORT;
-    }
-
-    nsCAutoString contentType;
-    rv = channel->GetContentType(contentType);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    if (!contentType.EqualsLiteral("text/cache-manifest")) {
-        LOG(("Rejected cache manifest with Content-Type %s (expecting text/cache-manifest)",
-             contentType.get()));
-        mParserState = PARSE_ERROR;
-        return NS_ERROR_ABORT;
-    }
-
-    rv = GetOldManifestContentHash(aRequest);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    return nsOfflineCacheUpdateItem::OnStartRequest(aRequest, aContext);
-}
-
-NS_IMETHODIMP
-nsOfflineManifestItem::OnDataAvailable(nsIRequest *aRequest,
-                                       nsISupports *aContext,
-                                       nsIInputStream *aStream,
-                                       PRUint32 aOffset,
-                                       PRUint32 aCount)
-{
-    PRUint32 bytesRead = 0;
-    aStream->ReadSegments(ReadManifest, this, aCount, &bytesRead);
-    mBytesRead += bytesRead;
-
-    if (mParserState == PARSE_ERROR) {
-        LOG(("OnDataAvailable is canceling the request due a parse error\n"));
-        return NS_ERROR_ABORT;
-    }
-
-    LOG(("loaded %u bytes into offline cache [offset=%u]\n",
-         bytesRead, aOffset));
-
-    // All the parent method does is read and discard, don't bother
-    // chaining up.
-
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsOfflineManifestItem::OnStopRequest(nsIRequest *aRequest,
-                                     nsISupports *aContext,
-                                     nsresult aStatus)
-{
-    // handle any leftover manifest data
-    nsCString::const_iterator begin, end;
-    mReadBuf.BeginReading(begin);
-    mReadBuf.EndReading(end);
-    nsresult rv = HandleManifestLine(begin, end);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    if (mBytesRead == 0) {
-        // we didn't need to read (because LOAD_ONLY_IF_MODIFIED was
-        // specified.)
-        mNeedsUpdate = PR_FALSE;
-    } else {
-        rv = CheckNewManifestContentHash(aRequest);
-        NS_ENSURE_SUCCESS(rv, rv);
-    }
-
-    return nsOfflineCacheUpdateItem::OnStopRequest(aRequest, aContext, aStatus);
-}
-
-//-----------------------------------------------------------------------------
-// nsOfflineCacheUpdate::nsISupports
-//-----------------------------------------------------------------------------
-
-NS_IMPL_ISUPPORTS1(nsOfflineCacheUpdate,
-                   nsIOfflineCacheUpdate)
-
-//-----------------------------------------------------------------------------
-// nsOfflineCacheUpdate <public>
-//-----------------------------------------------------------------------------
-
-nsOfflineCacheUpdate::nsOfflineCacheUpdate()
-    : mState(STATE_UNINITIALIZED)
-    , mOwner(nsnull)
-    , mAddedItems(PR_FALSE)
-    , mPartialUpdate(PR_FALSE)
-    , mSucceeded(PR_TRUE)
-    , mObsolete(PR_FALSE)
-    , mCurrentItem(-1)
-    , mRescheduleCount(0)
-{
-}
-
-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(nsIURI *aManifestURI,
-                           nsIURI *aDocumentURI)
-{
-    nsresult rv;
-
-    // Make sure the service has been initialized
-    nsOfflineCacheUpdateService* service =
-        nsOfflineCacheUpdateService::EnsureService();
-    if (!service)
-        return NS_ERROR_FAILURE;
-
-    LOG(("nsOfflineCacheUpdate::Init [%p]", this));
-
-    mPartialUpdate = PR_FALSE;
-
-    // Only http and https applications are supported.
-    PRBool match;
-    rv = aManifestURI->SchemeIs("http", &match);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    if (!match) {
-        rv = aManifestURI->SchemeIs("https", &match);
-        NS_ENSURE_SUCCESS(rv, rv);
-        if (!match)
-            return NS_ERROR_ABORT;
-    }
-
-    mManifestURI = aManifestURI;
-
-    rv = mManifestURI->GetAsciiHost(mUpdateDomain);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    nsCAutoString manifestSpec;
-
-    rv = GetCacheKey(mManifestURI, manifestSpec);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    mDocumentURI = aDocumentURI;
-
-    nsCOMPtr<nsIApplicationCacheService> cacheService =
-        do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = cacheService->GetActiveCache(manifestSpec,
-                                      getter_AddRefs(mPreviousApplicationCache));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    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;
-}
-
-nsresult
-nsOfflineCacheUpdate::InitPartial(nsIURI *aManifestURI,
-                                  const nsACString& clientID,
-                                  nsIURI *aDocumentURI)
-{
-    nsresult rv;
-
-    // Make sure the service has been initialized
-    nsOfflineCacheUpdateService* service =
-        nsOfflineCacheUpdateService::EnsureService();
-    if (!service)
-        return NS_ERROR_FAILURE;
-
-    LOG(("nsOfflineCacheUpdate::InitPartial [%p]", this));
-
-    mPartialUpdate = PR_TRUE;
-    mClientID = clientID;
-    mDocumentURI = aDocumentURI;
-
-    mManifestURI = aManifestURI;
-    rv = mManifestURI->GetAsciiHost(mUpdateDomain);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    nsCOMPtr<nsIApplicationCacheService> cacheService =
-        do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = cacheService->GetApplicationCache(mClientID,
-                                           getter_AddRefs(mApplicationCache));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    if (!mApplicationCache) {
-        nsCAutoString manifestSpec;
-        rv = GetCacheKey(mManifestURI, manifestSpec);
-        NS_ENSURE_SUCCESS(rv, rv);
-
-        rv = cacheService->CreateApplicationCache
-            (manifestSpec, getter_AddRefs(mApplicationCache));
-        NS_ENSURE_SUCCESS(rv, rv);
-    }
-
-    nsCAutoString groupID;
-    rv = mApplicationCache->GetGroupID(groupID);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = NS_NewURI(getter_AddRefs(mManifestURI), groupID);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    mState = STATE_INITIALIZED;
-    return NS_OK;
-}
-
-nsresult
-nsOfflineCacheUpdate::HandleManifest(PRBool *aDoUpdate)
-{
-    // Be pessimistic
-    *aDoUpdate = PR_FALSE;
-
-    PRBool succeeded;
-    nsresult rv = mManifestItem->GetRequestSucceeded(&succeeded);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    if (!succeeded || !mManifestItem->ParseSucceeded()) {
-        return NS_ERROR_FAILURE;
-    }
-
-    if (!mManifestItem->NeedsUpdate()) {
-        return NS_OK;
-    }
-
-    // Add items requested by the manifest.
-    const nsCOMArray<nsIURI> &manifestURIs = mManifestItem->GetExplicitURIs();
-    for (PRInt32 i = 0; i < manifestURIs.Count(); i++) {
-        rv = AddURI(manifestURIs[i], nsIApplicationCache::ITEM_EXPLICIT);
-        NS_ENSURE_SUCCESS(rv, rv);
-    }
-
-    const nsCOMArray<nsIURI> &fallbackURIs = mManifestItem->GetFallbackURIs();
-    for (PRInt32 i = 0; i < fallbackURIs.Count(); i++) {
-        rv = AddURI(fallbackURIs[i], nsIApplicationCache::ITEM_FALLBACK);
-        NS_ENSURE_SUCCESS(rv, rv);
-    }
-
-    // The document that requested the manifest is implicitly included
-    // as part of that manifest update.
-    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 = AddExistingItems(nsIApplicationCache::ITEM_DYNAMIC);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    // Add opportunistically cached items conforming current opportunistic
-    // namespace list
-    rv = AddExistingItems(nsIApplicationCache::ITEM_OPPORTUNISTIC,
-                          &mManifestItem->GetOpportunisticNamespaces());
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    *aDoUpdate = PR_TRUE;
-
-    return NS_OK;
-}
-
-void
-nsOfflineCacheUpdate::LoadCompleted()
-{
-    nsresult rv;
-
-    // Keep the object alive through a Finish() call.
-    nsCOMPtr<nsIOfflineCacheUpdate> kungFuDeathGrip(this);
-
-    LOG(("nsOfflineCacheUpdate::LoadCompleted [%p]", this));
-
-    if (mState == STATE_CANCELLED) {
-        Finish();
-        return;
-    }
-
-    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;
-            if (mPreviousApplicationCache) {
-                NotifyObsolete();
-            } else {
-                NotifyError();
-            }
-            Finish();
-            return;
-        }
-
-        PRBool doUpdate;
-        if (NS_FAILED(HandleManifest(&doUpdate))) {
-            mSucceeded = PR_FALSE;
-            NotifyError();
-            Finish();
-            return;
-        }
-
-        if (!doUpdate) {
-            mSucceeded = PR_FALSE;
-
-            for (PRInt32 i = 0; i < mDocuments.Count(); i++) {
-                AssociateDocument(mDocuments[i], mPreviousApplicationCache);
-            }
-
-            ScheduleImplicit();
-
-            // If we didn't need an implicit update, we can
-            // send noupdate and end the update now.
-            if (!mImplicitUpdate) {
-                NotifyNoUpdate();
-                Finish();
-            }
-            return;
-        }
-
-        rv = mApplicationCache->MarkEntry(mManifestItem->mCacheKey,
-                                          mManifestItem->mItemType);
-        if (NS_FAILED(rv)) {
-            mSucceeded = PR_FALSE;
-            NotifyError();
-            Finish();
-            return;
-        }
-
-        mState = STATE_DOWNLOADING;
-        NotifyDownloading();
-
-        // Start fetching resources.
-        ProcessNextURI();
-
-        return;
-    }
-
-    // Normal load finished.
-
-    nsRefPtr<nsOfflineCacheUpdateItem> item = mItems[mCurrentItem];
-    mCurrentItem++;
-
-    PRBool succeeded;
-    rv = item->GetRequestSucceeded(&succeeded);
-
-    // Check for failures.  3XX, 4XX and 5XX errors on items explicitly
-    // listed in the manifest will cause the update to fail.
-    if (NS_FAILED(rv) || !succeeded) {
-        if (item->mItemType &
-            (nsIApplicationCache::ITEM_EXPLICIT |
-             nsIApplicationCache::ITEM_FALLBACK)) {
-            mSucceeded = PR_FALSE;
-        }
-    } else {
-        rv = mApplicationCache->MarkEntry(item->mCacheKey, item->mItemType);
-        if (NS_FAILED(rv)) {
-            mSucceeded = PR_FALSE;
-        }
-    }
-
-    if (!mSucceeded) {
-        NotifyError();
-        Finish();
-        return;
-    }
-
-    rv = NotifyCompleted(item);
-    if (NS_FAILED(rv)) return;
-
-    ProcessNextURI();
-}
-
-void
-nsOfflineCacheUpdate::ManifestCheckCompleted(nsresult aStatus,
-                                             const nsCString &aManifestHash)
-{
-    // Keep the object alive through a Finish() call.
-    nsCOMPtr<nsIOfflineCacheUpdate> kungFuDeathGrip(this);
-
-    if (NS_SUCCEEDED(aStatus)) {
-        nsCAutoString firstManifestHash;
-        mManifestItem->GetManifestHash(firstManifestHash);
-        if (aManifestHash != firstManifestHash) {
-            aStatus = NS_ERROR_FAILURE;
-        }
-    }
-
-    if (NS_FAILED(aStatus)) {
-        mSucceeded = PR_FALSE;
-        NotifyError();
-    }
-
-    Finish();
-
-    if (NS_FAILED(aStatus) && mRescheduleCount < kRescheduleLimit) {
-        // Reschedule this update.
-        nsRefPtr<nsOfflineCacheUpdate> newUpdate =
-            new nsOfflineCacheUpdate();
-        newUpdate->Init(mManifestURI, mDocumentURI);
-
-        for (PRInt32 i = 0; i < mDocuments.Count(); i++) {
-            newUpdate->AddDocument(mDocuments[i]);
-        }
-
-        newUpdate->mRescheduleCount = mRescheduleCount + 1;
-        newUpdate->Schedule();
-    }
-}
-
-nsresult
-nsOfflineCacheUpdate::Begin()
-{
-    LOG(("nsOfflineCacheUpdate::Begin [%p]", this));
-
-    // Keep the object alive through a ProcessNextURI()/Finish() call.
-    nsCOMPtr<nsIOfflineCacheUpdate> kungFuDeathGrip(this);
-
-    mCurrentItem = 0;
-
-    if (mPartialUpdate) {
-        mState = STATE_DOWNLOADING;
-        NotifyDownloading();
-        ProcessNextURI();
-        return NS_OK;
-    }
-
-    // Start checking the manifest.
-    nsCOMPtr<nsIURI> uri;
-
-    mManifestItem = new nsOfflineManifestItem(this, mManifestURI,
-                                              mDocumentURI,
-                                              mPreviousApplicationCache,
-                                              mClientID);
-    if (!mManifestItem) {
-        return NS_ERROR_OUT_OF_MEMORY;
-    }
-
-    mState = STATE_CHECKING;
-    NotifyChecking();
-
-    nsresult rv = mManifestItem->OpenChannel();
-    if (NS_FAILED(rv)) {
-        LoadCompleted();
-    }
-
-    return NS_OK;
-}
-
-nsresult
-nsOfflineCacheUpdate::Cancel()
-{
-    LOG(("nsOfflineCacheUpdate::Cancel [%p]", this));
-
-    mState = STATE_CANCELLED;
-    mSucceeded = PR_FALSE;
-
-    if (mCurrentItem >= 0 &&
-        mCurrentItem < static_cast<PRInt32>(mItems.Length())) {
-        // Load might be running
-        mItems[mCurrentItem]->Cancel();
-    }
-
-    return NS_OK;
-}
-
-//-----------------------------------------------------------------------------
-// nsOfflineCacheUpdate <private>
-//-----------------------------------------------------------------------------
-
-nsresult
-nsOfflineCacheUpdate::AddExistingItems(PRUint32 aType,
-                                       nsTArray<nsCString>* namespaceFilter)
-{
-    if (!mPreviousApplicationCache) {
-        return NS_OK;
-    }
-
-    if (namespaceFilter && namespaceFilter->Length() == 0) {
-        // Don't bother to walk entries when there are no namespaces
-        // defined.
-        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);
-
-    for (PRUint32 i = 0; i < count; i++) {
-        if (namespaceFilter) {
-            PRBool found = PR_FALSE;
-            for (PRUint32 j = 0; j < namespaceFilter->Length() && !found; j++) {
-                found = StringBeginsWith(nsDependentCString(keys[i]),
-                                         namespaceFilter->ElementAt(j));
-            }
-
-            if (!found)
-                continue;
-        }
-
-        nsCOMPtr<nsIURI> uri;
-        if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(uri), keys[i]))) {
-            rv = AddURI(uri, aType);
-            NS_ENSURE_SUCCESS(rv, rv);
-        }
-    }
-
-    return NS_OK;
-}
-
-nsresult
-nsOfflineCacheUpdate::ProcessNextURI()
-{
-    // Keep the object alive through a Finish() call.
-    nsCOMPtr<nsIOfflineCacheUpdate> kungFuDeathGrip(this);
-
-    LOG(("nsOfflineCacheUpdate::ProcessNextURI [%p, current=%d, numItems=%d]",
-         this, mCurrentItem, mItems.Length()));
-
-    NS_ASSERTION(mState == STATE_DOWNLOADING,
-                 "ProcessNextURI should only be called from the DOWNLOADING state");
-
-    if (mCurrentItem >= static_cast<PRInt32>(mItems.Length())) {
-        if (mPartialUpdate) {
-            return Finish();
-        } else {
-            // Verify that the manifest wasn't changed during the
-            // update, to prevent capturing a cache while the server
-            // is being updated.  The check will call
-            // ManifestCheckCompleted() when it's done.
-            nsRefPtr<nsManifestCheck> manifestCheck =
-                new nsManifestCheck(this, mManifestURI, mDocumentURI);
-            if (NS_FAILED(manifestCheck->Begin())) {
-                mSucceeded = PR_FALSE;
-                NotifyError();
-                return Finish();
-            }
-
-            return NS_OK;
-        }
-    }
-
-#if defined(PR_LOGGING)
-    if (LOG_ENABLED()) {
-        nsCAutoString spec;
-        mItems[mCurrentItem]->mURI->GetSpec(spec);
-        LOG(("%p: Opening channel for %s", this, spec.get()));
-    }
-#endif
-
-    NotifyStarted(mItems[mCurrentItem]);
-
-    nsresult rv = mItems[mCurrentItem]->OpenChannel();
-    if (NS_FAILED(rv)) {
-        LoadCompleted();
-        return rv;
-    }
-
-    return NS_OK;
-}
-
-nsresult
-nsOfflineCacheUpdate::GatherObservers(nsCOMArray<nsIOfflineCacheUpdateObserver> &aObservers)
-{
-    for (PRInt32 i = 0; i < mWeakObservers.Count(); i++) {
-        nsCOMPtr<nsIOfflineCacheUpdateObserver> observer =
-            do_QueryReferent(mWeakObservers[i]);
-        if (observer)
-            aObservers.AppendObject(observer);
-        else
-            mWeakObservers.RemoveObjectAt(i--);
-    }
-
-    for (PRInt32 i = 0; i < mObservers.Count(); i++) {
-        aObservers.AppendObject(mObservers[i]);
-    }
-
-    return NS_OK;
-}
-
-nsresult
-nsOfflineCacheUpdate::NotifyError()
-{
-    LOG(("nsOfflineCacheUpdate::NotifyError [%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]->Error(this);
-    }
-
-    return NS_OK;
-}
-
-nsresult
-nsOfflineCacheUpdate::NotifyChecking()
-{
-    LOG(("nsOfflineCacheUpdate::NotifyChecking [%p]", this));
-
-    nsCOMArray<nsIOfflineCacheUpdateObserver> observers;
-    nsresult rv = GatherObservers(observers);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    for (PRInt32 i = 0; i < observers.Count(); i++) {
-        observers[i]->Checking(this);
-    }
-
-    return NS_OK;
-}
-
-nsresult
-nsOfflineCacheUpdate::NotifyNoUpdate()
-{
-    LOG(("nsOfflineCacheUpdate::NotifyNoUpdate [%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]->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);
-
-    for (PRInt32 i = 0; i < observers.Count(); i++) {
-        observers[i]->Downloading(this);
-    }
-
-    return NS_OK;
-}
-
-nsresult
-nsOfflineCacheUpdate::NotifyStarted(nsOfflineCacheUpdateItem *aItem)
-{
-    LOG(("nsOfflineCacheUpdate::NotifyStarted [%p, %p]", this, aItem));
-
-    nsCOMArray<nsIOfflineCacheUpdateObserver> observers;
-    nsresult rv = GatherObservers(observers);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    for (PRInt32 i = 0; i < observers.Count(); i++) {
-        observers[i]->ItemStarted(this, aItem);
-    }
-
-    return NS_OK;
-}
-
-nsresult
-nsOfflineCacheUpdate::NotifyCompleted(nsOfflineCacheUpdateItem *aItem)
-{
-    LOG(("nsOfflineCacheUpdate::NotifyCompleted [%p, %p]", this, aItem));
-
-    nsCOMArray<nsIOfflineCacheUpdateObserver> observers;
-    nsresult rv = GatherObservers(observers);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    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);
-}
-
-void
-nsOfflineCacheUpdate::SetOwner(nsOfflineCacheUpdateOwner *aOwner)
-{
-    NS_ASSERTION(!mOwner, "Tried to set cache update owner twice.");
-    mOwner = aOwner;
-}
-
-nsresult
-nsOfflineCacheUpdate::UpdateFinished(nsOfflineCacheUpdate *aUpdate)
-{
-    // Keep the object alive through a Finish() call.
-    nsCOMPtr<nsIOfflineCacheUpdate> kungFuDeathGrip(this);
-
-    mImplicitUpdate = nsnull;
-
-    NotifyNoUpdate();
-    Finish();
-
-    return NS_OK;
-}
-
-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;
-
-        nsCOMPtr<nsIDOMElement> root = do_QueryInterface(doc->GetRootElement());
-        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;
-
-    update->SetOwner(this);
-    rv = update->Begin();
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    mImplicitUpdate = update;
-
-    return NS_OK;
-}
-
-nsresult
-nsOfflineCacheUpdate::AssociateDocument(nsIDOMDocument *aDocument,
-                                        nsIApplicationCache *aApplicationCache)
-{
-    // 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);
-    if (!container)
-        return NS_OK;
-
-    nsCOMPtr<nsIApplicationCache> existingCache;
-    nsresult rv = container->GetApplicationCache(getter_AddRefs(existingCache));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    if (!existingCache) {
-#if defined(PR_LOGGING)
-        if (LOG_ENABLED()) {
-            nsCAutoString clientID;
-            if (aApplicationCache) {
-                aApplicationCache->GetClientID(clientID);
-            }
-            LOG(("Update %p: associating app cache %s to document %p",
-                 this, clientID.get(), aDocument));
-        }
-#endif
-
-        rv = container->SetApplicationCache(aApplicationCache);
-        NS_ENSURE_SUCCESS(rv, rv);
-    }
-
-    return NS_OK;
-}
-
-nsresult
-nsOfflineCacheUpdate::Finish()
-{
-    LOG(("nsOfflineCacheUpdate::Finish [%p]", this));
-
-    mState = STATE_FINISHED;
-
-    if (!mPartialUpdate) {
-        if (mSucceeded) {
-            nsIArray *namespaces = mManifestItem->GetNamespaces();
-            nsresult rv = mApplicationCache->AddNamespaces(namespaces);
-            if (NS_FAILED(rv)) {
-                NotifyError();
-                mSucceeded = PR_FALSE;
-            }
-
-            rv = mApplicationCache->Activate();
-            if (NS_FAILED(rv)) {
-                NotifyError();
-                mSucceeded = PR_FALSE;
-            }
-
-            for (PRInt32 i = 0; i < mDocuments.Count(); i++) {
-                AssociateDocument(mDocuments[i], mApplicationCache);
-            }
-        }
-
-        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();
-        }
-    }
-
-    nsresult rv = NS_OK;
-
-    if (mOwner) {
-        rv = mOwner->UpdateFinished(this);
-        mOwner = nsnull;
-    }
-
-    return rv;
-}
-
-//-----------------------------------------------------------------------------
-// nsOfflineCacheUpdate::nsIOfflineCacheUpdate
-//-----------------------------------------------------------------------------
-
-NS_IMETHODIMP
-nsOfflineCacheUpdate::GetUpdateDomain(nsACString &aUpdateDomain)
-{
-    NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
-
-    aUpdateDomain = mUpdateDomain;
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsOfflineCacheUpdate::GetStatus(PRUint16 *aStatus)
-{
-    switch (mState) {
-    case STATE_CHECKING :
-        *aStatus = nsIDOMOfflineResourceList::CHECKING;
-        return NS_OK;
-    case STATE_DOWNLOADING :
-        *aStatus = nsIDOMOfflineResourceList::DOWNLOADING;
-        return NS_OK;
-    default :
-        *aStatus = nsIDOMOfflineResourceList::IDLE;
-        return NS_OK;
-    }
-
-    return NS_ERROR_FAILURE;
-}
-
-NS_IMETHODIMP
-nsOfflineCacheUpdate::GetPartial(PRBool *aPartial)
-{
-    *aPartial = mPartialUpdate;
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsOfflineCacheUpdate::GetManifestURI(nsIURI **aManifestURI)
-{
-    NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
-
-    NS_IF_ADDREF(*aManifestURI = mManifestURI);
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsOfflineCacheUpdate::GetSucceeded(PRBool *aSucceeded)
-{
-    NS_ENSURE_TRUE(mState == STATE_FINISHED, NS_ERROR_NOT_AVAILABLE);
-
-    *aSucceeded = mSucceeded;
-
-    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, PRUint32 aType)
-{
-    NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
-
-    if (mState >= STATE_DOWNLOADING)
-        return NS_ERROR_NOT_AVAILABLE;
-
-    // Resource URIs must have the same scheme as the manifest.
-    nsCAutoString scheme;
-    aURI->GetScheme(scheme);
-
-    PRBool match;
-    if (NS_FAILED(mManifestURI->SchemeIs(scheme.get(), &match)) || !match)
-        return NS_ERROR_FAILURE;
-
-    // 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,
-                                     mPreviousApplicationCache, mClientID,
-                                     aType);
-    if (!item) return NS_ERROR_OUT_OF_MEMORY;
-
-    mItems.AppendElement(item);
-    mAddedItems = PR_TRUE;
-
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsOfflineCacheUpdate::AddDynamicURI(nsIURI *aURI)
-{
-    // 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
-nsOfflineCacheUpdate::GetCount(PRUint32 *aNumItems)
-{
-    LOG(("nsOfflineCacheUpdate::GetNumItems [%p, num=%d]",
-         this, mItems.Length()));
-
-    NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
-
-    *aNumItems = mItems.Length();
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsOfflineCacheUpdate::Item(PRUint32 aIndex, nsIDOMLoadStatus **aItem)
-{
-    LOG(("nsOfflineCacheUpdate::GetItems [%p, index=%d]", this, aIndex));
-
-    NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
-
-    if (aIndex < mItems.Length())
-        NS_IF_ADDREF(*aItem = mItems.ElementAt(aIndex));
-    else
-        *aItem = nsnull;
-
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsOfflineCacheUpdate::AddObserver(nsIOfflineCacheUpdateObserver *aObserver,
-                                  PRBool aHoldWeak)
-{
-    LOG(("nsOfflineCacheUpdate::AddObserver [%p]", this));
-
-    NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
-
-    if (aHoldWeak) {
-        nsCOMPtr<nsIWeakReference> weakRef = do_GetWeakReference(aObserver);
-        mWeakObservers.AppendObject(weakRef);
-    } else {
-        mObservers.AppendObject(aObserver);
-    }
-
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsOfflineCacheUpdate::RemoveObserver(nsIOfflineCacheUpdateObserver *aObserver)
-{
-    LOG(("nsOfflineCacheUpdate::RemoveObserver [%p]", this));
-
-    NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
-
-    for (PRInt32 i = 0; i < mWeakObservers.Count(); i++) {
-        nsCOMPtr<nsIOfflineCacheUpdateObserver> observer =
-            do_QueryReferent(mWeakObservers[i]);
-        if (observer == aObserver) {
-            mWeakObservers.RemoveObjectAt(i);
-            return NS_OK;
-        }
-    }
-
-    for (PRInt32 i = 0; i < mObservers.Count(); i++) {
-        if (mObservers[i] == aObserver) {
-            mObservers.RemoveObjectAt(i);
-            return NS_OK;
-        }
-    }
-
-    return NS_OK;
-}
-
-
-NS_IMETHODIMP
-nsOfflineCacheUpdate::Schedule()
-{
-    LOG(("nsOfflineCacheUpdate::Schedule [%p]", this));
-
-    nsOfflineCacheUpdateService* service =
-        nsOfflineCacheUpdateService::EnsureService();
-
-    if (!service) {
-        return NS_ERROR_FAILURE;
-    }
-
-    return service->Schedule(this);
-}
-
-//-----------------------------------------------------------------------------
 // nsOfflineCachePendingUpdate
 //-----------------------------------------------------------------------------
 
 class nsOfflineCachePendingUpdate : public nsIWebProgressListener
                                   , public nsSupportsWeakReference
 {
 public:
     NS_DECL_ISUPPORTS
@@ -2279,17 +204,17 @@ nsOfflineCachePendingUpdate::OnStateChan
 
     LOG(("nsOfflineCachePendingUpdate::OnStateChange [%p, doc=%p]",
          this, progressDoc.get()));
 
     // Only schedule the update if the document loaded successfully
     if (NS_SUCCEEDED(aStatus)) {
         nsCOMPtr<nsIOfflineCacheUpdate> update;
         mService->Schedule(mManifestURI, mDocumentURI,
-                           updateDoc, getter_AddRefs(update));
+                           updateDoc, window, getter_AddRefs(update));
     }
 
     aWebProgress->RemoveProgressListener(this);
     NS_RELEASE_THIS();
 
     return NS_OK;
 }
 
@@ -2400,34 +325,24 @@ nsOfflineCacheUpdateService::EnsureServi
         nsCOMPtr<nsIOfflineCacheUpdateService> service =
             do_GetService(NS_OFFLINECACHEUPDATESERVICE_CONTRACTID);
     }
 
     return gOfflineCacheUpdateService;
 }
 
 nsresult
-nsOfflineCacheUpdateService::Schedule(nsOfflineCacheUpdate *aUpdate)
+nsOfflineCacheUpdateService::ScheduleUpdate(nsOfflineCacheUpdate *aUpdate)
 {
     LOG(("nsOfflineCacheUpdateService::Schedule [%p, update=%p]",
          this, aUpdate));
 
     aUpdate->SetOwner(this);
 
-    nsCOMPtr<nsIObserverService> observerService =
-      mozilla::services::GetObserverService();
-    if (!observerService)
-      return NS_ERROR_FAILURE;
-
-    observerService->NotifyObservers(static_cast<nsIOfflineCacheUpdate*>(aUpdate),
-                                     "offline-cache-update-added",
-                                     nsnull);
-
     mUpdates.AppendElement(aUpdate);
-
     ProcessNextUpdate();
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsOfflineCacheUpdateService::ScheduleOnDocumentStop(nsIURI *aManifestURI,
                                                     nsIURI *aDocumentURI,
@@ -2466,25 +381,16 @@ nsOfflineCacheUpdateService::UpdateFinis
     NS_ASSERTION(mUpdates.Length() > 0 &&
                  mUpdates[0] == aUpdate, "Unknown update completed");
 
     // keep this item alive until we're done notifying observers
     nsRefPtr<nsOfflineCacheUpdate> update = mUpdates[0];
     mUpdates.RemoveElementAt(0);
     mUpdateRunning = PR_FALSE;
 
-    nsCOMPtr<nsIObserverService> observerService =
-      mozilla::services::GetObserverService();
-    if (!observerService)
-      return NS_ERROR_FAILURE;
-
-    observerService->NotifyObservers(static_cast<nsIOfflineCacheUpdate*>(aUpdate),
-                                     "offline-cache-update-completed",
-                                     nsnull);
-
     ProcessNextUpdate();
 
     return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // nsOfflineCacheUpdateService <private>
 //-----------------------------------------------------------------------------
@@ -2533,79 +439,88 @@ nsOfflineCacheUpdateService::GetUpdate(P
     } else {
         *aUpdate = nsnull;
     }
 
     return NS_OK;
 }
 
 nsresult
-nsOfflineCacheUpdateService::Schedule(nsIURI *aManifestURI,
-                                      nsIURI *aDocumentURI,
-                                      nsIDOMDocument *aDocument,
-                                      nsIOfflineCacheUpdate **aUpdate)
+nsOfflineCacheUpdateService::FindUpdate(nsIURI *aManifestURI,
+                                        nsIURI *aDocumentURI,
+                                        nsOfflineCacheUpdate **aUpdate)
 {
-    // Check for existing updates
     nsresult rv;
+
+    nsRefPtr<nsOfflineCacheUpdate> update;
     for (PRUint32 i = 0; i < mUpdates.Length(); i++) {
-        nsRefPtr<nsOfflineCacheUpdate> update = mUpdates[i];
+        update = mUpdates[i];
 
         PRBool partial;
         rv = update->GetPartial(&partial);
         NS_ENSURE_SUCCESS(rv, rv);
 
         if (partial) {
             // Partial updates aren't considered
             continue;
         }
 
         nsCOMPtr<nsIURI> manifestURI;
         update->GetManifestURI(getter_AddRefs(manifestURI));
         if (manifestURI) {
             PRBool equals;
             rv = manifestURI->Equals(aManifestURI, &equals);
             if (equals) {
-                if (aDocument) {
-                    LOG(("Document %p added to update %p", aDocument, update.get()));
-                    update->AddDocument(aDocument);
-                }
-                NS_ADDREF(*aUpdate = update);
+                update.swap(*aUpdate);
                 return NS_OK;
             }
         }
     }
 
-    // There is no existing update, start one.
-
-    nsRefPtr<nsOfflineCacheUpdate> update = new nsOfflineCacheUpdate();
-    if (!update)
-        return NS_ERROR_OUT_OF_MEMORY;
+    return NS_ERROR_NOT_AVAILABLE;
+}
 
-    rv = update->Init(aManifestURI, aDocumentURI);
-    NS_ENSURE_SUCCESS(rv, rv);
+nsresult
+nsOfflineCacheUpdateService::Schedule(nsIURI *aManifestURI,
+                                      nsIURI *aDocumentURI,
+                                      nsIDOMDocument *aDocument,
+                                      nsIDOMWindow* aWindow,
+                                      nsIOfflineCacheUpdate **aUpdate)
+{
+    nsCOMPtr<nsIOfflineCacheUpdate> update;
+#ifdef MOZ_IPC
+    if (GeckoProcessType_Default != XRE_GetProcessType()) {
+        update = new OfflineCacheUpdateChild(aWindow);
+    }
+    else
+#endif
+    {
+        update = new OfflineCacheUpdateGlue();
+    }
 
-    if (aDocument) {
-        LOG(("First document %p added to update %p", aDocument, update.get()));
-        update->AddDocument(aDocument);
-    }
+    nsresult rv;
+
+    rv = update->Init(aManifestURI, aDocumentURI, aDocument);
+    NS_ENSURE_SUCCESS(rv, rv);
 
     rv = update->Schedule();
     NS_ENSURE_SUCCESS(rv, rv);
 
     NS_ADDREF(*aUpdate = update);
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsOfflineCacheUpdateService::ScheduleUpdate(nsIURI *aManifestURI,
                                             nsIURI *aDocumentURI,
+                                            nsIDOMWindow *aWindow,
                                             nsIOfflineCacheUpdate **aUpdate)
 {
-    return Schedule(aManifestURI, aDocumentURI, nsnull, aUpdate);
+    return Schedule(aManifestURI, aDocumentURI, nsnull, aWindow, aUpdate);
 }
 
 //-----------------------------------------------------------------------------
 // nsOfflineCacheUpdateService::nsIObserver
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 nsOfflineCacheUpdateService::Observe(nsISupports     *aSubject,