Bumping manifests a=b2g-bump
[gecko.git] / js / src / jswrapper.cpp
blob488ab0e0adfed6e79f80b83c02cea8d805dea017
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 #include "jswrapper.h"
9 #include "jscntxt.h"
10 #include "jscompartment.h"
11 #include "jsexn.h"
12 #include "jsgc.h"
13 #include "jsiter.h"
15 #include "vm/ErrorObject.h"
16 #include "vm/WrapperObject.h"
18 #include "jsobjinlines.h"
20 using namespace js;
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
28 * 720619.
30 bool
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);
39 JSObject*
40 Wrapper::New(JSContext* cx, JSObject* obj, JSObject* parent, const Wrapper* handler,
41 const WrapperOptions* options)
43 JS_ASSERT(parent);
45 RootedValue priv(cx, ObjectValue(*obj));
46 mozilla::Maybe<WrapperOptions> opts;
47 if (!options) {
48 opts.emplace();
49 opts->selectDefaultClass(obj->isCallable());
50 options = opts.ptr();
52 return NewProxyObject(cx, handler, priv, options->proto(), parent, *options);
55 JSObject*
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));
60 return existing;
63 const Wrapper*
64 Wrapper::wrapperHandler(JSObject* wrapper)
66 JS_ASSERT(wrapper->is<WrapperObject>());
67 return static_cast<const Wrapper*>(wrapper->as<ProxyObject>().handler());
70 JSObject*
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)
80 unsigned flags = 0;
81 while (true) {
82 if (!wrapped->is<WrapperObject>() ||
83 MOZ_UNLIKELY(stopAtOuter && wrapped->getClass()->ext.innerObject))
85 break;
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.
92 if (wrapped)
93 wrapped = MaybeForwarded(wrapped);
95 if (flagsp)
96 *flagsp = flags;
97 return wrapped;
100 JS_FRIEND_API(JSObject*)
101 js::CheckedUnwrap(JSObject* obj, bool stopAtOuter)
103 while (true) {
104 JSObject* wrapper = obj;
105 obj = UnwrapOneChecked(obj, stopAtOuter);
106 if (!obj || obj == wrapper)
107 return obj;
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))
117 return obj;
120 const Wrapper* handler = Wrapper::wrapperHandler(obj);
121 return handler->hasSecurityPolicy() ? nullptr : Wrapper::wrappedObject(obj);
124 bool
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;
136 /* Compartments. */
138 extern JSObject*
139 js::TransparentObjectWrapper(JSContext* cx, HandleObject existing, HandleObject obj,
140 HandleObject parent)
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()) {
151 RootedValue exc(cx);
152 if (cx->getPendingException(&exc) && exc.isObject() && exc.toObject().is<ErrorObject>()) {
153 cx->clearPendingException();
154 ac.reset();
155 Rooted<ErrorObject*> errObj(cx, &exc.toObject().as<ErrorObject>());
156 JSObject* copyobj = js_CopyErrorObject(cx, errObj);
157 if (copyobj)
158 cx->setPendingException(ObjectValue(*copyobj));
163 /* Cross compartment wrappers. */
165 bool Wrapper::finalizeInBackground(Value priv) const
167 if (!priv.isObject())
168 return true;
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()))
178 return true;
179 return IsBackgroundFinalized(priv.toObject().tenuredGetAllocKind());
182 #define PIERCE(cx, wrapper, pre, op, post) \
183 JS_BEGIN_MACRO \
184 bool ok; \
186 AutoCompartment call(cx, wrappedObject(wrapper)); \
187 ok = (pre) && (op); \
189 return ok && (post); \
190 JS_END_MACRO
192 #define NOTHING (true)
194 bool
195 CrossCompartmentWrapper::isExtensible(JSContext* cx, HandleObject wrapper, bool* extensible) const
197 PIERCE(cx, wrapper,
198 NOTHING,
199 Wrapper::isExtensible(cx, wrapper, extensible),
200 NOTHING);
203 bool
204 CrossCompartmentWrapper::preventExtensions(JSContext* cx, HandleObject wrapper) const
206 PIERCE(cx, wrapper,
207 NOTHING,
208 Wrapper::preventExtensions(cx, wrapper),
209 NOTHING);
212 bool
213 CrossCompartmentWrapper::getPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id,
214 MutableHandle<PropertyDescriptor> desc) const
216 PIERCE(cx, wrapper,
217 NOTHING,
218 Wrapper::getPropertyDescriptor(cx, wrapper, id, desc),
219 cx->compartment()->wrap(cx, desc));
222 bool
223 CrossCompartmentWrapper::getOwnPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id,
224 MutableHandle<PropertyDescriptor> desc) const
226 PIERCE(cx, wrapper,
227 NOTHING,
228 Wrapper::getOwnPropertyDescriptor(cx, wrapper, id, desc),
229 cx->compartment()->wrap(cx, desc));
232 bool
233 CrossCompartmentWrapper::defineProperty(JSContext* cx, HandleObject wrapper, HandleId id,
234 MutableHandle<PropertyDescriptor> desc) const
236 Rooted<PropertyDescriptor> desc2(cx, desc);
237 PIERCE(cx, wrapper,
238 cx->compartment()->wrap(cx, &desc2),
239 Wrapper::defineProperty(cx, wrapper, id, &desc2),
240 NOTHING);
243 bool
244 CrossCompartmentWrapper::getOwnPropertyNames(JSContext* cx, HandleObject wrapper,
245 AutoIdVector& props) const
247 PIERCE(cx, wrapper,
248 NOTHING,
249 Wrapper::getOwnPropertyNames(cx, wrapper, props),
250 NOTHING);
253 bool
254 CrossCompartmentWrapper::delete_(JSContext* cx, HandleObject wrapper, HandleId id, bool* bp) const
256 PIERCE(cx, wrapper,
257 NOTHING,
258 Wrapper::delete_(cx, wrapper, id, bp),
259 NOTHING);
262 bool
263 CrossCompartmentWrapper::enumerate(JSContext* cx, HandleObject wrapper, AutoIdVector& props) const
265 PIERCE(cx, wrapper,
266 NOTHING,
267 Wrapper::enumerate(cx, wrapper, props),
268 NOTHING);
271 bool
272 CrossCompartmentWrapper::has(JSContext* cx, HandleObject wrapper, HandleId id, bool* bp) const
274 PIERCE(cx, wrapper,
275 NOTHING,
276 Wrapper::has(cx, wrapper, id, bp),
277 NOTHING);
280 bool
281 CrossCompartmentWrapper::hasOwn(JSContext* cx, HandleObject wrapper, HandleId id, bool* bp) const
283 PIERCE(cx, wrapper,
284 NOTHING,
285 Wrapper::hasOwn(cx, wrapper, id, bp),
286 NOTHING);
289 bool
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))
297 return false;
299 if (!Wrapper::get(cx, wrapper, receiverCopy, id, vp))
300 return false;
302 return cx->compartment()->wrap(cx, vp);
305 bool
306 CrossCompartmentWrapper::set(JSContext* cx, HandleObject wrapper, HandleObject receiver,
307 HandleId id, bool strict, MutableHandleValue vp) const
309 RootedObject receiverCopy(cx, receiver);
310 PIERCE(cx, wrapper,
311 cx->compartment()->wrap(cx, &receiverCopy) &&
312 cx->compartment()->wrap(cx, vp),
313 Wrapper::set(cx, wrapper, receiverCopy, id, strict, vp),
314 NOTHING);
317 bool
318 CrossCompartmentWrapper::keys(JSContext* cx, HandleObject wrapper, AutoIdVector& props) const
320 PIERCE(cx, wrapper,
321 NOTHING,
322 Wrapper::keys(cx, wrapper, props),
323 NOTHING);
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.
330 static bool
331 CanReify(HandleValue vp)
333 JSObject* obj;
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; }
347 private:
348 JSContext* cx;
349 RootedObject obj;
352 static bool
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))
363 return false;
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);
373 if (length > 0) {
374 if (!keys.reserve(length))
375 return false;
376 for (size_t i = 0; i < length; ++i) {
377 RootedId id(cx);
378 RootedValue v(cx, StringValue(ni->begin()[i]));
379 if (!ValueToId<CanGC>(cx, v, &id))
380 return false;
381 keys.infallibleAppend(id);
385 close.clear();
386 if (!CloseIterator(cx, iterObj))
387 return false;
389 if (isKeyIter) {
390 if (!VectorToKeyIterator(cx, obj, ni->flags, keys, vp))
391 return false;
392 } else {
393 if (!VectorToValueIterator(cx, obj, ni->flags, keys, vp))
394 return false;
396 return true;
399 bool
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))
406 return false;
409 if (CanReify(vp))
410 return Reify(cx, cx->compartment(), vp);
411 return cx->compartment()->wrap(cx, vp);
414 bool
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()))
424 return false;
426 for (size_t n = 0; n < args.length(); ++n) {
427 if (!cx->compartment()->wrap(cx, args[n]))
428 return false;
431 if (!Wrapper::call(cx, wrapper, args))
432 return false;
435 return cx->compartment()->wrap(cx, args.rval());
438 bool
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]))
447 return false;
449 if (!Wrapper::construct(cx, wrapper, args))
450 return false;
452 return cx->compartment()->wrap(cx, args.rval());
455 bool
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()))
468 return false;
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) {
476 source = *src;
477 if (!cx->compartment()->wrap(cx, &source))
478 return false;
479 *dst = source.get();
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))
497 return false;
499 srcArgs.rval().set(dstArgs.rval());
501 return cx->compartment()->wrap(cx, srcArgs.rval());
504 bool
505 CrossCompartmentWrapper::hasInstance(JSContext* cx, HandleObject wrapper, MutableHandleValue v,
506 bool* bp) const
508 AutoCompartment call(cx, wrappedObject(wrapper));
509 if (!cx->compartment()->wrap(cx, v))
510 return false;
511 return Wrapper::hasInstance(cx, wrapper, v, bp);
514 const char*
515 CrossCompartmentWrapper::className(JSContext* cx, HandleObject wrapper) const
517 AutoCompartment call(cx, wrappedObject(wrapper));
518 return Wrapper::className(cx, wrapper);
521 JSString*
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);
528 if (!str)
529 return nullptr;
531 if (!cx->compartment()->wrap(cx, str.address()))
532 return nullptr;
533 return str;
536 bool
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))
543 return false;
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);
551 bool
552 CrossCompartmentWrapper::boxedValue_unbox(JSContext* cx, HandleObject wrapper, MutableHandleValue vp) const
554 PIERCE(cx, wrapper,
555 NOTHING,
556 Wrapper::boxedValue_unbox(cx, wrapper, vp),
557 cx->compartment()->wrap(cx, vp));
560 bool
561 CrossCompartmentWrapper::defaultValue(JSContext* cx, HandleObject wrapper, JSType hint,
562 MutableHandleValue vp) const
564 PIERCE(cx, wrapper,
565 NOTHING,
566 Wrapper::defaultValue(cx, wrapper, hint, vp),
567 cx->compartment()->wrap(cx, vp));
570 bool
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))
578 return false;
579 if (protop)
580 protop->setDelegate(cx);
583 return cx->compartment()->wrap(cx, protop);
586 bool
587 CrossCompartmentWrapper::setPrototypeOf(JSContext* cx, HandleObject wrapper,
588 HandleObject proto, bool* bp) const
590 RootedObject protoCopy(cx, proto);
591 PIERCE(cx, wrapper,
592 cx->compartment()->wrap(cx, &protoCopy),
593 Wrapper::setPrototypeOf(cx, wrapper, protoCopy, bp),
594 NOTHING);
597 const CrossCompartmentWrapper CrossCompartmentWrapper::singleton(0u);
599 /* Security wrappers. */
601 template <class Base>
602 bool
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.
608 *extensible = true;
609 return true;
612 template <class Base>
613 bool
614 SecurityWrapper<Base>::preventExtensions(JSContext* cx, HandleObject wrapper) const
616 // See above.
617 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNWRAP_DENIED);
618 return false;
621 template <class Base>
622 bool
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);
627 *bp = false;
628 return false;
631 template <class Base>
632 bool
633 SecurityWrapper<Base>::nativeCall(JSContext* cx, IsAcceptableThis test, NativeImpl impl,
634 CallArgs args) const
636 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNWRAP_DENIED);
637 return false;
640 template <class Base>
641 bool
642 SecurityWrapper<Base>::setPrototypeOf(JSContext* cx, HandleObject wrapper,
643 HandleObject proto, bool* bp) const
645 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNWRAP_DENIED);
646 return false;
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>
653 bool
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>
661 bool
662 SecurityWrapper<Base>::objectClassIs(HandleObject obj, ESClassValue classValue, JSContext* cx) const
664 return false;
667 template <class Base>
668 bool
669 SecurityWrapper<Base>::regexp_toShared(JSContext* cx, HandleObject obj, RegExpGuard* g) const
671 return Base::regexp_toShared(cx, obj, g);
674 template <class Base>
675 bool
676 SecurityWrapper<Base>::boxedValue_unbox(JSContext* cx, HandleObject obj, MutableHandleValue vp) const
678 vp.setUndefined();
679 return true;
682 template <class Base>
683 bool
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);
695 return false;
698 return Base::defineProperty(cx, wrapper, id, desc);
701 template <class Base>
702 bool
703 SecurityWrapper<Base>::watch(JSContext* cx, HandleObject proxy,
704 HandleId id, HandleObject callable) const
706 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNWRAP_DENIED);
707 return false;
710 template <class Base>
711 bool
712 SecurityWrapper<Base>::unwatch(JSContext* cx, HandleObject proxy,
713 HandleId id) const
715 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNWRAP_DENIED);
716 return false;
720 template class js::SecurityWrapper<Wrapper>;
721 template class js::SecurityWrapper<CrossCompartmentWrapper>;
723 bool
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.
728 *extensible = true;
729 return true;
732 bool
733 DeadObjectProxy::preventExtensions(JSContext* cx, HandleObject proxy) const
735 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
736 return false;
739 bool
740 DeadObjectProxy::getPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id,
741 MutableHandle<PropertyDescriptor> desc) const
743 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
744 return false;
747 bool
748 DeadObjectProxy::getOwnPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id,
749 MutableHandle<PropertyDescriptor> desc) const
751 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
752 return false;
755 bool
756 DeadObjectProxy::defineProperty(JSContext* cx, HandleObject wrapper, HandleId id,
757 MutableHandle<PropertyDescriptor> desc) const
759 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
760 return false;
763 bool
764 DeadObjectProxy::getOwnPropertyNames(JSContext* cx, HandleObject wrapper,
765 AutoIdVector& props) const
767 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
768 return false;
771 bool
772 DeadObjectProxy::delete_(JSContext* cx, HandleObject wrapper, HandleId id, bool* bp) const
774 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
775 return false;
778 bool
779 DeadObjectProxy::enumerate(JSContext* cx, HandleObject wrapper, AutoIdVector& props) const
781 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
782 return false;
785 bool
786 DeadObjectProxy::call(JSContext* cx, HandleObject wrapper, const CallArgs& args) const
788 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
789 return false;
792 bool
793 DeadObjectProxy::construct(JSContext* cx, HandleObject wrapper, const CallArgs& args) const
795 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
796 return false;
799 bool
800 DeadObjectProxy::nativeCall(JSContext* cx, IsAcceptableThis test, NativeImpl impl,
801 CallArgs args) const
803 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
804 return false;
807 bool
808 DeadObjectProxy::hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v,
809 bool* bp) const
811 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
812 return false;
815 bool
816 DeadObjectProxy::objectClassIs(HandleObject obj, ESClassValue classValue, JSContext* cx) const
818 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
819 return false;
822 const char*
823 DeadObjectProxy::className(JSContext* cx, HandleObject wrapper) const
825 return "DeadObject";
828 JSString*
829 DeadObjectProxy::fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const
831 return nullptr;
834 bool
835 DeadObjectProxy::regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const
837 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
838 return false;
841 bool
842 DeadObjectProxy::defaultValue(JSContext* cx, HandleObject obj, JSType hint,
843 MutableHandleValue vp) const
845 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
846 return false;
849 bool
850 DeadObjectProxy::getPrototypeOf(JSContext* cx, HandleObject proxy, MutableHandleObject protop) const
852 protop.set(nullptr);
853 return true;
856 const char DeadObjectProxy::family = 0;
857 const DeadObjectProxy DeadObjectProxy::singleton;
859 bool
860 js::IsDeadProxyObject(JSObject* obj)
862 return obj->is<ProxyObject>() &&
863 obj->as<ProxyObject>().handler() == &DeadObjectProxy::singleton;
866 void
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.
886 JS_FRIEND_API(bool)
887 js::NukeCrossCompartmentWrappers(JSContext* cx,
888 const CompartmentFilter& sourceFilter,
889 const CompartmentFilter& targetFilter,
890 js::NukeReferencesToWindow nukeReferencesToWindow)
892 CHECK_REQUEST(cx);
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))
900 continue;
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)
908 continue;
910 AutoWrapperRooter wobj(cx, WrapperValue(e));
911 JSObject* wrapped = UncheckedUnwrap(wobj);
913 if (nukeReferencesToWindow == DontNukeWindowReferences &&
914 wrapped->getClass()->ext.innerObject)
915 continue;
917 if (targetFilter.match(wrapped->compartment())) {
918 // We found a wrapper to nuke.
919 e.removeFront();
920 NukeCrossCompartmentWrapper(cx, wobj);
925 return true;
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.
931 bool
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))
967 MOZ_CRASH();
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|.
973 if (tobj != wobj) {
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
976 // new one.
977 if (!JSObject::swap(cx, wobj, tobj))
978 MOZ_CRASH();
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));
989 return true;
992 // Remap all cross-compartment wrappers pointing to |oldTarget| to point to
993 // |newTarget|. All wrappers are recomputed.
994 JS_FRIEND_API(bool)
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))
1003 return false;
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))
1016 MOZ_CRASH();
1019 return true;
1022 JS_FRIEND_API(bool)
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))
1031 continue;
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)
1038 continue;
1040 // Filter by target compartment.
1041 if (!targetFilter.match(static_cast<JSObject*>(k.wrapped)->compartment()))
1042 continue;
1044 // Add it to the list.
1045 if (!toRecompute.append(WrapperValue(e)))
1046 return false;
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))
1056 MOZ_CRASH();
1059 return true;