Bug 814247 - Add auth cache jars for separate apps. r=mayhemer
authorJosh Matthews <josh@joshmatthews.net>
Wed, 05 Dec 2012 02:33:20 -0500
changeset 125893 b6b432b50d6106eaa349b46a7ac5e63cb4152ca7
parent 125892 93f081cda42e90544c44822c784f147385f14700
child 125894 7315385d2b38bc5b49e5c2e81aa7800a2b10ee61
push id2151
push userlsblakk@mozilla.com
push dateTue, 19 Feb 2013 18:06:57 +0000
treeherdermozilla-beta@4952e88741ec [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmayhemer
bugs814247
milestone20.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 814247 - Add auth cache jars for separate apps. r=mayhemer
dom/browser-element/mochitest/browserElement_Auth.js
dom/plugins/base/nsNPAPIPlugin.cpp
netwerk/protocol/http/nsHttpAuthCache.cpp
netwerk/protocol/http/nsHttpAuthCache.h
netwerk/protocol/http/nsHttpAuthManager.cpp
netwerk/protocol/http/nsHttpChannelAuthProvider.cpp
netwerk/protocol/http/nsIHttpAuthManager.idl
netwerk/test/unit/test_auth_jar.js
netwerk/test/unit/xpcshell.ini
--- a/dom/browser-element/mochitest/browserElement_Auth.js
+++ b/dom/browser-element/mochitest/browserElement_Auth.js
@@ -35,56 +35,126 @@ function runTest() {
 function testHttpAuthCancel(e) {
   iframe.removeEventListener("mozbrowserusernameandpasswordrequired", testHttpAuthCancel);
   // Will cancel authentication, but prompt should not be shown again. Instead,
   // we will be led to fail message
   iframe.addEventListener("mozbrowserusernameandpasswordrequired", testFail);
   iframe.addEventListener("mozbrowsertitlechange", function onTitleChange(e) {
     iframe.removeEventListener("mozbrowsertitlechange", onTitleChange);
     iframe.removeEventListener("mozbrowserusernameandpasswordrequired", testFail);
-    is(e.detail, 'http auth failed');
+    is(e.detail, 'http auth failed', 'expected authentication to fail');
     iframe.addEventListener('mozbrowserusernameandpasswordrequired', testHttpAuth);
     SimpleTest.executeSoon(function() {
       // Use absolute path because we need to specify host.
       iframe.src = 'http://test/tests/dom/browser-element/mochitest/file_http_401_response.sjs';
     });
   });
 
-  is(e.detail.realm, 'http_realm');
-  is(e.detail.host, 'http://test');
+  is(e.detail.realm, 'http_realm', 'expected realm matches');
+  is(e.detail.host, 'http://test', 'expected host matches');
   e.preventDefault();
 
   SimpleTest.executeSoon(function() {
     e.detail.cancel();
   });
 }
 
 function testHttpAuth(e) {
   iframe.removeEventListener("mozbrowserusernameandpasswordrequired", testHttpAuth);
 
   // Will authenticate with correct password, prompt should not be
   // called again.
   iframe.addEventListener("mozbrowserusernameandpasswordrequired", testFail);
   iframe.addEventListener("mozbrowsertitlechange", function onTitleChange(e) {
     iframe.removeEventListener("mozbrowsertitlechange", onTitleChange);
     iframe.removeEventListener("mozbrowserusernameandpasswordrequired", testFail);
-    is(e.detail, 'http auth success');
-    SimpleTest.executeSoon(testFinish);
+    is(e.detail, 'http auth success', 'expect authentication to succeed');
+    SimpleTest.executeSoon(testAuthJarNoInterfere);
   });
 
-  is(e.detail.realm, 'http_realm');
-  is(e.detail.host, 'http://test');
+  is(e.detail.realm, 'http_realm', 'expected realm matches');
+  is(e.detail.host, 'http://test', 'expected host matches');
   e.preventDefault();
 
   SimpleTest.executeSoon(function() {
     e.detail.authenticate("httpuser", "httppass");
   });
 }
 
+function testAuthJarNoInterfere(e) {
+  var authMgr = SpecialPowers.Cc['@mozilla.org/network/http-auth-manager;1']
+    .getService(SpecialPowers.Ci.nsIHttpAuthManager);
+  var secMan = SpecialPowers.Cc["@mozilla.org/scriptsecuritymanager;1"]
+               .getService(SpecialPowers.Ci.nsIScriptSecurityManager);
+  var ioService = SpecialPowers.Cc["@mozilla.org/network/io-service;1"]
+                  .getService(SpecialPowers.Ci.nsIIOService);
+  var uri = ioService.newURI("http://test/tests/dom/browser-element/mochitest/file_http_401_response.sjs", null, null);
+
+  // Set a bunch of auth data that should not conflict with the correct auth data already
+  // stored in the cache.
+  var principal = secMan.getAppCodebasePrincipal(uri, 1, false);
+  authMgr.setAuthIdentity('http', 'test', -1, 'basic', 'http_realm',
+                          'tests/dom/browser-element/mochitest/file_http_401_response.sjs',
+                          '', 'httpuser', 'wrongpass', false, principal);
+  principal = secMan.getAppCodebasePrincipal(uri, 1, true);
+  authMgr.setAuthIdentity('http', 'test', -1, 'basic', 'http_realm',
+                          'tests/dom/browser-element/mochitest/file_http_401_response.sjs',
+                          '', 'httpuser', 'wrongpass', false, principal);
+  principal = secMan.getAppCodebasePrincipal(uri, secMan.NO_APP_ID, false);
+  authMgr.setAuthIdentity('http', 'test', -1, 'basic', 'http_realm',
+                          'tests/dom/browser-element/mochitest/file_http_401_response.sjs',
+                          '', 'httpuser', 'wrongpass', false, principal);
+
+  // Will authenticate with correct password, prompt should not be
+  // called again.
+  iframe.addEventListener("mozbrowserusernameandpasswordrequired", testFail);
+  iframe.addEventListener("mozbrowsertitlechange", function onTitleChange(e) {
+    iframe.removeEventListener("mozbrowsertitlechange", onTitleChange);
+    iframe.removeEventListener("mozbrowserusernameandpasswordrequired", testFail);
+    is(e.detail, 'http auth success', 'expected authentication success');
+    SimpleTest.executeSoon(testAuthJarInterfere);
+  });
+
+  // Once more with feeling. Ensure that our new auth data doesn't interfere with this mozbrowser's
+  // auth data.
+  iframe.src = 'http://test/tests/dom/browser-element/mochitest/file_http_401_response.sjs';
+}
+
+function testAuthJarInterfere(e) {
+  var authMgr = SpecialPowers.Cc['@mozilla.org/network/http-auth-manager;1']
+    .getService(SpecialPowers.Ci.nsIHttpAuthManager);
+  var secMan = SpecialPowers.Cc["@mozilla.org/scriptsecuritymanager;1"]
+               .getService(SpecialPowers.Ci.nsIScriptSecurityManager);
+  var ioService = SpecialPowers.Cc["@mozilla.org/network/io-service;1"]
+                  .getService(SpecialPowers.Ci.nsIIOService);
+  var uri = ioService.newURI("http://test/tests/dom/browser-element/mochitest/file_http_401_response.sjs", null, null);
+
+  // Set some auth data that should overwrite the successful stored details.
+  var principal = secMan.getAppCodebasePrincipal(uri, secMan.NO_APP_ID, true);
+  authMgr.setAuthIdentity('http', 'test', -1, 'basic', 'http_realm',
+                          'tests/dom/browser-element/mochitest/file_http_401_response.sjs',
+                          '', 'httpuser', 'wrongpass', false, principal);
+
+  // Will authenticate with correct password, prompt should not be
+  // called again.
+  iframe.addEventListener("mozbrowserusernameandpasswordrequired", testFinish);
+  iframe.addEventListener("mozbrowsertitlechange", function onTitleChange(e) {
+    iframe.removeEventListener("mozbrowsertitlechange", onTitleChange);
+    iframe.removeEventListener("mozbrowserusernameandpasswordrequired", testFinish);
+    SimpleTest.execute(testFail);
+  });
+
+  // Once more with feeling. Ensure that our new auth data interferes with this mozbrowser's
+  // auth data.
+  iframe.src = 'http://test/tests/dom/browser-element/mochitest/file_http_401_response.sjs';
+}
+
 function testFinish() {
+  iframe.removeEventListener("mozbrowserusernameandpasswordrequired", testFinish);
+
   // Clear login information stored in password manager.
   var authMgr = SpecialPowers.Cc['@mozilla.org/network/http-auth-manager;1']
     .getService(SpecialPowers.Ci.nsIHttpAuthManager);
   authMgr.clearAll();
 
   var pwmgr = SpecialPowers.Cc["@mozilla.org/login-manager;1"]
     .getService(SpecialPowers.Ci.nsILoginManager);
   pwmgr.removeAllLogins();
--- a/dom/plugins/base/nsNPAPIPlugin.cpp
+++ b/dom/plugins/base/nsNPAPIPlugin.cpp
@@ -2766,22 +2766,26 @@ NPError NP_CALLBACK
   nsCOMPtr<nsIHttpAuthManager> authManager =
     do_GetService("@mozilla.org/network/http-auth-manager;1");
   if (!authManager)
     return NPERR_GENERIC_ERROR;
 
   bool authPrivate = false;
   GetPrivacyFromNPP(instance, &authPrivate);
 
+  nsIDocument *doc = GetDocumentFromNPP(instance);
+  NS_ENSURE_TRUE(doc, NPERR_GENERIC_ERROR);
+  nsIPrincipal *principal = doc->NodePrincipal();
+
   nsAutoString unused, uname16, pwd16;
   if (NS_FAILED(authManager->GetAuthIdentity(proto, nsDependentCString(host),
                                              port, nsDependentCString(scheme),
                                              nsDependentCString(realm),
                                              EmptyCString(), unused, uname16,
-                                             pwd16, authPrivate))) {
+                                             pwd16, authPrivate, principal))) {
     return NPERR_GENERIC_ERROR;
   }
 
   NS_ConvertUTF16toUTF8 uname8(uname16);
   NS_ConvertUTF16toUTF8 pwd8(pwd16);
 
   *username = ToNewCString(uname8);
   *ulen = *username ? uname8.Length() : 0;
--- a/netwerk/protocol/http/nsHttpAuthCache.cpp
+++ b/netwerk/protocol/http/nsHttpAuthCache.cpp
@@ -4,21 +4,30 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include <stdlib.h>
 #include "nsHttp.h"
 #include "nsHttpAuthCache.h"
 #include "nsString.h"
 #include "nsCRT.h"
 #include "prprf.h"
+#include "mozIApplicationClearPrivateDataParams.h"
+#include "nsIObserverService.h"
+#include "mozilla/Services.h"
+#include "nsNetUtil.h"
 
 static inline void
-GetAuthKey(const char *scheme, const char *host, int32_t port, nsCString &key)
+GetAuthKey(const char *scheme, const char *host, int32_t port, uint32_t appId, bool inBrowserElement, nsCString &key)
 {
-    key.Assign(scheme);
+    key.Truncate();
+    key.AppendInt(appId);
+    key.Append(':');
+    key.AppendInt(inBrowserElement);
+    key.Append(':');
+    key.Append(scheme);
     key.AppendLiteral("://");
     key.Append(host);
     key.Append(':');
     key.AppendInt(port);
 }
 
 // return true if the two strings are equal or both empty.  an empty string
 // is either null or zero length.
@@ -36,23 +45,33 @@ StrEquivalent(const PRUnichar *a, const 
 }
 
 //-----------------------------------------------------------------------------
 // nsHttpAuthCache <public>
 //-----------------------------------------------------------------------------
 
 nsHttpAuthCache::nsHttpAuthCache()
     : mDB(nullptr)
+    , mObserver(new AppDataClearObserver(this))
 {
+    nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
+    if (obsSvc) {
+        obsSvc->AddObserver(mObserver, "webapps-clear-data", false);
+    }
 }
 
 nsHttpAuthCache::~nsHttpAuthCache()
 {
     if (mDB)
         ClearAll();
+    nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
+    if (obsSvc) {
+        obsSvc->RemoveObserver(mObserver, "webapps-clear-data");
+        mObserver->mOwner = nullptr;
+    }
 }
 
 nsresult
 nsHttpAuthCache::Init()
 {
     NS_ENSURE_TRUE(!mDB, NS_ERROR_ALREADY_INITIALIZED);
 
     LOG(("nsHttpAuthCache::Init\n"));
@@ -66,73 +85,79 @@ nsHttpAuthCache::Init()
     return NS_OK;
 }
 
 nsresult
 nsHttpAuthCache::GetAuthEntryForPath(const char *scheme,
                                      const char *host,
                                      int32_t     port,
                                      const char *path,
+                                     uint32_t    appId,
+                                     bool        inBrowserElement,
                                      nsHttpAuthEntry **entry)
 {
     LOG(("nsHttpAuthCache::GetAuthEntryForPath [key=%s://%s:%d path=%s]\n",
         scheme, host, port, path));
 
     nsAutoCString key;
-    nsHttpAuthNode *node = LookupAuthNode(scheme, host, port, key);
+    nsHttpAuthNode *node = LookupAuthNode(scheme, host, port, appId, inBrowserElement, key);
     if (!node)
         return NS_ERROR_NOT_AVAILABLE;
 
     *entry = node->LookupEntryByPath(path);
     return *entry ? NS_OK : NS_ERROR_NOT_AVAILABLE;
 }
 
 nsresult
 nsHttpAuthCache::GetAuthEntryForDomain(const char *scheme,
                                        const char *host,
                                        int32_t     port,
                                        const char *realm,
+                                       uint32_t    appId,
+                                       bool        inBrowserElement,
                                        nsHttpAuthEntry **entry)
 
 {
     LOG(("nsHttpAuthCache::GetAuthEntryForDomain [key=%s://%s:%d realm=%s]\n",
         scheme, host, port, realm));
 
     nsAutoCString key;
-    nsHttpAuthNode *node = LookupAuthNode(scheme, host, port, key);
+    nsHttpAuthNode *node = LookupAuthNode(scheme, host, port, appId, inBrowserElement, key);
     if (!node)
         return NS_ERROR_NOT_AVAILABLE;
 
     *entry = node->LookupEntryByRealm(realm);
     return *entry ? NS_OK : NS_ERROR_NOT_AVAILABLE;
 }
 
 nsresult
 nsHttpAuthCache::SetAuthEntry(const char *scheme,
                               const char *host,
                               int32_t     port,
                               const char *path,
                               const char *realm,
                               const char *creds,
                               const char *challenge,
+                              uint32_t    appId,
+                              bool        inBrowserElement,
                               const nsHttpAuthIdentity *ident,
                               nsISupports *metadata)
 {
     nsresult rv;
 
     LOG(("nsHttpAuthCache::SetAuthEntry [key=%s://%s:%d realm=%s path=%s metadata=%x]\n",
         scheme, host, port, realm, path, metadata));
 
     if (!mDB) {
         rv = Init();
         if (NS_FAILED(rv)) return rv;
     }
 
     nsAutoCString key;
-    nsHttpAuthNode *node = LookupAuthNode(scheme, host, port, key);
+    nsHttpAuthNode *node = LookupAuthNode(scheme, host, port, appId, inBrowserElement, key);
 
     if (!node) {
         // create a new entry node and set the given entry
         node = new nsHttpAuthNode();
         if (!node)
             return NS_ERROR_OUT_OF_MEMORY;
         rv = node->SetAuthEntry(path, realm, creds, challenge, ident, metadata);
         if (NS_FAILED(rv))
@@ -144,23 +169,25 @@ nsHttpAuthCache::SetAuthEntry(const char
 
     return node->SetAuthEntry(path, realm, creds, challenge, ident, metadata);
 }
 
 void
 nsHttpAuthCache::ClearAuthEntry(const char *scheme,
                                 const char *host,
                                 int32_t     port,
-                                const char *realm)
+                                const char *realm,
+                                uint32_t    appId,
+                                bool        inBrowserElement)
 {
     if (!mDB)
         return;
 
     nsAutoCString key;
-    GetAuthKey(scheme, host, port, key);
+    GetAuthKey(scheme, host, port, appId, inBrowserElement, key);
     PL_HashTableRemove(mDB, key.get());
 }
 
 nsresult
 nsHttpAuthCache::ClearAll()
 {
     LOG(("nsHttpAuthCache::ClearAll\n"));
 
@@ -174,22 +201,24 @@ nsHttpAuthCache::ClearAll()
 //-----------------------------------------------------------------------------
 // nsHttpAuthCache <private>
 //-----------------------------------------------------------------------------
 
 nsHttpAuthNode *
 nsHttpAuthCache::LookupAuthNode(const char *scheme,
                                 const char *host,
                                 int32_t     port,
+                                uint32_t    appId,
+                                bool        inBrowserElement,
                                 nsCString  &key)
 {
     if (!mDB)
         return nullptr;
 
-    GetAuthKey(scheme, host, port, key);
+    GetAuthKey(scheme, host, port, appId, inBrowserElement, key);
 
     return (nsHttpAuthNode *) PL_HashTableLookup(mDB, key.get());
 }
 
 void *
 nsHttpAuthCache::AllocTable(void *self, size_t size)
 {
     return malloc(size);
@@ -227,16 +256,72 @@ nsHttpAuthCache::FreeEntry(void *self, P
 PLHashAllocOps nsHttpAuthCache::gHashAllocOps =
 {
     nsHttpAuthCache::AllocTable,
     nsHttpAuthCache::FreeTable,
     nsHttpAuthCache::AllocEntry,
     nsHttpAuthCache::FreeEntry
 };
 
+NS_IMPL_ISUPPORTS1(nsHttpAuthCache::AppDataClearObserver, nsIObserver)
+
+NS_IMETHODIMP
+nsHttpAuthCache::AppDataClearObserver::Observe(nsISupports *subject,
+                                               const char *      topic,
+                                               const PRUnichar * data_unicode)
+{
+    NS_ENSURE_TRUE(mOwner, NS_ERROR_NOT_AVAILABLE);
+
+    nsCOMPtr<mozIApplicationClearPrivateDataParams> params =
+            do_QueryInterface(subject);
+    if (!params) {
+        NS_ERROR("'webapps-clear-data' notification's subject should be a mozIApplicationClearPrivateDataParams");
+        return NS_ERROR_UNEXPECTED;
+    }
+
+    uint32_t appId;
+    bool browserOnly;
+
+    nsresult rv = params->GetAppId(&appId);
+    NS_ENSURE_SUCCESS(rv, rv);
+    rv = params->GetBrowserOnly(&browserOnly);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    MOZ_ASSERT(appId != NECKO_UNKNOWN_APP_ID);
+    mOwner->ClearAppData(appId, browserOnly);
+    return NS_OK;
+}
+
+static int
+RemoveEntriesForApp(PLHashEntry *entry, int32_t number, void *arg)
+{
+    nsDependentCString key(static_cast<const char*>(entry->key));
+    nsAutoCString* prefix = static_cast<nsAutoCString*>(arg);
+    if (StringBeginsWith(key, *prefix)) {
+        return HT_ENUMERATE_NEXT | HT_ENUMERATE_REMOVE;
+    }
+    return HT_ENUMERATE_NEXT;
+}
+
+void
+nsHttpAuthCache::ClearAppData(uint32_t appId, bool browserOnly)
+{
+    if (!mDB) {
+        return;
+    }
+    nsAutoCString keyPrefix;
+    keyPrefix.AppendInt(appId);
+    keyPrefix.Append(':');
+    if (browserOnly) {
+        keyPrefix.AppendInt(browserOnly);
+        keyPrefix.Append(':');
+    }
+    PL_HashTableEnumerateEntries(mDB, RemoveEntriesForApp, &keyPrefix);
+}
+
 //-----------------------------------------------------------------------------
 // nsHttpAuthIdentity
 //-----------------------------------------------------------------------------
 
 nsresult
 nsHttpAuthIdentity::Set(const PRUnichar *domain,
                         const PRUnichar *user,
                         const PRUnichar *pass)
--- a/netwerk/protocol/http/nsHttpAuthCache.h
+++ b/netwerk/protocol/http/nsHttpAuthCache.h
@@ -10,16 +10,18 @@
 #include "nsError.h"
 #include "nsTArray.h"
 #include "nsAutoPtr.h"
 #include "nsAString.h"
 #include "nsString.h"
 #include "nsCOMPtr.h"
 #include "plhash.h"
 #include "nsCRT.h"
+#include "nsIObserver.h"
+
 
 
 struct nsHttpAuthPath {
     struct nsHttpAuthPath *mNext;
     char                   mPath[1];
 };
 
 //-----------------------------------------------------------------------------
@@ -175,61 +177,83 @@ public:
 
     // |scheme|, |host|, and |port| are required
     // |path| can be null
     // |entry| is either null or a weak reference
     nsresult GetAuthEntryForPath(const char *scheme,
                                  const char *host,
                                  int32_t     port,
                                  const char *path,
+                                 uint32_t    appId,
+                                 bool        inBrowserElement,
                                  nsHttpAuthEntry **entry);
 
     // |scheme|, |host|, and |port| are required
     // |realm| must not be null
     // |entry| is either null or a weak reference
     nsresult GetAuthEntryForDomain(const char *scheme,
                                    const char *host,
                                    int32_t     port,
                                    const char *realm,
+                                   uint32_t    appId,
+                                   bool        inBrowserElement,
                                    nsHttpAuthEntry **entry);
 
     // |scheme|, |host|, and |port| are required
     // |path| can be null
     // |realm| must not be null
     // if |credentials|, |user|, |pass|, and |challenge| are each
     // null, then the entry is deleted.
     nsresult SetAuthEntry(const char *scheme,
                           const char *host,
                           int32_t     port,
                           const char *directory,
                           const char *realm,
                           const char *credentials,
                           const char *challenge,
+                          uint32_t    appId,
+                          bool        inBrowserElement,
                           const nsHttpAuthIdentity *ident,
                           nsISupports *metadata);
 
     void ClearAuthEntry(const char *scheme,
                         const char *host,
                         int32_t     port,
-                        const char *realm);
+                        const char *realm,
+                        uint32_t    appId,
+                        bool        inBrowserElement);
 
     // expire all existing auth list entries including proxy auths. 
     nsresult ClearAll();
 
 private:
     nsHttpAuthNode *LookupAuthNode(const char *scheme,
                                    const char *host,
                                    int32_t     port,
+                                   uint32_t    appId,
+                                   bool        inBrowserElement,
                                    nsCString  &key);
 
     // hash table allocation functions
     static void*        AllocTable(void *, size_t size);
     static void         FreeTable(void *, void *item);
     static PLHashEntry* AllocEntry(void *, const void *key);
     static void         FreeEntry(void *, PLHashEntry *he, unsigned flag);
 
     static PLHashAllocOps gHashAllocOps;
+
+    class AppDataClearObserver : public nsIObserver {
+    public:
+      NS_DECL_ISUPPORTS
+      NS_DECL_NSIOBSERVER
+      AppDataClearObserver(nsHttpAuthCache* aOwner) : mOwner(aOwner) {}
+      virtual ~AppDataClearObserver() {}
+      nsHttpAuthCache* mOwner;
+    };
+
+    void ClearAppData(uint32_t appId, bool browserOnly);
     
 private:
     PLHashTable *mDB; // "host:port" --> nsHttpAuthNode
+    nsRefPtr<AppDataClearObserver> mObserver;
 };
 
 #endif // nsHttpAuthCache_h__
--- a/netwerk/protocol/http/nsHttpAuthManager.cpp
+++ b/netwerk/protocol/http/nsHttpAuthManager.cpp
@@ -3,16 +3,17 @@
  * 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 "nsHttpHandler.h"
 #include "nsHttpChannel.h"
 #include "nsHttpAuthManager.h"
 #include "nsReadableUtils.h"
 #include "nsNetUtil.h"
+#include "nsIPrincipal.h"
 
 NS_IMPL_ISUPPORTS1(nsHttpAuthManager, nsIHttpAuthManager)
 
 nsHttpAuthManager::nsHttpAuthManager()
 {
 }
 
 nsresult nsHttpAuthManager::Init()
@@ -51,32 +52,42 @@ nsHttpAuthManager::GetAuthIdentity(const
                                    const nsACString & aHost,
                                    int32_t aPort,
                                    const nsACString & aAuthType,
                                    const nsACString & aRealm,
                                    const nsACString & aPath,
                                    nsAString & aUserDomain,
                                    nsAString & aUserName,
                                    nsAString & aUserPassword,
-                                   bool aIsPrivate)
+                                   bool aIsPrivate,
+                                   nsIPrincipal* aPrincipal)
 {
   nsHttpAuthCache* auth_cache = aIsPrivate ? mPrivateAuthCache : mAuthCache;
   nsHttpAuthEntry * entry = nullptr;
   nsresult rv;
+  uint32_t appId = NECKO_NO_APP_ID;
+  bool inBrowserElement = false;
+  if (aPrincipal) {
+    appId = aPrincipal->GetAppId();
+    inBrowserElement = aPrincipal->GetIsInBrowserElement();
+  }
+
   if (!aPath.IsEmpty())
     rv = auth_cache->GetAuthEntryForPath(PromiseFlatCString(aScheme).get(),
                                          PromiseFlatCString(aHost).get(),
                                          aPort,
                                          PromiseFlatCString(aPath).get(),
+                                         appId, inBrowserElement,
                                          &entry);
   else
     rv = auth_cache->GetAuthEntryForDomain(PromiseFlatCString(aScheme).get(),
                                            PromiseFlatCString(aHost).get(),
                                            aPort,
                                            PromiseFlatCString(aRealm).get(),
+                                           appId, inBrowserElement,
                                            &entry);
 
   if (NS_FAILED(rv))
     return rv;
   if (!entry)
     return NS_ERROR_UNEXPECTED;
 
   aUserDomain.Assign(entry->Domain());
@@ -90,30 +101,39 @@ nsHttpAuthManager::SetAuthIdentity(const
                                    const nsACString & aHost,
                                    int32_t aPort,
                                    const nsACString & aAuthType,
                                    const nsACString & aRealm,
                                    const nsACString & aPath,
                                    const nsAString & aUserDomain,
                                    const nsAString & aUserName,
                                    const nsAString & aUserPassword,
-                                   bool aIsPrivate)
+                                   bool aIsPrivate,
+                                   nsIPrincipal* aPrincipal)
 {
   nsHttpAuthIdentity ident(PromiseFlatString(aUserDomain).get(),
                            PromiseFlatString(aUserName).get(),
                            PromiseFlatString(aUserPassword).get());
 
+  uint32_t appId = NECKO_NO_APP_ID;
+  bool inBrowserElement = false;
+  if (aPrincipal) {
+    appId = aPrincipal->GetAppId();
+    inBrowserElement = aPrincipal->GetIsInBrowserElement();
+  }
+
   nsHttpAuthCache* auth_cache = aIsPrivate ? mPrivateAuthCache : mAuthCache;
   return auth_cache->SetAuthEntry(PromiseFlatCString(aScheme).get(),
                                   PromiseFlatCString(aHost).get(),
                                   aPort,
                                   PromiseFlatCString(aPath).get(),
                                   PromiseFlatCString(aRealm).get(),
                                   nullptr,  // credentials
                                   nullptr,  // challenge
+                                  appId, inBrowserElement,
                                   &ident,
                                   nullptr); // metadata
 }
 
 NS_IMETHODIMP
 nsHttpAuthManager::ClearAll()
 {
   nsresult rv = mAuthCache->ClearAll();
--- a/netwerk/protocol/http/nsHttpChannelAuthProvider.cpp
+++ b/netwerk/protocol/http/nsHttpChannelAuthProvider.cpp
@@ -15,16 +15,33 @@
 #include "nsEscape.h"
 #include "nsAuthInformationHolder.h"
 #include "nsIStringBundle.h"
 #include "nsIPrompt.h"
 #include "nsIAuthModule.h"
 #include "nsIDNSService.h"
 #include "nsNetCID.h"
 #include "nsIDNSRecord.h"
+#include "nsNetUtil.h"
+
+static void
+GetAppIdAndBrowserStatus(nsIChannel* aChan, uint32_t* aAppId, bool* aInBrowserElem)
+{
+    nsCOMPtr<nsILoadContext> loadContext;
+    if (aChan) {
+        NS_QueryNotificationCallbacks(aChan, loadContext);
+    }
+    if (!loadContext) {
+        *aAppId = NECKO_NO_APP_ID;
+        *aInBrowserElem = false;
+    } else {
+        loadContext->GetAppId(aAppId);
+        loadContext->GetIsInBrowserElement(aInBrowserElem);
+    }
+}
 
 nsHttpChannelAuthProvider::nsHttpChannelAuthProvider()
     : mAuthChannel(nullptr)
     , mIsPrivate(false)
     , mProxyAuthContinuationState(nullptr)
     , mAuthContinuationState(nullptr)
     , mProxyAuth(false)
     , mTriedProxyAuth(false)
@@ -367,25 +384,31 @@ nsHttpChannelAuthProvider::GenCredsAndSe
         0 != (authFlags & nsIHttpAuthenticator::REUSABLE_CHALLENGE);
 
     bool saveIdentity =
         0 == (generateFlags & nsIHttpAuthenticator::USING_INTERNAL_IDENTITY);
 
     // this getter never fails
     nsHttpAuthCache *authCache = gHttpHandler->AuthCache(mIsPrivate);
 
+    nsCOMPtr<nsIChannel> chan = do_QueryInterface(mAuthChannel);
+    uint32_t appId;
+    bool isInBrowserElement;
+    GetAppIdAndBrowserStatus(chan, &appId, &isInBrowserElement);
+
     // create a cache entry.  we do this even though we don't yet know that
     // these credentials are valid b/c we need to avoid prompting the user
     // more than once in case the credentials are valid.
     //
     // if the credentials are not reusable, then we don't bother sticking
     // them in the auth cache.
     rv = authCache->SetAuthEntry(scheme, host, port, directory, realm,
                                  saveCreds ? *result : nullptr,
                                  saveChallenge ? challenge : nullptr,
+                                 appId, isInBrowserElement,
                                  saveIdentity ? &ident : nullptr,
                                  sessionState);
     return rv;
 }
 
 nsresult
 nsHttpChannelAuthProvider::PrepareForAuthentication(bool proxyAuth)
 {
@@ -689,25 +712,31 @@ nsHttpChannelAuthProvider::GetCredential
         // Let explicit URL credentials pass
         // regardless of the LOAD_ANONYMOUS flag
     }
     else if ((loadFlags & nsIRequest::LOAD_ANONYMOUS) && !UsingHttpProxy()) {
         LOG(("Skipping authentication for anonymous non-proxy request\n"));
         return NS_ERROR_NOT_AVAILABLE;
     }
 
+    nsCOMPtr<nsIChannel> chan = do_QueryInterface(mAuthChannel);
+    uint32_t appId;
+    bool isInBrowserElement;
+    GetAppIdAndBrowserStatus(chan, &appId, &isInBrowserElement);
+
     //
     // if we already tried some credentials for this transaction, then
     // we need to possibly clear them from the cache, unless the credentials
     // in the cache have changed, in which case we'd want to give them a
     // try instead.
     //
     nsHttpAuthEntry *entry = nullptr;
     authCache->GetAuthEntryForDomain(scheme.get(), host, port,
-                                     realm.get(), &entry);
+                                     realm.get(), appId,
+                                     isInBrowserElement, &entry);
 
     // hold reference to the auth session state (in case we clear our
     // reference to the entry).
     nsCOMPtr<nsISupports> sessionStateGrip;
     if (entry)
         sessionStateGrip = entry->mMetaData;
 
     // for digest auth, maybe our cached nonce value simply timed out...
@@ -727,17 +756,18 @@ nsHttpChannelAuthProvider::GetCredential
     if (identityInvalid) {
         if (entry) {
             if (ident->Equals(entry->Identity())) {
                 if (!identFromURI) {
                     LOG(("  clearing bad auth cache entry\n"));
                     // ok, we've already tried this user identity, so clear the
                     // corresponding entry from the auth cache.
                     authCache->ClearAuthEntry(scheme.get(), host,
-                                              port, realm.get());
+                                              port, realm.get(),
+                                              appId, isInBrowserElement);
                     entry = nullptr;
                     ident->Clear();
                 }
             }
             else if (!identFromURI ||
                      (nsCRT::strcmp(ident->User(),
                                     entry->Identity().User()) == 0 &&
                      !(loadFlags &
@@ -1052,20 +1082,27 @@ NS_IMETHODIMP nsHttpChannelAuthProvider:
     rv = GetAuthorizationMembers(mProxyAuth, scheme, host, port,
                                  path, ident, continuationState);
     if (NS_FAILED(rv))
         OnAuthCancelled(aContext, false);
 
     nsAutoCString realm;
     ParseRealm(mCurrentChallenge.get(), realm);
 
+    nsCOMPtr<nsIChannel> chan = do_QueryInterface(mAuthChannel);
+    uint32_t appId;
+    bool isInBrowserElement;
+    GetAppIdAndBrowserStatus(chan, &appId, &isInBrowserElement);
+
     nsHttpAuthCache *authCache = gHttpHandler->AuthCache(mIsPrivate);
     nsHttpAuthEntry *entry = nullptr;
     authCache->GetAuthEntryForDomain(scheme.get(), host, port,
-                                     realm.get(), &entry);
+                                     realm.get(), appId,
+                                     isInBrowserElement,
+                                     &entry);
 
     nsCOMPtr<nsISupports> sessionStateGrip;
     if (entry)
         sessionStateGrip = entry->mMetaData;
 
     nsAuthInformationHolder* holder =
             static_cast<nsAuthInformationHolder*>(aAuthInfo);
     if (holder) {
@@ -1287,17 +1324,23 @@ nsHttpChannelAuthProvider::SetAuthorizat
     nsISupports **continuationState;
 
     if (header == nsHttp::Proxy_Authorization) {
         continuationState = &mProxyAuthContinuationState;
     } else {
         continuationState = &mAuthContinuationState;
     }
 
-    rv = authCache->GetAuthEntryForPath(scheme, host, port, path, &entry);
+    nsCOMPtr<nsIChannel> chan = do_QueryInterface(mAuthChannel);
+    uint32_t appId;
+    bool isInBrowserElement;
+    GetAppIdAndBrowserStatus(chan, &appId, &isInBrowserElement);
+
+    rv = authCache->GetAuthEntryForPath(scheme, host, port, path,
+                                        appId, isInBrowserElement, &entry);
     if (NS_SUCCEEDED(rv)) {
         // if we are trying to add a header for origin server auth and if the
         // URL contains an explicit username, then try the given username first.
         // we only want to do this, however, if we know the URL requires auth
         // based on the presence of an auth cache entry for this URL (which is
         // true since we are here).  but, if the username from the URL matches
         // the username from the cache, then we should prefer the password
         // stored in the cache since that is most likely to be valid.
--- a/netwerk/protocol/http/nsIHttpAuthManager.idl
+++ b/netwerk/protocol/http/nsIHttpAuthManager.idl
@@ -1,30 +1,32 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* 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 nsIPrincipal;
+
 /**
  * nsIHttpAuthManager
  *
  * This service provides access to cached HTTP authentication 
  * user credentials (domain, username, password) for sites
  * visited during the current browser session.
  *
  * This interface exists to provide other HTTP stacks with the
  * ability to share HTTP authentication credentials with Necko.
  * This is currently used by the Java plugin (version 1.5 and
  * higher) to avoid duplicate authentication prompts when the
  * Java client fetches content from a HTTP site that the user
  * has already logged into.
  */
-[scriptable, uuid(1301b517-ac72-48f6-a781-70c196eaaf3d)]
+[scriptable, uuid(54f90444-c52b-4d2d-8916-c59a2bb25938)]
 interface nsIHttpAuthManager : nsISupports
 {
     /**
      * Lookup auth identity.
      *
      * @param aScheme
      *        the URL scheme (e.g., "http").  NOTE: for proxy authentication,
      *        this should be "http" (this includes authentication for CONNECT
@@ -40,27 +42,34 @@ interface nsIHttpAuthManager : nsISuppor
      * @param aPath
      *        optional string identifying auth path. empty for proxy auth.
      * @param aUserDomain
      *        return value containing user domain.
      * @param aUserName
      *        return value containing user name.
      * @param aUserPassword
      *        return value containing user password.
+     * @param aIsPrivate
+     *        whether to look up a private or public identity (they are
+     *        stored separately, for use by private browsing)
+     * @param aPrincipal
+     *        the principal from which to derive information about which
+     *        app/mozbrowser is in use for this request
      */
     void getAuthIdentity(in ACString aScheme,
                          in ACString aHost,
                          in int32_t  aPort,
                          in ACString aAuthType,
                          in ACString aRealm,
                          in ACString aPath,
                          out AString aUserDomain,
                          out AString aUserName,
                          out AString aUserPassword,
-                         [optional] in bool aIsPrivate);
+                         [optional] in bool aIsPrivate,
+                         [optional] in nsIPrincipal aPrincipal);
 
     /**
      * Store auth identity.
      *
      * @param aScheme
      *        the URL scheme (e.g., "http").  NOTE: for proxy authentication,
      *        this should be "http" (this includes authentication for CONNECT
      *        tunneling).
@@ -75,25 +84,32 @@ interface nsIHttpAuthManager : nsISuppor
      * @param aPath
      *        optional string identifying auth path. empty for proxy auth.
      * @param aUserDomain
      *        optional string containing user domain.
      * @param aUserName
      *        optional string containing user name.
      * @param aUserPassword
      *        optional string containing user password.
+     * @param aIsPrivate
+     *        whether to store a private or public identity (they are
+     *        stored separately, for use by private browsing)
+     * @param aPrincipal
+     *        the principal from which to derive information about which
+     *        app/mozbrowser is in use for this request
      */
     void setAuthIdentity(in ACString aScheme,
                          in ACString aHost,
                          in int32_t  aPort,
                          in ACString aAuthType,
                          in ACString aRealm,
                          in ACString aPath,
                          in AString  aUserDomain,
                          in AString  aUserName,
                          in AString  aUserPassword,
-                         [optional] in boolean aIsPrivate);
+                         [optional] in boolean aIsPrivate,
+                         [optional] in nsIPrincipal aPrincipal);
 
     /**
      * Clear all auth cache.
      */
     void clearAll();
 };
new file mode 100644
--- /dev/null
+++ b/netwerk/test/unit/test_auth_jar.js
@@ -0,0 +1,52 @@
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+Components.utils.import("resource://gre/modules/Services.jsm");
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+function createURI(s) {
+  let service = Components.classes["@mozilla.org/network/io-service;1"]
+                .getService(Components.interfaces.nsIIOService);
+  return service.newURI(s, null, null);
+}
+ 
+function run_test() {
+  var secMan = Cc["@mozilla.org/scriptsecuritymanager;1"].getService(Ci.nsIScriptSecurityManager);
+  const kURI1 = "http://example.com";
+  var app1 = secMan.getAppCodebasePrincipal(createURI(kURI1), 1, false);
+  var app10 = secMan.getAppCodebasePrincipal(createURI(kURI1), 10, false);
+  var app1browser = secMan.getAppCodebasePrincipal(createURI(kURI1), 1, true);
+
+  var am = Cc["@mozilla.org/network/http-auth-manager;1"].
+           getService(Ci.nsIHttpAuthManager);
+  am.setAuthIdentity("http", "a.example.com", -1, "basic", "realm", "", "example.com", "user", "pass", false, app1);
+  am.setAuthIdentity("http", "a.example.com", -1, "basic", "realm", "", "example.com", "user3", "pass3", false, app1browser);
+  am.setAuthIdentity("http", "a.example.com", -1, "basic", "realm", "", "example.com", "user2", "pass2", false, app10);
+
+  let subject = {
+    appId: 1,
+    browserOnly: true,
+    QueryInterface: XPCOMUtils.generateQI([Ci.mozIApplicationClearPrivateDataParams])
+  };
+  Services.obs.notifyObservers(subject, "webapps-clear-data", null);
+  
+  var domain = {value: ""}, user = {value: ""}, pass = {value: ""};
+  try {
+    am.getAuthIdentity("http", "a.example.com", -1, "basic", "realm", "", domain, user, pass, false, app1browser);
+    do_check_false(true); // no identity should be present
+  } catch (x) {
+    do_check_eq(domain.value, "");
+    do_check_eq(user.value, "");
+    do_check_eq(pass.value, "");
+  }
+
+  am.getAuthIdentity("http", "a.example.com", -1, "basic", "realm", "", domain, user, pass, false, app1);
+  do_check_eq(domain.value, "example.com");
+  do_check_eq(user.value, "user");
+  do_check_eq(pass.value, "pass");
+
+
+  am.getAuthIdentity("http", "a.example.com", -1, "basic", "realm", "", domain, user, pass, false, app10);
+  do_check_eq(domain.value, "example.com");
+  do_check_eq(user.value, "user2");
+  do_check_eq(pass.value, "pass2");
+}
\ No newline at end of file
--- a/netwerk/test/unit/xpcshell.ini
+++ b/netwerk/test/unit/xpcshell.ini
@@ -4,16 +4,17 @@ tail =
 
 [test_304_responses.js]
 [test_cacheForOfflineUse_no-store.js]
 [test_307_redirect.js]
 [test_NetUtil.js]
 [test_URIs.js]
 [test_aboutblank.js]
 [test_assoc.js]
+[test_auth_jar.js]
 [test_auth_proxy.js]
 [test_authentication.js]
 # Bug 675039: test hangs consistently on Android
 skip-if = os == "android"
 [test_authpromptwrapper.js]
 [test_backgroundfilesaver.js]
 [test_bug203271.js]
 [test_bug248970_cache.js]