1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sw=4 et tw=80:
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
17 * The Original Code is mozilla.org code.
19 * The Initial Developer of the Original Code is
20 * The Mozilla Foundation.
21 * Portions created by the Initial Developer are Copyright (C) 2010
22 * the Initial Developer. All Rights Reserved.
25 * Ben Newman <b{enjam,newma}n@mozilla.com> (original author)
27 * Alternatively, the contents of this file may be used under the terms of
28 * either of the GNU General Public License Version 2 or later (the "GPL"),
29 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
41 #include "mozilla/jsipc/ObjectWrapperParent.h"
42 #include "mozilla/jsipc/ContextWrapperParent.h"
43 #include "mozilla/jsipc/CPOWTypes.h"
44 #include "mozilla/unused.h"
45 #include "nsJSUtils.h"
51 using namespace mozilla::jsipc
;
55 // Only need one reserved slot because the ObjectWrapperParent* is
56 // stored in the private slot.
57 static const uintN sFlagsSlot
= 0;
58 static const uintN sNumSlots
= 1;
59 static const uintN CPOW_FLAG_RESOLVING
= 1 << 0;
66 JS_DECL_USE_GUARD_OBJECT_NOTIFIER
68 static uintN
GetFlags(JSContext
* cx
, JSObject
* obj
) {
73 JS_GetReservedSlot(cx
, obj
, sFlagsSlot
, &v
);
74 NS_ASSERTION(ok
, "Failed to get CPOW flags");
75 return JSVAL_TO_INT(v
);
78 static uintN
SetFlags(JSContext
* cx
, JSObject
* obj
, uintN flags
) {
79 uintN oldFlags
= GetFlags(cx
, obj
);
80 if (oldFlags
!= flags
)
81 JS_SetReservedSlot(cx
, obj
, sFlagsSlot
, INT_TO_JSVAL(flags
));
87 AutoResolveFlag(JSContext
* cx
,
89 JS_GUARD_OBJECT_NOTIFIER_PARAM
)
92 , mOldFlags(SetFlags(cx
, obj
,
93 GetFlags(cx
, obj
) | CPOW_FLAG_RESOLVING
))
95 JS_GUARD_OBJECT_NOTIFIER_INIT
;
99 SetFlags(mContext
, mObj
, mOldFlags
);
102 static JSBool
IsSet(JSContext
* cx
, JSObject
* obj
) {
103 return GetFlags(cx
, obj
) & CPOW_FLAG_RESOLVING
;
108 class StatusMemberOwner
110 OperationStatus mStatus
;
112 StatusMemberOwner() : mStatus(JS_FALSE
) {}
113 OperationStatus
* StatusPtr() {
118 typedef AutoCheckOperationBase
<StatusMemberOwner
> ACOBase
;
120 class AutoCheckOperation
: public ACOBase
122 JS_DECL_USE_GUARD_OBJECT_NOTIFIER
124 AutoCheckOperation(JSContext
* cx
,
125 ObjectWrapperParent
* owp
126 JS_GUARD_OBJECT_NOTIFIER_PARAM
)
129 JS_GUARD_OBJECT_NOTIFIER_INIT
;
136 ObjectWrapperParent::CheckOperation(JSContext
* cx
,
137 OperationStatus
* status
)
139 NS_PRECONDITION(status
->type() != OperationStatus::T__None
,
140 "Checking an uninitialized operation.");
142 switch (status
->type()) {
143 case OperationStatus::TJSVariant
:
146 if (jsval_from_JSVariant(cx
, status
->get_JSVariant(), &thrown
))
147 JS_SetPendingException(cx
, thrown
);
151 case OperationStatus::TJSBool
:
152 if (!status
->get_JSBool() && !JS_IsExceptionPending(cx
)) {
153 NS_WARNING("CPOW operation failed without setting an exception.");
157 NS_NOTREACHED("Invalid or uninitialized OperationStatus type.");
162 template <typename RType
>
164 with_error(JSContext
* cx
,
166 const char* error
= NULL
)
168 if (!JS_IsExceptionPending(cx
))
169 JS_ReportError(cx
, error
? error
: "Unspecified CPOW error");
173 const js::Class
ObjectWrapperParent::sCPOW_JSClass
= {
174 "CrossProcessObjectWrapper",
175 JSCLASS_NEW_RESOLVE
| JSCLASS_NEW_ENUMERATE
|
176 JSCLASS_HAS_PRIVATE
| JSCLASS_HAS_RESERVED_SLOTS(sNumSlots
),
177 JS_VALUEIFY(js::PropertyOp
, ObjectWrapperParent::CPOW_AddProperty
),
178 JS_VALUEIFY(js::PropertyOp
, ObjectWrapperParent::CPOW_DelProperty
),
179 JS_VALUEIFY(js::PropertyOp
, ObjectWrapperParent::CPOW_GetProperty
),
180 JS_VALUEIFY(js::StrictPropertyOp
, ObjectWrapperParent::CPOW_SetProperty
),
181 (JSEnumerateOp
) ObjectWrapperParent::CPOW_NewEnumerate
,
182 (JSResolveOp
) ObjectWrapperParent::CPOW_NewResolve
,
183 JS_VALUEIFY(js::ConvertOp
, ObjectWrapperParent::CPOW_Convert
),
184 ObjectWrapperParent::CPOW_Finalize
,
186 nsnull
, // checkAccess
187 JS_VALUEIFY(js::CallOp
, ObjectWrapperParent::CPOW_Call
),
188 JS_VALUEIFY(js::CallOp
, ObjectWrapperParent::CPOW_Construct
),
190 JS_VALUEIFY(js::HasInstanceOp
, ObjectWrapperParent::CPOW_HasInstance
),
193 JS_VALUEIFY(js::EqualityOp
, ObjectWrapperParent::CPOW_Equality
),
194 nsnull
, // outerObject
195 nsnull
, // innerObject
196 nsnull
, // iteratorObject
197 nsnull
, // wrappedObject
202 ObjectWrapperParent::ActorDestroy(ActorDestroyReason
)
205 mObj
->setPrivate(NULL
);
210 ContextWrapperParent
*
211 ObjectWrapperParent::Manager()
213 PContextWrapperParent
* pcwp
= PObjectWrapperParent::Manager();
214 return static_cast<ContextWrapperParent
*>(pcwp
);
218 ObjectWrapperParent::GetJSObject(JSContext
* cx
) const
220 js::Class
*clasp
= const_cast<js::Class
*>(&ObjectWrapperParent::sCPOW_JSClass
);
221 if (!mObj
&& (mObj
= JS_NewObject(cx
, js::Jsvalify(clasp
), NULL
, NULL
))) {
222 JS_SetPrivate(cx
, mObj
, (void*)this);
223 JS_SetReservedSlot(cx
, mObj
, sFlagsSlot
, JSVAL_ZERO
);
228 static ObjectWrapperParent
*
229 Unwrap(JSContext
* cx
, JSObject
* obj
)
231 while (obj
->getClass() != &ObjectWrapperParent::sCPOW_JSClass
)
232 if (!(obj
= obj
->getProto()))
235 ObjectWrapperParent
* self
=
236 static_cast<ObjectWrapperParent
*>(JS_GetPrivate(cx
, obj
));
238 NS_ASSERTION(!self
|| self
->GetJSObject(cx
) == obj
,
239 "Wrapper and wrapped object disagree?");
245 ObjectWrapperParent::jsval_to_JSVariant(JSContext
* cx
, jsval from
,
248 switch (JS_TypeOfValue(cx
, from
)) {
253 if (from
!= JSVAL_NULL
)
256 case JSTYPE_FUNCTION
:
257 // CPOWs can fool JS_TypeOfValue into returning JSTYPE_FUNCTION
258 // because they have a call hook, but CPOWs are really objects, so
259 // fall through to the JSTYPE_OBJECT case:
262 PObjectWrapperParent
* powp
;
263 if (!JSObject_to_PObjectWrapperParent(cx
, JSVAL_TO_OBJECT(from
), &powp
))
264 return with_error(cx
, false, "Cannot pass parent-created object to child");
270 nsDependentJSString depStr
;
271 if (!depStr
.init(cx
, from
))
277 if (JSVAL_IS_INT(from
))
278 *to
= JSVAL_TO_INT(from
);
279 else if (JSVAL_IS_DOUBLE(from
))
280 *to
= JSVAL_TO_DOUBLE(from
);
284 *to
= !!JSVAL_TO_BOOLEAN(from
);
287 return with_error(cx
, false, "CPOWs currently cannot handle JSTYPE_XML");
289 return with_error(cx
, false, "Bad jsval type");
294 ObjectWrapperParent::jsval_from_JSVariant(JSContext
* cx
, const JSVariant
& from
,
297 switch (from
.type()) {
298 case JSVariant::Tvoid_t
:
301 case JSVariant::TPObjectWrapperParent
:
302 return jsval_from_PObjectWrapperParent(cx
, from
.get_PObjectWrapperParent(), to
);
303 case JSVariant::TnsString
:
305 JSString
* str
= JS_NewUCStringCopyZ(cx
, from
.get_nsString().BeginReading());
308 *to
= STRING_TO_JSVAL(str
);
311 case JSVariant::Tint
:
312 *to
= INT_TO_JSVAL(from
.get_int());
314 case JSVariant::Tdouble
:
315 return !!JS_NewNumberValue(cx
, from
.get_double(), to
);
316 case JSVariant::Tbool
:
317 *to
= BOOLEAN_TO_JSVAL(from
.get_bool());
325 ObjectWrapperParent::
326 JSObject_to_PObjectWrapperParent(JSContext
* cx
, JSObject
* from
,
327 PObjectWrapperParent
** to
)
333 ObjectWrapperParent
* owp
= Unwrap(cx
, from
);
341 ObjectWrapperParent::
342 JSObject_from_PObjectWrapperParent(JSContext
* cx
,
343 const PObjectWrapperParent
* from
,
346 const ObjectWrapperParent
* owp
=
347 static_cast<const ObjectWrapperParent
*>(from
);
349 ? owp
->GetJSObject(cx
)
350 : JSVAL_TO_OBJECT(JSVAL_NULL
);
355 ObjectWrapperParent::
356 jsval_from_PObjectWrapperParent(JSContext
* cx
,
357 const PObjectWrapperParent
* from
,
361 if (!JSObject_from_PObjectWrapperParent(cx
, from
, &obj
))
363 *to
= OBJECT_TO_JSVAL(obj
);
368 jsid_from_int(JSContext
* cx
, int from
, jsid
* to
)
370 jsval v
= INT_TO_JSVAL(from
);
371 return JS_ValueToId(cx
, v
, to
);
375 jsid_from_nsString(JSContext
* cx
, const nsString
& from
, jsid
* to
)
377 JSString
* str
= JS_NewUCStringCopyZ(cx
, from
.BeginReading());
380 return JS_ValueToId(cx
, STRING_TO_JSVAL(str
), to
);
384 jsval_to_nsString(JSContext
* cx
, jsid from
, nsString
* to
)
389 if (JS_IdToValue(cx
, from
, &idval
) &&
390 (str
= JS_ValueToString(cx
, idval
)) &&
391 (chars
= JS_GetStringCharsZ(cx
, str
))) {
399 ObjectWrapperParent::CPOW_AddProperty(JSContext
*cx
, JSObject
*obj
, jsid id
,
402 CPOW_LOG(("Calling CPOW_AddProperty (%s)...",
403 JSVAL_TO_CSTR(cx
, id
)));
405 ObjectWrapperParent
* self
= Unwrap(cx
, obj
);
407 return with_error(cx
, JS_FALSE
, "Unwrapping failed in CPOW_AddProperty");
409 if (AutoResolveFlag::IsSet(cx
, obj
))
412 AutoCheckOperation
aco(cx
, self
);
416 if (!jsval_to_nsString(cx
, id
, &in_id
))
419 return (self
->Manager()->RequestRunToCompletion() &&
420 self
->CallAddProperty(in_id
,
426 ObjectWrapperParent::CPOW_GetProperty(JSContext
*cx
, JSObject
*obj
, jsid id
,
429 CPOW_LOG(("Calling CPOW_GetProperty (%s)...",
430 JSVAL_TO_CSTR(cx
, id
)));
432 ObjectWrapperParent
* self
= Unwrap(cx
, obj
);
434 return with_error(cx
, JS_FALSE
, "Unwrapping failed in CPOW_GetProperty");
436 AutoCheckOperation
aco(cx
, self
);
440 if (!jsval_to_nsString(cx
, id
, &in_id
))
445 return (self
->Manager()->RequestRunToCompletion() &&
446 self
->CallGetProperty(in_id
,
447 aco
.StatusPtr(), &out_v
) &&
449 self
->jsval_from_JSVariant(cx
, out_v
, vp
));
453 ObjectWrapperParent::CPOW_SetProperty(JSContext
*cx
, JSObject
*obj
, jsid id
,
454 JSBool strict
, jsval
*vp
)
456 CPOW_LOG(("Calling CPOW_SetProperty (%s)...",
457 JSVAL_TO_CSTR(cx
, id
)));
459 ObjectWrapperParent
* self
= Unwrap(cx
, obj
);
461 return with_error(cx
, JS_FALSE
, "Unwrapping failed in CPOW_SetProperty");
463 AutoCheckOperation
aco(cx
, self
);
468 if (!jsval_to_nsString(cx
, id
, &in_id
) ||
469 !self
->jsval_to_JSVariant(cx
, *vp
, &in_v
))
474 return (self
->Manager()->RequestRunToCompletion() &&
475 self
->CallSetProperty(in_id
, in_v
,
476 aco
.StatusPtr(), &out_v
) &&
478 self
->jsval_from_JSVariant(cx
, out_v
, vp
));
482 ObjectWrapperParent::CPOW_DelProperty(JSContext
*cx
, JSObject
*obj
, jsid id
,
485 CPOW_LOG(("Calling CPOW_DelProperty (%s)...",
486 JSVAL_TO_CSTR(cx
, id
)));
488 ObjectWrapperParent
* self
= Unwrap(cx
, obj
);
490 return with_error(cx
, JS_FALSE
, "Unwrapping failed in CPOW_DelProperty");
492 AutoCheckOperation
aco(cx
, self
);
496 if (!jsval_to_nsString(cx
, id
, &in_id
))
501 return (self
->Manager()->RequestRunToCompletion() &&
502 self
->CallDelProperty(in_id
,
503 aco
.StatusPtr(), &out_v
) &&
505 jsval_from_JSVariant(cx
, out_v
, vp
));
509 ObjectWrapperParent::NewEnumerateInit(JSContext
* cx
, jsval
* statep
, jsid
* idp
)
511 AutoCheckOperation
aco(cx
, this);
516 return (CallNewEnumerateInit(aco
.StatusPtr(), &out_state
, &out_id
) &&
518 jsval_from_JSVariant(cx
, out_state
, statep
) &&
519 (!idp
|| jsid_from_int(cx
, out_id
, idp
)));
523 ObjectWrapperParent::NewEnumerateNext(JSContext
* cx
, jsval
* statep
, jsid
* idp
)
525 AutoCheckOperation
aco(cx
, this);
529 if (!jsval_to_JSVariant(cx
, *statep
, &in_state
))
535 if (CallNewEnumerateNext(in_state
,
536 aco
.StatusPtr(), &out_state
, &out_id
) &&
538 jsval_from_JSVariant(cx
, out_state
, statep
) &&
539 jsid_from_nsString(cx
, out_id
, idp
))
541 JSObject
* obj
= GetJSObject(cx
);
542 AutoResolveFlag
arf(cx
, obj
);
543 return JS_DefinePropertyById(cx
, obj
, *idp
, JSVAL_VOID
, NULL
, NULL
,
550 ObjectWrapperParent::NewEnumerateDestroy(JSContext
* cx
, jsval state
)
552 AutoCheckOperation
aco(cx
, this);
556 if (!jsval_to_JSVariant(cx
, state
, &in_state
))
559 return SendNewEnumerateDestroy(in_state
);
563 ObjectWrapperParent::CPOW_NewEnumerate(JSContext
*cx
, JSObject
*obj
,
564 JSIterateOp enum_op
, jsval
*statep
,
567 CPOW_LOG(("Calling CPOW_NewEnumerate..."));
569 ObjectWrapperParent
* self
= Unwrap(cx
, obj
);
571 return with_error(cx
, JS_FALSE
, "Unwrapping failed in CPOW_NewEnumerate");
574 case JSENUMERATE_INIT
:
575 case JSENUMERATE_INIT_ALL
:
576 self
->Manager()->RequestRunToCompletion();
577 return self
->NewEnumerateInit(cx
, statep
, idp
);
578 case JSENUMERATE_NEXT
:
579 return self
->NewEnumerateNext(cx
, statep
, idp
);
580 case JSENUMERATE_DESTROY
:
581 return self
->NewEnumerateDestroy(cx
, *statep
);
584 NS_NOTREACHED("Unknown enum_op value in CPOW_NewEnumerate");
589 ObjectWrapperParent::CPOW_NewResolve(JSContext
*cx
, JSObject
*obj
, jsid id
,
590 uintN flags
, JSObject
**objp
)
592 CPOW_LOG(("Calling CPOW_NewResolve (%s)...",
593 JSVAL_TO_CSTR(cx
, id
)));
595 ObjectWrapperParent
* self
= Unwrap(cx
, obj
);
597 return with_error(cx
, JS_FALSE
, "Unwrapping failed in CPOW_NewResolve");
599 AutoCheckOperation
aco(cx
, self
);
603 if (!jsval_to_nsString(cx
, id
, &in_id
))
606 PObjectWrapperParent
* out_pobj
;
608 if (!self
->Manager()->RequestRunToCompletion() ||
609 !self
->CallNewResolve(in_id
, flags
,
610 aco
.StatusPtr(), &out_pobj
) ||
612 !JSObject_from_PObjectWrapperParent(cx
, out_pobj
, objp
))
616 AutoResolveFlag
arf(cx
, *objp
);
617 JS_DefinePropertyById(cx
, *objp
, id
, JSVAL_VOID
, NULL
, NULL
,
624 ObjectWrapperParent::CPOW_Convert(JSContext
*cx
, JSObject
*obj
, JSType type
,
627 CPOW_LOG(("Calling CPOW_Convert (to %s)...",
628 JS_GetTypeName(cx
, type
)));
630 ObjectWrapperParent
* self
= Unwrap(cx
, obj
);
632 return with_error(cx
, JS_FALSE
, "Unwrapping failed in CPOW_Convert");
634 *vp
= OBJECT_TO_JSVAL(obj
);
640 ObjectWrapperParent::CPOW_Finalize(JSContext
* cx
, JSObject
* obj
)
642 CPOW_LOG(("Calling CPOW_Finalize..."));
644 ObjectWrapperParent
* self
= Unwrap(cx
, obj
);
647 unused
<< ObjectWrapperParent::Send__delete__(self
);
652 ObjectWrapperParent::CPOW_Call(JSContext
* cx
, uintN argc
, jsval
* vp
)
654 CPOW_LOG(("Calling CPOW_Call..."));
656 JSObject
* thisobj
= JS_THIS_OBJECT(cx
, vp
);
660 ObjectWrapperParent
* function
=
661 Unwrap(cx
, JSVAL_TO_OBJECT(JS_CALLEE(cx
, vp
)));
663 return with_error(cx
, JS_FALSE
, "Could not unwrap CPOW function");
665 AutoCheckOperation
aco(cx
, function
);
667 ObjectWrapperParent
* receiver
= Unwrap(cx
, thisobj
);
669 // Substitute child global for parent global object.
670 // TODO First make sure we're really replacing the global object?
671 ContextWrapperParent
* manager
=
672 static_cast<ContextWrapperParent
*>(function
->Manager());
673 receiver
= manager
->GetGlobalObjectWrapper();
676 InfallibleTArray
<JSVariant
> in_argv(argc
);
677 jsval
* argv
= JS_ARGV(cx
, vp
);
678 for (uintN i
= 0; i
< argc
; i
++)
679 if (!jsval_to_JSVariant(cx
, argv
[i
], in_argv
.AppendElement()))
684 return (function
->Manager()->RequestRunToCompletion() &&
685 function
->CallCall(receiver
, in_argv
,
686 aco
.StatusPtr(), &out_rval
) &&
688 jsval_from_JSVariant(cx
, out_rval
, vp
));
692 ObjectWrapperParent::CPOW_Construct(JSContext
* cx
, uintN argc
, jsval
* vp
)
694 CPOW_LOG(("Calling CPOW_Construct..."));
696 ObjectWrapperParent
* constructor
= Unwrap(cx
, JSVAL_TO_OBJECT(JS_CALLEE(cx
, vp
)));
698 return with_error(cx
, JS_FALSE
, "Could not unwrap CPOW constructor function");
700 AutoCheckOperation
aco(cx
, constructor
);
702 InfallibleTArray
<JSVariant
> in_argv(argc
);
703 jsval
* argv
= JS_ARGV(cx
, vp
);
704 for (uintN i
= 0; i
< argc
; i
++)
705 if (!jsval_to_JSVariant(cx
, argv
[i
], in_argv
.AppendElement()))
708 PObjectWrapperParent
* out_powp
;
710 return (constructor
->Manager()->RequestRunToCompletion() &&
711 constructor
->CallConstruct(in_argv
, aco
.StatusPtr(), &out_powp
) &&
713 jsval_from_PObjectWrapperParent(cx
, out_powp
, vp
));
717 ObjectWrapperParent::CPOW_HasInstance(JSContext
*cx
, JSObject
*obj
, const jsval
*v
,
720 CPOW_LOG(("Calling CPOW_HasInstance..."));
724 ObjectWrapperParent
* self
= Unwrap(cx
, obj
);
726 return with_error(cx
, JS_FALSE
, "Unwrapping failed in CPOW_HasInstance");
728 AutoCheckOperation
aco(cx
, self
);
732 if (!jsval_to_JSVariant(cx
, *v
, &in_v
))
735 return (self
->Manager()->RequestRunToCompletion() &&
736 self
->CallHasInstance(in_v
,
737 aco
.StatusPtr(), bp
) &&
742 ObjectWrapperParent::CPOW_Equality(JSContext
*cx
, JSObject
*obj
, const jsval
*v
,
745 CPOW_LOG(("Calling CPOW_Equality..."));
749 ObjectWrapperParent
* self
= Unwrap(cx
, obj
);
751 return with_error(cx
, JS_FALSE
, "Unwrapping failed in CPOW_Equality");
753 if (JSVAL_IS_PRIMITIVE(*v
))
756 ObjectWrapperParent
* other
= Unwrap(cx
, JSVAL_TO_OBJECT(*v
));
760 *bp
= (self
== other
);