diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index 8a509acc3dc..f252970e308 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -903,6 +903,22 @@ nsDOMClassInfo::Create(nsIXPConnectWrappedNative *wrapper, return NS_ERROR_UNEXPECTED; } +NS_IMETHODIMP +nsDOMClassInfo::PostCreate(nsIXPConnectWrappedNative *wrapper, + JSContext *cx, JSObject *obj) +{ + NS_WARNING("nsDOMClassInfo::PostCreate Don't call me!"); + + return NS_ERROR_UNEXPECTED; +} + +NS_IMETHODIMP +nsDOMClassInfo::PostTransplant(nsIXPConnectWrappedNative *wrapper, + JSContext *cx, JSObject *obj) +{ + MOZ_CRASH("nsDOMClassInfo::PostTransplant Don't call me!"); +} + NS_IMETHODIMP nsDOMClassInfo::AddProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, JSObject *obj, jsid id, jsval *vp, diff --git a/js/xpconnect/idl/nsIXPCScriptable.idl b/js/xpconnect/idl/nsIXPCScriptable.idl index 6fd0690a9c0..0117c1439e5 100644 --- a/js/xpconnect/idl/nsIXPCScriptable.idl +++ b/js/xpconnect/idl/nsIXPCScriptable.idl @@ -32,14 +32,14 @@ interface nsIXPConnectWrappedNative; * boolean to PR_TRUE before making the call. Implementations may skip writing * to *_retval unless they want to return PR_FALSE. */ -[uuid(24425dab-4e6b-4df5-b621-da2458c3bc42)] +[uuid(e7dee706-1286-4a22-954b-f0d8a9821bc9)] interface nsIXPCScriptable : nsISupports { /* bitflags used for 'flags' (only 32 bits available!) */ const uint32_t WANT_PRECREATE = 1 << 0; const uint32_t WANT_CREATE = 1 << 1; - // unused bit here + const uint32_t WANT_POSTCREATE = 1 << 2; const uint32_t WANT_ADDPROPERTY = 1 << 3; const uint32_t WANT_DELPROPERTY = 1 << 4; const uint32_t WANT_GETPROPERTY = 1 << 5; @@ -80,6 +80,16 @@ interface nsIXPCScriptable : nsISupports void create(in nsIXPConnectWrappedNative wrapper, in JSContextPtr cx, in JSObjectPtr obj); + // Both methods here are protected by WANT_POSTCREATE. If you want to do + // something after a wrapper is created, there's a good chance you also + // want to do something when the wrapper is transplanted to a new + // compartment. + void postCreate(in nsIXPConnectWrappedNative wrapper, + in JSContextPtr cx, in JSObjectPtr obj); + + void postTransplant(in nsIXPConnectWrappedNative wrapper, + in JSContextPtr cx, in JSObjectPtr obj); + boolean addProperty(in nsIXPConnectWrappedNative wrapper, in JSContextPtr cx, in JSObjectPtr obj, in jsid id, in JSValPtr vp); diff --git a/js/xpconnect/public/xpc_map_end.h b/js/xpconnect/public/xpc_map_end.h index 978490f06ea..540d291cffb 100644 --- a/js/xpconnect/public/xpc_map_end.h +++ b/js/xpconnect/public/xpc_map_end.h @@ -37,6 +37,9 @@ XPC_MAP_CLASSNAME::GetScriptableFlags() #ifdef XPC_MAP_WANT_CREATE nsIXPCScriptable::WANT_CREATE | #endif +#ifdef XPC_MAP_WANT_POSTCREATE + nsIXPCScriptable::WANT_POSTCREATE | +#endif #ifdef XPC_MAP_WANT_ADDPROPERTY nsIXPCScriptable::WANT_ADDPROPERTY | #endif @@ -92,6 +95,14 @@ NS_IMETHODIMP XPC_MAP_CLASSNAME::Create(nsIXPConnectWrappedNative *wrapper, JSCo {NS_ERROR("never called"); return NS_ERROR_NOT_IMPLEMENTED;} #endif +#ifndef XPC_MAP_WANT_POSTCREATE +NS_IMETHODIMP XPC_MAP_CLASSNAME::PostCreate(nsIXPConnectWrappedNative *wrapper, JSContext * cx, JSObject * obj) + {NS_ERROR("never called"); return NS_ERROR_NOT_IMPLEMENTED;} + +NS_IMETHODIMP XPC_MAP_CLASSNAME::PostTransplant(nsIXPConnectWrappedNative *wrapper, JSContext * cx, JSObject * obj) + {NS_ERROR("never called"); return NS_ERROR_NOT_IMPLEMENTED;} +#endif + #ifndef XPC_MAP_WANT_ADDPROPERTY NS_IMETHODIMP XPC_MAP_CLASSNAME::AddProperty(nsIXPConnectWrappedNative *wrapper, JSContext * cx, JSObject * obj, jsid id, JS::Value * vp, bool *_retval) {NS_ERROR("never called"); return NS_ERROR_NOT_IMPLEMENTED;} @@ -170,6 +181,10 @@ NS_IMETHODIMP XPC_MAP_CLASSNAME::PostCreatePrototype(JSContext *cx, JSObject *pr #undef XPC_MAP_WANT_CREATE #endif +#ifdef XPC_MAP_WANT_POSTCREATE +#undef XPC_MAP_WANT_POSTCREATE +#endif + #ifdef XPC_MAP_WANT_ADDPROPERTY #undef XPC_MAP_WANT_ADDPROPERTY #endif diff --git a/js/xpconnect/src/XPCWrappedNative.cpp b/js/xpconnect/src/XPCWrappedNative.cpp index 5dc4c090a50..63da2af8fd9 100644 --- a/js/xpconnect/src/XPCWrappedNative.cpp +++ b/js/xpconnect/src/XPCWrappedNative.cpp @@ -480,6 +480,36 @@ FinishCreate(XPCWrappedNativeScope* Scope, if (cache && !cache->GetWrapperPreserveColor()) cache->SetWrapper(flat); + + // Our newly created wrapper is the one that we just added to the table. + // All is well. Call PostCreate as necessary. + XPCNativeScriptableInfo* si = wrapper->GetScriptableInfo(); + if (si && si->GetFlags().WantPostCreate()) { + nsresult rv = si->GetCallback()->PostCreate(wrapper, cx, flat); + if (NS_FAILED(rv)) { + // PostCreate failed and that's Very Bad. We'll remove it from + // the map and mark it as invalid, but the PostCreate function + // may have handed the partially-constructed-and-now-invalid + // wrapper to someone before failing. Or, perhaps worse, the + // PostCreate call could have triggered code that reentered + // XPConnect and tried to wrap the same object. In that case + // *we* hand out the invalid wrapper since it is already in our + // map :( + NS_ERROR("PostCreate failed! This is known to cause " + "inconsistent state for some class types and may even " + "cause a crash in combination with a JS GC. Fix the " + "failing PostCreate ASAP!"); + + map->Remove(wrapper); + + // This would be a good place to tell the wrapper not to remove + // itself from the map when it dies... See bug 429442. + + if (cache) + cache->ClearWrapper(); + return rv; + } + } } DEBUG_CheckClassInfoClaims(wrapper); diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h index f34d3869d9b..d9b1a3007db 100644 --- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -1614,6 +1614,7 @@ public: bool WantPreCreate() GET_IT(WANT_PRECREATE) bool WantCreate() GET_IT(WANT_CREATE) + bool WantPostCreate() GET_IT(WANT_POSTCREATE) bool WantAddProperty() GET_IT(WANT_ADDPROPERTY) bool WantDelProperty() GET_IT(WANT_DELPROPERTY) bool WantGetProperty() GET_IT(WANT_GETPROPERTY)