js/xpconnect/src/XPCCallContext.cpp

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:fe5f7b03052b
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

mercurial