Merge m-c to inbound.
authorRyan VanderMeulen <ryanvm@gmail.com>
Thu, 23 Aug 2012 19:50:46 -0400
changeset 105286 14f0f28ad1a99989b4885d376dbd0ea6794d57ee
parent 105285 670c8a2695b3e729322627fb947df319b47c0a2a (current diff)
parent 105172 cace7cc25814f222284ade58e875f11c5f32c36c (diff)
child 105287 c97a0ffcf50061f24a924001ca46064f18ef3857
push id55
push usershu@rfrn.org
push dateThu, 30 Aug 2012 01:33:09 +0000
milestone17.0a1
Merge m-c to inbound.
build/automation.py.in
dom/ipc/ContentChild.cpp
dom/ipc/ContentParent.cpp
netwerk/ipc/NeckoMessageUtils.h
testing/mochitest/tests/SimpleTest/specialpowersAPI.js
--- a/browser/themes/pinstripe/browser.css
+++ b/browser/themes/pinstripe/browser.css
@@ -1523,23 +1523,23 @@ window[tabsontop="false"] richlistitem[t
 #editBookmarkPanel .expander-up {
   list-style-image: url("chrome://browser/skin/panel-expander-open.png");
 }
 
 #editBookmarkPanel .expander-down {
   list-style-image: url("chrome://browser/skin/panel-expander-closed.png");
 }
 
-#editBookmarkPanel .expander-up .button-icon,
-#editBookmarkPanel .expander-down .button-icon {
+#editBookmarkPanel .expander-up > .button-box > .button-icon,
+#editBookmarkPanel .expander-down > .button-box > .button-icon {
   margin: 1px 0 0;
 }
 
-#editBookmarkPanel .expander-up .button-text,
-#editBookmarkPanel .expander-down .button-text {
+#editBookmarkPanel .expander-up > .button-box > .button-text,
+#editBookmarkPanel .expander-down > .button-box > .button-text {
   display: none;
 }
 
 #editBMPanel_tagsField > .autocomplete-textbox-container > .textbox-input-box > html|*.textbox-input:-moz-placeholder {
   color: #bbb;
 }
 
 .editBMPanel_rowLabel {
--- a/build/automation.py.in
+++ b/build/automation.py.in
@@ -259,31 +259,36 @@ class Automation(object):
       raise SyntaxError(lineno + 1, "missing primary location")
 
     return locations
 
   def setupPermissionsDatabase(self, profileDir, permissions):
     # Open database and create table
     permDB = sqlite3.connect(os.path.join(profileDir, "permissions.sqlite"))
     cursor = permDB.cursor();
+
+    cursor.execute("PRAGMA user_version=3");
+
     # SQL copied from nsPermissionManager.cpp
     cursor.execute("""CREATE TABLE moz_hosts (
        id INTEGER PRIMARY KEY,
        host TEXT,
        type TEXT,
        permission INTEGER,
        expireType INTEGER,
-       expireTime INTEGER)""")
+       expireTime INTEGER,
+       appId INTEGER,
+       isInBrowserElement INTEGER)""")
 
     # Insert desired permissions
     c = 0
     for perm in permissions.keys():
       for host,allow in permissions[perm]:
         c += 1
-        cursor.execute("INSERT INTO moz_hosts values(?, ?, ?, ?, 0, 0)",
+        cursor.execute("INSERT INTO moz_hosts values(?, ?, ?, ?, 0, 0, 0, 0)",
                        (c, host, perm, 1 if allow else 2))
 
     # Commit and close
     permDB.commit()
     cursor.close()
 
   def setupTestApps(self, profileDir, apps):
     webappJSONTemplate = Template(""""$name": {
--- a/caps/tests/mochitest/test_principal_extendedorigin_appid_appstatus.html
+++ b/caps/tests/mochitest/test_principal_extendedorigin_appid_appstatus.html
@@ -384,16 +384,18 @@ function runTest() {
   for (var i=0; i<gData.length; ++i) {
     let data = gData[i];
 
     var iframe = document.createElement('iframe');
     iframe.check = function() {
       checkIFrame(this, data);
     };
     iframe.addChild = function() {
+      SpecialPowers.addPermission("browser", true, iframe.contentDocument);
+
       var childFrame = document.createElement('iframe');
 
       if (data.child.app) {
         childFrame.setAttribute('mozapp', data.child.app)
         childFrame.setAttribute('mozbrowser', '');
       } else if (data.child.browser) {
         childFrame.setAttribute('mozbrowser', '');
       }
@@ -424,16 +426,15 @@ function runTest() {
     content.appendChild(iframe);
 
     yield;
   }
 }
 
 var gTestRunner = runTest();
 
-SpecialPowers.addPermission("browser", true, "http://example.org");
 SpecialPowers.pushPrefEnv({'set':[["dom.mozBrowserFramesEnabled", true]]},
                            function() { gTestRunner.next(); });
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/browser-element/mochitest/browserElement_SetVisibleFrames.js
+++ b/dom/browser-element/mochitest/browserElement_SetVisibleFrames.js
@@ -13,16 +13,21 @@
 SimpleTest.waitForExplicitFinish();
 
 var iframe;
 
 function runTest() {
   browserElementTestHelpers.setEnabledPref(true);
   browserElementTestHelpers.addPermission();
 
+  var principal = SpecialPowers.wrap(SpecialPowers.getNodePrincipal(document));
+  SpecialPowers.addPermission("browser", true, { url: SpecialPowers.wrap(principal.URI).spec,
+                                                 appId: principal.appId,
+                                                 isInBrowserElement: true });
+
   iframe = document.createElement('iframe');
   iframe.mozbrowser = true;
 
   // Our test involves three <iframe mozbrowser>'s, parent, child1, and child2.
   // child1 and child2 are contained inside parent.  child1 is visibile, and
   // child2 is not.
   //
   // For the purposes of this test, we want there to be a process barrier
@@ -50,16 +55,22 @@ function test2() {
 
 function finish() {
   // We need to remove this listener because when this test finishes and the
   // iframe containing this document is navigated, we'll fire a
   // visibilitychange(false) event on all child iframes.  That's OK and
   // expected, but if we don't remove our listener, then we'll end up causing
   // the /next/ test to fail!
   iframe.removeEventListener('mozbrowsershowmodalprompt', checkMessage);
+
+  var principal = SpecialPowers.wrap(SpecialPowers.getNodePrincipal(document));
+  SpecialPowers.removePermission("browser", { url: SpecialPowers.wrap(principal.URI).spec,
+                                              appId: principal.appId,
+                                              isInBrowserElement: true });
+
   SimpleTest.finish();
 }
 
 var expectedMsg = null;
 var expectedMsgCallback = null;
 function expectMessage(msg, next) {
   expectedMsg = msg;
   expectedMsgCallback = next;
--- a/dom/browser-element/mochitest/browserElement_SetVisibleFrames2.js
+++ b/dom/browser-element/mochitest/browserElement_SetVisibleFrames2.js
@@ -7,16 +7,21 @@
 "use strict";
 
 SimpleTest.waitForExplicitFinish();
 
 function runTest() {
   browserElementTestHelpers.setEnabledPref(true);
   browserElementTestHelpers.addPermission();
 
+  var principal = SpecialPowers.wrap(SpecialPowers.getNodePrincipal(document));
+  SpecialPowers.addPermission("browser", true, { url: SpecialPowers.wrap(principal.URI).spec,
+                                                 appId: principal.appId,
+                                                 isInBrowserElement: true });
+
   var iframe = document.createElement('iframe');
   iframe.mozbrowser = true;
 
   // We need remote = false here until bug 761935 is fixed; see
   // SetVisibleFrames.js for an explanation.
   iframe.remote = false;
 
   iframe.addEventListener('mozbrowserloadend', function loadEnd(e) {
@@ -30,24 +35,33 @@ function runTest() {
       ok(true, "Got parent:finish");
 
       // Give any extra events a chance to fire, then end the test.
       SimpleTest.executeSoon(function() {
         SimpleTest.executeSoon(function() {
           SimpleTest.executeSoon(function() {
             SimpleTest.executeSoon(function() {
               SimpleTest.executeSoon(function() {
-                SimpleTest.finish();
+                finish();
               });
             });
           });
         });
       });
     }
     else {
       ok(false, "Got unexpected message: " + e.detail.message);
     }
   });
 
   document.body.appendChild(iframe);
 }
 
+function finish() {
+  var principal = SpecialPowers.wrap(SpecialPowers.getNodePrincipal(document));
+  SpecialPowers.removePermission("browser", { url: SpecialPowers.wrap(principal.URI).spec,
+                                              appId: principal.appId,
+                                              isInBrowserElement: true });
+
+  SimpleTest.finish();
+}
+
 runTest();
--- a/dom/indexedDB/IDBFactory.cpp
+++ b/dom/indexedDB/IDBFactory.cpp
@@ -203,21 +203,16 @@ IDBFactory::Create(ContentParent* aConte
       NS_ASSERTION(!lastCx, "We should only be called from C++!");
     }
     else {
       NS_ERROR("nsIThreadJSContextStack::Peek should never fail!");
     }
   }
 #endif
 
-  nsCString origin;
-  nsresult rv =
-    IndexedDatabaseManager::GetASCIIOriginFromWindow(nullptr, origin);
-  NS_ENSURE_SUCCESS(rv, rv);
-
   nsCOMPtr<nsIPrincipal> principal =
     do_CreateInstance("@mozilla.org/nullprincipal;1");
   NS_ENSURE_TRUE(principal, NS_ERROR_FAILURE);
 
   JSContext* cx = nsContentUtils::GetSafeJSContext();
   NS_ENSURE_TRUE(cx, NS_ERROR_FAILURE);
 
   nsCxPusher pusher;
@@ -227,17 +222,17 @@ IDBFactory::Create(ContentParent* aConte
   }
 
   JSAutoRequest ar(cx);
 
   nsIXPConnect* xpc = nsContentUtils::XPConnect();
   NS_ASSERTION(xpc, "This should never be null!");
 
   nsCOMPtr<nsIXPConnectJSObjectHolder> globalHolder;
-  rv = xpc->CreateSandbox(cx, principal, getter_AddRefs(globalHolder));
+  nsresult rv = xpc->CreateSandbox(cx, principal, getter_AddRefs(globalHolder));
   NS_ENSURE_SUCCESS(rv, rv);
 
   JSObject* global;
   rv = globalHolder->GetJSObject(&global);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // The CreateSandbox call returns a proxy to the actual sandbox object. We
   // don't need a proxy here.
@@ -630,9 +625,9 @@ IDBFactory::Cmp(const jsval& aFirst,
   }
 
   if (first.IsUnset() || second.IsUnset()) {
     return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
   }
 
   *_retval = Key::CompareKeys(first, second);
   return NS_OK;
-}
\ No newline at end of file
+}
--- a/dom/indexedDB/IndexedDatabaseManager.cpp
+++ b/dom/indexedDB/IndexedDatabaseManager.cpp
@@ -1013,17 +1013,17 @@ IndexedDatabaseManager::GetASCIIOriginFr
 
   nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal();
   NS_ENSURE_TRUE(principal, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   if (nsContentUtils::IsSystemPrincipal(principal)) {
     aASCIIOrigin.AssignLiteral("chrome");
   }
   else {
-    nsresult rv = nsContentUtils::GetASCIIOrigin(principal, aASCIIOrigin);
+    nsresult rv = principal->GetExtendedOrigin(aASCIIOrigin);
     NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
     if (aASCIIOrigin.EqualsLiteral("null")) {
       NS_WARNING("IndexedDB databases not allowed for this principal!");
       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
     }
   }
 
--- a/dom/indexedDB/test/Makefile.in
+++ b/dom/indexedDB/test/Makefile.in
@@ -96,16 +96,20 @@ MOCHITEST_FILES = \
   test_transaction_ordering.html \
   test_setVersion.html \
   test_setVersion_abort.html \
   test_setVersion_events.html \
   test_setVersion_exclusion.html \
   test_unique_index_update.html \
   third_party_iframe1.html \
   third_party_iframe2.html \
+  test_app_isolation_inproc.html \
+  test_app_isolation_oop.html \
+  file_app_isolation.html \
+  file_app_isolation.js \
   $(NULL)
 
 #   test_writer_starvation.html  disabled for infinite loops, bug 595368
 
 ifeq (browser,$(MOZ_BUILD_APP))
 MOCHITEST_BROWSER_FILES = \
   browser_forgetThisSite.js \
   browser_forgetThisSiteAdd.html \
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/file_app_isolation.html
@@ -0,0 +1,88 @@
+<!DOCTYPE html>
+<html>
+  <body>
+    foobar!
+  </body>
+  <script>
+    var data = [
+      { id: "0", name: "foo" },
+    ];
+
+    var action = window.location.search.substring(1);
+    var finished = false;
+    var created = false; // We use that for 'read-no' action.
+
+    function finish(value) {
+      value ? alert('success') : alert('failure');
+      finished = true;
+    }
+
+    var request = window.indexedDB.open('AppIsolationTest');
+
+    request.onupgradeneeded = function(event) {
+      if (finished) {
+        finish(false);
+        return;
+      }
+
+      switch (action) {
+        case 'read-no':
+          created = true;
+          break;
+        case 'read-yes':
+          finish(false);
+          break;
+        case 'write':
+          created = true;
+
+          var db = event.target.result;
+
+          var objectStore = db.createObjectStore("test", { keyPath: "id" });
+          for (var i in data) {
+            objectStore.add(data[i]);
+          }
+          break;
+      }
+    }
+
+    request.onsuccess = function(event) {
+      if (finished) {
+        finish(false);
+        return;
+      }
+
+      var db = event.target.result;
+
+      // Think about close the db!
+      switch (action) {
+        case 'read-no':
+          db.close();
+
+          if (created) { // That means we have created it.
+            indexedDB.deleteDatabase('AppIsolationTest').onsuccess = function() {
+              finish(true);
+            };
+          } else {
+            finish(false);
+          }
+          break;
+        case 'read-yes':
+          db.transaction("test").objectStore("test").get("0").onsuccess = function(event) {
+            var name = event.target.result.name;
+            db.close();
+
+            indexedDB.deleteDatabase('AppIsolationTest').onsuccess = function() {
+              finish(name == 'foo');
+            };
+          };
+          break;
+        case 'write':
+          db.close();
+
+          // Success only if the db was actually created.
+          finish(created);
+          break;
+      }
+    };
+  </script>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/file_app_isolation.js
@@ -0,0 +1,169 @@
+SimpleTest.waitForExplicitFinish();
+
+var fileTestOnCurrentOrigin = (location.protocol + '//' + location.host + location.pathname)
+                              .replace('test_', 'file_')
+                              .replace('_inproc', '').replace('_oop', '');
+
+var previousPrefs = {
+  mozBrowserFramesEnabled: undefined,
+  mozBrowserFramesWhitelist: undefined,
+  oop_by_default: undefined,
+};
+
+try {
+  previousPrefs.mozBrowserFramesEnabled = SpecialPowers.getBoolPref('dom.mozBrowserFramesEnabled');
+} catch(e)
+{
+}
+
+try {
+  previousPrefs.mozBrowserFramesWhitelist = SpecialPowers.getCharPref('dom.mozBrowserFramesWhitelist');
+} catch(e) {
+}
+
+try {
+  previousPrefs.oop_by_default = SpecialPowers.getBoolPref('dom.ipc.browser_frames.oop_by_default');
+} catch(e) {
+}
+
+SpecialPowers.setBoolPref('dom.mozBrowserFramesEnabled', true);
+SpecialPowers.setCharPref('dom.mozBrowserFramesWhitelist', location.protocol + "//" + location.host);
+SpecialPowers.setBoolPref("dom.ipc.browser_frames.oop_by_default", location.pathname.indexOf('_inproc') == -1);
+
+var gData = [
+  // APP 1
+  {
+    app: 'http://example.org/manifest.webapp',
+    action: 'read-no',
+    src: fileTestOnCurrentOrigin,
+  },
+  {
+    app: 'http://example.org/manifest.webapp',
+    action: 'write',
+    src: fileTestOnCurrentOrigin,
+  },
+  {
+    app: 'http://example.org/manifest.webapp',
+    action: 'read-yes',
+    src: fileTestOnCurrentOrigin,
+  },
+  // APP 2
+  {
+    app: 'https://example.com/manifest.webapp',
+    action: 'read-no',
+    src: fileTestOnCurrentOrigin,
+  },
+  {
+    app: 'https://example.com/manifest.webapp',
+    action: 'write',
+    src: fileTestOnCurrentOrigin,
+  },
+  {
+    app: 'https://example.com/manifest.webapp',
+    action: 'read-yes',
+    src: fileTestOnCurrentOrigin,
+  },
+  // Browser
+  {
+    browser: true,
+    action: 'read-no',
+    src: fileTestOnCurrentOrigin,
+  },
+  {
+    browser: true,
+    action: 'write',
+    src: fileTestOnCurrentOrigin,
+  },
+  {
+    browser: true,
+    action: 'read-yes',
+    src: fileTestOnCurrentOrigin,
+  },
+];
+
+function runTest() {
+  for (var i in gData) {
+    var iframe = document.createElement('iframe');
+    var data = gData[i];
+
+    if (data.app) {
+      iframe.setAttribute('mozbrowser', '');
+      iframe.setAttribute('mozapp', data.app);
+    } else if (data.browser) {
+      iframe.setAttribute('mozbrowser', '');
+    }
+
+    if (data.app || data.browser) {
+      iframe.addEventListener('mozbrowsershowmodalprompt', function(e) {
+        is(e.detail.message, 'success', 'test number ' + i);
+
+//        document.getElementById('content').removeChild(iframe);
+
+        i++;
+        if (i >= gData.length) {
+          if (previousPrefs.mozBrowserFramesEnabled !== undefined) {
+            SpecialPowers.setBoolPref('dom.mozBrowserFramesEnabled', previousPrefs.mozBrowserFramesEnabled);
+          }
+          if (previousPrefs.mozBrowserFramesWhitelist !== undefined) {
+            SpecialPowers.setCharPref('dom.mozBrowserFramesWhitelist', previousPrefs.mozBrowserFramesWhitelist);
+          }
+          if (previousPrefs.oop_by_default !== undefined) {
+            SpecialPowers.setBoolPref("dom.ipc.browser_frames.oop_by_default", previousPrefs.oop_by_default);
+          }
+
+          indexedDB.deleteDatabase('AppIsolationTest').onsuccess = function() {
+            SimpleTest.finish();
+          };
+        } else {
+          gTestRunner.next();
+        }
+      });
+    }
+
+    iframe.src = data.src + '?' + data.action;
+
+    document.getElementById('content').appendChild(iframe);
+
+    yield;
+  }
+}
+
+var gTestRunner = runTest();
+
+function startTest() {
+  var request = window.indexedDB.open('AppIsolationTest');
+  var created = false;
+
+  request.onupgradeneeded = function(event) {
+    created = true;
+    var db = event.target.result;
+    var data = [
+      { id: "0", name: "foo" },
+    ];
+    var objectStore = db.createObjectStore("test", { keyPath: "id" });
+    for (var i in data) {
+      objectStore.add(data[i]);
+    }
+  }
+
+  request.onsuccess = function(event) {
+    var db = event.target.result;
+    is(created, true, "we should have created the db");
+
+    db.transaction("test").objectStore("test").get("0").onsuccess = function(event) {
+      is(event.target.result.name, 'foo', 'data have been written');
+      db.close();
+
+      gTestRunner.next();
+    };
+  }
+}
+
+// test_ipc.html executes all the tests in this directory in content process.
+// It will fail on this one for the moment.
+if (!SpecialPowers.isMainProcess()) {
+  todo(false, "We should make this work on content process");
+  SimpleTest.finish();
+} else {
+  startTest();
+}
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/test_app_isolation_inproc.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=756645
+-->
+<head>
+  <title>Test for IndexedDB app isolation (unique process)</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=756645">Mozilla Bug 756645</a>
+<div id="content" style="display: none">
+  
+</div>
+<pre id="test">
+<script type="application/javascript;version=1.7" src="file_app_isolation.js">
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/test_app_isolation_oop.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=756645
+-->
+<head>
+  <title>Test for IndexedDB app isolation (unique process)</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=756645">Mozilla Bug 756645</a>
+<div id="content" style="display: none">
+  
+</div>
+<pre id="test">
+<script type="application/javascript;version=1.7" src="file_app_isolation.js">
+</script>
+</pre>
+</body>
+</html>
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -90,16 +90,19 @@
 #include "mozilla/dom/indexedDB/PIndexedDBChild.h"
 #include "mozilla/dom/sms/SmsChild.h"
 #include "mozilla/dom/devicestorage/DeviceStorageRequestChild.h"
 
 #include "nsDOMFile.h"
 #include "nsIRemoteBlob.h"
 #include "StructuredCloneUtils.h"
 #include "URIUtils.h"
+#include "nsIScriptSecurityManager.h"
+#include "nsContentUtils.h"
+#include "nsIPrincipal.h"
 
 using namespace mozilla::docshell;
 using namespace mozilla::dom::devicestorage;
 using namespace mozilla::dom::sms;
 using namespace mozilla::dom::indexedDB;
 using namespace mozilla::hal_sandbox;
 using namespace mozilla::ipc;
 using namespace mozilla::layers;
@@ -842,17 +845,30 @@ ContentChild::RecvAddPermission(const IP
 #if MOZ_PERMISSIONS
   nsCOMPtr<nsIPermissionManager> permissionManagerIface =
       do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
   nsPermissionManager* permissionManager =
       static_cast<nsPermissionManager*>(permissionManagerIface.get());
   NS_ABORT_IF_FALSE(permissionManager, 
                    "We have no permissionManager in the Content process !");
 
-  permissionManager->AddInternal(nsCString(permission.host),
+  nsCOMPtr<nsIURI> uri;
+  NS_NewURI(getter_AddRefs(uri), NS_LITERAL_CSTRING("http://") + nsCString(permission.host));
+  NS_ENSURE_TRUE(uri, true);
+
+  nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
+  MOZ_ASSERT(secMan);
+
+  nsCOMPtr<nsIPrincipal> principal;
+  nsresult rv = secMan->GetAppCodebasePrincipal(uri, permission.appId,
+                                                permission.isInBrowserElement,
+                                                getter_AddRefs(principal));
+  NS_ENSURE_SUCCESS(rv, true);
+
+  permissionManager->AddInternal(principal,
                                  nsCString(permission.type),
                                  permission.capability,
                                  0,
                                  permission.expireType,
                                  permission.expireTime,
                                  nsPermissionManager::eNotify,
                                  nsPermissionManager::eNoDBOperation);
 #endif
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -779,27 +779,33 @@ ContentParent::RecvReadPermissions(Infal
             break;
 
         nsCOMPtr<nsISupports> supp;
         enumerator->GetNext(getter_AddRefs(supp));
         nsCOMPtr<nsIPermission> perm = do_QueryInterface(supp);
 
         nsCString host;
         perm->GetHost(host);
+        uint32_t appId;
+        perm->GetAppId(&appId);
+        bool isInBrowserElement;
+        perm->GetIsInBrowserElement(&isInBrowserElement);
         nsCString type;
         perm->GetType(type);
         uint32_t capability;
         perm->GetCapability(&capability);
         uint32_t expireType;
         perm->GetExpireType(&expireType);
         int64_t expireTime;
         perm->GetExpireTime(&expireTime);
 
-        aPermissions->AppendElement(IPC::Permission(host, type, capability,
-                                                    expireType, expireTime));
+        aPermissions->AppendElement(IPC::Permission(host, appId,
+                                                    isInBrowserElement, type,
+                                                    capability, expireType,
+                                                    expireTime));
     }
 
     // Ask for future changes
     mSendPermissionUpdates = true;
 #endif
 
     return true;
 }
--- a/dom/tests/mochitest/webapps/apphelper.js
+++ b/dom/tests/mochitest/webapps/apphelper.js
@@ -100,14 +100,13 @@ function readFile(aFile) {
 }
 
 function getOrigin(url) {
   return Services.io.newURI(url, null, null).prePath;
 }
 
 function tearDown() {
   debug("in " + arguments.callee.name);
-  uninstallAll();
   popupNotifications.panel.removeEventListener("popupshown", mainCommand, false);
   SpecialPowers.clearUserPref('browser.mozApps.installer.dry_run');
   DOMApplicationRegistry.allAppsLaunchable = originalAAL;
 }
 
--- a/dom/tests/mochitest/webapps/jshelper.js
+++ b/dom/tests/mochitest/webapps/jshelper.js
@@ -29,51 +29,16 @@ function onIframeLoad(name, check, next)
   document.getElementById(name).contentWindow.wrappedJSObject.getOrigin = getOrigin;
   document.getElementById(name).contentWindow.wrappedJSObject.check = check;
   document.getElementById(name).contentWindow.wrappedJSObject.debug = debug;
   document.getElementById(name).contentWindow.wrappedJSObject.appURL = SERVERS[name]; 
   document.getElementById(name).contentWindow.wrappedJSObject.popup_listener = popup_listener;
   document.getElementById(name).contentWindow.wrappedJSObject.readFile = readFile;
 }
 
-/**
- * Uninstall All uninstalls all Apps
- * @next  The next operation to jump to, this might need to be invoked by the iframe when the test has completed
- */
-
-function uninstallAll(next) {
-  var pendingGetAll = navigator.mozApps.mgmt.getAll();
-  pendingGetAll.onsuccess = function() {
-    var m = this.result;
-    var total = m.length;
-    var finished = (total === 0);
-    debug("total = " + total);
-    for (var i=0; i < m.length; i++) {
-      var app = m[i];
-      var pendingUninstall = app.uninstall();
-      pendingUninstall.onsuccess = function(r) {
-        finished = (--total === 0);
-        if(finished == true) {
-          next();
-        }
-      };
-      pendingUninstall.onerror = function () {
-        finished = true;
-        throw('Failed');
-        if(finished == true) {
-          next();
-        }
-      };
-    }
-    if(finished == true && total ==  0) {
-      next();
-    }
-  }
-}
-
 function subsetOf(resultObj, list) {
   var returnObj = {} ;
   for (var i=0; i < list.length; i++) {
     returnObj[list[i]] = resultObj[list[i]];
   }
   return returnObj;
 }
 
--- a/dom/tests/mochitest/webapps/test_cross_domain.xul
+++ b/dom/tests/mochitest/webapps/test_cross_domain.xul
@@ -21,44 +21,29 @@
      target="_blank">Mozilla Bug 741549</a>
   <iframe id="super_crazy" onload="go();" src="http://www.example.com:80/chrome/dom/tests/mochitest/webapps/apps/include.html"/>
   <iframe id="no_delegated_install" onload="go();" src="http://sub2.test2.example.org:80/chrome/dom/tests/mochitest/webapps/apps/include.html"/>
 
   </body>
 
 <script> 
 
-steps = [setUp, verify_no_apps, get_installed_returns_nothing, install_super_crazy, check_event_listener_fired, get_self_returns_nothing, 
+steps = [get_installed_returns_nothing, install_super_crazy, check_event_listener_fired, get_self_returns_nothing, 
          get_self_on_domain, install_on_domain, check_event_listener_fired, get_all_installed, 
          get_installed_on_domain, uninstall_on_domain, get_all_on_domain, tearDown];
 var iframes_loaded = 0 ;
 
 function go() {
   ++iframes_loaded;
   if (iframes_loaded >= 2) {
     runAll(steps);
   } 
 }
 
 
-function setUp(next) { 
-  debug("in " + arguments.callee.name);
-  
-  uninstallAll(next);
-}
-
-function verify_no_apps(next)  {
-  debug("in " + arguments.callee.name);
-  mozAppscb(navigator.mozApps.mgmt.getAll(),
-            [{}],
-            "success",
-            ok,
-            next);
-}
-
 function get_installed_returns_nothing(next) {
   debug("in " + arguments.callee.name);
   mozAppscb(navigator.mozApps.getInstalled(), 
             [{}],
             "success",
             ok, 
             next);
 }
--- a/dom/tests/mochitest/webapps/test_getNotInstalled.xul
+++ b/dom/tests/mochitest/webapps/test_getNotInstalled.xul
@@ -10,60 +10,64 @@
         title="Mozilla Bug 781379">
 <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
 <script type="application/javascript" src="apphelper.js"/>
 <script type="application/javascript" src="jshelper.js"/>
 <script type="application/javascript;version=1.8">
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 Cu.import("resource://gre/modules/Webapps.jsm");
 
+let notInstalled = 0;
+
 let _isLaunchable;
 let steps = [
-  cleanUp, installApp, monkeyPatchDOMApplicationRegistry, getNotInstalled,
-  unmonkeyPatchDOMApplicationRegistry, cleanUp, tearDown
+  monkeyPatchDOMApplicationRegistry, getNotInstalled, installApp,
+  compareNotInstalled, unmonkeyPatchDOMApplicationRegistry, cleanUp, tearDown
 ];
 runAll(steps);
 
-// Remove all installed apps and apps from the appregistry
-function cleanUp (next) { 
-  uninstallAll(next);
-}
-
-// Add an app to the appregistry
-function installApp (next) {
-  let appURL = SERVERS["super_crazy"];
-  install(appURL, ok, next);
-}
-
 // Monkey patch DOMApplicationRegistry._isLaunchable for testing.
 // This way, we don't have to create a platform specific application with a
 // status other than "installed".
 function monkeyPatchDOMApplicationRegistry (next) {
   _isLaunchable = DOMApplicationRegistry._isLaunchable;
   DOMApplicationRegistry._isLaunchable = function mockIsLaunchable (aOrigin) {
     return false;
   }
   next();
 }
 
-// Call navigator.mozApps.mgmt.getNotInstalled
+// Call navigator.mozApps.mgmt.getNotInstalled and save the result.
 function getNotInstalled (next) {
+  window.navigator.mozApps.mgmt.getNotInstalled().onsuccess = function() {
+    notInstalled = this.result.length;
+    next();
+  };
+}
+
+// Add an app to the appregistry
+function installApp (next) {
+  let appURL = SERVERS["super_crazy"];
+  install(appURL, ok, next);
+}
+
+// Call navigator.mozApps.mgmt.getNotInstalled and make sure there is one more.
+function compareNotInstalled (next) {
   let results;
   function getNotInstalledError () {
     ok(false, "window.mozApps.mgmt.getNotInstalled onerror called");
     next();
   }
   function getNotInstalledSuccess () {
     ok(true, "window.mozApps.mgmt.getNotInstalled onsuccess called");
-    if (this.result.length === 1) {
-      ok(true, "got the single notInstalled app back")
-      is(this.result[0].origin, "http://www.example.com",
+    is(this.result.length, notInstalled + 1, "should get one more notInstalled app");
+
+    if (this.result.length > 0) {
+      is(this.result[this.result.length-1].origin, "http://www.example.com",
         "getNotInstalled returned the expected app");
-    } else {
-      ok(false, "got an unexpected result from the call to getNotInstalled");
     }
     next();
   }
 
   let type = typeof window.navigator.mozApps.getNotInstalled;
   is(type, "undefined", "getNotInstalled moved from window.navigator");
   type = typeof window.navigator.mozApps.mgmt.getNotInstalled;
   if (type === "function") {
@@ -81,10 +85,17 @@ function unmonkeyPatchDOMApplicationRegi
   if (typeof _isLaunchable === "function") {
     DOMApplicationRegistry._isLaunchable = _isLaunchable;
     ok(true, "restored DOMApplicationRegistry._isLaunchable")
   } else {
     ok(false, "can't restore DOMApplicationRegistry._isLaunchable")
   }
   next();
 }
+
+// Remove installed app.
+function cleanUp (next) {
+  let appURL = SERVERS["super_crazy"];
+  uninstall(appURL, ok, next);
+}
+
 </script>
 </window>
--- a/dom/tests/mochitest/webapps/test_install_app.xul
+++ b/dom/tests/mochitest/webapps/test_install_app.xul
@@ -19,35 +19,21 @@
   <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=741549"
      target="_blank">Mozilla Bug 741549</a> <br />
   <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=734294"
      target="_blank">Mozilla Bug 734294</a>
   </body>
 
 <script> 
 
-steps = [setUp, verify_no_apps, get_installed_returns_nothing, install_super_crazy, get_self_returns_nothing, 
+steps = [get_installed_returns_nothing, install_super_crazy, get_self_returns_nothing, 
          install_wild_crazy, uninstall_wild_crazy, tearDown];
 
 runAll(steps);
 
-function setUp(next) { 
-  debug("in " + arguments.callee.name);
-  uninstallAll(next);
-}
-
-function verify_no_apps(next)  {
-  debug("in " + arguments.callee.name);
-  mozAppscb(navigator.mozApps.mgmt.getAll(), 
-            [{}],
-            "success",
-            ok,
-            next);
-}
-
 function get_installed_returns_nothing(next) {
   debug("in " + arguments.callee.name);
   mozAppscb(navigator.mozApps.getInstalled(), 
             [{}],
             "success",
             ok,
             next);
 }
--- a/dom/tests/mochitest/webapps/test_install_errors.xul
+++ b/dom/tests/mochitest/webapps/test_install_errors.xul
@@ -21,23 +21,18 @@
   </body>
 
 <script> 
 
 function go() {
   runAll(steps);
 }
 
-steps = [setUp, no_args, parse_error, invalid_manifest, permission_denied, invalid_content,
-         mgmt_api_errors, mgmt_api_add_listener, tearDown];
-
-function setUp(next) {
-  debug("in " + arguments.callee.name);
-  uninstallAll(next);
-}
+steps = [no_args, parse_error, invalid_manifest, permission_denied, invalid_content,
+         mgmt_api_errors, mgmt_api_add_listener, uninstall_apps, tearDown];
 
 function no_args(next)  {
   debug("in " + arguments.callee.name);
   try { 
     navigator.mozApps.install();
   } catch (e) {
     ok(e.message == "Not enough arguments \[mozIDOMApplicationRegistry.install\]", "install returned " + e.message);
     next();
@@ -75,12 +70,21 @@ function mgmt_api_errors(next) {
 }
 
 function mgmt_api_add_listener(next) {
   debug("in " + arguments.callee.name);
   onIframeLoad("no_delegated_install", todo, next);
   document.getElementById("no_delegated_install").contentWindow.postMessage("mgmt.event_error", '*');
 }
 
+function uninstall_apps(next) {
+  debug("in " + arguments.callee.name);
+  var appURL = SERVERS['bad_content_type'];
+  uninstall(appURL, ok, function() {
+    appURL = SERVERS['no_delegated_install'];
+    uninstall(appURL, ok, function() { next(); });
+  });
+}
+
 </script> 
 
 </window>
 
--- a/dom/tests/mochitest/webapps/test_install_receipts.xul
+++ b/dom/tests/mochitest/webapps/test_install_receipts.xul
@@ -17,26 +17,20 @@
   <!-- test results are displayed in the html:body -->
   <body xmlns="http://www.w3.org/1999/xhtml">
   <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=741549"
      target="_blank">Mozilla Bug 741549</a>
   </body>
 
 <script> 
 
-steps = [setUp, install_super_crazy, tearDown];
+steps = [install_super_crazy, uninstall_super_crazy, tearDown];
 
 runAll(steps);
 
-function setUp(next) { 
-  debug("in " + arguments.callee.name);
-  uninstallAll(next);
-}
-
-
 function install_super_crazy(next)  {
   debug("in " + arguments.callee.name);
   var appURL = SERVERS['super_crazy'];
   var pending = navigator.mozApps.install(appURL, {receipts: ["a",0, true,false,null,undefined,{one:1},{nest:{ed:"hi"}},NaN,Infinity]});
   pending.onsuccess = function ()  {
     var receipts = pending.result.receipts; 
     ok(receipts[0] == "a", receipts[0]);
     ok(receipts[1] == 0, receipts[1]);
@@ -48,12 +42,18 @@ function install_super_crazy(next)  {
     ok(receipts[7].nest.ed == "hi", receipts[7].nest.ed);
     todo(receipts[8] == NaN, receipts[8]);
     todo(receipts[9] == Infinity, receipts[9]);
  
     next();
   };
 }
 
+function uninstall_super_crazy(next)  {
+  debug("in " + arguments.callee.name);
+  var appURL = SERVERS['super_crazy'];
+  uninstall(appURL, ok, function() { next(); });
+}
+
 </script> 
 
 </window>
 
--- a/dom/tests/mochitest/webapps/test_install_utf8.xul
+++ b/dom/tests/mochitest/webapps/test_install_utf8.xul
@@ -17,52 +17,43 @@
   <!-- test results are displayed in the html:body -->
   <body xmlns="http://www.w3.org/1999/xhtml">
   <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=741549"
      target="_blank">Mozilla Bug 741549</a>
   </body>
 
 <script> 
 
-steps = [setUp, verify_no_apps, get_installed_returns_nothing, install_bom, tearDown];
+steps = [get_installed_returns_nothing, install_bom, uninstall_bom, tearDown];
 
 runAll(steps);
 
-function setUp(next) { 
-  debug("in " + arguments.callee.name);
-  uninstallAll(next);
-}
-
-function verify_no_apps(next)  {
-  debug("in " + arguments.callee.name);
-  mozAppscb(navigator.mozApps.mgmt.getAll(), 
-            [{}],
-            "success",
-            ok,
-            next);
-}
-
 function get_installed_returns_nothing(next) {
   debug("in " + arguments.callee.name);
-  mozAppscb(navigator.mozApps.getInstalled(), 
-            [{}],
-            "success",
-            ok, 
-            next);
+  navigator.mozApps.getInstalled().onsuccess = function() {
+    is(this.result.length, 0, "should get no installed app");
+    next();
+  };
 }
 
 function install_bom(next)  {
   debug("in " + arguments.callee.name);
   var appURL = SERVERS['manifest_with_bom'];
   var pending = navigator.mozApps.install(appURL, null);
   pending.onsuccess = function()  {
      var name = pending.result.manifest.name;
      var description = pending.result.manifest.description;
      ok(name == "TheBOM  ゲゴケ゚セニツ゚ヅヂチ", name);
      ok(description == "This App is THE BOM, yo. ヅヂチ", description);
      next();
   };
 }
 
+function uninstall_bom(next)  {
+  debug("in " + arguments.callee.name);
+  var appURL = SERVERS['manifest_with_bom'];
+  uninstall(appURL, ok, function() { next(); });
+}
+
 </script> 
 
 </window>
 
--- a/extensions/cookie/nsPermission.cpp
+++ b/extensions/cookie/nsPermission.cpp
@@ -5,40 +5,54 @@
 
 #include "nsPermission.h"
 
 // nsPermission Implementation
 
 NS_IMPL_ISUPPORTS1(nsPermission, nsIPermission)
 
 nsPermission::nsPermission(const nsACString &aHost,
+                           uint32_t aAppId,
+                           bool aIsInBrowserElement,
                            const nsACString &aType,
                            uint32_t         aCapability,
                            uint32_t         aExpireType,
                            int64_t          aExpireTime)
  : mHost(aHost)
  , mType(aType)
  , mCapability(aCapability)
  , mExpireType(aExpireType)
  , mExpireTime(aExpireTime)
-{
-}
-
-nsPermission::~nsPermission()
+ , mAppId(aAppId)
+ , mIsInBrowserElement(aIsInBrowserElement)
 {
 }
 
 NS_IMETHODIMP
 nsPermission::GetHost(nsACString &aHost)
 {
   aHost = mHost;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsPermission::GetAppId(uint32_t* aAppId)
+{
+  *aAppId = mAppId;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPermission::GetIsInBrowserElement(bool* aIsInBrowserElement)
+{
+  *aIsInBrowserElement = mIsInBrowserElement;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsPermission::GetType(nsACString &aType)
 {
   aType = mType;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsPermission::GetCapability(uint32_t *aCapability)
--- a/extensions/cookie/nsPermission.h
+++ b/extensions/cookie/nsPermission.h
@@ -14,24 +14,28 @@
 class nsPermission : public nsIPermission
 {
 public:
   // nsISupports
   NS_DECL_ISUPPORTS
   NS_DECL_NSIPERMISSION
 
   nsPermission(const nsACString &aHost,
-               const nsACString &aType, 
+               uint32_t aAppId,
+               bool aIsInBrowserElement,
+               const nsACString &aType,
                uint32_t aCapability,
                uint32_t aExpireType,
                int64_t aExpireTime);
 
-  virtual ~nsPermission();
-  
+  virtual ~nsPermission() {};
+
 protected:
   nsCString mHost;
   nsCString mType;
   uint32_t  mCapability;
   uint32_t  mExpireType;
   int64_t   mExpireTime;
+  uint32_t  mAppId;
+  bool      mIsInBrowserElement;
 };
 
 #endif // nsPermission_h__
--- a/extensions/cookie/nsPermissionManager.cpp
+++ b/extensions/cookie/nsPermissionManager.cpp
@@ -18,16 +18,17 @@
 #include "nsIIDNService.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "prprf.h"
 #include "mozilla/storage.h"
 #include "mozilla/Attributes.h"
 #include "nsXULAppAPI.h"
 #include "nsIPrincipal.h"
 #include "nsContentUtils.h"
+#include "nsIScriptSecurityManager.h"
 
 static nsPermissionManager *gPermissionManager = nullptr;
 
 using mozilla::dom::ContentParent;
 using mozilla::dom::ContentChild;
 using mozilla::unused; // ha!
 
 static bool
@@ -65,52 +66,76 @@ ChildProcess()
 #define ENSURE_NOT_CHILD_PROCESS \
   ENSURE_NOT_CHILD_PROCESS_({ return NS_ERROR_NOT_AVAILABLE; })
 
 #define ENSURE_NOT_CHILD_PROCESS_NORET \
   ENSURE_NOT_CHILD_PROCESS_()
 
 ////////////////////////////////////////////////////////////////////////////////
 
-#define PL_ARENA_CONST_ALIGN_MASK 3
-#include "plarena.h"
+namespace {
 
-static PLArenaPool *gHostArena = nullptr;
-
-// making sHostArena 512b for nice allocation
-// growing is quite cheap
-#define HOST_ARENA_SIZE 512
+nsresult
+GetPrincipal(const nsACString& aHost, uint32_t aAppId, bool aIsInBrowserElement,
+             nsIPrincipal** aPrincipal)
+{
+  nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
+  NS_ENSURE_TRUE(secMan, NS_ERROR_FAILURE);
 
-// equivalent to strdup() - does no error checking,
-// we're assuming we're only called with a valid pointer
-static char *
-ArenaStrDup(const char* str, PLArenaPool* aArena)
+  nsCOMPtr<nsIURI> uri;
+  // NOTE: we use "http://" as a protocal but we will just use the host so it
+  // doesn't really matter.
+  NS_NewURI(getter_AddRefs(uri), NS_LITERAL_CSTRING("http://") + aHost);
+
+  return secMan->GetAppCodebasePrincipal(uri, aAppId, aIsInBrowserElement, aPrincipal);
+}
+
+nsresult
+GetPrincipal(nsIURI* aURI, nsIPrincipal** aPrincipal)
 {
-  void* mem;
-  const uint32_t size = strlen(str) + 1;
-  PL_ARENA_ALLOCATE(mem, aArena, size);
-  if (mem)
-    memcpy(mem, str, size);
-  return static_cast<char*>(mem);
+  nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
+  NS_ENSURE_TRUE(secMan, NS_ERROR_FAILURE);
+
+  return secMan->GetNoAppCodebasePrincipal(aURI, aPrincipal);
 }
 
-nsHostEntry::nsHostEntry(const char* aHost)
+nsresult
+GetPrincipal(const nsACString& aHost, nsIPrincipal** aPrincipal)
 {
-  mHost = ArenaStrDup(aHost, gHostArena);
+  return GetPrincipal(aHost, nsIScriptSecurityManager::NO_APP_ID, false, aPrincipal);
 }
 
-// XXX this can fail on OOM
-nsHostEntry::nsHostEntry(const nsHostEntry& toCopy)
- : mHost(toCopy.mHost)
- , mPermissions(toCopy.mPermissions)
+nsresult
+GetHostForPrincipal(nsIPrincipal* aPrincipal, nsACString& aHost)
 {
+  nsCOMPtr<nsIURI> uri;
+  nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  uri = NS_GetInnermostURI(uri);
+  NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE);
+
+  rv = uri->GetAsciiHost(aHost);
+  if (NS_FAILED(rv) || aHost.IsEmpty()) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  return NS_OK;
 }
 
+} // anonymous namespace
+
 ////////////////////////////////////////////////////////////////////////////////
 
+nsPermissionManager::PermissionKey::PermissionKey(nsIPrincipal* aPrincipal)
+{
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(GetHostForPrincipal(aPrincipal, mHost)));
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aPrincipal->GetAppId(&mAppId)));
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aPrincipal->GetIsInBrowserElement(&mIsInBrowserElement)));
+}
 
 /**
  * Simple callback used by |AsyncClose| to trigger a treatment once
  * the database is closed.
  *
  * Note: Beware that, if you hold onto a |CloseDatabaseListener| from a
  * |nsPermissionManager|, this will create a cycle.
  *
@@ -212,17 +237,17 @@ NS_IMETHODIMP DeleteFromMozHostListener:
 
   return NS_OK;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsPermissionManager Implementation
 
 static const char kPermissionsFileName[] = "permissions.sqlite";
-#define HOSTS_SCHEMA_VERSION 2
+#define HOSTS_SCHEMA_VERSION 3
 
 static const char kHostpermFileName[] = "hostperm.1";
 
 static const char kPermissionChangeNotification[] = PERM_CHANGE_NOTIFICATION;
 
 NS_IMPL_ISUPPORTS3(nsPermissionManager, nsIPermissionManager, nsIObserver, nsISupportsWeakReference)
 
 nsPermissionManager::nsPermissionManager()
@@ -263,32 +288,37 @@ nsPermissionManager::GetXPCOMSingleton()
   return gPermissionManager;
 }
 
 nsresult
 nsPermissionManager::Init()
 {
   nsresult rv;
 
-  mHostTable.Init();
+  mPermissionTable.Init();
 
   mObserverService = do_GetService("@mozilla.org/observer-service;1", &rv);
   if (NS_SUCCEEDED(rv)) {
     mObserverService->AddObserver(this, "profile-before-change", true);
     mObserverService->AddObserver(this, "profile-do-change", true);
   }
 
   if (IsChildProcess()) {
     // Get the permissions from the parent process
     InfallibleTArray<IPC::Permission> perms;
     ChildProcess()->SendReadPermissions(&perms);
 
     for (uint32_t i = 0; i < perms.Length(); i++) {
       const IPC::Permission &perm = perms[i];
-      AddInternal(perm.host, perm.type, perm.capability, 0, perm.expireType,
+
+      nsCOMPtr<nsIPrincipal> principal;
+      rv = GetPrincipal(perm.host, perm.appId, perm.isInBrowserElement, getter_AddRefs(principal));
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      AddInternal(principal, perm.type, perm.capability, 0, perm.expireType,
                   perm.expireTime, eNotify, eNoDBOperation);
     }
 
     // Stop here; we don't need the DB in the child process
     return NS_OK;
   }
 
   // ignore failure here, since it's non-fatal (we can run fine without
@@ -369,53 +399,55 @@ nsPermissionManager::InitDB(bool aRemove
         // expiration columns
         rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
               "ALTER TABLE moz_hosts ADD expireType INTEGER"));
         NS_ENSURE_SUCCESS(rv, rv);
 
         rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
               "ALTER TABLE moz_hosts ADD expireTime INTEGER"));
         NS_ENSURE_SUCCESS(rv, rv);
+      }
+
+      // fall through to the next upgrade
+
+    // TODO: we want to make default version as version 2 in order to fix bug 784875.
+    case 0:
+    case 2:
+      {
+        // Add appId/isInBrowserElement fields.
+        rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+              "ALTER TABLE moz_hosts ADD appId INTEGER"));
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+              "ALTER TABLE moz_hosts ADD isInBrowserElement INTEGER"));
+        NS_ENSURE_SUCCESS(rv, rv);
 
         rv = mDBConn->SetSchemaVersion(HOSTS_SCHEMA_VERSION);
         NS_ENSURE_SUCCESS(rv, rv);
       }
 
       // fall through to the next upgrade
-      
+
     // current version.
     case HOSTS_SCHEMA_VERSION:
       break;
 
-    case 0:
-      {
-        NS_WARNING("couldn't get schema version!");
-          
-        // the table may be usable; someone might've just clobbered the schema
-        // version. we can treat this case like a downgrade using the codepath
-        // below, by verifying the columns we care about are all there. for now,
-        // re-set the schema version in the db, in case the checks succeed (if
-        // they don't, we're dropping the table anyway).
-        rv = mDBConn->SetSchemaVersion(HOSTS_SCHEMA_VERSION);
-        NS_ENSURE_SUCCESS(rv, rv);
-      }
-      // fall through to downgrade check
-
     // downgrading.
     // if columns have been added to the table, we can still use the ones we
     // understand safely. if columns have been deleted or altered, just
     // blow away the table and start from scratch! if you change the way
     // a column is interpreted, make sure you also change its name so this
     // check will catch it.
     default:
       {
         // check if all the expected columns exist
         nsCOMPtr<mozIStorageStatement> stmt;
         rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
-          "SELECT host, type, permission, expireType, expireTime FROM moz_hosts"),
+          "SELECT host, type, permission, expireType, expireTime, appId, isInBrowserElement FROM moz_hosts"),
           getter_AddRefs(stmt));
         if (NS_SUCCEEDED(rv))
           break;
 
         // our columns aren't there - drop the table!
         rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DROP TABLE moz_hosts"));
         NS_ENSURE_SUCCESS(rv, rv);
 
@@ -427,18 +459,18 @@ nsPermissionManager::InitDB(bool aRemove
   }
 
   // make operations on the table asynchronous, for performance
   mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("PRAGMA synchronous = OFF"));
 
   // cache frequently used statements (for insertion, deletion, and updating)
   rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
     "INSERT INTO moz_hosts "
-    "(id, host, type, permission, expireType, expireTime) "
-    "VALUES (?1, ?2, ?3, ?4, ?5, ?6)"), getter_AddRefs(mStmtInsert));
+    "(id, host, type, permission, expireType, expireTime, appId, isInBrowserElement) "
+    "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)"), getter_AddRefs(mStmtInsert));
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
     "DELETE FROM moz_hosts "
     "WHERE id = ?1"), getter_AddRefs(mStmtDelete));
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
@@ -468,124 +500,126 @@ nsPermissionManager::CreateTable()
   return mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     "CREATE TABLE moz_hosts ("
       " id INTEGER PRIMARY KEY"
       ",host TEXT"
       ",type TEXT"
       ",permission INTEGER"
       ",expireType INTEGER"
       ",expireTime INTEGER"
+      ",appId INTEGER"
+      ",isInBrowserElement INTEGER"
     ")"));
 }
 
 NS_IMETHODIMP
 nsPermissionManager::Add(nsIURI     *aURI,
                          const char *aType,
                          uint32_t    aPermission,
                          uint32_t    aExpireType,
                          int64_t     aExpireTime)
 {
-  ENSURE_NOT_CHILD_PROCESS;
-
   NS_ENSURE_ARG_POINTER(aURI);
-  NS_ENSURE_ARG_POINTER(aType);
-  NS_ENSURE_TRUE(aExpireType == nsIPermissionManager::EXPIRE_NEVER ||
-                 aExpireType == nsIPermissionManager::EXPIRE_TIME ||
-                 aExpireType == nsIPermissionManager::EXPIRE_SESSION,
-                 NS_ERROR_INVALID_ARG);
 
-  nsresult rv;
-
-  // Skip addition if the permission is already expired.
-  if (aExpireType == nsIPermissionManager::EXPIRE_TIME &&
-      aExpireTime <= PR_Now() / 1000)
-    return NS_OK;
-
-  nsCAutoString host;
-  rv = GetHost(aURI, host);
+  nsCOMPtr<nsIPrincipal> principal;
+  nsresult rv = GetPrincipal(aURI, getter_AddRefs(principal));
   NS_ENSURE_SUCCESS(rv, rv);
 
-  return AddInternal(host, nsDependentCString(aType), aPermission, 0, 
-                     aExpireType, aExpireTime, eNotify, eWriteToDB);
+  return AddFromPrincipal(principal, aType, aPermission, aExpireType, aExpireTime);
 }
 
 NS_IMETHODIMP
 nsPermissionManager::AddFromPrincipal(nsIPrincipal* aPrincipal,
                                       const char* aType, uint32_t aPermission,
                                       uint32_t aExpireType, int64_t aExpireTime)
 {
+  ENSURE_NOT_CHILD_PROCESS;
   NS_ENSURE_ARG_POINTER(aPrincipal);
+  NS_ENSURE_ARG_POINTER(aType);
+  NS_ENSURE_TRUE(aExpireType == nsIPermissionManager::EXPIRE_NEVER ||
+                 aExpireType == nsIPermissionManager::EXPIRE_TIME ||
+                 aExpireType == nsIPermissionManager::EXPIRE_SESSION,
+                 NS_ERROR_INVALID_ARG);
+
+  // Skip addition if the permission is already expired.
+  if (aExpireType == nsIPermissionManager::EXPIRE_TIME &&
+      aExpireTime <= (PR_Now() / 1000)) {
+    return NS_OK;
+  }
 
   // We don't add the system principal because it actually has no URI and we
   // always allow action for them.
   if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
     return NS_OK;
   }
 
-  nsCOMPtr<nsIURI> uri;
-  aPrincipal->GetURI(getter_AddRefs(uri));
-
-  return Add(uri, aType, aPermission, aExpireType, aExpireTime);
+  return AddInternal(aPrincipal, nsDependentCString(aType), aPermission, 0,
+                     aExpireType, aExpireTime, eNotify, eWriteToDB);
 }
 
 nsresult
-nsPermissionManager::AddInternal(const nsAFlatCString &aHost,
+nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal,
                                  const nsAFlatCString &aType,
                                  uint32_t              aPermission,
                                  int64_t               aID,
                                  uint32_t              aExpireType,
                                  int64_t               aExpireTime,
                                  NotifyOperationType   aNotifyOperation,
                                  DBOperationType       aDBOperation)
 {
+  nsCAutoString host;
+  nsresult rv = GetHostForPrincipal(aPrincipal, host);
+  NS_ENSURE_SUCCESS(rv, rv);
+
   if (!IsChildProcess()) {
-    IPC::Permission permission((aHost),
-                               (aType),
+    uint32_t appId;
+    rv = aPrincipal->GetAppId(&appId);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    bool isInBrowserElement;
+    rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    IPC::Permission permission(host, appId, isInBrowserElement, aType,
                                aPermission, aExpireType, aExpireTime);
 
     nsTArray<ContentParent*> cplist;
     ContentParent::GetAll(cplist);
     for (uint32_t i = 0; i < cplist.Length(); ++i) {
       ContentParent* cp = cplist[i];
       if (cp->NeedsPermissionsUpdate())
         unused << cp->SendAddPermission(permission);
     }
   }
 
-  if (!gHostArena) {
-    gHostArena = new PLArenaPool;
-    if (!gHostArena)
-      return NS_ERROR_OUT_OF_MEMORY;    
-    PL_INIT_ARENA_POOL(gHostArena, "PermissionHostArena", HOST_ARENA_SIZE);
-  }
-
   // look up the type index
   int32_t typeIndex = GetTypeIndex(aType.get(), true);
   NS_ENSURE_TRUE(typeIndex != -1, NS_ERROR_OUT_OF_MEMORY);
 
   // When an entry already exists, PutEntry will return that, instead
   // of adding a new one
-  nsHostEntry *entry = mHostTable.PutEntry(aHost.get());
+  nsRefPtr<PermissionKey> key = new PermissionKey(aPrincipal);
+  PermissionHashKey* entry = mPermissionTable.PutEntry(key);
   if (!entry) return NS_ERROR_FAILURE;
   if (!entry->GetKey()) {
-    mHostTable.RawRemoveEntry(entry);
+    mPermissionTable.RawRemoveEntry(entry);
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   // figure out the transaction type, and get any existing permission value
   OperationType op;
   int32_t index = entry->GetPermissionIndex(typeIndex);
   if (index == -1) {
     if (aPermission == nsIPermissionManager::UNKNOWN_ACTION)
       op = eOperationNone;
     else
       op = eOperationAdding;
 
   } else {
-    nsPermissionEntry oldPermissionEntry = entry->GetPermissions()[index];
+    PermissionEntry oldPermissionEntry = entry->GetPermissions()[index];
 
     // remove the permission if the permission is UNKNOWN, update the
     // permission if its value or expire type have changed OR if the time has
     // changed and the expire type is time, otherwise, don't modify.  There's
     // no need to modify a permission that doesn't expire with time when the
     // only thing changed is the expire time.
     if (aPermission == oldPermissionEntry.mPermission && 
         aExpireType == oldPermissionEntry.mExpireType &&
@@ -613,69 +647,89 @@ nsPermissionManager::AddInternal(const n
       if (aDBOperation == eWriteToDB) {
         // we'll be writing to the database - generate a known unique id
         id = ++mLargestID;
       } else {
         // we're reading from the database - use the id already assigned
         id = aID;
       }
 
-      entry->GetPermissions().AppendElement(nsPermissionEntry(typeIndex, aPermission, id, aExpireType, aExpireTime));
+      entry->GetPermissions().AppendElement(PermissionEntry(id, typeIndex, aPermission, aExpireType, aExpireTime));
+
+      if (aDBOperation == eWriteToDB && aExpireType != nsIPermissionManager::EXPIRE_SESSION) {
+        uint32_t appId;
+        rv = aPrincipal->GetAppId(&appId);
+        NS_ENSURE_SUCCESS(rv, rv);
 
-      if (aDBOperation == eWriteToDB && aExpireType != nsIPermissionManager::EXPIRE_SESSION)
-        UpdateDB(op, mStmtInsert, id, aHost, aType, aPermission, aExpireType, aExpireTime);
+        bool isInBrowserElement;
+        rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        UpdateDB(op, mStmtInsert, id, host, aType, aPermission, aExpireType, aExpireTime, appId, isInBrowserElement);
+      }
 
       if (aNotifyOperation == eNotify) {
-        NotifyObserversWithPermission(aHost,
+        NotifyObserversWithPermission(host,
+                                      entry->GetKey()->mAppId,
+                                      entry->GetKey()->mIsInBrowserElement,
                                       mTypeArray[typeIndex],
                                       aPermission,
                                       aExpireType,
                                       aExpireTime,
                                       NS_LITERAL_STRING("added").get());
       }
 
       break;
     }
 
   case eOperationRemoving:
     {
-      nsPermissionEntry oldPermissionEntry = entry->GetPermissions()[index];
+      PermissionEntry oldPermissionEntry = entry->GetPermissions()[index];
       id = oldPermissionEntry.mID;
       entry->GetPermissions().RemoveElementAt(index);
 
       // If no more types are present, remove the entry
       if (entry->GetPermissions().IsEmpty())
-        mHostTable.RawRemoveEntry(entry);
+        mPermissionTable.RawRemoveEntry(entry);
 
       if (aDBOperation == eWriteToDB)
-        UpdateDB(op, mStmtDelete, id, EmptyCString(), EmptyCString(), 0, 
-                 nsIPermissionManager::EXPIRE_NEVER, 0);
+        // We care only about the id here so we pass dummy values for all other
+        // parameters.
+        UpdateDB(op, mStmtDelete, id, EmptyCString(), EmptyCString(), 0,
+                 nsIPermissionManager::EXPIRE_NEVER, 0, 0, false);
 
       if (aNotifyOperation == eNotify) {
-        NotifyObserversWithPermission(aHost,
+        NotifyObserversWithPermission(host,
+                                      entry->GetKey()->mAppId,
+                                      entry->GetKey()->mIsInBrowserElement,
                                       mTypeArray[typeIndex],
                                       oldPermissionEntry.mPermission,
                                       oldPermissionEntry.mExpireType,
                                       oldPermissionEntry.mExpireTime,
                                       NS_LITERAL_STRING("deleted").get());
       }
 
       break;
     }
 
   case eOperationChanging:
     {
       id = entry->GetPermissions()[index].mID;
       entry->GetPermissions()[index].mPermission = aPermission;
 
       if (aDBOperation == eWriteToDB && aExpireType != nsIPermissionManager::EXPIRE_SESSION)
-        UpdateDB(op, mStmtUpdate, id, EmptyCString(), EmptyCString(), aPermission, aExpireType, aExpireTime);
+        // We care only about the id, the permission and expireType/expireTime here.
+        // We pass dummy values for all other parameters.
+        UpdateDB(op, mStmtUpdate, id, EmptyCString(), EmptyCString(),
+                 aPermission, aExpireType, aExpireTime, 0, false);
 
       if (aNotifyOperation == eNotify) {
-        NotifyObserversWithPermission(aHost,
+        NotifyObserversWithPermission(host,
+                                      entry->GetKey()->mAppId,
+                                      entry->GetKey()->mIsInBrowserElement,
                                       mTypeArray[typeIndex],
                                       aPermission,
                                       aExpireType,
                                       aExpireTime,
                                       NS_LITERAL_STRING("changed").get());
       }
 
       break;
@@ -684,53 +738,48 @@ nsPermissionManager::AddInternal(const n
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsPermissionManager::Remove(const nsACString &aHost,
                             const char       *aType)
 {
+  nsCOMPtr<nsIPrincipal> principal;
+  nsresult rv = GetPrincipal(aHost, getter_AddRefs(principal));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return RemoveFromPrincipal(principal, aType);
+}
+
+NS_IMETHODIMP
+nsPermissionManager::RemoveFromPrincipal(nsIPrincipal* aPrincipal,
+                                         const char* aType)
+{
   ENSURE_NOT_CHILD_PROCESS;
-
+  NS_ENSURE_ARG_POINTER(aPrincipal);
   NS_ENSURE_ARG_POINTER(aType);
 
+  // System principals are never added to the database, no need to remove them.
+  if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
+    return NS_OK;
+  }
+
   // AddInternal() handles removal, just let it do the work
-  return AddInternal(PromiseFlatCString(aHost),
+  return AddInternal(aPrincipal,
                      nsDependentCString(aType),
                      nsIPermissionManager::UNKNOWN_ACTION,
                      0,
                      nsIPermissionManager::EXPIRE_NEVER,
                      0,
                      eNotify,
                      eWriteToDB);
 }
 
 NS_IMETHODIMP
-nsPermissionManager::RemoveFromPrincipal(nsIPrincipal* aPrincipal,
-                                         const char* aType)
-{
-  NS_ENSURE_ARG_POINTER(aPrincipal);
-
-  // System principals are never added to the database, no need to remove them.
-  if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
-    return NS_OK;
-  }
-
-  nsCOMPtr<nsIURI> uri;
-  aPrincipal->GetURI(getter_AddRefs(uri));
-  NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE);
-
-  nsCAutoString host;
-  uri->GetHost(host);
-
-  return Remove(host, aType);
-}
-
-NS_IMETHODIMP
 nsPermissionManager::RemoveAll()
 {
   ENSURE_NOT_CHILD_PROCESS;
   return RemoveAllInternal(true);
 }
 
 void
 nsPermissionManager::CloseDB(bool aRebuildOnSuccess)
@@ -781,132 +830,159 @@ nsPermissionManager::RemoveAllInternal(b
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsPermissionManager::TestExactPermission(nsIURI     *aURI,
                                          const char *aType,
                                          uint32_t   *aPermission)
 {
-  return CommonTestPermission(aURI, aType, aPermission, true);
-}
-
-NS_IMETHODIMP
-nsPermissionManager::TestPermission(nsIURI     *aURI,
-                                    const char *aType,
-                                    uint32_t   *aPermission)
-{
-  return CommonTestPermission(aURI, aType, aPermission, false);
-}
+  nsCOMPtr<nsIPrincipal> principal;
+  nsresult rv = GetPrincipal(aURI, getter_AddRefs(principal));
+  NS_ENSURE_SUCCESS(rv, rv);
 
-NS_IMETHODIMP
-nsPermissionManager::TestPermissionFromPrincipal(nsIPrincipal* aPrincipal,
-                                                 const char* aType,
-                                                 uint32_t* aPermission)
-{
-  NS_ENSURE_ARG_POINTER(aPrincipal);
-
-  // System principals do not have URI so we can't try to get
-  // retro-compatibility here.
-  if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
-    *aPermission = nsIPermissionManager::ALLOW_ACTION;
-    return NS_OK;
-  }
-
-  nsCOMPtr<nsIURI> uri;
-  aPrincipal->GetURI(getter_AddRefs(uri));
-
-  return TestPermission(uri, aType, aPermission);
+  return TestExactPermissionFromPrincipal(principal, aType, aPermission);
 }
 
 NS_IMETHODIMP
 nsPermissionManager::TestExactPermissionFromPrincipal(nsIPrincipal* aPrincipal,
                                                       const char* aType,
                                                       uint32_t* aPermission)
 {
   NS_ENSURE_ARG_POINTER(aPrincipal);
 
   // System principals do not have URI so we can't try to get
   // retro-compatibility here.
   if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
     *aPermission = nsIPermissionManager::ALLOW_ACTION;
     return NS_OK;
   }
 
-  nsCOMPtr<nsIURI> uri;
-  aPrincipal->GetURI(getter_AddRefs(uri));
+  return CommonTestPermission(aPrincipal, aType, aPermission, true);
+}
+
+NS_IMETHODIMP
+nsPermissionManager::TestPermission(nsIURI     *aURI,
+                                    const char *aType,
+                                    uint32_t   *aPermission)
+{
+  nsCOMPtr<nsIPrincipal> principal;
+  nsresult rv = GetPrincipal(aURI, getter_AddRefs(principal));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return TestPermissionFromPrincipal(principal, aType, aPermission);
+}
 
-  return TestExactPermission(uri, aType, aPermission);
+NS_IMETHODIMP
+nsPermissionManager::TestPermissionFromPrincipal(nsIPrincipal* aPrincipal,
+                                                 const char* aType,
+                                                 uint32_t* aPermission)
+{
+  NS_ENSURE_ARG_POINTER(aPrincipal);
+
+  // System principals do not have URI so we can't try to get
+  // retro-compatibility here.
+  if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
+    *aPermission = nsIPermissionManager::ALLOW_ACTION;
+    return NS_OK;
+  }
+
+  return CommonTestPermission(aPrincipal, aType, aPermission, false);
 }
 
 nsresult
-nsPermissionManager::CommonTestPermission(nsIURI     *aURI,
+nsPermissionManager::CommonTestPermission(nsIPrincipal* aPrincipal,
                                           const char *aType,
                                           uint32_t   *aPermission,
                                           bool        aExactHostMatch)
 {
-  NS_ENSURE_ARG_POINTER(aURI);
+  NS_ENSURE_ARG_POINTER(aPrincipal);
   NS_ENSURE_ARG_POINTER(aType);
 
   // set the default
   *aPermission = nsIPermissionManager::UNKNOWN_ACTION;
 
+  nsCOMPtr<nsIURI> uri;
+  nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
+  NS_ENSURE_SUCCESS(rv, rv);
+
   nsCAutoString host;
-  nsresult rv = GetHost(aURI, host);
+  rv = GetHostForPrincipal(aPrincipal, host);
+
   // No host doesn't mean an error. Just return the default. Unless this is
   // a file uri. In that case use a magic host.
   if (NS_FAILED(rv)) {
     bool isFile;
-    rv = aURI->SchemeIs("file", &isFile);
+    rv = uri->SchemeIs("file", &isFile);
     NS_ENSURE_SUCCESS(rv, rv);
     if (isFile) {
       host.AssignLiteral("<file>");
     }
     else {
       return NS_OK;
     }
   }
-  
+
   int32_t typeIndex = GetTypeIndex(aType, false);
   // If type == -1, the type isn't known,
   // so just return NS_OK
   if (typeIndex == -1) return NS_OK;
 
-  nsHostEntry *entry = GetHostEntry(host, typeIndex, aExactHostMatch);
-  if (entry)
+  uint32_t appId;
+  rv = aPrincipal->GetAppId(&appId);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  bool isInBrowserElement;
+  rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  PermissionHashKey* entry = GetPermissionHashKey(host, appId, isInBrowserElement,
+                                                  typeIndex, aExactHostMatch);
+  if (entry) {
     *aPermission = entry->GetPermission(typeIndex).mPermission;
+  }
 
   return NS_OK;
 }
 
-// Get hostentry for given host string and permission type.
-// walk up the domain if needed.
-// return null if nothing found.
+// Returns PermissionHashKey for a given { host, appId, isInBrowserElement } tuple.
+// This is not simply using PermissionKey because we will walk-up domains in
+// case of |host| contains sub-domains.
+// Returns null if nothing found.
 // Also accepts host on the format "<foo>". This will perform an exact match
 // lookup as the string doesn't contain any dots.
-nsHostEntry *
-nsPermissionManager::GetHostEntry(const nsAFlatCString &aHost,
-                                  uint32_t              aType,
-                                  bool                  aExactHostMatch)
+nsPermissionManager::PermissionHashKey*
+nsPermissionManager::GetPermissionHashKey(const nsACString& aHost,
+                                          uint32_t aAppId,
+                                          bool aIsInBrowserElement,
+                                          uint32_t aType,
+                                          bool aExactHostMatch)
 {
   uint32_t offset = 0;
-  nsHostEntry *entry;
+  PermissionHashKey* entry;
   int64_t now = PR_Now() / 1000;
 
   do {
-    entry = mHostTable.GetEntry(aHost.get() + offset);
+    nsRefPtr<PermissionKey> key = new PermissionKey(Substring(aHost, offset), aAppId, aIsInBrowserElement);
+    entry = mPermissionTable.GetEntry(key);
     if (entry) {
-      nsPermissionEntry permEntry = entry->GetPermission(aType);
+      PermissionEntry permEntry = entry->GetPermission(aType);
 
       // if the entry is expired, remove and keep looking for others.
       if (permEntry.mExpireType == nsIPermissionManager::EXPIRE_TIME &&
-          permEntry.mExpireTime <= now)
-        Remove(aHost, mTypeArray[aType].get());
-      else if (permEntry.mPermission != nsIPermissionManager::UNKNOWN_ACTION)
+          permEntry.mExpireTime <= now) {
+        nsCOMPtr<nsIPrincipal> principal;
+        if (NS_FAILED(GetPrincipal(aHost, aAppId, aIsInBrowserElement, getter_AddRefs(principal)))) {
+          return nullptr;
+        }
+
+        RemoveFromPrincipal(principal, mTypeArray[aType].get());
+      } else if (permEntry.mPermission != nsIPermissionManager::UNKNOWN_ACTION) {
         break;
+      }
 
       // reset entry, to be able to return null on failure
       entry = nullptr;
     }
     if (aExactHostMatch)
       break; // do not try super domains
 
     offset = aHost.FindChar('.', offset) + 1;
@@ -924,24 +1000,26 @@ struct nsGetEnumeratorData
    : array(aArray)
    , types(aTypes) {}
 
   nsCOMArray<nsIPermission> *array;
   const nsTArray<nsCString> *types;
 };
 
 static PLDHashOperator
-AddPermissionsToList(nsHostEntry *entry, void *arg)
+AddPermissionsToList(nsPermissionManager::PermissionHashKey* entry, void *arg)
 {
   nsGetEnumeratorData *data = static_cast<nsGetEnumeratorData *>(arg);
 
   for (uint32_t i = 0; i < entry->GetPermissions().Length(); ++i) {
-    nsPermissionEntry &permEntry = entry->GetPermissions()[i];
+    nsPermissionManager::PermissionEntry& permEntry = entry->GetPermissions()[i];
 
-    nsPermission *perm = new nsPermission(entry->GetHost(), 
+    nsPermission *perm = new nsPermission(entry->GetKey()->mHost,
+                                          entry->GetKey()->mAppId,
+                                          entry->GetKey()->mIsInBrowserElement,
                                           data->types->ElementAt(permEntry.mType),
                                           permEntry.mPermission,
                                           permEntry.mExpireType,
                                           permEntry.mExpireTime);
 
     data->array->AppendObject(perm);
   }
 
@@ -949,17 +1027,17 @@ AddPermissionsToList(nsHostEntry *entry,
 }
 
 NS_IMETHODIMP nsPermissionManager::GetEnumerator(nsISimpleEnumerator **aEnum)
 {
   // roll an nsCOMArray of all our permissions, then hand out an enumerator
   nsCOMArray<nsIPermission> array;
   nsGetEnumeratorData data(&array, &mTypeArray);
 
-  mHostTable.EnumerateEntries(AddPermissionsToList, &data);
+  mPermissionTable.EnumerateEntries(AddPermissionsToList, &data);
 
   return NS_NewArrayEnumerator(aEnum, array);
 }
 
 NS_IMETHODIMP nsPermissionManager::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *someData)
 {
   ENSURE_NOT_CHILD_PROCESS;
 
@@ -987,22 +1065,18 @@ NS_IMETHODIMP nsPermissionManager::Obser
 //*** nsPermissionManager private methods
 //*****************************************************************************
 
 nsresult
 nsPermissionManager::RemoveAllFromMemory()
 {
   mLargestID = 0;
   mTypeArray.Clear();
-  mHostTable.Clear();
-  if (gHostArena) {
-    PL_FinishArenaPool(gHostArena);
-    delete gHostArena;
-  }
-  gHostArena = nullptr;
+  mPermissionTable.Clear();
+
   return NS_OK;
 }
 
 // Returns -1 on failure
 int32_t
 nsPermissionManager::GetTypeIndex(const char *aType,
                                   bool        aAdd)
 {
@@ -1024,24 +1098,27 @@ nsPermissionManager::GetTypeIndex(const 
   elem->Assign(aType);
   return mTypeArray.Length() - 1;
 }
 
 // wrapper function for mangling (host,type,perm,expireType,expireTime)
 // set into an nsIPermission.
 void
 nsPermissionManager::NotifyObserversWithPermission(const nsACString &aHost,
+                                                   uint32_t          aAppId,
+                                                   bool              aIsInBrowserElement,
                                                    const nsCString  &aType,
                                                    uint32_t          aPermission,
                                                    uint32_t          aExpireType,
                                                    int64_t           aExpireTime,
                                                    const PRUnichar  *aData)
 {
   nsCOMPtr<nsIPermission> permission =
-    new nsPermission(aHost, aType, aPermission, aExpireType, aExpireTime);
+    new nsPermission(aHost, aAppId, aIsInBrowserElement, aType, aPermission,
+                     aExpireType, aExpireTime);
   if (permission)
     NotifyObservers(permission, aData);
 }
 
 // notify observers that the permission list changed. there are four possible
 // values for aData:
 // "deleted" means a permission was deleted. aPermission is the deleted permission.
 // "added"   means a permission was added. aPermission is the added permission.
@@ -1081,25 +1158,27 @@ nsPermissionManager::Read()
 
     bool hasResult;
     rv = stmtDeleteExpired->ExecuteStep(&hasResult);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   nsCOMPtr<mozIStorageStatement> stmt;
   rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
-    "SELECT id, host, type, permission, expireType, expireTime "
+    "SELECT id, host, type, permission, expireType, expireTime, appId, isInBrowserElement "
     "FROM moz_hosts"), getter_AddRefs(stmt));
   NS_ENSURE_SUCCESS(rv, rv);
 
   int64_t id;
   nsCAutoString host, type;
   uint32_t permission;
   uint32_t expireType;
   int64_t expireTime;
+  uint32_t appId;
+  bool isInBrowserElement;
   bool hasResult;
   while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
     // explicitly set our entry id counter for use in AddInternal(),
     // and keep track of the largest id so we know where to pick up.
     id = stmt->AsInt64(0);
     if (id > mLargestID)
       mLargestID = id;
 
@@ -1110,17 +1189,25 @@ nsPermissionManager::Read()
     NS_ENSURE_SUCCESS(rv, rv);
 
     permission = stmt->AsInt32(3);
     expireType = stmt->AsInt32(4);
 
     // convert into int64_t value (milliseconds)
     expireTime = stmt->AsInt64(5);
 
-    rv = AddInternal(host, type, permission, id, expireType, expireTime,
+    MOZ_ASSERT(stmt->AsInt64(6) >= 0);
+    appId = static_cast<uint32_t>(stmt->AsInt64(6));
+    isInBrowserElement = static_cast<bool>(stmt->AsInt32(7));
+
+    nsCOMPtr<nsIPrincipal> principal;
+    nsresult rv = GetPrincipal(host, appId, isInBrowserElement, getter_AddRefs(principal));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = AddInternal(principal, type, permission, id, expireType, expireTime,
                      eDontNotify, eNoDBOperation);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return NS_OK;
 }
 
 static const char kMatchTypeHost[] = "host";
@@ -1180,17 +1267,21 @@ nsPermissionManager::Import()
 
       // hosts might be encoded in UTF8; switch them to ACE to be consistent
       if (!IsASCII(lineArray[3])) {
         rv = NormalizeToACE(lineArray[3]);
         if (NS_FAILED(rv))
           continue;
       }
 
-      rv = AddInternal(lineArray[3], lineArray[1], permission, 0, 
+      nsCOMPtr<nsIPrincipal> principal;
+      nsresult rv = GetPrincipal(lineArray[3], getter_AddRefs(principal));
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      rv = AddInternal(principal, lineArray[1], permission, 0,
                        nsIPermissionManager::EXPIRE_NEVER, 0, eDontNotify, eWriteToDB);
       NS_ENSURE_SUCCESS(rv, rv);
     }
   }
 
   // we're done importing - delete the old file
   permissionsFile->Remove(false);
 
@@ -1205,39 +1296,27 @@ nsPermissionManager::NormalizeToACE(nsCS
     nsresult rv;
     mIDNService = do_GetService(NS_IDNSERVICE_CONTRACTID, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return mIDNService->ConvertUTF8toACE(aHost, aHost);
 }
 
-nsresult
-nsPermissionManager::GetHost(nsIURI *aURI, nsACString &aResult)
-{
-  nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(aURI);
-  if (!innerURI) return NS_ERROR_FAILURE;
-
-  nsresult rv = innerURI->GetAsciiHost(aResult);
-
-  if (NS_FAILED(rv) || aResult.IsEmpty())
-    return NS_ERROR_UNEXPECTED;
-
-  return NS_OK;
-}
-
 void
 nsPermissionManager::UpdateDB(OperationType         aOp,
                               mozIStorageStatement* aStmt,
                               int64_t               aID,
                               const nsACString     &aHost,
                               const nsACString     &aType,
                               uint32_t              aPermission,
                               uint32_t              aExpireType,
-                              int64_t               aExpireTime)
+                              int64_t               aExpireTime,
+                              uint32_t              aAppId,
+                              bool                  aIsInBrowserElement)
 {
   ENSURE_NOT_CHILD_PROCESS_NORET;
 
   nsresult rv;
 
   // no statement is ok - just means we don't have a profile
   if (!aStmt)
     return;
@@ -1256,16 +1335,22 @@ nsPermissionManager::UpdateDB(OperationT
 
       rv = aStmt->BindInt32ByIndex(3, aPermission);
       if (NS_FAILED(rv)) break;
 
       rv = aStmt->BindInt32ByIndex(4, aExpireType);
       if (NS_FAILED(rv)) break;
 
       rv = aStmt->BindInt64ByIndex(5, aExpireTime);
+      if (NS_FAILED(rv)) break;
+
+      rv = aStmt->BindInt64ByIndex(6, aAppId);
+      if (NS_FAILED(rv)) break;
+
+      rv = aStmt->BindInt64ByIndex(7, aIsInBrowserElement);
       break;
     }
 
   case eOperationRemoving:
     {
       rv = aStmt->BindInt64ByIndex(0, aID);
       break;
     }
--- a/extensions/cookie/nsPermissionManager.h
+++ b/extensions/cookie/nsPermissionManager.h
@@ -11,125 +11,152 @@
 #include "nsIObserverService.h"
 #include "nsWeakReference.h"
 #include "nsCOMPtr.h"
 #include "nsIFile.h"
 #include "nsTHashtable.h"
 #include "nsTArray.h"
 #include "nsString.h"
 #include "nsPermission.h"
+#include "nsHashKeys.h"
+#include "nsAutoPtr.h"
 
 class nsIPermission;
 class nsIIDNService;
 class mozIStorageConnection;
 class mozIStorageStatement;
 
 ////////////////////////////////////////////////////////////////////////////////
 
-class nsPermissionEntry
-{
-public:
-  nsPermissionEntry(uint32_t aType, uint32_t aPermission, int64_t aID, 
-                    uint32_t aExpireType, int64_t aExpireTime)
-   : mType(aType)
-   , mPermission(aPermission)
-   , mID(aID)
-   , mExpireType(aExpireType)
-   , mExpireTime(aExpireTime) {}
-
-  uint32_t mType;
-  uint32_t mPermission;
-  int64_t  mID;
-  uint32_t mExpireType;
-  int64_t  mExpireTime;
-};
-
-class nsHostEntry : public PLDHashEntryHdr
-{
-public:
-  // Hash methods
-  typedef const char* KeyType;
-  typedef const char* KeyTypePointer;
-
-  nsHostEntry(const char* aHost);
-  nsHostEntry(const nsHostEntry& toCopy);
-
-  ~nsHostEntry()
-  {
-  }
-
-  KeyType GetKey() const
-  {
-    return mHost;
-  }
-
-  bool KeyEquals(KeyTypePointer aKey) const
-  {
-    return !strcmp(mHost, aKey);
-  }
-
-  static KeyTypePointer KeyToPointer(KeyType aKey)
-  {
-    return aKey;
-  }
-
-  static PLDHashNumber HashKey(KeyTypePointer aKey)
-  {
-    // PL_DHashStringKey doesn't use the table parameter, so we can safely
-    // pass nullptr
-    return PL_DHashStringKey(nullptr, aKey);
-  }
-
-  // force the hashtable to use the copy constructor when shuffling entries
-  // around, otherwise the Auto part of our nsAutoTArray won't be happy!
-  enum { ALLOW_MEMMOVE = false };
-
-  // Permissions methods
-  inline const nsDependentCString GetHost() const
-  {
-    return nsDependentCString(mHost);
-  }
-
-  inline nsTArray<nsPermissionEntry> & GetPermissions()
-  {
-    return mPermissions;
-  }
-
-  inline int32_t GetPermissionIndex(uint32_t aType) const
-  {
-    for (uint32_t i = 0; i < mPermissions.Length(); ++i)
-      if (mPermissions[i].mType == aType)
-        return i;
-
-    return -1;
-  }
-
-  inline nsPermissionEntry GetPermission(uint32_t aType) const
-  {
-    for (uint32_t i = 0; i < mPermissions.Length(); ++i)
-      if (mPermissions[i].mType == aType)
-        return mPermissions[i];
-
-    // unknown permission... return relevant data 
-    nsPermissionEntry unk = nsPermissionEntry(aType, nsIPermissionManager::UNKNOWN_ACTION,
-                                              -1, nsIPermissionManager::EXPIRE_NEVER, 0);
-    return unk;
-  }
-
-private:
-  const char *mHost;
-  nsAutoTArray<nsPermissionEntry, 1> mPermissions;
-};
-
-
 class nsPermissionManager : public nsIPermissionManager,
                             public nsIObserver,
                             public nsSupportsWeakReference
 {
 public:
+  class PermissionEntry
+  {
+  public:
+    PermissionEntry(int64_t aID, uint32_t aType, uint32_t aPermission,
+                    uint32_t aExpireType, int64_t aExpireTime)
+     : mID(aID)
+     , mType(aType)
+     , mPermission(aPermission)
+     , mExpireType(aExpireType)
+     , mExpireTime(aExpireTime)
+    {}
+
+    int64_t  mID;
+    uint32_t mType;
+    uint32_t mPermission;
+    uint32_t mExpireType;
+    int64_t  mExpireTime;
+  };
+
+  /**
+   * PermissionKey is the key used by PermissionHashKey hash table.
+   *
+   * NOTE: It could be implementing nsIHashable but there is no reason to worry
+   * with XPCOM interfaces while we don't need to.
+   */
+  class PermissionKey
+  {
+  public:
+    PermissionKey(nsIPrincipal* aPrincipal);
+    PermissionKey(const nsACString& aHost,
+                  uint32_t aAppId,
+                  bool aIsInBrowserElement)
+      : mHost(aHost)
+      , mAppId(aAppId)
+      , mIsInBrowserElement(aIsInBrowserElement)
+    {
+    }
+
+    bool operator==(const PermissionKey& aKey) const {
+      return mHost.Equals(aKey.mHost) &&
+             mAppId == aKey.mAppId &&
+             mIsInBrowserElement == aKey.mIsInBrowserElement;
+    }
+
+    PLDHashNumber GetHashCode() const {
+      nsCAutoString str;
+      str.Assign(mHost);
+      str.AppendInt(mAppId);
+      str.AppendInt(static_cast<int32_t>(mIsInBrowserElement));
+
+      return mozilla::HashString(str);
+    }
+
+    NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PermissionKey);
+
+    nsCString mHost;
+    uint32_t  mAppId;
+    bool      mIsInBrowserElement;
+
+  private:
+    // Default ctor shouldn't be used.
+    PermissionKey() MOZ_DELETE;
+
+    // Dtor shouldn't be used outside of the class.
+    ~PermissionKey() {};
+  };
+
+  class PermissionHashKey : public nsRefPtrHashKey<PermissionKey>
+  {
+  public:
+    PermissionHashKey(const PermissionKey* aPermissionKey)
+      : nsRefPtrHashKey<PermissionKey>(aPermissionKey)
+    {}
+
+    PermissionHashKey(const PermissionHashKey& toCopy)
+      : nsRefPtrHashKey<PermissionKey>(toCopy)
+      , mPermissions(toCopy.mPermissions)
+    {}
+
+    bool KeyEquals(const PermissionKey* aKey) const
+    {
+      return *aKey == *GetKey();
+    }
+
+    static PLDHashNumber HashKey(const PermissionKey* aKey)
+    {
+      return aKey->GetHashCode();
+    }
+
+    // Force the hashtable to use the copy constructor when shuffling entries
+    // around, otherwise the Auto part of our nsAutoTArray won't be happy!
+    enum { ALLOW_MEMMOVE = false };
+
+    inline nsTArray<PermissionEntry> & GetPermissions()
+    {
+      return mPermissions;
+    }
+
+    inline int32_t GetPermissionIndex(uint32_t aType) const
+    {
+      for (uint32_t i = 0; i < mPermissions.Length(); ++i)
+        if (mPermissions[i].mType == aType)
+          return i;
+
+      return -1;
+    }
+
+    inline PermissionEntry GetPermission(uint32_t aType) const
+    {
+      for (uint32_t i = 0; i < mPermissions.Length(); ++i)
+        if (mPermissions[i].mType == aType)
+          return mPermissions[i];
+
+      // unknown permission... return relevant data 
+      return PermissionEntry(-1, aType, nsIPermissionManager::UNKNOWN_ACTION,
+                             nsIPermissionManager::EXPIRE_NEVER, 0);
+    }
+
+  private:
+    nsAutoTArray<PermissionEntry, 1> mPermissions;
+  };
 
   // nsISupports
   NS_DECL_ISUPPORTS
   NS_DECL_NSIPERMISSIONMANAGER
   NS_DECL_NSIOBSERVER
 
   nsPermissionManager();
   virtual ~nsPermissionManager();
@@ -149,77 +176,81 @@ public:
     eWriteToDB
   };
 
   enum NotifyOperationType {
     eDontNotify,
     eNotify
   };
 
-  nsresult AddInternal(const nsAFlatCString &aHost,
+  nsresult AddInternal(nsIPrincipal* aPrincipal,
                        const nsAFlatCString &aType,
                        uint32_t aPermission,
                        int64_t aID,
                        uint32_t aExpireType,
                        int64_t  aExpireTime,
                        NotifyOperationType aNotifyOperation,
                        DBOperationType aDBOperation);
 
 private:
-
   int32_t GetTypeIndex(const char *aTypeString,
                        bool        aAdd);
 
-  nsHostEntry *GetHostEntry(const nsAFlatCString &aHost,
-                            uint32_t              aType,
-                            bool                  aExactHostMatch);
+  PermissionHashKey* GetPermissionHashKey(const nsACString& aHost,
+                                          uint32_t aAppId,
+                                          bool aIsInBrowserElement,
+                                          uint32_t          aType,
+                                          bool              aExactHostMatch);
 
-  nsresult CommonTestPermission(nsIURI     *aURI,
+  nsresult CommonTestPermission(nsIPrincipal* aPrincipal,
                                 const char *aType,
                                 uint32_t   *aPermission,
                                 bool        aExactHostMatch);
 
   nsresult InitDB(bool aRemoveFile);
   nsresult CreateTable();
   nsresult Import();
   nsresult Read();
   void     NotifyObserversWithPermission(const nsACString &aHost,
+                                         uint32_t          aAppId,
+                                         bool              aIsInBrowserElement,
                                          const nsCString  &aType,
                                          uint32_t          aPermission,
                                          uint32_t          aExpireType,
                                          int64_t           aExpireTime,
                                          const PRUnichar  *aData);
   void     NotifyObservers(nsIPermission *aPermission, const PRUnichar *aData);
 
   // Finalize all statements, close the DB and null it.
   // if aRebuildOnSuccess, reinitialize database
   void     CloseDB(bool aRebuildOnSuccess = false);
 
   nsresult RemoveAllInternal(bool aNotifyObservers);
   nsresult RemoveAllFromMemory();
   nsresult NormalizeToACE(nsCString &aHost);
-  nsresult GetHost(nsIURI *aURI, nsACString &aResult);
   static void UpdateDB(OperationType         aOp,
                        mozIStorageStatement* aStmt,
                        int64_t               aID,
                        const nsACString     &aHost,
                        const nsACString     &aType,
                        uint32_t              aPermission,
                        uint32_t              aExpireType,
-                       int64_t               aExpireTime);
+                       int64_t               aExpireTime,
+                       uint32_t              aAppId,
+                       bool                  aIsInBrowserElement);
 
   nsCOMPtr<nsIObserverService> mObserverService;
   nsCOMPtr<nsIIDNService>      mIDNService;
 
   nsCOMPtr<mozIStorageConnection> mDBConn;
   nsCOMPtr<mozIStorageStatement> mStmtInsert;
   nsCOMPtr<mozIStorageStatement> mStmtDelete;
   nsCOMPtr<mozIStorageStatement> mStmtUpdate;
 
-  nsTHashtable<nsHostEntry>    mHostTable;
+  nsTHashtable<PermissionHashKey> mPermissionTable;
   // a unique, monotonically increasing id used to identify each database entry
   int64_t                      mLargestID;
 
   // An array to store the strings identifying the different types.
   nsTArray<nsCString>          mTypeArray;
 
   // Initially, |false|. Set to |true| once shutdown has started, to avoid
   // reopening the database.
--- a/extensions/cookie/test/Makefile.in
+++ b/extensions/cookie/test/Makefile.in
@@ -47,16 +47,20 @@ MOCHITEST_FILES = \
   test_same_base_domain_3.html \
   test_same_base_domain_4.html \
   file_localhost_inner.html \
   test_same_base_domain_5.html \
   test_same_base_domain_6.html \
   file_loopback_inner.html \
   $(NULL)
 
+MOCHITEST_CHROME_FILES = \
+  test_permissionmanager_app_isolation.html \
+  $(NULL)
+
 MOCHITEST_BROWSER_FILES = \
   browser_test_favicon.js \
   $(NULL)
 
 XPCSHELL_TESTS = unit
 
 # FIXME/bug 575918: out-of-process xpcshell is broken on OS X
 ifneq ($(OS_ARCH),Darwin)
new file mode 100644
--- /dev/null
+++ b/extensions/cookie/test/test_permissionmanager_app_isolation.html
@@ -0,0 +1,168 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=758258
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for nsIPrincipal extendedOrigin, appStatus and appId</title>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=758258">Mozilla Bug 758258</a>
+<p id="display"></p>
+<div id="content">
+  
+</div>
+<pre id="test">
+<script type="application/javascript;version=1.7">
+
+/** Test for Bug 758258 **/
+
+var Ci = Components.interfaces;
+var Cc = Components.classes;
+
+SimpleTest.waitForExplicitFinish();
+
+var permManager = Cc["@mozilla.org/permissionmanager;1"]
+                    .getService(Ci.nsIPermissionManager);
+
+const gPermName = 'foobar';
+
+var previousPrefs = {
+  mozBrowserFramesEnabled: undefined,
+};
+
+try {
+  previousPrefs.mozBrowserFramesEnabled = SpecialPowers.getBoolPref('dom.mozBrowserFramesEnabled');
+} catch(e)
+{
+}
+
+SpecialPowers.setBoolPref('dom.mozBrowserFramesEnabled', true);
+
+// We use http://test/ as url so all apps use the same url and app isolation is
+// more obvious.
+var gData = [
+  // APP 1
+  {
+    app: 'http://example.org/manifest.webapp',
+    action: 'read-no',
+    src: 'http://test/',
+  },
+  {
+    app: 'http://example.org/manifest.webapp',
+    action: 'write',
+    src: 'http://test/',
+  },
+  {
+    app: 'http://example.org/manifest.webapp',
+    action: 'read-yes',
+    src: 'http://test/',
+  },
+  // APP 2
+  {
+    app: 'https://example.com/manifest.webapp',
+    action: 'read-no',
+    src: 'http://test/',
+  },
+  {
+    app: 'https://example.com/manifest.webapp',
+    action: 'write',
+    src: 'http://test/',
+  },
+  {
+    app: 'https://example.com/manifest.webapp',
+    action: 'read-yes',
+    src: 'http://test/',
+  },
+  // Browser
+  {
+    browser: true,
+    action: 'read-no',
+    src: 'http://test/',
+  },
+  {
+    browser: true,
+    action: 'write',
+    src: 'http://test/',
+  },
+  {
+    browser: true,
+    action: 'read-yes',
+    src: 'http://test/',
+  },
+];
+
+function runTest() {
+  for (var i in gData) {
+    var iframe = document.createElement('iframe');
+    var data = gData[i];
+
+    if (data.app) {
+      iframe.setAttribute('mozbrowser', '');
+      iframe.setAttribute('mozapp', data.app);
+    } else if (data.browser) {
+      iframe.setAttribute('mozbrowser', '');
+    }
+
+    if (data.app || data.browser) {
+      iframe.addEventListener('load', function(e) {
+        var principal = iframe.contentDocument.nodePrincipal;
+
+        switch (data.action) {
+          case 'read-no':
+            is(permManager.testPermissionFromPrincipal(principal, gPermName),
+               Ci.nsIPermissionManager.UNKNOWN_ACTION,
+               "Permission should not be set yet");
+            is(permManager.testExactPermissionFromPrincipal(principal, gPermName),
+               Ci.nsIPermissionManager.UNKNOWN_ACTION,
+               "Permission should not be set yet");
+            break;
+          case 'write':
+            permManager.addFromPrincipal(principal, gPermName, Ci.nsIPermissionManager.ALLOW_ACTION);
+            break;
+          case 'read-yes':
+            is(permManager.testPermissionFromPrincipal(principal, gPermName),
+               Ci.nsIPermissionManager.ALLOW_ACTION,
+               "Permission should be set");
+            is(permManager.testExactPermissionFromPrincipal(principal, gPermName),
+               Ci.nsIPermissionManager.ALLOW_ACTION,
+               "Permission should be set");
+            break;
+          default:
+            ok(false, "shouldn't be there");
+        }
+
+        // Calling removeChild() produces an error that creates failures.
+        //document.getElementById('content').removeChild(iframe);
+
+        i++;
+        if (i >= gData.length) {
+          if (previousPrefs.mozBrowserFramesEnabled !== undefined) {
+            SpecialPowers.setBoolPref('dom.mozBrowserFramesEnabled', previousPrefs.mozBrowserFramesEnabled);
+          }
+
+          SimpleTest.finish();
+        } else {
+          gTestRunner.next();
+        }
+      });
+    }
+
+    iframe.src = data.src;
+
+    document.getElementById('content').appendChild(iframe);
+
+    yield;
+  }
+}
+
+var gTestRunner = runTest();
+gTestRunner.next();
+
+</script>
+</pre>
+</body>
+</html>
--- a/netwerk/base/public/nsIPermission.idl
+++ b/netwerk/base/public/nsIPermission.idl
@@ -1,31 +1,41 @@
 /* -*- 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"
 
-[scriptable, uuid(5036f0f6-f77b-4168-9d57-a1c0dd66cf02)]
+[scriptable, uuid(cfb08e46-193c-4be7-a467-d7775fb2a31e)]
 /**
  * This interface defines a "permission" object,
  * used to specify allowed/blocked objects from
  * user-specified sites (cookies, images etc).
  */
 
 interface nsIPermission : nsISupports
 {
     /**
      * The name of the host for which the permission is set
      */
     readonly attribute AUTF8String host;
 
     /**
+     * The id of the app for which the permission is set.
+     */
+    readonly attribute unsigned long appId;
+
+    /**
+     * Whether the permission has been set to a page inside a browser element.
+     */
+    readonly attribute boolean isInBrowserElement;
+
+    /**
      * a case-sensitive ASCII string, indicating the type of permission
      * (e.g., "cookie", "image", etc).
      * This string is specified by the consumer when adding a permission 
      * via nsIPermissionManager.
      * @see nsIPermissionManager
      */
     readonly attribute ACString type;
 
--- a/netwerk/ipc/NeckoMessageUtils.h
+++ b/netwerk/ipc/NeckoMessageUtils.h
@@ -15,53 +15,76 @@ namespace IPC {
 
 // nsIPermissionManager utilities
 
 struct Permission
 {
   nsCString host, type;
   uint32_t capability, expireType;
   int64_t expireTime;
+  uint32_t appId;
+  bool isInBrowserElement;
 
   Permission() { }
   Permission(const nsCString& aHost,
+             const uint32_t aAppId,
+             const bool aIsInBrowserElement,
              const nsCString& aType,
              const uint32_t aCapability,
              const uint32_t aExpireType,
              const int64_t aExpireTime) : host(aHost),
                                           type(aType),
                                           capability(aCapability),
                                           expireType(aExpireType),
-                                          expireTime(aExpireTime) { }
+                                          expireTime(aExpireTime),
+                                          appId(aAppId),
+                                          isInBrowserElement(aIsInBrowserElement)
+  {}
 };
 
 template<>
 struct ParamTraits<Permission>
 {
   static void Write(Message* aMsg, const Permission& aParam)
   {
     WriteParam(aMsg, aParam.host);
     WriteParam(aMsg, aParam.type);
     WriteParam(aMsg, aParam.capability);
     WriteParam(aMsg, aParam.expireType);
     WriteParam(aMsg, aParam.expireTime);
+    WriteParam(aMsg, aParam.appId);
+    WriteParam(aMsg, aParam.isInBrowserElement);
   }
 
   static bool Read(const Message* aMsg, void** aIter, Permission* aResult)
   {
     return ReadParam(aMsg, aIter, &aResult->host) &&
            ReadParam(aMsg, aIter, &aResult->type) &&
            ReadParam(aMsg, aIter, &aResult->capability) &&
            ReadParam(aMsg, aIter, &aResult->expireType) &&
-           ReadParam(aMsg, aIter, &aResult->expireTime);
+           ReadParam(aMsg, aIter, &aResult->expireTime) &&
+           ReadParam(aMsg, aIter, &aResult->appId) &&
+           ReadParam(aMsg, aIter, &aResult->isInBrowserElement);
   }
 
-  static void Log(const Permission& aParam, std::wstring* aLog)
+  static void Log(const Permission& p, std::wstring* l)
   {
-    aLog->append(StringPrintf(L"[%s]", aParam.host.get()));
+    l->append(L"(");
+    LogParam(p.host, l);
+    l->append(L", ");
+    LogParam(p.appId, l);
+    l->append(L", ");
+    LogParam(p.isInBrowserElement, l);
+    l->append(L", ");
+    LogParam(p.capability, l);
+    l->append(L", ");
+    LogParam(p.expireTime, l);
+    l->append(L", ");
+    LogParam(p.expireType, l);
+    l->append(L")");
   }
 };
 
 template<>
 struct ParamTraits<PRNetAddr>
 {
   static void Write(Message* aMsg, const PRNetAddr &aParam)
   {
--- a/testing/mochitest/tests/SimpleTest/SpecialPowersObserverAPI.js
+++ b/testing/mochitest/tests/SimpleTest/SpecialPowersObserverAPI.js
@@ -170,24 +170,27 @@ SpecialPowersObserverAPI.prototype = {
             throw new SpecialPowersException("Invalid operation for SPProcessCrashService");
         }
         break;
 
       case "SPPermissionManager":
         let perms =
           Components.classes["@mozilla.org/permissionmanager;1"]
                     .getService(Components.interfaces.nsIPermissionManager);
-        let uri = this._getURI(aMessage.json.url);
+        let msg = aMessage.json;
 
-        switch (aMessage.json.op) {
+        let secMan = Cc["@mozilla.org/scriptsecuritymanager;1"].getService(Ci.nsIScriptSecurityManager);
+        let principal = secMan.getAppCodebasePrincipal(this._getURI(msg.url), msg.appId, msg.isInBrowserElement);
+
+        switch (msg.op) {
           case "add":
-            perms.add(uri, aMessage.json.type, aMessage.json.permission);
+            perms.addFromPrincipal(principal, msg.type, msg.permission);
             break;
           case "remove":
-            perms.remove(uri.host, aMessage.json.type);
+            perms.removeFromPrincipal(principal, msg.type);
             break;
           default:
             throw new SpecialPowersException("Invalid operation for " +
                                              "SPPermissionManager");
         }
         break;
 
       default:
--- a/testing/mochitest/tests/SimpleTest/specialpowersAPI.js
+++ b/testing/mochitest/tests/SimpleTest/specialpowersAPI.js
@@ -1126,50 +1126,68 @@ SpecialPowersAPI.prototype = {
     obsvc.notifyObservers(document, "fullscreen-approved", null);
   },
   
   removeFullscreenAllowed: function(document) {
     var pm = Cc["@mozilla.org/permissionmanager;1"].getService(Ci.nsIPermissionManager);
     pm.removeFromPrincipal(document.nodePrincipal, "fullscreen");
   },
 
-  _getURI: function(urlOrDocument) {
-    if (typeof(urlOrDocument) == "string") {
-      return Cc["@mozilla.org/network/io-service;1"].
-               getService(Ci.nsIIOService).
-               newURI(urlOrDocument, null, null);
+  _getInfoFromPermissionArg: function(arg) {
+    let url = "";
+    let appId = Ci.nsIScriptSecurityManager.NO_APP_ID;
+    let isInBrowserElement = false;
+
+    if (typeof(arg) == "string") {
+      // It's an URL.
+      url = Cc["@mozilla.org/network/io-service;1"]
+              .getService(Ci.nsIIOService)
+              .newURI(arg, null, null)
+              .spec;
+    } else if (arg.nodePrincipal) {
+      // It's a document.
+      url = arg.nodePrincipal.URI.spec;
+      appId = arg.nodePrincipal.appId;
+      isInBrowserElement = arg.nodePrincipal.isInBrowserElement;
+    } else {
+      url = arg.url;
+      appId = arg.appId;
+      isInBrowserElement = arg.isInBrowserElement;
     }
-    // Assume document.
-    return this.getDocumentURIObject(urlOrDocument);
+
+    return [ url, appId, isInBrowserElement ];
   },
 
-  addPermission: function(type, allow, urlOrDocument) {
-    let uri = this._getURI(urlOrDocument);
+  addPermission: function(type, allow, arg) {
+    let [url, appId, isInBrowserElement] = this._getInfoFromPermissionArg(arg);
 
-    let permission = allow ?
-                     Ci.nsIPermissionManager.ALLOW_ACTION :
-                     Ci.nsIPermissionManager.DENY_ACTION;
+    let permission = allow ? Ci.nsIPermissionManager.ALLOW_ACTION
+                           : Ci.nsIPermissionManager.DENY_ACTION;
 
     var msg = {
       'op': "add",
       'type': type,
-      'url': uri.spec,
-      'permission': permission
+      'permission': permission,
+      'url': url,
+      'appId': appId,
+      'isInBrowserElement': isInBrowserElement
     };
 
     this._sendSyncMessage('SPPermissionManager', msg);
   },
 
-  removePermission: function(type, urlOrDocument) {
-    let uri = this._getURI(urlOrDocument);
+  removePermission: function(type, arg) {
+    let [url, appId, isInBrowserElement] = this._getInfoFromPermissionArg(arg);
 
     var msg = {
       'op': "remove",
       'type': type,
-      'url': uri.spec
+      'url': url,
+      'appId': appId,
+      'isInBrowserElement': isInBrowserElement
     };
 
     this._sendSyncMessage('SPPermissionManager', msg);
   },
 
   getMozFullPath: function(file) {
     return file.mozFullPath;
   }
--- a/testing/mozbase/mozprofile/mozprofile/permissions.py
+++ b/testing/mozbase/mozprofile/mozprofile/permissions.py
@@ -226,36 +226,41 @@ class Permissions(object):
                 self._locations.read(locations)
 
     def write_db(self, locations):
         """write permissions to the sqlite database"""
 
         # Open database and create table
         permDB = sqlite3.connect(os.path.join(self._profileDir, "permissions.sqlite"))
         cursor = permDB.cursor();
+
+        cursor.execute("PRAGMA user_version=3");
+
         # SQL copied from
         # http://mxr.mozilla.org/mozilla-central/source/extensions/cookie/nsPermissionManager.cpp
         cursor.execute("""CREATE TABLE IF NOT EXISTS moz_hosts (
            id INTEGER PRIMARY KEY,
            host TEXT,
            type TEXT,
            permission INTEGER,
            expireType INTEGER,
-           expireTime INTEGER)""")
+           expireTime INTEGER,
+           appId INTEGER,
+           isInBrowserElement INTEGER)""")
 
         for location in locations:
             # set the permissions
             permissions = { 'allowXULXBL': 'noxul' not in location.options }
             for perm, allow in permissions.iteritems():
                 self._num_permissions += 1
                 if allow:
                     permission_type = 1
                 else:
                     permission_type = 2
-                cursor.execute("INSERT INTO moz_hosts values(?, ?, ?, ?, 0, 0)",
+                cursor.execute("INSERT INTO moz_hosts values(?, ?, ?, ?, 0, 0, 0, 0)",
                                (self._num_permissions, location.host, perm,
                                 permission_type))
 
         # Commit and close
         permDB.commit()
         cursor.close()
 
     def network_prefs(self, proxy=False):