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/. */
10 #include "jscompartment.h"
15 #include "vm/ErrorObject.h"
16 #include "vm/WrapperObject.h"
18 #include "jsobjinlines.h"
21 using namespace js::gc
;
24 * Wrapper forwards this call directly to the wrapped object for efficiency
25 * and transparency. In particular, the hint is needed to properly stringify
26 * Date objects in certain cases - see bug 646129. Note also the
27 * SecurityWrapper overrides this trap to avoid information leaks. See bug
31 Wrapper::defaultValue(JSContext
* cx
, HandleObject proxy
, JSType hint
, MutableHandleValue vp
) const
33 vp
.set(ObjectValue(*proxy
->as
<ProxyObject
>().target()));
34 if (hint
== JSTYPE_VOID
)
35 return ToPrimitive(cx
, vp
);
36 return ToPrimitive(cx
, hint
, vp
);
40 Wrapper::New(JSContext
* cx
, JSObject
* obj
, JSObject
* parent
, const Wrapper
* handler
,
41 const WrapperOptions
* options
)
45 RootedValue
priv(cx
, ObjectValue(*obj
));
46 mozilla::Maybe
<WrapperOptions
> opts
;
49 opts
->selectDefaultClass(obj
->isCallable());
52 return NewProxyObject(cx
, handler
, priv
, options
->proto(), parent
, *options
);
56 Wrapper::Renew(JSContext
* cx
, JSObject
* existing
, JSObject
* obj
, const Wrapper
* handler
)
58 JS_ASSERT(!obj
->isCallable());
59 existing
->as
<ProxyObject
>().renew(cx
, handler
, ObjectValue(*obj
));
64 Wrapper::wrapperHandler(JSObject
* wrapper
)
66 JS_ASSERT(wrapper
->is
<WrapperObject
>());
67 return static_cast<const Wrapper
*>(wrapper
->as
<ProxyObject
>().handler());
71 Wrapper::wrappedObject(JSObject
* wrapper
)
73 JS_ASSERT(wrapper
->is
<WrapperObject
>());
74 return wrapper
->as
<ProxyObject
>().target();
77 JS_FRIEND_API(JSObject
*)
78 js::UncheckedUnwrap(JSObject
* wrapped
, bool stopAtOuter
, unsigned* flagsp
)
82 if (!wrapped
->is
<WrapperObject
>() ||
83 MOZ_UNLIKELY(stopAtOuter
&& wrapped
->getClass()->ext
.innerObject
))
87 flags
|= Wrapper::wrapperHandler(wrapped
)->flags();
88 wrapped
= wrapped
->as
<ProxyObject
>().private_().toObjectOrNull();
90 // This can be called from DirectProxyHandler::weakmapKeyDelegate() on a
91 // wrapper whose referent has been moved while it is still unmarked.
93 wrapped
= MaybeForwarded(wrapped
);
100 JS_FRIEND_API(JSObject
*)
101 js::CheckedUnwrap(JSObject
* obj
, bool stopAtOuter
)
104 JSObject
* wrapper
= obj
;
105 obj
= UnwrapOneChecked(obj
, stopAtOuter
);
106 if (!obj
|| obj
== wrapper
)
111 JS_FRIEND_API(JSObject
*)
112 js::UnwrapOneChecked(JSObject
* obj
, bool stopAtOuter
)
114 if (!obj
->is
<WrapperObject
>() ||
115 MOZ_UNLIKELY(!!obj
->getClass()->ext
.innerObject
&& stopAtOuter
))
120 const Wrapper
* handler
= Wrapper::wrapperHandler(obj
);
121 return handler
->hasSecurityPolicy() ? nullptr : Wrapper::wrappedObject(obj
);
125 js::IsCrossCompartmentWrapper(JSObject
* obj
)
127 return IsWrapper(obj
) &&
128 !!(Wrapper::wrapperHandler(obj
)->flags() & Wrapper::CROSS_COMPARTMENT
);
131 const char Wrapper::family
= 0;
132 const Wrapper
Wrapper::singleton((unsigned)0);
133 const Wrapper
Wrapper::singletonWithPrototype((unsigned)0, true);
134 JSObject
* Wrapper::defaultProto
= TaggedProto::LazyProto
;
139 js::TransparentObjectWrapper(JSContext
* cx
, HandleObject existing
, HandleObject obj
,
142 // Allow wrapping outer window proxies.
143 JS_ASSERT(!obj
->is
<WrapperObject
>() || obj
->getClass()->ext
.innerObject
);
144 return Wrapper::New(cx
, obj
, parent
, &CrossCompartmentWrapper::singleton
);
147 ErrorCopier::~ErrorCopier()
149 JSContext
* cx
= ac
->context()->asJSContext();
150 if (ac
->origin() != cx
->compartment() && cx
->isExceptionPending()) {
152 if (cx
->getPendingException(&exc
) && exc
.isObject() && exc
.toObject().is
<ErrorObject
>()) {
153 cx
->clearPendingException();
155 Rooted
<ErrorObject
*> errObj(cx
, &exc
.toObject().as
<ErrorObject
>());
156 JSObject
* copyobj
= js_CopyErrorObject(cx
, errObj
);
158 cx
->setPendingException(ObjectValue(*copyobj
));
163 /* Cross compartment wrappers. */
165 bool Wrapper::finalizeInBackground(Value priv
) const
167 if (!priv
.isObject())
171 * Make the 'background-finalized-ness' of the wrapper the same as the
172 * wrapped object, to allow transplanting between them.
174 * If the wrapped object is in the nursery then we know it doesn't have a
175 * finalizer, and so background finalization is ok.
177 if (IsInsideNursery(&priv
.toObject()))
179 return IsBackgroundFinalized(priv
.toObject().tenuredGetAllocKind());
182 #define PIERCE(cx, wrapper, pre, op, post) \
186 AutoCompartment call(cx, wrappedObject(wrapper)); \
187 ok = (pre) && (op); \
189 return ok && (post); \
192 #define NOTHING (true)
195 CrossCompartmentWrapper::isExtensible(JSContext
* cx
, HandleObject wrapper
, bool* extensible
) const
199 Wrapper::isExtensible(cx
, wrapper
, extensible
),
204 CrossCompartmentWrapper::preventExtensions(JSContext
* cx
, HandleObject wrapper
) const
208 Wrapper::preventExtensions(cx
, wrapper
),
213 CrossCompartmentWrapper::getPropertyDescriptor(JSContext
* cx
, HandleObject wrapper
, HandleId id
,
214 MutableHandle
<PropertyDescriptor
> desc
) const
218 Wrapper::getPropertyDescriptor(cx
, wrapper
, id
, desc
),
219 cx
->compartment()->wrap(cx
, desc
));
223 CrossCompartmentWrapper::getOwnPropertyDescriptor(JSContext
* cx
, HandleObject wrapper
, HandleId id
,
224 MutableHandle
<PropertyDescriptor
> desc
) const
228 Wrapper::getOwnPropertyDescriptor(cx
, wrapper
, id
, desc
),
229 cx
->compartment()->wrap(cx
, desc
));
233 CrossCompartmentWrapper::defineProperty(JSContext
* cx
, HandleObject wrapper
, HandleId id
,
234 MutableHandle
<PropertyDescriptor
> desc
) const
236 Rooted
<PropertyDescriptor
> desc2(cx
, desc
);
238 cx
->compartment()->wrap(cx
, &desc2
),
239 Wrapper::defineProperty(cx
, wrapper
, id
, &desc2
),
244 CrossCompartmentWrapper::getOwnPropertyNames(JSContext
* cx
, HandleObject wrapper
,
245 AutoIdVector
& props
) const
249 Wrapper::getOwnPropertyNames(cx
, wrapper
, props
),
254 CrossCompartmentWrapper::delete_(JSContext
* cx
, HandleObject wrapper
, HandleId id
, bool* bp
) const
258 Wrapper::delete_(cx
, wrapper
, id
, bp
),
263 CrossCompartmentWrapper::enumerate(JSContext
* cx
, HandleObject wrapper
, AutoIdVector
& props
) const
267 Wrapper::enumerate(cx
, wrapper
, props
),
272 CrossCompartmentWrapper::has(JSContext
* cx
, HandleObject wrapper
, HandleId id
, bool* bp
) const
276 Wrapper::has(cx
, wrapper
, id
, bp
),
281 CrossCompartmentWrapper::hasOwn(JSContext
* cx
, HandleObject wrapper
, HandleId id
, bool* bp
) const
285 Wrapper::hasOwn(cx
, wrapper
, id
, bp
),
290 CrossCompartmentWrapper::get(JSContext
* cx
, HandleObject wrapper
, HandleObject receiver
,
291 HandleId id
, MutableHandleValue vp
) const
293 RootedObject
receiverCopy(cx
, receiver
);
295 AutoCompartment
call(cx
, wrappedObject(wrapper
));
296 if (!cx
->compartment()->wrap(cx
, &receiverCopy
))
299 if (!Wrapper::get(cx
, wrapper
, receiverCopy
, id
, vp
))
302 return cx
->compartment()->wrap(cx
, vp
);
306 CrossCompartmentWrapper::set(JSContext
* cx
, HandleObject wrapper
, HandleObject receiver
,
307 HandleId id
, bool strict
, MutableHandleValue vp
) const
309 RootedObject
receiverCopy(cx
, receiver
);
311 cx
->compartment()->wrap(cx
, &receiverCopy
) &&
312 cx
->compartment()->wrap(cx
, vp
),
313 Wrapper::set(cx
, wrapper
, receiverCopy
, id
, strict
, vp
),
318 CrossCompartmentWrapper::keys(JSContext
* cx
, HandleObject wrapper
, AutoIdVector
& props
) const
322 Wrapper::keys(cx
, wrapper
, props
),
327 * We can reify non-escaping iterator objects instead of having to wrap them. This
328 * allows fast iteration over objects across a compartment boundary.
331 CanReify(HandleValue vp
)
334 return vp
.isObject() &&
335 (obj
= &vp
.toObject())->is
<PropertyIteratorObject
>() &&
336 (obj
->as
<PropertyIteratorObject
>().getNativeIterator()->flags
& JSITER_ENUMERATE
);
339 struct AutoCloseIterator
341 AutoCloseIterator(JSContext
* cx
, JSObject
* obj
) : cx(cx
), obj(cx
, obj
) {}
343 ~AutoCloseIterator() { if (obj
) CloseIterator(cx
, obj
); }
345 void clear() { obj
= nullptr; }
353 Reify(JSContext
* cx
, JSCompartment
* origin
, MutableHandleValue vp
)
355 Rooted
<PropertyIteratorObject
*> iterObj(cx
, &vp
.toObject().as
<PropertyIteratorObject
>());
356 NativeIterator
* ni
= iterObj
->getNativeIterator();
358 AutoCloseIterator
close(cx
, iterObj
);
360 /* Wrap the iteratee. */
361 RootedObject
obj(cx
, ni
->obj
);
362 if (!origin
->wrap(cx
, &obj
))
366 * Wrap the elements in the iterator's snapshot.
367 * N.B. the order of closing/creating iterators is important due to the
368 * implicit cx->enumerators state.
370 size_t length
= ni
->numKeys();
371 bool isKeyIter
= ni
->isKeyIter();
372 AutoIdVector
keys(cx
);
374 if (!keys
.reserve(length
))
376 for (size_t i
= 0; i
< length
; ++i
) {
378 RootedValue
v(cx
, StringValue(ni
->begin()[i
]));
379 if (!ValueToId
<CanGC
>(cx
, v
, &id
))
381 keys
.infallibleAppend(id
);
386 if (!CloseIterator(cx
, iterObj
))
390 if (!VectorToKeyIterator(cx
, obj
, ni
->flags
, keys
, vp
))
393 if (!VectorToValueIterator(cx
, obj
, ni
->flags
, keys
, vp
))
400 CrossCompartmentWrapper::iterate(JSContext
* cx
, HandleObject wrapper
, unsigned flags
,
401 MutableHandleValue vp
) const
404 AutoCompartment
call(cx
, wrappedObject(wrapper
));
405 if (!Wrapper::iterate(cx
, wrapper
, flags
, vp
))
410 return Reify(cx
, cx
->compartment(), vp
);
411 return cx
->compartment()->wrap(cx
, vp
);
415 CrossCompartmentWrapper::call(JSContext
* cx
, HandleObject wrapper
, const CallArgs
& args
) const
417 RootedObject
wrapped(cx
, wrappedObject(wrapper
));
420 AutoCompartment
call(cx
, wrapped
);
422 args
.setCallee(ObjectValue(*wrapped
));
423 if (!cx
->compartment()->wrap(cx
, args
.mutableThisv()))
426 for (size_t n
= 0; n
< args
.length(); ++n
) {
427 if (!cx
->compartment()->wrap(cx
, args
[n
]))
431 if (!Wrapper::call(cx
, wrapper
, args
))
435 return cx
->compartment()->wrap(cx
, args
.rval());
439 CrossCompartmentWrapper::construct(JSContext
* cx
, HandleObject wrapper
, const CallArgs
& args
) const
441 RootedObject
wrapped(cx
, wrappedObject(wrapper
));
443 AutoCompartment
call(cx
, wrapped
);
445 for (size_t n
= 0; n
< args
.length(); ++n
) {
446 if (!cx
->compartment()->wrap(cx
, args
[n
]))
449 if (!Wrapper::construct(cx
, wrapper
, args
))
452 return cx
->compartment()->wrap(cx
, args
.rval());
456 CrossCompartmentWrapper::nativeCall(JSContext
* cx
, IsAcceptableThis test
, NativeImpl impl
,
457 CallArgs srcArgs
) const
459 RootedObject
wrapper(cx
, &srcArgs
.thisv().toObject());
460 JS_ASSERT(srcArgs
.thisv().isMagic(JS_IS_CONSTRUCTING
) ||
461 !UncheckedUnwrap(wrapper
)->is
<CrossCompartmentWrapperObject
>());
463 RootedObject
wrapped(cx
, wrappedObject(wrapper
));
465 AutoCompartment
call(cx
, wrapped
);
466 InvokeArgs
dstArgs(cx
);
467 if (!dstArgs
.init(srcArgs
.length()))
470 Value
* src
= srcArgs
.base();
471 Value
* srcend
= srcArgs
.array() + srcArgs
.length();
472 Value
* dst
= dstArgs
.base();
474 RootedValue
source(cx
);
475 for (; src
< srcend
; ++src
, ++dst
) {
477 if (!cx
->compartment()->wrap(cx
, &source
))
481 // Handle |this| specially. When we rewrap on the other side of the
482 // membrane, we might apply a same-compartment security wrapper that
483 // will stymie this whole process. If that happens, unwrap the wrapper.
484 // This logic can go away when same-compartment security wrappers go away.
485 if ((src
== srcArgs
.base() + 1) && dst
->isObject()) {
486 RootedObject
thisObj(cx
, &dst
->toObject());
487 if (thisObj
->is
<WrapperObject
>() &&
488 Wrapper::wrapperHandler(thisObj
)->hasSecurityPolicy())
490 JS_ASSERT(!thisObj
->is
<CrossCompartmentWrapperObject
>());
491 *dst
= ObjectValue(*Wrapper::wrappedObject(thisObj
));
496 if (!CallNonGenericMethod(cx
, test
, impl
, dstArgs
))
499 srcArgs
.rval().set(dstArgs
.rval());
501 return cx
->compartment()->wrap(cx
, srcArgs
.rval());
505 CrossCompartmentWrapper::hasInstance(JSContext
* cx
, HandleObject wrapper
, MutableHandleValue v
,
508 AutoCompartment
call(cx
, wrappedObject(wrapper
));
509 if (!cx
->compartment()->wrap(cx
, v
))
511 return Wrapper::hasInstance(cx
, wrapper
, v
, bp
);
515 CrossCompartmentWrapper::className(JSContext
* cx
, HandleObject wrapper
) const
517 AutoCompartment
call(cx
, wrappedObject(wrapper
));
518 return Wrapper::className(cx
, wrapper
);
522 CrossCompartmentWrapper::fun_toString(JSContext
* cx
, HandleObject wrapper
, unsigned indent
) const
524 RootedString
str(cx
);
526 AutoCompartment
call(cx
, wrappedObject(wrapper
));
527 str
= Wrapper::fun_toString(cx
, wrapper
, indent
);
531 if (!cx
->compartment()->wrap(cx
, str
.address()))
537 CrossCompartmentWrapper::regexp_toShared(JSContext
* cx
, HandleObject wrapper
, RegExpGuard
* g
) const
539 RegExpGuard
wrapperGuard(cx
);
541 AutoCompartment
call(cx
, wrappedObject(wrapper
));
542 if (!Wrapper::regexp_toShared(cx
, wrapper
, &wrapperGuard
))
546 // Get an equivalent RegExpShared associated with the current compartment.
547 RegExpShared
* re
= wrapperGuard
.re();
548 return cx
->compartment()->regExps
.get(cx
, re
->getSource(), re
->getFlags(), g
);
552 CrossCompartmentWrapper::boxedValue_unbox(JSContext
* cx
, HandleObject wrapper
, MutableHandleValue vp
) const
556 Wrapper::boxedValue_unbox(cx
, wrapper
, vp
),
557 cx
->compartment()->wrap(cx
, vp
));
561 CrossCompartmentWrapper::defaultValue(JSContext
* cx
, HandleObject wrapper
, JSType hint
,
562 MutableHandleValue vp
) const
566 Wrapper::defaultValue(cx
, wrapper
, hint
, vp
),
567 cx
->compartment()->wrap(cx
, vp
));
571 CrossCompartmentWrapper::getPrototypeOf(JSContext
* cx
, HandleObject wrapper
,
572 MutableHandleObject protop
) const
575 RootedObject
wrapped(cx
, wrappedObject(wrapper
));
576 AutoCompartment
call(cx
, wrapped
);
577 if (!JSObject::getProto(cx
, wrapped
, protop
))
580 protop
->setDelegate(cx
);
583 return cx
->compartment()->wrap(cx
, protop
);
587 CrossCompartmentWrapper::setPrototypeOf(JSContext
* cx
, HandleObject wrapper
,
588 HandleObject proto
, bool* bp
) const
590 RootedObject
protoCopy(cx
, proto
);
592 cx
->compartment()->wrap(cx
, &protoCopy
),
593 Wrapper::setPrototypeOf(cx
, wrapper
, protoCopy
, bp
),
597 const CrossCompartmentWrapper
CrossCompartmentWrapper::singleton(0u);
599 /* Security wrappers. */
601 template <class Base
>
603 SecurityWrapper
<Base
>::isExtensible(JSContext
* cx
, HandleObject wrapper
, bool* extensible
) const
605 // Just like BaseProxyHandler, SecurityWrappers claim by default to always
606 // be extensible, so as not to leak information about the state of the
607 // underlying wrapped thing.
612 template <class Base
>
614 SecurityWrapper
<Base
>::preventExtensions(JSContext
* cx
, HandleObject wrapper
) const
617 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, nullptr, JSMSG_UNWRAP_DENIED
);
621 template <class Base
>
623 SecurityWrapper
<Base
>::enter(JSContext
* cx
, HandleObject wrapper
, HandleId id
,
624 Wrapper::Action act
, bool* bp
) const
626 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, nullptr, JSMSG_UNWRAP_DENIED
);
631 template <class Base
>
633 SecurityWrapper
<Base
>::nativeCall(JSContext
* cx
, IsAcceptableThis test
, NativeImpl impl
,
636 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, nullptr, JSMSG_UNWRAP_DENIED
);
640 template <class Base
>
642 SecurityWrapper
<Base
>::setPrototypeOf(JSContext
* cx
, HandleObject wrapper
,
643 HandleObject proto
, bool* bp
) const
645 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, nullptr, JSMSG_UNWRAP_DENIED
);
649 // For security wrappers, we run the DefaultValue algorithm on the wrapper
650 // itself, which means that the existing security policy on operations like
651 // toString() will take effect and do the right thing here.
652 template <class Base
>
654 SecurityWrapper
<Base
>::defaultValue(JSContext
* cx
, HandleObject wrapper
,
655 JSType hint
, MutableHandleValue vp
) const
657 return DefaultValue(cx
, wrapper
, hint
, vp
);
660 template <class Base
>
662 SecurityWrapper
<Base
>::objectClassIs(HandleObject obj
, ESClassValue classValue
, JSContext
* cx
) const
667 template <class Base
>
669 SecurityWrapper
<Base
>::regexp_toShared(JSContext
* cx
, HandleObject obj
, RegExpGuard
* g
) const
671 return Base::regexp_toShared(cx
, obj
, g
);
674 template <class Base
>
676 SecurityWrapper
<Base
>::boxedValue_unbox(JSContext
* cx
, HandleObject obj
, MutableHandleValue vp
) const
682 template <class Base
>
684 SecurityWrapper
<Base
>::defineProperty(JSContext
* cx
, HandleObject wrapper
,
685 HandleId id
, MutableHandle
<PropertyDescriptor
> desc
) const
687 if (desc
.getter() || desc
.setter()) {
688 JSString
* str
= IdToString(cx
, id
);
689 AutoStableStringChars
chars(cx
);
690 const jschar
* prop
= nullptr;
691 if (str
->ensureFlat(cx
) && chars
.initTwoByte(cx
, str
))
692 prop
= chars
.twoByteChars();
693 JS_ReportErrorNumberUC(cx
, js_GetErrorMessage
, nullptr,
694 JSMSG_ACCESSOR_DEF_DENIED
, prop
);
698 return Base::defineProperty(cx
, wrapper
, id
, desc
);
701 template <class Base
>
703 SecurityWrapper
<Base
>::watch(JSContext
* cx
, HandleObject proxy
,
704 HandleId id
, HandleObject callable
) const
706 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, nullptr, JSMSG_UNWRAP_DENIED
);
710 template <class Base
>
712 SecurityWrapper
<Base
>::unwatch(JSContext
* cx
, HandleObject proxy
,
715 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, nullptr, JSMSG_UNWRAP_DENIED
);
720 template class js::SecurityWrapper
<Wrapper
>;
721 template class js::SecurityWrapper
<CrossCompartmentWrapper
>;
724 DeadObjectProxy::isExtensible(JSContext
* cx
, HandleObject proxy
, bool* extensible
) const
726 // This is kind of meaningless, but dead-object semantics aside,
727 // [[Extensible]] always being true is consistent with other proxy types.
733 DeadObjectProxy::preventExtensions(JSContext
* cx
, HandleObject proxy
) const
735 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, nullptr, JSMSG_DEAD_OBJECT
);
740 DeadObjectProxy::getPropertyDescriptor(JSContext
* cx
, HandleObject wrapper
, HandleId id
,
741 MutableHandle
<PropertyDescriptor
> desc
) const
743 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, nullptr, JSMSG_DEAD_OBJECT
);
748 DeadObjectProxy::getOwnPropertyDescriptor(JSContext
* cx
, HandleObject wrapper
, HandleId id
,
749 MutableHandle
<PropertyDescriptor
> desc
) const
751 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, nullptr, JSMSG_DEAD_OBJECT
);
756 DeadObjectProxy::defineProperty(JSContext
* cx
, HandleObject wrapper
, HandleId id
,
757 MutableHandle
<PropertyDescriptor
> desc
) const
759 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, nullptr, JSMSG_DEAD_OBJECT
);
764 DeadObjectProxy::getOwnPropertyNames(JSContext
* cx
, HandleObject wrapper
,
765 AutoIdVector
& props
) const
767 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, nullptr, JSMSG_DEAD_OBJECT
);
772 DeadObjectProxy::delete_(JSContext
* cx
, HandleObject wrapper
, HandleId id
, bool* bp
) const
774 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, nullptr, JSMSG_DEAD_OBJECT
);
779 DeadObjectProxy::enumerate(JSContext
* cx
, HandleObject wrapper
, AutoIdVector
& props
) const
781 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, nullptr, JSMSG_DEAD_OBJECT
);
786 DeadObjectProxy::call(JSContext
* cx
, HandleObject wrapper
, const CallArgs
& args
) const
788 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, nullptr, JSMSG_DEAD_OBJECT
);
793 DeadObjectProxy::construct(JSContext
* cx
, HandleObject wrapper
, const CallArgs
& args
) const
795 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, nullptr, JSMSG_DEAD_OBJECT
);
800 DeadObjectProxy::nativeCall(JSContext
* cx
, IsAcceptableThis test
, NativeImpl impl
,
803 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, nullptr, JSMSG_DEAD_OBJECT
);
808 DeadObjectProxy::hasInstance(JSContext
* cx
, HandleObject proxy
, MutableHandleValue v
,
811 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, nullptr, JSMSG_DEAD_OBJECT
);
816 DeadObjectProxy::objectClassIs(HandleObject obj
, ESClassValue classValue
, JSContext
* cx
) const
818 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, nullptr, JSMSG_DEAD_OBJECT
);
823 DeadObjectProxy::className(JSContext
* cx
, HandleObject wrapper
) const
829 DeadObjectProxy::fun_toString(JSContext
* cx
, HandleObject proxy
, unsigned indent
) const
835 DeadObjectProxy::regexp_toShared(JSContext
* cx
, HandleObject proxy
, RegExpGuard
* g
) const
837 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, nullptr, JSMSG_DEAD_OBJECT
);
842 DeadObjectProxy::defaultValue(JSContext
* cx
, HandleObject obj
, JSType hint
,
843 MutableHandleValue vp
) const
845 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, nullptr, JSMSG_DEAD_OBJECT
);
850 DeadObjectProxy::getPrototypeOf(JSContext
* cx
, HandleObject proxy
, MutableHandleObject protop
) const
856 const char DeadObjectProxy::family
= 0;
857 const DeadObjectProxy
DeadObjectProxy::singleton
;
860 js::IsDeadProxyObject(JSObject
* obj
)
862 return obj
->is
<ProxyObject
>() &&
863 obj
->as
<ProxyObject
>().handler() == &DeadObjectProxy::singleton
;
867 js::NukeCrossCompartmentWrapper(JSContext
* cx
, JSObject
* wrapper
)
869 JS_ASSERT(wrapper
->is
<CrossCompartmentWrapperObject
>());
871 NotifyGCNukeWrapper(wrapper
);
873 wrapper
->as
<ProxyObject
>().nuke(&DeadObjectProxy::singleton
);
875 JS_ASSERT(IsDeadProxyObject(wrapper
));
879 * NukeChromeCrossCompartmentWrappersForGlobal reaches into chrome and cuts
880 * all of the cross-compartment wrappers that point to objects parented to
881 * obj's global. The snag here is that we need to avoid cutting wrappers that
882 * point to the window object on page navigation (inner window destruction)
883 * and only do that on tab close (outer window destruction). Thus the
884 * option of how to handle the global object.
887 js::NukeCrossCompartmentWrappers(JSContext
* cx
,
888 const CompartmentFilter
& sourceFilter
,
889 const CompartmentFilter
& targetFilter
,
890 js::NukeReferencesToWindow nukeReferencesToWindow
)
893 JSRuntime
* rt
= cx
->runtime();
895 // Iterate through scopes looking for system cross compartment wrappers
896 // that point to an object that shares a global with obj.
898 for (CompartmentsIter
c(rt
, SkipAtoms
); !c
.done(); c
.next()) {
899 if (!sourceFilter
.match(c
))
902 // Iterate the wrappers looking for anything interesting.
903 for (JSCompartment::WrapperEnum
e(c
); !e
.empty(); e
.popFront()) {
904 // Some cross-compartment wrappers are for strings. We're not
905 // interested in those.
906 const CrossCompartmentKey
& k
= e
.front().key();
907 if (k
.kind
!= CrossCompartmentKey::ObjectWrapper
)
910 AutoWrapperRooter
wobj(cx
, WrapperValue(e
));
911 JSObject
* wrapped
= UncheckedUnwrap(wobj
);
913 if (nukeReferencesToWindow
== DontNukeWindowReferences
&&
914 wrapped
->getClass()->ext
.innerObject
)
917 if (targetFilter
.match(wrapped
->compartment())) {
918 // We found a wrapper to nuke.
920 NukeCrossCompartmentWrapper(cx
, wobj
);
928 // Given a cross-compartment wrapper |wobj|, update it to point to
929 // |newTarget|. This recomputes the wrapper with JS_WrapValue, and thus can be
930 // useful even if wrapper already points to newTarget.
932 js::RemapWrapper(JSContext
* cx
, JSObject
* wobjArg
, JSObject
* newTargetArg
)
934 RootedObject
wobj(cx
, wobjArg
);
935 RootedObject
newTarget(cx
, newTargetArg
);
936 JS_ASSERT(wobj
->is
<CrossCompartmentWrapperObject
>());
937 JS_ASSERT(!newTarget
->is
<CrossCompartmentWrapperObject
>());
938 JSObject
* origTarget
= Wrapper::wrappedObject(wobj
);
939 JS_ASSERT(origTarget
);
940 Value origv
= ObjectValue(*origTarget
);
941 JSCompartment
* wcompartment
= wobj
->compartment();
943 AutoDisableProxyCheck
adpc(cx
->runtime());
945 // If we're mapping to a different target (as opposed to just recomputing
946 // for the same target), we must not have an existing wrapper for the new
947 // target, otherwise this will break.
948 JS_ASSERT_IF(origTarget
!= newTarget
,
949 !wcompartment
->lookupWrapper(ObjectValue(*newTarget
)));
951 // The old value should still be in the cross-compartment wrapper map, and
952 // the lookup should return wobj.
953 WrapperMap::Ptr p
= wcompartment
->lookupWrapper(origv
);
954 JS_ASSERT(&p
->value().unsafeGet()->toObject() == wobj
);
955 wcompartment
->removeWrapper(p
);
957 // When we remove origv from the wrapper map, its wrapper, wobj, must
958 // immediately cease to be a cross-compartment wrapper. Neuter it.
959 NukeCrossCompartmentWrapper(cx
, wobj
);
961 // First, we wrap it in the new compartment. We try to use the existing
962 // wrapper, |wobj|, since it's been nuked anyway. The wrap() function has
963 // the choice to reuse |wobj| or not.
964 RootedObject
tobj(cx
, newTarget
);
965 AutoCompartment
ac(cx
, wobj
);
966 if (!wcompartment
->wrap(cx
, &tobj
, wobj
))
969 // If wrap() reused |wobj|, it will have overwritten it and returned with
970 // |tobj == wobj|. Otherwise, |tobj| will point to a new wrapper and |wobj|
971 // will still be nuked. In the latter case, we replace |wobj| with the
972 // contents of the new wrapper in |tobj|.
974 // Now, because we need to maintain object identity, we do a brain
975 // transplant on the old object so that it contains the contents of the
977 if (!JSObject::swap(cx
, wobj
, tobj
))
981 // Before swapping, this wrapper came out of wrap(), which enforces the
982 // invariant that the wrapper in the map points directly to the key.
983 JS_ASSERT(Wrapper::wrappedObject(wobj
) == newTarget
);
985 // Update the entry in the compartment's wrapper map to point to the old
986 // wrapper, which has now been updated (via reuse or swap).
987 JS_ASSERT(wobj
->is
<WrapperObject
>());
988 wcompartment
->putWrapper(cx
, CrossCompartmentKey(newTarget
), ObjectValue(*wobj
));
992 // Remap all cross-compartment wrappers pointing to |oldTarget| to point to
993 // |newTarget|. All wrappers are recomputed.
995 js::RemapAllWrappersForObject(JSContext
* cx
, JSObject
* oldTargetArg
,
996 JSObject
* newTargetArg
)
998 RootedValue
origv(cx
, ObjectValue(*oldTargetArg
));
999 RootedObject
newTarget(cx
, newTargetArg
);
1001 AutoWrapperVector
toTransplant(cx
);
1002 if (!toTransplant
.reserve(cx
->runtime()->numCompartments
))
1005 for (CompartmentsIter
c(cx
->runtime(), SkipAtoms
); !c
.done(); c
.next()) {
1006 if (WrapperMap::Ptr wp
= c
->lookupWrapper(origv
)) {
1007 // We found a wrapper. Remember and root it.
1008 toTransplant
.infallibleAppend(WrapperValue(wp
));
1012 for (WrapperValue
* begin
= toTransplant
.begin(), *end
= toTransplant
.end();
1013 begin
!= end
; ++begin
)
1015 if (!RemapWrapper(cx
, &begin
->toObject(), newTarget
))
1023 js::RecomputeWrappers(JSContext
* cx
, const CompartmentFilter
& sourceFilter
,
1024 const CompartmentFilter
& targetFilter
)
1026 AutoWrapperVector
toRecompute(cx
);
1028 for (CompartmentsIter
c(cx
->runtime(), SkipAtoms
); !c
.done(); c
.next()) {
1029 // Filter by source compartment.
1030 if (!sourceFilter
.match(c
))
1033 // Iterate over the wrappers, filtering appropriately.
1034 for (JSCompartment::WrapperEnum
e(c
); !e
.empty(); e
.popFront()) {
1035 // Filter out non-objects.
1036 const CrossCompartmentKey
& k
= e
.front().key();
1037 if (k
.kind
!= CrossCompartmentKey::ObjectWrapper
)
1040 // Filter by target compartment.
1041 if (!targetFilter
.match(static_cast<JSObject
*>(k
.wrapped
)->compartment()))
1044 // Add it to the list.
1045 if (!toRecompute
.append(WrapperValue(e
)))
1050 // Recompute all the wrappers in the list.
1051 for (WrapperValue
* begin
= toRecompute
.begin(), *end
= toRecompute
.end(); begin
!= end
; ++begin
)
1053 JSObject
* wrapper
= &begin
->toObject();
1054 JSObject
* wrapped
= Wrapper::wrappedObject(wrapper
);
1055 if (!RemapWrapper(cx
, wrapper
, wrapped
))