From 7bc40759cfc43327484d76f707930b9412e7518f Mon Sep 17 00:00:00 2001 From: Ben Newman Date: Fri, 22 Jan 2010 12:52:57 -0800 Subject: [PATCH] Bug 541003 - CPOW: Support throwing arbitrary exceptions from content to chrome. r=mrbkap * * * CPOW-specific exceptions (bug 541003). r=mrbkap --- js/src/ipc/ObjectWrapperChild.cpp | 158 ++++++++++++++++++-------- js/src/ipc/ObjectWrapperChild.h | 30 +++-- js/src/ipc/ObjectWrapperParent.cpp | 173 +++++++++++++++++++++-------- js/src/ipc/ObjectWrapperParent.h | 39 ++++++- js/src/ipc/PObjectWrapper.ipdl | 27 +++-- 5 files changed, 307 insertions(+), 120 deletions(-) diff --git a/js/src/ipc/ObjectWrapperChild.cpp b/js/src/ipc/ObjectWrapperChild.cpp index 53c85ff98dbe..44082abe842c 100644 --- a/js/src/ipc/ObjectWrapperChild.cpp +++ b/js/src/ipc/ObjectWrapperChild.cpp @@ -59,6 +59,8 @@ namespace { nsCxPusher mStack; JSAutoRequest mRequest; + JSContext* const mContext; + const uint32 mSavedOptions; JS_DECL_USE_GUARD_OBJECT_NOTIFIER; public: @@ -66,6 +68,9 @@ namespace { AutoContextPusher(JSContext* cx JS_GUARD_OBJECT_NOTIFIER_PARAM) : mRequest(cx) + , mContext(cx) + , mSavedOptions(JS_SetOptions(cx, (JS_GetOptions(cx) | + JSOPTION_DONT_REPORT_UNCAUGHT))) { JS_GUARD_OBJECT_NOTIFIER_INIT; mStack.Push(cx); @@ -73,10 +78,70 @@ namespace { ~AutoContextPusher() { mStack.Pop(); + JS_SetOptions(mContext, mSavedOptions); } }; + class StatusPtrOwner + { + OperationStatus* mStatusPtr; + public: + StatusPtrOwner() : mStatusPtr(NULL) {} + void SetStatusPtr(OperationStatus* statusPtr) { + mStatusPtr = statusPtr; + // By default, initialize mStatusPtr to failure without an + // exception. Doing so only when the union is uninitialized + // allows AutoCheckOperation classes to be nested on the + // stack, just in case AnswerConstruct, for example, calls + // AnswerCall (as it once did, before there were unrelated + // problems with that approach). + if (mStatusPtr->type() == OperationStatus::T__None) + *mStatusPtr = JS_FALSE; + } + OperationStatus* StatusPtr() { + NS_ASSERTION(mStatusPtr, "Should have called SetStatusPtr by now."); + return mStatusPtr; + } + }; + + typedef AutoCheckOperationBase ACOBase; + + class AutoCheckOperation : public ACOBase + { + JS_DECL_USE_GUARD_OBJECT_NOTIFIER; + public: + AutoCheckOperation(ObjectWrapperChild* owc, + OperationStatus* statusPtr + JS_GUARD_OBJECT_NOTIFIER_PARAM) + : ACOBase(NULL, owc) + { + JS_GUARD_OBJECT_NOTIFIER_INIT; + SetStatusPtr(statusPtr); + } + }; +} + +void +ObjectWrapperChild::CheckOperation(JSContext*, + OperationStatus* status) +{ + NS_PRECONDITION(status->type() != OperationStatus::T__None, + "Checking an uninitialized operation."); + + JSContext* cx = Manager()->GetContext(); + jsval thrown; + + if (JS_GetPendingException(cx, &thrown)) { + NS_ASSERTION(!(status->type() == OperationStatus::TJSBool && + status->get_JSBool()), + "Operation succeeded but exception was thrown?"); + JSVariant exception; + if (!jsval_to_JSVariant(cx, thrown, &exception)) + exception = void_t(); // XXX Useful? + *status = exception; + JS_ClearPendingException(cx); + } } ObjectWrapperChild::ObjectWrapperChild(JSContext* cx, JSObject* obj) @@ -257,88 +322,85 @@ ObjectWrapperChild::AnswerSomething(/* in-parameters */ bool ObjectWrapperChild::AnswerAddProperty(const nsString& id, - JSBool* ok) + OperationStatus* status) { jsid interned_id; - *ok = JS_FALSE; - JSContext* cx = Manager()->GetContext(); AutoContextPusher acp(cx); + AutoCheckOperation aco(this, status); if (!jsid_from_nsString(cx, id, &interned_id)) return false; - *ok = JS_DefinePropertyById(cx, mObj, interned_id, JSVAL_VOID, - NULL, NULL, 0); + *status = JS_DefinePropertyById(cx, mObj, interned_id, JSVAL_VOID, + NULL, NULL, 0); return true; } bool ObjectWrapperChild::AnswerGetProperty(const nsString& id, - JSBool* ok, JSVariant* vp) + OperationStatus* status, JSVariant* vp) { jsid interned_id; jsval val; - *ok = JS_FALSE; - JSContext* cx = Manager()->GetContext(); AutoContextPusher acp(cx); + AutoCheckOperation aco(this, status); if (!jsid_from_nsString(cx, id, &interned_id)) return false; - *ok = JS_GetPropertyById(cx, mObj, interned_id, &val); + *status = JS_GetPropertyById(cx, mObj, interned_id, &val); // Since we fully expect this call to jsval_to_JSVariant to return // true, we can't just leave vp uninitialized when JS_GetPropertyById // returns JS_FALSE. This pitfall could be avoided in general if IPDL // ensured that outparams were pre-initialized to some default value // (XXXfixme cjones?). - return jsval_to_JSVariant(cx, *ok ? val : JSVAL_VOID, vp); + return jsval_to_JSVariant(cx, aco.Ok() ? val : JSVAL_VOID, vp); } bool ObjectWrapperChild::AnswerSetProperty(const nsString& id, const JSVariant& v, - JSBool* ok, JSVariant* vp) + OperationStatus* status, JSVariant* vp) { jsid interned_id; jsval val; - *ok = JS_FALSE; *vp = v; JSContext* cx = Manager()->GetContext(); AutoContextPusher acp(cx); + AutoCheckOperation aco(this, status); if (!jsid_from_nsString(cx, id, &interned_id) || !jsval_from_JSVariant(cx, v, &val)) return false; - *ok = JS_SetPropertyById(cx, mObj, interned_id, &val); + *status = JS_SetPropertyById(cx, mObj, interned_id, &val); - return jsval_to_JSVariant(cx, *ok ? val : JSVAL_VOID, vp); + return jsval_to_JSVariant(cx, aco.Ok() ? val : JSVAL_VOID, vp); } bool ObjectWrapperChild::AnswerDelProperty(const nsString& id, - JSBool* ok, JSVariant* vp) + OperationStatus* status, JSVariant* vp) { jsid interned_id; jsval val; - *ok = JS_FALSE; - JSContext* cx = Manager()->GetContext(); AutoContextPusher acp(cx); + AutoCheckOperation aco(this, status); if (!jsid_from_nsString(cx, id, &interned_id)) return false; - *ok = JS_DeletePropertyById2(cx, mObj, interned_id, &val); + *status = JS_DeletePropertyById2(cx, mObj, interned_id, &val); - return jsval_to_JSVariant(cx, *ok ? val : JSVAL_VOID, vp); + return jsval_to_JSVariant(cx, aco.Ok() ? val : JSVAL_VOID, vp); } static const PRUint32 sNextIdIndexSlot = 0; @@ -370,13 +432,13 @@ static const JSClass sCPOW_NewEnumerateState_JSClass = { bool ObjectWrapperChild::AnswerNewEnumerateInit(/* no in-parameters */ - JSBool* ok, JSVariant* statep, int* idp) + OperationStatus* status, JSVariant* statep, int* idp) { - *ok = JS_FALSE; *idp = 0; JSContext* cx = Manager()->GetContext(); AutoContextPusher acp(cx); + AutoCheckOperation aco(this, status); JSClass* clasp = const_cast(&sCPOW_NewEnumerateState_JSClass); JSObject* state = JS_NewObjectWithGivenProto(cx, clasp, NULL, NULL); @@ -408,27 +470,27 @@ ObjectWrapperChild::AnswerNewEnumerateInit(/* no in-parameters */ } *idp = strIds->Length(); - *ok = (JS_SetPrivate(cx, state, strIds) && - JS_SetReservedSlot(cx, state, sNextIdIndexSlot, - JSVAL_ZERO) && - JSObject_to_JSVariant(cx, state, statep)); + *status = (JS_SetPrivate(cx, state, strIds) && + JS_SetReservedSlot(cx, state, sNextIdIndexSlot, + JSVAL_ZERO) && + JSObject_to_JSVariant(cx, state, statep)); return true; } bool ObjectWrapperChild::AnswerNewEnumerateNext(const JSVariant& in_state, - JSBool* ok, JSVariant* statep, nsString* idp) + OperationStatus* status, JSVariant* statep, nsString* idp) { JSObject* state; jsval v; - *ok = JS_FALSE; *statep = in_state; idp->Truncate(); JSContext* cx = Manager()->GetContext(); AutoContextPusher acp(cx); + AutoCheckOperation aco(this, status); if (!JSObject_from_JSVariant(cx, in_state, &state)) return false; @@ -444,13 +506,13 @@ ObjectWrapperChild::AnswerNewEnumerateNext(const JSVariant& in_state, NS_ASSERTION(i <= strIds->Length(), "Index of next jsid too large?"); if (i == strIds->Length()) { - *ok = JS_TRUE; + *status = JS_TRUE; return JSObject_to_JSVariant(cx, NULL, statep); } *idp = strIds->ElementAt(i); - *ok = JS_SetReservedSlot(cx, state, sNextIdIndexSlot, - INT_TO_JSVAL(i + 1)); + *status = JS_SetReservedSlot(cx, state, sNextIdIndexSlot, + INT_TO_JSVAL(i + 1)); return true; } @@ -472,15 +534,15 @@ ObjectWrapperChild::RecvNewEnumerateDestroy(const JSVariant& in_state) bool ObjectWrapperChild::AnswerNewResolve(const nsString& id, const int& flags, - JSBool* ok, PObjectWrapperChild** obj2) + OperationStatus* status, PObjectWrapperChild** obj2) { jsid interned_id; - *ok = JS_FALSE; *obj2 = NULL; JSContext* cx = Manager()->GetContext(); AutoContextPusher acp(cx); + AutoCheckOperation aco(this, status); if (!jsid_from_nsString(cx, id, &interned_id)) return false; @@ -492,7 +554,7 @@ ObjectWrapperChild::AnswerNewResolve(const nsString& id, const int& flags, if (!JS_GetPropertyDescriptorById(cx, mObj, interned_id, flags, &desc)) return true; - *ok = JS_TRUE; + *status = JS_TRUE; if (desc.obj) *obj2 = Manager()->GetOrCreateWrapper(desc.obj); @@ -502,13 +564,14 @@ ObjectWrapperChild::AnswerNewResolve(const nsString& id, const int& flags, bool ObjectWrapperChild::AnswerConvert(const JSType& type, - JSBool* ok, JSVariant* vp) + OperationStatus* status, JSVariant* vp) { jsval v; JSContext* cx = Manager()->GetContext(); AutoContextPusher acp(cx); - *ok = JS_ConvertValue(cx, OBJECT_TO_JSVAL(mObj), type, &v); - return jsval_to_JSVariant(cx, *ok ? v : JSVAL_VOID, vp); + AutoCheckOperation aco(this, status); + *status = JS_ConvertValue(cx, OBJECT_TO_JSVAL(mObj), type, &v); + return jsval_to_JSVariant(cx, aco.Ok() ? v : JSVAL_VOID, vp); } namespace { @@ -518,12 +581,11 @@ namespace { bool ObjectWrapperChild::AnswerCall(PObjectWrapperChild* receiver, const nsTArray& argv, - JSBool* ok, JSVariant* rval) + OperationStatus* status, JSVariant* rval) { - *ok = JS_FALSE; - JSContext* cx = Manager()->GetContext(); AutoContextPusher acp(cx); + AutoCheckOperation aco(this, status); JSObject* obj; if (!JSObject_from_PObjectWrapperChild(cx, receiver, &obj)) @@ -541,20 +603,19 @@ ObjectWrapperChild::AnswerCall(PObjectWrapperChild* receiver, const nsTArray& argv, - JSBool* ok, PObjectWrapperChild** rval) + OperationStatus* status, PObjectWrapperChild** rval) { - *ok = JS_FALSE; - JSContext* cx = Manager()->GetContext(); AutoContextPusher acp(cx); + AutoCheckOperation aco(this, status); AutoJSArgs args; PRUint32 argc = argv.Length(); @@ -569,7 +630,7 @@ ObjectWrapperChild::AnswerConstruct(const nsTArray& argv, JSObject* obj = JS_New(cx, mObj, argc, jsargs); - *ok = !!obj; + *status = !!obj; *rval = Manager()->GetOrCreateWrapper(obj); return true; @@ -577,13 +638,14 @@ ObjectWrapperChild::AnswerConstruct(const nsTArray& argv, bool ObjectWrapperChild::AnswerHasInstance(const JSVariant& v, - JSBool* ok, JSBool* bp) + OperationStatus* status, JSBool* bp) { jsval candidate; JSContext* cx = Manager()->GetContext(); AutoContextPusher acp(cx); + AutoCheckOperation aco(this, status); if (!jsval_from_JSVariant(cx, v, &candidate)) return false; - *ok = JS_HasInstance(cx, mObj, candidate, bp); + *status = JS_HasInstance(cx, mObj, candidate, bp); return true; } diff --git a/js/src/ipc/ObjectWrapperChild.h b/js/src/ipc/ObjectWrapperChild.h index b6d823f2d321..dbf088d2fd92 100644 --- a/js/src/ipc/ObjectWrapperChild.h +++ b/js/src/ipc/ObjectWrapperChild.h @@ -43,21 +43,27 @@ #include "mozilla/jsipc/PObjectWrapperChild.h" +// For OperationChecker and AutoCheckOperationBase. +#include "mozilla/jsipc/ObjectWrapperParent.h" + using mozilla::jsipc::JSVariant; namespace mozilla { namespace jsipc { class ContextWrapperChild; - + class ObjectWrapperChild : public PObjectWrapperChild + , public OperationChecker { public: ObjectWrapperChild(JSContext* cx, JSObject* obj); JSObject* GetJSObject() const { return mObj; } + + void CheckOperation(JSContext* cx, OperationStatus* status); private: @@ -81,39 +87,39 @@ protected: void ActorDestroy(ActorDestroyReason why); bool AnswerAddProperty(const nsString& id, - JSBool* ok); + OperationStatus* status); bool AnswerGetProperty(const nsString& id, - JSBool* ok, JSVariant* vp); + OperationStatus* status, JSVariant* vp); bool AnswerSetProperty(const nsString& id, const JSVariant& v, - JSBool* ok, JSVariant* vp); + OperationStatus* status, JSVariant* vp); bool AnswerDelProperty(const nsString& id, - JSBool* ok, JSVariant* vp); + OperationStatus* status, JSVariant* vp); bool AnswerNewEnumerateInit(/* no in-parameters */ - JSBool* ok, JSVariant* statep, int* idp); + OperationStatus* status, JSVariant* statep, int* idp); bool AnswerNewEnumerateNext(const JSVariant& in_state, - JSBool* ok, JSVariant* statep, nsString* idp); + OperationStatus* status, JSVariant* statep, nsString* idp); bool RecvNewEnumerateDestroy(const JSVariant& in_state); bool AnswerNewResolve(const nsString& id, const int& flags, - JSBool* ok, PObjectWrapperChild** obj2); + OperationStatus* status, PObjectWrapperChild** obj2); bool AnswerConvert(const JSType& type, - JSBool* ok, JSVariant* vp); + OperationStatus* status, JSVariant* vp); bool AnswerCall(PObjectWrapperChild* receiver, const nsTArray& argv, - JSBool* ok, JSVariant* rval); + OperationStatus* status, JSVariant* rval); bool AnswerConstruct(const nsTArray& argv, - JSBool* ok, PObjectWrapperChild** rval); + OperationStatus* status, PObjectWrapperChild** rval); bool AnswerHasInstance(const JSVariant& v, - JSBool* ok, JSBool* bp); + OperationStatus* status, JSBool* bp); }; }} diff --git a/js/src/ipc/ObjectWrapperParent.cpp b/js/src/ipc/ObjectWrapperParent.cpp index 3bd6abb6475c..92980cf326a1 100644 --- a/js/src/ipc/ObjectWrapperParent.cpp +++ b/js/src/ipc/ObjectWrapperParent.cpp @@ -103,6 +103,69 @@ namespace { }; + class StatusMemberOwner + { + OperationStatus mStatus; + public: + StatusMemberOwner() : mStatus(JS_FALSE) {} + OperationStatus* StatusPtr() { + return &mStatus; + } + }; + + typedef AutoCheckOperationBase ACOBase; + + class AutoCheckOperation : public ACOBase + { + JS_DECL_USE_GUARD_OBJECT_NOTIFIER; + public: + AutoCheckOperation(JSContext* cx, + ObjectWrapperParent* owp + JS_GUARD_OBJECT_NOTIFIER_PARAM) + : ACOBase(cx, owp) + { + JS_GUARD_OBJECT_NOTIFIER_INIT; + } + }; + +} + +void +ObjectWrapperParent::CheckOperation(JSContext* cx, + OperationStatus* status) +{ + NS_PRECONDITION(status->type() != OperationStatus::T__None, + "Checking an uninitialized operation."); + + switch (status->type()) { + case OperationStatus::TJSVariant: + { + jsval thrown; + if (jsval_from_JSVariant(cx, status->get_JSVariant(), &thrown)) + JS_SetPendingException(cx, thrown); + *status = JS_FALSE; + } + break; + case OperationStatus::TJSBool: + if (!status->get_JSBool() && !JS_IsExceptionPending(cx)) { + NS_WARNING("CPOW operation failed without setting an exception."); + } + break; + default: + NS_NOTREACHED("Invalid or uninitialized OperationStatus type."); + break; + } +} + +template +static RType +with_error(JSContext* cx, + RType rval, + const char* error = NULL) +{ + if (!JS_IsExceptionPending(cx)) + JS_ReportError(cx, error ? error : "Unspecified CPOW error"); + return rval; } const JSExtendedClass ObjectWrapperParent::sCPOW_JSClass = { @@ -198,7 +261,7 @@ ObjectWrapperParent::jsval_to_JSVariant(JSContext* cx, jsval from, { PObjectWrapperParent* powp; if (!JSObject_to_PObjectWrapperParent(cx, JSVAL_TO_OBJECT(from), &powp)) - return false; + return with_error(cx, false, "Cannot pass parent-created object to child"); *to = powp; } return true; @@ -217,8 +280,9 @@ ObjectWrapperParent::jsval_to_JSVariant(JSContext* cx, jsval from, *to = !!JSVAL_TO_BOOLEAN(from); return true; case JSTYPE_XML: + return with_error(cx, false, "CPOWs currently cannot handle JSTYPE_XML"); default: - return false; + return with_error(cx, false, "Bad jsval type"); } } @@ -332,22 +396,22 @@ ObjectWrapperParent::CPOW_AddProperty(JSContext *cx, JSObject *obj, jsval id, ObjectWrapperParent* self = Unwrap(cx, obj); if (!self) - return JS_FALSE; + return with_error(cx, JS_FALSE, "Unwrapping failed in CPOW_AddProperty"); if (AutoResolveFlag::IsSet(cx, obj)) return JS_TRUE; - + + AutoCheckOperation aco(cx, self); + nsString in_id; if (!jsval_to_nsString(cx, id, &in_id)) return JS_FALSE; - JSBool out_ok; - return (self->Manager()->RequestRunToCompletion() && self->CallAddProperty(in_id, - &out_ok) && - out_ok); + aco.StatusPtr()) && + aco.Ok()); } /*static*/ JSBool @@ -359,20 +423,21 @@ ObjectWrapperParent::CPOW_GetProperty(JSContext *cx, JSObject *obj, jsval id, ObjectWrapperParent* self = Unwrap(cx, obj); if (!self) - return JS_FALSE; + return with_error(cx, JS_FALSE, "Unwrapping failed in CPOW_GetProperty"); + + AutoCheckOperation aco(cx, self); nsString in_id; if (!jsval_to_nsString(cx, id, &in_id)) return JS_FALSE; - JSBool out_ok; JSVariant out_v; return (self->Manager()->RequestRunToCompletion() && self->CallGetProperty(in_id, - &out_ok, &out_v) && - out_ok && + aco.StatusPtr(), &out_v) && + aco.Ok() && self->jsval_from_JSVariant(cx, out_v, vp)); } @@ -385,7 +450,9 @@ ObjectWrapperParent::CPOW_SetProperty(JSContext *cx, JSObject *obj, jsval id, ObjectWrapperParent* self = Unwrap(cx, obj); if (!self) - return JS_FALSE; + return with_error(cx, JS_FALSE, "Unwrapping failed in CPOW_SetProperty"); + + AutoCheckOperation aco(cx, self); nsString in_id; JSVariant in_v; @@ -393,14 +460,13 @@ ObjectWrapperParent::CPOW_SetProperty(JSContext *cx, JSObject *obj, jsval id, if (!jsval_to_nsString(cx, id, &in_id) || !self->jsval_to_JSVariant(cx, *vp, &in_v)) return JS_FALSE; - - JSBool out_ok; + JSVariant out_v; return (self->Manager()->RequestRunToCompletion() && self->CallSetProperty(in_id, in_v, - &out_ok, &out_v) && - out_ok && + aco.StatusPtr(), &out_v) && + aco.Ok() && self->jsval_from_JSVariant(cx, out_v, vp)); } @@ -413,32 +479,34 @@ ObjectWrapperParent::CPOW_DelProperty(JSContext *cx, JSObject *obj, jsval id, ObjectWrapperParent* self = Unwrap(cx, obj); if (!self) - return JS_FALSE; + return with_error(cx, JS_FALSE, "Unwrapping failed in CPOW_DelProperty"); + + AutoCheckOperation aco(cx, self); nsString in_id; if (!jsval_to_nsString(cx, id, &in_id)) return JS_FALSE; - JSBool out_ok; JSVariant out_v; return (self->Manager()->RequestRunToCompletion() && self->CallDelProperty(in_id, - &out_ok, &out_v) && - out_ok && + aco.StatusPtr(), &out_v) && + aco.Ok() && jsval_from_JSVariant(cx, out_v, vp)); } JSBool ObjectWrapperParent::NewEnumerateInit(JSContext* cx, jsval* statep, jsid* idp) { - JSBool out_ok; + AutoCheckOperation aco(cx, this); + JSVariant out_state; int out_id; - return (CallNewEnumerateInit(&out_ok, &out_state, &out_id) && - out_ok && + return (CallNewEnumerateInit(aco.StatusPtr(), &out_state, &out_id) && + aco.Ok() && jsval_from_JSVariant(cx, out_state, statep) && (!idp || jsid_from_int(cx, out_id, idp))); } @@ -446,17 +514,19 @@ ObjectWrapperParent::NewEnumerateInit(JSContext* cx, jsval* statep, jsid* idp) JSBool ObjectWrapperParent::NewEnumerateNext(JSContext* cx, jsval* statep, jsid* idp) { + AutoCheckOperation aco(cx, this); + JSVariant in_state; + if (!jsval_to_JSVariant(cx, *statep, &in_state)) return JS_FALSE; - JSBool out_ok; JSVariant out_state; nsString out_id; if (CallNewEnumerateNext(in_state, - &out_ok, &out_state, &out_id) && - out_ok && + aco.StatusPtr(), &out_state, &out_id) && + aco.Ok() && jsval_from_JSVariant(cx, out_state, statep) && jsid_from_nsString(cx, out_id, idp)) { @@ -471,7 +541,10 @@ ObjectWrapperParent::NewEnumerateNext(JSContext* cx, jsval* statep, jsid* idp) JSBool ObjectWrapperParent::NewEnumerateDestroy(JSContext* cx, jsval state) { + AutoCheckOperation aco(cx, this); + JSVariant in_state; + if (!jsval_to_JSVariant(cx, state, &in_state)) return JS_FALSE; @@ -487,7 +560,7 @@ ObjectWrapperParent::CPOW_NewEnumerate(JSContext *cx, JSObject *obj, ObjectWrapperParent* self = Unwrap(cx, obj); if (!self) - return JS_FALSE; + return with_error(cx, JS_FALSE, "Unwrapping failed in CPOW_NewEnumerate"); switch (enum_op) { case JSENUMERATE_INIT: @@ -499,6 +572,7 @@ ObjectWrapperParent::CPOW_NewEnumerate(JSContext *cx, JSObject *obj, return self->NewEnumerateDestroy(cx, *statep); } + NS_NOTREACHED("Unknown enum_op value in CPOW_NewEnumerate"); return JS_FALSE; } @@ -511,20 +585,21 @@ ObjectWrapperParent::CPOW_NewResolve(JSContext *cx, JSObject *obj, jsval id, ObjectWrapperParent* self = Unwrap(cx, obj); if (!self) - return JS_FALSE; + return with_error(cx, JS_FALSE, "Unwrapping failed in CPOW_NewResolve"); + + AutoCheckOperation aco(cx, self); nsString in_id; if (!jsval_to_nsString(cx, id, &in_id)) return JS_FALSE; - JSBool out_ok; PObjectWrapperParent* out_pobj; if (!self->Manager()->RequestRunToCompletion() || !self->CallNewResolve(in_id, flags, - &out_ok, &out_pobj) || - !out_ok || + aco.StatusPtr(), &out_pobj) || + !aco.Ok() || !JSObject_from_PObjectWrapperParent(cx, out_pobj, objp)) return JS_FALSE; @@ -547,7 +622,7 @@ ObjectWrapperParent::CPOW_Convert(JSContext *cx, JSObject *obj, JSType type, ObjectWrapperParent* self = Unwrap(cx, obj); if (!self) - return JS_FALSE; + return with_error(cx, JS_FALSE, "Unwrapping failed in CPOW_Convert"); *vp = OBJECT_TO_JSVAL(obj); @@ -573,7 +648,9 @@ ObjectWrapperParent::CPOW_Call(JSContext* cx, JSObject* obj, uintN argc, ObjectWrapperParent* function = Unwrap(cx, JSVAL_TO_OBJECT(JS_ARGV_CALLEE(argv))); if (!function) - return JS_FALSE; + return with_error(cx, JS_FALSE, "Could not unwrap CPOW function"); + + AutoCheckOperation aco(cx, function); ObjectWrapperParent* receiver = Unwrap(cx, obj); if (!receiver) { @@ -588,14 +665,13 @@ ObjectWrapperParent::CPOW_Call(JSContext* cx, JSObject* obj, uintN argc, for (uintN i = 0; i < argc; i++) if (!jsval_to_JSVariant(cx, argv[i], in_argv.AppendElement())) return JS_FALSE; - - JSBool out_ok; + JSVariant out_rval; return (function->Manager()->RequestRunToCompletion() && function->CallCall(receiver, in_argv, - &out_ok, &out_rval) && - out_ok && + aco.StatusPtr(), &out_rval) && + aco.Ok() && jsval_from_JSVariant(cx, out_rval, rval)); } @@ -608,20 +684,21 @@ ObjectWrapperParent::CPOW_Construct(JSContext *cx, JSObject *obj, uintN argc, ObjectWrapperParent* constructor = Unwrap(cx, JSVAL_TO_OBJECT(JS_ARGV_CALLEE(argv))); if (!constructor) - return JS_FALSE; + return with_error(cx, JS_FALSE, "Could not unwrap CPOW constructor function"); + + AutoCheckOperation aco(cx, constructor); nsTArray in_argv(argc); for (uintN i = 0; i < argc; i++) if (!jsval_to_JSVariant(cx, argv[i], in_argv.AppendElement())) return JS_FALSE; - JSBool out_ok; PObjectWrapperParent* out_powp; return (constructor->Manager()->RequestRunToCompletion() && constructor->CallConstruct(in_argv, - &out_ok, &out_powp) && - out_ok && + aco.StatusPtr(), &out_powp) && + aco.Ok() && jsval_from_PObjectWrapperParent(cx, out_powp, rval)); } @@ -635,19 +712,19 @@ ObjectWrapperParent::CPOW_HasInstance(JSContext *cx, JSObject *obj, jsval v, ObjectWrapperParent* self = Unwrap(cx, obj); if (!self) - return JS_FALSE; + return with_error(cx, JS_FALSE, "Unwrapping failed in CPOW_HasInstance"); + + AutoCheckOperation aco(cx, self); JSVariant in_v; if (!jsval_to_JSVariant(cx, v, &in_v)) return JS_FALSE; - JSBool out_ok; - return (self->Manager()->RequestRunToCompletion() && self->CallHasInstance(in_v, - &out_ok, bp) && - out_ok); + aco.StatusPtr(), bp) && + aco.Ok()); } /*static*/ JSBool @@ -660,7 +737,7 @@ ObjectWrapperParent::CPOW_Equality(JSContext *cx, JSObject *obj, jsval v, ObjectWrapperParent* self = Unwrap(cx, obj); if (!self) - return JS_FALSE; + return with_error(cx, JS_FALSE, "Unwrapping failed in CPOW_Equality"); if (JSVAL_IS_PRIMITIVE(v)) return JS_TRUE; diff --git a/js/src/ipc/ObjectWrapperParent.h b/js/src/ipc/ObjectWrapperParent.h index 92a1669d8e20..f19cdb2b38ad 100644 --- a/js/src/ipc/ObjectWrapperParent.h +++ b/js/src/ipc/ObjectWrapperParent.h @@ -50,8 +50,15 @@ namespace jsipc { class ContextWrapperParent; +class OperationChecker { +public: + virtual void CheckOperation(JSContext* cx, + OperationStatus* status) = 0; +}; + class ObjectWrapperParent : public PObjectWrapperParent + , public OperationChecker { public: @@ -65,6 +72,9 @@ public: return OBJECT_TO_JSVAL(GetJSObject(cx)); } + void CheckOperation(JSContext* cx, + OperationStatus* status); + static const JSExtendedClass sCPOW_JSClass; protected: @@ -136,6 +146,33 @@ private: jsval* to); }; +template +class AutoCheckOperationBase + : public StatusOwnerPolicy +{ + JSContext* const mContext; + OperationChecker* const mChecker; + +protected: + + AutoCheckOperationBase(JSContext* cx, + OperationChecker* checker) + : mContext(cx) + , mChecker(checker) + {} + + virtual ~AutoCheckOperationBase() { + mChecker->CheckOperation(mContext, StatusOwnerPolicy::StatusPtr()); + } + +public: + + bool Ok() { + return (StatusOwnerPolicy::StatusPtr()->type() == OperationStatus::TJSBool && + StatusOwnerPolicy::StatusPtr()->get_JSBool()); + } +}; + }} - + #endif diff --git a/js/src/ipc/PObjectWrapper.ipdl b/js/src/ipc/PObjectWrapper.ipdl index 141f4ca54e41..6eac781d1099 100644 --- a/js/src/ipc/PObjectWrapper.ipdl +++ b/js/src/ipc/PObjectWrapper.ipdl @@ -59,6 +59,11 @@ union JSVariant { // and IPC::ParamTraits mistakes JSIntn for int. }; +union OperationStatus { + JSBool; + JSVariant; // Exception thrown. +}; + rpc protocol PObjectWrapper { manager PContextWrapper; @@ -67,28 +72,28 @@ child: __delete__(); // unroot rpc AddProperty(nsString id) - returns (JSBool ok); + returns (OperationStatus status); rpc GetProperty(nsString id) - returns (JSBool ok, + returns (OperationStatus status, JSVariant vp); rpc SetProperty(nsString id, JSVariant v) - returns (JSBool ok, + returns (OperationStatus status, JSVariant vp); rpc DelProperty(nsString id) - returns (JSBool ok, + returns (OperationStatus status, JSVariant vp); rpc NewEnumerateInit() - returns (JSBool ok, + returns (OperationStatus status, JSVariant statep, int idp); rpc NewEnumerateNext(JSVariant in_state) - returns (JSBool ok, + returns (OperationStatus status, JSVariant statep, nsString idp); @@ -96,24 +101,24 @@ child: rpc NewResolve(nsString id, int flags) - returns (JSBool ok, + returns (OperationStatus status, nullable PObjectWrapper obj2); rpc Convert(JSType type) - returns (JSBool ok, + returns (OperationStatus status, JSVariant vp); rpc Call(PObjectWrapper receiver, JSVariant[] argv) - returns (JSBool ok, + returns (OperationStatus status, JSVariant rval); rpc Construct(JSVariant[] argv) - returns (JSBool ok, + returns (OperationStatus status, nullable PObjectWrapper rval); rpc HasInstance(JSVariant v) - returns (JSBool ok, + returns (OperationStatus status, JSBool bp); };