| |
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 |