Bug 536295 - e10s HTTP: offline application cache, r=dwitte, sr=cbiesinger, a=beltzner GECKO20b7pre_20101006_RELBRANCH
authorHonza Bambas <honzab.moz@firemni.cz>
Thu, 21 Oct 2010 00:42:25 +0200
branchGECKO20b7pre_20101006_RELBRANCH
changeset 56270 12f2f0fcb48495165a658f2313eafe9c3357f2ee
parent 56243 d48d4bc3142c216dbc8862a2b4530caa30538e86
child 56276 666a6214dc7dcb25fd044720ea487616901fff25
push idunknown
push userunknown
push dateunknown
reviewersdwitte, cbiesinger, beltzner
bugs536295
milestone2.0b7pre
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=beltzner
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
@@ -5745,17 +5745,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;
 
@@ -5922,17 +5926,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);
     }
 }
@@ -8569,17 +8579,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
@@ -8088,16 +8088,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
@@ -71,16 +71,17 @@
 #include "mozilla/chrome/RegistryMessageUtils.h"
 #include "nsFrameMessageManager.h"
 
 #include "nsIGeolocationProvider.h"
 
 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"
@@ -689,16 +690,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
@@ -61,16 +61,17 @@ IPDLDIRS =  \
   ipc/ipdl/test/cxx  \
   ipc/testshell  \
   js/ipc  \
   js/jetpack \
   layout/ipc \
   netwerk/ipc  \
   netwerk/protocol/http  \
   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
 
@@ -699,16 +701,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
@@ -820,16 +823,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
@@ -948,16 +952,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
@@ -243,16 +243,31 @@ class StartRequestEvent : public ChildCh
   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,
@@ -901,16 +916,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;
@@ -925,17 +956,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
 //-----------------------------------------------------------------------------
 
@@ -1070,58 +1102,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
@@ -134,16 +134,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 GetAssociatedContentSecurity(nsIAssociatedContentSecurity** res = nsnull);
 
 private:
   RequestHeaderTuples mRequestHeaders;
   nsRefPtr<HttpChannelChild> mRedirectChannelChild;
   nsCOMPtr<nsIURI> mRedirectOriginalURI;
   nsCOMPtr<nsISupports> mSecurityInfo;
--- 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 
@@ -263,16 +302,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));
@@ -286,16 +333,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 RecvRedirect2Result(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
   Redirect2Result(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();
+
 child:
   OnStartRequest(nsHttpResponseHead  responseHead,
                  PRBool              useResponseHead,
                  RequestHeaderTuples requestHeaders,
                  PRBool              isFromCache,
                  PRBool              cacheEntryAvailable,
                  PRUint32            cacheExpirationTime,
                  nsCString           cachedCharset,
@@ -127,16 +144,20 @@ child:
   // on the content process and reports result via OnRedirect2Result above
   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);
+
 both:
   __delete__();
 };
 
 
 } // 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
@@ -67,31 +67,35 @@
 #include "nsNetUtil.h"
 #include "nsServiceManagerUtils.h"
 #include "nsStreamUtils.h"
 #include "nsThreadUtils.h"
 #include "nsProxyRelease.h"
 #include "prlog.h"
 #include "nsIAsyncVerifyRedirectCallback.h"
 
+#ifdef MOZ_IPC
+#include "nsXULAppAPI.h"
+#endif
+
 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) {};
@@ -1114,17 +1118,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 +1167,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 +1362,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 +1436,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 +1626,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 +1673,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 +1726,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 +1755,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 +1825,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 +1943,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 +1969,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 +2025,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,