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
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