Bug 599428 - Optimize permissions IPC. r=dwitte a=blocking-fennec

This commit is contained in:
Alon Zakai 2010-10-09 11:07:38 -07:00
parent 8b82b6bc70
commit 3fad238bbf
13 changed files with 369 additions and 60 deletions

View File

@ -77,6 +77,11 @@
#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;
@ -436,5 +441,27 @@ ContentChild::RecvGeolocationUpdate(const GeoPosition& somewhere)
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

View File

@ -112,6 +112,8 @@ public:
virtual bool RecvGeolocationUpdate(const GeoPosition& somewhere);
virtual bool RecvAddPermission(const IPC::Permission& permission);
private:
NS_OVERRIDE
virtual void ActorDestroy(ActorDestroyReason why);

View File

@ -64,6 +64,11 @@
#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;
@ -198,23 +203,6 @@ ContentParent::RecvReadPrefs(nsCString* 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()
{
@ -226,16 +214,47 @@ ContentParent::EnsurePrefService()
}
}
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,

View File

@ -127,13 +127,9 @@ private:
virtual bool RecvReadPrefs(nsCString* prefs);
virtual bool RecvTestPermission(const IPC::URI& aUri,
const nsCString& aType,
const PRBool& aExact,
PRUint32* retValue);
void EnsurePrefService();
void EnsurePermissionService();
virtual bool RecvReadPermissions(nsTArray<IPC::Permission>* aPermissions);
virtual bool RecvStartVisitedQuery(const IPC::URI& uri);
@ -177,7 +173,6 @@ private:
bool mIsAlive;
nsCOMPtr<nsIPrefServiceInternal> mPrefService;
nsCOMPtr<nsIPermissionManager> mPermissionService;
};
} // namespace dom

View File

@ -81,8 +81,13 @@ LOCAL_INCLUDES += \
-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)

View File

@ -52,6 +52,7 @@ using ChromePackage;
using ResourceMapping;
using OverrideMapping;
using IPC::URI;
using IPC::Permission;
namespace mozilla {
namespace dom {
@ -81,6 +82,9 @@ child:
GeolocationUpdate(GeoPosition somewhere);
// nsIPermissionManager messages
AddPermission(Permission permission);
parent:
PNecko();
@ -95,9 +99,6 @@ parent:
// 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);
@ -120,6 +121,9 @@ parent:
PRUint32 lineNumber, PRUint32 colNumber, PRUint32 flags,
nsCString category);
// nsIPermissionManager messages
sync ReadPermissions() returns (Permission[] permissions);
both:
AsyncMessage(nsString aMessage, nsString aJSON);

View File

@ -46,7 +46,8 @@
#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)
@ -58,7 +59,7 @@ 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 },

View File

@ -38,6 +38,7 @@
* ***** END LICENSE BLOCK ***** */
#ifdef MOZ_IPC
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/ContentChild.h"
#endif
#include "nsPermissionManager.h"
@ -59,7 +60,10 @@
#include "mozStorageCID.h"
#include "nsXULAppAPI.h"
static nsPermissionManager *gPermissionManager = nsnull;
#ifdef MOZ_IPC
using mozilla::dom::ContentParent;
using mozilla::dom::ContentChild;
static PRBool
@ -84,16 +88,40 @@ ChildProcess()
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
@ -144,6 +172,7 @@ NS_IMPL_ISUPPORTS3(nsPermissionManager, nsIPermissionManager, nsIObserver, nsISu
nsPermissionManager::nsPermissionManager()
: mLargestID(0)
, mUpdateChildProcess(PR_FALSE)
{
}
@ -152,6 +181,39 @@ 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()
{
@ -162,9 +224,20 @@ nsPermissionManager::Init()
}
#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
@ -228,8 +301,8 @@ nsPermissionManager::InitDB(PRBool aRemoveFile)
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
@ -399,6 +472,18 @@ nsPermissionManager::AddInternal(const nsAFlatCString &aHost,
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)
@ -593,13 +678,6 @@ 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);
}
@ -608,13 +686,6 @@ 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);
}
@ -845,6 +916,10 @@ nsPermissionManager::NotifyObservers(nsIPermission *aPermission,
nsresult
nsPermissionManager::Read()
{
#ifdef MOZ_IPC
ENSURE_NOT_CHILD_PROCESS;
#endif
nsresult rv;
// delete expired permissions before we read in the db
@ -911,6 +986,10 @@ static const char kMatchTypeHost[] = "host";
nsresult
nsPermissionManager::Import()
{
#ifdef MOZ_IPC
ENSURE_NOT_CHILD_PROCESS;
#endif
nsresult rv;
nsCOMPtr<nsIFile> permissionsFile;
@ -1015,6 +1094,10 @@ nsPermissionManager::UpdateDB(OperationType aOp,
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

View File

@ -49,6 +49,7 @@
#include "nsTHashtable.h"
#include "nsTArray.h"
#include "nsString.h"
#include "nsPermission.h"
class nsIPermission;
class nsIIDNService;
@ -166,10 +167,10 @@ public:
nsPermissionManager();
virtual ~nsPermissionManager();
static nsIPermissionManager* GetXPCOMSingleton();
static nsIPermissionManager* GetSingleton();
nsresult Init();
private:
// enums for AddInternal()
enum OperationType {
eOperationNone,
@ -197,6 +198,8 @@ private:
NotifyOperationType aNotifyOperation,
DBOperationType aDBOperation);
private:
PRInt32 GetTypeIndex(const char *aTypeString,
PRBool aAdd);
@ -247,6 +250,19 @@ private:
// 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}

View File

@ -98,5 +98,12 @@ 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

View File

@ -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");
}
}

View File

@ -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");
}
}

View File

@ -174,6 +174,53 @@ struct ParamTraits<URI>
}
};
// 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