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