Bug 599428 - Optimize permissions IPC. r=dwitte a=blocking-fennec
authorAlon Zakai <azakai@mozilla.com>
Sat, 09 Oct 2010 11:07:38 -0700
changeset 55255 e14e2cc7d786b3a7f7a1f5cd5211c700b35ab9ff
parent 55254 26291a8c4caf2e51c41d41662da5209d97b7efcd
child 55256 e1042569cee403684e6da784498ce4371ed4a60c
push idunknown
push userunknown
push dateunknown
reviewersdwitte, blocking-fennec
bugs599428
milestone2.0b8pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 599428 - Optimize permissions IPC. r=dwitte a=blocking-fennec
dom/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/Makefile.in
dom/ipc/PContent.ipdl
extensions/cookie/nsCookieModule.cpp
extensions/cookie/nsPermissionManager.cpp
extensions/cookie/nsPermissionManager.h
extensions/cookie/test/Makefile.in
extensions/cookie/test/unit_ipc/test_child.js
extensions/cookie/test/unit_ipc/test_parent.js
netwerk/ipc/NeckoMessageUtils.h
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -72,16 +72,21 @@
 #include "base/task.h"
 
 #include "nsChromeRegistryContent.h"
 #include "mozilla/chrome/RegistryMessageUtils.h"
 #include "nsFrameMessageManager.h"
 
 #include "nsIGeolocationProvider.h"
 
+#ifdef MOZ_PERMISSIONS
+#include "nsPermission.h"
+#include "nsPermissionManager.h"
+#endif
+
 using namespace mozilla::ipc;
 using namespace mozilla::net;
 using namespace mozilla::places;
 
 namespace mozilla {
 namespace dom {
 class AlertObserver
 {
@@ -431,10 +436,32 @@ ContentChild::RecvGeolocationUpdate(cons
   if (!gs) {
     return true;
   }
   nsCOMPtr<nsIDOMGeoPosition> position = somewhere;
   gs->Update(position);
   return true;
 }
 
+bool
+ContentChild::RecvAddPermission(const IPC::Permission& permission)
+{
+#if MOZ_PERMISSIONS
+  nsPermissionManager *permissionManager =
+    (nsPermissionManager*)nsPermissionManager::GetSingleton();
+  NS_ABORT_IF_FALSE(permissionManager, 
+                   "We have no permissionManager in the Content process !");
+
+  permissionManager->AddInternal(nsCString(permission.host),
+                                 nsCString(permission.type),
+                                 permission.capability,
+                                 0,
+                                 permission.expireType,
+                                 permission.expireTime,
+                                 nsPermissionManager::eNotify,
+                                 nsPermissionManager::eNoDBOperation);
+#endif
+
+  return true;
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -107,16 +107,18 @@ public:
     virtual bool RecvPreferenceUpdate(const nsCString& aDomain);
     
     virtual bool RecvNotifyAlertsObserver(const nsCString& aType, const nsString& aData);
 
     virtual bool RecvAsyncMessage(const nsString& aMsg, const nsString& aJSON);
 
     virtual bool RecvGeolocationUpdate(const GeoPosition& somewhere);
 
+    virtual bool RecvAddPermission(const IPC::Permission& permission);
+
 private:
     NS_OVERRIDE
     virtual void ActorDestroy(ActorDestroyReason why);
 
     NS_OVERRIDE
     virtual void ProcessingError(Result what);
 
     /**
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -59,16 +59,21 @@
 #include "nsFrameMessageManager.h"
 #include "nsIAlertsService.h"
 #include "nsToolkitCompsCID.h"
 #include "nsIDOMGeoGeolocation.h"
 #include "nsIConsoleService.h"
 #include "nsIScriptError.h"
 #include "nsConsoleMessage.h"
 
+#ifdef MOZ_PERMISSIONS
+#include "nsPermission.h"
+#include "nsPermissionManager.h"
+#endif
+
 #include "mozilla/dom/ExternalHelperAppParent.h"
 
 using namespace mozilla::ipc;
 using namespace mozilla::net;
 using namespace mozilla::places;
 using mozilla::MonitorAutoEnter;
 
 namespace mozilla {
@@ -193,54 +198,68 @@ ContentParent::IsAlive()
 bool
 ContentParent::RecvReadPrefs(nsCString* prefs)
 {
     EnsurePrefService();
     mPrefService->SerializePreferences(*prefs);
     return true;
 }
 
-bool
-ContentParent::RecvTestPermission(const IPC::URI&  aUri,
-                                   const nsCString& aType,
-                                   const PRBool&    aExact,
-                                   PRUint32*        retValue)
-{
-    EnsurePermissionService();
-
-    nsCOMPtr<nsIURI> uri(aUri);
-    if (aExact) {
-        mPermissionService->TestExactPermission(uri, aType.get(), retValue);
-    } else {
-        mPermissionService->TestPermission(uri, aType.get(), retValue);
-    }
-    return true;
-}
-
 void
 ContentParent::EnsurePrefService()
 {
     nsresult rv;
     if (!mPrefService) {
         mPrefService = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
         NS_ASSERTION(NS_SUCCEEDED(rv), 
                      "We lost prefService in the Chrome process !");
     }
 }
 
-void
-ContentParent::EnsurePermissionService()
+bool
+ContentParent::RecvReadPermissions(nsTArray<IPC::Permission>* aPermissions)
 {
-    nsresult rv;
-    if (!mPermissionService) {
-        mPermissionService = do_GetService(
-            NS_PERMISSIONMANAGER_CONTRACTID, &rv);
-        NS_ASSERTION(NS_SUCCEEDED(rv), 
-                     "We lost permissionService in the Chrome process !");
+#ifdef MOZ_PERMISSIONS
+    nsPermissionManager *permissionManager =
+        (nsPermissionManager*)nsPermissionManager::GetSingleton();
+    NS_ABORT_IF_FALSE(permissionManager,
+                 "We have no permissionManager in the Chrome process !");
+
+    nsISimpleEnumerator *enumerator;
+    nsresult rv = permissionManager->GetEnumerator(&enumerator);
+    NS_ABORT_IF_FALSE(NS_SUCCEEDED(rv), "Could not get enumerator!");
+    while(1) {
+        PRBool hasMore;
+        enumerator->HasMoreElements(&hasMore);
+        if (!hasMore)
+            break;
+        nsISupports *supp;
+        enumerator->GetNext((nsISupports**)&supp);
+        nsCOMPtr<nsIPermission> perm = do_QueryInterface(supp);
+
+        nsCString host;
+        perm->GetHost(host);
+        nsCString type;
+        perm->GetType(type);
+        PRUint32 capability;
+        perm->GetCapability(&capability);
+        PRUint32 expireType;
+        perm->GetExpireType(&expireType);
+        PRInt64 expireTime;
+        perm->GetExpireTime(&expireTime);
+
+        aPermissions->AppendElement(IPC::Permission(host, type, capability,
+                                                    expireType, expireTime));
     }
+
+    // Ask for future changes
+    permissionManager->ChildRequestPermissions();
+#endif
+
+    return true;
 }
 
 NS_IMPL_THREADSAFE_ISUPPORTS3(ContentParent,
                               nsIObserver,
                               nsIThreadObserver,
                               nsIDOMGeoPositionCallback)
 
 namespace {
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -122,23 +122,19 @@ private:
             const nsCString& aMimeContentType,
             const nsCString& aContentDisposition,
             const bool& aForceSave,
             const PRInt64& aContentLength);
     virtual bool DeallocPExternalHelperApp(PExternalHelperAppParent* aService);
 
     virtual bool RecvReadPrefs(nsCString* prefs);
 
-    virtual bool RecvTestPermission(const IPC::URI&  aUri,
-                                    const nsCString& aType,
-                                    const PRBool&    aExact,
-                                    PRUint32*        retValue);
+    void EnsurePrefService();
 
-    void EnsurePrefService();
-    void EnsurePermissionService();
+    virtual bool RecvReadPermissions(nsTArray<IPC::Permission>* aPermissions);
 
     virtual bool RecvStartVisitedQuery(const IPC::URI& uri);
 
     virtual bool RecvVisitURI(const IPC::URI& uri,
                               const IPC::URI& referrer,
                               const PRUint32& flags);
 
     virtual bool RecvSetURITitle(const IPC::URI& uri,
@@ -172,15 +168,14 @@ private:
 
     PRInt32 mGeolocationWatchID;
     int mRunToCompletionDepth;
     bool mShouldCallUnblockChild;
     nsCOMPtr<nsIThreadObserver> mOldObserver;
 
     bool mIsAlive;
     nsCOMPtr<nsIPrefServiceInternal> mPrefService; 
-    nsCOMPtr<nsIPermissionManager> mPermissionService; 
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif
--- a/dom/ipc/Makefile.in
+++ b/dom/ipc/Makefile.in
@@ -76,13 +76,18 @@ LOCAL_INCLUDES += \
 		-I$(srcdir)/../../content/base/src \
 		-I$(srcdir)/../../content/events/src \
 		-I$(srcdir)/../../toolkit/components/places/src \
 		-I$(topsrcdir)/chrome/src \
 		-I$(topsrcdir)/uriloader/exthandler \
 		-I$(srcdir)/../../netwerk/base/src \
 		-I$(srcdir)/../src/base \
 		-I$(srcdir)/../../xpcom/base \
+		-I$(topsrcdir)/extensions/cookie \
 		$(NULL)
 
 DEFINES += -DBIN_SUFFIX='"$(BIN_SUFFIX)"'
 
+ifdef MOZ_PERMISSIONS
+DEFINES += -DMOZ_PERMISSIONS
+endif
+
 CXXFLAGS += $(TK_CFLAGS)
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -47,16 +47,17 @@ include "mozilla/net/NeckoMessageUtils.h
 include "nsGeoPositionIPCSerialiser.h";
 
 using GeoPosition;
 
 using ChromePackage;
 using ResourceMapping;
 using OverrideMapping;
 using IPC::URI;
+using IPC::Permission;
 
 namespace mozilla {
 namespace dom {
 
 rpc protocol PContent
 {
     manages PBrowser;
     manages PTestShell;
@@ -76,33 +77,33 @@ child:
     async NotifyVisited(URI uri);
 
     PreferenceUpdate(nsCString pref);
 
     NotifyAlertsObserver(nsCString topic, nsString data);
 
     GeolocationUpdate(GeoPosition somewhere);
 
+    // nsIPermissionManager messages
+    AddPermission(Permission permission);
+
 parent:
     PNecko();
 
     // Services remoting
 
     async StartVisitedQuery(URI uri);
     async VisitURI(URI uri, URI referrer, PRUint32 flags);
     async SetURITitle(URI uri, nsString title);
 
     async LoadURIExternal(URI uri);
 
     // PrefService messages
     sync ReadPrefs() returns (nsCString retValue);
 
-    // PermissionsManager messages
-    sync TestPermission(URI uri, nsCString type, PRBool exact) returns (PRUint32 retValue);
-
     sync SyncMessage(nsString aMessage, nsString aJSON)
       returns (nsString[] retval);
 
     ShowAlertNotification(nsString imageUrl, 
                           nsString title, 
                           nsString text, 
                           PRBool textClickable,
                           nsString cookie,
@@ -115,15 +116,18 @@ parent:
     GeolocationStart();
     GeolocationStop();
 
     ConsoleMessage(nsString message);
     ScriptError(nsString message, nsString sourceName, nsString sourceLine,
                 PRUint32 lineNumber, PRUint32 colNumber, PRUint32 flags,
                 nsCString category); 
 
+    // nsIPermissionManager messages
+    sync ReadPermissions() returns (Permission[] permissions);
+
 both:
      AsyncMessage(nsString aMessage, nsString aJSON);
 
 };
 
 }
 }
--- a/extensions/cookie/nsCookieModule.cpp
+++ b/extensions/cookie/nsCookieModule.cpp
@@ -41,29 +41,30 @@
 #include "nsPermissionManager.h"
 #include "nsPopupWindowManager.h"
 #include "nsICategoryManager.h"
 #include "nsCookiePromptService.h"
 #include "nsCookiePermission.h"
 #include "nsXPIDLString.h"
 
 // Define the constructor function for the objects
-NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPermissionManager, Init)
+NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIPermissionManager,
+  nsPermissionManager::GetXPCOMSingleton)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPopupWindowManager, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsCookiePermission)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsCookiePromptService)
 
 NS_DEFINE_NAMED_CID(NS_PERMISSIONMANAGER_CID);
 NS_DEFINE_NAMED_CID(NS_POPUPWINDOWMANAGER_CID);
 NS_DEFINE_NAMED_CID(NS_COOKIEPROMPTSERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_COOKIEPERMISSION_CID);
 
 
 static const mozilla::Module::CIDEntry kCookieCIDs[] = {
-    { &kNS_PERMISSIONMANAGER_CID, false, NULL, nsPermissionManagerConstructor },
+    { &kNS_PERMISSIONMANAGER_CID, false, NULL, nsIPermissionManagerConstructor },
     { &kNS_POPUPWINDOWMANAGER_CID, false, NULL, nsPopupWindowManagerConstructor },
     { &kNS_COOKIEPROMPTSERVICE_CID, false, NULL, nsCookiePromptServiceConstructor },
     { &kNS_COOKIEPERMISSION_CID, false, NULL, nsCookiePermissionConstructor },
     { NULL }
 };
 
 static const mozilla::Module::ContractIDEntry kCookieContracts[] = {
     { NS_PERMISSIONMANAGER_CONTRACTID, &kNS_PERMISSIONMANAGER_CID },
--- a/extensions/cookie/nsPermissionManager.cpp
+++ b/extensions/cookie/nsPermissionManager.cpp
@@ -33,16 +33,17 @@
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifdef MOZ_IPC
+#include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/ContentChild.h"
 #endif
 #include "nsPermissionManager.h"
 #include "nsPermission.h"
 #include "nsCRT.h"
 #include "nsNetUtil.h"
 #include "nsCOMArray.h"
 #include "nsArrayEnumerator.h"
@@ -54,17 +55,20 @@
 #include "prprf.h"
 #include "mozIStorageService.h"
 #include "mozIStorageStatement.h"
 #include "mozIStorageConnection.h"
 #include "mozStorageHelper.h"
 #include "mozStorageCID.h"
 #include "nsXULAppAPI.h"
 
+static nsPermissionManager *gPermissionManager = nsnull;
+
 #ifdef MOZ_IPC
+using mozilla::dom::ContentParent;
 using mozilla::dom::ContentChild;
 
 static PRBool
 IsChildProcess()
 {
   return XRE_GetProcessType() == GeckoProcessType_Content;
 }
 
@@ -79,26 +83,50 @@ ChildProcess()
     ContentChild* cpc = ContentChild::GetSingleton();
     if (!cpc)
       NS_RUNTIMEABORT("Content Process is NULL!");
     return cpc;
   }
 
   return nsnull;
 }
+
+
+/**
+ * @returns The parent process object, or if we are not in the parent
+ *          process, nsnull.
+ */
+static ContentParent*
+ParentProcess()
+{
+  if (!IsChildProcess()) {
+    ContentParent* cpc = ContentParent::GetSingleton();
+    if (!cpc)
+      NS_RUNTIMEABORT("Content Process is NULL!");
+    return cpc;
+  }
+
+  return nsnull;
+}
 #endif
 
-#define ENSURE_NOT_CHILD_PROCESS \
+#define ENSURE_NOT_CHILD_PROCESS_(onError) \
   PR_BEGIN_MACRO \
   if (IsChildProcess()) { \
-    NS_ERROR("cannot set permission from content process"); \
-    return NS_ERROR_NOT_AVAILABLE; \
+    NS_ERROR("Cannot perform action in content process!"); \
+    onError \
   } \
   PR_END_MACRO
 
+#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"
 
 static PLArenaPool *gHostArena = nsnull;
 
 // making sHostArena 512b for nice allocation
@@ -139,37 +167,82 @@ static const char kPermissionsFileName[]
 static const char kHostpermFileName[] = "hostperm.1";
 
 static const char kPermissionChangeNotification[] = PERM_CHANGE_NOTIFICATION;
 
 NS_IMPL_ISUPPORTS3(nsPermissionManager, nsIPermissionManager, nsIObserver, nsISupportsWeakReference)
 
 nsPermissionManager::nsPermissionManager()
  : mLargestID(0)
+ , mUpdateChildProcess(PR_FALSE)
 {
 }
 
 nsPermissionManager::~nsPermissionManager()
 {
   RemoveAllFromMemory();
 }
 
+// static
+nsIPermissionManager*
+nsPermissionManager::GetXPCOMSingleton()
+{
+  return GetSingleton();
+}
+
+// static
+nsIPermissionManager*
+nsPermissionManager::GetSingleton()
+{
+  if (gPermissionManager) {
+    NS_ADDREF(gPermissionManager);
+    return gPermissionManager;
+  }
+
+  // Create a new singleton nsPermissionManager.
+  // We AddRef only once since XPCOM has rules about the ordering of module
+  // teardowns - by the time our module destructor is called, it's too late to
+  // Release our members, since GC cycles have already been completed and
+  // would result in serious leaks.
+  // See bug 209571.
+  gPermissionManager = new nsPermissionManager();
+  if (gPermissionManager) {
+    NS_ADDREF(gPermissionManager);
+    if (NS_FAILED(gPermissionManager->Init())) {
+      NS_RELEASE(gPermissionManager);
+    }
+  }
+
+  return gPermissionManager;
+}
+
 nsresult
 nsPermissionManager::Init()
 {
   nsresult rv;
 
   if (!mHostTable.Init()) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
 #ifdef MOZ_IPC
-  // Child will route messages to parent, so no need for further initialization
-  if (IsChildProcess())
+  if (IsChildProcess()) {
+    // Get the permissions from the parent process
+    nsTArray<IPC::Permission> perms;
+    ChildProcess()->SendReadPermissions(&perms);
+
+    for (int i = 0; i < perms.Length(); i++) {
+      const IPC::Permission &perm = perms[i];
+      AddInternal(perm.host, 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;
+  }
 #endif
 
   // ignore failure here, since it's non-fatal (we can run fine without
   // persistent storage - e.g. if there's no profile).
   // XXX should we tell the user about this?
   InitDB(PR_FALSE);
 
   mObserverService = do_GetService("@mozilla.org/observer-service;1", &rv);
@@ -223,18 +296,18 @@ nsPermissionManager::InitDB(PRBool aRemo
     mDBConn->GetConnectionReady(&ready);
     if (!ready)
       return NS_ERROR_UNEXPECTED;
   }
 
   PRBool tableExists = PR_FALSE;
   mDBConn->TableExists(NS_LITERAL_CSTRING("moz_hosts"), &tableExists);
   if (!tableExists) {
-      rv = CreateTable();
-      NS_ENSURE_SUCCESS(rv, rv);
+    rv = CreateTable();
+    NS_ENSURE_SUCCESS(rv, rv);
 
   } else {
     // table already exists; check the schema version before reading
     PRInt32 dbSchemaVersion;
     rv = mDBConn->GetSchemaVersion(&dbSchemaVersion);
     NS_ENSURE_SUCCESS(rv, rv);
 
     switch (dbSchemaVersion) {
@@ -394,16 +467,28 @@ nsPermissionManager::AddInternal(const n
                                  const nsAFlatCString &aType,
                                  PRUint32              aPermission,
                                  PRInt64               aID,
                                  PRUint32              aExpireType,
                                  PRInt64               aExpireTime,
                                  NotifyOperationType   aNotifyOperation,
                                  DBOperationType       aDBOperation)
 {
+#ifdef MOZ_IPC
+  if (!IsChildProcess()) {
+    // In the parent, send the update now, if the child is ready
+    if (mUpdateChildProcess) {
+      IPC::Permission permission((aHost),
+                                 (aType),
+                                 aPermission, aExpireType, aExpireTime);
+      ParentProcess()->SendAddPermission(permission);
+    }
+  }
+#endif
+
   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
@@ -588,38 +673,24 @@ nsPermissionManager::RemoveAllInternal()
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsPermissionManager::TestExactPermission(nsIURI     *aURI,
                                          const char *aType,
                                          PRUint32   *aPermission)
 {
-#ifdef MOZ_IPC
-  ContentChild* cpc = ChildProcess();
-  if (cpc) {
-    return cpc->SendTestPermission(aURI, nsDependentCString(aType), PR_TRUE,
-      aPermission) ? NS_OK : NS_ERROR_FAILURE;
-  }
-#endif
   return CommonTestPermission(aURI, aType, aPermission, PR_TRUE);
 }
 
 NS_IMETHODIMP
 nsPermissionManager::TestPermission(nsIURI     *aURI,
                                     const char *aType,
                                     PRUint32   *aPermission)
 {
-#ifdef MOZ_IPC
-  ContentChild* cpc = ChildProcess();
-  if (cpc) {
-    return cpc->SendTestPermission(aURI, nsDependentCString(aType), PR_FALSE,
-      aPermission) ? NS_OK : NS_ERROR_FAILURE;
-  }
-#endif
   return CommonTestPermission(aURI, aType, aPermission, PR_FALSE);
 }
 
 nsresult
 nsPermissionManager::CommonTestPermission(nsIURI     *aURI,
                                           const char *aType,
                                           PRUint32   *aPermission,
                                           PRBool      aExactHostMatch)
@@ -840,16 +911,20 @@ nsPermissionManager::NotifyObservers(nsI
     mObserverService->NotifyObservers(aPermission,
                                       kPermissionChangeNotification,
                                       aData);
 }
 
 nsresult
 nsPermissionManager::Read()
 {
+#ifdef MOZ_IPC
+  ENSURE_NOT_CHILD_PROCESS;
+#endif
+
   nsresult rv;
 
   // delete expired permissions before we read in the db
   {
     // this deletion has its own scope so the write lock is released when done.
     nsCOMPtr<mozIStorageStatement> stmtDeleteExpired;
     rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
           "DELETE FROM moz_hosts WHERE expireType = ?1 AND expireTime <= ?2"),
@@ -906,16 +981,20 @@ nsPermissionManager::Read()
   return NS_OK;
 }
 
 static const char kMatchTypeHost[] = "host";
 
 nsresult
 nsPermissionManager::Import()
 {
+#ifdef MOZ_IPC
+  ENSURE_NOT_CHILD_PROCESS;
+#endif
+
   nsresult rv;
 
   nsCOMPtr<nsIFile> permissionsFile;
   rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(permissionsFile));
   if (NS_FAILED(rv)) return rv;
 
   rv = permissionsFile->AppendNative(NS_LITERAL_CSTRING(kHostpermFileName));
   NS_ENSURE_SUCCESS(rv, rv);
@@ -1010,16 +1089,20 @@ nsPermissionManager::UpdateDB(OperationT
                               mozIStorageStatement* aStmt,
                               PRInt64               aID,
                               const nsACString     &aHost,
                               const nsACString     &aType,
                               PRUint32              aPermission,
                               PRUint32              aExpireType,
                               PRInt64               aExpireTime)
 {
+#ifdef MOZ_IPC
+  ENSURE_NOT_CHILD_PROCESS_NORET;
+#endif
+
   nsresult rv;
 
   // no statement is ok - just means we don't have a profile
   if (!aStmt)
     return;
 
   switch (aOp) {
   case eOperationAdding:
--- a/extensions/cookie/nsPermissionManager.h
+++ b/extensions/cookie/nsPermissionManager.h
@@ -44,16 +44,17 @@
 #include "nsIObserver.h"
 #include "nsIObserverService.h"
 #include "nsWeakReference.h"
 #include "nsCOMPtr.h"
 #include "nsIFile.h"
 #include "nsTHashtable.h"
 #include "nsTArray.h"
 #include "nsString.h"
+#include "nsPermission.h"
 
 class nsIPermission;
 class nsIIDNService;
 class mozIStorageConnection;
 class mozIStorageStatement;
 
 ////////////////////////////////////////////////////////////////////////////////
 
@@ -161,20 +162,20 @@ public:
 
   // nsISupports
   NS_DECL_ISUPPORTS
   NS_DECL_NSIPERMISSIONMANAGER
   NS_DECL_NSIOBSERVER
 
   nsPermissionManager();
   virtual ~nsPermissionManager();
+  static nsIPermissionManager* GetXPCOMSingleton();
+  static nsIPermissionManager* GetSingleton();
   nsresult Init();
 
-private:
-
   // enums for AddInternal()
   enum OperationType {
     eOperationNone,
     eOperationAdding,
     eOperationRemoving,
     eOperationChanging
   };
 
@@ -192,16 +193,18 @@ private:
                        const nsAFlatCString &aType,
                        PRUint32 aPermission,
                        PRInt64 aID,
                        PRUint32 aExpireType,
                        PRInt64  aExpireTime,
                        NotifyOperationType aNotifyOperation,
                        DBOperationType aDBOperation);
 
+private:
+
   PRInt32 GetTypeIndex(const char *aTypeString,
                        PRBool      aAdd);
 
   nsHostEntry *GetHostEntry(const nsAFlatCString &aHost,
                             PRUint32              aType,
                             PRBool                aExactHostMatch);
 
   nsresult CommonTestPermission(nsIURI     *aURI,
@@ -242,15 +245,28 @@ private:
   nsCOMPtr<mozIStorageStatement> mStmtUpdate;
 
   nsTHashtable<nsHostEntry>    mHostTable;
   // a unique, monotonically increasing id used to identify each database entry
   PRInt64                      mLargestID;
 
   // An array to store the strings identifying the different types.
   nsTArray<nsCString>          mTypeArray;
+
+#ifdef MOZ_IPC
+  // Whether we should update the child process with every change to a
+  // permission. This is set to true once the child is ready to receive
+  // such updates.
+  PRBool                       mUpdateChildProcess;
+
+public:
+  void ChildRequestPermissions()
+  {
+    mUpdateChildProcess = PR_TRUE;
+  }
+#endif
 };
 
 // {4F6B5E00-0C36-11d5-A535-0010A401EB10}
 #define NS_PERMISSIONMANAGER_CID \
 { 0x4f6b5e00, 0xc36, 0x11d5, { 0xa5, 0x35, 0x0, 0x10, 0xa4, 0x1, 0xeb, 0x10 } }
 
 #endif /* nsPermissionManager_h__ */
--- a/extensions/cookie/test/Makefile.in
+++ b/extensions/cookie/test/Makefile.in
@@ -93,10 +93,17 @@ libs:: $(_TEST_FILES)
 # see above, only defined for Firefox
 ifdef MOZ_PHOENIX
 libs:: $(_BROWSER_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
 endif
 
 XPCSHELL_TESTS = unit
 
+ifdef MOZ_IPC
+# FIXME/bug 575918: out-of-process xpcshell is broken on OS X
+ifneq ($(OS_ARCH),Darwin)
+XPCSHELL_TESTS += unit_ipc
+endif
+endif
+
 include $(topsrcdir)/config/rules.mk
 
new file mode 100644
--- /dev/null
+++ b/extensions/cookie/test/unit_ipc/test_child.js
@@ -0,0 +1,51 @@
+const Ci = Components.interfaces;
+const Cc = Components.classes;
+const Cr = Components.results;
+
+var gIoService = Components.classes["@mozilla.org/network/io-service;1"]
+                           .getService(Components.interfaces.nsIIOService);
+
+function isParentProcess() {
+    let appInfo = Cc["@mozilla.org/xre/app-info;1"];
+    return (!appInfo || appInfo.getService(Ci.nsIXULRuntime).processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT);
+}
+
+function run_test() {
+  if (!isParentProcess()) {
+    const Ci = Components.interfaces;
+    const Cc = Components.classes;
+
+    var mM = Cc["@mozilla.org/childprocessmessagemanager;1"].
+                         getService(Ci.nsISyncMessageSender);
+
+    var messageListener = {
+      receiveMessage: function(aMessage) {
+        switch(aMessage.name) {
+          case "TESTING:Stage2A":
+            // Permissions created after the child is present
+            do_check_eq(pm.testPermission(gIoService.newURI("http://mozilla.org", null, null), "cookie1"), pm.ALLOW_ACTION);
+            do_check_eq(pm.testPermission(gIoService.newURI("http://mozilla.com", null, null), "cookie2"), pm.DENY_ACTION);
+            do_check_eq(pm.testPermission(gIoService.newURI("http://mozilla.net", null, null), "cookie3"), pm.ALLOW_ACTION);
+            do_check_eq(pm.testPermission(gIoService.newURI("http://firefox.org", null, null), "cookie1"), pm.ALLOW_ACTION);
+            do_check_eq(pm.testPermission(gIoService.newURI("http://firefox.com", null, null), "cookie2"), pm.DENY_ACTION);
+            do_check_eq(pm.testPermission(gIoService.newURI("http://firefox.net", null, null), "cookie3"), pm.ALLOW_ACTION);
+
+            mM.sendAsyncMessage("TESTING:Stage3");
+            break;
+
+        }
+        return true;
+      },
+    };
+
+    mM.addMessageListener("TESTING:Stage2A", messageListener);
+
+    var pm = Cc["@mozilla.org/permissionmanager;1"].getService(Ci.nsIPermissionManager);
+    do_check_eq(pm.testPermission(gIoService.newURI("http://mozilla.org", null, null), "cookie1"), pm.ALLOW_ACTION);
+    do_check_eq(pm.testPermission(gIoService.newURI("http://mozilla.com", null, null), "cookie2"), pm.DENY_ACTION);
+    do_check_eq(pm.testPermission(gIoService.newURI("http://mozilla.net", null, null), "cookie3"), pm.ALLOW_ACTION);
+
+    mM.sendAsyncMessage("TESTING:Stage2");
+  }
+}
+
new file mode 100644
--- /dev/null
+++ b/extensions/cookie/test/unit_ipc/test_parent.js
@@ -0,0 +1,52 @@
+const Ci = Components.interfaces;
+const Cc = Components.classes;
+const Cr = Components.results;
+
+var gIoService = Components.classes["@mozilla.org/network/io-service;1"]
+                           .getService(Components.interfaces.nsIIOService);
+
+function isParentProcess() {
+    let appInfo = Cc["@mozilla.org/xre/app-info;1"];
+    return (!appInfo || appInfo.getService(Ci.nsIXULRuntime).processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT);
+}
+
+function run_test() {
+  if (isParentProcess()) {
+    var pm = Cc["@mozilla.org/permissionmanager;1"].getService(Ci.nsIPermissionManager);
+
+    // Permissions created before the child is present
+    pm.add(gIoService.newURI("http://mozilla.org", null, null), "cookie1", pm.ALLOW_ACTION, pm.EXPIRE_NEVER, 0);
+    pm.add(gIoService.newURI("http://mozilla.com", null, null), "cookie2", pm.DENY_ACTION, pm.EXPIRE_SESSION, 0);
+    pm.add(gIoService.newURI("http://mozilla.net", null, null), "cookie3", pm.ALLOW_ACTION, pm.EXPIRE_TIME, Date.now() + 1000*60*60*24);
+
+    var mM = Cc["@mozilla.org/parentprocessmessagemanager;1"].
+             getService(Ci.nsIFrameMessageManager);
+
+    var messageListener = {
+      receiveMessage: function(aMessage) {
+        switch(aMessage.name) {
+          case "TESTING:Stage2":
+            // Permissions created after the child is present
+            pm.add(gIoService.newURI("http://firefox.org", null, null), "cookie1", pm.ALLOW_ACTION, pm.EXPIRE_NEVER, 0);
+            pm.add(gIoService.newURI("http://firefox.com", null, null), "cookie2", pm.DENY_ACTION, pm.EXPIRE_SESSION, 0);
+            pm.add(gIoService.newURI("http://firefox.net", null, null), "cookie3", pm.ALLOW_ACTION, pm.EXPIRE_TIME, Date.now() + 1000*60*60*24);
+            mM.sendAsyncMessage("TESTING:Stage2A");
+            break;
+
+          case "TESTING:Stage3":
+            do_test_finished();
+            break;
+        }
+        return true;
+      },
+    };
+
+    mM.addMessageListener("TESTING:Stage2", messageListener);
+    mM.addMessageListener("TESTING:Stage3", messageListener);
+
+    do_test_pending();
+    do_load_child_test_harness();
+    run_test_in_child("test_child.js");
+  }
+}
+
--- a/netwerk/ipc/NeckoMessageUtils.h
+++ b/netwerk/ipc/NeckoMessageUtils.h
@@ -169,11 +169,58 @@ struct ParamTraits<URI>
       aLog->append(StringPrintf(L"[%s]", spec.get()));
     }
     else {
       aLog->append(L"[]");
     }
   }
 };
 
+// nsIPermissionManager utilities
+
+struct Permission
+{
+  nsCString host, type;
+  PRUint32 capability, expireType;
+  PRInt64 expireTime;
+
+  Permission() { }
+  Permission(const nsCString& aHost,
+             const nsCString& aType,
+             const PRUint32 aCapability,
+             const PRUint32 aExpireType,
+             const PRInt64 aExpireTime) : host(aHost),
+                                          type(aType),
+                                          capability(aCapability),
+                                          expireType(aExpireType),
+                                          expireTime(aExpireTime) { }
+};
+
+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);
+  }
+
+  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);
+  }
+
+  static void Log(const Permission& aParam, std::wstring* aLog)
+  {
+    aLog->append(StringPrintf(L"[%s]", aParam.host.get()));
+  }
+};
+
 }
 
 #endif // mozilla_net_NeckoMessageUtils_h