1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
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 /* JavaScript JSClasses and JSOps for our Wrapped Native JS Objects. */
8 
9 #include "xpcprivate.h"
10 #include "xpc_make_class.h"
11 #include "mozilla/dom/BindingUtils.h"
12 #include "mozilla/Preferences.h"
13 #include "js/CharacterEncoding.h"
14 #include "js/Class.h"
15 #include "js/Object.h"  // JS::GetClass
16 #include "js/Printf.h"
17 #include "js/Symbol.h"
18 
19 using namespace mozilla;
20 using namespace JS;
21 
22 /***************************************************************************/
23 
24 // All of the exceptions thrown into JS from this file go through here.
25 // That makes this a nice place to set a breakpoint.
26 
Throw(nsresult errNum,JSContext * cx)27 static bool Throw(nsresult errNum, JSContext* cx) {
28   XPCThrower::Throw(errNum, cx);
29   return false;
30 }
31 
32 // Handy macro used in many callback stub below.
33 
34 #define THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper)                         \
35   PR_BEGIN_MACRO                                                             \
36   if (!wrapper) return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx);           \
37   if (!wrapper->IsValid()) return Throw(NS_ERROR_XPC_HAS_BEEN_SHUTDOWN, cx); \
38   PR_END_MACRO
39 
40 /***************************************************************************/
41 
ToStringGuts(XPCCallContext & ccx)42 static bool ToStringGuts(XPCCallContext& ccx) {
43   UniqueChars sz;
44   XPCWrappedNative* wrapper = ccx.GetWrapper();
45 
46   if (wrapper) {
47     sz.reset(wrapper->ToString(ccx.GetTearOff()));
48   } else {
49     sz = JS_smprintf("[xpconnect wrapped native prototype]");
50   }
51 
52   if (!sz) {
53     JS_ReportOutOfMemory(ccx);
54     return false;
55   }
56 
57   JSString* str = JS_NewStringCopyZ(ccx, sz.get());
58   if (!str) {
59     return false;
60   }
61 
62   ccx.SetRetVal(JS::StringValue(str));
63   return true;
64 }
65 
66 /***************************************************************************/
67 
XPC_WN_Shared_ToString(JSContext * cx,unsigned argc,Value * vp)68 static bool XPC_WN_Shared_ToString(JSContext* cx, unsigned argc, Value* vp) {
69   CallArgs args = CallArgsFromVp(argc, vp);
70 
71   RootedObject obj(cx);
72   if (!args.computeThis(cx, &obj)) {
73     return false;
74   }
75 
76   XPCCallContext ccx(cx, obj);
77   if (!ccx.IsValid()) {
78     return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx);
79   }
80   ccx.SetName(ccx.GetContext()->GetStringID(XPCJSContext::IDX_TO_STRING));
81   ccx.SetArgsAndResultPtr(args.length(), args.array(), vp);
82   return ToStringGuts(ccx);
83 }
84 
XPC_WN_Shared_ToSource(JSContext * cx,unsigned argc,Value * vp)85 static bool XPC_WN_Shared_ToSource(JSContext* cx, unsigned argc, Value* vp) {
86   CallArgs args = CallArgsFromVp(argc, vp);
87   static const char empty[] = "({})";
88   JSString* str = JS_NewStringCopyN(cx, empty, sizeof(empty) - 1);
89   if (!str) {
90     return false;
91   }
92   args.rval().setString(str);
93 
94   return true;
95 }
96 
XPC_WN_Shared_toPrimitive(JSContext * cx,unsigned argc,Value * vp)97 static bool XPC_WN_Shared_toPrimitive(JSContext* cx, unsigned argc, Value* vp) {
98   CallArgs args = CallArgsFromVp(argc, vp);
99 
100   RootedObject obj(cx);
101   if (!JS_ValueToObject(cx, args.thisv(), &obj)) {
102     return false;
103   }
104   XPCCallContext ccx(cx, obj);
105   XPCWrappedNative* wrapper = ccx.GetWrapper();
106   THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
107 
108   JSType hint;
109   if (!GetFirstArgumentAsTypeHint(cx, args, &hint)) {
110     return false;
111   }
112 
113   if (hint == JSTYPE_NUMBER) {
114     args.rval().set(NaNValue());
115     return true;
116   }
117 
118   MOZ_ASSERT(hint == JSTYPE_STRING || hint == JSTYPE_UNDEFINED);
119   ccx.SetName(ccx.GetContext()->GetStringID(XPCJSContext::IDX_TO_STRING));
120   ccx.SetArgsAndResultPtr(0, nullptr, args.rval().address());
121 
122   XPCNativeMember* member = ccx.GetMember();
123   if (member && member->IsMethod()) {
124     if (!XPCWrappedNative::CallMethod(ccx)) {
125       return false;
126     }
127 
128     if (args.rval().isPrimitive()) {
129       return true;
130     }
131   }
132 
133   // else...
134   return ToStringGuts(ccx);
135 }
136 
137 /***************************************************************************/
138 
139 // A "double wrapped object" is a user JSObject that has been wrapped as a
140 // wrappedJS in order to be used by native code and then re-wrapped by a
141 // wrappedNative wrapper to be used by JS code. One might think of it as:
142 //    wrappedNative(wrappedJS(underlying_JSObject))
143 // This is done (as opposed to just unwrapping the wrapped JS and automatically
144 // returning the underlying JSObject) so that JS callers will see what looks
145 // Like any other xpcom object - and be limited to use its interfaces.
146 //
147 
148 /**
149  * When JavaScript code uses a component that is itself implemented in
150  * JavaScript then XPConnect will build a wrapper rather than directly
151  * expose the JSObject of the component. This allows components implemented
152  * in JavaScript to 'look' just like any other xpcom component (from the
153  * perspective of the JavaScript caller). This insulates the component from
154  * the caller and hides any properties or methods that are not part of the
155  * interface as declared in xpidl. Usually this is a good thing.
156  *
157  * However, in some cases it is useful to allow the JS caller access to the
158  * JS component's underlying implementation. In order to facilitate this
159  * XPConnect supports the 'wrappedJSObject' property. This 'wrappedJSObject'
160  * property is different than the XrayWrapper meaning. (The naming collision
161  * avoids having more than one magic XPConnect property name, but is
162  * confusing.)
163  *
164  * The caller code can do:
165  *
166  * // 'foo' is some xpcom component (that might be implemented in JS).
167  * var bar = foo.wrappedJSObject;
168  * if(bar) {
169  *    // bar is the underlying JSObject. Do stuff with it here.
170  * }
171  *
172  * Recall that 'foo' above is an XPConnect wrapper, not the underlying JS
173  * object. The property get "foo.wrappedJSObject" will only succeed if three
174  * conditions are met:
175  *
176  * 1) 'foo' really is an XPConnect wrapper around a JSObject.
177  * 3) The caller must be system JS and not content. Double-wrapped XPCWJS should
178  *    not be exposed to content except with a remote-XUL domain.
179  *
180  * Notes:
181  *
182  * a) If 'foo' above were the underlying JSObject and not a wrapper at all,
183  *    then this all just works and XPConnect is not part of the picture at all.
184  * b) One might ask why 'foo' should not just implement an interface through
185  *    which callers might get at the underlying object. There are two reasons:
186  *   i)   XPConnect would still have to do magic since JSObject is not a
187  *        scriptable type.
188  *   ii)  Avoiding the explicit interface makes it easier for both the caller
189  *        and the component.
190  */
191 
GetDoubleWrappedJSObject(XPCCallContext & ccx,XPCWrappedNative * wrapper)192 static JSObject* GetDoubleWrappedJSObject(XPCCallContext& ccx,
193                                           XPCWrappedNative* wrapper) {
194   RootedObject obj(ccx);
195   {
196     nsCOMPtr<nsIXPConnectWrappedJS> underware =
197         do_QueryInterface(wrapper->GetIdentityObject());
198     if (!underware) {
199       return nullptr;
200     }
201     RootedObject mainObj(ccx, underware->GetJSObject());
202     if (mainObj) {
203       JSAutoRealm ar(ccx, underware->GetJSObjectGlobal());
204 
205       // We don't have to root this ID, as it's already rooted by our context.
206       HandleId id =
207           ccx.GetContext()->GetStringID(XPCJSContext::IDX_WRAPPED_JSOBJECT);
208 
209       // If the `wrappedJSObject` property is defined, use the result of getting
210       // that property, otherwise fall back to the `mainObj` object which is
211       // directly being wrapped.
212       RootedValue val(ccx);
213       if (JS_GetPropertyById(ccx, mainObj, id, &val) && !val.isPrimitive()) {
214         obj = val.toObjectOrNull();
215       } else {
216         obj = mainObj;
217       }
218     }
219   }
220   return obj;
221 }
222 
223 // This is the getter native function we use to handle 'wrappedJSObject' for
224 // double wrapped JSObjects.
225 
XPC_WN_DoubleWrappedGetter(JSContext * cx,unsigned argc,Value * vp)226 static bool XPC_WN_DoubleWrappedGetter(JSContext* cx, unsigned argc,
227                                        Value* vp) {
228   CallArgs args = CallArgsFromVp(argc, vp);
229 
230   if (!args.thisv().isObject()) {
231     JS_ReportErrorASCII(
232         cx,
233         "xpconnect double wrapped getter called on incompatible non-object");
234     return false;
235   }
236   RootedObject obj(cx, &args.thisv().toObject());
237 
238   XPCCallContext ccx(cx, obj);
239   XPCWrappedNative* wrapper = ccx.GetWrapper();
240   THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
241 
242   MOZ_ASSERT(JS_TypeOfValue(cx, args.calleev()) == JSTYPE_FUNCTION,
243              "bad function");
244 
245   RootedObject realObject(cx, GetDoubleWrappedJSObject(ccx, wrapper));
246   if (!realObject) {
247     // This is pretty unexpected at this point. The object originally
248     // responded to this get property call and now gives no object.
249     // XXX Should this throw something at the caller?
250     args.rval().setNull();
251     return true;
252   }
253 
254   // It is a double wrapped object. This should really never appear in
255   // content these days, but addons still do it - see bug 965921.
256   if (MOZ_UNLIKELY(!nsContentUtils::IsSystemCaller(cx))) {
257     JS_ReportErrorASCII(cx,
258                         "Attempt to use .wrappedJSObject in untrusted code");
259     return false;
260   }
261   args.rval().setObject(*realObject);
262   return JS_WrapValue(cx, args.rval());
263 }
264 
265 /***************************************************************************/
266 
267 // This is our shared function to define properties on our JSObjects.
268 
269 /*
270  * NOTE:
271  * We *never* set the tearoff names (e.g. nsIFoo) as JS_ENUMERATE.
272  * We *never* set toString or toSource as JS_ENUMERATE.
273  */
274 
DefinePropertyIfFound(XPCCallContext & ccx,HandleObject obj,HandleId idArg,XPCNativeSet * set,XPCNativeInterface * ifaceArg,XPCNativeMember * member,XPCWrappedNativeScope * scope,bool reflectToStringAndToSource,XPCWrappedNative * wrapperToReflectInterfaceNames,XPCWrappedNative * wrapperToReflectDoubleWrap,nsIXPCScriptable * scr,unsigned propFlags,bool * resolved)275 static bool DefinePropertyIfFound(
276     XPCCallContext& ccx, HandleObject obj, HandleId idArg, XPCNativeSet* set,
277     XPCNativeInterface* ifaceArg, XPCNativeMember* member,
278     XPCWrappedNativeScope* scope, bool reflectToStringAndToSource,
279     XPCWrappedNative* wrapperToReflectInterfaceNames,
280     XPCWrappedNative* wrapperToReflectDoubleWrap, nsIXPCScriptable* scr,
281     unsigned propFlags, bool* resolved) {
282   RootedId id(ccx, idArg);
283   RefPtr<XPCNativeInterface> iface = ifaceArg;
284   XPCJSContext* xpccx = ccx.GetContext();
285   bool found;
286   const char* name;
287 
288   propFlags |= JSPROP_RESOLVING;
289 
290   if (set) {
291     if (iface) {
292       found = true;
293     } else {
294       found = set->FindMember(id, &member, &iface);
295     }
296   } else
297     found = (nullptr != (member = iface->FindMember(id)));
298 
299   if (!found) {
300     if (reflectToStringAndToSource) {
301       JSNative call;
302       if (id == xpccx->GetStringID(XPCJSContext::IDX_TO_STRING)) {
303         call = XPC_WN_Shared_ToString;
304         name = xpccx->GetStringName(XPCJSContext::IDX_TO_STRING);
305       } else if (id == xpccx->GetStringID(XPCJSContext::IDX_TO_SOURCE)) {
306         call = XPC_WN_Shared_ToSource;
307         name = xpccx->GetStringName(XPCJSContext::IDX_TO_SOURCE);
308       } else if (id.isWellKnownSymbol(JS::SymbolCode::toPrimitive)) {
309         call = XPC_WN_Shared_toPrimitive;
310         name = "[Symbol.toPrimitive]";
311       } else {
312         call = nullptr;
313       }
314 
315       if (call) {
316         RootedFunction fun(ccx, JS_NewFunction(ccx, call, 0, 0, name));
317         if (!fun) {
318           JS_ReportOutOfMemory(ccx);
319           return false;
320         }
321 
322         AutoResolveName arn(ccx, id);
323         if (resolved) {
324           *resolved = true;
325         }
326         RootedObject value(ccx, JS_GetFunctionObject(fun));
327         return JS_DefinePropertyById(ccx, obj, id, value,
328                                      propFlags & ~JSPROP_ENUMERATE);
329       }
330     }
331     // This *might* be a tearoff name that is not yet part of our
332     // set. Let's lookup the name and see if it is the name of an
333     // interface. Then we'll see if the object actually *does* this
334     // interface and add a tearoff as necessary.
335 
336     if (wrapperToReflectInterfaceNames) {
337       JS::UniqueChars name;
338       RefPtr<XPCNativeInterface> iface2;
339       XPCWrappedNativeTearOff* to;
340       RootedObject jso(ccx);
341       nsresult rv = NS_OK;
342 
343       bool defineProperty = false;
344       do {
345         if (!JSID_IS_STRING(id)) {
346           break;
347         }
348 
349         name = JS_EncodeStringToLatin1(ccx, JSID_TO_STRING(id));
350         if (!name) {
351           break;
352         }
353 
354         iface2 = XPCNativeInterface::GetNewOrUsed(ccx, name.get());
355         if (!iface2) {
356           break;
357         }
358 
359         to =
360             wrapperToReflectInterfaceNames->FindTearOff(ccx, iface2, true, &rv);
361         if (!to) {
362           break;
363         }
364 
365         jso = to->GetJSObject();
366         if (!jso) {
367           break;
368         }
369 
370         defineProperty = true;
371       } while (false);
372 
373       if (defineProperty) {
374         AutoResolveName arn(ccx, id);
375         if (resolved) {
376           *resolved = true;
377         }
378         return JS_DefinePropertyById(ccx, obj, id, jso,
379                                      propFlags & ~JSPROP_ENUMERATE);
380       } else if (NS_FAILED(rv) && rv != NS_ERROR_NO_INTERFACE) {
381         return Throw(rv, ccx);
382       }
383     }
384 
385     // This *might* be a double wrapped JSObject
386     if (wrapperToReflectDoubleWrap &&
387         id == xpccx->GetStringID(XPCJSContext::IDX_WRAPPED_JSOBJECT) &&
388         GetDoubleWrappedJSObject(ccx, wrapperToReflectDoubleWrap)) {
389       // We build and add a getter function.
390       // A security check is done on a per-get basis.
391 
392       JSFunction* fun;
393 
394       id = xpccx->GetStringID(XPCJSContext::IDX_WRAPPED_JSOBJECT);
395       name = xpccx->GetStringName(XPCJSContext::IDX_WRAPPED_JSOBJECT);
396 
397       fun = JS_NewFunction(ccx, XPC_WN_DoubleWrappedGetter, 0, 0, name);
398 
399       if (!fun) {
400         return false;
401       }
402 
403       RootedObject funobj(ccx, JS_GetFunctionObject(fun));
404       if (!funobj) {
405         return false;
406       }
407 
408       propFlags &= ~JSPROP_ENUMERATE;
409 
410       AutoResolveName arn(ccx, id);
411       if (resolved) {
412         *resolved = true;
413       }
414       return JS_DefinePropertyById(ccx, obj, id, funobj, nullptr, propFlags);
415     }
416 
417     if (resolved) {
418       *resolved = false;
419     }
420     return true;
421   }
422 
423   if (!member) {
424     if (wrapperToReflectInterfaceNames) {
425       XPCWrappedNativeTearOff* to =
426           wrapperToReflectInterfaceNames->FindTearOff(ccx, iface, true);
427 
428       if (!to) {
429         return false;
430       }
431       RootedObject jso(ccx, to->GetJSObject());
432       if (!jso) {
433         return false;
434       }
435 
436       AutoResolveName arn(ccx, id);
437       if (resolved) {
438         *resolved = true;
439       }
440       return JS_DefinePropertyById(ccx, obj, id, jso,
441                                    propFlags & ~JSPROP_ENUMERATE);
442     }
443     if (resolved) {
444       *resolved = false;
445     }
446     return true;
447   }
448 
449   if (member->IsConstant()) {
450     RootedValue val(ccx);
451     AutoResolveName arn(ccx, id);
452     if (resolved) {
453       *resolved = true;
454     }
455     return member->GetConstantValue(ccx, iface, val.address()) &&
456            JS_DefinePropertyById(ccx, obj, id, val, propFlags);
457   }
458 
459   if (id == xpccx->GetStringID(XPCJSContext::IDX_TO_STRING) ||
460       id == xpccx->GetStringID(XPCJSContext::IDX_TO_SOURCE) ||
461       (scr && scr->DontEnumQueryInterface() &&
462        id == xpccx->GetStringID(XPCJSContext::IDX_QUERY_INTERFACE)))
463     propFlags &= ~JSPROP_ENUMERATE;
464 
465   RootedValue funval(ccx);
466   if (!member->NewFunctionObject(ccx, iface, obj, funval.address())) {
467     return false;
468   }
469 
470   if (member->IsMethod()) {
471     AutoResolveName arn(ccx, id);
472     if (resolved) {
473       *resolved = true;
474     }
475     return JS_DefinePropertyById(ccx, obj, id, funval, propFlags);
476   }
477 
478   // else...
479 
480   MOZ_ASSERT(member->IsAttribute(), "way broken!");
481 
482   propFlags &= ~JSPROP_READONLY;
483   RootedObject funobjGetter(ccx, funval.toObjectOrNull());
484   RootedObject funobjSetter(ccx);
485   if (member->IsWritableAttribute()) {
486     funobjSetter = funobjGetter;
487   }
488 
489   AutoResolveName arn(ccx, id);
490   if (resolved) {
491     *resolved = true;
492   }
493 
494   return JS_DefinePropertyById(ccx, obj, id, funobjGetter, funobjSetter,
495                                propFlags);
496 }
497 
498 /***************************************************************************/
499 /***************************************************************************/
500 
XPC_WN_OnlyIWrite_AddPropertyStub(JSContext * cx,HandleObject obj,HandleId id,HandleValue v)501 static bool XPC_WN_OnlyIWrite_AddPropertyStub(JSContext* cx, HandleObject obj,
502                                               HandleId id, HandleValue v) {
503   XPCCallContext ccx(cx, obj, nullptr, id);
504   XPCWrappedNative* wrapper = ccx.GetWrapper();
505   THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
506 
507   // Allow only XPConnect to add/set the property
508   if (ccx.GetResolveName() == id) {
509     return true;
510   }
511 
512   return Throw(NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN, cx);
513 }
514 
XPC_WN_CannotModifyPropertyStub(JSContext * cx,HandleObject obj,HandleId id,HandleValue v)515 bool XPC_WN_CannotModifyPropertyStub(JSContext* cx, HandleObject obj,
516                                      HandleId id, HandleValue v) {
517   return Throw(NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN, cx);
518 }
519 
XPC_WN_CannotDeletePropertyStub(JSContext * cx,HandleObject obj,HandleId id,ObjectOpResult & result)520 bool XPC_WN_CannotDeletePropertyStub(JSContext* cx, HandleObject obj,
521                                      HandleId id, ObjectOpResult& result) {
522   return Throw(NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN, cx);
523 }
524 
XPC_WN_Shared_Enumerate(JSContext * cx,HandleObject obj)525 bool XPC_WN_Shared_Enumerate(JSContext* cx, HandleObject obj) {
526   XPCCallContext ccx(cx, obj);
527   XPCWrappedNative* wrapper = ccx.GetWrapper();
528   THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
529 
530   // Since we aren't going to enumerate tearoff names and the prototype
531   // handles non-mutated members, we can do this potential short-circuit.
532   if (!wrapper->HasMutatedSet()) {
533     return true;
534   }
535 
536   XPCNativeSet* set = wrapper->GetSet();
537   XPCNativeSet* protoSet =
538       wrapper->HasProto() ? wrapper->GetProto()->GetSet() : nullptr;
539 
540   uint16_t interface_count = set->GetInterfaceCount();
541   XPCNativeInterface** interfaceArray = set->GetInterfaceArray();
542   for (uint16_t i = 0; i < interface_count; i++) {
543     XPCNativeInterface* iface = interfaceArray[i];
544     uint16_t member_count = iface->GetMemberCount();
545     for (uint16_t k = 0; k < member_count; k++) {
546       XPCNativeMember* member = iface->GetMemberAt(k);
547       jsid name = member->GetName();
548 
549       // Skip if this member is going to come from the proto.
550       uint16_t index;
551       if (protoSet && protoSet->FindMember(name, nullptr, &index) && index == i)
552         continue;
553       if (!xpc_ForcePropertyResolve(cx, obj, name)) {
554         return false;
555       }
556     }
557   }
558   return true;
559 }
560 
561 /***************************************************************************/
562 
563 enum WNHelperType { WN_NOHELPER, WN_HELPER };
564 
WrappedNativeFinalize(JSFreeOp * fop,JSObject * obj,WNHelperType helperType)565 static void WrappedNativeFinalize(JSFreeOp* fop, JSObject* obj,
566                                   WNHelperType helperType) {
567   const JSClass* clazz = JS::GetClass(obj);
568   if (clazz->flags & JSCLASS_DOM_GLOBAL) {
569     mozilla::dom::DestroyProtoAndIfaceCache(obj);
570   }
571   nsISupports* p = static_cast<nsISupports*>(xpc_GetJSPrivate(obj));
572   if (!p) {
573     return;
574   }
575 
576   XPCWrappedNative* wrapper = static_cast<XPCWrappedNative*>(p);
577   if (helperType == WN_HELPER) {
578     wrapper->GetScriptable()->Finalize(wrapper, fop, obj);
579   }
580   wrapper->FlatJSObjectFinalized();
581 }
582 
WrappedNativeObjectMoved(JSObject * obj,JSObject * old)583 static size_t WrappedNativeObjectMoved(JSObject* obj, JSObject* old) {
584   nsISupports* p = static_cast<nsISupports*>(xpc_GetJSPrivate(obj));
585   if (!p) {
586     return 0;
587   }
588 
589   XPCWrappedNative* wrapper = static_cast<XPCWrappedNative*>(p);
590   wrapper->FlatJSObjectMoved(obj, old);
591   return 0;
592 }
593 
XPC_WN_NoHelper_Finalize(JSFreeOp * fop,JSObject * obj)594 void XPC_WN_NoHelper_Finalize(JSFreeOp* fop, JSObject* obj) {
595   WrappedNativeFinalize(fop, obj, WN_NOHELPER);
596 }
597 
598 /*
599  * General comment about XPConnect tracing: Given a C++ object |wrapper| and its
600  * corresponding JS object |obj|, calling |wrapper->TraceSelf| will ask the JS
601  * engine to mark |obj|. Eventually, this will lead to the trace hook being
602  * called for |obj|. The trace hook should call |wrapper->TraceInside|, which
603  * should mark any JS objects held by |wrapper| as members.
604  */
605 
606 /* static */
Trace(JSTracer * trc,JSObject * obj)607 void XPCWrappedNative::Trace(JSTracer* trc, JSObject* obj) {
608   const JSClass* clazz = JS::GetClass(obj);
609   if (clazz->flags & JSCLASS_DOM_GLOBAL) {
610     mozilla::dom::TraceProtoAndIfaceCache(trc, obj);
611   }
612   MOZ_ASSERT(IS_WN_CLASS(clazz));
613 
614   XPCWrappedNative* wrapper = XPCWrappedNative::Get(obj);
615   if (wrapper && wrapper->IsValid()) {
616     wrapper->TraceInside(trc);
617   }
618 }
619 
XPCWrappedNative_Trace(JSTracer * trc,JSObject * obj)620 void XPCWrappedNative_Trace(JSTracer* trc, JSObject* obj) {
621   XPCWrappedNative::Trace(trc, obj);
622 }
623 
XPC_WN_NoHelper_Resolve(JSContext * cx,HandleObject obj,HandleId id,bool * resolvedp)624 static bool XPC_WN_NoHelper_Resolve(JSContext* cx, HandleObject obj,
625                                     HandleId id, bool* resolvedp) {
626   XPCCallContext ccx(cx, obj, nullptr, id);
627   XPCWrappedNative* wrapper = ccx.GetWrapper();
628   THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
629 
630   XPCNativeSet* set = ccx.GetSet();
631   if (!set) {
632     return true;
633   }
634 
635   // Don't resolve properties that are on our prototype.
636   if (ccx.GetInterface() && !ccx.GetStaticMemberIsLocal()) {
637     return true;
638   }
639 
640   return DefinePropertyIfFound(
641       ccx, obj, id, set, nullptr, nullptr, wrapper->GetScope(), true, wrapper,
642       wrapper, nullptr, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT,
643       resolvedp);
644 }
645 
646 static const JSClassOps XPC_WN_NoHelper_JSClassOps = {
647     XPC_WN_OnlyIWrite_AddPropertyStub,  // addProperty
648     XPC_WN_CannotDeletePropertyStub,    // delProperty
649     XPC_WN_Shared_Enumerate,            // enumerate
650     nullptr,                            // newEnumerate
651     XPC_WN_NoHelper_Resolve,            // resolve
652     nullptr,                            // mayResolve
653     XPC_WN_NoHelper_Finalize,           // finalize
654     nullptr,                            // call
655     nullptr,                            // hasInstance
656     nullptr,                            // construct
657     XPCWrappedNative::Trace,            // trace
658 };
659 
660 const js::ClassExtension XPC_WN_JSClassExtension = {
661     WrappedNativeObjectMoved,  // objectMovedOp
662 };
663 
664 const JSClass XPC_WN_NoHelper_JSClass = {
665     "XPCWrappedNative_NoHelper",
666     XPC_WRAPPER_FLAGS | JSCLASS_IS_WRAPPED_NATIVE |
667         JSCLASS_PRIVATE_IS_NSISUPPORTS | JSCLASS_FOREGROUND_FINALIZE,
668     &XPC_WN_NoHelper_JSClassOps,
669     JS_NULL_CLASS_SPEC,
670     &XPC_WN_JSClassExtension,
671     JS_NULL_OBJECT_OPS};
672 
673 /***************************************************************************/
674 
XPC_WN_MaybeResolvingPropertyStub(JSContext * cx,HandleObject obj,HandleId id,HandleValue v)675 bool XPC_WN_MaybeResolvingPropertyStub(JSContext* cx, HandleObject obj,
676                                        HandleId id, HandleValue v) {
677   XPCCallContext ccx(cx, obj);
678   XPCWrappedNative* wrapper = ccx.GetWrapper();
679   THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
680 
681   if (ccx.GetResolvingWrapper() == wrapper) {
682     return true;
683   }
684   return Throw(NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN, cx);
685 }
686 
XPC_WN_MaybeResolvingDeletePropertyStub(JSContext * cx,HandleObject obj,HandleId id,ObjectOpResult & result)687 bool XPC_WN_MaybeResolvingDeletePropertyStub(JSContext* cx, HandleObject obj,
688                                              HandleId id,
689                                              ObjectOpResult& result) {
690   XPCCallContext ccx(cx, obj);
691   XPCWrappedNative* wrapper = ccx.GetWrapper();
692   THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
693 
694   if (ccx.GetResolvingWrapper() == wrapper) {
695     return result.succeed();
696   }
697   return Throw(NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN, cx);
698 }
699 
700 // macro fun!
701 #define PRE_HELPER_STUB                                                 \
702   /* It's very important for "unwrapped" to be rooted here.  */         \
703   RootedObject unwrapped(cx, js::CheckedUnwrapDynamic(obj, cx, false)); \
704   if (!unwrapped) {                                                     \
705     JS_ReportErrorASCII(cx, "Permission denied to operate on object."); \
706     return false;                                                       \
707   }                                                                     \
708   if (!IS_WN_REFLECTOR(unwrapped)) {                                    \
709     return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx);                  \
710   }                                                                     \
711   XPCWrappedNative* wrapper = XPCWrappedNative::Get(unwrapped);         \
712   THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);                         \
713   bool retval = true;                                                   \
714   nsresult rv = wrapper->GetScriptable()->
715 
716 #define POST_HELPER_STUB                   \
717   if (NS_FAILED(rv)) return Throw(rv, cx); \
718   return retval;
719 
XPC_WN_Helper_Call(JSContext * cx,unsigned argc,Value * vp)720 bool XPC_WN_Helper_Call(JSContext* cx, unsigned argc, Value* vp) {
721   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
722   // N.B. we want obj to be the callee, not JS_THIS(cx, vp)
723   RootedObject obj(cx, &args.callee());
724 
725   XPCCallContext ccx(cx, obj, nullptr, JSID_VOIDHANDLE, args.length(),
726                      args.array(), args.rval().address());
727   if (!ccx.IsValid()) {
728     return false;
729   }
730 
731   PRE_HELPER_STUB
732   Call(wrapper, cx, obj, args, &retval);
733   POST_HELPER_STUB
734 }
735 
XPC_WN_Helper_Construct(JSContext * cx,unsigned argc,Value * vp)736 bool XPC_WN_Helper_Construct(JSContext* cx, unsigned argc, Value* vp) {
737   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
738   RootedObject obj(cx, &args.callee());
739   if (!obj) {
740     return false;
741   }
742 
743   XPCCallContext ccx(cx, obj, nullptr, JSID_VOIDHANDLE, args.length(),
744                      args.array(), args.rval().address());
745   if (!ccx.IsValid()) {
746     return false;
747   }
748 
749   PRE_HELPER_STUB
750   Construct(wrapper, cx, obj, args, &retval);
751   POST_HELPER_STUB
752 }
753 
XPC_WN_Helper_HasInstance(JSContext * cx,HandleObject obj,MutableHandleValue valp,bool * bp)754 bool XPC_WN_Helper_HasInstance(JSContext* cx, HandleObject obj,
755                                MutableHandleValue valp, bool* bp) {
756   bool retval2;
757   PRE_HELPER_STUB
758   HasInstance(wrapper, cx, obj, valp, &retval2, &retval);
759   *bp = retval2;
760   POST_HELPER_STUB
761 }
762 
XPC_WN_Helper_Finalize(JSFreeOp * fop,JSObject * obj)763 void XPC_WN_Helper_Finalize(JSFreeOp* fop, JSObject* obj) {
764   WrappedNativeFinalize(fop, obj, WN_HELPER);
765 }
766 
XPC_WN_Helper_Resolve(JSContext * cx,HandleObject obj,HandleId id,bool * resolvedp)767 bool XPC_WN_Helper_Resolve(JSContext* cx, HandleObject obj, HandleId id,
768                            bool* resolvedp) {
769   nsresult rv = NS_OK;
770   bool retval = true;
771   bool resolved = false;
772   XPCCallContext ccx(cx, obj);
773   XPCWrappedNative* wrapper = ccx.GetWrapper();
774   THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
775 
776   RootedId old(cx, ccx.SetResolveName(id));
777 
778   nsCOMPtr<nsIXPCScriptable> scr = wrapper->GetScriptable();
779   if (scr && scr->WantResolve()) {
780     XPCWrappedNative* oldResolvingWrapper;
781     bool allowPropMods = scr->AllowPropModsDuringResolve();
782 
783     if (allowPropMods) {
784       oldResolvingWrapper = ccx.SetResolvingWrapper(wrapper);
785     }
786 
787     rv = scr->Resolve(wrapper, cx, obj, id, &resolved, &retval);
788 
789     if (allowPropMods) {
790       (void)ccx.SetResolvingWrapper(oldResolvingWrapper);
791     }
792   }
793 
794   old = ccx.SetResolveName(old);
795   MOZ_ASSERT(old == id, "bad nest");
796 
797   if (NS_FAILED(rv)) {
798     return Throw(rv, cx);
799   }
800 
801   if (resolved) {
802     *resolvedp = true;
803   } else if (wrapper->HasMutatedSet()) {
804     // We are here if scriptable did not resolve this property and
805     // it *might* be in the instance set but not the proto set.
806 
807     XPCNativeSet* set = wrapper->GetSet();
808     XPCNativeSet* protoSet =
809         wrapper->HasProto() ? wrapper->GetProto()->GetSet() : nullptr;
810     XPCNativeMember* member = nullptr;
811     RefPtr<XPCNativeInterface> iface;
812     bool IsLocal = false;
813 
814     if (set->FindMember(id, &member, &iface, protoSet, &IsLocal) && IsLocal) {
815       XPCWrappedNative* wrapperForInterfaceNames =
816           (scr && scr->DontReflectInterfaceNames()) ? nullptr : wrapper;
817 
818       XPCWrappedNative* oldResolvingWrapper = ccx.SetResolvingWrapper(wrapper);
819       retval = DefinePropertyIfFound(
820           ccx, obj, id, set, iface, member, wrapper->GetScope(), false,
821           wrapperForInterfaceNames, nullptr, scr, JSPROP_ENUMERATE, resolvedp);
822       (void)ccx.SetResolvingWrapper(oldResolvingWrapper);
823     }
824   }
825 
826   return retval;
827 }
828 
829 /***************************************************************************/
830 
XPC_WN_NewEnumerate(JSContext * cx,HandleObject obj,MutableHandleIdVector properties,bool enumerableOnly)831 bool XPC_WN_NewEnumerate(JSContext* cx, HandleObject obj,
832                          MutableHandleIdVector properties,
833                          bool enumerableOnly) {
834   XPCCallContext ccx(cx, obj);
835   XPCWrappedNative* wrapper = ccx.GetWrapper();
836   THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
837 
838   nsCOMPtr<nsIXPCScriptable> scr = wrapper->GetScriptable();
839   if (!scr || !scr->WantNewEnumerate()) {
840     return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx);
841   }
842 
843   if (!XPC_WN_Shared_Enumerate(cx, obj)) {
844     return false;
845   }
846 
847   bool retval = true;
848   nsresult rv =
849       scr->NewEnumerate(wrapper, cx, obj, properties, enumerableOnly, &retval);
850   if (NS_FAILED(rv)) {
851     return Throw(rv, cx);
852   }
853   return retval;
854 }
855 
856 /***************************************************************************/
857 /***************************************************************************/
858 
859 // Compatibility hack.
860 //
861 // XPConnect used to do all sorts of funny tricks to find the "correct"
862 // |this| object for a given method (often to the detriment of proper
863 // call/apply). When these tricks were removed, a fair amount of chrome
864 // code broke, because it was relying on being able to grab methods off
865 // some XPCOM object (like the nsITelemetry service) and invoke them without
866 // a proper |this|. So, if it's quite clear that we're in this situation and
867 // about to use a |this| argument that just won't work, fix things up.
868 //
869 // This hack is only useful for getters/setters if someone sets an XPCOM object
870 // as the prototype for a vanilla JS object and expects the XPCOM attributes to
871 // work on the derived object, which we really don't want to support. But we
872 // handle it anyway, for now, to minimize regression risk on an already-risky
873 // landing.
874 //
875 // This hack is mainly useful for the NoHelper JSClass. We also fix up
876 // Components.utils because it implements nsIXPCScriptable (giving it a custom
877 // JSClass) but not nsIClassInfo (which would put the methods on a prototype).
878 
879 #define IS_NOHELPER_CLASS(clasp) (clasp == &XPC_WN_NoHelper_JSClass)
880 #define IS_CU_CLASS(clasp) \
881   (clasp->name[0] == 'n' && !strcmp(clasp->name, "nsXPCComponents_Utils"))
882 
FixUpThisIfBroken(JSObject * obj,JSObject * funobj)883 MOZ_ALWAYS_INLINE JSObject* FixUpThisIfBroken(JSObject* obj, JSObject* funobj) {
884   if (funobj) {
885     JSObject* parentObj =
886         &js::GetFunctionNativeReserved(funobj, XPC_FUNCTION_PARENT_OBJECT_SLOT)
887              .toObject();
888     const JSClass* parentClass = JS::GetClass(parentObj);
889     if (MOZ_UNLIKELY(
890             (IS_NOHELPER_CLASS(parentClass) || IS_CU_CLASS(parentClass)) &&
891             (JS::GetClass(obj) != parentClass))) {
892       return parentObj;
893     }
894   }
895   return obj;
896 }
897 
XPC_WN_CallMethod(JSContext * cx,unsigned argc,Value * vp)898 bool XPC_WN_CallMethod(JSContext* cx, unsigned argc, Value* vp) {
899   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
900   MOZ_ASSERT(JS_TypeOfValue(cx, args.calleev()) == JSTYPE_FUNCTION,
901              "bad function");
902   RootedObject funobj(cx, &args.callee());
903 
904   RootedObject obj(cx);
905   if (!args.computeThis(cx, &obj)) {
906     return false;
907   }
908 
909   obj = FixUpThisIfBroken(obj, funobj);
910   XPCCallContext ccx(cx, obj, funobj, JSID_VOIDHANDLE, args.length(),
911                      args.array(), vp);
912   XPCWrappedNative* wrapper = ccx.GetWrapper();
913   THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
914 
915   RefPtr<XPCNativeInterface> iface;
916   XPCNativeMember* member;
917 
918   if (!XPCNativeMember::GetCallInfo(funobj, &iface, &member)) {
919     return Throw(NS_ERROR_XPC_CANT_GET_METHOD_INFO, cx);
920   }
921   ccx.SetCallInfo(iface, member, false);
922   return XPCWrappedNative::CallMethod(ccx);
923 }
924 
XPC_WN_GetterSetter(JSContext * cx,unsigned argc,Value * vp)925 bool XPC_WN_GetterSetter(JSContext* cx, unsigned argc, Value* vp) {
926   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
927   MOZ_ASSERT(JS_TypeOfValue(cx, args.calleev()) == JSTYPE_FUNCTION,
928              "bad function");
929   RootedObject funobj(cx, &args.callee());
930 
931   if (!args.thisv().isObject()) {
932     JS_ReportErrorASCII(
933         cx, "xpconnect getter/setter called on incompatible non-object");
934     return false;
935   }
936   RootedObject obj(cx, &args.thisv().toObject());
937 
938   obj = FixUpThisIfBroken(obj, funobj);
939   XPCCallContext ccx(cx, obj, funobj, JSID_VOIDHANDLE, args.length(),
940                      args.array(), vp);
941   XPCWrappedNative* wrapper = ccx.GetWrapper();
942   THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
943 
944   RefPtr<XPCNativeInterface> iface;
945   XPCNativeMember* member;
946 
947   if (!XPCNativeMember::GetCallInfo(funobj, &iface, &member)) {
948     return Throw(NS_ERROR_XPC_CANT_GET_METHOD_INFO, cx);
949   }
950 
951   if (args.length() != 0 && member->IsWritableAttribute()) {
952     ccx.SetCallInfo(iface, member, true);
953     bool retval = XPCWrappedNative::SetAttribute(ccx);
954     if (retval) {
955       args.rval().set(args[0]);
956     }
957     return retval;
958   }
959   // else...
960 
961   ccx.SetCallInfo(iface, member, false);
962   return XPCWrappedNative::GetAttribute(ccx);
963 }
964 
965 /***************************************************************************/
966 
XPC_WN_Proto_Enumerate(JSContext * cx,HandleObject obj)967 static bool XPC_WN_Proto_Enumerate(JSContext* cx, HandleObject obj) {
968   MOZ_ASSERT(JS::GetClass(obj) == &XPC_WN_Proto_JSClass, "bad proto");
969   XPCWrappedNativeProto* self = (XPCWrappedNativeProto*)xpc_GetJSPrivate(obj);
970   if (!self) {
971     return false;
972   }
973 
974   XPCNativeSet* set = self->GetSet();
975   if (!set) {
976     return false;
977   }
978 
979   XPCCallContext ccx(cx);
980   if (!ccx.IsValid()) {
981     return false;
982   }
983 
984   uint16_t interface_count = set->GetInterfaceCount();
985   XPCNativeInterface** interfaceArray = set->GetInterfaceArray();
986   for (uint16_t i = 0; i < interface_count; i++) {
987     XPCNativeInterface* iface = interfaceArray[i];
988     uint16_t member_count = iface->GetMemberCount();
989 
990     for (uint16_t k = 0; k < member_count; k++) {
991       if (!xpc_ForcePropertyResolve(cx, obj,
992                                     iface->GetMemberAt(k)->GetName())) {
993         return false;
994       }
995     }
996   }
997 
998   return true;
999 }
1000 
XPC_WN_Proto_Finalize(JSFreeOp * fop,JSObject * obj)1001 static void XPC_WN_Proto_Finalize(JSFreeOp* fop, JSObject* obj) {
1002   // This can be null if xpc shutdown has already happened
1003   XPCWrappedNativeProto* p = (XPCWrappedNativeProto*)xpc_GetJSPrivate(obj);
1004   if (p) {
1005     p->JSProtoObjectFinalized(fop, obj);
1006   }
1007 }
1008 
XPC_WN_Proto_ObjectMoved(JSObject * obj,JSObject * old)1009 static size_t XPC_WN_Proto_ObjectMoved(JSObject* obj, JSObject* old) {
1010   // This can be null if xpc shutdown has already happened
1011   XPCWrappedNativeProto* p = (XPCWrappedNativeProto*)xpc_GetJSPrivate(obj);
1012   if (!p) {
1013     return 0;
1014   }
1015 
1016   p->JSProtoObjectMoved(obj, old);
1017   return 0;
1018 }
1019 
1020 /*****************************************************/
1021 
XPC_WN_OnlyIWrite_Proto_AddPropertyStub(JSContext * cx,HandleObject obj,HandleId id,HandleValue v)1022 static bool XPC_WN_OnlyIWrite_Proto_AddPropertyStub(JSContext* cx,
1023                                                     HandleObject obj,
1024                                                     HandleId id,
1025                                                     HandleValue v) {
1026   MOZ_ASSERT(JS::GetClass(obj) == &XPC_WN_Proto_JSClass, "bad proto");
1027 
1028   XPCWrappedNativeProto* self = (XPCWrappedNativeProto*)xpc_GetJSPrivate(obj);
1029   if (!self) {
1030     return false;
1031   }
1032 
1033   XPCCallContext ccx(cx);
1034   if (!ccx.IsValid()) {
1035     return false;
1036   }
1037 
1038   // Allow XPConnect to add the property only
1039   if (ccx.GetResolveName() == id) {
1040     return true;
1041   }
1042 
1043   return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx);
1044 }
1045 
XPC_WN_Proto_Resolve(JSContext * cx,HandleObject obj,HandleId id,bool * resolvedp)1046 static bool XPC_WN_Proto_Resolve(JSContext* cx, HandleObject obj, HandleId id,
1047                                  bool* resolvedp) {
1048   MOZ_ASSERT(JS::GetClass(obj) == &XPC_WN_Proto_JSClass, "bad proto");
1049 
1050   XPCWrappedNativeProto* self = (XPCWrappedNativeProto*)xpc_GetJSPrivate(obj);
1051   if (!self) {
1052     return false;
1053   }
1054 
1055   XPCCallContext ccx(cx);
1056   if (!ccx.IsValid()) {
1057     return false;
1058   }
1059 
1060   nsCOMPtr<nsIXPCScriptable> scr = self->GetScriptable();
1061 
1062   return DefinePropertyIfFound(
1063       ccx, obj, id, self->GetSet(), nullptr, nullptr, self->GetScope(), true,
1064       nullptr, nullptr, scr,
1065       JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_ENUMERATE, resolvedp);
1066 }
1067 
1068 static const JSClassOps XPC_WN_Proto_JSClassOps = {
1069     XPC_WN_OnlyIWrite_Proto_AddPropertyStub,  // addProperty
1070     XPC_WN_CannotDeletePropertyStub,          // delProperty
1071     XPC_WN_Proto_Enumerate,                   // enumerate
1072     nullptr,                                  // newEnumerate
1073     XPC_WN_Proto_Resolve,                     // resolve
1074     nullptr,                                  // mayResolve
1075     XPC_WN_Proto_Finalize,                    // finalize
1076     nullptr,                                  // call
1077     nullptr,                                  // hasInstance
1078     nullptr,                                  // construct
1079     nullptr,                                  // trace
1080 };
1081 
1082 static const js::ClassExtension XPC_WN_Proto_ClassExtension = {
1083     XPC_WN_Proto_ObjectMoved,  // objectMovedOp
1084 };
1085 
1086 const JSClass XPC_WN_Proto_JSClass = {
1087     "XPC_WN_Proto_JSClass",       XPC_WRAPPER_FLAGS,
1088     &XPC_WN_Proto_JSClassOps,     JS_NULL_CLASS_SPEC,
1089     &XPC_WN_Proto_ClassExtension, JS_NULL_OBJECT_OPS};
1090 
1091 /***************************************************************************/
1092 
XPC_WN_TearOff_Enumerate(JSContext * cx,HandleObject obj)1093 static bool XPC_WN_TearOff_Enumerate(JSContext* cx, HandleObject obj) {
1094   XPCCallContext ccx(cx, obj);
1095   XPCWrappedNative* wrapper = ccx.GetWrapper();
1096   THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
1097 
1098   XPCWrappedNativeTearOff* to = ccx.GetTearOff();
1099   XPCNativeInterface* iface;
1100 
1101   if (!to || nullptr == (iface = to->GetInterface())) {
1102     return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx);
1103   }
1104 
1105   uint16_t member_count = iface->GetMemberCount();
1106   for (uint16_t k = 0; k < member_count; k++) {
1107     if (!xpc_ForcePropertyResolve(cx, obj, iface->GetMemberAt(k)->GetName())) {
1108       return false;
1109     }
1110   }
1111 
1112   return true;
1113 }
1114 
XPC_WN_TearOff_Resolve(JSContext * cx,HandleObject obj,HandleId id,bool * resolvedp)1115 static bool XPC_WN_TearOff_Resolve(JSContext* cx, HandleObject obj, HandleId id,
1116                                    bool* resolvedp) {
1117   XPCCallContext ccx(cx, obj);
1118   XPCWrappedNative* wrapper = ccx.GetWrapper();
1119   THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
1120 
1121   XPCWrappedNativeTearOff* to = ccx.GetTearOff();
1122   XPCNativeInterface* iface;
1123 
1124   if (!to || nullptr == (iface = to->GetInterface())) {
1125     return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx);
1126   }
1127 
1128   return DefinePropertyIfFound(
1129       ccx, obj, id, nullptr, iface, nullptr, wrapper->GetScope(), true, nullptr,
1130       nullptr, nullptr, JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_ENUMERATE,
1131       resolvedp);
1132 }
1133 
XPC_WN_TearOff_Finalize(JSFreeOp * fop,JSObject * obj)1134 static void XPC_WN_TearOff_Finalize(JSFreeOp* fop, JSObject* obj) {
1135   XPCWrappedNativeTearOff* p = (XPCWrappedNativeTearOff*)xpc_GetJSPrivate(obj);
1136   if (!p) {
1137     return;
1138   }
1139   p->JSObjectFinalized();
1140 }
1141 
XPC_WN_TearOff_ObjectMoved(JSObject * obj,JSObject * old)1142 static size_t XPC_WN_TearOff_ObjectMoved(JSObject* obj, JSObject* old) {
1143   XPCWrappedNativeTearOff* p = (XPCWrappedNativeTearOff*)xpc_GetJSPrivate(obj);
1144   if (!p) {
1145     return 0;
1146   }
1147   p->JSObjectMoved(obj, old);
1148   return 0;
1149 }
1150 
1151 // Make sure XPC_WRAPPER_FLAGS has no reserved slots, so our
1152 // XPC_WN_TEAROFF_RESERVED_SLOTS value is OK.
1153 
1154 static_assert(((XPC_WRAPPER_FLAGS >> JSCLASS_RESERVED_SLOTS_SHIFT) &
1155                JSCLASS_RESERVED_SLOTS_MASK) == 0,
1156               "XPC_WRAPPER_FLAGS should not include any reserved slots");
1157 
1158 static const JSClassOps XPC_WN_Tearoff_JSClassOps = {
1159     XPC_WN_OnlyIWrite_AddPropertyStub,  // addProperty
1160     XPC_WN_CannotDeletePropertyStub,    // delProperty
1161     XPC_WN_TearOff_Enumerate,           // enumerate
1162     nullptr,                            // newEnumerate
1163     XPC_WN_TearOff_Resolve,             // resolve
1164     nullptr,                            // mayResolve
1165     XPC_WN_TearOff_Finalize,            // finalize
1166     nullptr,                            // call
1167     nullptr,                            // hasInstance
1168     nullptr,                            // construct
1169     nullptr,                            // trace
1170 };
1171 
1172 static const js::ClassExtension XPC_WN_Tearoff_JSClassExtension = {
1173     XPC_WN_TearOff_ObjectMoved,  // objectMovedOp
1174 };
1175 
1176 const JSClass XPC_WN_Tearoff_JSClass = {
1177     "WrappedNative_TearOff",
1178     XPC_WRAPPER_FLAGS |
1179         JSCLASS_HAS_RESERVED_SLOTS(XPC_WN_TEAROFF_RESERVED_SLOTS),
1180     &XPC_WN_Tearoff_JSClassOps, JS_NULL_CLASS_SPEC,
1181     &XPC_WN_Tearoff_JSClassExtension};
1182