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