michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* vim: set ts=8 sts=4 et sw=4 tw=99: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* Shared proto object for XPCWrappedNative. */ michael@0: michael@0: #include "xpcprivate.h" michael@0: #include "nsCxPusher.h" michael@0: #include "pratom.h" michael@0: michael@0: using namespace mozilla; michael@0: michael@0: #ifdef DEBUG michael@0: int32_t XPCWrappedNativeProto::gDEBUG_LiveProtoCount = 0; michael@0: #endif michael@0: michael@0: XPCWrappedNativeProto::XPCWrappedNativeProto(XPCWrappedNativeScope* Scope, michael@0: nsIClassInfo* ClassInfo, michael@0: uint32_t ClassInfoFlags, michael@0: XPCNativeSet* Set) michael@0: : mScope(Scope), michael@0: mJSProtoObject(nullptr), michael@0: mClassInfo(ClassInfo), michael@0: mClassInfoFlags(ClassInfoFlags), michael@0: mSet(Set), michael@0: mScriptableInfo(nullptr) michael@0: { michael@0: // This native object lives as long as its associated JSObject - killed michael@0: // by finalization of the JSObject (or explicitly if Init fails). michael@0: michael@0: MOZ_COUNT_CTOR(XPCWrappedNativeProto); michael@0: MOZ_ASSERT(mScope); michael@0: michael@0: #ifdef DEBUG michael@0: PR_ATOMIC_INCREMENT(&gDEBUG_LiveProtoCount); michael@0: #endif michael@0: } michael@0: michael@0: XPCWrappedNativeProto::~XPCWrappedNativeProto() michael@0: { michael@0: MOZ_ASSERT(!mJSProtoObject, "JSProtoObject still alive"); michael@0: michael@0: MOZ_COUNT_DTOR(XPCWrappedNativeProto); michael@0: michael@0: #ifdef DEBUG michael@0: PR_ATOMIC_DECREMENT(&gDEBUG_LiveProtoCount); michael@0: #endif michael@0: michael@0: // Note that our weak ref to mScope is not to be trusted at this point. michael@0: michael@0: XPCNativeSet::ClearCacheEntryForClassInfo(mClassInfo); michael@0: michael@0: delete mScriptableInfo; michael@0: } michael@0: michael@0: bool michael@0: XPCWrappedNativeProto::Init(const XPCNativeScriptableCreateInfo* scriptableCreateInfo, michael@0: bool callPostCreatePrototype) michael@0: { michael@0: AutoJSContext cx; michael@0: nsIXPCScriptable *callback = scriptableCreateInfo ? michael@0: scriptableCreateInfo->GetCallback() : michael@0: nullptr; michael@0: if (callback) { michael@0: mScriptableInfo = michael@0: XPCNativeScriptableInfo::Construct(scriptableCreateInfo); michael@0: if (!mScriptableInfo) michael@0: return false; michael@0: } michael@0: michael@0: const js::Class* jsclazz; michael@0: michael@0: if (mScriptableInfo) { michael@0: const XPCNativeScriptableFlags& flags(mScriptableInfo->GetFlags()); michael@0: michael@0: if (flags.AllowPropModsToPrototype()) { michael@0: jsclazz = flags.WantCall() ? michael@0: &XPC_WN_ModsAllowed_WithCall_Proto_JSClass : michael@0: &XPC_WN_ModsAllowed_NoCall_Proto_JSClass; michael@0: } else { michael@0: jsclazz = flags.WantCall() ? michael@0: &XPC_WN_NoMods_WithCall_Proto_JSClass : michael@0: &XPC_WN_NoMods_NoCall_Proto_JSClass; michael@0: } michael@0: } else { michael@0: jsclazz = &XPC_WN_NoMods_NoCall_Proto_JSClass; michael@0: } michael@0: michael@0: JS::RootedObject parent(cx, mScope->GetGlobalJSObject()); michael@0: JS::RootedObject proto(cx, JS_GetObjectPrototype(cx, parent)); michael@0: mJSProtoObject = JS_NewObjectWithUniqueType(cx, js::Jsvalify(jsclazz), michael@0: proto, parent); michael@0: michael@0: bool success = !!mJSProtoObject; michael@0: if (success) { michael@0: JS_SetPrivate(mJSProtoObject, this); michael@0: if (callPostCreatePrototype) michael@0: success = CallPostCreatePrototype(); michael@0: } michael@0: michael@0: return success; michael@0: } michael@0: michael@0: bool michael@0: XPCWrappedNativeProto::CallPostCreatePrototype() michael@0: { michael@0: AutoJSContext cx; michael@0: michael@0: // Nothing to do if we don't have a scriptable callback. michael@0: nsIXPCScriptable *callback = mScriptableInfo ? mScriptableInfo->GetCallback() michael@0: : nullptr; michael@0: if (!callback) michael@0: return true; michael@0: michael@0: // Call the helper. This can handle being called if it's not implemented, michael@0: // so we don't have to check any sort of "want" here. See xpc_map_end.h. michael@0: nsresult rv = callback->PostCreatePrototype(cx, mJSProtoObject); michael@0: if (NS_FAILED(rv)) { michael@0: JS_SetPrivate(mJSProtoObject, nullptr); michael@0: mJSProtoObject = nullptr; michael@0: XPCThrower::Throw(rv, cx); michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: XPCWrappedNativeProto::JSProtoObjectFinalized(js::FreeOp *fop, JSObject *obj) michael@0: { michael@0: MOZ_ASSERT(obj == mJSProtoObject, "huh?"); michael@0: michael@0: // Only remove this proto from the map if it is the one in the map. michael@0: ClassInfo2WrappedNativeProtoMap* map = GetScope()->GetWrappedNativeProtoMap(); michael@0: if (map->Find(mClassInfo) == this) michael@0: map->Remove(mClassInfo); michael@0: michael@0: GetRuntime()->GetDetachedWrappedNativeProtoMap()->Remove(this); michael@0: GetRuntime()->GetDyingWrappedNativeProtoMap()->Add(this); michael@0: michael@0: mJSProtoObject.finalize(js::CastToJSFreeOp(fop)->runtime()); michael@0: } michael@0: michael@0: void michael@0: XPCWrappedNativeProto::SystemIsBeingShutDown() michael@0: { michael@0: // Note that the instance might receive this call multiple times michael@0: // as we walk to here from various places. michael@0: michael@0: if (mJSProtoObject) { michael@0: // short circuit future finalization michael@0: JS_SetPrivate(mJSProtoObject, nullptr); michael@0: mJSProtoObject = nullptr; michael@0: } michael@0: } michael@0: michael@0: // static michael@0: XPCWrappedNativeProto* michael@0: XPCWrappedNativeProto::GetNewOrUsed(XPCWrappedNativeScope* scope, michael@0: nsIClassInfo* classInfo, michael@0: const XPCNativeScriptableCreateInfo* scriptableCreateInfo, michael@0: bool callPostCreatePrototype) michael@0: { michael@0: AutoJSContext cx; michael@0: MOZ_ASSERT(scope, "bad param"); michael@0: MOZ_ASSERT(classInfo, "bad param"); michael@0: michael@0: AutoMarkingWrappedNativeProtoPtr proto(cx); michael@0: ClassInfo2WrappedNativeProtoMap* map = nullptr; michael@0: michael@0: uint32_t ciFlags; michael@0: if (NS_FAILED(classInfo->GetFlags(&ciFlags))) michael@0: ciFlags = 0; michael@0: michael@0: map = scope->GetWrappedNativeProtoMap(); michael@0: proto = map->Find(classInfo); michael@0: if (proto) michael@0: return proto; michael@0: michael@0: AutoMarkingNativeSetPtr set(cx); michael@0: set = XPCNativeSet::GetNewOrUsed(classInfo); michael@0: if (!set) michael@0: return nullptr; michael@0: michael@0: proto = new XPCWrappedNativeProto(scope, classInfo, ciFlags, set); michael@0: michael@0: if (!proto || !proto->Init(scriptableCreateInfo, callPostCreatePrototype)) { michael@0: delete proto.get(); michael@0: return nullptr; michael@0: } michael@0: michael@0: map->Add(classInfo, proto); michael@0: michael@0: return proto; michael@0: } michael@0: michael@0: void michael@0: XPCWrappedNativeProto::DebugDump(int16_t depth) michael@0: { michael@0: #ifdef DEBUG michael@0: depth-- ; michael@0: XPC_LOG_ALWAYS(("XPCWrappedNativeProto @ %x", this)); michael@0: XPC_LOG_INDENT(); michael@0: XPC_LOG_ALWAYS(("gDEBUG_LiveProtoCount is %d", gDEBUG_LiveProtoCount)); michael@0: XPC_LOG_ALWAYS(("mScope @ %x", mScope)); michael@0: XPC_LOG_ALWAYS(("mJSProtoObject @ %x", mJSProtoObject.get())); michael@0: XPC_LOG_ALWAYS(("mSet @ %x", mSet)); michael@0: XPC_LOG_ALWAYS(("mScriptableInfo @ %x", mScriptableInfo)); michael@0: if (depth && mScriptableInfo) { michael@0: XPC_LOG_INDENT(); michael@0: XPC_LOG_ALWAYS(("mScriptable @ %x", mScriptableInfo->GetCallback())); michael@0: XPC_LOG_ALWAYS(("mFlags of %x", (uint32_t)mScriptableInfo->GetFlags())); michael@0: XPC_LOG_ALWAYS(("mJSClass @ %x", mScriptableInfo->GetJSClass())); michael@0: XPC_LOG_OUTDENT(); michael@0: } michael@0: XPC_LOG_OUTDENT(); michael@0: #endif michael@0: } michael@0: michael@0: