Bug 968335 - Implement GetCallerPrincipalOverride. r=bz

This commit is contained in:
Bobby Holley 2014-02-14 16:13:38 -08:00
parent 6c95fae0b3
commit 7149c177ee
5 changed files with 80 additions and 4 deletions

View File

@ -155,6 +155,45 @@ GetIncumbentGlobal()
return ScriptSettingsStack::Ref().IncumbentGlobal();
}
nsIPrincipal*
GetWebIDLCallerPrincipal()
{
MOZ_ASSERT(NS_IsMainThread());
ScriptSettingsStackEntry *entry = ScriptSettingsStack::Ref().EntryPoint();
// If we have an entry point that is not the system singleton, we know it
// must be an AutoEntryScript.
if (!entry || entry->IsSystemSingleton()) {
return nullptr;
}
AutoEntryScript* aes = static_cast<AutoEntryScript*>(entry);
// We can't yet rely on the Script Settings Stack to properly determine the
// entry script, because there are still lots of places in the tree where we
// don't yet use an AutoEntryScript (bug 951991 tracks this work). In the
// mean time though, we can make some observations to hack around the
// problem:
//
// (1) All calls into JS-implemented WebIDL go through CallSetup, which goes
// through AutoEntryScript.
// (2) The top candidate entry point in the Script Settings Stack is the
// entry point if and only if no other JSContexts have been pushed on
// top of the push made by that entry's AutoEntryScript.
//
// Because of (1), all of the cases where we might return a non-null
// WebIDL Caller are guaranteed to have put an entry on the Script Settings
// Stack, so we can restrict our search to that. Moreover, (2) gives us a
// criterion to determine whether an entry in the Script Setting Stack means
// that we should return a non-null WebIDL Caller.
//
// Once we fix bug 951991, this can all be simplified.
if (!aes->mCxPusher.ref().IsStackTop()) {
return nullptr;
}
return aes->mWebIDLCallerPrincipal;
}
AutoEntryScript::AutoEntryScript(nsIGlobalObject* aGlobalObject,
bool aIsMainThread,
JSContext* aCx)

View File

@ -12,6 +12,7 @@
#include "nsCxPusher.h"
#include "MainThreadUtils.h"
#include "nsIGlobalObject.h"
#include "nsIPrincipal.h"
#include "mozilla/Maybe.h"
@ -38,6 +39,24 @@ nsIGlobalObject* BrokenGetEntryGlobal();
// can mostly be inferred from the JS stack.
nsIGlobalObject* GetIncumbentGlobal();
// JS-implemented WebIDL presents an interesting situation with respect to the
// subject principal. A regular C++-implemented API can simply examine the
// compartment of the most-recently-executed script, and use that to infer the
// responsible party. However, JS-implemented APIs are run with system
// principal, and thus clobber the subject principal of the script that
// invoked the API. So we have to do some extra work to keep track of this
// information.
//
// We therefore implement the following behavior:
// * Each Script Settings Object has an optional WebIDL Caller Principal field.
// This defaults to null.
// * When we push an Entry Point in preparation to run a JS-implemented WebIDL
// callback, we grab the subject principal at the time of invocation, and
// store that as the WebIDL Caller Principal.
// * When non-null, callers can query this principal from script via an API on
// Components.utils.
nsIPrincipal* GetWebIDLCallerPrincipal();
class ScriptSettingsStack;
struct ScriptSettingsStackEntry {
nsCOMPtr<nsIGlobalObject> mGlobalObject;
@ -79,11 +98,17 @@ public:
JSContext* aCx = nullptr);
~AutoEntryScript();
void SetWebIDLCallerPrincipal(nsIPrincipal *aPrincipal) {
mWebIDLCallerPrincipal = aPrincipal;
}
private:
dom::ScriptSettingsStack& mStack;
nsCOMPtr<nsIPrincipal> mWebIDLCallerPrincipal;
mozilla::Maybe<AutoCxPusher> mCxPusher;
mozilla::Maybe<JSAutoCompartment> mAc; // This can de-Maybe-fy when mCxPusher
// goes away.
friend nsIPrincipal* GetWebIDLCallerPrincipal();
};
/*

View File

@ -49,7 +49,8 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_END
CallbackObject::CallSetup::CallSetup(CallbackObject* aCallback,
ErrorResult& aRv,
ExceptionHandling aExceptionHandling,
JSCompartment* aCompartment)
JSCompartment* aCompartment,
bool aIsJSImplementedWebIDL)
: mCx(nullptr)
, mCompartment(aCompartment)
, mErrorResult(aRv)
@ -59,6 +60,14 @@ CallbackObject::CallSetup::CallSetup(CallbackObject* aCallback,
if (mIsMainThread) {
nsContentUtils::EnterMicroTask();
}
// Compute the caller's subject principal (if necessary) early, before we
// do anything that might perturb the relevant state.
nsIPrincipal* webIDLCallerPrincipal = nullptr;
if (aIsJSImplementedWebIDL) {
webIDLCallerPrincipal = nsContentUtils::GetSubjectPrincipal();
}
// We need to produce a useful JSContext here. Ideally one that the callback
// is in some sense associated with, so that we can sort of treat it as a
// "script entry point". Though once we actually have script entry points,
@ -112,6 +121,7 @@ CallbackObject::CallSetup::CallSetup(CallbackObject* aCallback,
}
mAutoEntryScript.construct(globalObject, mIsMainThread, cx);
mAutoEntryScript.ref().SetWebIDLCallerPrincipal(webIDLCallerPrincipal);
if (aCallback->IncumbentGlobalOrNull()) {
mAutoIncumbentScript.construct(aCallback->IncumbentGlobalOrNull());
}

View File

@ -154,10 +154,11 @@ protected:
*/
public:
// If aExceptionHandling == eRethrowContentExceptions then aCompartment
// needs to be set to the caller's compartment.
// needs to be set to the compartment in which exceptions will be rethrown.
CallSetup(CallbackObject* aCallback, ErrorResult& aRv,
ExceptionHandling aExceptionHandling,
JSCompartment* aCompartment = nullptr);
JSCompartment* aCompartment = nullptr,
bool aIsJSImplementedWebIDL = false);
~CallSetup();
JSContext* GetContext() const

View File

@ -11289,7 +11289,8 @@ class CallbackMember(CGNativeMember):
if self.rethrowContentException:
# getArgs doesn't add the aExceptionHandling argument but does add
# aCompartment for us.
callSetup += ", eRethrowContentExceptions, aCompartment"
callSetup += ", eRethrowContentExceptions, aCompartment, /* aIsJSImplementedWebIDL = */ "
callSetup += toStringBool(isJSImplementedDescriptor(self.descriptorProvider))
else:
callSetup += ", aExceptionHandling"
callSetup += ");"