Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=4 sw=4 et tw=80:
3 : *
4 : * This Source Code Form is subject to the terms of the Mozilla Public
5 : * License, v. 2.0. If a copy of the MPL was not distributed with this
6 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 :
8 : #include "WrapperOwner.h"
9 : #include "JavaScriptLogging.h"
10 : #include "mozilla/Unused.h"
11 : #include "mozilla/dom/BindingUtils.h"
12 : #include "jsfriendapi.h"
13 : #include "js/CharacterEncoding.h"
14 : #include "xpcprivate.h"
15 : #include "CPOWTimer.h"
16 : #include "WrapperFactory.h"
17 :
18 : #include "nsIDocShellTreeItem.h"
19 : #include "nsIDOMDocument.h"
20 :
21 : using namespace js;
22 : using namespace JS;
23 : using namespace mozilla;
24 : using namespace mozilla::jsipc;
25 :
26 0 : struct AuxCPOWData
27 : {
28 : ObjectId id;
29 : bool isCallable;
30 : bool isConstructor;
31 : bool isDOMObject;
32 :
33 : // The object tag is just some auxilliary information that clients can use
34 : // however they see fit.
35 : nsCString objectTag;
36 :
37 : // The class name for WrapperOwner::className, below.
38 : nsCString className;
39 :
40 5 : AuxCPOWData(ObjectId id,
41 : bool isCallable,
42 : bool isConstructor,
43 : bool isDOMObject,
44 : const nsACString& objectTag)
45 5 : : id(id),
46 : isCallable(isCallable),
47 : isConstructor(isConstructor),
48 : isDOMObject(isDOMObject),
49 5 : objectTag(objectTag)
50 5 : {}
51 : };
52 :
53 3 : WrapperOwner::WrapperOwner()
54 3 : : inactive_(false)
55 : {
56 3 : }
57 :
58 : static inline AuxCPOWData*
59 0 : AuxCPOWDataOf(JSObject* obj)
60 : {
61 0 : MOZ_ASSERT(IsCPOW(obj));
62 0 : return static_cast<AuxCPOWData*>(GetProxyReservedSlot(obj, 1).toPrivate());
63 : }
64 :
65 : static inline WrapperOwner*
66 0 : OwnerOf(JSObject* obj)
67 : {
68 0 : MOZ_ASSERT(IsCPOW(obj));
69 0 : return reinterpret_cast<WrapperOwner*>(GetProxyReservedSlot(obj, 0).toPrivate());
70 : }
71 :
72 : ObjectId
73 0 : WrapperOwner::idOfUnchecked(JSObject* obj)
74 : {
75 0 : MOZ_ASSERT(IsCPOW(obj));
76 :
77 0 : AuxCPOWData* aux = AuxCPOWDataOf(obj);
78 0 : MOZ_ASSERT(!aux->id.isNull());
79 0 : return aux->id;
80 : }
81 :
82 : ObjectId
83 0 : WrapperOwner::idOf(JSObject* obj)
84 : {
85 0 : ObjectId objId = idOfUnchecked(obj);
86 0 : MOZ_ASSERT(hasCPOW(objId, obj));
87 0 : return objId;
88 : }
89 :
90 : class CPOWProxyHandler : public BaseProxyHandler
91 : {
92 : public:
93 : constexpr CPOWProxyHandler()
94 : : BaseProxyHandler(&family) {}
95 :
96 5 : virtual bool finalizeInBackground(const Value& priv) const override {
97 5 : return false;
98 : }
99 :
100 : virtual bool getOwnPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
101 : MutableHandle<PropertyDescriptor> desc) const override;
102 : virtual bool defineProperty(JSContext* cx, HandleObject proxy, HandleId id,
103 : Handle<PropertyDescriptor> desc,
104 : ObjectOpResult& result) const override;
105 : virtual bool ownPropertyKeys(JSContext* cx, HandleObject proxy,
106 : AutoIdVector& props) const override;
107 : virtual bool delete_(JSContext* cx, HandleObject proxy, HandleId id,
108 : ObjectOpResult& result) const override;
109 : virtual JSObject* enumerate(JSContext* cx, HandleObject proxy) const override;
110 : virtual bool preventExtensions(JSContext* cx, HandleObject proxy,
111 : ObjectOpResult& result) const override;
112 : virtual bool isExtensible(JSContext* cx, HandleObject proxy, bool* extensible) const override;
113 : virtual bool has(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const override;
114 : virtual bool get(JSContext* cx, HandleObject proxy, HandleValue receiver,
115 : HandleId id, MutableHandleValue vp) const override;
116 : virtual bool set(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, JS::HandleValue v,
117 : JS::HandleValue receiver, JS::ObjectOpResult& result) const override;
118 : virtual bool call(JSContext* cx, HandleObject proxy, const CallArgs& args) const override;
119 : virtual bool construct(JSContext* cx, HandleObject proxy, const CallArgs& args) const override;
120 :
121 : virtual bool getPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
122 : MutableHandle<PropertyDescriptor> desc) const override;
123 : virtual bool hasOwn(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const override;
124 : virtual bool getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject proxy,
125 : AutoIdVector& props) const override;
126 : virtual bool hasInstance(JSContext* cx, HandleObject proxy,
127 : MutableHandleValue v, bool* bp) const override;
128 : virtual bool getBuiltinClass(JSContext* cx, HandleObject obj, js::ESClass* cls) const override;
129 : virtual bool isArray(JSContext* cx, HandleObject obj,
130 : IsArrayAnswer* answer) const override;
131 : virtual const char* className(JSContext* cx, HandleObject proxy) const override;
132 : virtual RegExpShared* regexp_toShared(JSContext* cx, HandleObject proxy) const override;
133 : virtual void finalize(JSFreeOp* fop, JSObject* proxy) const override;
134 : virtual void objectMoved(JSObject* proxy, const JSObject* old) const override;
135 : virtual bool isCallable(JSObject* obj) const override;
136 : virtual bool isConstructor(JSObject* obj) const override;
137 : virtual bool getPrototype(JSContext* cx, HandleObject proxy, MutableHandleObject protop) const override;
138 : virtual bool getPrototypeIfOrdinary(JSContext* cx, HandleObject proxy, bool* isOrdinary,
139 : MutableHandleObject protop) const override;
140 :
141 : static const char family;
142 : static const CPOWProxyHandler singleton;
143 : };
144 :
145 : const char CPOWProxyHandler::family = 0;
146 : const CPOWProxyHandler CPOWProxyHandler::singleton;
147 :
148 : #define FORWARD(call, args, failRetVal) \
149 : AUTO_PROFILER_LABEL(__func__, JS); \
150 : WrapperOwner* owner = OwnerOf(proxy); \
151 : if (!owner->active()) { \
152 : JS_ReportErrorASCII(cx, "cannot use a CPOW whose process is gone"); \
153 : return failRetVal; \
154 : } \
155 : if (!owner->allowMessage(cx)) { \
156 : return failRetVal; \
157 : } \
158 : { \
159 : CPOWTimer timer(cx); \
160 : return owner->call args; \
161 : }
162 :
163 : bool
164 0 : CPOWProxyHandler::getPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
165 : MutableHandle<PropertyDescriptor> desc) const
166 : {
167 0 : FORWARD(getPropertyDescriptor, (cx, proxy, id, desc), false);
168 : }
169 :
170 : bool
171 0 : WrapperOwner::getPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
172 : MutableHandle<PropertyDescriptor> desc)
173 : {
174 0 : ObjectId objId = idOf(proxy);
175 :
176 0 : JSIDVariant idVar;
177 0 : if (!toJSIDVariant(cx, id, &idVar))
178 0 : return false;
179 :
180 0 : ReturnStatus status;
181 0 : PPropertyDescriptor result;
182 0 : if (!SendGetPropertyDescriptor(objId, idVar, &status, &result))
183 0 : return ipcfail(cx);
184 :
185 0 : LOG_STACK();
186 :
187 0 : if (!ok(cx, status))
188 0 : return false;
189 :
190 0 : return toDescriptor(cx, result, desc);
191 : }
192 :
193 : bool
194 0 : CPOWProxyHandler::getOwnPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
195 : MutableHandle<PropertyDescriptor> desc) const
196 : {
197 0 : FORWARD(getOwnPropertyDescriptor, (cx, proxy, id, desc), false);
198 : }
199 :
200 : bool
201 0 : WrapperOwner::getOwnPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
202 : MutableHandle<PropertyDescriptor> desc)
203 : {
204 0 : ObjectId objId = idOf(proxy);
205 :
206 0 : JSIDVariant idVar;
207 0 : if (!toJSIDVariant(cx, id, &idVar))
208 0 : return false;
209 :
210 0 : ReturnStatus status;
211 0 : PPropertyDescriptor result;
212 0 : if (!SendGetOwnPropertyDescriptor(objId, idVar, &status, &result))
213 0 : return ipcfail(cx);
214 :
215 0 : LOG_STACK();
216 :
217 0 : if (!ok(cx, status))
218 0 : return false;
219 :
220 0 : return toDescriptor(cx, result, desc);
221 : }
222 :
223 : bool
224 0 : CPOWProxyHandler::defineProperty(JSContext* cx, HandleObject proxy, HandleId id,
225 : Handle<PropertyDescriptor> desc,
226 : ObjectOpResult& result) const
227 : {
228 0 : FORWARD(defineProperty, (cx, proxy, id, desc, result), false);
229 : }
230 :
231 : bool
232 0 : WrapperOwner::defineProperty(JSContext* cx, HandleObject proxy, HandleId id,
233 : Handle<PropertyDescriptor> desc,
234 : ObjectOpResult& result)
235 : {
236 0 : ObjectId objId = idOf(proxy);
237 :
238 0 : JSIDVariant idVar;
239 0 : if (!toJSIDVariant(cx, id, &idVar))
240 0 : return false;
241 :
242 0 : PPropertyDescriptor descriptor;
243 0 : if (!fromDescriptor(cx, desc, &descriptor))
244 0 : return false;
245 :
246 0 : ReturnStatus status;
247 0 : if (!SendDefineProperty(objId, idVar, descriptor, &status))
248 0 : return ipcfail(cx);
249 :
250 0 : LOG_STACK();
251 :
252 0 : return ok(cx, status, result);
253 : }
254 :
255 : bool
256 0 : CPOWProxyHandler::ownPropertyKeys(JSContext* cx, HandleObject proxy,
257 : AutoIdVector& props) const
258 : {
259 0 : FORWARD(ownPropertyKeys, (cx, proxy, props), false);
260 : }
261 :
262 : bool
263 0 : WrapperOwner::ownPropertyKeys(JSContext* cx, HandleObject proxy, AutoIdVector& props)
264 : {
265 0 : return getPropertyKeys(cx, proxy, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, props);
266 : }
267 :
268 : bool
269 0 : CPOWProxyHandler::delete_(JSContext* cx, HandleObject proxy, HandleId id,
270 : ObjectOpResult& result) const
271 : {
272 0 : FORWARD(delete_, (cx, proxy, id, result), false);
273 : }
274 :
275 : bool
276 0 : WrapperOwner::delete_(JSContext* cx, HandleObject proxy, HandleId id, ObjectOpResult& result)
277 : {
278 0 : ObjectId objId = idOf(proxy);
279 :
280 0 : JSIDVariant idVar;
281 0 : if (!toJSIDVariant(cx, id, &idVar))
282 0 : return false;
283 :
284 0 : ReturnStatus status;
285 0 : if (!SendDelete(objId, idVar, &status))
286 0 : return ipcfail(cx);
287 :
288 0 : LOG_STACK();
289 :
290 0 : return ok(cx, status, result);
291 : }
292 :
293 : JSObject*
294 0 : CPOWProxyHandler::enumerate(JSContext* cx, HandleObject proxy) const
295 : {
296 : // Using a CPOW for the Iterator would slow down for .. in performance, instead
297 : // call the base hook, that will use our implementation of getOwnEnumerablePropertyKeys
298 : // and follow the proto chain.
299 0 : return BaseProxyHandler::enumerate(cx, proxy);
300 : }
301 :
302 : bool
303 0 : CPOWProxyHandler::has(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const
304 : {
305 0 : FORWARD(has, (cx, proxy, id, bp), false);
306 : }
307 :
308 : bool
309 0 : WrapperOwner::has(JSContext* cx, HandleObject proxy, HandleId id, bool* bp)
310 : {
311 0 : ObjectId objId = idOf(proxy);
312 :
313 0 : JSIDVariant idVar;
314 0 : if (!toJSIDVariant(cx, id, &idVar))
315 0 : return false;
316 :
317 0 : ReturnStatus status;
318 0 : if (!SendHas(objId, idVar, &status, bp))
319 0 : return ipcfail(cx);
320 :
321 0 : LOG_STACK();
322 :
323 0 : return ok(cx, status);
324 : }
325 :
326 : bool
327 0 : CPOWProxyHandler::hasOwn(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const
328 : {
329 0 : FORWARD(hasOwn, (cx, proxy, id, bp), false);
330 : }
331 :
332 : bool
333 0 : WrapperOwner::hasOwn(JSContext* cx, HandleObject proxy, HandleId id, bool* bp)
334 : {
335 0 : ObjectId objId = idOf(proxy);
336 :
337 0 : JSIDVariant idVar;
338 0 : if (!toJSIDVariant(cx, id, &idVar))
339 0 : return false;
340 :
341 0 : ReturnStatus status;
342 0 : if (!SendHasOwn(objId, idVar, &status, bp))
343 0 : return ipcfail(cx);
344 :
345 0 : LOG_STACK();
346 :
347 0 : return !!ok(cx, status);
348 : }
349 :
350 : bool
351 0 : CPOWProxyHandler::get(JSContext* cx, HandleObject proxy, HandleValue receiver,
352 : HandleId id, MutableHandleValue vp) const
353 : {
354 0 : FORWARD(get, (cx, proxy, receiver, id, vp), false);
355 : }
356 :
357 : static bool
358 0 : CPOWDOMQI(JSContext* cx, unsigned argc, Value* vp)
359 : {
360 0 : CallArgs args = CallArgsFromVp(argc, vp);
361 0 : if (!args.thisv().isObject() || !IsCPOW(&args.thisv().toObject())) {
362 0 : JS_ReportErrorASCII(cx, "bad this object passed to special QI");
363 0 : return false;
364 : }
365 :
366 0 : RootedObject proxy(cx, &args.thisv().toObject());
367 0 : FORWARD(DOMQI, (cx, proxy, args), false);
368 : }
369 :
370 : static bool
371 0 : CPOWToString(JSContext* cx, unsigned argc, Value* vp)
372 : {
373 0 : CallArgs args = CallArgsFromVp(argc, vp);
374 0 : RootedObject callee(cx, &args.callee());
375 0 : RootedValue cpowValue(cx);
376 0 : if (!JS_GetProperty(cx, callee, "__cpow__", &cpowValue))
377 0 : return false;
378 :
379 0 : if (!cpowValue.isObject() || !IsCPOW(&cpowValue.toObject())) {
380 0 : JS_ReportErrorASCII(cx, "CPOWToString called on an incompatible object");
381 0 : return false;
382 : }
383 :
384 0 : RootedObject proxy(cx, &cpowValue.toObject());
385 0 : FORWARD(toString, (cx, proxy, args), false);
386 : }
387 :
388 : bool
389 0 : WrapperOwner::toString(JSContext* cx, HandleObject cpow, JS::CallArgs& args)
390 : {
391 : // Ask the other side to call its toString method. Update the callee so that
392 : // it points to the CPOW and not to the synthesized CPOWToString function.
393 0 : args.setCallee(ObjectValue(*cpow));
394 0 : if (!callOrConstruct(cx, cpow, args, false))
395 0 : return false;
396 :
397 0 : if (!args.rval().isString())
398 0 : return true;
399 :
400 0 : RootedString cpowResult(cx, args.rval().toString());
401 0 : nsAutoJSString toStringResult;
402 0 : if (!toStringResult.init(cx, cpowResult))
403 0 : return false;
404 :
405 : // We don't want to wrap toString() results for things like the location
406 : // object, where toString() is supposed to return a URL and nothing else.
407 0 : nsAutoString result;
408 0 : if (toStringResult[0] == '[') {
409 0 : result.AppendLiteral("[object CPOW ");
410 0 : result += toStringResult;
411 0 : result.AppendLiteral("]");
412 : } else {
413 0 : result += toStringResult;
414 : }
415 :
416 0 : JSString* str = JS_NewUCStringCopyN(cx, result.get(), result.Length());
417 0 : if (!str)
418 0 : return false;
419 :
420 0 : args.rval().setString(str);
421 0 : return true;
422 : }
423 :
424 : bool
425 0 : WrapperOwner::DOMQI(JSContext* cx, JS::HandleObject proxy, JS::CallArgs& args)
426 : {
427 : // Someone's calling us, handle nsISupports specially to avoid unnecessary
428 : // CPOW traffic.
429 0 : HandleValue id = args[0];
430 0 : if (id.isObject()) {
431 0 : RootedObject idobj(cx, &id.toObject());
432 0 : nsCOMPtr<nsIJSID> jsid;
433 :
434 0 : nsresult rv = UnwrapArg<nsIJSID>(cx, idobj, getter_AddRefs(jsid));
435 0 : if (NS_SUCCEEDED(rv)) {
436 0 : MOZ_ASSERT(jsid, "bad wrapJS");
437 0 : const nsID* idptr = jsid->GetID();
438 0 : if (idptr->Equals(NS_GET_IID(nsISupports))) {
439 0 : args.rval().set(args.thisv());
440 0 : return true;
441 : }
442 :
443 : // Webidl-implemented DOM objects never have nsIClassInfo.
444 0 : if (idptr->Equals(NS_GET_IID(nsIClassInfo)))
445 0 : return Throw(cx, NS_ERROR_NO_INTERFACE);
446 : }
447 : }
448 :
449 : // It wasn't nsISupports, call into the other process to do the QI for us
450 : // (since we don't know what other interfaces our object supports). Note
451 : // that we have to use JS_GetPropertyDescriptor here to avoid infinite
452 : // recursion back into CPOWDOMQI via WrapperOwner::get().
453 : // We could stash the actual QI function on our own function object to avoid
454 : // if we're called multiple times, but since we're transient, there's no
455 : // point right now.
456 0 : JS::Rooted<PropertyDescriptor> propDesc(cx);
457 0 : if (!JS_GetPropertyDescriptor(cx, proxy, "QueryInterface", &propDesc))
458 0 : return false;
459 :
460 0 : if (!propDesc.value().isObject()) {
461 0 : MOZ_ASSERT_UNREACHABLE("We didn't get QueryInterface off a node");
462 : return Throw(cx, NS_ERROR_UNEXPECTED);
463 : }
464 0 : return JS_CallFunctionValue(cx, proxy, propDesc.value(), args, args.rval());
465 : }
466 :
467 : bool
468 0 : WrapperOwner::get(JSContext* cx, HandleObject proxy, HandleValue receiver,
469 : HandleId id, MutableHandleValue vp)
470 : {
471 0 : ObjectId objId = idOf(proxy);
472 :
473 0 : JSVariant receiverVar;
474 0 : if (!toVariant(cx, receiver, &receiverVar))
475 0 : return false;
476 :
477 0 : JSIDVariant idVar;
478 0 : if (!toJSIDVariant(cx, id, &idVar))
479 0 : return false;
480 :
481 0 : AuxCPOWData* data = AuxCPOWDataOf(proxy);
482 0 : if (data->isDOMObject &&
483 0 : idVar.type() == JSIDVariant::TnsString &&
484 0 : idVar.get_nsString().EqualsLiteral("QueryInterface"))
485 : {
486 : // Handle QueryInterface on DOM Objects specially since we can assume
487 : // certain things about their implementation.
488 0 : RootedFunction qi(cx, JS_NewFunction(cx, CPOWDOMQI, 1, 0,
489 0 : "QueryInterface"));
490 0 : if (!qi)
491 0 : return false;
492 :
493 0 : vp.set(ObjectValue(*JS_GetFunctionObject(qi)));
494 0 : return true;
495 : }
496 :
497 0 : JSVariant val;
498 0 : ReturnStatus status;
499 0 : if (!SendGet(objId, receiverVar, idVar, &status, &val))
500 0 : return ipcfail(cx);
501 :
502 0 : LOG_STACK();
503 :
504 0 : if (!ok(cx, status))
505 0 : return false;
506 :
507 0 : if (!fromVariant(cx, val, vp))
508 0 : return false;
509 :
510 0 : if (idVar.type() == JSIDVariant::TnsString &&
511 0 : idVar.get_nsString().EqualsLiteral("toString")) {
512 0 : RootedFunction toString(cx, JS_NewFunction(cx, CPOWToString, 0, 0,
513 0 : "toString"));
514 0 : if (!toString)
515 0 : return false;
516 :
517 0 : RootedObject toStringObj(cx, JS_GetFunctionObject(toString));
518 :
519 0 : if (!JS_DefineProperty(cx, toStringObj, "__cpow__", vp, JSPROP_PERMANENT | JSPROP_READONLY))
520 0 : return false;
521 :
522 0 : vp.set(ObjectValue(*toStringObj));
523 : }
524 :
525 0 : return true;
526 : }
527 :
528 : bool
529 0 : CPOWProxyHandler::set(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, JS::HandleValue v,
530 : JS::HandleValue receiver, JS::ObjectOpResult& result) const
531 : {
532 0 : FORWARD(set, (cx, proxy, id, v, receiver, result), false);
533 : }
534 :
535 : bool
536 0 : WrapperOwner::set(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, JS::HandleValue v,
537 : JS::HandleValue receiver, JS::ObjectOpResult& result)
538 : {
539 0 : ObjectId objId = idOf(proxy);
540 :
541 0 : JSIDVariant idVar;
542 0 : if (!toJSIDVariant(cx, id, &idVar))
543 0 : return false;
544 :
545 0 : JSVariant val;
546 0 : if (!toVariant(cx, v, &val))
547 0 : return false;
548 :
549 0 : JSVariant receiverVar;
550 0 : if (!toVariant(cx, receiver, &receiverVar))
551 0 : return false;
552 :
553 0 : ReturnStatus status;
554 0 : if (!SendSet(objId, idVar, val, receiverVar, &status))
555 0 : return ipcfail(cx);
556 :
557 0 : LOG_STACK();
558 :
559 0 : return ok(cx, status, result);
560 : }
561 :
562 : bool
563 0 : CPOWProxyHandler::getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject proxy,
564 : AutoIdVector& props) const
565 : {
566 0 : FORWARD(getOwnEnumerablePropertyKeys, (cx, proxy, props), false);
567 : }
568 :
569 : bool
570 0 : WrapperOwner::getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject proxy, AutoIdVector& props)
571 : {
572 0 : return getPropertyKeys(cx, proxy, JSITER_OWNONLY, props);
573 : }
574 :
575 : bool
576 0 : CPOWProxyHandler::preventExtensions(JSContext* cx, HandleObject proxy, ObjectOpResult& result) const
577 : {
578 0 : FORWARD(preventExtensions, (cx, proxy, result), false);
579 : }
580 :
581 : bool
582 0 : WrapperOwner::preventExtensions(JSContext* cx, HandleObject proxy, ObjectOpResult& result)
583 : {
584 0 : ObjectId objId = idOf(proxy);
585 :
586 0 : ReturnStatus status;
587 0 : if (!SendPreventExtensions(objId, &status))
588 0 : return ipcfail(cx);
589 :
590 0 : LOG_STACK();
591 :
592 0 : return ok(cx, status, result);
593 : }
594 :
595 : bool
596 0 : CPOWProxyHandler::isExtensible(JSContext* cx, HandleObject proxy, bool* extensible) const
597 : {
598 0 : FORWARD(isExtensible, (cx, proxy, extensible), false);
599 : }
600 :
601 : bool
602 0 : WrapperOwner::isExtensible(JSContext* cx, HandleObject proxy, bool* extensible)
603 : {
604 0 : ObjectId objId = idOf(proxy);
605 :
606 0 : ReturnStatus status;
607 0 : if (!SendIsExtensible(objId, &status, extensible))
608 0 : return ipcfail(cx);
609 :
610 0 : LOG_STACK();
611 :
612 0 : return ok(cx, status);
613 : }
614 :
615 : bool
616 0 : CPOWProxyHandler::call(JSContext* cx, HandleObject proxy, const CallArgs& args) const
617 : {
618 0 : FORWARD(callOrConstruct, (cx, proxy, args, false), false);
619 : }
620 :
621 : bool
622 0 : CPOWProxyHandler::construct(JSContext* cx, HandleObject proxy, const CallArgs& args) const
623 : {
624 0 : FORWARD(callOrConstruct, (cx, proxy, args, true), false);
625 : }
626 :
627 : bool
628 0 : WrapperOwner::callOrConstruct(JSContext* cx, HandleObject proxy, const CallArgs& args,
629 : bool construct)
630 : {
631 0 : ObjectId objId = idOf(proxy);
632 :
633 0 : InfallibleTArray<JSParam> vals;
634 0 : AutoValueVector outobjects(cx);
635 :
636 0 : RootedValue v(cx);
637 0 : for (size_t i = 0; i < args.length() + 2; i++) {
638 : // The |this| value for constructors is a magic value that we won't be
639 : // able to convert, so skip it.
640 0 : if (i == 1 && construct)
641 0 : v = UndefinedValue();
642 : else
643 0 : v = args.base()[i];
644 0 : if (v.isObject()) {
645 0 : RootedObject obj(cx, &v.toObject());
646 0 : if (xpc::IsOutObject(cx, obj)) {
647 : // Make sure it is not an in-out object.
648 : bool found;
649 0 : if (!JS_HasProperty(cx, obj, "value", &found))
650 0 : return false;
651 0 : if (found) {
652 0 : JS_ReportErrorASCII(cx, "in-out objects cannot be sent via CPOWs yet");
653 0 : return false;
654 : }
655 :
656 0 : vals.AppendElement(JSParam(void_t()));
657 0 : if (!outobjects.append(ObjectValue(*obj)))
658 0 : return false;
659 0 : continue;
660 : }
661 : }
662 0 : JSVariant val;
663 0 : if (!toVariant(cx, v, &val))
664 0 : return false;
665 0 : vals.AppendElement(JSParam(val));
666 : }
667 :
668 0 : JSVariant result;
669 0 : ReturnStatus status;
670 0 : InfallibleTArray<JSParam> outparams;
671 0 : if (!SendCallOrConstruct(objId, vals, construct, &status, &result, &outparams))
672 0 : return ipcfail(cx);
673 :
674 0 : LOG_STACK();
675 :
676 0 : if (!ok(cx, status))
677 0 : return false;
678 :
679 0 : if (outparams.Length() != outobjects.length())
680 0 : return ipcfail(cx);
681 :
682 0 : RootedObject obj(cx);
683 0 : for (size_t i = 0; i < outparams.Length(); i++) {
684 : // Don't bother doing anything for outparams that weren't set.
685 0 : if (outparams[i].type() == JSParam::Tvoid_t)
686 0 : continue;
687 :
688 : // Take the value the child process returned, and set it on the XPC
689 : // object.
690 0 : if (!fromVariant(cx, outparams[i], &v))
691 0 : return false;
692 :
693 0 : obj = &outobjects[i].toObject();
694 0 : if (!JS_SetProperty(cx, obj, "value", v))
695 0 : return false;
696 : }
697 :
698 0 : if (!fromVariant(cx, result, args.rval()))
699 0 : return false;
700 :
701 0 : return true;
702 : }
703 :
704 : bool
705 0 : CPOWProxyHandler::hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v, bool* bp) const
706 : {
707 0 : FORWARD(hasInstance, (cx, proxy, v, bp), false);
708 : }
709 :
710 : bool
711 0 : WrapperOwner::hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v, bool* bp)
712 : {
713 0 : ObjectId objId = idOf(proxy);
714 :
715 0 : JSVariant vVar;
716 0 : if (!toVariant(cx, v, &vVar))
717 0 : return false;
718 :
719 0 : ReturnStatus status;
720 0 : if (!SendHasInstance(objId, vVar, &status, bp))
721 0 : return ipcfail(cx);
722 :
723 0 : LOG_STACK();
724 :
725 0 : return ok(cx, status);
726 : }
727 :
728 : bool
729 0 : CPOWProxyHandler::getBuiltinClass(JSContext* cx, HandleObject proxy, ESClass* cls) const
730 : {
731 0 : FORWARD(getBuiltinClass, (cx, proxy, cls), false);
732 : }
733 :
734 : bool
735 0 : WrapperOwner::getBuiltinClass(JSContext* cx, HandleObject proxy, ESClass* cls)
736 : {
737 0 : ObjectId objId = idOf(proxy);
738 :
739 0 : uint32_t classValue = uint32_t(ESClass::Other);
740 0 : ReturnStatus status;
741 0 : if (!SendGetBuiltinClass(objId, &status, &classValue))
742 0 : return ipcfail(cx);
743 0 : *cls = ESClass(classValue);
744 :
745 0 : LOG_STACK();
746 :
747 0 : return ok(cx, status);
748 : }
749 :
750 : bool
751 0 : CPOWProxyHandler::isArray(JSContext* cx, HandleObject proxy,
752 : IsArrayAnswer* answer) const
753 : {
754 0 : FORWARD(isArray, (cx, proxy, answer), false);
755 : }
756 :
757 : bool
758 0 : WrapperOwner::isArray(JSContext* cx, HandleObject proxy, IsArrayAnswer* answer)
759 : {
760 0 : ObjectId objId = idOf(proxy);
761 :
762 : uint32_t ans;
763 0 : ReturnStatus status;
764 0 : if (!SendIsArray(objId, &status, &ans))
765 0 : return ipcfail(cx);
766 :
767 0 : LOG_STACK();
768 :
769 0 : *answer = IsArrayAnswer(ans);
770 0 : MOZ_ASSERT(*answer == IsArrayAnswer::Array ||
771 : *answer == IsArrayAnswer::NotArray ||
772 : *answer == IsArrayAnswer::RevokedProxy);
773 :
774 0 : return ok(cx, status);
775 : }
776 :
777 : const char*
778 0 : CPOWProxyHandler::className(JSContext* cx, HandleObject proxy) const
779 : {
780 0 : WrapperOwner* parent = OwnerOf(proxy);
781 0 : if (!parent->active())
782 0 : return "<dead CPOW>";
783 0 : return parent->className(cx, proxy);
784 : }
785 :
786 : const char*
787 0 : WrapperOwner::className(JSContext* cx, HandleObject proxy)
788 : {
789 0 : AuxCPOWData* data = AuxCPOWDataOf(proxy);
790 0 : if (data->className.IsEmpty()) {
791 0 : ObjectId objId = idOf(proxy);
792 :
793 0 : if (!SendClassName(objId, &data->className))
794 0 : return "<error>";
795 :
796 0 : LOG_STACK();
797 : }
798 :
799 0 : return data->className.get();
800 : }
801 :
802 : bool
803 0 : CPOWProxyHandler::getPrototype(JSContext* cx, HandleObject proxy, MutableHandleObject objp) const
804 : {
805 0 : FORWARD(getPrototype, (cx, proxy, objp), false);
806 : }
807 :
808 : bool
809 0 : WrapperOwner::getPrototype(JSContext* cx, HandleObject proxy, MutableHandleObject objp)
810 : {
811 0 : ObjectId objId = idOf(proxy);
812 :
813 0 : ObjectOrNullVariant val;
814 0 : ReturnStatus status;
815 0 : if (!SendGetPrototype(objId, &status, &val))
816 0 : return ipcfail(cx);
817 :
818 0 : LOG_STACK();
819 :
820 0 : if (!ok(cx, status))
821 0 : return false;
822 :
823 0 : objp.set(fromObjectOrNullVariant(cx, val));
824 :
825 0 : return true;
826 : }
827 :
828 : bool
829 0 : CPOWProxyHandler::getPrototypeIfOrdinary(JSContext* cx, HandleObject proxy, bool* isOrdinary,
830 : MutableHandleObject objp) const
831 : {
832 0 : FORWARD(getPrototypeIfOrdinary, (cx, proxy, isOrdinary, objp), false);
833 : }
834 :
835 : bool
836 0 : WrapperOwner::getPrototypeIfOrdinary(JSContext* cx, HandleObject proxy, bool* isOrdinary,
837 : MutableHandleObject objp)
838 : {
839 0 : ObjectId objId = idOf(proxy);
840 :
841 0 : ObjectOrNullVariant val;
842 0 : ReturnStatus status;
843 0 : if (!SendGetPrototypeIfOrdinary(objId, &status, isOrdinary, &val))
844 0 : return ipcfail(cx);
845 :
846 0 : LOG_STACK();
847 :
848 0 : if (!ok(cx, status))
849 0 : return false;
850 :
851 0 : objp.set(fromObjectOrNullVariant(cx, val));
852 :
853 0 : return true;
854 : }
855 :
856 : RegExpShared*
857 0 : CPOWProxyHandler::regexp_toShared(JSContext* cx, HandleObject proxy) const
858 : {
859 0 : FORWARD(regexp_toShared, (cx, proxy), nullptr);
860 : }
861 :
862 : RegExpShared*
863 0 : WrapperOwner::regexp_toShared(JSContext* cx, HandleObject proxy)
864 : {
865 0 : ObjectId objId = idOf(proxy);
866 :
867 0 : ReturnStatus status;
868 0 : nsString source;
869 0 : unsigned flags = 0;
870 0 : if (!SendRegExpToShared(objId, &status, &source, &flags)) {
871 0 : MOZ_ALWAYS_FALSE(ipcfail(cx));
872 0 : return nullptr;
873 : }
874 0 : LOG_STACK();
875 :
876 0 : if (!ok(cx, status))
877 0 : return nullptr;
878 :
879 0 : RootedObject regexp(cx);
880 0 : regexp = JS_NewUCRegExpObject(cx, source.get(), source.Length(), flags);
881 0 : if (!regexp)
882 0 : return nullptr;
883 :
884 0 : return js::RegExpToSharedNonInline(cx, regexp);
885 : }
886 :
887 : void
888 0 : CPOWProxyHandler::finalize(JSFreeOp* fop, JSObject* proxy) const
889 : {
890 0 : AuxCPOWData* aux = AuxCPOWDataOf(proxy);
891 :
892 0 : OwnerOf(proxy)->drop(proxy);
893 :
894 0 : if (aux)
895 0 : delete aux;
896 0 : }
897 :
898 : void
899 0 : CPOWProxyHandler::objectMoved(JSObject* proxy, const JSObject* old) const
900 : {
901 0 : OwnerOf(proxy)->updatePointer(proxy, old);
902 0 : }
903 :
904 : bool
905 0 : CPOWProxyHandler::isCallable(JSObject* proxy) const
906 : {
907 0 : AuxCPOWData* aux = AuxCPOWDataOf(proxy);
908 0 : return aux->isCallable;
909 : }
910 :
911 : bool
912 0 : CPOWProxyHandler::isConstructor(JSObject* proxy) const
913 : {
914 0 : AuxCPOWData* aux = AuxCPOWDataOf(proxy);
915 0 : return aux->isConstructor;
916 : }
917 :
918 : void
919 0 : WrapperOwner::drop(JSObject* obj)
920 : {
921 : // The association may have already been swept from the table but if it's
922 : // there then remove it.
923 0 : ObjectId objId = idOfUnchecked(obj);
924 0 : if (cpows_.findPreserveColor(objId) == obj)
925 0 : cpows_.remove(objId);
926 :
927 0 : if (active())
928 0 : Unused << SendDropObject(objId);
929 0 : decref();
930 0 : }
931 :
932 : void
933 0 : WrapperOwner::updatePointer(JSObject* obj, const JSObject* old)
934 : {
935 0 : ObjectId objId = idOfUnchecked(obj);
936 0 : MOZ_ASSERT(hasCPOW(objId, old));
937 0 : cpows_.add(objId, obj);
938 0 : }
939 :
940 : bool
941 3 : WrapperOwner::init()
942 : {
943 3 : if (!JavaScriptShared::init())
944 0 : return false;
945 :
946 3 : return true;
947 : }
948 :
949 : bool
950 0 : WrapperOwner::getPropertyKeys(JSContext* cx, HandleObject proxy, uint32_t flags, AutoIdVector& props)
951 : {
952 0 : ObjectId objId = idOf(proxy);
953 :
954 0 : ReturnStatus status;
955 0 : InfallibleTArray<JSIDVariant> ids;
956 0 : if (!SendGetPropertyKeys(objId, flags, &status, &ids))
957 0 : return ipcfail(cx);
958 :
959 0 : LOG_STACK();
960 :
961 0 : if (!ok(cx, status))
962 0 : return false;
963 :
964 0 : for (size_t i = 0; i < ids.Length(); i++) {
965 0 : RootedId id(cx);
966 0 : if (!fromJSIDVariant(cx, ids[i], &id))
967 0 : return false;
968 0 : if (!props.append(id))
969 0 : return false;
970 : }
971 :
972 0 : return true;
973 : }
974 :
975 : namespace mozilla {
976 : namespace jsipc {
977 :
978 : bool
979 7837 : IsCPOW(JSObject* obj)
980 : {
981 7837 : return IsProxy(obj) && GetProxyHandler(obj) == &CPOWProxyHandler::singleton;
982 : }
983 :
984 : bool
985 7669 : IsWrappedCPOW(JSObject* obj)
986 : {
987 7669 : JSObject* unwrapped = js::UncheckedUnwrap(obj, true);
988 7669 : if (!unwrapped)
989 0 : return false;
990 7669 : return IsCPOW(unwrapped);
991 : }
992 :
993 : void
994 0 : GetWrappedCPOWTag(JSObject* obj, nsACString& out)
995 : {
996 0 : JSObject* unwrapped = js::UncheckedUnwrap(obj, true);
997 0 : MOZ_ASSERT(IsCPOW(unwrapped));
998 :
999 0 : AuxCPOWData* aux = AuxCPOWDataOf(unwrapped);
1000 0 : if (aux)
1001 0 : out = aux->objectTag;
1002 0 : }
1003 :
1004 : nsresult
1005 0 : InstanceOf(JSObject* proxy, const nsID* id, bool* bp)
1006 : {
1007 0 : WrapperOwner* parent = OwnerOf(proxy);
1008 0 : if (!parent->active())
1009 0 : return NS_ERROR_UNEXPECTED;
1010 0 : return parent->instanceOf(proxy, id, bp);
1011 : }
1012 :
1013 : bool
1014 0 : DOMInstanceOf(JSContext* cx, JSObject* proxyArg, int prototypeID, int depth, bool* bp)
1015 : {
1016 0 : RootedObject proxy(cx, proxyArg);
1017 0 : FORWARD(domInstanceOf, (cx, proxy, prototypeID, depth, bp), false);
1018 : }
1019 :
1020 : } /* namespace jsipc */
1021 : } /* namespace mozilla */
1022 :
1023 : nsresult
1024 0 : WrapperOwner::instanceOf(JSObject* obj, const nsID* id, bool* bp)
1025 : {
1026 0 : ObjectId objId = idOf(obj);
1027 :
1028 0 : JSIID iid;
1029 0 : ConvertID(*id, &iid);
1030 :
1031 0 : ReturnStatus status;
1032 0 : if (!SendInstanceOf(objId, iid, &status, bp))
1033 0 : return NS_ERROR_UNEXPECTED;
1034 :
1035 0 : if (status.type() != ReturnStatus::TReturnSuccess)
1036 0 : return NS_ERROR_UNEXPECTED;
1037 :
1038 0 : return NS_OK;
1039 : }
1040 :
1041 : bool
1042 0 : WrapperOwner::domInstanceOf(JSContext* cx, JSObject* obj, int prototypeID, int depth, bool* bp)
1043 : {
1044 0 : ObjectId objId = idOf(obj);
1045 :
1046 0 : ReturnStatus status;
1047 0 : if (!SendDOMInstanceOf(objId, prototypeID, depth, &status, bp))
1048 0 : return ipcfail(cx);
1049 :
1050 0 : LOG_STACK();
1051 :
1052 0 : return ok(cx, status);
1053 : }
1054 :
1055 : void
1056 0 : WrapperOwner::ActorDestroy(ActorDestroyReason why)
1057 : {
1058 0 : inactive_ = true;
1059 :
1060 0 : objects_.clear();
1061 0 : unwaivedObjectIds_.clear();
1062 0 : waivedObjectIds_.clear();
1063 0 : }
1064 :
1065 : bool
1066 0 : WrapperOwner::ipcfail(JSContext* cx)
1067 : {
1068 0 : JS_ReportErrorASCII(cx, "cross-process JS call failed");
1069 0 : return false;
1070 : }
1071 :
1072 : bool
1073 0 : WrapperOwner::ok(JSContext* cx, const ReturnStatus& status)
1074 : {
1075 0 : if (status.type() == ReturnStatus::TReturnSuccess)
1076 0 : return true;
1077 :
1078 0 : if (status.type() == ReturnStatus::TReturnStopIteration)
1079 0 : return JS_ThrowStopIteration(cx);
1080 :
1081 0 : if (status.type() == ReturnStatus::TReturnDeadCPOW) {
1082 0 : JS_ReportErrorASCII(cx, "operation not possible on dead CPOW");
1083 0 : return false;
1084 : }
1085 :
1086 0 : RootedValue exn(cx);
1087 0 : if (!fromVariant(cx, status.get_ReturnException().exn(), &exn))
1088 0 : return false;
1089 :
1090 0 : JS_SetPendingException(cx, exn);
1091 0 : return false;
1092 : }
1093 :
1094 : bool
1095 0 : WrapperOwner::ok(JSContext* cx, const ReturnStatus& status, ObjectOpResult& result)
1096 : {
1097 0 : if (status.type() == ReturnStatus::TReturnObjectOpResult)
1098 0 : return result.fail(status.get_ReturnObjectOpResult().code());
1099 0 : if (!ok(cx, status))
1100 0 : return false;
1101 0 : return result.succeed();
1102 : }
1103 :
1104 : // CPOWs can have a tag string attached to them, originating in the local
1105 : // process from this function. It's sent with the CPOW to the remote process,
1106 : // where it can be fetched with Components.utils.getCrossProcessWrapperTag.
1107 : static nsCString
1108 29 : GetRemoteObjectTag(JS::Handle<JSObject*> obj)
1109 : {
1110 52 : if (nsCOMPtr<nsISupports> supports = xpc::UnwrapReflectorToISupports(obj)) {
1111 52 : nsCOMPtr<nsIDocShellTreeItem> treeItem(do_QueryInterface(supports));
1112 29 : if (treeItem)
1113 6 : return NS_LITERAL_CSTRING("ContentDocShellTreeItem");
1114 :
1115 46 : nsCOMPtr<nsIDOMDocument> doc(do_QueryInterface(supports));
1116 23 : if (doc)
1117 0 : return NS_LITERAL_CSTRING("ContentDocument");
1118 : }
1119 :
1120 23 : return NS_LITERAL_CSTRING("generic");
1121 : }
1122 :
1123 : static RemoteObject
1124 29 : MakeRemoteObject(JSContext* cx, ObjectId id, HandleObject obj)
1125 : {
1126 58 : return RemoteObject(id.serialize(),
1127 58 : JS::IsCallable(obj),
1128 58 : JS::IsConstructor(obj),
1129 58 : dom::IsDOMObject(obj),
1130 87 : GetRemoteObjectTag(obj));
1131 : }
1132 :
1133 : bool
1134 29 : WrapperOwner::toObjectVariant(JSContext* cx, JSObject* objArg, ObjectVariant* objVarp)
1135 : {
1136 58 : RootedObject obj(cx, objArg);
1137 29 : MOZ_ASSERT(obj);
1138 :
1139 : // We always save objects unwrapped in the CPOW table. If we stored
1140 : // wrappers, then the wrapper might be GCed while the target remained alive.
1141 : // Whenever operating on an object that comes from the table, we wrap it
1142 : // in findObjectById.
1143 29 : unsigned wrapperFlags = 0;
1144 29 : obj = js::UncheckedUnwrap(obj, true, &wrapperFlags);
1145 29 : if (obj && IsCPOW(obj) && OwnerOf(obj) == this) {
1146 0 : *objVarp = LocalObject(idOf(obj).serialize());
1147 0 : return true;
1148 : }
1149 29 : bool waiveXray = wrapperFlags & xpc::WrapperFactory::WAIVE_XRAY_WRAPPER_FLAG;
1150 :
1151 29 : ObjectId id = objectIdMap(waiveXray).find(obj);
1152 29 : if (!id.isNull()) {
1153 24 : MOZ_ASSERT(id.hasXrayWaiver() == waiveXray);
1154 24 : *objVarp = MakeRemoteObject(cx, id, obj);
1155 24 : return true;
1156 : }
1157 :
1158 : // Need to call PreserveWrapper on |obj| in case it's a reflector.
1159 : // FIXME: What if it's an XPCWrappedNative?
1160 5 : if (mozilla::dom::IsDOMObject(obj))
1161 0 : mozilla::dom::TryPreserveWrapper(obj);
1162 :
1163 5 : id = ObjectId(nextSerialNumber_++, waiveXray);
1164 5 : if (!objects_.add(id, obj))
1165 0 : return false;
1166 5 : if (!objectIdMap(waiveXray).add(cx, obj, id))
1167 0 : return false;
1168 :
1169 5 : *objVarp = MakeRemoteObject(cx, id, obj);
1170 5 : return true;
1171 : }
1172 :
1173 : JSObject*
1174 29 : WrapperOwner::fromObjectVariant(JSContext* cx, const ObjectVariant& objVar)
1175 : {
1176 29 : if (objVar.type() == ObjectVariant::TRemoteObject) {
1177 29 : return fromRemoteObjectVariant(cx, objVar.get_RemoteObject());
1178 : } else {
1179 0 : return fromLocalObjectVariant(cx, objVar.get_LocalObject());
1180 : }
1181 : }
1182 :
1183 : JSObject*
1184 29 : WrapperOwner::fromRemoteObjectVariant(JSContext* cx, const RemoteObject& objVar)
1185 : {
1186 29 : ObjectId objId = ObjectId::deserialize(objVar.serializedId());
1187 58 : RootedObject obj(cx, findCPOWById(objId));
1188 29 : if (!obj) {
1189 :
1190 : // All CPOWs live in the privileged junk scope.
1191 10 : RootedObject junkScope(cx, xpc::PrivilegedJunkScope());
1192 10 : JSAutoCompartment ac(cx, junkScope);
1193 10 : RootedValue v(cx, UndefinedValue());
1194 : // We need to setLazyProto for the getPrototype/getPrototypeIfOrdinary
1195 : // hooks.
1196 5 : ProxyOptions options;
1197 5 : options.setLazyProto(true);
1198 10 : obj = NewProxyObject(cx,
1199 : &CPOWProxyHandler::singleton,
1200 : v,
1201 : nullptr,
1202 5 : options);
1203 5 : if (!obj)
1204 0 : return nullptr;
1205 :
1206 5 : if (!cpows_.add(objId, obj))
1207 0 : return nullptr;
1208 :
1209 5 : nextCPOWNumber_ = objId.serialNumber() + 1;
1210 :
1211 : // Incref once we know the decref will be called.
1212 5 : incref();
1213 :
1214 : AuxCPOWData* aux = new AuxCPOWData(objId,
1215 5 : objVar.isCallable(),
1216 5 : objVar.isConstructor(),
1217 5 : objVar.isDOMObject(),
1218 15 : objVar.objectTag());
1219 :
1220 5 : SetProxyReservedSlot(obj, 0, PrivateValue(this));
1221 5 : SetProxyReservedSlot(obj, 1, PrivateValue(aux));
1222 : }
1223 :
1224 29 : if (!JS_WrapObject(cx, &obj))
1225 0 : return nullptr;
1226 29 : return obj;
1227 : }
1228 :
1229 : JSObject*
1230 0 : WrapperOwner::fromLocalObjectVariant(JSContext* cx, const LocalObject& objVar)
1231 : {
1232 0 : ObjectId id = ObjectId::deserialize(objVar.serializedId());
1233 0 : Rooted<JSObject*> obj(cx, findObjectById(cx, id));
1234 0 : if (!obj)
1235 0 : return nullptr;
1236 0 : if (!JS_WrapObject(cx, &obj))
1237 0 : return nullptr;
1238 0 : return obj;
1239 : }
|