Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
michael@0 | 2 | /* vim: set ts=8 sts=4 et sw=4 tw=99: */ |
michael@0 | 3 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 6 | |
michael@0 | 7 | /* Shared proto object for XPCWrappedNative. */ |
michael@0 | 8 | |
michael@0 | 9 | #include "xpcprivate.h" |
michael@0 | 10 | #include "nsCxPusher.h" |
michael@0 | 11 | #include "pratom.h" |
michael@0 | 12 | |
michael@0 | 13 | using namespace mozilla; |
michael@0 | 14 | |
michael@0 | 15 | #ifdef DEBUG |
michael@0 | 16 | int32_t XPCWrappedNativeProto::gDEBUG_LiveProtoCount = 0; |
michael@0 | 17 | #endif |
michael@0 | 18 | |
michael@0 | 19 | XPCWrappedNativeProto::XPCWrappedNativeProto(XPCWrappedNativeScope* Scope, |
michael@0 | 20 | nsIClassInfo* ClassInfo, |
michael@0 | 21 | uint32_t ClassInfoFlags, |
michael@0 | 22 | XPCNativeSet* Set) |
michael@0 | 23 | : mScope(Scope), |
michael@0 | 24 | mJSProtoObject(nullptr), |
michael@0 | 25 | mClassInfo(ClassInfo), |
michael@0 | 26 | mClassInfoFlags(ClassInfoFlags), |
michael@0 | 27 | mSet(Set), |
michael@0 | 28 | mScriptableInfo(nullptr) |
michael@0 | 29 | { |
michael@0 | 30 | // This native object lives as long as its associated JSObject - killed |
michael@0 | 31 | // by finalization of the JSObject (or explicitly if Init fails). |
michael@0 | 32 | |
michael@0 | 33 | MOZ_COUNT_CTOR(XPCWrappedNativeProto); |
michael@0 | 34 | MOZ_ASSERT(mScope); |
michael@0 | 35 | |
michael@0 | 36 | #ifdef DEBUG |
michael@0 | 37 | PR_ATOMIC_INCREMENT(&gDEBUG_LiveProtoCount); |
michael@0 | 38 | #endif |
michael@0 | 39 | } |
michael@0 | 40 | |
michael@0 | 41 | XPCWrappedNativeProto::~XPCWrappedNativeProto() |
michael@0 | 42 | { |
michael@0 | 43 | MOZ_ASSERT(!mJSProtoObject, "JSProtoObject still alive"); |
michael@0 | 44 | |
michael@0 | 45 | MOZ_COUNT_DTOR(XPCWrappedNativeProto); |
michael@0 | 46 | |
michael@0 | 47 | #ifdef DEBUG |
michael@0 | 48 | PR_ATOMIC_DECREMENT(&gDEBUG_LiveProtoCount); |
michael@0 | 49 | #endif |
michael@0 | 50 | |
michael@0 | 51 | // Note that our weak ref to mScope is not to be trusted at this point. |
michael@0 | 52 | |
michael@0 | 53 | XPCNativeSet::ClearCacheEntryForClassInfo(mClassInfo); |
michael@0 | 54 | |
michael@0 | 55 | delete mScriptableInfo; |
michael@0 | 56 | } |
michael@0 | 57 | |
michael@0 | 58 | bool |
michael@0 | 59 | XPCWrappedNativeProto::Init(const XPCNativeScriptableCreateInfo* scriptableCreateInfo, |
michael@0 | 60 | bool callPostCreatePrototype) |
michael@0 | 61 | { |
michael@0 | 62 | AutoJSContext cx; |
michael@0 | 63 | nsIXPCScriptable *callback = scriptableCreateInfo ? |
michael@0 | 64 | scriptableCreateInfo->GetCallback() : |
michael@0 | 65 | nullptr; |
michael@0 | 66 | if (callback) { |
michael@0 | 67 | mScriptableInfo = |
michael@0 | 68 | XPCNativeScriptableInfo::Construct(scriptableCreateInfo); |
michael@0 | 69 | if (!mScriptableInfo) |
michael@0 | 70 | return false; |
michael@0 | 71 | } |
michael@0 | 72 | |
michael@0 | 73 | const js::Class* jsclazz; |
michael@0 | 74 | |
michael@0 | 75 | if (mScriptableInfo) { |
michael@0 | 76 | const XPCNativeScriptableFlags& flags(mScriptableInfo->GetFlags()); |
michael@0 | 77 | |
michael@0 | 78 | if (flags.AllowPropModsToPrototype()) { |
michael@0 | 79 | jsclazz = flags.WantCall() ? |
michael@0 | 80 | &XPC_WN_ModsAllowed_WithCall_Proto_JSClass : |
michael@0 | 81 | &XPC_WN_ModsAllowed_NoCall_Proto_JSClass; |
michael@0 | 82 | } else { |
michael@0 | 83 | jsclazz = flags.WantCall() ? |
michael@0 | 84 | &XPC_WN_NoMods_WithCall_Proto_JSClass : |
michael@0 | 85 | &XPC_WN_NoMods_NoCall_Proto_JSClass; |
michael@0 | 86 | } |
michael@0 | 87 | } else { |
michael@0 | 88 | jsclazz = &XPC_WN_NoMods_NoCall_Proto_JSClass; |
michael@0 | 89 | } |
michael@0 | 90 | |
michael@0 | 91 | JS::RootedObject parent(cx, mScope->GetGlobalJSObject()); |
michael@0 | 92 | JS::RootedObject proto(cx, JS_GetObjectPrototype(cx, parent)); |
michael@0 | 93 | mJSProtoObject = JS_NewObjectWithUniqueType(cx, js::Jsvalify(jsclazz), |
michael@0 | 94 | proto, parent); |
michael@0 | 95 | |
michael@0 | 96 | bool success = !!mJSProtoObject; |
michael@0 | 97 | if (success) { |
michael@0 | 98 | JS_SetPrivate(mJSProtoObject, this); |
michael@0 | 99 | if (callPostCreatePrototype) |
michael@0 | 100 | success = CallPostCreatePrototype(); |
michael@0 | 101 | } |
michael@0 | 102 | |
michael@0 | 103 | return success; |
michael@0 | 104 | } |
michael@0 | 105 | |
michael@0 | 106 | bool |
michael@0 | 107 | XPCWrappedNativeProto::CallPostCreatePrototype() |
michael@0 | 108 | { |
michael@0 | 109 | AutoJSContext cx; |
michael@0 | 110 | |
michael@0 | 111 | // Nothing to do if we don't have a scriptable callback. |
michael@0 | 112 | nsIXPCScriptable *callback = mScriptableInfo ? mScriptableInfo->GetCallback() |
michael@0 | 113 | : nullptr; |
michael@0 | 114 | if (!callback) |
michael@0 | 115 | return true; |
michael@0 | 116 | |
michael@0 | 117 | // Call the helper. This can handle being called if it's not implemented, |
michael@0 | 118 | // so we don't have to check any sort of "want" here. See xpc_map_end.h. |
michael@0 | 119 | nsresult rv = callback->PostCreatePrototype(cx, mJSProtoObject); |
michael@0 | 120 | if (NS_FAILED(rv)) { |
michael@0 | 121 | JS_SetPrivate(mJSProtoObject, nullptr); |
michael@0 | 122 | mJSProtoObject = nullptr; |
michael@0 | 123 | XPCThrower::Throw(rv, cx); |
michael@0 | 124 | return false; |
michael@0 | 125 | } |
michael@0 | 126 | |
michael@0 | 127 | return true; |
michael@0 | 128 | } |
michael@0 | 129 | |
michael@0 | 130 | void |
michael@0 | 131 | XPCWrappedNativeProto::JSProtoObjectFinalized(js::FreeOp *fop, JSObject *obj) |
michael@0 | 132 | { |
michael@0 | 133 | MOZ_ASSERT(obj == mJSProtoObject, "huh?"); |
michael@0 | 134 | |
michael@0 | 135 | // Only remove this proto from the map if it is the one in the map. |
michael@0 | 136 | ClassInfo2WrappedNativeProtoMap* map = GetScope()->GetWrappedNativeProtoMap(); |
michael@0 | 137 | if (map->Find(mClassInfo) == this) |
michael@0 | 138 | map->Remove(mClassInfo); |
michael@0 | 139 | |
michael@0 | 140 | GetRuntime()->GetDetachedWrappedNativeProtoMap()->Remove(this); |
michael@0 | 141 | GetRuntime()->GetDyingWrappedNativeProtoMap()->Add(this); |
michael@0 | 142 | |
michael@0 | 143 | mJSProtoObject.finalize(js::CastToJSFreeOp(fop)->runtime()); |
michael@0 | 144 | } |
michael@0 | 145 | |
michael@0 | 146 | void |
michael@0 | 147 | XPCWrappedNativeProto::SystemIsBeingShutDown() |
michael@0 | 148 | { |
michael@0 | 149 | // Note that the instance might receive this call multiple times |
michael@0 | 150 | // as we walk to here from various places. |
michael@0 | 151 | |
michael@0 | 152 | if (mJSProtoObject) { |
michael@0 | 153 | // short circuit future finalization |
michael@0 | 154 | JS_SetPrivate(mJSProtoObject, nullptr); |
michael@0 | 155 | mJSProtoObject = nullptr; |
michael@0 | 156 | } |
michael@0 | 157 | } |
michael@0 | 158 | |
michael@0 | 159 | // static |
michael@0 | 160 | XPCWrappedNativeProto* |
michael@0 | 161 | XPCWrappedNativeProto::GetNewOrUsed(XPCWrappedNativeScope* scope, |
michael@0 | 162 | nsIClassInfo* classInfo, |
michael@0 | 163 | const XPCNativeScriptableCreateInfo* scriptableCreateInfo, |
michael@0 | 164 | bool callPostCreatePrototype) |
michael@0 | 165 | { |
michael@0 | 166 | AutoJSContext cx; |
michael@0 | 167 | MOZ_ASSERT(scope, "bad param"); |
michael@0 | 168 | MOZ_ASSERT(classInfo, "bad param"); |
michael@0 | 169 | |
michael@0 | 170 | AutoMarkingWrappedNativeProtoPtr proto(cx); |
michael@0 | 171 | ClassInfo2WrappedNativeProtoMap* map = nullptr; |
michael@0 | 172 | |
michael@0 | 173 | uint32_t ciFlags; |
michael@0 | 174 | if (NS_FAILED(classInfo->GetFlags(&ciFlags))) |
michael@0 | 175 | ciFlags = 0; |
michael@0 | 176 | |
michael@0 | 177 | map = scope->GetWrappedNativeProtoMap(); |
michael@0 | 178 | proto = map->Find(classInfo); |
michael@0 | 179 | if (proto) |
michael@0 | 180 | return proto; |
michael@0 | 181 | |
michael@0 | 182 | AutoMarkingNativeSetPtr set(cx); |
michael@0 | 183 | set = XPCNativeSet::GetNewOrUsed(classInfo); |
michael@0 | 184 | if (!set) |
michael@0 | 185 | return nullptr; |
michael@0 | 186 | |
michael@0 | 187 | proto = new XPCWrappedNativeProto(scope, classInfo, ciFlags, set); |
michael@0 | 188 | |
michael@0 | 189 | if (!proto || !proto->Init(scriptableCreateInfo, callPostCreatePrototype)) { |
michael@0 | 190 | delete proto.get(); |
michael@0 | 191 | return nullptr; |
michael@0 | 192 | } |
michael@0 | 193 | |
michael@0 | 194 | map->Add(classInfo, proto); |
michael@0 | 195 | |
michael@0 | 196 | return proto; |
michael@0 | 197 | } |
michael@0 | 198 | |
michael@0 | 199 | void |
michael@0 | 200 | XPCWrappedNativeProto::DebugDump(int16_t depth) |
michael@0 | 201 | { |
michael@0 | 202 | #ifdef DEBUG |
michael@0 | 203 | depth-- ; |
michael@0 | 204 | XPC_LOG_ALWAYS(("XPCWrappedNativeProto @ %x", this)); |
michael@0 | 205 | XPC_LOG_INDENT(); |
michael@0 | 206 | XPC_LOG_ALWAYS(("gDEBUG_LiveProtoCount is %d", gDEBUG_LiveProtoCount)); |
michael@0 | 207 | XPC_LOG_ALWAYS(("mScope @ %x", mScope)); |
michael@0 | 208 | XPC_LOG_ALWAYS(("mJSProtoObject @ %x", mJSProtoObject.get())); |
michael@0 | 209 | XPC_LOG_ALWAYS(("mSet @ %x", mSet)); |
michael@0 | 210 | XPC_LOG_ALWAYS(("mScriptableInfo @ %x", mScriptableInfo)); |
michael@0 | 211 | if (depth && mScriptableInfo) { |
michael@0 | 212 | XPC_LOG_INDENT(); |
michael@0 | 213 | XPC_LOG_ALWAYS(("mScriptable @ %x", mScriptableInfo->GetCallback())); |
michael@0 | 214 | XPC_LOG_ALWAYS(("mFlags of %x", (uint32_t)mScriptableInfo->GetFlags())); |
michael@0 | 215 | XPC_LOG_ALWAYS(("mJSClass @ %x", mScriptableInfo->GetJSClass())); |
michael@0 | 216 | XPC_LOG_OUTDENT(); |
michael@0 | 217 | } |
michael@0 | 218 | XPC_LOG_OUTDENT(); |
michael@0 | 219 | #endif |
michael@0 | 220 | } |
michael@0 | 221 | |
michael@0 | 222 |