Bug 756717 - Implement 'appcache jar' for apps, r=tlee+jduell
authorHonza Bambas <honzab.moz@firemni.cz>
Tue, 31 Jul 2012 02:36:00 -0400
changeset 114335 293dee8a857a56ea4650835ac067e91787694373
parent 114334 0fc339d6174c02a1b8a52ebea1df63a6d74fd9e7
child 114336 051d7855b93f05a6a5b94367d699ae529e8548c0
push id1708
push userakeybl@mozilla.com
push dateMon, 19 Nov 2012 21:10:21 +0000
treeherdermozilla-beta@27b14fe50103 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstlee
bugs756717
milestone18.0a1
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 756717 - Implement 'appcache jar' for apps, r=tlee+jduell
content/base/src/nsContentSink.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/tests/mochitest/ajax/offline/foreign2.html
dom/tests/mochitest/ajax/offline/offlineTests.js
dom/tests/mochitest/ajax/offline/test_bug460353.html
dom/tests/mochitest/ajax/offline/test_fallback.html
dom/tests/mochitest/ajax/offline/test_foreign.html
netwerk/base/public/nsIApplicationCache.idl
netwerk/base/public/nsIApplicationCacheService.idl
netwerk/cache/nsApplicationCacheService.cpp
netwerk/cache/nsApplicationCacheService.h
netwerk/cache/nsDiskCacheDeviceSQL.cpp
netwerk/cache/nsDiskCacheDeviceSQL.h
netwerk/protocol/http/nsHttpChannel.cpp
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/nsIOfflineCacheUpdate.idl
uriloader/prefetch/nsOfflineCacheUpdate.cpp
uriloader/prefetch/nsOfflineCacheUpdate.h
uriloader/prefetch/nsOfflineCacheUpdateService.cpp
--- a/content/base/src/nsContentSink.cpp
+++ b/content/base/src/nsContentSink.cpp
@@ -881,22 +881,18 @@ nsContentSink::SelectDocAppCache(nsIAppl
   *aAction = CACHE_SELECTION_NONE;
 
   nsCOMPtr<nsIApplicationCacheContainer> applicationCacheDocument =
     do_QueryInterface(mDocument);
   NS_ASSERTION(applicationCacheDocument,
                "mDocument must implement nsIApplicationCacheContainer.");
 
   if (aLoadApplicationCache) {
-    nsAutoCString groupID;
-    rv = aLoadApplicationCache->GetGroupID(groupID);
-    NS_ENSURE_SUCCESS(rv, rv);
-
     nsCOMPtr<nsIURI> groupURI;
-    rv = NS_NewURI(getter_AddRefs(groupURI), groupID);
+    rv = aLoadApplicationCache->GetManifestURI(getter_AddRefs(groupURI));
     NS_ENSURE_SUCCESS(rv, rv);
 
     bool equal = false;
     rv = groupURI->Equals(aManifestURI, &equal);
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (!equal) {
       // This is a foreign entry, force a reload to avoid loading the foreign
@@ -970,21 +966,17 @@ nsContentSink::SelectDocAppCacheNoManife
         ("Selection, no manifest: assigning app cache %s to document %s", clientID.get(), docURISpec.get()));
 #endif
 
     rv = applicationCacheDocument->SetApplicationCache(aLoadApplicationCache);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Return the uri and invoke the update process for the selected
     // application cache.
-    nsAutoCString groupID;
-    rv = aLoadApplicationCache->GetGroupID(groupID);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = NS_NewURI(aManifestURI, groupID);
+    rv = aLoadApplicationCache->GetManifestURI(aManifestURI);
     NS_ENSURE_SUCCESS(rv, rv);
 
     *aAction = CACHE_SELECTION_UPDATE;
   }
 
   return NS_OK;
 }
 
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -225,17 +225,18 @@ parent:
      *   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(URIParams manifestURI, URIParams documentURI,
-                        nsCString clientID, bool stickDocument);
+                        bool isInBrowserElement, uint32_t appId,
+                        bool stickDocument);
 
     sync PIndexedDB(nsCString asciiOrigin)
         returns (bool allowed);
 
     /**
      * window.open from inside <iframe mozbrowser> is special.  When the child
      * process calls window.open, it creates a new PBrowser (in its own
      * process), then calls BrowserFrameOpenWindow on it.
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -1172,19 +1172,20 @@ TabChild::RecvActivateFrameEvent(const n
   nsRefPtr<ContentListener> listener = new ContentListener(this);
   NS_ENSURE_TRUE(listener, true);
   chromeHandler->AddEventListener(aType, listener, capture);
   return true;
 }
 
 POfflineCacheUpdateChild*
 TabChild::AllocPOfflineCacheUpdate(const URIParams& manifestURI,
-            const URIParams& documentURI,
-            const nsCString& clientID,
-            const bool& stickDocument)
+                                   const URIParams& documentURI,
+                                   const bool& isInBrowserElement,
+                                   const uint32_t& appId,
+                                   const bool& stickDocument)
 {
   NS_RUNTIMEABORT("unused");
   return nullptr;
 }
 
 bool
 TabChild::DeallocPOfflineCacheUpdate(POfflineCacheUpdateChild* actor)
 {
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -248,19 +248,21 @@ public:
       child->mIPCOpen = true;
       return request;
     }
 #endif /* DEBUG */
 
     virtual PContentPermissionRequestChild* AllocPContentPermissionRequest(const nsCString& aType, const IPC::Principal& aPrincipal);
     virtual bool DeallocPContentPermissionRequest(PContentPermissionRequestChild* actor);
 
-    virtual POfflineCacheUpdateChild* AllocPOfflineCacheUpdate(const URIParams& manifestURI,
+    virtual POfflineCacheUpdateChild* AllocPOfflineCacheUpdate(
+            const URIParams& manifestURI,
             const URIParams& documentURI,
-            const nsCString& clientID,
+            const bool& isInBrowserElement,
+            const uint32_t& appId,
             const bool& stickDocument);
     virtual bool DeallocPOfflineCacheUpdate(POfflineCacheUpdateChild* offlineCacheUpdate);
 
     nsIWebNavigation* WebNavigation() { return mWebNav; }
 
     JSContext* GetJSContext() { return mCx; }
 
     nsIPrincipal* GetPrincipal() { return mPrincipal; }
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -1023,24 +1023,25 @@ TabParent::DeallocPRenderFrame(PRenderFr
 {
   delete aFrame;
   return true;
 }
 
 mozilla::docshell::POfflineCacheUpdateParent*
 TabParent::AllocPOfflineCacheUpdate(const URIParams& aManifestURI,
                                     const URIParams& aDocumentURI,
-                                    const nsCString& aClientID,
+                                    const bool& isInBrowserElement,
+                                    const uint32_t& appId,
                                     const bool& stickDocument)
 {
   nsRefPtr<mozilla::docshell::OfflineCacheUpdateParent> update =
     new mozilla::docshell::OfflineCacheUpdateParent();
 
-  nsresult rv = update->Schedule(aManifestURI, aDocumentURI, aClientID,
-                                 stickDocument);
+  nsresult rv = update->Schedule(aManifestURI, aDocumentURI,
+                                 isInBrowserElement, appId, stickDocument);
   if (NS_FAILED(rv))
     return nullptr;
 
   POfflineCacheUpdateParent* result = update.get();
   update.forget();
   return result;
 }
 
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -176,17 +176,18 @@ public:
 
     virtual PContentPermissionRequestParent*
     AllocPContentPermissionRequest(const nsCString& aType, const IPC::Principal& aPrincipal);
     virtual bool DeallocPContentPermissionRequest(PContentPermissionRequestParent* actor);
 
     virtual POfflineCacheUpdateParent* AllocPOfflineCacheUpdate(
             const URIParams& aManifestURI,
             const URIParams& aDocumentURI,
-            const nsCString& aClientID,
+            const bool& isInBrowserElement,
+            const uint32_t& appId,
             const bool& stickDocument);
     virtual bool DeallocPOfflineCacheUpdate(POfflineCacheUpdateParent* actor);
 
     JSBool GetGlobalJSObject(JSContext* cx, JSObject** globalp);
 
     NS_DECL_ISUPPORTS
     NS_DECL_NSIAUTHPROMPTPROVIDER
     NS_DECL_NSISECUREBROWSERUI
--- a/dom/src/offline/nsDOMOfflineResourceList.cpp
+++ b/dom/src/offline/nsDOMOfflineResourceList.cpp
@@ -768,18 +768,26 @@ nsresult
 nsDOMOfflineResourceList::CacheKeys()
 {
   if (IS_CHILD_PROCESS()) 
     return NS_ERROR_NOT_IMPLEMENTED;
 
   if (mCachedKeys)
     return NS_OK;
 
+  nsCOMPtr<nsIDOMWindow> window = do_QueryInterface(GetOwner());
+  nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(window);
+  nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(webNav);
+
+  nsAutoCString groupID;
+  mApplicationCacheService->BuildGroupID(
+      mManifestURI, loadContext, groupID);
+
   nsCOMPtr<nsIApplicationCache> appCache;
-  mApplicationCacheService->GetActiveCache(mManifestSpec,
+  mApplicationCacheService->GetActiveCache(groupID,
                                            getter_AddRefs(appCache));
 
   if (!appCache) {
     return NS_ERROR_DOM_INVALID_STATE_ERR;
   }
 
   return appCache->GatherEntries(nsIApplicationCache::ITEM_DYNAMIC,
                                  &mCachedKeysCount, &mCachedKeys);
--- a/dom/tests/mochitest/ajax/offline/foreign2.html
+++ b/dom/tests/mochitest/ajax/offline/foreign2.html
@@ -9,45 +9,45 @@
 <script class="testbody" type="text/javascript">
 
 function manifestUpdated()
 {
   var appCacheService = SpecialPowers.Components.classes["@mozilla.org/network/application-cache-service;1"]
     .getService(SpecialPowers.Ci.nsIApplicationCacheService);
 
   var foreign2cache = appCacheService.chooseApplicationCache(
-    "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/foreign2.html");
+    "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/foreign2.html", OfflineTest.loadContext());
 
   OfflineTest.ok(foreign2cache, "Foreign 2 cache present, chosen for foreign2.html");
-  OfflineTest.is(foreign2cache.groupID, "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/foreign2.cacheManifest")
+  OfflineTest.is(foreign2cache.manifestURI.asciiSpec, "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/foreign2.cacheManifest")
 
-  var foreign1cache = appCacheService.getActiveCache(
+  var foreign1cache = OfflineTest.getActiveCache(
     "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/foreign1.cacheManifest");
   OfflineTest.ok(foreign1cache, "Foreign 1 cache loaded");
   foreign1cache.discard();
 
   OfflineTest.teardown();
   OfflineTest.finish();
 }
 
 function onLoaded()
 {
   var appCacheService = SpecialPowers.Components.classes["@mozilla.org/network/application-cache-service;1"]
     .getService(SpecialPowers.Ci.nsIApplicationCacheService);
 
-  var foreign1cache = appCacheService.getActiveCache(
+  var foreign1cache = OfflineTest.getActiveCache(
     "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/foreign1.cacheManifest");
   OfflineTest.ok(foreign1cache, "Foreign 1 cache loaded");
 
-  var foreign2cache = appCacheService.getActiveCache(
+  var foreign2cache = OfflineTest.getActiveCache(
     "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/foreign2.cacheManifest");
   OfflineTest.ok(!foreign2cache, "Foreign 2 cache not present");
 
   foreign1cache = appCacheService.chooseApplicationCache(
-    "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/foreign2.html");
+    "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/foreign2.html", OfflineTest.loadContext());
   OfflineTest.ok(!foreign1cache, "foreign2.html not chosen from foreign1 cache");
 
   try
   {
     OfflineTest.ok(applicationCache.status == SpecialPowers.Ci.nsIDOMOfflineResourceList.UNCACHED,
         "there is no associated application cache");
   }
   catch (ex)
--- a/dom/tests/mochitest/ajax/offline/offlineTests.js
+++ b/dom/tests/mochitest/ajax/offline/offlineTests.js
@@ -230,28 +230,43 @@ waitForAdd: function(url, onFinished) {
     cacheSession.asyncOpenCacheEntry(url,
                                      Ci.nsICache.ACCESS_READ,
                                      waitForAddListener);
   }
 
   setTimeout(this.priv(waitFunc), 500);
 },
 
-getManifestUrl: function()
+manifestURL: function(overload)
 {
-  return window.top.document.documentElement.getAttribute("manifest");
+  var manifestURLspec = overload || window.top.document.documentElement.getAttribute("manifest");
+
+  var ios = Cc["@mozilla.org/network/io-service;1"]
+            .getService(Ci.nsIIOService)
+
+  var baseURI = ios.newURI(window.location.href, null, null);
+  return ios.newURI(manifestURLspec, null, baseURI);
 },
 
-getActiveCache: function()
+loadContext: function()
+{
+  return SpecialPowers.wrap(window).QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+                                   .getInterface(Components.interfaces.nsIWebNavigation)
+                                   .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+                                   .getInterface(Components.interfaces.nsILoadContext);
+},
+
+getActiveCache: function(overload)
 {
   // Note that this is the current active cache in the cache stack, not the
   // one associated with this window.
   var serv = Cc["@mozilla.org/network/application-cache-service;1"]
              .getService(Ci.nsIApplicationCacheService);
-  return serv.getActiveCache(this.getManifestUrl());
+  var groupID = serv.buildGroupID(this.manifestURL(overload), this.loadContext());
+  return serv.getActiveCache(groupID);
 },
 
 getActiveSession: function()
 {
   var cache = this.getActiveCache();
   if (!cache)
     return null;
 
@@ -266,33 +281,16 @@ priv: function(func)
 {
   var self = this;
   return function() {
     netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
     func(arguments);
   }
 },
 
-checkCustomCache: function(group, url, expectEntry, callback)
-{
-  var serv = Cc["@mozilla.org/network/application-cache-service;1"]
-             .getService(Ci.nsIApplicationCacheService);
-  var cache = serv.getActiveCache(group);
-  var cacheSession = null;
-  if (cache) {
-    var cacheService = Cc["@mozilla.org/network/cache-service;1"]
-                       .getService(Ci.nsICacheService);
-    cacheSession = cacheService.createSession(cache.clientID,
-                                      Ci.nsICache.STORE_OFFLINE,
-                                      true);
-  }
-
-  this._checkCache(cacheSession, url, expectEntry, callback);
-},
-
 checkCacheEntries: function(entries, callback)
 {
   var checkNextEntry = function() {
     if (entries.length == 0) {
       setTimeout(OfflineTest.priv(callback), 0);
     } else {
       OfflineTest.checkCache(entries[0][0], entries[0][1], checkNextEntry);
       entries.shift();
--- a/dom/tests/mochitest/ajax/offline/test_bug460353.html
+++ b/dom/tests/mochitest/ajax/offline/test_bug460353.html
@@ -6,16 +6,17 @@
   This test checks that each iframe creates its own
   scope. Actually, we just check that it loads and updates
   its associated cache. There is no check that the cache is the
   expected one, there is no API to gain that information.
 -->
 
 <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+<script type="text/javascript" src="/tests/dom/tests/mochitest/ajax/offline/offlineTests.js"></script>
 
 <script class="testbody" type="text/javascript">
 
 var result = new Array();
 var expectedUpdates = 2;
 
 init();
 
@@ -91,22 +92,17 @@ function finish()
 
   SimpleTest.finish();
 }
 
 function cleanCache(manifestURL)
 {
   netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
 
-  var Cc = Components.classes;
-  var Ci = Components.interfaces;
-
-  var serv = Cc["@mozilla.org/network/application-cache-service;1"]
-             .getService(Ci.nsIApplicationCacheService);
-  var cache = serv.getActiveCache(manifestURL);
+  var cache = OfflineTest.getActiveCache(manifestURL);
   if (cache)
     cache.discard();
 }
 
 SimpleTest.waitForExplicitFinish();
 
 </script>
 
--- a/dom/tests/mochitest/ajax/offline/test_fallback.html
+++ b/dom/tests/mochitest/ajax/offline/test_fallback.html
@@ -121,11 +121,11 @@ if (OfflineTest.setup()) {
   applicationCache.oncached = OfflineTest.priv(manifestUpdated);
 }
 
 </script>
 
 </head>
 
 <body>
-<iframe name="fallbackFrame" src="http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/namespace1/non-existing.html"></iframe>
+<iframe name="fallbackFrame" src=""></iframe>
 </body>
 </html>
--- a/dom/tests/mochitest/ajax/offline/test_foreign.html
+++ b/dom/tests/mochitest/ajax/offline/test_foreign.html
@@ -20,20 +20,20 @@
  */
 
 function manifestUpdated()
 {
   var appCacheService = SpecialPowers.Components.classes["@mozilla.org/network/application-cache-service;1"]
     .getService(SpecialPowers.Ci.nsIApplicationCacheService);
 
   foreign1cache = appCacheService.chooseApplicationCache(
-    "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/foreign2.html");
+    "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/foreign2.html", OfflineTest.loadContext());
 
   OfflineTest.ok(foreign1cache, "foreign2.html chosen from foreign1 cache");
-  OfflineTest.is(foreign1cache.groupID, "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/foreign1.cacheManifest")
+  OfflineTest.is(foreign1cache.manifestURI.asciiSpec, "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/foreign1.cacheManifest")
 
   window.location = "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/foreign2.html";
 }
 
 SimpleTest.waitForExplicitFinish();
 
 if (OfflineTest.setup()) {
   applicationCache.onerror = OfflineTest.failEvent;
--- a/netwerk/base/public/nsIApplicationCache.idl
+++ b/netwerk/base/public/nsIApplicationCache.idl
@@ -3,16 +3,17 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
 interface nsIArray;
 interface nsIFile;
+interface nsIURI;
 
 /**
  * Application caches can store a set of namespace entries that affect
  * loads from the application cache.  If a load from the cache fails
  * to match an exact cache entry, namespaces entries will be searched
  * for a substring match, and should be applied appropriately.
  */
 [scriptable, uuid(96e4c264-2065-4ce9-93bb-43734c62c4eb)]
@@ -74,17 +75,17 @@ 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(C3A17414-763B-4235-8BB7-B48324F95DF8)]
+[scriptable, uuid(06568DAE-C374-4383-A122-0CC96C7177F2)]
 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);
@@ -114,18 +115,23 @@ interface nsIApplicationCache : nsISuppo
     /* This item was listed as a fallback entry. */
     const unsigned long ITEM_FALLBACK = 1 << 5;
 
     /* This item matched an opportunistic cache namespace and was
      * cached accordingly. */
     const unsigned long ITEM_OPPORTUNISTIC = 1 << 6;
 
     /**
-     * The group ID for this cache group.  This is the URI of the
-     * manifest file.
+     * URI of the manfiest specifying this application cache.
+     **/
+    readonly attribute nsIURI manifestURI;
+
+    /**
+     * The group ID for this cache group.  It is an internally generated string
+     * and cannot be used as manifest URL spec.
      **/
     readonly attribute ACString groupID;
 
     /**
      * The client ID for this application cache.  Clients can open a
      * session with nsICacheService::createSession() using this client
      * ID and a storage policy of STORE_OFFLINE to access this cache.
      */
--- a/netwerk/base/public/nsIApplicationCacheService.idl
+++ b/netwerk/base/public/nsIApplicationCacheService.idl
@@ -3,25 +3,34 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
 interface nsIApplicationCache;
 interface nsIFile;
+interface nsIURI;
+interface nsILoadContext;
 
 /**
  * The application cache service manages the set of application cache
  * groups.
  */
-[scriptable, uuid(28adfdc7-6718-4b3e-bdb2-ecfefa3c8910)]
+[scriptable, uuid(F94DB1CC-AB56-480b-8F86-40748878557E)]
 interface nsIApplicationCacheService : nsISupports
 {
     /**
+     * Create group string identifying cache group according the manifest
+     * URL and the given load context.
+     */
+    ACString buildGroupID(in nsIURI aManifestURL,
+                          in nsILoadContext aLoadContext);
+
+    /**
      * Create a new, empty application cache for the given cache
      * group.
      */
     nsIApplicationCache createApplicationCache(in ACString group);
 
     /**
      * Create a new, empty application cache for the given cache
      * group residing in a custom directory with a custom quota.
@@ -51,17 +60,18 @@ interface nsIApplicationCacheService : n
     /**
      * Deactivate the currently-active cache object for a cache group.
      */
     void deactivateGroup(in ACString group);
 
     /**
      * Try to find the best application cache to serve a resource.
      */
-    nsIApplicationCache chooseApplicationCache(in ACString key);
+    nsIApplicationCache chooseApplicationCache(in ACString key,
+                                               [optional] in nsILoadContext loadContext);
 
     /**
      * Flags the key as being opportunistically cached.
      *
      * This method should also propagate the entry to other
      * application caches with the same opportunistic namespace, but
      * this is not currently implemented.
      *
--- a/netwerk/cache/nsApplicationCacheService.cpp
+++ b/netwerk/cache/nsApplicationCacheService.cpp
@@ -17,16 +17,28 @@ NS_IMPL_ISUPPORTS1(nsApplicationCacheSer
 
 nsApplicationCacheService::nsApplicationCacheService()
 {
     nsCOMPtr<nsICacheService> serv = do_GetService(kCacheServiceCID);
     mCacheService = nsCacheService::GlobalInstance();
 }
 
 NS_IMETHODIMP
+nsApplicationCacheService::BuildGroupID(nsIURI *aManifestURL,
+                                        nsILoadContext *aLoadContext,
+                                        nsACString &_result)
+{
+    nsresult rv = nsOfflineCacheDevice::BuildApplicationCacheGroupID(
+        aManifestURL, aLoadContext, _result);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
 nsApplicationCacheService::CreateApplicationCache(const nsACString &group,
                                                   nsIApplicationCache **out)
 {
     if (!mCacheService)
         return NS_ERROR_UNEXPECTED;
 
     nsRefPtr<nsOfflineCacheDevice> device;
     nsresult rv = mCacheService->GetOfflineDevice(getter_AddRefs(device));
@@ -86,25 +98,27 @@ nsApplicationCacheService::DeactivateGro
     nsRefPtr<nsOfflineCacheDevice> device;
     nsresult rv = mCacheService->GetOfflineDevice(getter_AddRefs(device));
     NS_ENSURE_SUCCESS(rv, rv);
     return device->DeactivateGroup(group);
 }
 
 NS_IMETHODIMP
 nsApplicationCacheService::ChooseApplicationCache(const nsACString &key,
+                                                  nsILoadContext *aLoadContext,
                                                   nsIApplicationCache **out)
 {
     if (!mCacheService)
         return NS_ERROR_UNEXPECTED;
 
     nsRefPtr<nsOfflineCacheDevice> device;
     nsresult rv = mCacheService->GetOfflineDevice(getter_AddRefs(device));
     NS_ENSURE_SUCCESS(rv, rv);
-    return device->ChooseApplicationCache(key, out);
+
+    return device->ChooseApplicationCache(key, aLoadContext, out);
 }
 
 NS_IMETHODIMP
 nsApplicationCacheService::CacheOpportunistically(nsIApplicationCache* cache,
                                                   const nsACString &key)
 {
     if (!mCacheService)
         return NS_ERROR_UNEXPECTED;
--- a/netwerk/cache/nsApplicationCacheService.h
+++ b/netwerk/cache/nsApplicationCacheService.h
@@ -10,12 +10,16 @@
 class nsApplicationCacheService MOZ_FINAL : public nsIApplicationCacheService
 {
 public:
     nsApplicationCacheService();
 
     NS_DECL_ISUPPORTS
     NS_DECL_NSIAPPLICATIONCACHESERVICE
 private:
+    nsresult GetJARIdentifier(nsIURI *aURI,
+                              nsILoadContext *aLoadContext,
+                              nsACString &_result);
+
     nsRefPtr<nsCacheService> mCacheService;
 };
 
 #endif // _nsApplicationCacheService_h_
--- a/netwerk/cache/nsDiskCacheDeviceSQL.cpp
+++ b/netwerk/cache/nsDiskCacheDeviceSQL.cpp
@@ -659,16 +659,29 @@ nsApplicationCache::InitAsHandle(const n
   NS_ENSURE_TRUE(mGroup.IsEmpty(), NS_ERROR_ALREADY_INITIALIZED);
 
   mGroup = groupId;
   mClientID = clientId;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsApplicationCache::GetManifestURI(nsIURI **out)
+{
+  nsCOMPtr<nsIURI> uri;
+  nsresult rv = NS_NewURI(getter_AddRefs(uri), mGroup);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = uri->CloneIgnoringRef(out);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsApplicationCache::GetGroupID(nsACString &out)
 {
   out = mGroup;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsApplicationCache::GetClientID(nsACString &out)
@@ -1215,16 +1228,92 @@ nsOfflineCacheDevice::Init()
   }
 
   rv = InitActiveCaches();
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
+namespace {
+
+nsresult
+GetGroupForCache(const nsCSubstring &clientID, nsCString &group)
+{
+  group.Assign(clientID);
+  group.Truncate(group.FindChar('|'));
+  NS_UnescapeURL(group);
+
+  return NS_OK;
+}
+
+nsresult
+GetJARIdentifier(nsIURI *aURI,
+                 nsILoadContext *aLoadContext,
+                 nsACString &_result)
+{
+    _result.Truncate();
+
+    if (!aLoadContext)
+        return NS_OK;
+
+    // These lines are here for compatibility only.  We must not fill the
+    // JAR identifier when this is no-app context, otherwise web content
+    // offline application cache loads would not be satisfied (cache would
+    // not be found).
+    bool isInBrowserElement;
+    nsresult rv = aLoadContext->GetIsInBrowserElement(&isInBrowserElement);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    uint32_t appId;
+    rv = aLoadContext->GetAppId(&appId);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (!isInBrowserElement && appId == NECKO_NO_APP_ID)
+        return NS_OK;
+
+    // This load context has some special attributes, create a jar identifier
+    _result.AppendInt(appId);
+    _result.Append('+');
+    _result.Append(isInBrowserElement ? 't' : 'f');
+
+    return NS_OK;
+}
+
+} // anon namespace
+
+// static
+nsresult
+nsOfflineCacheDevice::BuildApplicationCacheGroupID(nsIURI *aManifestURL,
+                                                   nsILoadContext *aLoadContext,
+                                                   nsACString &_result)
+{
+  nsCOMPtr<nsIURI> newURI;
+  nsresult rv = aManifestURL->CloneIgnoringRef(getter_AddRefs(newURI));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsAutoCString manifestSpec;
+  rv = newURI->GetAsciiSpec(manifestSpec);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  _result.Assign(manifestSpec);
+
+  nsAutoCString jarid;
+  rv = GetJARIdentifier(aManifestURL, aLoadContext, jarid);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Include JAR ID, i.e. the extended origin if present.
+  if (!jarid.IsEmpty()) {
+    _result.Append('#');
+    _result.Append(jarid);
+  }
+
+  return NS_OK;
+}
+
 nsresult
 nsOfflineCacheDevice::InitActiveCaches()
 {
   mCaches.Init();
   mActiveCachesByGroup.Init();
 
   mActiveCaches.Init(5);
 
@@ -2269,44 +2358,59 @@ nsOfflineCacheDevice::DeactivateGroup(co
     mActiveCachesByGroup.Remove(group);
     active = nullptr;
   }
 
   return NS_OK;
 }
 
 bool
-nsOfflineCacheDevice::CanUseCache(nsIURI *keyURI, const nsCString &clientID)
+nsOfflineCacheDevice::CanUseCache(nsIURI *keyURI,
+                                  const nsACString &clientID,
+                                  nsILoadContext *loadContext)
 {
-  if (mActiveCaches.Contains(clientID)) {
-    nsAutoCString groupID;
-    nsresult rv = GetGroupForCache(clientID, groupID);
-    NS_ENSURE_SUCCESS(rv, false);
-
-    nsCOMPtr<nsIURI> groupURI;
-    rv = NS_NewURI(getter_AddRefs(groupURI), groupID);
-    if (NS_SUCCEEDED(rv)) {
-      // When we are choosing an initial cache to load the top
-      // level document from, the URL of that document must have
-      // the same origin as the manifest, according to the spec.
-      // The following check is here because explicit, fallback
-      // and dynamic entries might have origin different from the
-      // manifest origin.
-      if (NS_SecurityCompareURIs(keyURI, groupURI,
-                                 GetStrictFileOriginPolicy()))
-        return true;
-    }
-  }
-
-  return false;
+  if (!mActiveCaches.Contains(clientID))
+    return false;
+
+  nsAutoCString groupID;
+  nsresult rv = GetGroupForCache(clientID, groupID);
+  NS_ENSURE_SUCCESS(rv, false);
+
+  nsCOMPtr<nsIURI> groupURI;
+  rv = NS_NewURI(getter_AddRefs(groupURI), groupID);
+  if (NS_FAILED(rv))
+    return false;
+
+  // When we are choosing an initial cache to load the top
+  // level document from, the URL of that document must have
+  // the same origin as the manifest, according to the spec.
+  // The following check is here because explicit, fallback
+  // and dynamic entries might have origin different from the
+  // manifest origin.
+  if (!NS_SecurityCompareURIs(keyURI, groupURI,
+                              GetStrictFileOriginPolicy()))
+    return false;
+
+  // Check the groupID we found is equal to groupID based
+  // on the load context demanding load from app cache.
+  // This is check of extended origin.
+  nsAutoCString demandedGroupID;
+  rv = BuildApplicationCacheGroupID(groupURI, loadContext, demandedGroupID);
+  NS_ENSURE_SUCCESS(rv, false);
+
+  if (groupID != demandedGroupID)
+    return false;
+
+  return true;
 }
 
 
 nsresult
 nsOfflineCacheDevice::ChooseApplicationCache(const nsACString &key,
+                                             nsILoadContext *loadContext,
                                              nsIApplicationCache **out)
 {
   *out = nullptr;
 
   nsCOMPtr<nsIURI> keyURI;
   nsresult rv = NS_NewURI(getter_AddRefs(keyURI), key);
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -2324,17 +2428,17 @@ nsOfflineCacheDevice::ChooseApplicationC
     rv = statement->GetInt32(1, &itemType);
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (!(itemType & nsIApplicationCache::ITEM_FOREIGN)) {
       nsAutoCString clientID;
       rv = statement->GetUTF8String(0, clientID);
       NS_ENSURE_SUCCESS(rv, rv);
 
-      if (CanUseCache(keyURI, clientID)) {
+      if (CanUseCache(keyURI, clientID, loadContext)) {
         return GetApplicationCache(clientID, out);
       }
     }
 
     rv = statement->ExecuteStep(&hasRows);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
@@ -2356,17 +2460,17 @@ nsOfflineCacheDevice::ChooseApplicationC
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Don't associate with a cache based solely on a whitelist entry
     if (!(itemType & nsIApplicationCacheNamespace::NAMESPACE_BYPASS)) {
       nsAutoCString clientID;
       rv = nsstatement->GetUTF8String(0, clientID);
       NS_ENSURE_SUCCESS(rv, rv);
 
-      if (CanUseCache(keyURI, clientID)) {
+      if (CanUseCache(keyURI, clientID, loadContext)) {
         return GetApplicationCache(clientID, out);
       }
     }
 
     rv = nsstatement->ExecuteStep(&hasRows);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
@@ -2423,27 +2527,16 @@ nsOfflineCacheDevice::ActivateCache(cons
 bool
 nsOfflineCacheDevice::IsActiveCache(const nsCSubstring &group,
                                     const nsCSubstring &clientID)
 {
   nsCString *active = nullptr;
   return mActiveCachesByGroup.Get(group, &active) && *active == clientID;
 }
 
-nsresult
-nsOfflineCacheDevice::GetGroupForCache(const nsACString &clientID,
-                                       nsCString &out)
-{
-  out.Assign(clientID);
-  out.Truncate(out.FindChar('|'));
-  NS_UnescapeURL(out);
-
-  return NS_OK;
-}
-
 /**
  * Preference accessors
  */
 
 void
 nsOfflineCacheDevice::SetCacheParentDirectory(nsIFile *parentDir)
 {
   if (Initialized())
--- a/netwerk/cache/nsDiskCacheDeviceSQL.h
+++ b/netwerk/cache/nsDiskCacheDeviceSQL.h
@@ -127,35 +127,37 @@ public:
                                      const nsACString &       ownerURI,
                                      const nsACString &       key,
                                      bool *                 isOwned);
 
   nsresult                ClearKeysOwnedByDomain(const char *clientID,
                                                  const nsACString &ownerDomain);
   nsresult                EvictUnownedEntries(const char *clientID);
 
+  static nsresult         BuildApplicationCacheGroupID(nsIURI *aManifestURL,
+                                                       nsILoadContext *aLoadContext,
+                                                       nsACString &_result);
+
   nsresult                ActivateCache(const nsCSubstring &group,
                                         const nsCSubstring &clientID);
   bool                    IsActiveCache(const nsCSubstring &group,
                                         const nsCSubstring &clientID);
-  nsresult                GetGroupForCache(const nsCSubstring &clientID,
-                                           nsCString &out);
-
   nsresult                CreateApplicationCache(const nsACString &group,
                                                  nsIApplicationCache **out);
 
   nsresult                GetApplicationCache(const nsACString &clientID,
                                               nsIApplicationCache **out);
 
   nsresult                GetActiveCache(const nsACString &group,
                                          nsIApplicationCache **out);
 
   nsresult                DeactivateGroup(const nsACString &group);
 
   nsresult                ChooseApplicationCache(const nsACString &key,
+                                                 nsILoadContext *loadContext,
                                                  nsIApplicationCache **out);
 
   nsresult                CacheOpportunistically(nsIApplicationCache* cache,
                                                  const nsACString &key);
 
   nsresult                GetGroups(uint32_t *count,char ***keys);
 
   nsresult                GetGroupsTimeOrdered(uint32_t *count,
@@ -194,17 +196,17 @@ private:
   nsresult InitActiveCaches();
   nsresult UpdateEntry(nsCacheEntry *entry);
   nsresult UpdateEntrySize(nsCacheEntry *entry, uint32_t newSize);
   nsresult DeleteEntry(nsCacheEntry *entry, bool deleteData);
   nsresult DeleteData(nsCacheEntry *entry);
   nsresult EnableEvictionObserver();
   nsresult DisableEvictionObserver();
 
-  bool CanUseCache(nsIURI *keyURI, const nsCString &clientID);
+  bool CanUseCache(nsIURI *keyURI, const nsACString &clientID, nsILoadContext *loadContext);
 
   nsresult MarkEntry(const nsCString &clientID,
                      const nsACString &key,
                      uint32_t typeBits);
   nsresult UnmarkEntry(const nsCString &clientID,
                        const nsACString &key,
                        uint32_t typeBits);
 
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -2417,18 +2417,24 @@ nsHttpChannel::OpenCacheEntry(bool using
     if (!mApplicationCache &&
         (mChooseApplicationCache || (mLoadFlags & LOAD_CHECK_OFFLINE_CACHE))) {
         // We're supposed to load from an application cache, but
         // one was not supplied by the load group.  Ask the
         // application cache service to choose one for us.
         nsCOMPtr<nsIApplicationCacheService> appCacheService =
             do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID);
         if (appCacheService) {
+            nsCOMPtr<nsILoadContext> loadContext;
+            GetCallback(loadContext);
+
+            if (!loadContext)
+                LOG(("  no load context while choosing application cache"));
+
             nsresult rv = appCacheService->ChooseApplicationCache
-                (cacheKey, getter_AddRefs(mApplicationCache));
+                (cacheKey, loadContext, getter_AddRefs(mApplicationCache));
             NS_ENSURE_SUCCESS(rv, rv);
         }
     }
 
     nsCOMPtr<nsICacheSession> session;
 
     // If we have an application cache, we check it first.
     if (mApplicationCache) {
--- a/uriloader/prefetch/OfflineCacheUpdateChild.cpp
+++ b/uriloader/prefetch/OfflineCacheUpdateChild.cpp
@@ -175,17 +175,18 @@ OfflineCacheUpdateChild::AssociateDocume
 //-----------------------------------------------------------------------------
 // OfflineCacheUpdateChild::nsIOfflineCacheUpdate
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 OfflineCacheUpdateChild::Init(nsIURI *aManifestURI,
                               nsIURI *aDocumentURI,
                               nsIDOMDocument *aDocument,
-                              nsIFile *aCustomProfileDir)
+                              nsIFile *aCustomProfileDir,
+                              nsILoadContext *aLoadContext)
 {
     nsresult rv;
 
     // Make sure the service has been initialized
     nsOfflineCacheUpdateService* service =
         nsOfflineCacheUpdateService::EnsureService();
     if (!service)
         return NS_ERROR_FAILURE;
@@ -216,16 +217,18 @@ OfflineCacheUpdateChild::Init(nsIURI *aM
 
     mDocumentURI = aDocumentURI;
 
     mState = STATE_INITIALIZED;
 
     if (aDocument)
         SetDocument(aDocument);
 
+    mLoadContext = aLoadContext;
+
     return NS_OK;
 }
 
 NS_IMETHODIMP
 OfflineCacheUpdateChild::InitPartial(nsIURI *aManifestURI,
                                   const nsACString& clientID,
                                   nsIURI *aDocumentURI)
 {
@@ -406,21 +409,30 @@ OfflineCacheUpdateChild::Schedule()
     // 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 != nullptr; 
 
+    // Carry load context to the parent
+    bool isInBrowserElement = false;
+    uint32_t appId = NECKO_NO_APP_ID;
+    if (mLoadContext) {
+        mLoadContext->GetIsInBrowserElement(&isInBrowserElement);
+        mLoadContext->GetAppId(&appId);
+    }
+
     // 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, manifestURI, documentURI,
-                                              mClientID, stickDocument);
+                                              isInBrowserElement, appId,
+                                              stickDocument);
 
     mIPCActivated = true;
     this->AddRef();
 
     return NS_OK;
 }
 
 bool
--- a/uriloader/prefetch/OfflineCacheUpdateChild.h
+++ b/uriloader/prefetch/OfflineCacheUpdateChild.h
@@ -67,19 +67,18 @@ private:
     bool mIsUpgrade;
     bool mSucceeded;
     bool mIPCActivated;
 
     nsCString mUpdateDomain;
     nsCOMPtr<nsIURI> mManifestURI;
     nsCOMPtr<nsIURI> mDocumentURI;
 
-    nsCString mClientID;
-
     nsCOMPtr<nsIObserverService> mObserverService;
+    nsCOMPtr<nsILoadContext> mLoadContext;
 
     /* Clients watching this update for changes */
     nsCOMArray<nsIWeakReference> mWeakObservers;
     nsCOMArray<nsIOfflineCacheUpdateObserver> mObservers;
 
     /* Document that requested this update */
     nsCOMPtr<nsIDOMDocument> mDocument;
 
--- a/uriloader/prefetch/OfflineCacheUpdateGlue.cpp
+++ b/uriloader/prefetch/OfflineCacheUpdateGlue.cpp
@@ -87,27 +87,28 @@ OfflineCacheUpdateGlue::Schedule()
 
     return mUpdate->Schedule();
 }
 
 NS_IMETHODIMP
 OfflineCacheUpdateGlue::Init(nsIURI *aManifestURI, 
                              nsIURI *aDocumentURI,
                              nsIDOMDocument *aDocument,
-                             nsIFile *aCustomProfileDir)
+                             nsIFile *aCustomProfileDir,
+                             nsILoadContext *aLoadContext)
 {
     if (!EnsureUpdate())
         return NS_ERROR_NULL_POINTER;
 
     mDocumentURI = aDocumentURI;
 
     if (aDocument)
         SetDocument(aDocument);
 
-    return mUpdate->Init(aManifestURI, aDocumentURI, nullptr, aCustomProfileDir);
+    return mUpdate->Init(aManifestURI, aDocumentURI, nullptr, aCustomProfileDir, aLoadContext);
 }
 
 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");
--- a/uriloader/prefetch/OfflineCacheUpdateGlue.h
+++ b/uriloader/prefetch/OfflineCacheUpdateGlue.h
@@ -44,19 +44,20 @@ public:
 
 private:
     nsIOfflineCacheUpdate* EnsureUpdate();
 
 public:
     NS_ADJUSTED_FORWARD_NSIOFFLINECACHEUPDATE(EnsureUpdate())
     NS_IMETHOD Schedule(void);
     NS_IMETHOD Init(nsIURI *aManifestURI, 
-                                  nsIURI *aDocumentURI, 
-                                  nsIDOMDocument *aDocument,
-                                  nsIFile *aCustomProfileDir);
+                    nsIURI *aDocumentURI,
+                    nsIDOMDocument *aDocument,
+                    nsIFile *aCustomProfileDir,
+                    nsILoadContext *aLoadContext);
 
     NS_DECL_NSIOFFLINECACHEUPDATEOBSERVER
 
     OfflineCacheUpdateGlue();
     ~OfflineCacheUpdateGlue();
 
     void SetDocument(nsIDOMDocument *aDocument);
 
--- a/uriloader/prefetch/OfflineCacheUpdateParent.cpp
+++ b/uriloader/prefetch/OfflineCacheUpdateParent.cpp
@@ -18,28 +18,30 @@ using namespace mozilla::ipc;
 //    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
+#undef LOG
 #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)
+NS_IMPL_ISUPPORTS2(OfflineCacheUpdateParent,
+                   nsIOfflineCacheUpdateObserver,
+                   nsILoadContext)
 
 //-----------------------------------------------------------------------------
 // OfflineCacheUpdateParent <public>
 //-----------------------------------------------------------------------------
 
 OfflineCacheUpdateParent::OfflineCacheUpdateParent()
     : mIPCClosed(false)
 {
@@ -61,43 +63,48 @@ void
 OfflineCacheUpdateParent::ActorDestroy(ActorDestroyReason why)
 {
     mIPCClosed = true;
 }
 
 nsresult
 OfflineCacheUpdateParent::Schedule(const URIParams& aManifestURI,
                                    const URIParams& aDocumentURI,
-                                   const nsCString& aClientID,
+                                   const bool& isInBrowserElement,
+                                   const uint32_t& appId,
                                    const bool& stickDocument)
 {
     LOG(("OfflineCacheUpdateParent::RecvSchedule [%p]", this));
 
+    // Load context members
+    mIsInBrowserElement = isInBrowserElement;
+    mAppId = appId;
+
     nsRefPtr<nsOfflineCacheUpdate> update;
     nsCOMPtr<nsIURI> manifestURI = DeserializeURI(aManifestURI);
     if (!manifestURI)
         return NS_ERROR_FAILURE;
 
     nsCOMPtr<nsIURI> documentURI = DeserializeURI(aDocumentURI);
     if (!documentURI)
         return NS_ERROR_FAILURE;
 
     nsOfflineCacheUpdateService* service =
         nsOfflineCacheUpdateService::EnsureService();
     if (!service)
         return NS_ERROR_FAILURE;
 
-    service->FindUpdate(manifestURI, documentURI, getter_AddRefs(update));
+    service->FindUpdate(manifestURI, this, 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, nullptr, nullptr);
+        rv = update->Init(manifestURI, documentURI, nullptr, nullptr, this);
         NS_ENSURE_SUCCESS(rv, rv);
 
         rv = update->Schedule();
         NS_ENSURE_SUCCESS(rv, rv);
     }
 
     update->AddObserver(this, false);
 
@@ -149,10 +156,69 @@ OfflineCacheUpdateParent::ApplicationCac
     aApplicationCache->GetClientID(cacheClientId);
     nsCString cacheGroupId;
     aApplicationCache->GetGroupID(cacheGroupId);
 
     SendAssociateDocuments(cacheGroupId, cacheClientId);
     return NS_OK;
 }
 
+//-----------------------------------------------------------------------------
+// OfflineCacheUpdateParent::nsILoadContext
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+OfflineCacheUpdateParent::GetAssociatedWindow(nsIDOMWindow * *aAssociatedWindow)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+OfflineCacheUpdateParent::GetTopWindow(nsIDOMWindow * *aTopWindow)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+OfflineCacheUpdateParent::GetTopFrameElement(nsIDOMElement** aElement)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+OfflineCacheUpdateParent::IsAppOfType(uint32_t appType, bool *_retval)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+OfflineCacheUpdateParent::GetIsContent(bool *aIsContent)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+OfflineCacheUpdateParent::GetUsePrivateBrowsing(bool *aUsePrivateBrowsing)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+NS_IMETHODIMP
+OfflineCacheUpdateParent::SetUsePrivateBrowsing(bool aUsePrivateBrowsing)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+OfflineCacheUpdateParent::GetIsInBrowserElement(bool *aIsInBrowserElement)
+{
+    *aIsInBrowserElement = mIsInBrowserElement;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+OfflineCacheUpdateParent::GetAppId(uint32_t *aAppId)
+{
+    *aAppId = mAppId;
+    return NS_OK;
+}
+
 } // docshell
 } // mozilla
--- a/uriloader/prefetch/OfflineCacheUpdateParent.h
+++ b/uriloader/prefetch/OfflineCacheUpdateParent.h
@@ -5,46 +5,53 @@
 
 #ifndef nsOfflineCacheUpdateParent_h
 #define nsOfflineCacheUpdateParent_h
 
 #include "mozilla/docshell/POfflineCacheUpdateParent.h"
 #include "nsIOfflineCacheUpdate.h"
 
 #include "nsString.h"
+#include "nsILoadContext.h"
 
 namespace mozilla {
 
 namespace ipc {
 class URIParams;
 } // namespace ipc
 
 namespace docshell {
 
 class OfflineCacheUpdateParent : public POfflineCacheUpdateParent
                                , public nsIOfflineCacheUpdateObserver
+                               , public nsILoadContext
 {
     typedef mozilla::ipc::URIParams URIParams;
 
 public:
     NS_DECL_ISUPPORTS
     NS_DECL_NSIOFFLINECACHEUPDATEOBSERVER
+    NS_DECL_NSILOADCONTEXT
 
     nsresult
     Schedule(const URIParams& manifestURI,
              const URIParams& documentURI,
-             const nsCString& clientID,
+             const bool& isInBrowserElement,
+             const uint32_t& appId,
              const bool& stickDocument);
 
     OfflineCacheUpdateParent();
     ~OfflineCacheUpdateParent();
 
     virtual void ActorDestroy(ActorDestroyReason why);
 
 private:
     void RefcountHitZero();
     bool mIPCClosed;
+
+    bool     mIsInBrowserElement;
+    uint32_t mAppId;
 };
 
 } // namespace docshell
 } // namespace mozilla
 
 #endif
--- a/uriloader/prefetch/nsIOfflineCacheUpdate.idl
+++ b/uriloader/prefetch/nsIOfflineCacheUpdate.idl
@@ -10,16 +10,17 @@ interface nsIDOMWindow;
 interface nsIDOMNode;
 interface nsIDOMDocument;
 interface nsIDOMLoadStatus;
 interface nsIOfflineCacheUpdate;
 interface nsIPrincipal;
 interface nsIPrefBranch;
 interface nsIApplicationCache;
 interface nsIFile;
+interface nsILoadContext;
 
 [scriptable, uuid(47360d57-8ef4-4a5d-8865-1a27a739ad1a)]
 interface nsIOfflineCacheUpdateObserver : nsISupports {
   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;
@@ -57,17 +58,17 @@ interface nsIOfflineCacheUpdateObserver 
  * 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(2FA574B8-AE62-426b-BE95-08E6AA957455)]
+[scriptable, uuid(D47966B2-1EBC-45c5-8639-2937ED200281)]
 interface nsIOfflineCacheUpdate : nsISupports {
   /**
    * Fetch the status of the running update.  This will return a value
    * defined in nsIDOMOfflineResourceList.
    */
   readonly attribute unsigned short status;
 
   /**
@@ -102,17 +103,18 @@ 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, in nsIDOMDocument aDocument,
-            [optional] in nsIFile aCustomProfileDir);
+            [optional] in nsIFile aCustomProfileDir,
+            [optional] in nsILoadContext aLoadContext);
 
   /**
    * 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
@@ -232,10 +234,8 @@ interface nsIOfflineCacheUpdateService :
      * @param aPrefBranch
      *        The pref branch to use to check the
      *        offline-apps.allow_by_default pref.  If not specified,
      *        the pref service will be used.
      */
     boolean offlineAppAllowedForURI(in nsIURI aURI,
                                     in nsIPrefBranch aPrefBranch);
 };
-
-
--- a/uriloader/prefetch/nsOfflineCacheUpdate.cpp
+++ b/uriloader/prefetch/nsOfflineCacheUpdate.cpp
@@ -1172,17 +1172,18 @@ nsOfflineCacheUpdate::GetCacheKey(nsIURI
 
     return NS_OK;
 }
 
 nsresult
 nsOfflineCacheUpdate::Init(nsIURI *aManifestURI,
                            nsIURI *aDocumentURI,
                            nsIDOMDocument *aDocument,
-                           nsIFile *aCustomProfileDir)
+                           nsIFile *aCustomProfileDir,
+                           nsILoadContext *aLoadContext)
 {
     nsresult rv;
 
     // Make sure the service has been initialized
     nsOfflineCacheUpdateService* service =
         nsOfflineCacheUpdateService::EnsureService();
     if (!service)
         return NS_ERROR_FAILURE;
@@ -1203,59 +1204,64 @@ nsOfflineCacheUpdate::Init(nsIURI *aMani
             return NS_ERROR_ABORT;
     }
 
     mManifestURI = aManifestURI;
 
     rv = mManifestURI->GetAsciiHost(mUpdateDomain);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    nsAutoCString manifestSpec;
-
-    rv = GetCacheKey(mManifestURI, manifestSpec);
+    nsCOMPtr<nsIApplicationCacheService> cacheService =
+        do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
 
     mDocumentURI = aDocumentURI;
 
-    nsCOMPtr<nsIApplicationCacheService> cacheService =
-        do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
+    if (aCustomProfileDir) {
+        rv = GetCacheKey(aManifestURI, mGroupID);
+        NS_ENSURE_SUCCESS(rv, rv);
 
-    if (aCustomProfileDir) {
         // Create only a new offline application cache in the custom profile
         // This is a preload of a new cache.
 
         // XXX Custom updates don't support "updating" of an existing cache
         // in the custom profile at the moment.  This support can be, though,
         // simply added as well when needed.
         mPreviousApplicationCache = nullptr;
 
-        rv = cacheService->CreateCustomApplicationCache(manifestSpec,
+        rv = cacheService->CreateCustomApplicationCache(mGroupID,
                                                         aCustomProfileDir,
                                                         kCustomProfileQuota,
                                                         getter_AddRefs(mApplicationCache));
         NS_ENSURE_SUCCESS(rv, rv);
 
         mCustomProfileDir = aCustomProfileDir;
     }
     else {
-        rv = cacheService->GetActiveCache(manifestSpec,
+        rv = cacheService->BuildGroupID(aManifestURI,
+                                        aLoadContext,
+                                        mGroupID);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        rv = cacheService->GetActiveCache(mGroupID,
                                           getter_AddRefs(mPreviousApplicationCache));
         NS_ENSURE_SUCCESS(rv, rv);
 
-        rv = cacheService->CreateApplicationCache(manifestSpec,
+        rv = cacheService->CreateApplicationCache(mGroupID,
                                                   getter_AddRefs(mApplicationCache));
         NS_ENSURE_SUCCESS(rv, rv);
     }
 
     rv = nsOfflineCacheUpdateService::OfflineAppPinnedForURI(aDocumentURI,
                                                              NULL,
                                                              &mPinned);
     NS_ENSURE_SUCCESS(rv, rv);
 
+    mLoadContext = aLoadContext;
+
     mState = STATE_INITIALIZED;
     return NS_OK;
 }
 
 nsresult
 nsOfflineCacheUpdate::InitPartial(nsIURI *aManifestURI,
                                   const nsACString& clientID,
                                   nsIURI *aDocumentURI)
@@ -1290,23 +1296,23 @@ nsOfflineCacheUpdate::InitPartial(nsIURI
         rv = GetCacheKey(mManifestURI, manifestSpec);
         NS_ENSURE_SUCCESS(rv, rv);
 
         rv = cacheService->CreateApplicationCache
             (manifestSpec, getter_AddRefs(mApplicationCache));
         NS_ENSURE_SUCCESS(rv, rv);
     }
 
+    rv = mApplicationCache->GetManifestURI(getter_AddRefs(mManifestURI));
+    NS_ENSURE_SUCCESS(rv, rv);
+
     nsAutoCString groupID;
     rv = mApplicationCache->GetGroupID(groupID);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    rv = NS_NewURI(getter_AddRefs(mManifestURI), groupID);
-    NS_ENSURE_SUCCESS(rv, rv);
-
     rv = nsOfflineCacheUpdateService::OfflineAppPinnedForURI(aDocumentURI,
                                                              NULL,
                                                              &mPinned);
     NS_ENSURE_SUCCESS(rv, rv);
 
     mState = STATE_INITIALIZED;
     return NS_OK;
 }
@@ -1561,17 +1567,18 @@ nsOfflineCacheUpdate::ManifestCheckCompl
         // from a new update through this dead update to them is absolutely
         // correct.
         FinishNoNotify();
 
         nsRefPtr<nsOfflineCacheUpdate> newUpdate =
             new nsOfflineCacheUpdate();
         // Leave aDocument argument null. Only glues and children keep
         // document instances.
-        newUpdate->Init(mManifestURI, mDocumentURI, nullptr, mCustomProfileDir);
+        newUpdate->Init(mManifestURI, mDocumentURI, nullptr,
+                        mCustomProfileDir, mLoadContext);
 
         // 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 (int32_t i = 0; i < mDocumentURIs.Count(); i++) {
             newUpdate->StickDocument(mDocumentURIs[i]);
         }
 
@@ -1825,16 +1832,22 @@ nsOfflineCacheUpdate::StickDocument(nsIU
 
 void
 nsOfflineCacheUpdate::SetOwner(nsOfflineCacheUpdateOwner *aOwner)
 {
     NS_ASSERTION(!mOwner, "Tried to set cache update owner twice.");
     mOwner = aOwner;
 }
 
+bool
+nsOfflineCacheUpdate::IsForGroupID(const nsCSubstring &groupID)
+{
+    return mGroupID == groupID;
+}
+
 nsresult
 nsOfflineCacheUpdate::UpdateFinished(nsOfflineCacheUpdate *aUpdate)
 {
     // Keep the object alive through a Finish() call.
     nsCOMPtr<nsIOfflineCacheUpdate> kungFuDeathGrip(this);
 
     mImplicitUpdate = nullptr;
 
--- a/uriloader/prefetch/nsOfflineCacheUpdate.h
+++ b/uriloader/prefetch/nsOfflineCacheUpdate.h
@@ -204,16 +204,18 @@ public:
 
     void LoadCompleted(nsOfflineCacheUpdateItem *aItem);
     void ManifestCheckCompleted(nsresult aStatus,
                                 const nsCString &aManifestHash);
     void StickDocument(nsIURI *aDocumentURI);
 
     void SetOwner(nsOfflineCacheUpdateOwner *aOwner);
 
+    bool IsForGroupID(const nsCSubstring &groupID);
+
     virtual nsresult UpdateFinished(nsOfflineCacheUpdate *aUpdate);
 
 protected:
     friend class nsOfflineCacheUpdateItem;
     void OnByteProgress(uint64_t byteIncrement);
 
 private:
     nsresult HandleManifest(bool *aDoUpdate);
@@ -249,19 +251,21 @@ private:
     nsOfflineCacheUpdateOwner *mOwner;
 
     bool mAddedItems;
     bool mPartialUpdate;
     bool mSucceeded;
     bool mObsolete;
 
     nsCString mUpdateDomain;
+    nsCString mGroupID;
     nsCOMPtr<nsIURI> mManifestURI;
     nsCOMPtr<nsIURI> mDocumentURI;
     nsCOMPtr<nsIFile> mCustomProfileDir;
+    nsCOMPtr<nsILoadContext> mLoadContext;
 
     nsCOMPtr<nsIApplicationCache> mApplicationCache;
     nsCOMPtr<nsIApplicationCache> mPreviousApplicationCache;
 
     nsCOMPtr<nsIObserverService> mObserverService;
 
     nsRefPtr<nsOfflineManifestItem> mManifestItem;
 
@@ -303,17 +307,17 @@ public:
 
     nsOfflineCacheUpdateService();
     ~nsOfflineCacheUpdateService();
 
     nsresult Init();
 
     nsresult ScheduleUpdate(nsOfflineCacheUpdate *aUpdate);
     nsresult FindUpdate(nsIURI *aManifestURI,
-                        nsIURI *aDocumentURI,
+                        nsILoadContext *aLoadContext,
                         nsOfflineCacheUpdate **aUpdate);
 
     nsresult Schedule(nsIURI *aManifestURI,
                       nsIURI *aDocumentURI,
                       nsIDOMDocument *aDocument,
                       nsIDOMWindow* aWindow,
                       nsIFile* aCustomProfileDir,
                       nsIOfflineCacheUpdate **aUpdate);
--- a/uriloader/prefetch/nsOfflineCacheUpdateService.cpp
+++ b/uriloader/prefetch/nsOfflineCacheUpdateService.cpp
@@ -22,16 +22,17 @@
 #include "nsIDocumentLoader.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMWindow.h"
 #include "nsIDOMOfflineResourceList.h"
 #include "nsIDocument.h"
 #include "nsIObserverService.h"
 #include "nsIURL.h"
 #include "nsIWebProgress.h"
+#include "nsIWebNavigation.h"
 #include "nsICryptoHash.h"
 #include "nsICacheEntryDescriptor.h"
 #include "nsIPermissionManager.h"
 #include "nsIPrincipal.h"
 #include "nsNetCID.h"
 #include "nsNetUtil.h"
 #include "nsServiceManagerUtils.h"
 #include "nsStreamUtils.h"
@@ -394,43 +395,47 @@ nsOfflineCacheUpdateService::GetUpdate(u
         *aUpdate = nullptr;
     }
 
     return NS_OK;
 }
 
 nsresult
 nsOfflineCacheUpdateService::FindUpdate(nsIURI *aManifestURI,
-                                        nsIURI *aDocumentURI,
+                                        nsILoadContext *aLoadContext,
                                         nsOfflineCacheUpdate **aUpdate)
 {
     nsresult rv;
 
+    nsCOMPtr<nsIApplicationCacheService> cacheService =
+        do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsAutoCString groupID;
+    rv = cacheService->BuildGroupID(aManifestURI,
+                                    aLoadContext,
+                                    groupID);
+    NS_ENSURE_SUCCESS(rv, rv);
+
     nsRefPtr<nsOfflineCacheUpdate> update;
     for (uint32_t i = 0; i < mUpdates.Length(); i++) {
         update = mUpdates[i];
 
         bool 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) {
-            bool equals;
-            rv = manifestURI->Equals(aManifestURI, &equals);
-            if (equals) {
-                update.swap(*aUpdate);
-                return NS_OK;
-            }
+        if (update->IsForGroupID(groupID)) {
+            update.swap(*aUpdate);
+            return NS_OK;
         }
     }
 
     return NS_ERROR_NOT_AVAILABLE;
 }
 
 nsresult
 nsOfflineCacheUpdateService::Schedule(nsIURI *aManifestURI,
@@ -445,17 +450,24 @@ nsOfflineCacheUpdateService::Schedule(ns
         update = new OfflineCacheUpdateChild(aWindow);
     }
     else {
         update = new OfflineCacheUpdateGlue();
     }
 
     nsresult rv;
 
-    rv = update->Init(aManifestURI, aDocumentURI, aDocument, aCustomProfileDir);
+    nsCOMPtr<nsILoadContext> loadContext;
+    if (aWindow) {
+        nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(aWindow);
+        loadContext = do_QueryInterface(webNav);
+    }
+
+    rv = update->Init(aManifestURI, aDocumentURI, aDocument,
+                      aCustomProfileDir, loadContext);
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = update->Schedule();
     NS_ENSURE_SUCCESS(rv, rv);
 
     NS_ADDREF(*aUpdate = update);
 
     return NS_OK;