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/. */
6 
7 #include "jsiter.h"
8 #include "jswrapper.h"
9 
10 #include "proxy/DeadObjectProxy.h"
11 #include "vm/WrapperObject.h"
12 
13 #include "jscompartmentinlines.h"
14 #include "jsobjinlines.h"
15 
16 using namespace js;
17 
18 #define PIERCE(cx, wrapper, pre, op, post)                      \
19     JS_BEGIN_MACRO                                              \
20         bool ok;                                                \
21         {                                                       \
22             AutoCompartment call(cx, wrappedObject(wrapper));   \
23             ok = (pre) && (op);                                 \
24         }                                                       \
25         return ok && (post);                                    \
26     JS_END_MACRO
27 
28 #define NOTHING (true)
29 
30 bool
getPropertyDescriptor(JSContext * cx,HandleObject wrapper,HandleId id,MutableHandle<PropertyDescriptor> desc) const31 CrossCompartmentWrapper::getPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id,
32                                                MutableHandle<PropertyDescriptor> desc) const
33 {
34     PIERCE(cx, wrapper,
35            NOTHING,
36            Wrapper::getPropertyDescriptor(cx, wrapper, id, desc),
37            cx->compartment()->wrap(cx, desc));
38 }
39 
40 bool
getOwnPropertyDescriptor(JSContext * cx,HandleObject wrapper,HandleId id,MutableHandle<PropertyDescriptor> desc) const41 CrossCompartmentWrapper::getOwnPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id,
42                                                   MutableHandle<PropertyDescriptor> desc) const
43 {
44     PIERCE(cx, wrapper,
45            NOTHING,
46            Wrapper::getOwnPropertyDescriptor(cx, wrapper, id, desc),
47            cx->compartment()->wrap(cx, desc));
48 }
49 
50 bool
defineProperty(JSContext * cx,HandleObject wrapper,HandleId id,Handle<PropertyDescriptor> desc,ObjectOpResult & result) const51 CrossCompartmentWrapper::defineProperty(JSContext* cx, HandleObject wrapper, HandleId id,
52                                         Handle<PropertyDescriptor> desc,
53                                         ObjectOpResult& result) const
54 {
55     Rooted<PropertyDescriptor> desc2(cx, desc);
56     PIERCE(cx, wrapper,
57            cx->compartment()->wrap(cx, &desc2),
58            Wrapper::defineProperty(cx, wrapper, id, desc2, result),
59            NOTHING);
60 }
61 
62 bool
ownPropertyKeys(JSContext * cx,HandleObject wrapper,AutoIdVector & props) const63 CrossCompartmentWrapper::ownPropertyKeys(JSContext* cx, HandleObject wrapper,
64                                          AutoIdVector& props) const
65 {
66     PIERCE(cx, wrapper,
67            NOTHING,
68            Wrapper::ownPropertyKeys(cx, wrapper, props),
69            NOTHING);
70 }
71 
72 bool
delete_(JSContext * cx,HandleObject wrapper,HandleId id,ObjectOpResult & result) const73 CrossCompartmentWrapper::delete_(JSContext* cx, HandleObject wrapper, HandleId id,
74                                  ObjectOpResult& result) const
75 {
76     PIERCE(cx, wrapper,
77            NOTHING,
78            Wrapper::delete_(cx, wrapper, id, result),
79            NOTHING);
80 }
81 
82 bool
getPrototype(JSContext * cx,HandleObject wrapper,MutableHandleObject protop) const83 CrossCompartmentWrapper::getPrototype(JSContext* cx, HandleObject wrapper,
84                                       MutableHandleObject protop) const
85 {
86     {
87         RootedObject wrapped(cx, wrappedObject(wrapper));
88         AutoCompartment call(cx, wrapped);
89         if (!GetPrototype(cx, wrapped, protop))
90             return false;
91         if (protop)
92             protop->setDelegate(cx);
93     }
94 
95     return cx->compartment()->wrap(cx, protop);
96 }
97 
98 bool
setPrototype(JSContext * cx,HandleObject wrapper,HandleObject proto,ObjectOpResult & result) const99 CrossCompartmentWrapper::setPrototype(JSContext* cx, HandleObject wrapper,
100                                       HandleObject proto, ObjectOpResult& result) const
101 {
102     RootedObject protoCopy(cx, proto);
103     PIERCE(cx, wrapper,
104            cx->compartment()->wrap(cx, &protoCopy),
105            Wrapper::setPrototype(cx, wrapper, protoCopy, result),
106            NOTHING);
107 }
108 
109 bool
setImmutablePrototype(JSContext * cx,HandleObject wrapper,bool * succeeded) const110 CrossCompartmentWrapper::setImmutablePrototype(JSContext* cx, HandleObject wrapper, bool* succeeded) const
111 {
112     PIERCE(cx, wrapper,
113            NOTHING,
114            Wrapper::setImmutablePrototype(cx, wrapper, succeeded),
115            NOTHING);
116 }
117 
118 bool
preventExtensions(JSContext * cx,HandleObject wrapper,ObjectOpResult & result) const119 CrossCompartmentWrapper::preventExtensions(JSContext* cx, HandleObject wrapper,
120                                            ObjectOpResult& result) const
121 {
122     PIERCE(cx, wrapper,
123            NOTHING,
124            Wrapper::preventExtensions(cx, wrapper, result),
125            NOTHING);
126 }
127 
128 bool
isExtensible(JSContext * cx,HandleObject wrapper,bool * extensible) const129 CrossCompartmentWrapper::isExtensible(JSContext* cx, HandleObject wrapper, bool* extensible) const
130 {
131     PIERCE(cx, wrapper,
132            NOTHING,
133            Wrapper::isExtensible(cx, wrapper, extensible),
134            NOTHING);
135 }
136 
137 bool
has(JSContext * cx,HandleObject wrapper,HandleId id,bool * bp) const138 CrossCompartmentWrapper::has(JSContext* cx, HandleObject wrapper, HandleId id, bool* bp) const
139 {
140     PIERCE(cx, wrapper,
141            NOTHING,
142            Wrapper::has(cx, wrapper, id, bp),
143            NOTHING);
144 }
145 
146 bool
hasOwn(JSContext * cx,HandleObject wrapper,HandleId id,bool * bp) const147 CrossCompartmentWrapper::hasOwn(JSContext* cx, HandleObject wrapper, HandleId id, bool* bp) const
148 {
149     PIERCE(cx, wrapper,
150            NOTHING,
151            Wrapper::hasOwn(cx, wrapper, id, bp),
152            NOTHING);
153 }
154 
155 bool
get(JSContext * cx,HandleObject wrapper,HandleValue receiver,HandleId id,MutableHandleValue vp) const156 CrossCompartmentWrapper::get(JSContext* cx, HandleObject wrapper, HandleValue receiver,
157                              HandleId id, MutableHandleValue vp) const
158 {
159     RootedValue receiverCopy(cx, receiver);
160     {
161         AutoCompartment call(cx, wrappedObject(wrapper));
162         if (!cx->compartment()->wrap(cx, &receiverCopy))
163             return false;
164 
165         if (!Wrapper::get(cx, wrapper, receiverCopy, id, vp))
166             return false;
167     }
168     return cx->compartment()->wrap(cx, vp);
169 }
170 
171 bool
set(JSContext * cx,HandleObject wrapper,HandleId id,HandleValue v,HandleValue receiver,ObjectOpResult & result) const172 CrossCompartmentWrapper::set(JSContext* cx, HandleObject wrapper, HandleId id, HandleValue v,
173                              HandleValue receiver, ObjectOpResult& result) const
174 {
175     RootedValue valCopy(cx, v);
176     RootedValue receiverCopy(cx, receiver);
177     PIERCE(cx, wrapper,
178            cx->compartment()->wrap(cx, &valCopy) &&
179            cx->compartment()->wrap(cx, &receiverCopy),
180            Wrapper::set(cx, wrapper, id, valCopy, receiverCopy, result),
181            NOTHING);
182 }
183 
184 bool
getOwnEnumerablePropertyKeys(JSContext * cx,HandleObject wrapper,AutoIdVector & props) const185 CrossCompartmentWrapper::getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject wrapper,
186                                                       AutoIdVector& props) const
187 {
188     PIERCE(cx, wrapper,
189            NOTHING,
190            Wrapper::getOwnEnumerablePropertyKeys(cx, wrapper, props),
191            NOTHING);
192 }
193 
194 /*
195  * We can reify non-escaping iterator objects instead of having to wrap them. This
196  * allows fast iteration over objects across a compartment boundary.
197  */
198 static bool
CanReify(HandleObject obj)199 CanReify(HandleObject obj)
200 {
201     return obj->is<PropertyIteratorObject>() &&
202            (obj->as<PropertyIteratorObject>().getNativeIterator()->flags & JSITER_ENUMERATE);
203 }
204 
205 struct AutoCloseIterator
206 {
AutoCloseIteratorAutoCloseIterator207     AutoCloseIterator(JSContext* cx, JSObject* obj) : cx(cx), obj(cx, obj) {}
208 
~AutoCloseIteratorAutoCloseIterator209     ~AutoCloseIterator() { if (obj) CloseIterator(cx, obj); }
210 
clearAutoCloseIterator211     void clear() { obj = nullptr; }
212 
213   private:
214     JSContext* cx;
215     RootedObject obj;
216 };
217 
218 static bool
Reify(JSContext * cx,JSCompartment * origin,MutableHandleObject objp)219 Reify(JSContext* cx, JSCompartment* origin, MutableHandleObject objp)
220 {
221     Rooted<PropertyIteratorObject*> iterObj(cx, &objp->as<PropertyIteratorObject>());
222     NativeIterator* ni = iterObj->getNativeIterator();
223 
224     AutoCloseIterator close(cx, iterObj);
225 
226     /* Wrap the iteratee. */
227     RootedObject obj(cx, ni->obj);
228     if (!origin->wrap(cx, &obj))
229         return false;
230 
231     /*
232      * Wrap the elements in the iterator's snapshot.
233      * N.B. the order of closing/creating iterators is important due to the
234      * implicit cx->enumerators state.
235      */
236     size_t length = ni->numKeys();
237     AutoIdVector keys(cx);
238     if (length > 0) {
239         if (!keys.reserve(length))
240             return false;
241         for (size_t i = 0; i < length; ++i) {
242             RootedId id(cx);
243             RootedValue v(cx, StringValue(ni->begin()[i]));
244             if (!ValueToId<CanGC>(cx, v, &id))
245                 return false;
246             keys.infallibleAppend(id);
247         }
248     }
249 
250     close.clear();
251     if (!CloseIterator(cx, iterObj))
252         return false;
253 
254     return EnumeratedIdVectorToIterator(cx, obj, ni->flags, keys, objp);
255 }
256 
257 bool
enumerate(JSContext * cx,HandleObject wrapper,MutableHandleObject objp) const258 CrossCompartmentWrapper::enumerate(JSContext* cx, HandleObject wrapper,
259                                    MutableHandleObject objp) const
260 {
261     {
262         AutoCompartment call(cx, wrappedObject(wrapper));
263         if (!Wrapper::enumerate(cx, wrapper, objp))
264             return false;
265     }
266 
267     if (CanReify(objp))
268         return Reify(cx, cx->compartment(), objp);
269     return cx->compartment()->wrap(cx, objp);
270 }
271 
272 bool
call(JSContext * cx,HandleObject wrapper,const CallArgs & args) const273 CrossCompartmentWrapper::call(JSContext* cx, HandleObject wrapper, const CallArgs& args) const
274 {
275     RootedObject wrapped(cx, wrappedObject(wrapper));
276 
277     {
278         AutoCompartment call(cx, wrapped);
279 
280         args.setCallee(ObjectValue(*wrapped));
281         if (!cx->compartment()->wrap(cx, args.mutableThisv()))
282             return false;
283 
284         for (size_t n = 0; n < args.length(); ++n) {
285             if (!cx->compartment()->wrap(cx, args[n]))
286                 return false;
287         }
288 
289         if (!Wrapper::call(cx, wrapper, args))
290             return false;
291     }
292 
293     return cx->compartment()->wrap(cx, args.rval());
294 }
295 
296 bool
construct(JSContext * cx,HandleObject wrapper,const CallArgs & args) const297 CrossCompartmentWrapper::construct(JSContext* cx, HandleObject wrapper, const CallArgs& args) const
298 {
299     RootedObject wrapped(cx, wrappedObject(wrapper));
300     {
301         AutoCompartment call(cx, wrapped);
302 
303         for (size_t n = 0; n < args.length(); ++n) {
304             if (!cx->compartment()->wrap(cx, args[n]))
305                 return false;
306         }
307         if (!cx->compartment()->wrap(cx, args.newTarget()))
308             return false;
309         if (!Wrapper::construct(cx, wrapper, args))
310             return false;
311     }
312     return cx->compartment()->wrap(cx, args.rval());
313 }
314 
315 bool
nativeCall(JSContext * cx,IsAcceptableThis test,NativeImpl impl,const CallArgs & srcArgs) const316 CrossCompartmentWrapper::nativeCall(JSContext* cx, IsAcceptableThis test, NativeImpl impl,
317                                     const CallArgs& srcArgs) const
318 {
319     RootedObject wrapper(cx, &srcArgs.thisv().toObject());
320     MOZ_ASSERT(srcArgs.thisv().isMagic(JS_IS_CONSTRUCTING) ||
321                !UncheckedUnwrap(wrapper)->is<CrossCompartmentWrapperObject>());
322 
323     RootedObject wrapped(cx, wrappedObject(wrapper));
324     {
325         AutoCompartment call(cx, wrapped);
326         InvokeArgs dstArgs(cx);
327         if (!dstArgs.init(cx, srcArgs.length()))
328             return false;
329 
330         Value* src = srcArgs.base();
331         Value* srcend = srcArgs.array() + srcArgs.length();
332         Value* dst = dstArgs.base();
333 
334         RootedValue source(cx);
335         for (; src < srcend; ++src, ++dst) {
336             source = *src;
337             if (!cx->compartment()->wrap(cx, &source))
338                 return false;
339             *dst = source.get();
340 
341             // Handle |this| specially. When we rewrap on the other side of the
342             // membrane, we might apply a same-compartment security wrapper that
343             // will stymie this whole process. If that happens, unwrap the wrapper.
344             // This logic can go away when same-compartment security wrappers go away.
345             if ((src == srcArgs.base() + 1) && dst->isObject()) {
346                 RootedObject thisObj(cx, &dst->toObject());
347                 if (thisObj->is<WrapperObject>() &&
348                     Wrapper::wrapperHandler(thisObj)->hasSecurityPolicy())
349                 {
350                     MOZ_ASSERT(!thisObj->is<CrossCompartmentWrapperObject>());
351                     *dst = ObjectValue(*Wrapper::wrappedObject(thisObj));
352                 }
353             }
354         }
355 
356         if (!CallNonGenericMethod(cx, test, impl, dstArgs))
357             return false;
358 
359         srcArgs.rval().set(dstArgs.rval());
360     }
361     return cx->compartment()->wrap(cx, srcArgs.rval());
362 }
363 
364 bool
hasInstance(JSContext * cx,HandleObject wrapper,MutableHandleValue v,bool * bp) const365 CrossCompartmentWrapper::hasInstance(JSContext* cx, HandleObject wrapper, MutableHandleValue v,
366                                      bool* bp) const
367 {
368     AutoCompartment call(cx, wrappedObject(wrapper));
369     if (!cx->compartment()->wrap(cx, v))
370         return false;
371     return Wrapper::hasInstance(cx, wrapper, v, bp);
372 }
373 
374 const char*
className(JSContext * cx,HandleObject wrapper) const375 CrossCompartmentWrapper::className(JSContext* cx, HandleObject wrapper) const
376 {
377     AutoCompartment call(cx, wrappedObject(wrapper));
378     return Wrapper::className(cx, wrapper);
379 }
380 
381 JSString*
fun_toString(JSContext * cx,HandleObject wrapper,unsigned indent) const382 CrossCompartmentWrapper::fun_toString(JSContext* cx, HandleObject wrapper, unsigned indent) const
383 {
384     RootedString str(cx);
385     {
386         AutoCompartment call(cx, wrappedObject(wrapper));
387         str = Wrapper::fun_toString(cx, wrapper, indent);
388         if (!str)
389             return nullptr;
390     }
391     if (!cx->compartment()->wrap(cx, &str))
392         return nullptr;
393     return str;
394 }
395 
396 bool
regexp_toShared(JSContext * cx,HandleObject wrapper,RegExpGuard * g) const397 CrossCompartmentWrapper::regexp_toShared(JSContext* cx, HandleObject wrapper, RegExpGuard* g) const
398 {
399     RegExpGuard wrapperGuard(cx);
400     {
401         AutoCompartment call(cx, wrappedObject(wrapper));
402         if (!Wrapper::regexp_toShared(cx, wrapper, &wrapperGuard))
403             return false;
404     }
405 
406     // Get an equivalent RegExpShared associated with the current compartment.
407     RegExpShared* re = wrapperGuard.re();
408     return cx->compartment()->regExps.get(cx, re->getSource(), re->getFlags(), g);
409 }
410 
411 bool
boxedValue_unbox(JSContext * cx,HandleObject wrapper,MutableHandleValue vp) const412 CrossCompartmentWrapper::boxedValue_unbox(JSContext* cx, HandleObject wrapper, MutableHandleValue vp) const
413 {
414     PIERCE(cx, wrapper,
415            NOTHING,
416            Wrapper::boxedValue_unbox(cx, wrapper, vp),
417            cx->compartment()->wrap(cx, vp));
418 }
419 
420 const CrossCompartmentWrapper CrossCompartmentWrapper::singleton(0u);
421 
422 bool
IsCrossCompartmentWrapper(JSObject * obj)423 js::IsCrossCompartmentWrapper(JSObject* obj)
424 {
425     return IsWrapper(obj) &&
426            !!(Wrapper::wrapperHandler(obj)->flags() & Wrapper::CROSS_COMPARTMENT);
427 }
428 
429 void
NukeCrossCompartmentWrapper(JSContext * cx,JSObject * wrapper)430 js::NukeCrossCompartmentWrapper(JSContext* cx, JSObject* wrapper)
431 {
432     MOZ_ASSERT(wrapper->is<CrossCompartmentWrapperObject>());
433 
434     NotifyGCNukeWrapper(wrapper);
435 
436     wrapper->as<ProxyObject>().nuke(&DeadObjectProxy::singleton);
437 
438     MOZ_ASSERT(IsDeadProxyObject(wrapper));
439 }
440 
441 /*
442  * NukeChromeCrossCompartmentWrappersForGlobal reaches into chrome and cuts
443  * all of the cross-compartment wrappers that point to objects parented to
444  * obj's global.  The snag here is that we need to avoid cutting wrappers that
445  * point to the window object on page navigation (inner window destruction)
446  * and only do that on tab close (outer window destruction).  Thus the
447  * option of how to handle the global object.
448  */
JS_FRIEND_API(bool)449 JS_FRIEND_API(bool)
450 js::NukeCrossCompartmentWrappers(JSContext* cx,
451                                  const CompartmentFilter& sourceFilter,
452                                  const CompartmentFilter& targetFilter,
453                                  js::NukeReferencesToWindow nukeReferencesToWindow)
454 {
455     CHECK_REQUEST(cx);
456     JSRuntime* rt = cx->runtime();
457 
458     // Iterate through scopes looking for system cross compartment wrappers
459     // that point to an object that shares a global with obj.
460 
461     for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
462         if (!sourceFilter.match(c))
463             continue;
464 
465         // Iterate the wrappers looking for anything interesting.
466         for (JSCompartment::WrapperEnum e(c); !e.empty(); e.popFront()) {
467             // Some cross-compartment wrappers are for strings.  We're not
468             // interested in those.
469             const CrossCompartmentKey& k = e.front().key();
470             if (k.kind != CrossCompartmentKey::ObjectWrapper)
471                 continue;
472 
473             AutoWrapperRooter wobj(cx, WrapperValue(e));
474             JSObject* wrapped = UncheckedUnwrap(wobj);
475 
476             if (nukeReferencesToWindow == DontNukeWindowReferences &&
477                 IsWindowProxy(wrapped))
478             {
479                 continue;
480             }
481 
482             if (targetFilter.match(wrapped->compartment())) {
483                 // We found a wrapper to nuke.
484                 e.removeFront();
485                 NukeCrossCompartmentWrapper(cx, wobj);
486             }
487         }
488     }
489 
490     return true;
491 }
492 
493 // Given a cross-compartment wrapper |wobj|, update it to point to
494 // |newTarget|. This recomputes the wrapper with JS_WrapValue, and thus can be
495 // useful even if wrapper already points to newTarget.
496 bool
RemapWrapper(JSContext * cx,JSObject * wobjArg,JSObject * newTargetArg)497 js::RemapWrapper(JSContext* cx, JSObject* wobjArg, JSObject* newTargetArg)
498 {
499     RootedObject wobj(cx, wobjArg);
500     RootedObject newTarget(cx, newTargetArg);
501     MOZ_ASSERT(wobj->is<CrossCompartmentWrapperObject>());
502     MOZ_ASSERT(!newTarget->is<CrossCompartmentWrapperObject>());
503     JSObject* origTarget = Wrapper::wrappedObject(wobj);
504     MOZ_ASSERT(origTarget);
505     Value origv = ObjectValue(*origTarget);
506     JSCompartment* wcompartment = wobj->compartment();
507 
508     AutoDisableProxyCheck adpc(cx->runtime());
509 
510     // If we're mapping to a different target (as opposed to just recomputing
511     // for the same target), we must not have an existing wrapper for the new
512     // target, otherwise this will break.
513     MOZ_ASSERT_IF(origTarget != newTarget,
514                   !wcompartment->lookupWrapper(ObjectValue(*newTarget)));
515 
516     // The old value should still be in the cross-compartment wrapper map, and
517     // the lookup should return wobj.
518     WrapperMap::Ptr p = wcompartment->lookupWrapper(origv);
519     MOZ_ASSERT(&p->value().unsafeGet()->toObject() == wobj);
520     wcompartment->removeWrapper(p);
521 
522     // When we remove origv from the wrapper map, its wrapper, wobj, must
523     // immediately cease to be a cross-compartment wrapper. Neuter it.
524     NukeCrossCompartmentWrapper(cx, wobj);
525 
526     // First, we wrap it in the new compartment. We try to use the existing
527     // wrapper, |wobj|, since it's been nuked anyway. The wrap() function has
528     // the choice to reuse |wobj| or not.
529     RootedObject tobj(cx, newTarget);
530     AutoCompartment ac(cx, wobj);
531     if (!wcompartment->wrap(cx, &tobj, wobj))
532         MOZ_CRASH();
533 
534     // If wrap() reused |wobj|, it will have overwritten it and returned with
535     // |tobj == wobj|. Otherwise, |tobj| will point to a new wrapper and |wobj|
536     // will still be nuked. In the latter case, we replace |wobj| with the
537     // contents of the new wrapper in |tobj|.
538     if (tobj != wobj) {
539         // Now, because we need to maintain object identity, we do a brain
540         // transplant on the old object so that it contains the contents of the
541         // new one.
542         if (!JSObject::swap(cx, wobj, tobj))
543             MOZ_CRASH();
544     }
545 
546     // Before swapping, this wrapper came out of wrap(), which enforces the
547     // invariant that the wrapper in the map points directly to the key.
548     MOZ_ASSERT(Wrapper::wrappedObject(wobj) == newTarget);
549 
550     // Update the entry in the compartment's wrapper map to point to the old
551     // wrapper, which has now been updated (via reuse or swap).
552     MOZ_ASSERT(wobj->is<WrapperObject>());
553     wcompartment->putWrapper(cx, CrossCompartmentKey(newTarget), ObjectValue(*wobj));
554     return true;
555 }
556 
557 // Remap all cross-compartment wrappers pointing to |oldTarget| to point to
558 // |newTarget|. All wrappers are recomputed.
JS_FRIEND_API(bool)559 JS_FRIEND_API(bool)
560 js::RemapAllWrappersForObject(JSContext* cx, JSObject* oldTargetArg,
561                               JSObject* newTargetArg)
562 {
563     RootedValue origv(cx, ObjectValue(*oldTargetArg));
564     RootedObject newTarget(cx, newTargetArg);
565 
566     AutoWrapperVector toTransplant(cx);
567     if (!toTransplant.reserve(cx->runtime()->numCompartments))
568         return false;
569 
570     for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) {
571         if (WrapperMap::Ptr wp = c->lookupWrapper(origv)) {
572             // We found a wrapper. Remember and root it.
573             toTransplant.infallibleAppend(WrapperValue(wp));
574         }
575     }
576 
577     for (const WrapperValue& v : toTransplant) {
578         if (!RemapWrapper(cx, &v.toObject(), newTarget))
579             MOZ_CRASH();
580     }
581 
582     return true;
583 }
584 
JS_FRIEND_API(bool)585 JS_FRIEND_API(bool)
586 js::RecomputeWrappers(JSContext* cx, const CompartmentFilter& sourceFilter,
587                       const CompartmentFilter& targetFilter)
588 {
589     AutoWrapperVector toRecompute(cx);
590 
591     for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) {
592         // Filter by source compartment.
593         if (!sourceFilter.match(c))
594             continue;
595 
596         // Iterate over the wrappers, filtering appropriately.
597         for (JSCompartment::WrapperEnum e(c); !e.empty(); e.popFront()) {
598             // Filter out non-objects.
599             const CrossCompartmentKey& k = e.front().key();
600             if (k.kind != CrossCompartmentKey::ObjectWrapper)
601                 continue;
602 
603             // Filter by target compartment.
604             if (!targetFilter.match(static_cast<JSObject*>(k.wrapped)->compartment()))
605                 continue;
606 
607             // Add it to the list.
608             if (!toRecompute.append(WrapperValue(e)))
609                 return false;
610         }
611     }
612 
613     // Recompute all the wrappers in the list.
614     for (const WrapperValue& v : toRecompute) {
615         JSObject* wrapper = &v.toObject();
616         JSObject* wrapped = Wrapper::wrappedObject(wrapper);
617         if (!RemapWrapper(cx, wrapper, wrapped))
618             MOZ_CRASH();
619     }
620 
621     return true;
622 }
623