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 "base/basictypes.h"
44 #include "mozilla/jsipc/ContextWrapperChild.h"
45 #include "mozilla/jsipc/ObjectWrapperChild.h"
46 #include "mozilla/jsipc/CPOWTypes.h"
49 #include "nsAutoPtr.h"
51 #include "nsContentUtils.h"
52 #include "nsIJSContextStack.h"
53 #include "nsJSUtils.h"
55 using namespace mozilla::jsipc
;
60 class AutoContextPusher
{
63 JSAutoRequest mRequest
;
64 JSContext
* const mContext
;
65 const uint32 mSavedOptions
;
66 JS_DECL_USE_GUARD_OBJECT_NOTIFIER
70 AutoContextPusher(JSContext
* cx
71 JS_GUARD_OBJECT_NOTIFIER_PARAM
)
74 , mSavedOptions(JS_SetOptions(cx
, (JS_GetOptions(cx
) |
75 JSOPTION_DONT_REPORT_UNCAUGHT
)))
77 JS_GUARD_OBJECT_NOTIFIER_INIT
;
78 mStack
.Push(cx
, PR_FALSE
);
81 ~AutoContextPusher() {
83 JS_SetOptions(mContext
, mSavedOptions
);
90 OperationStatus
* mStatusPtr
;
92 StatusPtrOwner() : mStatusPtr(NULL
) {}
93 void SetStatusPtr(OperationStatus
* statusPtr
) {
94 mStatusPtr
= statusPtr
;
95 // By default, initialize mStatusPtr to failure without an
96 // exception. Doing so only when the union is uninitialized
97 // allows AutoCheckOperation classes to be nested on the
98 // stack, just in case AnswerConstruct, for example, calls
99 // AnswerCall (as it once did, before there were unrelated
100 // problems with that approach).
101 if (mStatusPtr
->type() == OperationStatus::T__None
)
102 *mStatusPtr
= JS_FALSE
;
104 OperationStatus
* StatusPtr() {
105 NS_ASSERTION(mStatusPtr
, "Should have called SetStatusPtr by now.");
110 typedef AutoCheckOperationBase
<StatusPtrOwner
> ACOBase
;
112 class AutoCheckOperation
: public ACOBase
114 JS_DECL_USE_GUARD_OBJECT_NOTIFIER
116 AutoCheckOperation(ObjectWrapperChild
* owc
,
117 OperationStatus
* statusPtr
118 JS_GUARD_OBJECT_NOTIFIER_PARAM
)
121 JS_GUARD_OBJECT_NOTIFIER_INIT
;
122 SetStatusPtr(statusPtr
);
128 ObjectWrapperChild::CheckOperation(JSContext
*,
129 OperationStatus
* status
)
131 NS_PRECONDITION(status
->type() != OperationStatus::T__None
,
132 "Checking an uninitialized operation.");
134 JSContext
* cx
= Manager()->GetContext();
137 if (JS_GetPendingException(cx
, &thrown
)) {
138 NS_ASSERTION(!(status
->type() == OperationStatus::TJSBool
&&
139 status
->get_JSBool()),
140 "Operation succeeded but exception was thrown?");
142 if (!jsval_to_JSVariant(cx
, thrown
, &exception
))
143 exception
= void_t(); // XXX Useful?
145 JS_ClearPendingException(cx
);
149 ObjectWrapperChild::ObjectWrapperChild(JSContext
* cx
, JSObject
* obj
)
152 JSAutoRequest
request(cx
);
156 JS_AddObjectRoot(cx
, &mObj
);
157 NS_ASSERTION(added
, "ObjectWrapperChild constructor failed to root JSObject*");
161 ObjectWrapperChild::ActorDestroy(ActorDestroyReason why
)
163 JSContext
* cx
= Manager()->GetContext();
164 JSAutoRequest
request(cx
);
165 JS_RemoveObjectRoot(cx
, &mObj
);
169 ObjectWrapperChild::JSObject_to_JSVariant(JSContext
* cx
, JSObject
* from
,
172 *to
= Manager()->GetOrCreateWrapper(from
);
177 ObjectWrapperChild::jsval_to_JSVariant(JSContext
* cx
, jsval from
, JSVariant
* to
)
179 switch (JS_TypeOfValue(cx
, from
)) {
184 if (from
!= JSVAL_NULL
)
187 case JSTYPE_FUNCTION
:
190 return JSObject_to_JSVariant(cx
, JSVAL_TO_OBJECT(from
), to
);
193 nsDependentJSString depStr
;
194 if (!depStr
.init(cx
, from
))
200 if (JSVAL_IS_INT(from
))
201 *to
= JSVAL_TO_INT(from
);
202 else if (JSVAL_IS_DOUBLE(from
))
203 *to
= JSVAL_TO_DOUBLE(from
);
207 *to
= !!JSVAL_TO_BOOLEAN(from
);
218 JSObject_from_PObjectWrapperChild(JSContext
*,
219 const PObjectWrapperChild
* from
,
222 const ObjectWrapperChild
* owc
=
223 static_cast<const ObjectWrapperChild
*>(from
);
224 *to
= owc
? owc
->mObj
: NULL
;
229 ObjectWrapperChild::JSObject_from_JSVariant(JSContext
* cx
,
230 const JSVariant
& from
,
233 if (from
.type() != JSVariant::TPObjectWrapperChild
)
235 return JSObject_from_PObjectWrapperChild(cx
,
236 from
.get_PObjectWrapperChild(),
241 ObjectWrapperChild::jsval_from_JSVariant(JSContext
* cx
, const JSVariant
& from
,
244 switch (from
.type()) {
245 case JSVariant::Tvoid_t
:
248 case JSVariant::TPObjectWrapperChild
:
251 if (!JSObject_from_JSVariant(cx
, from
, &obj
))
253 *to
= OBJECT_TO_JSVAL(obj
);
256 case JSVariant::TnsString
:
258 const nsString
& str
= from
.get_nsString();
259 JSString
* s
= JS_NewUCStringCopyN(cx
,
264 *to
= STRING_TO_JSVAL(s
);
267 case JSVariant::Tint
:
268 *to
= INT_TO_JSVAL(from
.get_int());
270 case JSVariant::Tdouble
:
271 return !!JS_NewNumberValue(cx
, from
.get_double(), to
);
272 case JSVariant::Tbool
:
273 *to
= BOOLEAN_TO_JSVAL(from
.get_bool());
281 ObjectWrapperChild::Manager()
283 PContextWrapperChild
* pcwc
= PObjectWrapperChild::Manager();
284 return static_cast<ContextWrapperChild
*>(pcwc
);
288 jsid_to_nsString(JSContext
* cx
, jsid from
, nsString
* to
)
290 if (JSID_IS_STRING(from
)) {
292 const jschar
* chars
= JS_GetInternedStringCharsAndLength(JSID_TO_STRING(from
), &length
);
293 *to
= nsDependentString(chars
, length
);
300 jsid_from_nsString(JSContext
* cx
, const nsString
& from
, jsid
* to
)
302 JSString
* str
= JS_NewUCStringCopyN(cx
, from
.BeginReading(), from
.Length());
305 return JS_ValueToId(cx
, STRING_TO_JSVAL(str
), to
);
309 // The general schema for ObjectWrapperChild::Answer* methods:
311 ObjectWrapperChild::AnswerSomething(/* in-parameters */
312 /* out-parameters */)
314 // initialize out-parameters for failure
315 JSContext
* cx
= Manager()->GetContext();
316 AutoContextPusher
acp(cx
);
317 // validate in-parameters, else return false
318 // successfully perform local JS operations, else return true
319 // perform out-parameter conversions, else return false
322 // There's an important subtlety here: though a local JS operation may
323 // fail, leaving out-parameters uninitialized, we must initialize all
324 // out-parameters when reporting success (returning true) to the IPC
325 // messaging system. See AnswerGetProperty for illustration.
329 ObjectWrapperChild::AnswerAddProperty(const nsString
& id
,
330 OperationStatus
* status
)
334 JSContext
* cx
= Manager()->GetContext();
335 AutoContextPusher
acp(cx
);
336 AutoCheckOperation
aco(this, status
);
338 if (!jsid_from_nsString(cx
, id
, &interned_id
))
341 *status
= JS_DefinePropertyById(cx
, mObj
, interned_id
, JSVAL_VOID
,
347 ObjectWrapperChild::AnswerGetProperty(const nsString
& id
,
348 OperationStatus
* status
, JSVariant
* vp
)
353 JSContext
* cx
= Manager()->GetContext();
354 AutoContextPusher
acp(cx
);
355 AutoCheckOperation
aco(this, status
);
357 if (!jsid_from_nsString(cx
, id
, &interned_id
))
360 *status
= JS_GetPropertyById(cx
, mObj
, interned_id
, &val
);
362 // Since we fully expect this call to jsval_to_JSVariant to return
363 // true, we can't just leave vp uninitialized when JS_GetPropertyById
364 // returns JS_FALSE. This pitfall could be avoided in general if IPDL
365 // ensured that outparams were pre-initialized to some default value
366 // (XXXfixme cjones?).
367 return jsval_to_JSVariant(cx
, aco
.Ok() ? val
: JSVAL_VOID
, vp
);
371 ObjectWrapperChild::AnswerSetProperty(const nsString
& id
, const JSVariant
& v
,
372 OperationStatus
* status
, JSVariant
* vp
)
379 JSContext
* cx
= Manager()->GetContext();
380 AutoContextPusher
acp(cx
);
381 AutoCheckOperation
aco(this, status
);
383 if (!jsid_from_nsString(cx
, id
, &interned_id
) ||
384 !jsval_from_JSVariant(cx
, v
, &val
))
387 *status
= JS_SetPropertyById(cx
, mObj
, interned_id
, &val
);
389 return jsval_to_JSVariant(cx
, aco
.Ok() ? val
: JSVAL_VOID
, vp
);
393 ObjectWrapperChild::AnswerDelProperty(const nsString
& id
,
394 OperationStatus
* status
, JSVariant
* vp
)
399 JSContext
* cx
= Manager()->GetContext();
400 AutoContextPusher
acp(cx
);
401 AutoCheckOperation
aco(this, status
);
403 if (!jsid_from_nsString(cx
, id
, &interned_id
))
406 *status
= JS_DeletePropertyById2(cx
, mObj
, interned_id
, &val
);
408 return jsval_to_JSVariant(cx
, aco
.Ok() ? val
: JSVAL_VOID
, vp
);
411 static const PRUint32 sNextIdIndexSlot
= 0;
412 static const PRUint32 sNumNewEnumerateStateSlots
= 1;
415 CPOW_NewEnumerateState_Finalize(JSContext
* cx
, JSObject
* state
)
417 nsTArray
<nsString
>* strIds
=
418 static_cast<nsTArray
<nsString
>*>(JS_GetPrivate(cx
, state
));
422 JS_SetPrivate(cx
, state
, NULL
);
426 // Similar to IteratorClass in XPCWrapper.cpp
427 static const JSClass sCPOW_NewEnumerateState_JSClass
= {
428 "CPOW NewEnumerate State",
429 JSCLASS_HAS_PRIVATE
|
430 JSCLASS_HAS_RESERVED_SLOTS(sNumNewEnumerateStateSlots
),
431 JS_PropertyStub
, JS_PropertyStub
,
432 JS_PropertyStub
, JS_StrictPropertyStub
,
433 JS_EnumerateStub
, JS_ResolveStub
,
434 JS_ConvertStub
, CPOW_NewEnumerateState_Finalize
,
435 JSCLASS_NO_OPTIONAL_MEMBERS
439 ObjectWrapperChild::AnswerNewEnumerateInit(/* no in-parameters */
440 OperationStatus
* status
, JSVariant
* statep
, int* idp
)
444 JSContext
* cx
= Manager()->GetContext();
445 AutoContextPusher
acp(cx
);
446 AutoCheckOperation
aco(this, status
);
448 JSClass
* clasp
= const_cast<JSClass
*>(&sCPOW_NewEnumerateState_JSClass
);
449 JSObject
* state
= JS_NewObjectWithGivenProto(cx
, clasp
, NULL
, NULL
);
452 AutoObjectRooter
tvr(cx
, state
);
454 for (JSObject
* proto
= mObj
;
456 proto
= JS_GetPrototype(cx
, proto
))
458 AutoIdArray
ids(cx
, JS_Enumerate(cx
, proto
));
459 for (uint i
= 0; i
< ids
.length(); ++i
)
460 JS_DefinePropertyById(cx
, state
, ids
[i
], JSVAL_VOID
,
461 NULL
, NULL
, JSPROP_ENUMERATE
| JSPROP_SHARED
);
464 InfallibleTArray
<nsString
>* strIds
;
466 AutoIdArray
ids(cx
, JS_Enumerate(cx
, state
));
469 strIds
= new InfallibleTArray
<nsString
>(ids
.length());
470 for (uint i
= 0; i
< ids
.length(); ++i
)
471 if (!jsid_to_nsString(cx
, ids
[i
], strIds
->AppendElement())) {
476 *idp
= strIds
->Length();
478 *status
= (JS_SetPrivate(cx
, state
, strIds
) &&
479 JS_SetReservedSlot(cx
, state
, sNextIdIndexSlot
,
481 JSObject_to_JSVariant(cx
, state
, statep
));
487 ObjectWrapperChild::AnswerNewEnumerateNext(const JSVariant
& in_state
,
488 OperationStatus
* status
, JSVariant
* statep
, nsString
* idp
)
496 JSContext
* cx
= Manager()->GetContext();
497 AutoContextPusher
acp(cx
);
498 AutoCheckOperation
aco(this, status
);
500 if (!JSObject_from_JSVariant(cx
, in_state
, &state
))
503 InfallibleTArray
<nsString
>* strIds
=
504 static_cast<InfallibleTArray
<nsString
>*>(JS_GetPrivate(cx
, state
));
506 if (!strIds
|| !JS_GetReservedSlot(cx
, state
, sNextIdIndexSlot
, &v
))
509 jsuint i
= JSVAL_TO_INT(v
);
510 NS_ASSERTION(i
>= 0, "Index of next jsid negative?");
511 NS_ASSERTION(i
<= strIds
->Length(), "Index of next jsid too large?");
513 if (jsuint(i
) == strIds
->Length()) {
515 return JSObject_to_JSVariant(cx
, NULL
, statep
);
518 *idp
= strIds
->ElementAt(i
);
519 *status
= JS_SetReservedSlot(cx
, state
, sNextIdIndexSlot
,
520 INT_TO_JSVAL(i
+ 1));
525 ObjectWrapperChild::RecvNewEnumerateDestroy(const JSVariant
& in_state
)
529 JSContext
* cx
= Manager()->GetContext();
530 AutoContextPusher
acp(cx
);
532 if (!JSObject_from_JSVariant(cx
, in_state
, &state
))
535 CPOW_NewEnumerateState_Finalize(cx
, state
);
541 ObjectWrapperChild::AnswerNewResolve(const nsString
& id
, const int& flags
,
542 OperationStatus
* status
, PObjectWrapperChild
** obj2
)
548 JSContext
* cx
= Manager()->GetContext();
549 AutoContextPusher
acp(cx
);
550 AutoCheckOperation
aco(this, status
);
552 if (!jsid_from_nsString(cx
, id
, &interned_id
))
555 CPOW_LOG(("new-resolving \"%s\"...",
556 NS_ConvertUTF16toUTF8(id
).get()));
558 JSPropertyDescriptor desc
;
559 if (!JS_GetPropertyDescriptorById(cx
, mObj
, interned_id
, flags
, &desc
))
565 *obj2
= Manager()->GetOrCreateWrapper(desc
.obj
);
571 ObjectWrapperChild::AnswerConvert(const JSType
& type
,
572 OperationStatus
* status
, JSVariant
* vp
)
575 JSContext
* cx
= Manager()->GetContext();
576 AutoContextPusher
acp(cx
);
577 AutoCheckOperation
aco(this, status
);
578 *status
= JS_ConvertValue(cx
, OBJECT_TO_JSVAL(mObj
), type
, &v
);
579 return jsval_to_JSVariant(cx
, aco
.Ok() ? v
: JSVAL_VOID
, vp
);
583 // Should be an overestimate of typical JS function arity.
584 typedef nsAutoTArray
<jsval
, 5> AutoJSArgs
;
588 ObjectWrapperChild::AnswerCall(PObjectWrapperChild
* receiver
, const InfallibleTArray
<JSVariant
>& argv
,
589 OperationStatus
* status
, JSVariant
* rval
)
591 JSContext
* cx
= Manager()->GetContext();
592 AutoContextPusher
acp(cx
);
593 AutoCheckOperation
aco(this, status
);
596 if (!JSObject_from_PObjectWrapperChild(cx
, receiver
, &obj
))
600 PRUint32 argc
= argv
.Length();
601 jsval
*jsargs
= args
.AppendElements(argc
);
604 AutoArrayRooter
tvr(cx
, argc
, jsargs
);
606 for (PRUint32 i
= 0; i
< argc
; ++i
)
607 if (!jsval_from_JSVariant(cx
, argv
.ElementAt(i
), jsargs
+ i
))
611 *status
= JS_CallFunctionValue(cx
, obj
, OBJECT_TO_JSVAL(mObj
),
612 argv
.Length(), jsargs
, &rv
);
614 return jsval_to_JSVariant(cx
, aco
.Ok() ? rv
: JSVAL_VOID
, rval
);
618 ObjectWrapperChild::AnswerConstruct(const InfallibleTArray
<JSVariant
>& argv
,
619 OperationStatus
* status
, PObjectWrapperChild
** rval
)
621 JSContext
* cx
= Manager()->GetContext();
622 AutoContextPusher
acp(cx
);
623 AutoCheckOperation
aco(this, status
);
626 PRUint32 argc
= argv
.Length();
627 jsval
* jsargs
= args
.AppendElements(argc
);
630 AutoArrayRooter
tvr(cx
, argc
, jsargs
);
632 for (PRUint32 i
= 0; i
< argc
; ++i
)
633 if (!jsval_from_JSVariant(cx
, argv
.ElementAt(i
), jsargs
+ i
))
636 JSObject
* obj
= JS_New(cx
, mObj
, argc
, jsargs
);
639 *rval
= Manager()->GetOrCreateWrapper(obj
);
645 ObjectWrapperChild::AnswerHasInstance(const JSVariant
& v
,
646 OperationStatus
* status
, JSBool
* bp
)
649 JSContext
* cx
= Manager()->GetContext();
650 AutoContextPusher
acp(cx
);
651 AutoCheckOperation
aco(this, status
);
652 if (!jsval_from_JSVariant(cx
, v
, &candidate
))
654 *status
= JS_HasInstance(cx
, mObj
, candidate
, bp
);