Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* vim: set ts=8 sts=4 et sw=4 tw=99: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /* JavaScript JSClasses and JSOps for our Wrapped Native JS Objects. */
9 #include "xpcprivate.h"
10 #include "jsprf.h"
11 #include "mozilla/dom/BindingUtils.h"
12 #include "mozilla/Preferences.h"
14 using namespace mozilla;
15 using namespace JS;
17 /***************************************************************************/
19 // All of the exceptions thrown into JS from this file go through here.
20 // That makes this a nice place to set a breakpoint.
22 static bool Throw(nsresult errNum, JSContext* cx)
23 {
24 XPCThrower::Throw(errNum, cx);
25 return false;
26 }
28 // Handy macro used in many callback stub below.
30 #define THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper) \
31 PR_BEGIN_MACRO \
32 if (!wrapper) \
33 return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx); \
34 if (!wrapper->IsValid()) \
35 return Throw(NS_ERROR_XPC_HAS_BEEN_SHUTDOWN, cx); \
36 PR_END_MACRO
38 /***************************************************************************/
40 static bool
41 ToStringGuts(XPCCallContext& ccx)
42 {
43 char* sz;
44 XPCWrappedNative* wrapper = ccx.GetWrapper();
46 if (wrapper)
47 sz = wrapper->ToString(ccx.GetTearOff());
48 else
49 sz = JS_smprintf("[xpconnect wrapped native prototype]");
51 if (!sz) {
52 JS_ReportOutOfMemory(ccx);
53 return false;
54 }
56 JSString* str = JS_NewStringCopyZ(ccx, sz);
57 JS_smprintf_free(sz);
58 if (!str)
59 return false;
61 ccx.SetRetVal(STRING_TO_JSVAL(str));
62 return true;
63 }
65 /***************************************************************************/
67 static bool
68 XPC_WN_Shared_ToString(JSContext *cx, unsigned argc, jsval *vp)
69 {
70 CallArgs args = CallArgsFromVp(argc, vp);
71 RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
72 if (!obj)
73 return false;
75 XPCCallContext ccx(JS_CALLER, cx, obj);
76 if (!ccx.IsValid())
77 return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx);
78 ccx.SetName(ccx.GetRuntime()->GetStringID(XPCJSRuntime::IDX_TO_STRING));
79 ccx.SetArgsAndResultPtr(args.length(), args.array(), vp);
80 return ToStringGuts(ccx);
81 }
83 static bool
84 XPC_WN_Shared_ToSource(JSContext *cx, unsigned argc, jsval *vp)
85 {
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 args.rval().setString(str);
93 return true;
94 }
96 /***************************************************************************/
98 // A "double wrapped object" is a user JSObject that has been wrapped as a
99 // wrappedJS in order to be used by native code and then re-wrapped by a
100 // wrappedNative wrapper to be used by JS code. One might think of it as:
101 // wrappedNative(wrappedJS(underlying_JSObject))
102 // This is done (as opposed to just unwrapping the wrapped JS and automatically
103 // returning the underlying JSObject) so that JS callers will see what looks
104 // Like any other xpcom object - and be limited to use its interfaces.
105 //
106 // See the comment preceding nsIXPCWrappedJSObjectGetter in nsIXPConnect.idl.
108 static JSObject*
109 GetDoubleWrappedJSObject(XPCCallContext& ccx, XPCWrappedNative* wrapper)
110 {
111 RootedObject obj(ccx);
112 nsCOMPtr<nsIXPConnectWrappedJS>
113 underware = do_QueryInterface(wrapper->GetIdentityObject());
114 if (underware) {
115 RootedObject mainObj(ccx, underware->GetJSObject());
116 if (mainObj) {
117 RootedId id(ccx, ccx.GetRuntime()->
118 GetStringID(XPCJSRuntime::IDX_WRAPPED_JSOBJECT));
120 JSAutoCompartment ac(ccx, mainObj);
122 RootedValue val(ccx);
123 if (JS_GetPropertyById(ccx, mainObj, id, &val) &&
124 !JSVAL_IS_PRIMITIVE(val)) {
125 obj = JSVAL_TO_OBJECT(val);
126 }
127 }
128 }
129 return obj;
130 }
132 // This is the getter native function we use to handle 'wrappedJSObject' for
133 // double wrapped JSObjects.
135 static bool
136 XPC_WN_DoubleWrappedGetter(JSContext *cx, unsigned argc, jsval *vp)
137 {
138 CallArgs args = CallArgsFromVp(argc, vp);
140 RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
141 if (!obj)
142 return false;
144 XPCCallContext ccx(JS_CALLER, cx, obj);
145 XPCWrappedNative* wrapper = ccx.GetWrapper();
146 THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
148 MOZ_ASSERT(JS_TypeOfValue(cx, args.calleev()) == JSTYPE_FUNCTION, "bad function");
150 RootedObject realObject(cx, GetDoubleWrappedJSObject(ccx, wrapper));
151 if (!realObject) {
152 // This is pretty unexpected at this point. The object originally
153 // responded to this get property call and now gives no object.
154 // XXX Should this throw something at the caller?
155 args.rval().setNull();
156 return true;
157 }
159 // It is a double wrapped object. This should really never appear in
160 // content these days, but addons still do it - see bug 965921.
161 if (MOZ_UNLIKELY(!nsContentUtils::IsCallerChrome())) {
162 JS_ReportError(cx, "Attempt to use .wrappedJSObject in untrusted code");
163 return false;
164 }
165 args.rval().setObject(*realObject);
166 return JS_WrapValue(cx, args.rval());
167 }
169 /***************************************************************************/
171 // This is our shared function to define properties on our JSObjects.
173 /*
174 * NOTE:
175 * We *never* set the tearoff names (e.g. nsIFoo) as JS_ENUMERATE.
176 * We *never* set toString or toSource as JS_ENUMERATE.
177 */
179 static bool
180 DefinePropertyIfFound(XPCCallContext& ccx,
181 HandleObject obj,
182 HandleId idArg,
183 XPCNativeSet* set,
184 XPCNativeInterface* iface,
185 XPCNativeMember* member,
186 XPCWrappedNativeScope* scope,
187 bool reflectToStringAndToSource,
188 XPCWrappedNative* wrapperToReflectInterfaceNames,
189 XPCWrappedNative* wrapperToReflectDoubleWrap,
190 XPCNativeScriptableInfo* scriptableInfo,
191 unsigned propFlags,
192 bool* resolved)
193 {
194 RootedId id(ccx, idArg);
195 XPCJSRuntime* rt = ccx.GetRuntime();
196 bool found;
197 const char* name;
199 if (set) {
200 if (iface)
201 found = true;
202 else
203 found = set->FindMember(id, &member, &iface);
204 } else
205 found = (nullptr != (member = iface->FindMember(id)));
207 if (!found) {
208 if (reflectToStringAndToSource) {
209 JSNative call;
210 uint32_t flags = 0;
212 if (scriptableInfo) {
213 nsCOMPtr<nsIClassInfo> classInfo = do_QueryInterface(
214 scriptableInfo->GetCallback());
216 if (classInfo) {
217 nsresult rv = classInfo->GetFlags(&flags);
218 if (NS_FAILED(rv))
219 return Throw(rv, ccx);
220 }
221 }
223 bool overwriteToString = !(flags & nsIClassInfo::DOM_OBJECT)
224 || Preferences::GetBool("dom.XPCToStringForDOMClasses", false);
226 if(id == rt->GetStringID(XPCJSRuntime::IDX_TO_STRING)
227 && overwriteToString)
228 {
229 call = XPC_WN_Shared_ToString;
230 name = rt->GetStringName(XPCJSRuntime::IDX_TO_STRING);
231 id = rt->GetStringID(XPCJSRuntime::IDX_TO_STRING);
232 } else if (id == rt->GetStringID(XPCJSRuntime::IDX_TO_SOURCE)) {
233 call = XPC_WN_Shared_ToSource;
234 name = rt->GetStringName(XPCJSRuntime::IDX_TO_SOURCE);
235 id = rt->GetStringID(XPCJSRuntime::IDX_TO_SOURCE);
236 }
238 else
239 call = nullptr;
241 if (call) {
242 RootedFunction fun(ccx, JS_NewFunction(ccx, call, 0, 0, obj, name));
243 if (!fun) {
244 JS_ReportOutOfMemory(ccx);
245 return false;
246 }
248 AutoResolveName arn(ccx, id);
249 if (resolved)
250 *resolved = true;
251 return JS_DefinePropertyById(ccx, obj, id,
252 OBJECT_TO_JSVAL(JS_GetFunctionObject(fun)),
253 nullptr, nullptr,
254 propFlags & ~JSPROP_ENUMERATE);
255 }
256 }
257 // This *might* be a tearoff name that is not yet part of our
258 // set. Let's lookup the name and see if it is the name of an
259 // interface. Then we'll see if the object actually *does* this
260 // interface and add a tearoff as necessary.
262 if (wrapperToReflectInterfaceNames) {
263 JSAutoByteString name;
264 AutoMarkingNativeInterfacePtr iface2(ccx);
265 XPCWrappedNativeTearOff* to;
266 RootedObject jso(ccx);
267 nsresult rv = NS_OK;
269 if (JSID_IS_STRING(id) &&
270 name.encodeLatin1(ccx, JSID_TO_STRING(id)) &&
271 (iface2 = XPCNativeInterface::GetNewOrUsed(name.ptr()), iface2) &&
272 nullptr != (to = wrapperToReflectInterfaceNames->
273 FindTearOff(iface2, true, &rv)) &&
274 nullptr != (jso = to->GetJSObject()))
276 {
277 AutoResolveName arn(ccx, id);
278 if (resolved)
279 *resolved = true;
280 return JS_DefinePropertyById(ccx, obj, id, OBJECT_TO_JSVAL(jso),
281 nullptr, nullptr,
282 propFlags & ~JSPROP_ENUMERATE);
283 } else if (NS_FAILED(rv) && rv != NS_ERROR_NO_INTERFACE) {
284 return Throw(rv, ccx);
285 }
286 }
288 // This *might* be a double wrapped JSObject
289 if (wrapperToReflectDoubleWrap &&
290 id == rt->GetStringID(XPCJSRuntime::IDX_WRAPPED_JSOBJECT) &&
291 GetDoubleWrappedJSObject(ccx, wrapperToReflectDoubleWrap)) {
292 // We build and add a getter function.
293 // A security check is done on a per-get basis.
295 JSFunction* fun;
297 id = rt->GetStringID(XPCJSRuntime::IDX_WRAPPED_JSOBJECT);
298 name = rt->GetStringName(XPCJSRuntime::IDX_WRAPPED_JSOBJECT);
300 fun = JS_NewFunction(ccx, XPC_WN_DoubleWrappedGetter,
301 0, 0, obj, name);
303 if (!fun)
304 return false;
306 RootedObject funobj(ccx, JS_GetFunctionObject(fun));
307 if (!funobj)
308 return false;
310 propFlags |= JSPROP_GETTER | JSPROP_SHARED;
311 propFlags &= ~JSPROP_ENUMERATE;
313 AutoResolveName arn(ccx, id);
314 if (resolved)
315 *resolved = true;
316 return JS_DefinePropertyById(ccx, obj, id, JSVAL_VOID,
317 JS_DATA_TO_FUNC_PTR(JSPropertyOp,
318 funobj.get()),
319 nullptr, propFlags);
320 }
322 if (resolved)
323 *resolved = false;
324 return true;
325 }
327 if (!member) {
328 if (wrapperToReflectInterfaceNames) {
329 XPCWrappedNativeTearOff* to =
330 wrapperToReflectInterfaceNames->FindTearOff(iface, true);
332 if (!to)
333 return false;
334 RootedObject jso(ccx, to->GetJSObject());
335 if (!jso)
336 return false;
338 AutoResolveName arn(ccx, id);
339 if (resolved)
340 *resolved = true;
341 return JS_DefinePropertyById(ccx, obj, id, OBJECT_TO_JSVAL(jso),
342 nullptr, nullptr,
343 propFlags & ~JSPROP_ENUMERATE);
344 }
345 if (resolved)
346 *resolved = false;
347 return true;
348 }
350 if (member->IsConstant()) {
351 RootedValue val(ccx);
352 AutoResolveName arn(ccx, id);
353 if (resolved)
354 *resolved = true;
355 return member->GetConstantValue(ccx, iface, val.address()) &&
356 JS_DefinePropertyById(ccx, obj, id, val, nullptr, nullptr,
357 propFlags);
358 }
360 if (id == rt->GetStringID(XPCJSRuntime::IDX_TO_STRING) ||
361 id == rt->GetStringID(XPCJSRuntime::IDX_TO_SOURCE) ||
362 (scriptableInfo &&
363 scriptableInfo->GetFlags().DontEnumQueryInterface() &&
364 id == rt->GetStringID(XPCJSRuntime::IDX_QUERY_INTERFACE)))
365 propFlags &= ~JSPROP_ENUMERATE;
367 RootedValue funval(ccx);
368 if (!member->NewFunctionObject(ccx, iface, obj, funval.address()))
369 return false;
371 if (member->IsMethod()) {
372 AutoResolveName arn(ccx, id);
373 if (resolved)
374 *resolved = true;
375 return JS_DefinePropertyById(ccx, obj, id, funval, nullptr, nullptr,
376 propFlags);
377 }
379 // else...
381 MOZ_ASSERT(member->IsAttribute(), "way broken!");
383 propFlags |= JSPROP_GETTER | JSPROP_SHARED;
384 JSObject* funobj = JSVAL_TO_OBJECT(funval);
385 JSPropertyOp getter = JS_DATA_TO_FUNC_PTR(JSPropertyOp, funobj);
386 JSStrictPropertyOp setter;
387 if (member->IsWritableAttribute()) {
388 propFlags |= JSPROP_SETTER;
389 propFlags &= ~JSPROP_READONLY;
390 setter = JS_DATA_TO_FUNC_PTR(JSStrictPropertyOp, funobj);
391 } else {
392 setter = js_GetterOnlyPropertyStub;
393 }
395 AutoResolveName arn(ccx, id);
396 if (resolved)
397 *resolved = true;
399 return JS_DefinePropertyById(ccx, obj, id, JSVAL_VOID, getter, setter,
400 propFlags);
401 }
403 /***************************************************************************/
404 /***************************************************************************/
406 static bool
407 XPC_WN_OnlyIWrite_AddPropertyStub(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp)
408 {
409 XPCCallContext ccx(JS_CALLER, cx, obj, NullPtr(), id);
410 XPCWrappedNative* wrapper = ccx.GetWrapper();
411 THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
413 // Allow only XPConnect to add/set the property
414 if (ccx.GetResolveName() == id)
415 return true;
417 return Throw(NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN, cx);
418 }
420 static bool
421 XPC_WN_OnlyIWrite_SetPropertyStub(JSContext *cx, HandleObject obj, HandleId id, bool strict,
422 MutableHandleValue vp)
423 {
424 return XPC_WN_OnlyIWrite_AddPropertyStub(cx, obj, id, vp);
425 }
427 static bool
428 XPC_WN_CannotModifyPropertyStub(JSContext *cx, HandleObject obj, HandleId id,
429 MutableHandleValue vp)
430 {
431 return Throw(NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN, cx);
432 }
434 static bool
435 XPC_WN_CantDeletePropertyStub(JSContext *cx, HandleObject obj, HandleId id,
436 bool *succeeded)
437 {
438 return Throw(NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN, cx);
439 }
441 static bool
442 XPC_WN_CannotModifyStrictPropertyStub(JSContext *cx, HandleObject obj, HandleId id, bool strict,
443 MutableHandleValue vp)
444 {
445 return XPC_WN_CannotModifyPropertyStub(cx, obj, id, vp);
446 }
448 static bool
449 XPC_WN_Shared_Convert(JSContext *cx, HandleObject obj, JSType type, MutableHandleValue vp)
450 {
451 if (type == JSTYPE_OBJECT) {
452 vp.set(OBJECT_TO_JSVAL(obj));
453 return true;
454 }
456 XPCCallContext ccx(JS_CALLER, cx, obj);
457 XPCWrappedNative* wrapper = ccx.GetWrapper();
458 THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
460 switch (type) {
461 case JSTYPE_FUNCTION:
462 {
463 if (!ccx.GetTearOff()) {
464 XPCNativeScriptableInfo* si = wrapper->GetScriptableInfo();
465 if (si && (si->GetFlags().WantCall() ||
466 si->GetFlags().WantConstruct())) {
467 vp.set(OBJECT_TO_JSVAL(obj));
468 return true;
469 }
470 }
471 }
472 return Throw(NS_ERROR_XPC_CANT_CONVERT_WN_TO_FUN, cx);
473 case JSTYPE_NUMBER:
474 vp.set(JS_GetNaNValue(cx));
475 return true;
476 case JSTYPE_BOOLEAN:
477 vp.set(JSVAL_TRUE);
478 return true;
479 case JSTYPE_VOID:
480 case JSTYPE_STRING:
481 {
482 ccx.SetName(ccx.GetRuntime()->GetStringID(XPCJSRuntime::IDX_TO_STRING));
483 ccx.SetArgsAndResultPtr(0, nullptr, vp.address());
485 XPCNativeMember* member = ccx.GetMember();
486 if (member && member->IsMethod()) {
487 if (!XPCWrappedNative::CallMethod(ccx))
488 return false;
490 if (JSVAL_IS_PRIMITIVE(vp))
491 return true;
492 }
494 // else...
495 return ToStringGuts(ccx);
496 }
497 default:
498 NS_ERROR("bad type in conversion");
499 return false;
500 }
501 NS_NOTREACHED("huh?");
502 return false;
503 }
505 static bool
506 XPC_WN_Shared_Enumerate(JSContext *cx, HandleObject obj)
507 {
508 XPCCallContext ccx(JS_CALLER, cx, obj);
509 XPCWrappedNative* wrapper = ccx.GetWrapper();
510 THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
512 // Since we aren't going to enumerate tearoff names and the prototype
513 // handles non-mutated members, we can do this potential short-circuit.
514 if (!wrapper->HasMutatedSet())
515 return true;
517 XPCNativeSet* set = wrapper->GetSet();
518 XPCNativeSet* protoSet = wrapper->HasProto() ?
519 wrapper->GetProto()->GetSet() : nullptr;
521 uint16_t interface_count = set->GetInterfaceCount();
522 XPCNativeInterface** interfaceArray = set->GetInterfaceArray();
523 for (uint16_t i = 0; i < interface_count; i++) {
524 XPCNativeInterface* iface = interfaceArray[i];
525 uint16_t member_count = iface->GetMemberCount();
526 for (uint16_t k = 0; k < member_count; k++) {
527 XPCNativeMember* member = iface->GetMemberAt(k);
528 jsid name = member->GetName();
530 // Skip if this member is going to come from the proto.
531 uint16_t index;
532 if (protoSet &&
533 protoSet->FindMember(name, nullptr, &index) && index == i)
534 continue;
535 if (!xpc_ForcePropertyResolve(cx, obj, name))
536 return false;
537 }
538 }
539 return true;
540 }
542 /***************************************************************************/
544 enum WNHelperType {
545 WN_NOHELPER,
546 WN_HELPER
547 };
549 static void
550 WrappedNativeFinalize(js::FreeOp *fop, JSObject *obj, WNHelperType helperType)
551 {
552 const js::Class* clazz = js::GetObjectClass(obj);
553 if (clazz->flags & JSCLASS_DOM_GLOBAL) {
554 mozilla::dom::DestroyProtoAndIfaceCache(obj);
555 }
556 nsISupports* p = static_cast<nsISupports*>(xpc_GetJSPrivate(obj));
557 if (!p)
558 return;
560 XPCWrappedNative* wrapper = static_cast<XPCWrappedNative*>(p);
561 if (helperType == WN_HELPER)
562 wrapper->GetScriptableCallback()->Finalize(wrapper, js::CastToJSFreeOp(fop), obj);
563 wrapper->FlatJSObjectFinalized();
564 }
566 static void
567 XPC_WN_NoHelper_Finalize(js::FreeOp *fop, JSObject *obj)
568 {
569 WrappedNativeFinalize(fop, obj, WN_NOHELPER);
570 }
572 /*
573 * General comment about XPConnect tracing: Given a C++ object |wrapper| and its
574 * corresponding JS object |obj|, calling |wrapper->TraceSelf| will ask the JS
575 * engine to mark |obj|. Eventually, this will lead to the trace hook being
576 * called for |obj|. The trace hook should call |wrapper->TraceInside|, which
577 * should mark any JS objects held by |wrapper| as members.
578 */
580 static void
581 MarkWrappedNative(JSTracer *trc, JSObject *obj)
582 {
583 const js::Class* clazz = js::GetObjectClass(obj);
584 if (clazz->flags & JSCLASS_DOM_GLOBAL) {
585 mozilla::dom::TraceProtoAndIfaceCache(trc, obj);
586 }
587 MOZ_ASSERT(IS_WN_CLASS(clazz));
589 XPCWrappedNative *wrapper = XPCWrappedNative::Get(obj);
590 if (wrapper && wrapper->IsValid())
591 wrapper->TraceInside(trc);
592 }
594 /* static */ void
595 XPCWrappedNative::Trace(JSTracer *trc, JSObject *obj)
596 {
597 MarkWrappedNative(trc, obj);
598 }
600 static bool
601 XPC_WN_NoHelper_Resolve(JSContext *cx, HandleObject obj, HandleId id)
602 {
603 XPCCallContext ccx(JS_CALLER, cx, obj, NullPtr(), id);
604 XPCWrappedNative* wrapper = ccx.GetWrapper();
605 THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
607 XPCNativeSet* set = ccx.GetSet();
608 if (!set)
609 return true;
611 // Don't resolve properties that are on our prototype.
612 if (ccx.GetInterface() && !ccx.GetStaticMemberIsLocal())
613 return true;
615 return DefinePropertyIfFound(ccx, obj, id,
616 set, nullptr, nullptr, wrapper->GetScope(),
617 true, wrapper, wrapper, nullptr,
618 JSPROP_ENUMERATE |
619 JSPROP_READONLY |
620 JSPROP_PERMANENT, nullptr);
621 }
623 static JSObject *
624 XPC_WN_OuterObject(JSContext *cx, HandleObject objArg)
625 {
626 JSObject *obj = objArg;
628 XPCWrappedNative *wrapper = XPCWrappedNative::Get(obj);
629 if (!wrapper) {
630 Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx);
632 return nullptr;
633 }
635 if (!wrapper->IsValid()) {
636 Throw(NS_ERROR_XPC_HAS_BEEN_SHUTDOWN, cx);
638 return nullptr;
639 }
641 XPCNativeScriptableInfo* si = wrapper->GetScriptableInfo();
642 if (si && si->GetFlags().WantOuterObject()) {
643 RootedObject newThis(cx);
644 nsresult rv =
645 si->GetCallback()->OuterObject(wrapper, cx, obj, newThis.address());
647 if (NS_FAILED(rv)) {
648 Throw(rv, cx);
650 return nullptr;
651 }
653 obj = newThis;
654 }
656 return obj;
657 }
659 const XPCWrappedNativeJSClass XPC_WN_NoHelper_JSClass = {
660 { // base
661 "XPCWrappedNative_NoHelper", // name;
662 WRAPPER_SLOTS |
663 JSCLASS_PRIVATE_IS_NSISUPPORTS, // flags
665 /* Mandatory non-null function pointer members. */
666 XPC_WN_OnlyIWrite_AddPropertyStub, // addProperty
667 XPC_WN_CantDeletePropertyStub, // delProperty
668 JS_PropertyStub, // getProperty
669 XPC_WN_OnlyIWrite_SetPropertyStub, // setProperty
671 XPC_WN_Shared_Enumerate, // enumerate
672 XPC_WN_NoHelper_Resolve, // resolve
673 XPC_WN_Shared_Convert, // convert
674 XPC_WN_NoHelper_Finalize, // finalize
676 /* Optionally non-null members start here. */
677 nullptr, // call
678 nullptr, // construct
679 nullptr, // hasInstance
680 XPCWrappedNative::Trace, // trace
681 JS_NULL_CLASS_SPEC,
683 // ClassExtension
684 {
685 nullptr, // outerObject
686 nullptr, // innerObject
687 nullptr, // iteratorObject
688 true, // isWrappedNative
689 },
691 // ObjectOps
692 {
693 nullptr, // lookupGeneric
694 nullptr, // lookupProperty
695 nullptr, // lookupElement
696 nullptr, // defineGeneric
697 nullptr, // defineProperty
698 nullptr, // defineElement
699 nullptr, // getGeneric
700 nullptr, // getProperty
701 nullptr, // getElement
702 nullptr, // setGeneric
703 nullptr, // setProperty
704 nullptr, // setElement
705 nullptr, // getGenericAttributes
706 nullptr, // setGenericAttributes
707 nullptr, // deleteProperty
708 nullptr, // deleteElement
709 nullptr, nullptr, // watch/unwatch
710 nullptr, // slice
711 XPC_WN_JSOp_Enumerate,
712 XPC_WN_JSOp_ThisObject,
713 }
714 },
715 0 // interfacesBitmap
716 };
719 /***************************************************************************/
721 static bool
722 XPC_WN_MaybeResolvingPropertyStub(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp)
723 {
724 XPCCallContext ccx(JS_CALLER, cx, obj);
725 XPCWrappedNative* wrapper = ccx.GetWrapper();
726 THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
728 if (ccx.GetResolvingWrapper() == wrapper)
729 return true;
730 return Throw(NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN, cx);
731 }
733 static bool
734 XPC_WN_MaybeResolvingStrictPropertyStub(JSContext *cx, HandleObject obj, HandleId id, bool strict,
735 MutableHandleValue vp)
736 {
737 return XPC_WN_MaybeResolvingPropertyStub(cx, obj, id, vp);
738 }
740 static bool
741 XPC_WN_MaybeResolvingDeletePropertyStub(JSContext *cx, HandleObject obj, HandleId id, bool *succeeded)
742 {
743 XPCCallContext ccx(JS_CALLER, cx, obj);
744 XPCWrappedNative* wrapper = ccx.GetWrapper();
745 THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
747 if (ccx.GetResolvingWrapper() == wrapper) {
748 *succeeded = true;
749 return true;
750 }
751 return Throw(NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN, cx);
752 }
754 // macro fun!
755 #define PRE_HELPER_STUB \
756 JSObject *unwrapped = js::CheckedUnwrap(obj, false); \
757 if (!unwrapped) { \
758 JS_ReportError(cx, "Permission denied to operate on object."); \
759 return false; \
760 } \
761 if (!IS_WN_REFLECTOR(unwrapped)) { \
762 return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx); \
763 } \
764 XPCWrappedNative *wrapper = XPCWrappedNative::Get(unwrapped); \
765 THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper); \
766 bool retval = true; \
767 nsresult rv = wrapper->GetScriptableCallback()->
769 #define POST_HELPER_STUB \
770 if (NS_FAILED(rv)) \
771 return Throw(rv, cx); \
772 return retval;
774 static bool
775 XPC_WN_Helper_AddProperty(JSContext *cx, HandleObject obj, HandleId id,
776 MutableHandleValue vp)
777 {
778 PRE_HELPER_STUB
779 AddProperty(wrapper, cx, obj, id, vp.address(), &retval);
780 POST_HELPER_STUB
781 }
783 static bool
784 XPC_WN_Helper_DelProperty(JSContext *cx, HandleObject obj, HandleId id,
785 bool *succeeded)
786 {
787 *succeeded = true;
788 PRE_HELPER_STUB
789 DelProperty(wrapper, cx, obj, id, &retval);
790 POST_HELPER_STUB
791 }
793 bool
794 XPC_WN_Helper_GetProperty(JSContext *cx, HandleObject obj, HandleId id,
795 MutableHandleValue vp)
796 {
797 PRE_HELPER_STUB
798 GetProperty(wrapper, cx, obj, id, vp.address(), &retval);
799 POST_HELPER_STUB
800 }
802 bool
803 XPC_WN_Helper_SetProperty(JSContext *cx, HandleObject obj, HandleId id, bool strict,
804 MutableHandleValue vp)
805 {
806 PRE_HELPER_STUB
807 SetProperty(wrapper, cx, obj, id, vp.address(), &retval);
808 POST_HELPER_STUB
809 }
811 static bool
812 XPC_WN_Helper_Convert(JSContext *cx, HandleObject obj, JSType type, MutableHandleValue vp)
813 {
814 PRE_HELPER_STUB
815 Convert(wrapper, cx, obj, type, vp.address(), &retval);
816 POST_HELPER_STUB
817 }
819 static bool
820 XPC_WN_Helper_Call(JSContext *cx, unsigned argc, jsval *vp)
821 {
822 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
823 // N.B. we want obj to be the callee, not JS_THIS(cx, vp)
824 RootedObject obj(cx, &args.callee());
826 XPCCallContext ccx(JS_CALLER, cx, obj, NullPtr(), JSID_VOIDHANDLE, args.length(),
827 args.array(), args.rval().address());
828 if (!ccx.IsValid())
829 return false;
831 MOZ_ASSERT(obj == ccx.GetFlattenedJSObject());
833 PRE_HELPER_STUB
834 Call(wrapper, cx, obj, args, &retval);
835 POST_HELPER_STUB
836 }
838 static bool
839 XPC_WN_Helper_Construct(JSContext *cx, unsigned argc, jsval *vp)
840 {
841 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
842 RootedObject obj(cx, &args.callee());
843 if (!obj)
844 return false;
846 XPCCallContext ccx(JS_CALLER, cx, obj, NullPtr(), JSID_VOIDHANDLE, args.length(),
847 args.array(), args.rval().address());
848 if (!ccx.IsValid())
849 return false;
851 MOZ_ASSERT(obj == ccx.GetFlattenedJSObject());
853 PRE_HELPER_STUB
854 Construct(wrapper, cx, obj, args, &retval);
855 POST_HELPER_STUB
856 }
858 static bool
859 XPC_WN_Helper_HasInstance(JSContext *cx, HandleObject obj, MutableHandleValue valp, bool *bp)
860 {
861 bool retval2;
862 PRE_HELPER_STUB
863 HasInstance(wrapper, cx, obj, valp, &retval2, &retval);
864 *bp = retval2;
865 POST_HELPER_STUB
866 }
868 static void
869 XPC_WN_Helper_Finalize(js::FreeOp *fop, JSObject *obj)
870 {
871 WrappedNativeFinalize(fop, obj, WN_HELPER);
872 }
874 static bool
875 XPC_WN_Helper_NewResolve(JSContext *cx, HandleObject obj, HandleId id,
876 MutableHandleObject objp)
877 {
878 nsresult rv = NS_OK;
879 bool retval = true;
880 RootedObject obj2FromScriptable(cx);
881 XPCCallContext ccx(JS_CALLER, cx, obj);
882 XPCWrappedNative* wrapper = ccx.GetWrapper();
883 THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
885 RootedId old(cx, ccx.SetResolveName(id));
887 XPCNativeScriptableInfo* si = wrapper->GetScriptableInfo();
888 if (si && si->GetFlags().WantNewResolve()) {
889 XPCWrappedNative* oldResolvingWrapper;
890 bool allowPropMods = si->GetFlags().AllowPropModsDuringResolve();
892 if (allowPropMods)
893 oldResolvingWrapper = ccx.SetResolvingWrapper(wrapper);
895 rv = si->GetCallback()->NewResolve(wrapper, cx, obj, id,
896 obj2FromScriptable.address(), &retval);
898 if (allowPropMods)
899 (void)ccx.SetResolvingWrapper(oldResolvingWrapper);
900 }
902 old = ccx.SetResolveName(old);
903 MOZ_ASSERT(old == id, "bad nest");
905 if (NS_FAILED(rv)) {
906 return Throw(rv, cx);
907 }
909 if (obj2FromScriptable) {
910 objp.set(obj2FromScriptable);
911 } else if (wrapper->HasMutatedSet()) {
912 // We are here if scriptable did not resolve this property and
913 // it *might* be in the instance set but not the proto set.
915 XPCNativeSet* set = wrapper->GetSet();
916 XPCNativeSet* protoSet = wrapper->HasProto() ?
917 wrapper->GetProto()->GetSet() : nullptr;
918 XPCNativeMember* member;
919 XPCNativeInterface* iface;
920 bool IsLocal;
922 if (set->FindMember(id, &member, &iface, protoSet, &IsLocal) &&
923 IsLocal) {
924 XPCWrappedNative* oldResolvingWrapper;
926 XPCNativeScriptableFlags siFlags(0);
927 if (si)
928 siFlags = si->GetFlags();
930 unsigned enumFlag =
931 siFlags.DontEnumStaticProps() ? 0 : JSPROP_ENUMERATE;
933 XPCWrappedNative* wrapperForInterfaceNames =
934 siFlags.DontReflectInterfaceNames() ? nullptr : wrapper;
936 bool resolved;
937 oldResolvingWrapper = ccx.SetResolvingWrapper(wrapper);
938 retval = DefinePropertyIfFound(ccx, obj, id,
939 set, iface, member,
940 wrapper->GetScope(),
941 false,
942 wrapperForInterfaceNames,
943 nullptr, si,
944 enumFlag, &resolved);
945 (void)ccx.SetResolvingWrapper(oldResolvingWrapper);
946 if (retval && resolved)
947 objp.set(obj);
948 }
949 }
951 return retval;
952 }
954 /***************************************************************************/
956 /*
957 Here are the enumerator cases:
959 set jsclass enumerate to stub (unless noted otherwise)
961 if ( helper wants new enumerate )
962 if ( DONT_ENUM_STATICS )
963 forward to scriptable enumerate
964 else
965 if ( set not mutated )
966 forward to scriptable enumerate
967 else
968 call shared enumerate
969 forward to scriptable enumerate
970 else if ( helper wants old enumerate )
971 use this JSOp
972 if ( DONT_ENUM_STATICS )
973 call scriptable enumerate
974 call stub
975 else
976 if ( set not mutated )
977 call scriptable enumerate
978 call stub
979 else
980 call shared enumerate
981 call scriptable enumerate
982 call stub
984 else //... if ( helper wants NO enumerate )
985 if ( DONT_ENUM_STATICS )
986 use enumerate stub - don't use this JSOp thing at all
987 else
988 do shared enumerate - don't use this JSOp thing at all
989 */
991 bool
992 XPC_WN_JSOp_Enumerate(JSContext *cx, HandleObject obj, JSIterateOp enum_op,
993 MutableHandleValue statep, MutableHandleId idp)
994 {
995 const js::Class *clazz = js::GetObjectClass(obj);
996 if (!IS_WN_CLASS(clazz) || clazz == &XPC_WN_NoHelper_JSClass.base) {
997 // obj must be a prototype object or a wrapper w/o a
998 // helper. Short circuit this call to the default
999 // implementation.
1001 return JS_EnumerateState(cx, obj, enum_op, statep, idp);
1002 }
1004 XPCCallContext ccx(JS_CALLER, cx, obj);
1005 XPCWrappedNative* wrapper = ccx.GetWrapper();
1006 THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
1008 XPCNativeScriptableInfo* si = wrapper->GetScriptableInfo();
1009 if (!si)
1010 return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx);
1012 bool retval = true;
1013 nsresult rv;
1015 if (si->GetFlags().WantNewEnumerate()) {
1016 if (((enum_op == JSENUMERATE_INIT &&
1017 !si->GetFlags().DontEnumStaticProps()) ||
1018 enum_op == JSENUMERATE_INIT_ALL) &&
1019 wrapper->HasMutatedSet() &&
1020 !XPC_WN_Shared_Enumerate(cx, obj)) {
1021 statep.set(JSVAL_NULL);
1022 return false;
1023 }
1025 // XXX Might we really need to wrap this call and *also* call
1026 // js_ObjectOps.enumerate ???
1028 rv = si->GetCallback()->
1029 NewEnumerate(wrapper, cx, obj, enum_op, statep.address(), idp.address(), &retval);
1031 if ((enum_op == JSENUMERATE_INIT || enum_op == JSENUMERATE_INIT_ALL) &&
1032 (NS_FAILED(rv) || !retval)) {
1033 statep.set(JSVAL_NULL);
1034 }
1036 if (NS_FAILED(rv))
1037 return Throw(rv, cx);
1038 return retval;
1039 }
1041 if (si->GetFlags().WantEnumerate()) {
1042 if (enum_op == JSENUMERATE_INIT || enum_op == JSENUMERATE_INIT_ALL) {
1043 if ((enum_op == JSENUMERATE_INIT_ALL ||
1044 !si->GetFlags().DontEnumStaticProps()) &&
1045 wrapper->HasMutatedSet() &&
1046 !XPC_WN_Shared_Enumerate(cx, obj)) {
1047 statep.set(JSVAL_NULL);
1048 return false;
1049 }
1050 rv = si->GetCallback()->
1051 Enumerate(wrapper, cx, obj, &retval);
1053 if (NS_FAILED(rv) || !retval)
1054 statep.set(JSVAL_NULL);
1056 if (NS_FAILED(rv))
1057 return Throw(rv, cx);
1058 if (!retval)
1059 return false;
1060 // Then fall through and call the default implementation...
1061 }
1062 }
1064 // else call js_ObjectOps.enumerate...
1066 return JS_EnumerateState(cx, obj, enum_op, statep, idp);
1067 }
1069 JSObject*
1070 XPC_WN_JSOp_ThisObject(JSContext *cx, HandleObject obj)
1071 {
1072 return JS_ObjectToOuterObject(cx, obj);
1073 }
1075 /***************************************************************************/
1077 // static
1078 XPCNativeScriptableInfo*
1079 XPCNativeScriptableInfo::Construct(const XPCNativeScriptableCreateInfo* sci)
1080 {
1081 MOZ_ASSERT(sci, "bad param");
1082 MOZ_ASSERT(sci->GetCallback(), "bad param");
1084 XPCNativeScriptableInfo* newObj =
1085 new XPCNativeScriptableInfo(sci->GetCallback());
1086 if (!newObj)
1087 return nullptr;
1089 char* name = nullptr;
1090 if (NS_FAILED(sci->GetCallback()->GetClassName(&name)) || !name) {
1091 delete newObj;
1092 return nullptr;
1093 }
1095 bool success;
1097 XPCJSRuntime* rt = XPCJSRuntime::Get();
1098 XPCNativeScriptableSharedMap* map = rt->GetNativeScriptableSharedMap();
1099 success = map->GetNewOrUsed(sci->GetFlags(), name,
1100 sci->GetInterfacesBitmap(), newObj);
1102 if (!success) {
1103 delete newObj;
1104 return nullptr;
1105 }
1107 return newObj;
1108 }
1110 void
1111 XPCNativeScriptableShared::PopulateJSClass()
1112 {
1113 MOZ_ASSERT(mJSClass.base.name, "bad state!");
1115 mJSClass.base.flags = WRAPPER_SLOTS |
1116 JSCLASS_PRIVATE_IS_NSISUPPORTS |
1117 JSCLASS_NEW_RESOLVE;
1119 if (mFlags.IsGlobalObject())
1120 mJSClass.base.flags |= XPCONNECT_GLOBAL_FLAGS;
1122 JSPropertyOp addProperty;
1123 if (mFlags.WantAddProperty())
1124 addProperty = XPC_WN_Helper_AddProperty;
1125 else if (mFlags.UseJSStubForAddProperty())
1126 addProperty = JS_PropertyStub;
1127 else if (mFlags.AllowPropModsDuringResolve())
1128 addProperty = XPC_WN_MaybeResolvingPropertyStub;
1129 else
1130 addProperty = XPC_WN_CannotModifyPropertyStub;
1131 mJSClass.base.addProperty = addProperty;
1133 JSDeletePropertyOp delProperty;
1134 if (mFlags.WantDelProperty())
1135 delProperty = XPC_WN_Helper_DelProperty;
1136 else if (mFlags.UseJSStubForDelProperty())
1137 delProperty = JS_DeletePropertyStub;
1138 else if (mFlags.AllowPropModsDuringResolve())
1139 delProperty = XPC_WN_MaybeResolvingDeletePropertyStub;
1140 else
1141 delProperty = XPC_WN_CantDeletePropertyStub;
1142 mJSClass.base.delProperty = delProperty;
1144 if (mFlags.WantGetProperty())
1145 mJSClass.base.getProperty = XPC_WN_Helper_GetProperty;
1146 else
1147 mJSClass.base.getProperty = JS_PropertyStub;
1149 JSStrictPropertyOp setProperty;
1150 if (mFlags.WantSetProperty())
1151 setProperty = XPC_WN_Helper_SetProperty;
1152 else if (mFlags.UseJSStubForSetProperty())
1153 setProperty = JS_StrictPropertyStub;
1154 else if (mFlags.AllowPropModsDuringResolve())
1155 setProperty = XPC_WN_MaybeResolvingStrictPropertyStub;
1156 else
1157 setProperty = XPC_WN_CannotModifyStrictPropertyStub;
1158 mJSClass.base.setProperty = setProperty;
1160 // We figure out most of the enumerate strategy at call time.
1162 if (mFlags.WantNewEnumerate() || mFlags.WantEnumerate() ||
1163 mFlags.DontEnumStaticProps())
1164 mJSClass.base.enumerate = JS_EnumerateStub;
1165 else
1166 mJSClass.base.enumerate = XPC_WN_Shared_Enumerate;
1168 // We have to figure out resolve strategy at call time
1169 mJSClass.base.resolve = (JSResolveOp) XPC_WN_Helper_NewResolve;
1171 // We need to respect content-defined toString() hooks on Window objects.
1172 // In particular, js::DefaultValue checks for a convert stub, and the one
1173 // we would install below ignores anything implemented in JS.
1174 //
1175 // We've always had this behavior for most XPCWrappedNative-implemented
1176 // objects. However, Window was special, because the outer-window proxy
1177 // had a null convert hook, which means that we'd end up with the default
1178 // JS-engine behavior (which respects toString() overrides). We've fixed
1179 // the convert hook on the outer-window proxy to invoke the defaultValue
1180 // hook on the proxy, which in this case invokes js::DefaultValue on the
1181 // target. So now we need to special-case this for Window to maintain
1182 // consistent behavior. This can go away once Window is on WebIDL bindings.
1183 //
1184 // Note that WantOuterObject() is true if and only if this is a Window object.
1185 if (mFlags.WantConvert())
1186 mJSClass.base.convert = XPC_WN_Helper_Convert;
1187 else if (mFlags.WantOuterObject())
1188 mJSClass.base.convert = JS_ConvertStub;
1189 else
1190 mJSClass.base.convert = XPC_WN_Shared_Convert;
1192 if (mFlags.WantFinalize())
1193 mJSClass.base.finalize = XPC_WN_Helper_Finalize;
1194 else
1195 mJSClass.base.finalize = XPC_WN_NoHelper_Finalize;
1197 js::ObjectOps *ops = &mJSClass.base.ops;
1198 ops->enumerate = XPC_WN_JSOp_Enumerate;
1199 ops->thisObject = XPC_WN_JSOp_ThisObject;
1202 if (mFlags.WantCall())
1203 mJSClass.base.call = XPC_WN_Helper_Call;
1204 if (mFlags.WantConstruct())
1205 mJSClass.base.construct = XPC_WN_Helper_Construct;
1207 if (mFlags.WantHasInstance())
1208 mJSClass.base.hasInstance = XPC_WN_Helper_HasInstance;
1210 if (mFlags.IsGlobalObject())
1211 mJSClass.base.trace = JS_GlobalObjectTraceHook;
1212 else
1213 mJSClass.base.trace = XPCWrappedNative::Trace;
1215 if (mFlags.WantOuterObject())
1216 mJSClass.base.ext.outerObject = XPC_WN_OuterObject;
1218 mJSClass.base.ext.isWrappedNative = true;
1219 }
1221 /***************************************************************************/
1222 /***************************************************************************/
1224 // Compatibility hack.
1225 //
1226 // XPConnect used to do all sorts of funny tricks to find the "correct"
1227 // |this| object for a given method (often to the detriment of proper
1228 // call/apply). When these tricks were removed, a fair amount of chrome
1229 // code broke, because it was relying on being able to grab methods off
1230 // some XPCOM object (like the nsITelemetry service) and invoke them without
1231 // a proper |this|. So, if it's quite clear that we're in this situation and
1232 // about to use a |this| argument that just won't work, fix things up.
1233 //
1234 // This hack is only useful for getters/setters if someone sets an XPCOM object
1235 // as the prototype for a vanilla JS object and expects the XPCOM attributes to
1236 // work on the derived object, which we really don't want to support. But we
1237 // handle it anyway, for now, to minimize regression risk on an already-risky
1238 // landing.
1239 //
1240 // This hack is mainly useful for the NoHelper JSClass. We also fix up
1241 // Components.utils because it implements nsIXPCScriptable (giving it a custom
1242 // JSClass) but not nsIClassInfo (which would put the methods on a prototype).
1244 #define IS_NOHELPER_CLASS(clasp) (clasp == &XPC_WN_NoHelper_JSClass.base)
1245 #define IS_CU_CLASS(clasp) (clasp->name[0] == 'n' && !strcmp(clasp->name, "nsXPCComponents_Utils"))
1247 MOZ_ALWAYS_INLINE JSObject*
1248 FixUpThisIfBroken(JSObject *obj, JSObject *funobj)
1249 {
1250 if (funobj) {
1251 const js::Class *parentClass = js::GetObjectClass(js::GetObjectParent(funobj));
1252 if (MOZ_UNLIKELY((IS_NOHELPER_CLASS(parentClass) || IS_CU_CLASS(parentClass)) &&
1253 (js::GetObjectClass(obj) != parentClass)))
1254 {
1255 return js::GetObjectParent(funobj);
1256 }
1257 }
1258 return obj;
1259 }
1261 bool
1262 XPC_WN_CallMethod(JSContext *cx, unsigned argc, jsval *vp)
1263 {
1264 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
1265 MOZ_ASSERT(JS_TypeOfValue(cx, args.calleev()) == JSTYPE_FUNCTION, "bad function");
1266 RootedObject funobj(cx, &args.callee());
1268 RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
1269 if (!obj)
1270 return false;
1272 obj = FixUpThisIfBroken(obj, funobj);
1273 XPCCallContext ccx(JS_CALLER, cx, obj, funobj, JSID_VOIDHANDLE, args.length(),
1274 args.array(), vp);
1275 XPCWrappedNative* wrapper = ccx.GetWrapper();
1276 THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
1278 XPCNativeInterface* iface;
1279 XPCNativeMember* member;
1281 if (!XPCNativeMember::GetCallInfo(funobj, &iface, &member))
1282 return Throw(NS_ERROR_XPC_CANT_GET_METHOD_INFO, cx);
1283 ccx.SetCallInfo(iface, member, false);
1284 return XPCWrappedNative::CallMethod(ccx);
1285 }
1287 bool
1288 XPC_WN_GetterSetter(JSContext *cx, unsigned argc, jsval *vp)
1289 {
1290 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
1291 MOZ_ASSERT(JS_TypeOfValue(cx, args.calleev()) == JSTYPE_FUNCTION, "bad function");
1292 RootedObject funobj(cx, &args.callee());
1294 RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
1295 if (!obj)
1296 return false;
1298 obj = FixUpThisIfBroken(obj, funobj);
1299 XPCCallContext ccx(JS_CALLER, cx, obj, funobj, JSID_VOIDHANDLE, args.length(),
1300 args.array(), vp);
1301 XPCWrappedNative* wrapper = ccx.GetWrapper();
1302 THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
1304 XPCNativeInterface* iface;
1305 XPCNativeMember* member;
1307 if (!XPCNativeMember::GetCallInfo(funobj, &iface, &member))
1308 return Throw(NS_ERROR_XPC_CANT_GET_METHOD_INFO, cx);
1310 if (args.length() != 0 && member->IsWritableAttribute()) {
1311 ccx.SetCallInfo(iface, member, true);
1312 bool retval = XPCWrappedNative::SetAttribute(ccx);
1313 if (retval)
1314 args.rval().set(args[0]);
1315 return retval;
1316 }
1317 // else...
1319 ccx.SetCallInfo(iface, member, false);
1320 return XPCWrappedNative::GetAttribute(ccx);
1321 }
1323 /***************************************************************************/
1325 static bool
1326 XPC_WN_Shared_Proto_Enumerate(JSContext *cx, HandleObject obj)
1327 {
1328 MOZ_ASSERT(js::GetObjectClass(obj) == &XPC_WN_ModsAllowed_WithCall_Proto_JSClass ||
1329 js::GetObjectClass(obj) == &XPC_WN_ModsAllowed_NoCall_Proto_JSClass ||
1330 js::GetObjectClass(obj) == &XPC_WN_NoMods_WithCall_Proto_JSClass ||
1331 js::GetObjectClass(obj) == &XPC_WN_NoMods_NoCall_Proto_JSClass,
1332 "bad proto");
1333 XPCWrappedNativeProto* self =
1334 (XPCWrappedNativeProto*) xpc_GetJSPrivate(obj);
1335 if (!self)
1336 return false;
1338 if (self->GetScriptableInfo() &&
1339 self->GetScriptableInfo()->GetFlags().DontEnumStaticProps())
1340 return true;
1342 XPCNativeSet* set = self->GetSet();
1343 if (!set)
1344 return false;
1346 XPCCallContext ccx(JS_CALLER, cx);
1347 if (!ccx.IsValid())
1348 return false;
1350 uint16_t interface_count = set->GetInterfaceCount();
1351 XPCNativeInterface** interfaceArray = set->GetInterfaceArray();
1352 for (uint16_t i = 0; i < interface_count; i++) {
1353 XPCNativeInterface* iface = interfaceArray[i];
1354 uint16_t member_count = iface->GetMemberCount();
1356 for (uint16_t k = 0; k < member_count; k++) {
1357 if (!xpc_ForcePropertyResolve(cx, obj, iface->GetMemberAt(k)->GetName()))
1358 return false;
1359 }
1360 }
1362 return true;
1363 }
1365 static void
1366 XPC_WN_Shared_Proto_Finalize(js::FreeOp *fop, JSObject *obj)
1367 {
1368 // This can be null if xpc shutdown has already happened
1369 XPCWrappedNativeProto* p = (XPCWrappedNativeProto*) xpc_GetJSPrivate(obj);
1370 if (p)
1371 p->JSProtoObjectFinalized(fop, obj);
1372 }
1374 static void
1375 XPC_WN_Shared_Proto_Trace(JSTracer *trc, JSObject *obj)
1376 {
1377 // This can be null if xpc shutdown has already happened
1378 XPCWrappedNativeProto* p =
1379 (XPCWrappedNativeProto*) xpc_GetJSPrivate(obj);
1380 if (p)
1381 p->TraceInside(trc);
1382 }
1384 /*****************************************************/
1386 static bool
1387 XPC_WN_ModsAllowed_Proto_Resolve(JSContext *cx, HandleObject obj, HandleId id)
1388 {
1389 MOZ_ASSERT(js::GetObjectClass(obj) == &XPC_WN_ModsAllowed_WithCall_Proto_JSClass ||
1390 js::GetObjectClass(obj) == &XPC_WN_ModsAllowed_NoCall_Proto_JSClass,
1391 "bad proto");
1393 XPCWrappedNativeProto* self =
1394 (XPCWrappedNativeProto*) xpc_GetJSPrivate(obj);
1395 if (!self)
1396 return false;
1398 XPCCallContext ccx(JS_CALLER, cx);
1399 if (!ccx.IsValid())
1400 return false;
1402 XPCNativeScriptableInfo* si = self->GetScriptableInfo();
1403 unsigned enumFlag = (si && si->GetFlags().DontEnumStaticProps()) ?
1404 0 : JSPROP_ENUMERATE;
1406 return DefinePropertyIfFound(ccx, obj, id,
1407 self->GetSet(), nullptr, nullptr,
1408 self->GetScope(),
1409 true, nullptr, nullptr, si,
1410 enumFlag, nullptr);
1411 }
1413 const js::Class XPC_WN_ModsAllowed_WithCall_Proto_JSClass = {
1414 "XPC_WN_ModsAllowed_WithCall_Proto_JSClass", // name;
1415 WRAPPER_SLOTS, // flags;
1417 /* Mandatory non-null function pointer members. */
1418 JS_PropertyStub, // addProperty;
1419 JS_DeletePropertyStub, // delProperty;
1420 JS_PropertyStub, // getProperty;
1421 JS_StrictPropertyStub, // setProperty;
1422 XPC_WN_Shared_Proto_Enumerate, // enumerate;
1423 XPC_WN_ModsAllowed_Proto_Resolve, // resolve;
1424 JS_ConvertStub, // convert;
1425 XPC_WN_Shared_Proto_Finalize, // finalize;
1427 /* Optionally non-null members start here. */
1428 nullptr, // call;
1429 nullptr, // construct;
1430 nullptr, // hasInstance;
1431 XPC_WN_Shared_Proto_Trace, // trace;
1433 JS_NULL_CLASS_SPEC,
1434 JS_NULL_CLASS_EXT,
1435 XPC_WN_WithCall_ObjectOps
1436 };
1438 const js::Class XPC_WN_ModsAllowed_NoCall_Proto_JSClass = {
1439 "XPC_WN_ModsAllowed_NoCall_Proto_JSClass", // name;
1440 WRAPPER_SLOTS, // flags;
1442 /* Mandatory non-null function pointer members. */
1443 JS_PropertyStub, // addProperty;
1444 JS_DeletePropertyStub, // delProperty;
1445 JS_PropertyStub, // getProperty;
1446 JS_StrictPropertyStub, // setProperty;
1447 XPC_WN_Shared_Proto_Enumerate, // enumerate;
1448 XPC_WN_ModsAllowed_Proto_Resolve, // resolve;
1449 JS_ConvertStub, // convert;
1450 XPC_WN_Shared_Proto_Finalize, // finalize;
1452 /* Optionally non-null members start here. */
1453 nullptr, // call;
1454 nullptr, // construct;
1455 nullptr, // hasInstance;
1456 XPC_WN_Shared_Proto_Trace, // trace;
1458 JS_NULL_CLASS_SPEC,
1459 JS_NULL_CLASS_EXT,
1460 XPC_WN_NoCall_ObjectOps
1461 };
1463 /***************************************************************************/
1465 static bool
1466 XPC_WN_OnlyIWrite_Proto_AddPropertyStub(JSContext *cx, HandleObject obj, HandleId id,
1467 MutableHandleValue vp)
1468 {
1469 MOZ_ASSERT(js::GetObjectClass(obj) == &XPC_WN_NoMods_WithCall_Proto_JSClass ||
1470 js::GetObjectClass(obj) == &XPC_WN_NoMods_NoCall_Proto_JSClass,
1471 "bad proto");
1473 XPCWrappedNativeProto* self =
1474 (XPCWrappedNativeProto*) xpc_GetJSPrivate(obj);
1475 if (!self)
1476 return false;
1478 XPCCallContext ccx(JS_CALLER, cx);
1479 if (!ccx.IsValid())
1480 return false;
1482 // Allow XPConnect to add the property only
1483 if (ccx.GetResolveName() == id)
1484 return true;
1486 return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx);
1487 }
1489 static bool
1490 XPC_WN_OnlyIWrite_Proto_SetPropertyStub(JSContext *cx, HandleObject obj, HandleId id, bool strict,
1491 MutableHandleValue vp)
1492 {
1493 return XPC_WN_OnlyIWrite_Proto_AddPropertyStub(cx, obj, id, vp);
1494 }
1496 static bool
1497 XPC_WN_NoMods_Proto_Resolve(JSContext *cx, HandleObject obj, HandleId id)
1498 {
1499 MOZ_ASSERT(js::GetObjectClass(obj) == &XPC_WN_NoMods_WithCall_Proto_JSClass ||
1500 js::GetObjectClass(obj) == &XPC_WN_NoMods_NoCall_Proto_JSClass,
1501 "bad proto");
1503 XPCWrappedNativeProto* self =
1504 (XPCWrappedNativeProto*) xpc_GetJSPrivate(obj);
1505 if (!self)
1506 return false;
1508 XPCCallContext ccx(JS_CALLER, cx);
1509 if (!ccx.IsValid())
1510 return false;
1512 XPCNativeScriptableInfo* si = self->GetScriptableInfo();
1513 unsigned enumFlag = (si && si->GetFlags().DontEnumStaticProps()) ?
1514 0 : JSPROP_ENUMERATE;
1516 return DefinePropertyIfFound(ccx, obj, id,
1517 self->GetSet(), nullptr, nullptr,
1518 self->GetScope(),
1519 true, nullptr, nullptr, si,
1520 JSPROP_READONLY |
1521 JSPROP_PERMANENT |
1522 enumFlag, nullptr);
1523 }
1525 const js::Class XPC_WN_NoMods_WithCall_Proto_JSClass = {
1526 "XPC_WN_NoMods_WithCall_Proto_JSClass", // name;
1527 WRAPPER_SLOTS, // flags;
1529 /* Mandatory non-null function pointer members. */
1530 XPC_WN_OnlyIWrite_Proto_AddPropertyStub, // addProperty;
1531 XPC_WN_CantDeletePropertyStub, // delProperty;
1532 JS_PropertyStub, // getProperty;
1533 XPC_WN_OnlyIWrite_Proto_SetPropertyStub, // setProperty;
1534 XPC_WN_Shared_Proto_Enumerate, // enumerate;
1535 XPC_WN_NoMods_Proto_Resolve, // resolve;
1536 JS_ConvertStub, // convert;
1537 XPC_WN_Shared_Proto_Finalize, // finalize;
1539 /* Optionally non-null members start here. */
1540 nullptr, // call;
1541 nullptr, // construct;
1542 nullptr, // hasInstance;
1543 XPC_WN_Shared_Proto_Trace, // trace;
1545 JS_NULL_CLASS_SPEC,
1546 JS_NULL_CLASS_EXT,
1547 XPC_WN_WithCall_ObjectOps
1548 };
1550 const js::Class XPC_WN_NoMods_NoCall_Proto_JSClass = {
1551 "XPC_WN_NoMods_NoCall_Proto_JSClass", // name;
1552 WRAPPER_SLOTS, // flags;
1554 /* Mandatory non-null function pointer members. */
1555 XPC_WN_OnlyIWrite_Proto_AddPropertyStub, // addProperty;
1556 XPC_WN_CantDeletePropertyStub, // delProperty;
1557 JS_PropertyStub, // getProperty;
1558 XPC_WN_OnlyIWrite_Proto_SetPropertyStub, // setProperty;
1559 XPC_WN_Shared_Proto_Enumerate, // enumerate;
1560 XPC_WN_NoMods_Proto_Resolve, // resolve;
1561 JS_ConvertStub, // convert;
1562 XPC_WN_Shared_Proto_Finalize, // finalize;
1564 /* Optionally non-null members start here. */
1565 nullptr, // call;
1566 nullptr, // construct;
1567 nullptr, // hasInstance;
1568 XPC_WN_Shared_Proto_Trace, // trace;
1570 JS_NULL_CLASS_SPEC,
1571 JS_NULL_CLASS_EXT,
1572 XPC_WN_NoCall_ObjectOps
1573 };
1575 /***************************************************************************/
1577 static bool
1578 XPC_WN_TearOff_Enumerate(JSContext *cx, HandleObject obj)
1579 {
1580 XPCCallContext ccx(JS_CALLER, cx, obj);
1581 XPCWrappedNative* wrapper = ccx.GetWrapper();
1582 THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
1584 XPCWrappedNativeTearOff* to = ccx.GetTearOff();
1585 XPCNativeInterface* iface;
1587 if (!to || nullptr == (iface = to->GetInterface()))
1588 return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx);
1590 uint16_t member_count = iface->GetMemberCount();
1591 for (uint16_t k = 0; k < member_count; k++) {
1592 if (!xpc_ForcePropertyResolve(cx, obj, iface->GetMemberAt(k)->GetName()))
1593 return false;
1594 }
1596 return true;
1597 }
1599 static bool
1600 XPC_WN_TearOff_Resolve(JSContext *cx, HandleObject obj, HandleId id)
1601 {
1602 XPCCallContext ccx(JS_CALLER, cx, obj);
1603 XPCWrappedNative* wrapper = ccx.GetWrapper();
1604 THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
1606 XPCWrappedNativeTearOff* to = ccx.GetTearOff();
1607 XPCNativeInterface* iface;
1609 if (!to || nullptr == (iface = to->GetInterface()))
1610 return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx);
1612 return DefinePropertyIfFound(ccx, obj, id, nullptr, iface, nullptr,
1613 wrapper->GetScope(),
1614 true, nullptr, nullptr, nullptr,
1615 JSPROP_READONLY |
1616 JSPROP_PERMANENT |
1617 JSPROP_ENUMERATE, nullptr);
1618 }
1620 static void
1621 XPC_WN_TearOff_Finalize(js::FreeOp *fop, JSObject *obj)
1622 {
1623 XPCWrappedNativeTearOff* p = (XPCWrappedNativeTearOff*)
1624 xpc_GetJSPrivate(obj);
1625 if (!p)
1626 return;
1627 p->JSObjectFinalized();
1628 }
1630 const js::Class XPC_WN_Tearoff_JSClass = {
1631 "WrappedNative_TearOff", // name;
1632 WRAPPER_SLOTS, // flags;
1634 XPC_WN_OnlyIWrite_AddPropertyStub, // addProperty;
1635 XPC_WN_CantDeletePropertyStub, // delProperty;
1636 JS_PropertyStub, // getProperty;
1637 XPC_WN_OnlyIWrite_SetPropertyStub, // setProperty;
1638 XPC_WN_TearOff_Enumerate, // enumerate;
1639 XPC_WN_TearOff_Resolve, // resolve;
1640 XPC_WN_Shared_Convert, // convert;
1641 XPC_WN_TearOff_Finalize // finalize;
1642 };