tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

CrossCompartmentWrapper.cpp (23831B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sts=2 et sw=2 tw=80:
      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 "builtin/FinalizationRegistryObject.h"
      8 #include "debugger/Debugger.h"
      9 #include "gc/GC.h"
     10 #include "gc/PublicIterators.h"
     11 #include "js/friend/WindowProxy.h"  // js::IsWindow, js::IsWindowProxy
     12 #include "js/Wrapper.h"
     13 #include "proxy/DeadObjectProxy.h"
     14 #include "proxy/DOMProxy.h"
     15 #include "vm/Iteration.h"
     16 #include "vm/Runtime.h"
     17 #include "vm/WrapperObject.h"
     18 
     19 #include "gc/Nursery-inl.h"
     20 #include "vm/Compartment-inl.h"
     21 #include "vm/JSObject-inl.h"
     22 #include "vm/Realm-inl.h"
     23 
     24 using namespace js;
     25 
     26 #define PIERCE(cx, wrapper, pre, op, post)        \
     27  JS_BEGIN_MACRO                                  \
     28    bool ok;                                      \
     29    {                                             \
     30      AutoRealm call(cx, wrappedObject(wrapper)); \
     31      ok = (pre) && (op);                         \
     32    }                                             \
     33    return ok && (post);                          \
     34  JS_END_MACRO
     35 
     36 #define NOTHING (true)
     37 
     38 static bool MarkAtoms(JSContext* cx, jsid id) {
     39  cx->markId(id);
     40  return true;
     41 }
     42 
     43 static bool MarkAtoms(JSContext* cx, HandleIdVector ids) {
     44  for (size_t i = 0; i < ids.length(); i++) {
     45    cx->markId(ids[i]);
     46  }
     47  return true;
     48 }
     49 
     50 bool CrossCompartmentWrapper::getOwnPropertyDescriptor(
     51    JSContext* cx, HandleObject wrapper, HandleId id,
     52    MutableHandle<mozilla::Maybe<PropertyDescriptor>> desc) const {
     53  PIERCE(cx, wrapper, MarkAtoms(cx, id),
     54         Wrapper::getOwnPropertyDescriptor(cx, wrapper, id, desc),
     55         cx->compartment()->wrap(cx, desc));
     56 }
     57 
     58 bool CrossCompartmentWrapper::defineProperty(JSContext* cx,
     59                                             HandleObject wrapper, HandleId id,
     60                                             Handle<PropertyDescriptor> desc,
     61                                             ObjectOpResult& result) const {
     62  Rooted<PropertyDescriptor> desc2(cx, desc);
     63  PIERCE(cx, wrapper, MarkAtoms(cx, id) && cx->compartment()->wrap(cx, &desc2),
     64         Wrapper::defineProperty(cx, wrapper, id, desc2, result), NOTHING);
     65 }
     66 
     67 bool CrossCompartmentWrapper::ownPropertyKeys(
     68    JSContext* cx, HandleObject wrapper, MutableHandleIdVector props) const {
     69  PIERCE(cx, wrapper, NOTHING, Wrapper::ownPropertyKeys(cx, wrapper, props),
     70         MarkAtoms(cx, props));
     71 }
     72 
     73 bool CrossCompartmentWrapper::delete_(JSContext* cx, HandleObject wrapper,
     74                                      HandleId id,
     75                                      ObjectOpResult& result) const {
     76  PIERCE(cx, wrapper, MarkAtoms(cx, id),
     77         Wrapper::delete_(cx, wrapper, id, result), NOTHING);
     78 }
     79 
     80 bool CrossCompartmentWrapper::getPrototype(JSContext* cx, HandleObject wrapper,
     81                                           MutableHandleObject protop) const {
     82  {
     83    RootedObject wrapped(cx, wrappedObject(wrapper));
     84    AutoRealm call(cx, wrapped);
     85    if (!GetPrototype(cx, wrapped, protop)) {
     86      return false;
     87    }
     88  }
     89 
     90  return cx->compartment()->wrap(cx, protop);
     91 }
     92 
     93 bool CrossCompartmentWrapper::setPrototype(JSContext* cx, HandleObject wrapper,
     94                                           HandleObject proto,
     95                                           ObjectOpResult& result) const {
     96  RootedObject protoCopy(cx, proto);
     97  PIERCE(cx, wrapper, cx->compartment()->wrap(cx, &protoCopy),
     98         Wrapper::setPrototype(cx, wrapper, protoCopy, result), NOTHING);
     99 }
    100 
    101 bool CrossCompartmentWrapper::getPrototypeIfOrdinary(
    102    JSContext* cx, HandleObject wrapper, bool* isOrdinary,
    103    MutableHandleObject protop) const {
    104  {
    105    RootedObject wrapped(cx, wrappedObject(wrapper));
    106    AutoRealm call(cx, wrapped);
    107    if (!GetPrototypeIfOrdinary(cx, wrapped, isOrdinary, protop)) {
    108      return false;
    109    }
    110 
    111    if (!*isOrdinary) {
    112      return true;
    113    }
    114  }
    115 
    116  return cx->compartment()->wrap(cx, protop);
    117 }
    118 
    119 bool CrossCompartmentWrapper::setImmutablePrototype(JSContext* cx,
    120                                                    HandleObject wrapper,
    121                                                    bool* succeeded) const {
    122  PIERCE(cx, wrapper, NOTHING,
    123         Wrapper::setImmutablePrototype(cx, wrapper, succeeded), NOTHING);
    124 }
    125 
    126 bool CrossCompartmentWrapper::preventExtensions(JSContext* cx,
    127                                                HandleObject wrapper,
    128                                                ObjectOpResult& result) const {
    129  PIERCE(cx, wrapper, NOTHING, Wrapper::preventExtensions(cx, wrapper, result),
    130         NOTHING);
    131 }
    132 
    133 bool CrossCompartmentWrapper::isExtensible(JSContext* cx, HandleObject wrapper,
    134                                           bool* extensible) const {
    135  PIERCE(cx, wrapper, NOTHING, Wrapper::isExtensible(cx, wrapper, extensible),
    136         NOTHING);
    137 }
    138 
    139 bool CrossCompartmentWrapper::has(JSContext* cx, HandleObject wrapper,
    140                                  HandleId id, bool* bp) const {
    141  PIERCE(cx, wrapper, MarkAtoms(cx, id), Wrapper::has(cx, wrapper, id, bp),
    142         NOTHING);
    143 }
    144 
    145 bool CrossCompartmentWrapper::hasOwn(JSContext* cx, HandleObject wrapper,
    146                                     HandleId id, bool* bp) const {
    147  PIERCE(cx, wrapper, MarkAtoms(cx, id), Wrapper::hasOwn(cx, wrapper, id, bp),
    148         NOTHING);
    149 }
    150 
    151 static bool WrapReceiver(JSContext* cx, HandleObject wrapper,
    152                         MutableHandleValue receiver) {
    153  // Usually the receiver is the wrapper and we can just unwrap it. If the
    154  // wrapped object is also a wrapper, things are more complicated and we
    155  // fall back to the slow path (it calls UncheckedUnwrap to unwrap all
    156  // wrappers).
    157  if (ObjectValue(*wrapper) == receiver) {
    158    JSObject* wrapped = Wrapper::wrappedObject(wrapper);
    159    if (!IsWrapper(wrapped)) {
    160      MOZ_ASSERT(wrapped->compartment() == cx->compartment());
    161      MOZ_ASSERT(!IsWindow(wrapped));
    162      receiver.setObject(*wrapped);
    163      return true;
    164    }
    165  }
    166 
    167  return cx->compartment()->wrap(cx, receiver);
    168 }
    169 
    170 bool CrossCompartmentWrapper::get(JSContext* cx, HandleObject wrapper,
    171                                  HandleValue receiver, HandleId id,
    172                                  MutableHandleValue vp) const {
    173  RootedValue receiverCopy(cx, receiver);
    174  {
    175    AutoRealm call(cx, wrappedObject(wrapper));
    176    if (!MarkAtoms(cx, id) || !WrapReceiver(cx, wrapper, &receiverCopy)) {
    177      return false;
    178    }
    179 
    180    if (!Wrapper::get(cx, wrapper, receiverCopy, id, vp)) {
    181      return false;
    182    }
    183  }
    184  return cx->compartment()->wrap(cx, vp);
    185 }
    186 
    187 bool CrossCompartmentWrapper::set(JSContext* cx, HandleObject wrapper,
    188                                  HandleId id, HandleValue v,
    189                                  HandleValue receiver,
    190                                  ObjectOpResult& result) const {
    191  RootedValue valCopy(cx, v);
    192  RootedValue receiverCopy(cx, receiver);
    193  PIERCE(cx, wrapper,
    194         MarkAtoms(cx, id) && cx->compartment()->wrap(cx, &valCopy) &&
    195             WrapReceiver(cx, wrapper, &receiverCopy),
    196         Wrapper::set(cx, wrapper, id, valCopy, receiverCopy, result), NOTHING);
    197 }
    198 
    199 bool CrossCompartmentWrapper::getOwnEnumerablePropertyKeys(
    200    JSContext* cx, HandleObject wrapper, MutableHandleIdVector props) const {
    201  PIERCE(cx, wrapper, NOTHING,
    202         Wrapper::getOwnEnumerablePropertyKeys(cx, wrapper, props),
    203         MarkAtoms(cx, props));
    204 }
    205 
    206 bool CrossCompartmentWrapper::enumerate(JSContext* cx, HandleObject wrapper,
    207                                        MutableHandleIdVector props) const {
    208  PIERCE(cx, wrapper, NOTHING, Wrapper::enumerate(cx, wrapper, props),
    209         MarkAtoms(cx, props));
    210 }
    211 
    212 bool CrossCompartmentWrapper::call(JSContext* cx, HandleObject wrapper,
    213                                   const CallArgs& args) const {
    214  RootedObject wrapped(cx, wrappedObject(wrapper));
    215 
    216  {
    217    AutoRealm call(cx, wrapped);
    218 
    219    args.setCallee(ObjectValue(*wrapped));
    220    if (!cx->compartment()->wrap(cx, args.mutableThisv())) {
    221      return false;
    222    }
    223 
    224    for (size_t n = 0; n < args.length(); ++n) {
    225      if (!cx->compartment()->wrap(cx, args[n])) {
    226        return false;
    227      }
    228    }
    229 
    230    if (!Wrapper::call(cx, wrapper, args)) {
    231      return false;
    232    }
    233  }
    234 
    235  return cx->compartment()->wrap(cx, args.rval());
    236 }
    237 
    238 bool CrossCompartmentWrapper::construct(JSContext* cx, HandleObject wrapper,
    239                                        const CallArgs& args) const {
    240  RootedObject wrapped(cx, wrappedObject(wrapper));
    241  {
    242    AutoRealm call(cx, wrapped);
    243 
    244    for (size_t n = 0; n < args.length(); ++n) {
    245      if (!cx->compartment()->wrap(cx, args[n])) {
    246        return false;
    247      }
    248    }
    249    if (!cx->compartment()->wrap(cx, args.newTarget())) {
    250      return false;
    251    }
    252    if (!Wrapper::construct(cx, wrapper, args)) {
    253      return false;
    254    }
    255  }
    256  return cx->compartment()->wrap(cx, args.rval());
    257 }
    258 
    259 bool CrossCompartmentWrapper::nativeCall(JSContext* cx, IsAcceptableThis test,
    260                                         NativeImpl impl,
    261                                         const CallArgs& srcArgs) const {
    262  RootedObject wrapper(cx, &srcArgs.thisv().toObject());
    263  MOZ_ASSERT(srcArgs.thisv().isMagic(JS_IS_CONSTRUCTING) ||
    264             !UncheckedUnwrap(wrapper)->is<CrossCompartmentWrapperObject>());
    265 
    266  RootedObject wrapped(cx, wrappedObject(wrapper));
    267  {
    268    AutoRealm call(cx, wrapped);
    269    InvokeArgs dstArgs(cx);
    270    if (!dstArgs.init(cx, srcArgs.length())) {
    271      return false;
    272    }
    273 
    274    Value* src = srcArgs.base();
    275    Value* srcend = srcArgs.array() + srcArgs.length();
    276    Value* dst = dstArgs.base();
    277 
    278    RootedValue source(cx);
    279    for (; src < srcend; ++src, ++dst) {
    280      source = *src;
    281      if (!cx->compartment()->wrap(cx, &source)) {
    282        return false;
    283      }
    284      *dst = source.get();
    285 
    286      // Handle |this| specially. When we rewrap on the other side of the
    287      // membrane, we might apply a same-compartment security wrapper that
    288      // will stymie this whole process. If that happens, unwrap the wrapper.
    289      // This logic can go away when same-compartment security wrappers go away.
    290      if ((src == srcArgs.base() + 1) && dst->isObject()) {
    291        RootedObject thisObj(cx, &dst->toObject());
    292        if (thisObj->is<WrapperObject>() &&
    293            Wrapper::wrapperHandler(thisObj)->hasSecurityPolicy()) {
    294          MOZ_ASSERT(!thisObj->is<CrossCompartmentWrapperObject>());
    295          *dst = ObjectValue(*Wrapper::wrappedObject(thisObj));
    296        }
    297      }
    298    }
    299 
    300    if (!CallNonGenericMethod(cx, test, impl, dstArgs)) {
    301      return false;
    302    }
    303 
    304    srcArgs.rval().set(dstArgs.rval());
    305  }
    306  return cx->compartment()->wrap(cx, srcArgs.rval());
    307 }
    308 
    309 const char* CrossCompartmentWrapper::className(JSContext* cx,
    310                                               HandleObject wrapper) const {
    311  AutoRealm call(cx, wrappedObject(wrapper));
    312  return Wrapper::className(cx, wrapper);
    313 }
    314 
    315 JSString* CrossCompartmentWrapper::fun_toString(JSContext* cx,
    316                                                HandleObject wrapper,
    317                                                bool isToSource) const {
    318  RootedString str(cx);
    319  {
    320    AutoRealm call(cx, wrappedObject(wrapper));
    321    str = Wrapper::fun_toString(cx, wrapper, isToSource);
    322    if (!str) {
    323      return nullptr;
    324    }
    325  }
    326  if (!cx->compartment()->wrap(cx, &str)) {
    327    return nullptr;
    328  }
    329  return str;
    330 }
    331 
    332 RegExpShared* CrossCompartmentWrapper::regexp_toShared(
    333    JSContext* cx, HandleObject wrapper) const {
    334  RootedRegExpShared re(cx);
    335  {
    336    AutoRealm call(cx, wrappedObject(wrapper));
    337    re = Wrapper::regexp_toShared(cx, wrapper);
    338    if (!re) {
    339      return nullptr;
    340    }
    341  }
    342 
    343  // Get an equivalent RegExpShared associated with the current compartment.
    344  Rooted<JSAtom*> source(cx, re->getSource());
    345  cx->markAtom(source);
    346  return cx->zone()->regExps().get(cx, source, re->getFlags());
    347 }
    348 
    349 bool CrossCompartmentWrapper::boxedValue_unbox(JSContext* cx,
    350                                               HandleObject wrapper,
    351                                               MutableHandleValue vp) const {
    352  PIERCE(cx, wrapper, NOTHING, Wrapper::boxedValue_unbox(cx, wrapper, vp),
    353         cx->compartment()->wrap(cx, vp));
    354 }
    355 
    356 const CrossCompartmentWrapper CrossCompartmentWrapper::singleton(0u);
    357 
    358 JS_PUBLIC_API void js::NukeCrossCompartmentWrapper(JSContext* cx,
    359                                                   JSObject* wrapper) {
    360  JS::Compartment* comp = wrapper->compartment();
    361  auto ptr = comp->lookupWrapper(Wrapper::wrappedObject(wrapper));
    362  if (ptr) {
    363    comp->removeWrapper(ptr);
    364  }
    365  NukeRemovedCrossCompartmentWrapper(cx, wrapper);
    366 }
    367 
    368 JS_PUBLIC_API void js::NukeCrossCompartmentWrapperIfExists(
    369    JSContext* cx, JS::Compartment* source, JSObject* target) {
    370  MOZ_ASSERT(source != target->compartment());
    371  MOZ_ASSERT(!target->is<CrossCompartmentWrapperObject>());
    372  auto ptr = source->lookupWrapper(target);
    373  if (ptr) {
    374    JSObject* wrapper = ptr->value().get();
    375    NukeCrossCompartmentWrapper(cx, wrapper);
    376  }
    377 }
    378 
    379 // Returns true iff all realms in the compartment have been nuked.
    380 static bool NukedAllRealms(JS::Compartment* comp) {
    381  for (RealmsInCompartmentIter realm(comp); !realm.done(); realm.next()) {
    382    if (!realm->nukedIncomingWrappers) {
    383      return false;
    384    }
    385  }
    386  return true;
    387 }
    388 
    389 /*
    390 * NukeCrossCompartmentWrappers invalidates all cross-compartment wrappers
    391 * that point to objects in the |target| realm.
    392 *
    393 * There is some complexity in targeting to preserve semantics which requires
    394 * the filtering and behavioural options:
    395 *
    396 * - |sourceFilter| limits the compartments searched for source pointers
    397 * - |nukeReferencesToWindow| will, if set to DontNukeWindowReferences skip
    398 *   wrappers whose target is the window proxy of the target realm.
    399 * - |nukeReferencesFromTarget| will, when set to NukeAllReferences, disallow
    400 *   the creation of new wrappers to the target realm. This option can also
    401 *   allow more wrappers to be cleaned up transitively.
    402 */
    403 JS_PUBLIC_API bool js::NukeCrossCompartmentWrappers(
    404    JSContext* cx, const CompartmentFilter& sourceFilter, JS::Realm* target,
    405    js::NukeReferencesToWindow nukeReferencesToWindow,
    406    js::NukeReferencesFromTarget nukeReferencesFromTarget) {
    407  CHECK_THREAD(cx);
    408  JSRuntime* rt = cx->runtime();
    409 
    410  // If we're nuking all wrappers into the target realm, prevent us from
    411  // creating new wrappers for it in the future.
    412  if (nukeReferencesFromTarget == NukeAllReferences) {
    413    target->nukedIncomingWrappers = true;
    414  }
    415 
    416  for (CompartmentsIter c(rt); !c.done(); c.next()) {
    417    if (!sourceFilter.match(c)) {
    418      continue;
    419    }
    420 
    421    // If the realm matches both the source and target filter, we may want to
    422    // cut outgoing wrappers too, if we nuked all realms in the compartment.
    423    bool nukeAll =
    424        (nukeReferencesFromTarget == NukeAllReferences &&
    425         target->compartment() == c.get() && NukedAllRealms(c.get()));
    426 
    427    // Iterate only the wrappers that have target compartment matched unless
    428    // |nukeAll| is true. Use Maybe to avoid copying from conditionally
    429    // initializing ObjectWrapperEnum.
    430    mozilla::Maybe<Compartment::ObjectWrapperEnum> e;
    431    if (MOZ_LIKELY(!nukeAll)) {
    432      e.emplace(c, target->compartment());
    433    } else {
    434      e.emplace(c);
    435      c.get()->nukedOutgoingWrappers = true;
    436    }
    437    for (; !e->empty(); e->popFront()) {
    438      JSObject* key = e->front().key();
    439 
    440      AutoWrapperRooter wobj(cx, WrapperValue(*e));
    441 
    442      // Unwrap from the wrapped object in key instead of the wrapper, this
    443      // could save us a bit of time.
    444      JSObject* wrapped = UncheckedUnwrap(key);
    445 
    446      // Don't nuke wrappers for objects in other realms in the target
    447      // compartment unless nukeAll is set because in that case we want to nuke
    448      // all outgoing wrappers for the current compartment.
    449      if (!nukeAll && wrapped->nonCCWRealm() != target) {
    450        continue;
    451      }
    452 
    453      // Don't nuke wrappers for debugger objects. These are used in Breakpoints
    454      // and nuking them breaks debugger invariants.
    455      if (MOZ_UNLIKELY(wrapped->is<DebuggerInstanceObject>())) {
    456        continue;
    457      }
    458 
    459      // We only skip nuking window references that point to a target
    460      // compartment, not the ones that belong to it.
    461      if (nukeReferencesToWindow == DontNukeWindowReferences &&
    462          MOZ_LIKELY(!nukeAll) && IsWindowProxy(wrapped)) {
    463        continue;
    464      }
    465 
    466      // Now this is the wrapper we want to nuke.
    467      e->removeFront();
    468      NukeRemovedCrossCompartmentWrapper(cx, wobj);
    469    }
    470  }
    471 
    472  return true;
    473 }
    474 
    475 JS_PUBLIC_API bool js::AllowNewWrapper(JS::Compartment* target, JSObject* obj) {
    476  // Disallow creating new wrappers if we nuked the object realm or target
    477  // compartment.
    478 
    479  MOZ_ASSERT(obj->compartment() != target);
    480 
    481  // Wrappers for debugger objects are not nuked and we must continue to allow
    482  // them to be created or we will break the invariants in Compartment::wrap.
    483  if (MOZ_UNLIKELY(obj->is<DebuggerInstanceObject>())) {
    484    return true;
    485  }
    486 
    487  if (target->nukedOutgoingWrappers ||
    488      obj->nonCCWRealm()->nukedIncomingWrappers) {
    489    return false;
    490  }
    491 
    492  return true;
    493 }
    494 
    495 JS_PUBLIC_API bool js::NukedObjectRealm(JSObject* obj) {
    496  return obj->nonCCWRealm()->nukedIncomingWrappers;
    497 }
    498 
    499 // Given a cross-compartment wrapper |wobj|, update it to point to
    500 // |newTarget|. This recomputes the wrapper with JS_WrapValue, and thus can be
    501 // useful even if wrapper already points to newTarget.
    502 // This operation crashes on failure rather than leaving the heap in an
    503 // inconsistent state.
    504 void js::RemapWrapper(JSContext* cx, JSObject* wobjArg,
    505                      JSObject* newTargetArg) {
    506  RootedObject wobj(cx, wobjArg);
    507  RootedObject newTarget(cx, newTargetArg);
    508  MOZ_ASSERT(wobj->is<CrossCompartmentWrapperObject>());
    509  MOZ_ASSERT(!newTarget->is<CrossCompartmentWrapperObject>());
    510  JSObject* origTarget = Wrapper::wrappedObject(wobj);
    511  MOZ_ASSERT(origTarget);
    512  JS::Compartment* wcompartment = wobj->compartment();
    513  MOZ_ASSERT(wcompartment != newTarget->compartment());
    514 
    515  AutoDisableProxyCheck adpc;
    516 
    517  // This can't GC (and RemapDeadWrapper suppresses it).
    518  JS::AutoAssertNoGC nogc(cx);
    519 
    520  // If we're mapping to a different target (as opposed to just recomputing
    521  // for the same target), we must not have an existing wrapper for the new
    522  // target, otherwise this will break.
    523  MOZ_ASSERT_IF(origTarget != newTarget,
    524                !wcompartment->lookupWrapper(newTarget));
    525 
    526  // The old value should still be in the cross-compartment wrapper map, and
    527  // the lookup should return wobj.
    528  ObjectWrapperMap::Ptr p = wcompartment->lookupWrapper(origTarget);
    529  MOZ_ASSERT(*p->value().unsafeGet() == wobj);
    530  wcompartment->removeWrapper(p);
    531 
    532  // When we remove origv from the wrapper map, its wrapper, wobj, must
    533  // immediately cease to be a cross-compartment wrapper. Nuke it.
    534  NukeCrossCompartmentWrapper(cx, wobj);
    535 
    536  // If the target is a dead wrapper, and we're just fixing wrappers for
    537  // it, then we're done now that the CCW is a dead wrapper.
    538  if (JS_IsDeadWrapper(origTarget)) {
    539    MOZ_RELEASE_ASSERT(origTarget == newTarget);
    540    return;
    541  }
    542 
    543  js::RemapDeadWrapper(cx, wobj, newTarget);
    544 }
    545 
    546 // Given a dead proxy object |wobj|, turn it into a cross-compartment wrapper
    547 // pointing at |newTarget|.
    548 // This operation crashes on failure rather than leaving the heap in an
    549 // inconsistent state.
    550 void js::RemapDeadWrapper(JSContext* cx, HandleObject wobj,
    551                          HandleObject newTarget) {
    552  MOZ_ASSERT(IsDeadProxyObject(wobj));
    553  MOZ_ASSERT(!newTarget->is<CrossCompartmentWrapperObject>());
    554 
    555  // These are not exposed. Doing this would require updating the
    556  // FinalizationObservers data structures.
    557  MOZ_ASSERT(!newTarget->is<FinalizationRecordObject>());
    558 
    559  AutoDisableProxyCheck adpc;
    560 
    561  // Suppress GC while we manipulate the wrapper map so that it can't observe
    562  // intervening state.
    563  gc::AutoSuppressGC nogc(cx);
    564 
    565  // wobj is not a cross-compartment wrapper, so we can use nonCCWRealm.
    566  Realm* wrealm = wobj->nonCCWRealm();
    567 
    568  // First, we wrap it in the new compartment. We try to use the existing
    569  // wrapper, |wobj|, since it's been nuked anyway. The rewrap() function has
    570  // the choice to reuse |wobj| or not.
    571  RootedObject tobj(cx, newTarget);
    572  AutoRealmUnchecked ar(cx, wrealm);
    573  AutoEnterOOMUnsafeRegion oomUnsafe;
    574  JS::Compartment* wcompartment = wobj->compartment();
    575  if (!wcompartment->rewrap(cx, &tobj, wobj)) {
    576    oomUnsafe.crash("js::RemapWrapper");
    577  }
    578 
    579  // If rewrap() reused |wobj|, it will have overwritten it and returned with
    580  // |tobj == wobj|. Otherwise, |tobj| will point to a new wrapper and |wobj|
    581  // will still be nuked. In the latter case, we replace |wobj| with the
    582  // contents of the new wrapper in |tobj|.
    583  if (tobj != wobj) {
    584    // Now, because we need to maintain object identity, we do a brain
    585    // transplant on the old object so that it contains the contents of the
    586    // new one.
    587    JSObject::swap(cx, wobj, tobj, oomUnsafe);
    588  }
    589 
    590  if (!wobj->is<WrapperObject>()) {
    591    MOZ_ASSERT(js::IsDOMRemoteProxyObject(wobj) || IsDeadProxyObject(wobj));
    592    return;
    593  }
    594 
    595  // Before swapping, this wrapper came out of rewrap(), which enforces the
    596  // invariant that the wrapper in the map points directly to the key.
    597  MOZ_ASSERT(Wrapper::wrappedObject(wobj) == newTarget);
    598 
    599  // Update the entry in the compartment's wrapper map to point to the old
    600  // wrapper, which has now been updated (via reuse or swap).
    601  if (!wcompartment->putWrapper(cx, newTarget, wobj)) {
    602    oomUnsafe.crash("js::RemapWrapper");
    603  }
    604 }
    605 
    606 // Remap all cross-compartment wrappers pointing to |oldTarget| to point to
    607 // |newTarget|. All wrappers are recomputed.
    608 JS_PUBLIC_API bool js::RemapAllWrappersForObject(JSContext* cx,
    609                                                 HandleObject oldTarget,
    610                                                 HandleObject newTarget) {
    611  AutoWrapperVector toTransplant(cx);
    612 
    613  for (CompartmentsIter c(cx->runtime()); !c.done(); c.next()) {
    614    if (ObjectWrapperMap::Ptr wp = c->lookupWrapper(oldTarget)) {
    615      // We found a wrapper. Remember and root it.
    616      if (!toTransplant.append(WrapperValue(wp))) {
    617        return false;
    618      }
    619    }
    620  }
    621 
    622  for (const WrapperValue& v : toTransplant) {
    623    RemapWrapper(cx, v, newTarget);
    624  }
    625 
    626  return true;
    627 }
    628 
    629 JS_PUBLIC_API bool js::RecomputeWrappers(
    630    JSContext* cx, const CompartmentFilter& sourceFilter,
    631    const CompartmentFilter& targetFilter) {
    632  bool evictedNursery = false;
    633 
    634  AutoWrapperVector toRecompute(cx);
    635  for (CompartmentsIter c(cx->runtime()); !c.done(); c.next()) {
    636    // Filter by source compartment.
    637    if (!sourceFilter.match(c)) {
    638      continue;
    639    }
    640 
    641    if (!evictedNursery &&
    642        c->hasNurseryAllocatedObjectWrapperEntries(targetFilter)) {
    643      cx->runtime()->gc.evictNursery();
    644      evictedNursery = true;
    645    }
    646 
    647    // Iterate over object wrappers, filtering appropriately.
    648    for (Compartment::ObjectWrapperEnum e(c, targetFilter); !e.empty();
    649         e.popFront()) {
    650      // Don't remap wrappers to finalization record objects. These are used
    651      // internally and are not exposed.
    652      JSObject* wrapper = *e.front().value().unsafeGet();
    653      if (Wrapper::wrappedObject(wrapper)->is<FinalizationRecordObject>()) {
    654        continue;
    655      }
    656 
    657      // Add the wrapper to the list.
    658      if (!toRecompute.append(WrapperValue(e))) {
    659        return false;
    660      }
    661    }
    662  }
    663 
    664  // Recompute all the wrappers in the list.
    665  for (const WrapperValue& wrapper : toRecompute) {
    666    JSObject* wrapped = Wrapper::wrappedObject(wrapper);
    667    RemapWrapper(cx, wrapper, wrapped);
    668  }
    669 
    670  return true;
    671 }