|
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 /* Call context. */ |
|
8 |
|
9 #include "xpcprivate.h" |
|
10 #include "jswrapper.h" |
|
11 |
|
12 using namespace mozilla; |
|
13 using namespace xpc; |
|
14 using namespace JS; |
|
15 |
|
16 #define IS_TEAROFF_CLASS(clazz) ((clazz) == &XPC_WN_Tearoff_JSClass) |
|
17 |
|
18 XPCCallContext::XPCCallContext(XPCContext::LangType callerLanguage, |
|
19 JSContext* cx, |
|
20 HandleObject obj /* = nullptr */, |
|
21 HandleObject funobj /* = nullptr */, |
|
22 HandleId name /* = JSID_VOID */, |
|
23 unsigned argc /* = NO_ARGS */, |
|
24 jsval *argv /* = nullptr */, |
|
25 jsval *rval /* = nullptr */) |
|
26 : mAr(cx), |
|
27 mState(INIT_FAILED), |
|
28 mXPC(nsXPConnect::XPConnect()), |
|
29 mXPCContext(nullptr), |
|
30 mJSContext(cx), |
|
31 mCallerLanguage(callerLanguage), |
|
32 mFlattenedJSObject(cx), |
|
33 mWrapper(nullptr), |
|
34 mTearOff(nullptr), |
|
35 mName(cx) |
|
36 { |
|
37 MOZ_ASSERT(cx); |
|
38 MOZ_ASSERT(cx == XPCJSRuntime::Get()->GetJSContextStack()->Peek()); |
|
39 |
|
40 if (!mXPC) |
|
41 return; |
|
42 |
|
43 mXPCContext = XPCContext::GetXPCContext(mJSContext); |
|
44 mPrevCallerLanguage = mXPCContext->SetCallingLangType(mCallerLanguage); |
|
45 |
|
46 // hook into call context chain. |
|
47 mPrevCallContext = XPCJSRuntime::Get()->SetCallContext(this); |
|
48 |
|
49 mState = HAVE_CONTEXT; |
|
50 |
|
51 if (!obj) |
|
52 return; |
|
53 |
|
54 mMethodIndex = 0xDEAD; |
|
55 |
|
56 mState = HAVE_OBJECT; |
|
57 |
|
58 mTearOff = nullptr; |
|
59 |
|
60 // If the object is a security wrapper, GetWrappedNativeOfJSObject can't |
|
61 // handle it. Do special handling here to make cross-origin Xrays work. |
|
62 JSObject *unwrapped = js::CheckedUnwrap(obj, /* stopAtOuter = */ false); |
|
63 if (!unwrapped) { |
|
64 mWrapper = UnwrapThisIfAllowed(obj, funobj, argc); |
|
65 if (!mWrapper) { |
|
66 JS_ReportError(mJSContext, "Permission denied to call method on |this|"); |
|
67 mState = INIT_FAILED; |
|
68 return; |
|
69 } |
|
70 } else { |
|
71 const js::Class *clasp = js::GetObjectClass(unwrapped); |
|
72 if (IS_WN_CLASS(clasp)) { |
|
73 mWrapper = XPCWrappedNative::Get(unwrapped); |
|
74 } else if (IS_TEAROFF_CLASS(clasp)) { |
|
75 mTearOff = (XPCWrappedNativeTearOff*)js::GetObjectPrivate(unwrapped); |
|
76 mWrapper = XPCWrappedNative::Get(js::GetObjectParent(unwrapped)); |
|
77 } |
|
78 } |
|
79 if (mWrapper) { |
|
80 mFlattenedJSObject = mWrapper->GetFlatJSObject(); |
|
81 |
|
82 if (mTearOff) |
|
83 mScriptableInfo = nullptr; |
|
84 else |
|
85 mScriptableInfo = mWrapper->GetScriptableInfo(); |
|
86 } else { |
|
87 MOZ_ASSERT(!mFlattenedJSObject, "What object do we have?"); |
|
88 } |
|
89 |
|
90 if (!JSID_IS_VOID(name)) |
|
91 SetName(name); |
|
92 |
|
93 if (argc != NO_ARGS) |
|
94 SetArgsAndResultPtr(argc, argv, rval); |
|
95 |
|
96 CHECK_STATE(HAVE_OBJECT); |
|
97 } |
|
98 |
|
99 // static |
|
100 JSContext * |
|
101 XPCCallContext::GetDefaultJSContext() |
|
102 { |
|
103 // This is slightly questionable. If called without an explicit |
|
104 // JSContext (generally a call to a wrappedJS) we will use the JSContext |
|
105 // on the top of the JSContext stack - if there is one - *before* |
|
106 // falling back on the safe JSContext. |
|
107 // This is good AND bad because it makes calls from JS -> native -> JS |
|
108 // have JS stack 'continuity' for purposes of stack traces etc. |
|
109 // Note: this *is* what the pre-XPCCallContext xpconnect did too. |
|
110 |
|
111 XPCJSContextStack* stack = XPCJSRuntime::Get()->GetJSContextStack(); |
|
112 JSContext *topJSContext = stack->Peek(); |
|
113 |
|
114 return topJSContext ? topJSContext : stack->GetSafeJSContext(); |
|
115 } |
|
116 |
|
117 void |
|
118 XPCCallContext::SetName(jsid name) |
|
119 { |
|
120 CHECK_STATE(HAVE_OBJECT); |
|
121 |
|
122 mName = name; |
|
123 |
|
124 if (mTearOff) { |
|
125 mSet = nullptr; |
|
126 mInterface = mTearOff->GetInterface(); |
|
127 mMember = mInterface->FindMember(mName); |
|
128 mStaticMemberIsLocal = true; |
|
129 if (mMember && !mMember->IsConstant()) |
|
130 mMethodIndex = mMember->GetIndex(); |
|
131 } else { |
|
132 mSet = mWrapper ? mWrapper->GetSet() : nullptr; |
|
133 |
|
134 if (mSet && |
|
135 mSet->FindMember(mName, &mMember, &mInterface, |
|
136 mWrapper->HasProto() ? |
|
137 mWrapper->GetProto()->GetSet() : |
|
138 nullptr, |
|
139 &mStaticMemberIsLocal)) { |
|
140 if (mMember && !mMember->IsConstant()) |
|
141 mMethodIndex = mMember->GetIndex(); |
|
142 } else { |
|
143 mMember = nullptr; |
|
144 mInterface = nullptr; |
|
145 mStaticMemberIsLocal = false; |
|
146 } |
|
147 } |
|
148 |
|
149 mState = HAVE_NAME; |
|
150 } |
|
151 |
|
152 void |
|
153 XPCCallContext::SetCallInfo(XPCNativeInterface* iface, XPCNativeMember* member, |
|
154 bool isSetter) |
|
155 { |
|
156 CHECK_STATE(HAVE_CONTEXT); |
|
157 |
|
158 // We are going straight to the method info and need not do a lookup |
|
159 // by id. |
|
160 |
|
161 // don't be tricked if method is called with wrong 'this' |
|
162 if (mTearOff && mTearOff->GetInterface() != iface) |
|
163 mTearOff = nullptr; |
|
164 |
|
165 mSet = nullptr; |
|
166 mInterface = iface; |
|
167 mMember = member; |
|
168 mMethodIndex = mMember->GetIndex() + (isSetter ? 1 : 0); |
|
169 mName = mMember->GetName(); |
|
170 |
|
171 if (mState < HAVE_NAME) |
|
172 mState = HAVE_NAME; |
|
173 } |
|
174 |
|
175 void |
|
176 XPCCallContext::SetArgsAndResultPtr(unsigned argc, |
|
177 jsval *argv, |
|
178 jsval *rval) |
|
179 { |
|
180 CHECK_STATE(HAVE_OBJECT); |
|
181 |
|
182 if (mState < HAVE_NAME) { |
|
183 mSet = nullptr; |
|
184 mInterface = nullptr; |
|
185 mMember = nullptr; |
|
186 mStaticMemberIsLocal = false; |
|
187 } |
|
188 |
|
189 mArgc = argc; |
|
190 mArgv = argv; |
|
191 mRetVal = rval; |
|
192 |
|
193 mState = HAVE_ARGS; |
|
194 } |
|
195 |
|
196 nsresult |
|
197 XPCCallContext::CanCallNow() |
|
198 { |
|
199 nsresult rv; |
|
200 |
|
201 if (!HasInterfaceAndMember()) |
|
202 return NS_ERROR_UNEXPECTED; |
|
203 if (mState < HAVE_ARGS) |
|
204 return NS_ERROR_UNEXPECTED; |
|
205 |
|
206 if (!mTearOff) { |
|
207 mTearOff = mWrapper->FindTearOff(mInterface, false, &rv); |
|
208 if (!mTearOff || mTearOff->GetInterface() != mInterface) { |
|
209 mTearOff = nullptr; |
|
210 return NS_FAILED(rv) ? rv : NS_ERROR_UNEXPECTED; |
|
211 } |
|
212 } |
|
213 |
|
214 // Refresh in case FindTearOff extended the set |
|
215 mSet = mWrapper->GetSet(); |
|
216 |
|
217 mState = READY_TO_CALL; |
|
218 return NS_OK; |
|
219 } |
|
220 |
|
221 void |
|
222 XPCCallContext::SystemIsBeingShutDown() |
|
223 { |
|
224 // XXX This is pretty questionable since the per thread cleanup stuff |
|
225 // can be making this call on one thread for call contexts on another |
|
226 // thread. |
|
227 NS_WARNING("Shutting Down XPConnect even through there is a live XPCCallContext"); |
|
228 mXPCContext = nullptr; |
|
229 mState = SYSTEM_SHUTDOWN; |
|
230 if (mPrevCallContext) |
|
231 mPrevCallContext->SystemIsBeingShutDown(); |
|
232 } |
|
233 |
|
234 XPCCallContext::~XPCCallContext() |
|
235 { |
|
236 if (mXPCContext) { |
|
237 mXPCContext->SetCallingLangType(mPrevCallerLanguage); |
|
238 |
|
239 DebugOnly<XPCCallContext*> old = XPCJSRuntime::Get()->SetCallContext(mPrevCallContext); |
|
240 MOZ_ASSERT(old == this, "bad pop from per thread data"); |
|
241 } |
|
242 } |
|
243 |
|
244 /* readonly attribute nsISupports Callee; */ |
|
245 NS_IMETHODIMP |
|
246 XPCCallContext::GetCallee(nsISupports * *aCallee) |
|
247 { |
|
248 nsCOMPtr<nsISupports> rval = mWrapper ? mWrapper->GetIdentityObject() : nullptr; |
|
249 rval.forget(aCallee); |
|
250 return NS_OK; |
|
251 } |
|
252 |
|
253 /* readonly attribute uint16_t CalleeMethodIndex; */ |
|
254 NS_IMETHODIMP |
|
255 XPCCallContext::GetCalleeMethodIndex(uint16_t *aCalleeMethodIndex) |
|
256 { |
|
257 *aCalleeMethodIndex = mMethodIndex; |
|
258 return NS_OK; |
|
259 } |
|
260 |
|
261 /* readonly attribute nsIXPConnectWrappedNative CalleeWrapper; */ |
|
262 NS_IMETHODIMP |
|
263 XPCCallContext::GetCalleeWrapper(nsIXPConnectWrappedNative * *aCalleeWrapper) |
|
264 { |
|
265 nsCOMPtr<nsIXPConnectWrappedNative> rval = mWrapper; |
|
266 rval.forget(aCalleeWrapper); |
|
267 return NS_OK; |
|
268 } |
|
269 |
|
270 /* readonly attribute XPCNativeInterface CalleeInterface; */ |
|
271 NS_IMETHODIMP |
|
272 XPCCallContext::GetCalleeInterface(nsIInterfaceInfo * *aCalleeInterface) |
|
273 { |
|
274 nsCOMPtr<nsIInterfaceInfo> rval = mInterface->GetInterfaceInfo(); |
|
275 rval.forget(aCalleeInterface); |
|
276 return NS_OK; |
|
277 } |
|
278 |
|
279 /* readonly attribute nsIClassInfo CalleeClassInfo; */ |
|
280 NS_IMETHODIMP |
|
281 XPCCallContext::GetCalleeClassInfo(nsIClassInfo * *aCalleeClassInfo) |
|
282 { |
|
283 nsCOMPtr<nsIClassInfo> rval = mWrapper ? mWrapper->GetClassInfo() : nullptr; |
|
284 rval.forget(aCalleeClassInfo); |
|
285 return NS_OK; |
|
286 } |
|
287 |
|
288 /* readonly attribute JSContextPtr JSContext; */ |
|
289 NS_IMETHODIMP |
|
290 XPCCallContext::GetJSContext(JSContext * *aJSContext) |
|
291 { |
|
292 JS_AbortIfWrongThread(JS_GetRuntime(mJSContext)); |
|
293 *aJSContext = mJSContext; |
|
294 return NS_OK; |
|
295 } |
|
296 |
|
297 /* readonly attribute uint32_t Argc; */ |
|
298 NS_IMETHODIMP |
|
299 XPCCallContext::GetArgc(uint32_t *aArgc) |
|
300 { |
|
301 *aArgc = (uint32_t) mArgc; |
|
302 return NS_OK; |
|
303 } |
|
304 |
|
305 /* readonly attribute JSValPtr ArgvPtr; */ |
|
306 NS_IMETHODIMP |
|
307 XPCCallContext::GetArgvPtr(jsval * *aArgvPtr) |
|
308 { |
|
309 *aArgvPtr = mArgv; |
|
310 return NS_OK; |
|
311 } |
|
312 |
|
313 NS_IMETHODIMP |
|
314 XPCCallContext::GetPreviousCallContext(nsAXPCNativeCallContext **aResult) |
|
315 { |
|
316 NS_ENSURE_ARG_POINTER(aResult); |
|
317 *aResult = GetPrevCallContext(); |
|
318 return NS_OK; |
|
319 } |
|
320 |
|
321 NS_IMETHODIMP |
|
322 XPCCallContext::GetLanguage(uint16_t *aResult) |
|
323 { |
|
324 NS_ENSURE_ARG_POINTER(aResult); |
|
325 *aResult = GetCallerLanguage(); |
|
326 return NS_OK; |
|
327 } |
|
328 |
|
329 XPCWrappedNative* |
|
330 XPCCallContext::UnwrapThisIfAllowed(HandleObject obj, HandleObject fun, unsigned argc) |
|
331 { |
|
332 // We should only get here for objects that aren't safe to unwrap. |
|
333 MOZ_ASSERT(!js::CheckedUnwrap(obj)); |
|
334 MOZ_ASSERT(js::IsObjectInContextCompartment(obj, mJSContext)); |
|
335 |
|
336 // We can't do anything here without a function. |
|
337 if (!fun) |
|
338 return nullptr; |
|
339 |
|
340 // Determine if we're allowed to unwrap the security wrapper to invoke the |
|
341 // method. |
|
342 // |
|
343 // We have the Interface and Member that this corresponds to, but |
|
344 // unfortunately our access checks are based on the object class name and |
|
345 // property name. So we cheat a little bit here - we verify that the object |
|
346 // does indeed implement the method's Interface, and then just check that we |
|
347 // can successfully access property with method's name from the object. |
|
348 |
|
349 // First, get the XPCWN out of the underlying object. We should have a wrapper |
|
350 // here, potentially an outer window proxy, and then an XPCWN. |
|
351 MOZ_ASSERT(js::IsWrapper(obj)); |
|
352 RootedObject unwrapped(mJSContext, js::UncheckedUnwrap(obj, /* stopAtOuter = */ false)); |
|
353 #ifdef DEBUG |
|
354 JS::Rooted<JSObject*> wrappedObj(mJSContext, js::Wrapper::wrappedObject(obj)); |
|
355 MOZ_ASSERT(unwrapped == JS_ObjectToInnerObject(mJSContext, wrappedObj)); |
|
356 #endif |
|
357 |
|
358 // Make sure we have an XPCWN, and grab it. |
|
359 if (!IS_WN_REFLECTOR(unwrapped)) |
|
360 return nullptr; |
|
361 XPCWrappedNative *wn = XPCWrappedNative::Get(unwrapped); |
|
362 |
|
363 // Next, get the call info off the function object. |
|
364 XPCNativeInterface *interface; |
|
365 XPCNativeMember *member; |
|
366 XPCNativeMember::GetCallInfo(fun, &interface, &member); |
|
367 |
|
368 // To be extra safe, make sure that the underlying native implements the |
|
369 // interface before unwrapping. Even if we didn't check this, we'd still |
|
370 // theoretically fail during tearoff lookup for mismatched methods. |
|
371 if (!wn->HasInterfaceNoQI(*interface->GetIID())) |
|
372 return nullptr; |
|
373 |
|
374 // See if the access is permitted. |
|
375 // |
|
376 // NB: This calculation of SET vs GET is a bit wonky, but that's what |
|
377 // XPC_WN_GetterSetter does. |
|
378 bool set = argc && argc != NO_ARGS && member->IsWritableAttribute(); |
|
379 js::Wrapper::Action act = set ? js::Wrapper::SET : js::Wrapper::GET; |
|
380 js::Wrapper *handler = js::Wrapper::wrapperHandler(obj); |
|
381 bool ignored; |
|
382 JS::Rooted<jsid> id(mJSContext, member->GetName()); |
|
383 if (!handler->enter(mJSContext, obj, id, act, &ignored)) |
|
384 return nullptr; |
|
385 |
|
386 // Ok, this call is safe. |
|
387 return wn; |
|
388 } |
|
389 |