Bug 989528 - Implement AutoJSAPI. r=bz

This commit is contained in:
Bobby Holley 2014-04-14 20:26:59 -07:00
parent 95b4d38749
commit 6ecb59c370
2 changed files with 89 additions and 25 deletions

View File

@ -184,7 +184,7 @@ GetWebIDLCallerPrincipal()
// that we should return a non-null WebIDL Caller. // that we should return a non-null WebIDL Caller.
// //
// Once we fix bug 951991, this can all be simplified. // Once we fix bug 951991, this can all be simplified.
if (!aes->mCxPusher.ref().IsStackTop()) { if (!aes->CxPusherIsStackTop()) {
return nullptr; return nullptr;
} }
@ -206,28 +206,43 @@ FindJSContext(nsIGlobalObject* aGlobalObject)
return cx; return cx;
} }
AutoEntryScript::AutoEntryScript(nsIGlobalObject* aGlobalObject, AutoJSAPI::AutoJSAPI()
bool aIsMainThread, : mCx(nsContentUtils::GetDefaultJSContextForThread())
JSContext* aCx)
: ScriptSettingsStackEntry(aGlobalObject, /* aCandidate = */ true)
, mStack(ScriptSettingsStack::Ref())
, mCx(aCx)
{ {
MOZ_ASSERT(aGlobalObject); if (NS_IsMainThread()) {
MOZ_ASSERT_IF(!mCx, aIsMainThread); // cx is mandatory off-main-thread. mCxPusher.construct(mCx);
MOZ_ASSERT_IF(mCx && aIsMainThread, mCx == FindJSContext(aGlobalObject));
if (!mCx) {
// If the caller didn't provide a cx, hunt one down. This isn't exactly
// fast, but the callers that care about performance can pass an explicit
// cx for now. Eventually, the whole cx pushing thing will go away
// entirely.
mCx = FindJSContext(aGlobalObject);
MOZ_ASSERT(mCx);
} }
// Leave the cx in a null compartment.
mNullAc.construct(mCx);
}
AutoJSAPI::AutoJSAPI(JSContext *aCx, bool aIsMainThread, bool aSkipNullAc)
: mCx(aCx)
{
MOZ_ASSERT_IF(aIsMainThread, NS_IsMainThread());
if (aIsMainThread) { if (aIsMainThread) {
mCxPusher.construct(mCx); mCxPusher.construct(mCx);
} }
mAc.construct(mCx, aGlobalObject->GetGlobalJSObject());
// In general we want to leave the cx in a null compartment, but we let
// subclasses skip this if they plan to immediately enter a compartment.
if (!aSkipNullAc) {
mNullAc.construct(mCx);
}
}
AutoEntryScript::AutoEntryScript(nsIGlobalObject* aGlobalObject,
bool aIsMainThread,
JSContext* aCx)
: AutoJSAPI(aCx ? aCx : FindJSContext(aGlobalObject), aIsMainThread, /* aSkipNullAc = */ true)
, ScriptSettingsStackEntry(aGlobalObject, /* aCandidate = */ true)
, mAc(cx(), aGlobalObject->GetGlobalJSObject())
, mStack(ScriptSettingsStack::Ref())
{
MOZ_ASSERT(aGlobalObject);
MOZ_ASSERT_IF(!aCx, aIsMainThread); // cx is mandatory off-main-thread.
MOZ_ASSERT_IF(aCx && aIsMainThread, aCx == FindJSContext(aGlobalObject));
mStack.Push(this); mStack.Push(this);
} }

View File

@ -95,10 +95,64 @@ private:
{} {}
}; };
/*
* For any interaction with JSAPI, an AutoJSAPI (or one of its subclasses)
* must be on the stack.
*
* This base class should be instantiated as-is when the caller wants to use
* JSAPI but doesn't expect to run script. Its current duties are as-follows:
*
* * Grabbing an appropriate JSContext, and, on the main thread, pushing it onto
* the JSContext stack.
* * Entering a null compartment, so that the consumer is forced to select a
* compartment to enter before manipulating objects.
*
* Additionally, the following duties are planned, but not yet implemented:
*
* * De-poisoning the JSRuntime to allow manipulation of JSAPI. We can't
* actually implement this poisoning until all the JSContext pushing in the
* system goes through AutoJSAPI (see bug 951991). For now, this de-poisoning
* effectively corresponds to having a non-null cx on the stack.
* * Reporting any exceptions left on the JSRuntime, unless the caller steals
* or silences them.
* * Entering a JSAutoRequest. At present, this is handled by the cx pushing
* on the main thread, and by other code on workers. Depending on the order
* in which various cleanup lands, this may never be necessary, because
* JSAutoRequests may go away.
*
* In situations where the consumer expects to run script, AutoEntryScript
* should be used, which does additional manipulation of the script settings
* stack. In bug 991758, we'll add hard invariants to SpiderMonkey, such that
* any attempt to run script without an AutoEntryScript on the stack will
* fail. This prevents system code from accidentally triggering script
* execution at inopportune moments via surreptitious getters and proxies.
*/
class AutoJSAPI {
public:
// Public constructor for use when the base class is constructed as-is. It
// uses the SafeJSContext (or worker equivalent), and enters a null
// compartment.
AutoJSAPI();
JSContext* cx() const { return mCx; }
bool CxPusherIsStackTop() { return mCxPusher.ref().IsStackTop(); }
protected:
// Protected constructor, allowing subclasses to specify a particular cx to
// be used.
AutoJSAPI(JSContext *aCx, bool aIsMainThread, bool aSkipNullAC = false);
private:
mozilla::Maybe<AutoCxPusher> mCxPusher;
mozilla::Maybe<JSAutoNullCompartment> mNullAc;
JSContext *mCx;
};
/* /*
* A class that represents a new script entry point. * A class that represents a new script entry point.
*/ */
class AutoEntryScript : protected ScriptSettingsStackEntry { class AutoEntryScript : public AutoJSAPI,
protected ScriptSettingsStackEntry {
public: public:
AutoEntryScript(nsIGlobalObject* aGlobalObject, AutoEntryScript(nsIGlobalObject* aGlobalObject,
bool aIsMainThread = NS_IsMainThread(), bool aIsMainThread = NS_IsMainThread(),
@ -110,15 +164,10 @@ public:
mWebIDLCallerPrincipal = aPrincipal; mWebIDLCallerPrincipal = aPrincipal;
} }
JSContext* cx() const { return mCx; }
private: private:
JSAutoCompartment mAc;
dom::ScriptSettingsStack& mStack; dom::ScriptSettingsStack& mStack;
nsCOMPtr<nsIPrincipal> mWebIDLCallerPrincipal; nsCOMPtr<nsIPrincipal> mWebIDLCallerPrincipal;
JSContext *mCx;
mozilla::Maybe<AutoCxPusher> mCxPusher;
mozilla::Maybe<JSAutoCompartment> mAc; // This can de-Maybe-fy when mCxPusher
// goes away.
friend nsIPrincipal* GetWebIDLCallerPrincipal(); friend nsIPrincipal* GetWebIDLCallerPrincipal();
}; };