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"
50 using namespace mozilla::jsipc
;
54 // Only need one reserved slot because the ObjectWrapperParent* is
55 // stored in the private slot.
56 static const uintN sFlagsSlot
= 0;
57 static const uintN sNumSlots
= 1;
58 static const uintN CPOW_FLAG_RESOLVING
= 1 << 0;
65 JS_DECL_USE_GUARD_OBJECT_NOTIFIER
;
67 static uintN
GetFlags(JSContext
* cx
, JSObject
* obj
) {
72 JS_GetReservedSlot(cx
, obj
, sFlagsSlot
, &v
);
73 NS_ASSERTION(ok
, "Failed to get CPOW flags");
74 return JSVAL_TO_INT(v
);
77 static uintN
SetFlags(JSContext
* cx
, JSObject
* obj
, uintN flags
) {
78 uintN oldFlags
= GetFlags(cx
, obj
);
79 if (oldFlags
!= flags
)
80 JS_SetReservedSlot(cx
, obj
, sFlagsSlot
, INT_TO_JSVAL(flags
));
86 AutoResolveFlag(JSContext
* cx
,
88 JS_GUARD_OBJECT_NOTIFIER_PARAM
)
91 , mOldFlags(SetFlags(cx
, obj
,
92 GetFlags(cx
, obj
) | CPOW_FLAG_RESOLVING
))
94 JS_GUARD_OBJECT_NOTIFIER_INIT
;
98 SetFlags(mContext
, mObj
, mOldFlags
);
101 static JSBool
IsSet(JSContext
* cx
, JSObject
* obj
) {
102 return GetFlags(cx
, obj
) & CPOW_FLAG_RESOLVING
;
107 class StatusMemberOwner
109 OperationStatus mStatus
;
111 StatusMemberOwner() : mStatus(JS_FALSE
) {}
112 OperationStatus
* StatusPtr() {
117 typedef AutoCheckOperationBase
<StatusMemberOwner
> ACOBase
;
119 class AutoCheckOperation
: public ACOBase
121 JS_DECL_USE_GUARD_OBJECT_NOTIFIER
;
123 AutoCheckOperation(JSContext
* cx
,
124 ObjectWrapperParent
* owp
125 JS_GUARD_OBJECT_NOTIFIER_PARAM
)
128 JS_GUARD_OBJECT_NOTIFIER_INIT
;
135 ObjectWrapperParent::CheckOperation(JSContext
* cx
,
136 OperationStatus
* status
)
138 NS_PRECONDITION(status
->type() != OperationStatus::T__None
,
139 "Checking an uninitialized operation.");
141 switch (status
->type()) {
142 case OperationStatus::TJSVariant
:
145 if (jsval_from_JSVariant(cx
, status
->get_JSVariant(), &thrown
))
146 JS_SetPendingException(cx
, thrown
);
150 case OperationStatus::TJSBool
:
151 if (!status
->get_JSBool() && !JS_IsExceptionPending(cx
)) {
152 NS_WARNING("CPOW operation failed without setting an exception.");
156 NS_NOTREACHED("Invalid or uninitialized OperationStatus type.");
161 template <typename RType
>
163 with_error(JSContext
* cx
,
165 const char* error
= NULL
)
167 if (!JS_IsExceptionPending(cx
))
168 JS_ReportError(cx
, error
? error
: "Unspecified CPOW error");
172 const js::Class
ObjectWrapperParent::sCPOW_JSClass
= {
173 "CrossProcessObjectWrapper",
174 JSCLASS_NEW_RESOLVE
| JSCLASS_NEW_ENUMERATE
|
175 JSCLASS_HAS_PRIVATE
| JSCLASS_HAS_RESERVED_SLOTS(sNumSlots
),
176 JS_VALUEIFY(js::PropertyOp
, ObjectWrapperParent::CPOW_AddProperty
),
177 JS_VALUEIFY(js::PropertyOp
, ObjectWrapperParent::CPOW_DelProperty
),
178 JS_VALUEIFY(js::PropertyOp
, ObjectWrapperParent::CPOW_GetProperty
),
179 JS_VALUEIFY(js::PropertyOp
, ObjectWrapperParent::CPOW_SetProperty
),
180 (JSEnumerateOp
) ObjectWrapperParent::CPOW_NewEnumerate
,
181 (JSResolveOp
) ObjectWrapperParent::CPOW_NewResolve
,
182 JS_VALUEIFY(js::ConvertOp
, ObjectWrapperParent::CPOW_Convert
),
183 ObjectWrapperParent::CPOW_Finalize
,
185 nsnull
, // checkAccess
186 JS_VALUEIFY(js::CallOp
, ObjectWrapperParent::CPOW_Call
),
187 JS_VALUEIFY(js::CallOp
, ObjectWrapperParent::CPOW_Construct
),
189 JS_VALUEIFY(js::HasInstanceOp
, ObjectWrapperParent::CPOW_HasInstance
),
192 JS_VALUEIFY(js::EqualityOp
, ObjectWrapperParent::CPOW_Equality
),
193 nsnull
, // outerObject
194 nsnull
, // innerObject
195 nsnull
, // iteratorObject
196 nsnull
, // wrappedObject
201 ObjectWrapperParent::ActorDestroy(ActorDestroyReason
)
204 mObj
->setPrivate(NULL
);
209 ContextWrapperParent
*
210 ObjectWrapperParent::Manager()
212 PContextWrapperParent
* pcwp
= PObjectWrapperParent::Manager();
213 return static_cast<ContextWrapperParent
*>(pcwp
);
217 ObjectWrapperParent::GetJSObject(JSContext
* cx
) const
219 js::Class
*clasp
= const_cast<js::Class
*>(&ObjectWrapperParent::sCPOW_JSClass
);
220 if (!mObj
&& (mObj
= JS_NewObject(cx
, js::Jsvalify(clasp
), NULL
, NULL
))) {
221 JS_SetPrivate(cx
, mObj
, (void*)this);
222 JS_SetReservedSlot(cx
, mObj
, sFlagsSlot
, JSVAL_ZERO
);
227 static ObjectWrapperParent
*
228 Unwrap(JSContext
* cx
, JSObject
* obj
)
230 while (obj
->getClass() != &ObjectWrapperParent::sCPOW_JSClass
)
231 if (!(obj
= obj
->getProto()))
234 ObjectWrapperParent
* self
=
235 static_cast<ObjectWrapperParent
*>(JS_GetPrivate(cx
, obj
));
237 NS_ASSERTION(!self
|| self
->GetJSObject(cx
) == obj
,
238 "Wrapper and wrapped object disagree?");
244 ObjectWrapperParent::jsval_to_JSVariant(JSContext
* cx
, jsval from
,
247 switch (JS_TypeOfValue(cx
, from
)) {
252 if (from
!= JSVAL_NULL
)
255 case JSTYPE_FUNCTION
:
256 // CPOWs can fool JS_TypeOfValue into returning JSTYPE_FUNCTION
257 // because they have a call hook, but CPOWs are really objects, so
258 // fall through to the JSTYPE_OBJECT case:
261 PObjectWrapperParent
* powp
;
262 if (!JSObject_to_PObjectWrapperParent(cx
, JSVAL_TO_OBJECT(from
), &powp
))
263 return with_error(cx
, false, "Cannot pass parent-created object to child");
268 *to
= nsDependentString((PRUnichar
*)JS_GetStringChars(JSVAL_TO_STRING(from
)),
269 JS_GetStringLength(JSVAL_TO_STRING(from
)));
272 if (JSVAL_IS_INT(from
))
273 *to
= JSVAL_TO_INT(from
);
274 else if (JSVAL_IS_DOUBLE(from
))
275 *to
= JSVAL_TO_DOUBLE(from
);
279 *to
= !!JSVAL_TO_BOOLEAN(from
);
282 return with_error(cx
, false, "CPOWs currently cannot handle JSTYPE_XML");
284 return with_error(cx
, false, "Bad jsval type");
289 ObjectWrapperParent::jsval_from_JSVariant(JSContext
* cx
, const JSVariant
& from
,
292 switch (from
.type()) {
293 case JSVariant::Tvoid_t
:
296 case JSVariant::TPObjectWrapperParent
:
297 return jsval_from_PObjectWrapperParent(cx
, from
.get_PObjectWrapperParent(), to
);
298 case JSVariant::TnsString
:
300 JSString
* str
= JS_NewUCStringCopyZ(cx
, from
.get_nsString().BeginReading());
303 *to
= STRING_TO_JSVAL(str
);
306 case JSVariant::Tint
:
307 *to
= INT_TO_JSVAL(from
.get_int());
309 case JSVariant::Tdouble
:
310 return !!JS_NewNumberValue(cx
, from
.get_double(), to
);
311 case JSVariant::Tbool
:
312 *to
= BOOLEAN_TO_JSVAL(from
.get_bool());
320 ObjectWrapperParent::
321 JSObject_to_PObjectWrapperParent(JSContext
* cx
, JSObject
* from
,
322 PObjectWrapperParent
** to
)
328 ObjectWrapperParent
* owp
= Unwrap(cx
, from
);
336 ObjectWrapperParent::
337 JSObject_from_PObjectWrapperParent(JSContext
* cx
,
338 const PObjectWrapperParent
* from
,
341 const ObjectWrapperParent
* owp
=
342 static_cast<const ObjectWrapperParent
*>(from
);
344 ? owp
->GetJSObject(cx
)
345 : JSVAL_TO_OBJECT(JSVAL_NULL
);
350 ObjectWrapperParent::
351 jsval_from_PObjectWrapperParent(JSContext
* cx
,
352 const PObjectWrapperParent
* from
,
356 if (!JSObject_from_PObjectWrapperParent(cx
, from
, &obj
))
358 *to
= OBJECT_TO_JSVAL(obj
);
363 jsid_from_int(JSContext
* cx
, int from
, jsid
* to
)
365 jsval v
= INT_TO_JSVAL(from
);
366 return JS_ValueToId(cx
, v
, to
);
370 jsid_from_nsString(JSContext
* cx
, const nsString
& from
, jsid
* to
)
372 JSString
* str
= JS_NewUCStringCopyZ(cx
, from
.BeginReading());
375 return JS_ValueToId(cx
, STRING_TO_JSVAL(str
), to
);
379 jsval_to_nsString(JSContext
* cx
, jsid from
, nsString
* to
)
383 if (JS_IdToValue(cx
, from
, &idval
) &&
384 (str
= JS_ValueToString(cx
, idval
))) {
385 *to
= JS_GetStringChars(str
);
392 ObjectWrapperParent::CPOW_AddProperty(JSContext
*cx
, JSObject
*obj
, jsid id
,
395 CPOW_LOG(("Calling CPOW_AddProperty (%s)...",
396 JSVAL_TO_CSTR(cx
, id
)));
398 ObjectWrapperParent
* self
= Unwrap(cx
, obj
);
400 return with_error(cx
, JS_FALSE
, "Unwrapping failed in CPOW_AddProperty");
402 if (AutoResolveFlag::IsSet(cx
, obj
))
405 AutoCheckOperation
aco(cx
, self
);
409 if (!jsval_to_nsString(cx
, id
, &in_id
))
412 return (self
->Manager()->RequestRunToCompletion() &&
413 self
->CallAddProperty(in_id
,
419 ObjectWrapperParent::CPOW_GetProperty(JSContext
*cx
, JSObject
*obj
, jsid id
,
422 CPOW_LOG(("Calling CPOW_GetProperty (%s)...",
423 JSVAL_TO_CSTR(cx
, id
)));
425 ObjectWrapperParent
* self
= Unwrap(cx
, obj
);
427 return with_error(cx
, JS_FALSE
, "Unwrapping failed in CPOW_GetProperty");
429 AutoCheckOperation
aco(cx
, self
);
433 if (!jsval_to_nsString(cx
, id
, &in_id
))
438 return (self
->Manager()->RequestRunToCompletion() &&
439 self
->CallGetProperty(in_id
,
440 aco
.StatusPtr(), &out_v
) &&
442 self
->jsval_from_JSVariant(cx
, out_v
, vp
));
446 ObjectWrapperParent::CPOW_SetProperty(JSContext
*cx
, JSObject
*obj
, jsid id
,
449 CPOW_LOG(("Calling CPOW_SetProperty (%s)...",
450 JSVAL_TO_CSTR(cx
, id
)));
452 ObjectWrapperParent
* self
= Unwrap(cx
, obj
);
454 return with_error(cx
, JS_FALSE
, "Unwrapping failed in CPOW_SetProperty");
456 AutoCheckOperation
aco(cx
, self
);
461 if (!jsval_to_nsString(cx
, id
, &in_id
) ||
462 !self
->jsval_to_JSVariant(cx
, *vp
, &in_v
))
467 return (self
->Manager()->RequestRunToCompletion() &&
468 self
->CallSetProperty(in_id
, in_v
,
469 aco
.StatusPtr(), &out_v
) &&
471 self
->jsval_from_JSVariant(cx
, out_v
, vp
));
475 ObjectWrapperParent::CPOW_DelProperty(JSContext
*cx
, JSObject
*obj
, jsid id
,
478 CPOW_LOG(("Calling CPOW_DelProperty (%s)...",
479 JSVAL_TO_CSTR(cx
, id
)));
481 ObjectWrapperParent
* self
= Unwrap(cx
, obj
);
483 return with_error(cx
, JS_FALSE
, "Unwrapping failed in CPOW_DelProperty");
485 AutoCheckOperation
aco(cx
, self
);
489 if (!jsval_to_nsString(cx
, id
, &in_id
))
494 return (self
->Manager()->RequestRunToCompletion() &&
495 self
->CallDelProperty(in_id
,
496 aco
.StatusPtr(), &out_v
) &&
498 jsval_from_JSVariant(cx
, out_v
, vp
));
502 ObjectWrapperParent::NewEnumerateInit(JSContext
* cx
, jsval
* statep
, jsid
* idp
)
504 AutoCheckOperation
aco(cx
, this);
509 return (CallNewEnumerateInit(aco
.StatusPtr(), &out_state
, &out_id
) &&
511 jsval_from_JSVariant(cx
, out_state
, statep
) &&
512 (!idp
|| jsid_from_int(cx
, out_id
, idp
)));
516 ObjectWrapperParent::NewEnumerateNext(JSContext
* cx
, jsval
* statep
, jsid
* idp
)
518 AutoCheckOperation
aco(cx
, this);
522 if (!jsval_to_JSVariant(cx
, *statep
, &in_state
))
528 if (CallNewEnumerateNext(in_state
,
529 aco
.StatusPtr(), &out_state
, &out_id
) &&
531 jsval_from_JSVariant(cx
, out_state
, statep
) &&
532 jsid_from_nsString(cx
, out_id
, idp
))
534 JSObject
* obj
= GetJSObject(cx
);
535 AutoResolveFlag
arf(cx
, obj
);
536 return JS_DefinePropertyById(cx
, obj
, *idp
, JSVAL_VOID
, NULL
, NULL
,
543 ObjectWrapperParent::NewEnumerateDestroy(JSContext
* cx
, jsval state
)
545 AutoCheckOperation
aco(cx
, this);
549 if (!jsval_to_JSVariant(cx
, state
, &in_state
))
552 return SendNewEnumerateDestroy(in_state
);
556 ObjectWrapperParent::CPOW_NewEnumerate(JSContext
*cx
, JSObject
*obj
,
557 JSIterateOp enum_op
, jsval
*statep
,
560 CPOW_LOG(("Calling CPOW_NewEnumerate..."));
562 ObjectWrapperParent
* self
= Unwrap(cx
, obj
);
564 return with_error(cx
, JS_FALSE
, "Unwrapping failed in CPOW_NewEnumerate");
567 case JSENUMERATE_INIT
:
568 case JSENUMERATE_INIT_ALL
:
569 self
->Manager()->RequestRunToCompletion();
570 return self
->NewEnumerateInit(cx
, statep
, idp
);
571 case JSENUMERATE_NEXT
:
572 return self
->NewEnumerateNext(cx
, statep
, idp
);
573 case JSENUMERATE_DESTROY
:
574 return self
->NewEnumerateDestroy(cx
, *statep
);
577 NS_NOTREACHED("Unknown enum_op value in CPOW_NewEnumerate");
582 ObjectWrapperParent::CPOW_NewResolve(JSContext
*cx
, JSObject
*obj
, jsid id
,
583 uintN flags
, JSObject
**objp
)
585 CPOW_LOG(("Calling CPOW_NewResolve (%s)...",
586 JSVAL_TO_CSTR(cx
, id
)));
588 ObjectWrapperParent
* self
= Unwrap(cx
, obj
);
590 return with_error(cx
, JS_FALSE
, "Unwrapping failed in CPOW_NewResolve");
592 AutoCheckOperation
aco(cx
, self
);
596 if (!jsval_to_nsString(cx
, id
, &in_id
))
599 PObjectWrapperParent
* out_pobj
;
601 if (!self
->Manager()->RequestRunToCompletion() ||
602 !self
->CallNewResolve(in_id
, flags
,
603 aco
.StatusPtr(), &out_pobj
) ||
605 !JSObject_from_PObjectWrapperParent(cx
, out_pobj
, objp
))
609 AutoResolveFlag
arf(cx
, *objp
);
610 JS_DefinePropertyById(cx
, *objp
, id
, JSVAL_VOID
, NULL
, NULL
,
617 ObjectWrapperParent::CPOW_Convert(JSContext
*cx
, JSObject
*obj
, JSType type
,
620 CPOW_LOG(("Calling CPOW_Convert (to %s)...",
621 JS_GetTypeName(cx
, type
)));
623 ObjectWrapperParent
* self
= Unwrap(cx
, obj
);
625 return with_error(cx
, JS_FALSE
, "Unwrapping failed in CPOW_Convert");
627 *vp
= OBJECT_TO_JSVAL(obj
);
633 ObjectWrapperParent::CPOW_Finalize(JSContext
* cx
, JSObject
* obj
)
635 CPOW_LOG(("Calling CPOW_Finalize..."));
637 ObjectWrapperParent
* self
= Unwrap(cx
, obj
);
640 unused
<< ObjectWrapperParent::Send__delete__(self
);
645 ObjectWrapperParent::CPOW_Call(JSContext
* cx
, uintN argc
, jsval
* vp
)
647 CPOW_LOG(("Calling CPOW_Call..."));
649 JSObject
* thisobj
= JS_THIS_OBJECT(cx
, vp
);
653 ObjectWrapperParent
* function
=
654 Unwrap(cx
, JSVAL_TO_OBJECT(JS_CALLEE(cx
, vp
)));
656 return with_error(cx
, JS_FALSE
, "Could not unwrap CPOW function");
658 AutoCheckOperation
aco(cx
, function
);
660 ObjectWrapperParent
* receiver
= Unwrap(cx
, thisobj
);
662 // Substitute child global for parent global object.
663 // TODO First make sure we're really replacing the global object?
664 ContextWrapperParent
* manager
=
665 static_cast<ContextWrapperParent
*>(function
->Manager());
666 receiver
= manager
->GetGlobalObjectWrapper();
669 nsTArray
<JSVariant
> in_argv(argc
);
670 jsval
* argv
= JS_ARGV(cx
, vp
);
671 for (uintN i
= 0; i
< argc
; i
++)
672 if (!jsval_to_JSVariant(cx
, argv
[i
], in_argv
.AppendElement()))
677 return (function
->Manager()->RequestRunToCompletion() &&
678 function
->CallCall(receiver
, in_argv
,
679 aco
.StatusPtr(), &out_rval
) &&
681 jsval_from_JSVariant(cx
, out_rval
, vp
));
685 ObjectWrapperParent::CPOW_Construct(JSContext
* cx
, uintN argc
, jsval
* vp
)
687 CPOW_LOG(("Calling CPOW_Construct..."));
689 ObjectWrapperParent
* constructor
= Unwrap(cx
, JSVAL_TO_OBJECT(JS_CALLEE(cx
, vp
)));
691 return with_error(cx
, JS_FALSE
, "Could not unwrap CPOW constructor function");
693 AutoCheckOperation
aco(cx
, constructor
);
695 nsTArray
<JSVariant
> in_argv(argc
);
696 jsval
* argv
= JS_ARGV(cx
, vp
);
697 for (uintN i
= 0; i
< argc
; i
++)
698 if (!jsval_to_JSVariant(cx
, argv
[i
], in_argv
.AppendElement()))
701 PObjectWrapperParent
* out_powp
;
703 return (constructor
->Manager()->RequestRunToCompletion() &&
704 constructor
->CallConstruct(in_argv
, aco
.StatusPtr(), &out_powp
) &&
706 jsval_from_PObjectWrapperParent(cx
, out_powp
, vp
));
710 ObjectWrapperParent::CPOW_HasInstance(JSContext
*cx
, JSObject
*obj
, const jsval
*v
,
713 CPOW_LOG(("Calling CPOW_HasInstance..."));
717 ObjectWrapperParent
* self
= Unwrap(cx
, obj
);
719 return with_error(cx
, JS_FALSE
, "Unwrapping failed in CPOW_HasInstance");
721 AutoCheckOperation
aco(cx
, self
);
725 if (!jsval_to_JSVariant(cx
, *v
, &in_v
))
728 return (self
->Manager()->RequestRunToCompletion() &&
729 self
->CallHasInstance(in_v
,
730 aco
.StatusPtr(), bp
) &&
735 ObjectWrapperParent::CPOW_Equality(JSContext
*cx
, JSObject
*obj
, const jsval
*v
,
738 CPOW_LOG(("Calling CPOW_Equality..."));
742 ObjectWrapperParent
* self
= Unwrap(cx
, obj
);
744 return with_error(cx
, JS_FALSE
, "Unwrapping failed in CPOW_Equality");
746 if (JSVAL_IS_PRIMITIVE(*v
))
749 ObjectWrapperParent
* other
= Unwrap(cx
, JSVAL_TO_OBJECT(*v
));
753 *bp
= (self
== other
);