Bug 896890 - TabContent::HasOwnApp() should be true iff TabContext::GetOwnApp() is non-null. r=khuey

This change reworks how TabContext stores its data.  Before, it stored
an app-id and translated that into an app; now we do the reverse.  This
lets us guarantee that HasOwnApp() is true iff GetOwnApp() is non-null.

We added a new class, MaybeInvalidTabContext to assist with converting
from an IPCTabContext to a TabContext.  This allows us to ensure that a
TabContext object is never invalid.

--HG--
extra : rebase_source : d43e858739a342ab420122a90336bfd585be268a
This commit is contained in:
Justin Lebar 2013-07-30 11:51:44 -07:00
parent 6c2e8f4922
commit 7aa4dc5297
5 changed files with 287 additions and 203 deletions

View File

@ -2056,12 +2056,15 @@ nsFrameLoader::TryRemoteBrowser()
eCaseMatters)) {
scrollingBehavior = ASYNC_PAN_ZOOM;
}
bool rv = true;
if (ownApp) {
context.SetTabContextForAppFrame(ownApp, containingApp, scrollingBehavior);
rv = context.SetTabContextForAppFrame(ownApp, containingApp, scrollingBehavior);
} else if (OwnerIsBrowserFrame()) {
// The |else| above is unnecessary; OwnerIsBrowserFrame() implies !ownApp.
context.SetTabContextForBrowserFrame(containingApp, scrollingBehavior);
rv = context.SetTabContextForBrowserFrame(containingApp, scrollingBehavior);
}
NS_ENSURE_TRUE(rv, false);
nsCOMPtr<Element> ownerElement = mOwnerContent;
mRemoteBrowser = ContentParent::CreateBrowserOrApp(context, ownerElement);

View File

@ -591,7 +591,15 @@ ContentChild::AllocPBrowserChild(const IPCTabContext& aContext,
// check that it's of a certain type for security purposes, because we
// believe whatever the parent process tells us.
nsRefPtr<TabChild> child = TabChild::Create(this, TabContext(aContext), aChromeFlags);
MaybeInvalidTabContext tc(aContext);
if (!tc.IsValid()) {
NS_ERROR(nsPrintfCString("Received an invalid TabContext from "
"the parent process. (%s) Crashing...",
tc.InvalidReason()));
MOZ_CRASH("Invalid TabContext received from the parent process.");
}
nsRefPtr<TabChild> child = TabChild::Create(this, tc.GetTabContext(), aChromeFlags);
// The ref here is released in DeallocPBrowserChild.
return child.forget().get();

View File

@ -1757,7 +1757,15 @@ ContentParent::AllocPBrowserParent(const IPCTabContext& aContext,
return nullptr;
}
TabParent* parent = new TabParent(this, TabContext(aContext));
MaybeInvalidTabContext tc(aContext);
if (!tc.IsValid()) {
NS_ERROR(nsPrintfCString("Child passed us an invalid TabContext. (%s) "
"Aborting AllocPBrowserParent.",
tc.InvalidReason()));
return nullptr;
}
TabParent* parent = new TabParent(this, tc.GetTabContext());
// We release this ref in DeallocPBrowserParent()
NS_ADDREF(parent);

View File

@ -9,6 +9,8 @@
#include "mozilla/dom/TabChild.h"
#include "nsIAppsService.h"
#define NO_APP_ID (nsIScriptSecurityManager::NO_APP_ID)
using namespace mozilla::dom::ipc;
using namespace mozilla::layout;
@ -17,92 +19,13 @@ namespace dom {
TabContext::TabContext()
: mInitialized(false)
, mOwnAppId(nsIScriptSecurityManager::NO_APP_ID)
, mContainingAppId(nsIScriptSecurityManager::NO_APP_ID)
, mOwnAppId(NO_APP_ID)
, mContainingAppId(NO_APP_ID)
, mScrollingBehavior(DEFAULT_SCROLLING)
, mIsBrowser(false)
{
}
TabContext::TabContext(const IPCTabContext& aParams)
: mInitialized(true)
{
const IPCTabAppBrowserContext& appBrowser = aParams.appBrowserContext();
switch(appBrowser.type()) {
case IPCTabAppBrowserContext::TPopupIPCTabContext: {
const PopupIPCTabContext &ipcContext = appBrowser.get_PopupIPCTabContext();
TabContext *context;
if (ipcContext.openerParent()) {
context = static_cast<TabParent*>(ipcContext.openerParent());
if (context->IsBrowserElement() && !ipcContext.isBrowserElement()) {
// If the TabParent corresponds to a browser element, then it can only
// open other browser elements, for security reasons. We should have
// checked this before calling the TabContext constructor, so this is
// a fatal error.
MOZ_CRASH();
}
}
else if (ipcContext.openerChild()) {
context = static_cast<TabChild*>(ipcContext.openerChild());
}
else {
// This should be unreachable because PopupIPCTabContext::opener is not a
// nullable field.
MOZ_CRASH();
}
// Browser elements can't nest other browser elements. So if
// our opener is browser element, we must be a new DOM window
// opened by it. In that case we inherit our containing app ID
// (if any).
//
// Otherwise, we're a new app window and we inherit from our
// opener app.
if (ipcContext.isBrowserElement()) {
mIsBrowser = true;
mOwnAppId = nsIScriptSecurityManager::NO_APP_ID;
mContainingAppId = context->OwnOrContainingAppId();
}
else {
mIsBrowser = false;
mOwnAppId = context->mOwnAppId;
mContainingAppId = context->mContainingAppId;
}
break;
}
case IPCTabAppBrowserContext::TAppFrameIPCTabContext: {
const AppFrameIPCTabContext &ipcContext =
appBrowser.get_AppFrameIPCTabContext();
mIsBrowser = false;
mOwnAppId = ipcContext.ownAppId();
mContainingAppId = ipcContext.appFrameOwnerAppId();
break;
}
case IPCTabAppBrowserContext::TBrowserFrameIPCTabContext: {
const BrowserFrameIPCTabContext &ipcContext =
appBrowser.get_BrowserFrameIPCTabContext();
mIsBrowser = true;
mOwnAppId = nsIScriptSecurityManager::NO_APP_ID;
mContainingAppId = ipcContext.browserFrameOwnerAppId();
break;
}
case IPCTabAppBrowserContext::TVanillaFrameIPCTabContext: {
mIsBrowser = false;
mOwnAppId = nsIScriptSecurityManager::NO_APP_ID;
mContainingAppId = nsIScriptSecurityManager::NO_APP_ID;
break;
}
default: {
MOZ_CRASH();
}
}
mScrollingBehavior = aParams.scrollingBehavior();
}
bool
TabContext::IsBrowserElement() const
{
@ -124,66 +47,73 @@ TabContext::OwnAppId() const
already_AddRefed<mozIApplication>
TabContext::GetOwnApp() const
{
return GetAppForId(OwnAppId());
nsCOMPtr<mozIApplication> ownApp = mOwnApp;
return ownApp.forget();
}
bool
TabContext::HasOwnApp() const
{
return mOwnAppId != nsIScriptSecurityManager::NO_APP_ID;
nsCOMPtr<mozIApplication> ownApp = GetOwnApp();
return !!ownApp;
}
uint32_t
TabContext::BrowserOwnerAppId() const
{
if (mIsBrowser) {
if (IsBrowserElement()) {
return mContainingAppId;
}
return nsIScriptSecurityManager::NO_APP_ID;
return NO_APP_ID;
}
already_AddRefed<mozIApplication>
TabContext::GetBrowserOwnerApp() const
{
return GetAppForId(BrowserOwnerAppId());
nsCOMPtr<mozIApplication> ownerApp;
if (IsBrowserElement()) {
ownerApp = mContainingApp;
}
return ownerApp.forget();
}
bool
TabContext::HasBrowserOwnerApp() const
{
return BrowserOwnerAppId() != nsIScriptSecurityManager::NO_APP_ID;
nsCOMPtr<mozIApplication> ownerApp = GetBrowserOwnerApp();
return !!ownerApp;
}
uint32_t
TabContext::AppOwnerAppId() const
{
if (mOwnAppId != nsIScriptSecurityManager::NO_APP_ID) {
if (HasOwnApp()) {
return mContainingAppId;
}
return nsIScriptSecurityManager::NO_APP_ID;
return NO_APP_ID;
}
already_AddRefed<mozIApplication>
TabContext::GetAppOwnerApp() const
{
return GetAppForId(AppOwnerAppId());
nsCOMPtr<mozIApplication> ownerApp;
if (HasOwnApp()) {
ownerApp = mContainingApp;
}
return ownerApp.forget();
}
bool
TabContext::HasAppOwnerApp() const
{
return AppOwnerAppId() != nsIScriptSecurityManager::NO_APP_ID;
nsCOMPtr<mozIApplication> ownerApp = GetAppOwnerApp();
return !!ownerApp;
}
uint32_t
TabContext::OwnOrContainingAppId() const
{
if (mIsBrowser) {
MOZ_ASSERT(mOwnAppId == nsIScriptSecurityManager::NO_APP_ID);
return mContainingAppId;
}
if (mOwnAppId) {
if (HasOwnApp()) {
return mOwnAppId;
}
@ -193,13 +123,21 @@ TabContext::OwnOrContainingAppId() const
already_AddRefed<mozIApplication>
TabContext::GetOwnOrContainingApp() const
{
return GetAppForId(OwnOrContainingAppId());
nsCOMPtr<mozIApplication> ownOrContainingApp;
if (HasOwnApp()) {
ownOrContainingApp = mOwnApp;
} else {
ownOrContainingApp = mContainingApp;
}
return ownOrContainingApp.forget();
}
bool
TabContext::HasOwnOrContainingApp() const
{
return OwnOrContainingAppId() != nsIScriptSecurityManager::NO_APP_ID;
nsCOMPtr<mozIApplication> ownOrContainingApp = GetOwnOrContainingApp();
return !!ownOrContainingApp;
}
bool
@ -207,44 +145,33 @@ TabContext::SetTabContext(const TabContext& aContext)
{
NS_ENSURE_FALSE(mInitialized, false);
// Verify that we can actually get apps for the given ids. This step gives us
// confidence that HasX() returns true iff GetX() returns true.
if (aContext.mOwnAppId != nsIScriptSecurityManager::NO_APP_ID) {
nsCOMPtr<mozIApplication> app = GetAppForId(aContext.mOwnAppId);
NS_ENSURE_TRUE(app, false);
}
if (aContext.mContainingAppId != nsIScriptSecurityManager::NO_APP_ID) {
nsCOMPtr<mozIApplication> app = GetAppForId(aContext.mContainingAppId);
NS_ENSURE_TRUE(app, false);
}
*this = aContext;
mInitialized = true;
mIsBrowser = aContext.mIsBrowser;
mOwnAppId = aContext.mOwnAppId;
mContainingAppId = aContext.mContainingAppId;
mScrollingBehavior = aContext.mScrollingBehavior;
return true;
}
bool
TabContext::SetTabContextForAppFrame(mozIApplication* aOwnApp, mozIApplication* aAppFrameOwnerApp,
TabContext::SetTabContextForAppFrame(mozIApplication* aOwnApp,
mozIApplication* aAppFrameOwnerApp,
ScrollingBehavior aRequestedBehavior)
{
NS_ENSURE_FALSE(mInitialized, false);
// Get ids for both apps and only write to our member variables after we've
// verified that this worked.
uint32_t ownAppId = nsIScriptSecurityManager::NO_APP_ID;
uint32_t ownAppId = NO_APP_ID;
if (aOwnApp) {
nsresult rv = aOwnApp->GetLocalId(&ownAppId);
NS_ENSURE_SUCCESS(rv, false);
NS_ENSURE_TRUE(ownAppId != NO_APP_ID, false);
}
uint32_t containingAppId = nsIScriptSecurityManager::NO_APP_ID;
uint32_t containingAppId = NO_APP_ID;
if (aAppFrameOwnerApp) {
nsresult rv = aOwnApp->GetLocalId(&containingAppId);
NS_ENSURE_SUCCESS(rv, false);
NS_ENSURE_TRUE(containingAppId != NO_APP_ID, false);
}
mInitialized = true;
@ -252,6 +179,8 @@ TabContext::SetTabContextForAppFrame(mozIApplication* aOwnApp, mozIApplication*
mOwnAppId = ownAppId;
mContainingAppId = containingAppId;
mScrollingBehavior = aRequestedBehavior;
mOwnApp = aOwnApp;
mContainingApp = aAppFrameOwnerApp;
return true;
}
@ -261,17 +190,19 @@ TabContext::SetTabContextForBrowserFrame(mozIApplication* aBrowserFrameOwnerApp,
{
NS_ENSURE_FALSE(mInitialized, false);
uint32_t containingAppId = nsIScriptSecurityManager::NO_APP_ID;
uint32_t containingAppId = NO_APP_ID;
if (aBrowserFrameOwnerApp) {
nsresult rv = aBrowserFrameOwnerApp->GetLocalId(&containingAppId);
NS_ENSURE_SUCCESS(rv, false);
NS_ENSURE_TRUE(containingAppId != NO_APP_ID, false);
}
mInitialized = true;
mIsBrowser = true;
mOwnAppId = nsIScriptSecurityManager::NO_APP_ID;
mOwnAppId = NO_APP_ID;
mContainingAppId = containingAppId;
mScrollingBehavior = aRequestedBehavior;
mContainingApp = aBrowserFrameOwnerApp;
return true;
}
@ -287,38 +218,8 @@ TabContext::AsIPCTabContext() const
mScrollingBehavior);
}
already_AddRefed<mozIApplication>
TabContext::GetAppForId(uint32_t aAppId) const
{
if (aAppId == nsIScriptSecurityManager::NO_APP_ID) {
return nullptr;
}
// This application caching is needed to avoid numerous unecessary application clones.
// See Bug 853632 for details.
if (aAppId == mOwnAppId) {
if (!mOwnApp) {
mOwnApp = GetAppForIdNoCache(aAppId);
}
nsCOMPtr<mozIApplication> ownApp = mOwnApp;
return ownApp.forget();
}
if (aAppId == mContainingAppId) {
if (!mContainingApp) {
mContainingApp = GetAppForIdNoCache(mContainingAppId);
}
nsCOMPtr<mozIApplication> containingApp = mContainingApp;
return containingApp.forget();
}
// We need the fallthrough here because mOwnAppId/mContainingAppId aren't always
// set before calling GetAppForId().
return GetAppForIdNoCache(aAppId);
}
already_AddRefed<mozIApplication>
TabContext::GetAppForIdNoCache(uint32_t aAppId) const
static already_AddRefed<mozIApplication>
GetAppForId(uint32_t aAppId)
{
nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID);
NS_ENSURE_TRUE(appsService, nullptr);
@ -330,5 +231,136 @@ TabContext::GetAppForIdNoCache(uint32_t aAppId) const
return app.forget();
}
MaybeInvalidTabContext::MaybeInvalidTabContext(const IPCTabContext& aParams)
: mInvalidReason(nullptr)
{
bool isBrowser = false;
uint32_t ownAppId = NO_APP_ID;
uint32_t containingAppId = NO_APP_ID;
const IPCTabAppBrowserContext& appBrowser = aParams.appBrowserContext();
switch(appBrowser.type()) {
case IPCTabAppBrowserContext::TPopupIPCTabContext: {
const PopupIPCTabContext &ipcContext = appBrowser.get_PopupIPCTabContext();
TabContext *context;
if (ipcContext.openerParent()) {
context = static_cast<TabParent*>(ipcContext.openerParent());
if (context->IsBrowserElement() && !ipcContext.isBrowserElement()) {
// If the TabParent corresponds to a browser element, then it can only
// open other browser elements, for security reasons. We should have
// checked this before calling the TabContext constructor, so this is
// a fatal error.
mInvalidReason = "Child is-browser process tried to "
"open a non-browser tab.";
return;
}
} else if (ipcContext.openerChild()) {
context = static_cast<TabChild*>(ipcContext.openerChild());
} else {
// This should be unreachable because PopupIPCTabContext::opener is not a
// nullable field.
mInvalidReason = "PopupIPCTabContext::opener was null (?!).";
return;
}
// Browser elements can't nest other browser elements. So if
// our opener is browser element, we must be a new DOM window
// opened by it. In that case we inherit our containing app ID
// (if any).
//
// Otherwise, we're a new app window and we inherit from our
// opener app.
if (ipcContext.isBrowserElement()) {
isBrowser = true;
ownAppId = NO_APP_ID;
containingAppId = context->OwnOrContainingAppId();
} else {
isBrowser = false;
ownAppId = context->mOwnAppId;
containingAppId = context->mContainingAppId;
}
break;
}
case IPCTabAppBrowserContext::TAppFrameIPCTabContext: {
const AppFrameIPCTabContext &ipcContext =
appBrowser.get_AppFrameIPCTabContext();
isBrowser = false;
ownAppId = ipcContext.ownAppId();
containingAppId = ipcContext.appFrameOwnerAppId();
break;
}
case IPCTabAppBrowserContext::TBrowserFrameIPCTabContext: {
const BrowserFrameIPCTabContext &ipcContext =
appBrowser.get_BrowserFrameIPCTabContext();
isBrowser = true;
ownAppId = NO_APP_ID;
containingAppId = ipcContext.browserFrameOwnerAppId();
break;
}
case IPCTabAppBrowserContext::TVanillaFrameIPCTabContext: {
isBrowser = false;
ownAppId = NO_APP_ID;
containingAppId = NO_APP_ID;
break;
}
default: {
MOZ_CRASH();
}
}
nsCOMPtr<mozIApplication> ownApp = GetAppForId(ownAppId);
if (ownApp == nullptr !=
ownAppId == NO_APP_ID) {
mInvalidReason = "Got an ownAppId that didn't correspond to an app.";
return;
}
nsCOMPtr<mozIApplication> containingApp = GetAppForId(containingAppId);
if (containingApp == nullptr !=
containingAppId == NO_APP_ID) {
mInvalidReason = "Got a containingAppId that didn't correspond to an app.";
return;
}
bool rv;
if (isBrowser) {
rv = mTabContext.SetTabContextForBrowserFrame(containingApp,
aParams.scrollingBehavior());
} else {
rv = mTabContext.SetTabContextForAppFrame(ownApp,
containingApp,
aParams.scrollingBehavior());
}
if (!rv) {
mInvalidReason = "Couldn't initialize TabContext.";
}
}
bool
MaybeInvalidTabContext::IsValid()
{
return mInvalidReason == nullptr;
}
const char*
MaybeInvalidTabContext::GetInvalidReason()
{
return mInvalidReason;
}
const TabContext&
MaybeInvalidTabContext::GetTabContext()
{
if (!IsValid()) {
MOZ_CRASH("Can't GetTabContext() if !IsValid().");
}
return mTabContext;
}
} // namespace dom
} // namespace mozilla

View File

@ -35,24 +35,9 @@ protected:
typedef mozilla::layout::ScrollingBehavior ScrollingBehavior;
public:
/**
* This constructor sets is-browser to false, and sets all relevant apps to
* NO_APP_ID. If you inherit from TabContext, you can mutate this object
* exactly once by calling one of the protected SetTabContext*() methods.
*/
TabContext();
/**
* This constructor copies the information in aContext. The TabContext is
* immutable after calling this method; you won't be able call any of the
* protected SetTabContext*() methods on an object constructed using this
* constructor.
*
* If aContext is a PopupIPCTabContext with isBrowserElement false and whose
* openerParent is a browser element, this constructor will crash (even in
* release builds). So please check that case before calling this method.
*/
TabContext(const IPCTabContext& aContext);
/* (The implicit copy-constructor and operator= are fine.) */
/**
* Generates IPCTabContext of type BrowserFrameIPCTabContext or
@ -128,6 +113,8 @@ public:
ScrollingBehavior GetScrollingBehavior() const { return mScrollingBehavior; }
protected:
friend class MaybeInvalidTabContext;
/**
* These protected mutator methods let you modify a TabContext once. Further
* attempts to modify a given TabContext will fail (the method will return
@ -158,48 +145,35 @@ protected:
ScrollingBehavior aRequestedBehavior);
private:
/**
* Translate an appId into a mozIApplication, using lazy caching.
*/
already_AddRefed<mozIApplication> GetAppForId(uint32_t aAppId) const;
/**
* Translate an appId into a mozIApplication.
*/
already_AddRefed<mozIApplication> GetAppForIdNoCache(uint32_t aAppId) const;
/**
* Has this TabContext been initialized? If so, mutator methods will fail.
*/
bool mInitialized;
/**
* This TabContext's own app id. If this is something other than NO_APP_ID,
* then this TabContext corresponds to an app, and mIsBrowser must be false.
* This TabContext's own app. If this is non-null, then this
* TabContext corresponds to an app, and mIsBrowser must be false.
*/
nsCOMPtr<mozIApplication> mOwnApp;
/**
* A cache of mOwnApp->GetLocalId(). Speed really does matter here, since we
* read this ID often during process startup.
*/
uint32_t mOwnAppId;
/**
* Cache of this TabContext's own app. If mOwnAppId is NO_APP_ID, this is
* guaranteed to be nullptr. Otherwise, it may or may not be null.
* This TabContext's containing app. If mIsBrowser, this corresponds to the
* app which contains the browser frame; otherwise, this corresponds to the
* app which contains the app frame.
*/
mutable nsCOMPtr<mozIApplication> mOwnApp;
nsCOMPtr<mozIApplication> mContainingApp;
/**
* The id of the app which contains this TabContext's frame. If mIsBrowser,
* this corresponds to the ID of the app which contains the browser frame;
* otherwise, this correspodns to the ID of the app which contains the app
* frame.
/*
* Cache of mContainingApp->GetLocalId().
*/
uint32_t mContainingAppId;
/**
* Cache of the app that contains this TabContext's frame. If mContainingAppId
* is NO_APP_ID, this is guaranteed to be nullptr. Otherwise, it may or may not
* be null.
*/
mutable nsCOMPtr<mozIApplication> mContainingApp;
/**
* The requested scrolling behavior for this frame.
*/
@ -208,15 +182,15 @@ private:
/**
* Does this TabContext correspond to a browser element?
*
* If this is true, mOwnAppId must be NO_APP_ID.
* If this is true, mOwnApp must be null.
*/
bool mIsBrowser;
};
/**
* MutableTabContext is the same as TabContext, except the mutation methods are
* public instead of protected. You can still only call these mutation methods
* once on a given object.
* MutableTabContext is the same as MaybeInvalidTabContext, except the mutation
* methods are public instead of protected. You can still only call these
* mutation methods once on a given object.
*/
class MutableTabContext : public TabContext
{
@ -241,6 +215,65 @@ public:
}
};
/**
* MaybeInvalidTabContext is a simple class that lets you transform an
* IPCTabContext into a TabContext.
*
* The issue is that an IPCTabContext is not necessarily valid; for example, it
* might specify an app-id which doesn't exist. So to convert an IPCTabContext
* into a TabContext, you construct a MaybeInvalidTabContext, check whether it's
* valid, and, if so, read out your TabContext.
*
* Example usage:
*
* void UseTabContext(const TabContext& aTabContext);
*
* void CreateTab(const IPCTabContext& aContext) {
* MaybeInvalidTabContext tc(aContext);
* if (!tc.IsValid()) {
* NS_ERROR(nsPrintfCString("Got an invalid IPCTabContext: %s",
* tc.GetInvalidReason()));
* return;
* }
* UseTabContext(tc.GetTabContext());
* }
*/
class MaybeInvalidTabContext
{
public:
/**
* This constructor copies the information in aContext and sets IsValid() as
* appropriate.
*/
MaybeInvalidTabContext(const IPCTabContext& aContext);
/**
* Was the IPCTabContext we received in our constructor valid?
*/
bool IsValid();
/**
* If IsValid(), this function returns null. Otherwise, it returns a
* human-readable string indicating why the IPCTabContext passed to our
* constructor was not valid.
*/
const char* GetInvalidReason();
/**
* If IsValid(), this function returns a reference to a TabContext
* corresponding to the IPCTabContext passed to our constructor. If
* !IsValid(), this function crashes.
*/
const TabContext& GetTabContext();
private:
MaybeInvalidTabContext(const MaybeInvalidTabContext&) MOZ_DELETE;
MaybeInvalidTabContext& operator=(const MaybeInvalidTabContext&) MOZ_DELETE;
const char* mInvalidReason;
MutableTabContext mTabContext;
};
} // namespace dom
} // namespace mozilla