LCOV - code coverage report
Current view: top level - js/src/proxy - CrossCompartmentWrapper.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 178 299 59.5 %
Date: 2017-07-14 16:53:18 Functions: 27 40 67.5 %
Legend: Lines: hit not hit

          Line data    Source code
       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             : #include "gc/Nursery-inl.h"
      17             : 
      18             : using namespace js;
      19             : 
      20             : #define PIERCE(cx, wrapper, pre, op, post)                      \
      21             :     JS_BEGIN_MACRO                                              \
      22             :         bool ok;                                                \
      23             :         {                                                       \
      24             :             AutoCompartment call(cx, wrappedObject(wrapper));   \
      25             :             ok = (pre) && (op);                                 \
      26             :         }                                                       \
      27             :         return ok && (post);                                    \
      28             :     JS_END_MACRO
      29             : 
      30             : #define NOTHING (true)
      31             : 
      32             : static bool
      33       12328 : MarkAtoms(JSContext* cx, jsid id)
      34             : {
      35       12328 :     cx->markId(id);
      36       12328 :     return true;
      37             : }
      38             : 
      39             : static bool
      40          55 : MarkAtoms(JSContext* cx, const AutoIdVector& ids)
      41             : {
      42         333 :     for (size_t i = 0; i < ids.length(); i++)
      43         278 :         cx->markId(ids[i]);
      44          55 :     return true;
      45             : }
      46             : 
      47             : bool
      48           0 : CrossCompartmentWrapper::getPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id,
      49             :                                                MutableHandle<PropertyDescriptor> desc) const
      50             : {
      51           0 :     PIERCE(cx, wrapper,
      52             :            MarkAtoms(cx, id),
      53             :            Wrapper::getPropertyDescriptor(cx, wrapper, id, desc),
      54             :            cx->compartment()->wrap(cx, desc));
      55             : }
      56             : 
      57             : bool
      58          97 : CrossCompartmentWrapper::getOwnPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id,
      59             :                                                   MutableHandle<PropertyDescriptor> desc) const
      60             : {
      61          97 :     PIERCE(cx, wrapper,
      62             :            MarkAtoms(cx, id),
      63             :            Wrapper::getOwnPropertyDescriptor(cx, wrapper, id, desc),
      64             :            cx->compartment()->wrap(cx, desc));
      65             : }
      66             : 
      67             : bool
      68        1787 : CrossCompartmentWrapper::defineProperty(JSContext* cx, HandleObject wrapper, HandleId id,
      69             :                                         Handle<PropertyDescriptor> desc,
      70             :                                         ObjectOpResult& result) const
      71             : {
      72        3574 :     Rooted<PropertyDescriptor> desc2(cx, desc);
      73        3574 :     PIERCE(cx, wrapper,
      74             :            MarkAtoms(cx, id) && cx->compartment()->wrap(cx, &desc2),
      75             :            Wrapper::defineProperty(cx, wrapper, id, desc2, result),
      76             :            NOTHING);
      77             : }
      78             : 
      79             : bool
      80           8 : CrossCompartmentWrapper::ownPropertyKeys(JSContext* cx, HandleObject wrapper,
      81             :                                          AutoIdVector& props) const
      82             : {
      83           8 :     PIERCE(cx, wrapper,
      84             :            NOTHING,
      85             :            Wrapper::ownPropertyKeys(cx, wrapper, props),
      86             :            MarkAtoms(cx, props));
      87             : }
      88             : 
      89             : bool
      90         370 : CrossCompartmentWrapper::delete_(JSContext* cx, HandleObject wrapper, HandleId id,
      91             :                                  ObjectOpResult& result) const
      92             : {
      93         370 :     PIERCE(cx, wrapper,
      94             :            MarkAtoms(cx, id),
      95             :            Wrapper::delete_(cx, wrapper, id, result),
      96             :            NOTHING);
      97             : }
      98             : 
      99             : bool
     100          53 : CrossCompartmentWrapper::getPrototype(JSContext* cx, HandleObject wrapper,
     101             :                                       MutableHandleObject protop) const
     102             : {
     103             :     {
     104         106 :         RootedObject wrapped(cx, wrappedObject(wrapper));
     105         106 :         AutoCompartment call(cx, wrapped);
     106          53 :         if (!GetPrototype(cx, wrapped, protop))
     107           0 :             return false;
     108          53 :         if (protop) {
     109          39 :             if (!JSObject::setDelegate(cx, protop))
     110           0 :                 return false;
     111             :         }
     112             :     }
     113             : 
     114          53 :     return cx->compartment()->wrap(cx, protop);
     115             : }
     116             : 
     117             : bool
     118           0 : CrossCompartmentWrapper::setPrototype(JSContext* cx, HandleObject wrapper,
     119             :                                       HandleObject proto, ObjectOpResult& result) const
     120             : {
     121           0 :     RootedObject protoCopy(cx, proto);
     122           0 :     PIERCE(cx, wrapper,
     123             :            cx->compartment()->wrap(cx, &protoCopy),
     124             :            Wrapper::setPrototype(cx, wrapper, protoCopy, result),
     125             :            NOTHING);
     126             : }
     127             : 
     128             : bool
     129           5 : CrossCompartmentWrapper::getPrototypeIfOrdinary(JSContext* cx, HandleObject wrapper,
     130             :                                                 bool* isOrdinary, MutableHandleObject protop) const
     131             : {
     132             :     {
     133          10 :         RootedObject wrapped(cx, wrappedObject(wrapper));
     134          10 :         AutoCompartment call(cx, wrapped);
     135           5 :         if (!GetPrototypeIfOrdinary(cx, wrapped, isOrdinary, protop))
     136           0 :             return false;
     137             : 
     138           5 :         if (!*isOrdinary)
     139           0 :             return true;
     140             : 
     141           5 :         if (protop) {
     142           3 :             if (!JSObject::setDelegate(cx, protop))
     143           0 :                 return false;
     144             :         }
     145             :     }
     146             : 
     147           5 :     return cx->compartment()->wrap(cx, protop);
     148             : }
     149             : 
     150             : bool
     151           0 : CrossCompartmentWrapper::setImmutablePrototype(JSContext* cx, HandleObject wrapper, bool* succeeded) const
     152             : {
     153           0 :     PIERCE(cx, wrapper,
     154             :            NOTHING,
     155             :            Wrapper::setImmutablePrototype(cx, wrapper, succeeded),
     156             :            NOTHING);
     157             : }
     158             : 
     159             : bool
     160           1 : CrossCompartmentWrapper::preventExtensions(JSContext* cx, HandleObject wrapper,
     161             :                                            ObjectOpResult& result) const
     162             : {
     163           1 :     PIERCE(cx, wrapper,
     164             :            NOTHING,
     165             :            Wrapper::preventExtensions(cx, wrapper, result),
     166             :            NOTHING);
     167             : }
     168             : 
     169             : bool
     170           0 : CrossCompartmentWrapper::isExtensible(JSContext* cx, HandleObject wrapper, bool* extensible) const
     171             : {
     172           0 :     PIERCE(cx, wrapper,
     173             :            NOTHING,
     174             :            Wrapper::isExtensible(cx, wrapper, extensible),
     175             :            NOTHING);
     176             : }
     177             : 
     178             : bool
     179         355 : CrossCompartmentWrapper::has(JSContext* cx, HandleObject wrapper, HandleId id, bool* bp) const
     180             : {
     181         355 :     PIERCE(cx, wrapper,
     182             :            MarkAtoms(cx, id),
     183             :            Wrapper::has(cx, wrapper, id, bp),
     184             :            NOTHING);
     185             : }
     186             : 
     187             : bool
     188          44 : CrossCompartmentWrapper::hasOwn(JSContext* cx, HandleObject wrapper, HandleId id, bool* bp) const
     189             : {
     190          44 :     PIERCE(cx, wrapper,
     191             :            MarkAtoms(cx, id),
     192             :            Wrapper::hasOwn(cx, wrapper, id, bp),
     193             :            NOTHING);
     194             : }
     195             : 
     196             : static bool
     197        9675 : WrapReceiver(JSContext* cx, HandleObject wrapper, MutableHandleValue receiver)
     198             : {
     199             :     // Usually the receiver is the wrapper and we can just unwrap it. If the
     200             :     // wrapped object is also a wrapper, things are more complicated and we
     201             :     // fall back to the slow path (it calls UncheckedUnwrap to unwrap all
     202             :     // wrappers).
     203        9675 :     if (ObjectValue(*wrapper) == receiver) {
     204        9484 :         JSObject* wrapped = Wrapper::wrappedObject(wrapper);
     205        9484 :         if (!IsWrapper(wrapped)) {
     206        9340 :             MOZ_ASSERT(wrapped->compartment() == cx->compartment());
     207        9340 :             MOZ_ASSERT(!IsWindow(wrapped));
     208        9340 :             receiver.setObject(*wrapped);
     209        9340 :             return true;
     210             :         }
     211             :     }
     212             : 
     213         335 :     return cx->compartment()->wrap(cx, receiver);
     214             : }
     215             : 
     216             : bool
     217        9314 : CrossCompartmentWrapper::get(JSContext* cx, HandleObject wrapper, HandleValue receiver,
     218             :                              HandleId id, MutableHandleValue vp) const
     219             : {
     220       18628 :     RootedValue receiverCopy(cx, receiver);
     221             :     {
     222       18628 :         AutoCompartment call(cx, wrappedObject(wrapper));
     223        9314 :         if (!MarkAtoms(cx, id) || !WrapReceiver(cx, wrapper, &receiverCopy))
     224           0 :             return false;
     225             : 
     226        9314 :         if (!Wrapper::get(cx, wrapper, receiverCopy, id, vp))
     227           0 :             return false;
     228             :     }
     229        9314 :     return cx->compartment()->wrap(cx, vp);
     230             : }
     231             : 
     232             : bool
     233         361 : CrossCompartmentWrapper::set(JSContext* cx, HandleObject wrapper, HandleId id, HandleValue v,
     234             :                              HandleValue receiver, ObjectOpResult& result) const
     235             : {
     236         722 :     RootedValue valCopy(cx, v);
     237         722 :     RootedValue receiverCopy(cx, receiver);
     238         722 :     PIERCE(cx, wrapper,
     239             :            MarkAtoms(cx, id) &&
     240             :            cx->compartment()->wrap(cx, &valCopy) &&
     241             :            WrapReceiver(cx, wrapper, &receiverCopy),
     242             :            Wrapper::set(cx, wrapper, id, valCopy, receiverCopy, result),
     243             :            NOTHING);
     244             : }
     245             : 
     246             : bool
     247          47 : CrossCompartmentWrapper::getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject wrapper,
     248             :                                                       AutoIdVector& props) const
     249             : {
     250          47 :     PIERCE(cx, wrapper,
     251             :            NOTHING,
     252             :            Wrapper::getOwnEnumerablePropertyKeys(cx, wrapper, props),
     253             :            MarkAtoms(cx, props));
     254             : }
     255             : 
     256             : /*
     257             :  * We can reify non-escaping iterator objects instead of having to wrap them. This
     258             :  * allows fast iteration over objects across a compartment boundary.
     259             :  */
     260             : static bool
     261           5 : CanReify(HandleObject obj)
     262             : {
     263          10 :     return obj->is<PropertyIteratorObject>() &&
     264          10 :            (obj->as<PropertyIteratorObject>().getNativeIterator()->flags & JSITER_ENUMERATE);
     265             : }
     266             : 
     267             : struct AutoCloseIterator
     268             : {
     269           0 :     AutoCloseIterator(JSContext* cx, PropertyIteratorObject* obj) : cx(cx), obj(cx, obj) {}
     270             : 
     271           0 :     ~AutoCloseIterator() {
     272           0 :         if (obj)
     273           0 :             MOZ_ALWAYS_TRUE(CloseIterator(cx, obj));
     274           0 :     }
     275             : 
     276           0 :     void clear() { obj = nullptr; }
     277             : 
     278             :   private:
     279             :     JSContext* cx;
     280             :     Rooted<PropertyIteratorObject*> obj;
     281             : };
     282             : 
     283             : static JSObject*
     284           0 : Reify(JSContext* cx, JSCompartment* origin, HandleObject objp)
     285             : {
     286           0 :     Rooted<PropertyIteratorObject*> iterObj(cx, &objp->as<PropertyIteratorObject>());
     287           0 :     NativeIterator* ni = iterObj->getNativeIterator();
     288             : 
     289           0 :     RootedObject obj(cx, ni->obj);
     290             :     {
     291           0 :         AutoCloseIterator close(cx, iterObj);
     292             : 
     293             :         /* Wrap the iteratee. */
     294           0 :         if (!origin->wrap(cx, &obj))
     295           0 :             return nullptr;
     296             : 
     297             :         /*
     298             :          * Wrap the elements in the iterator's snapshot.
     299             :          * N.B. the order of closing/creating iterators is important due to the
     300             :          * implicit cx->enumerators state.
     301             :          */
     302           0 :         size_t length = ni->numKeys();
     303           0 :         AutoIdVector keys(cx);
     304           0 :         if (length > 0) {
     305           0 :             if (!keys.reserve(length))
     306           0 :                 return nullptr;
     307           0 :             for (size_t i = 0; i < length; ++i) {
     308           0 :                 RootedId id(cx);
     309           0 :                 RootedValue v(cx, StringValue(ni->begin()[i]));
     310           0 :                 if (!ValueToId<CanGC>(cx, v, &id))
     311           0 :                     return nullptr;
     312           0 :                 keys.infallibleAppend(id);
     313             :             }
     314             :         }
     315             : 
     316           0 :         close.clear();
     317           0 :         MOZ_ALWAYS_TRUE(CloseIterator(cx, iterObj));
     318             : 
     319           0 :         obj = EnumeratedIdVectorToIterator(cx, obj, ni->flags, keys);
     320             :     }
     321           0 :     return obj;
     322             : }
     323             : 
     324             : JSObject*
     325           5 : CrossCompartmentWrapper::enumerate(JSContext* cx, HandleObject wrapper) const
     326             : {
     327          10 :     RootedObject res(cx);
     328             :     {
     329          10 :         AutoCompartment call(cx, wrappedObject(wrapper));
     330           5 :         res = Wrapper::enumerate(cx, wrapper);
     331           5 :         if (!res)
     332           0 :             return nullptr;
     333             :     }
     334             : 
     335           5 :     if (CanReify(res))
     336           0 :         return Reify(cx, cx->compartment(), res);
     337           5 :     if (!cx->compartment()->wrap(cx, &res))
     338           0 :         return nullptr;
     339           5 :     return res;
     340             : }
     341             : 
     342             : bool
     343        4925 : CrossCompartmentWrapper::call(JSContext* cx, HandleObject wrapper, const CallArgs& args) const
     344             : {
     345        9850 :     RootedObject wrapped(cx, wrappedObject(wrapper));
     346             : 
     347             :     {
     348        9763 :         AutoCompartment call(cx, wrapped);
     349             : 
     350        4925 :         args.setCallee(ObjectValue(*wrapped));
     351        4925 :         if (!cx->compartment()->wrap(cx, args.mutableThisv()))
     352           0 :             return false;
     353             : 
     354       12948 :         for (size_t n = 0; n < args.length(); ++n) {
     355        8023 :             if (!cx->compartment()->wrap(cx, args[n]))
     356           0 :                 return false;
     357             :         }
     358             : 
     359        4925 :         if (!Wrapper::call(cx, wrapper, args))
     360          87 :             return false;
     361             :     }
     362             : 
     363        4838 :     return cx->compartment()->wrap(cx, args.rval());
     364             : }
     365             : 
     366             : bool
     367         175 : CrossCompartmentWrapper::construct(JSContext* cx, HandleObject wrapper, const CallArgs& args) const
     368             : {
     369         350 :     RootedObject wrapped(cx, wrappedObject(wrapper));
     370             :     {
     371         350 :         AutoCompartment call(cx, wrapped);
     372             : 
     373         326 :         for (size_t n = 0; n < args.length(); ++n) {
     374         151 :             if (!cx->compartment()->wrap(cx, args[n]))
     375           0 :                 return false;
     376             :         }
     377         175 :         if (!cx->compartment()->wrap(cx, args.newTarget()))
     378           0 :             return false;
     379         175 :         if (!Wrapper::construct(cx, wrapper, args))
     380           0 :             return false;
     381             :     }
     382         175 :     return cx->compartment()->wrap(cx, args.rval());
     383             : }
     384             : 
     385             : bool
     386           0 : CrossCompartmentWrapper::nativeCall(JSContext* cx, IsAcceptableThis test, NativeImpl impl,
     387             :                                     const CallArgs& srcArgs) const
     388             : {
     389           0 :     RootedObject wrapper(cx, &srcArgs.thisv().toObject());
     390           0 :     MOZ_ASSERT(srcArgs.thisv().isMagic(JS_IS_CONSTRUCTING) ||
     391             :                !UncheckedUnwrap(wrapper)->is<CrossCompartmentWrapperObject>());
     392             : 
     393           0 :     RootedObject wrapped(cx, wrappedObject(wrapper));
     394             :     {
     395           0 :         AutoCompartment call(cx, wrapped);
     396           0 :         InvokeArgs dstArgs(cx);
     397           0 :         if (!dstArgs.init(cx, srcArgs.length()))
     398           0 :             return false;
     399             : 
     400           0 :         Value* src = srcArgs.base();
     401           0 :         Value* srcend = srcArgs.array() + srcArgs.length();
     402           0 :         Value* dst = dstArgs.base();
     403             : 
     404           0 :         RootedValue source(cx);
     405           0 :         for (; src < srcend; ++src, ++dst) {
     406           0 :             source = *src;
     407           0 :             if (!cx->compartment()->wrap(cx, &source))
     408           0 :                 return false;
     409           0 :             *dst = source.get();
     410             : 
     411             :             // Handle |this| specially. When we rewrap on the other side of the
     412             :             // membrane, we might apply a same-compartment security wrapper that
     413             :             // will stymie this whole process. If that happens, unwrap the wrapper.
     414             :             // This logic can go away when same-compartment security wrappers go away.
     415           0 :             if ((src == srcArgs.base() + 1) && dst->isObject()) {
     416           0 :                 RootedObject thisObj(cx, &dst->toObject());
     417           0 :                 if (thisObj->is<WrapperObject>() &&
     418           0 :                     Wrapper::wrapperHandler(thisObj)->hasSecurityPolicy())
     419             :                 {
     420           0 :                     MOZ_ASSERT(!thisObj->is<CrossCompartmentWrapperObject>());
     421           0 :                     *dst = ObjectValue(*Wrapper::wrappedObject(thisObj));
     422             :                 }
     423             :             }
     424             :         }
     425             : 
     426           0 :         if (!CallNonGenericMethod(cx, test, impl, dstArgs))
     427           0 :             return false;
     428             : 
     429           0 :         srcArgs.rval().set(dstArgs.rval());
     430             :     }
     431           0 :     return cx->compartment()->wrap(cx, srcArgs.rval());
     432             : }
     433             : 
     434             : bool
     435          24 : CrossCompartmentWrapper::hasInstance(JSContext* cx, HandleObject wrapper, MutableHandleValue v,
     436             :                                      bool* bp) const
     437             : {
     438          48 :     AutoCompartment call(cx, wrappedObject(wrapper));
     439          24 :     if (!cx->compartment()->wrap(cx, v))
     440           0 :         return false;
     441          24 :     return Wrapper::hasInstance(cx, wrapper, v, bp);
     442             : }
     443             : 
     444             : const char*
     445           0 : CrossCompartmentWrapper::className(JSContext* cx, HandleObject wrapper) const
     446             : {
     447           0 :     AutoCompartment call(cx, wrappedObject(wrapper));
     448           0 :     return Wrapper::className(cx, wrapper);
     449             : }
     450             : 
     451             : JSString*
     452           0 : CrossCompartmentWrapper::fun_toString(JSContext* cx, HandleObject wrapper, unsigned indent) const
     453             : {
     454           0 :     RootedString str(cx);
     455             :     {
     456           0 :         AutoCompartment call(cx, wrappedObject(wrapper));
     457           0 :         str = Wrapper::fun_toString(cx, wrapper, indent);
     458           0 :         if (!str)
     459           0 :             return nullptr;
     460             :     }
     461           0 :     if (!cx->compartment()->wrap(cx, &str))
     462           0 :         return nullptr;
     463           0 :     return str;
     464             : }
     465             : 
     466             : RegExpShared*
     467           0 : CrossCompartmentWrapper::regexp_toShared(JSContext* cx, HandleObject wrapper) const
     468             : {
     469           0 :     RootedRegExpShared re(cx);
     470             :     {
     471           0 :         AutoCompartment call(cx, wrappedObject(wrapper));
     472           0 :         re = Wrapper::regexp_toShared(cx, wrapper);
     473           0 :         if (!re)
     474           0 :             return nullptr;
     475             :     }
     476             : 
     477             :     // Get an equivalent RegExpShared associated with the current compartment.
     478           0 :     RootedAtom source(cx, re->getSource());
     479           0 :     cx->markAtom(source);
     480           0 :     return cx->zone()->regExps.get(cx, source, re->getFlags());
     481             : }
     482             : 
     483             : bool
     484           0 : CrossCompartmentWrapper::boxedValue_unbox(JSContext* cx, HandleObject wrapper, MutableHandleValue vp) const
     485             : {
     486           0 :     PIERCE(cx, wrapper,
     487             :            NOTHING,
     488             :            Wrapper::boxedValue_unbox(cx, wrapper, vp),
     489             :            cx->compartment()->wrap(cx, vp));
     490             : }
     491             : 
     492             : const CrossCompartmentWrapper CrossCompartmentWrapper::singleton(0u);
     493             : 
     494             : bool
     495       34748 : js::IsCrossCompartmentWrapper(JSObject* obj)
     496             : {
     497       62481 :     return IsWrapper(obj) &&
     498       62481 :            !!(Wrapper::wrapperHandler(obj)->flags() & Wrapper::CROSS_COMPARTMENT);
     499             : }
     500             : 
     501             : static void
     502          44 : NukeRemovedCrossCompartmentWrapper(JSContext* cx, JSObject* wrapper)
     503             : {
     504          44 :     MOZ_ASSERT(wrapper->is<CrossCompartmentWrapperObject>());
     505             : 
     506          44 :     NotifyGCNukeWrapper(wrapper);
     507             : 
     508          44 :     wrapper->as<ProxyObject>().nuke();
     509             : 
     510          44 :     MOZ_ASSERT(IsDeadProxyObject(wrapper));
     511          44 : }
     512             : 
     513             : JS_FRIEND_API(void)
     514          10 : js::NukeCrossCompartmentWrapper(JSContext* cx, JSObject* wrapper)
     515             : {
     516          10 :     JSCompartment* comp = wrapper->compartment();
     517          10 :     auto ptr = comp->lookupWrapper(Wrapper::wrappedObject(wrapper));
     518          10 :     if (ptr)
     519           0 :         comp->removeWrapper(ptr);
     520          10 :     NukeRemovedCrossCompartmentWrapper(cx, wrapper);
     521          10 : }
     522             : 
     523             : /*
     524             :  * NukeChromeCrossCompartmentWrappersForGlobal reaches into chrome and cuts
     525             :  * all of the cross-compartment wrappers that point to objects parented to
     526             :  * obj's global.  The snag here is that we need to avoid cutting wrappers that
     527             :  * point to the window object on page navigation (inner window destruction)
     528             :  * and only do that on tab close (outer window destruction).  Thus the
     529             :  * option of how to handle the global object.
     530             :  */
     531             : JS_FRIEND_API(bool)
     532           3 : js::NukeCrossCompartmentWrappers(JSContext* cx,
     533             :                                  const CompartmentFilter& sourceFilter,
     534             :                                  JSCompartment* target,
     535             :                                  js::NukeReferencesToWindow nukeReferencesToWindow,
     536             :                                  js::NukeReferencesFromTarget nukeReferencesFromTarget)
     537             : {
     538           6 :     CHECK_REQUEST(cx);
     539           3 :     JSRuntime* rt = cx->runtime();
     540             : 
     541         440 :     for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
     542         437 :         if (!sourceFilter.match(c))
     543          40 :             continue;
     544             : 
     545             :         // If the compartment matches both the source and target filter, we may
     546             :         // want to cut both incoming and outgoing wrappers.
     547         397 :         bool nukeAll = (nukeReferencesFromTarget == NukeAllReferences &&
     548         397 :                         target == c.get());
     549             : 
     550             :         // Iterate only the wrappers that have target compartment matched unless
     551             :         // |nukeAll| is true. The string wrappers that we're not interested in
     552             :         // won't be iterated, we can exclude them easily because they have
     553             :         // compartment nullptr. Use Maybe to avoid copying from conditionally
     554             :         // initializing NonStringWrapperEnum.
     555         794 :         mozilla::Maybe<JSCompartment::NonStringWrapperEnum> e;
     556         397 :         if (MOZ_LIKELY(!nukeAll))
     557         397 :             e.emplace(c, target);
     558             :         else
     559           0 :             e.emplace(c);
     560         471 :         for (; !e->empty(); e->popFront()) {
     561             :             // Skip debugger references because NukeCrossCompartmentWrapper()
     562             :             // doesn't know how to nuke them yet, see bug 1084626 for more
     563             :             // information.
     564          37 :             const CrossCompartmentKey& k = e->front().key();
     565          37 :             if (!k.is<JSObject*>())
     566           3 :                 continue;
     567             : 
     568          71 :             AutoWrapperRooter wobj(cx, WrapperValue(*e));
     569             : 
     570             :             // Unwrap from the wrapped object in CrossCompartmentKey instead of
     571             :             // the wrapper, this could save us a bit of time.
     572          37 :             JSObject* wrapped = UncheckedUnwrap(k.as<JSObject*>());
     573             : 
     574             :             // We never nuke script source objects, since only ever used internally by the JS
     575             :             // engine, and are expected to remain valid throughout a scripts lifetime.
     576          37 :             if (MOZ_UNLIKELY(wrapped->is<ScriptSourceObject>())) {
     577           0 :                 continue;
     578             :             }
     579             : 
     580             :             // We only skip nuking window references that point to a target
     581             :             // compartment, not the ones that belong to it.
     582          71 :             if (nukeReferencesToWindow == DontNukeWindowReferences &&
     583          71 :                 MOZ_LIKELY(!nukeAll) && IsWindowProxy(wrapped))
     584             :             {
     585           3 :                 continue;
     586             :             }
     587             : 
     588             :             // Now this is the wrapper we want to nuke.
     589          34 :             e->removeFront();
     590          34 :             NukeRemovedCrossCompartmentWrapper(cx, wobj);
     591             :         }
     592             :     }
     593             : 
     594           6 :     return true;
     595             : }
     596             : 
     597             : // Given a cross-compartment wrapper |wobj|, update it to point to
     598             : // |newTarget|. This recomputes the wrapper with JS_WrapValue, and thus can be
     599             : // useful even if wrapper already points to newTarget.
     600             : // This operation crashes on failure rather than leaving the heap in an
     601             : // inconsistent state.
     602             : void
     603          10 : js::RemapWrapper(JSContext* cx, JSObject* wobjArg, JSObject* newTargetArg)
     604             : {
     605          10 :     MOZ_ASSERT(!IsInsideNursery(wobjArg));
     606          10 :     MOZ_ASSERT(!IsInsideNursery(newTargetArg));
     607             : 
     608          20 :     RootedObject wobj(cx, wobjArg);
     609          20 :     RootedObject newTarget(cx, newTargetArg);
     610          10 :     MOZ_ASSERT(wobj->is<CrossCompartmentWrapperObject>());
     611          10 :     MOZ_ASSERT(!newTarget->is<CrossCompartmentWrapperObject>());
     612          10 :     JSObject* origTarget = Wrapper::wrappedObject(wobj);
     613          10 :     MOZ_ASSERT(origTarget);
     614          10 :     Value origv = ObjectValue(*origTarget);
     615          10 :     JSCompartment* wcompartment = wobj->compartment();
     616             : 
     617          20 :     AutoDisableProxyCheck adpc;
     618             : 
     619             :     // If we're mapping to a different target (as opposed to just recomputing
     620             :     // for the same target), we must not have an existing wrapper for the new
     621             :     // target, otherwise this will break.
     622          10 :     MOZ_ASSERT_IF(origTarget != newTarget,
     623             :                   !wcompartment->lookupWrapper(ObjectValue(*newTarget)));
     624             : 
     625             :     // The old value should still be in the cross-compartment wrapper map, and
     626             :     // the lookup should return wobj.
     627          10 :     WrapperMap::Ptr p = wcompartment->lookupWrapper(origv);
     628          10 :     MOZ_ASSERT(&p->value().unsafeGet()->toObject() == wobj);
     629          10 :     wcompartment->removeWrapper(p);
     630             : 
     631             :     // When we remove origv from the wrapper map, its wrapper, wobj, must
     632             :     // immediately cease to be a cross-compartment wrapper. Nuke it.
     633          10 :     NukeCrossCompartmentWrapper(cx, wobj);
     634             : 
     635             :     // First, we wrap it in the new compartment. We try to use the existing
     636             :     // wrapper, |wobj|, since it's been nuked anyway. The wrap() function has
     637             :     // the choice to reuse |wobj| or not.
     638          20 :     RootedObject tobj(cx, newTarget);
     639          20 :     AutoCompartmentUnchecked ac(cx, wcompartment);
     640          10 :     if (!wcompartment->rewrap(cx, &tobj, wobj))
     641           0 :         MOZ_CRASH();
     642             : 
     643             :     // If wrap() reused |wobj|, it will have overwritten it and returned with
     644             :     // |tobj == wobj|. Otherwise, |tobj| will point to a new wrapper and |wobj|
     645             :     // will still be nuked. In the latter case, we replace |wobj| with the
     646             :     // contents of the new wrapper in |tobj|.
     647          10 :     if (tobj != wobj) {
     648             :         // Now, because we need to maintain object identity, we do a brain
     649             :         // transplant on the old object so that it contains the contents of the
     650             :         // new one.
     651           0 :         if (!JSObject::swap(cx, wobj, tobj))
     652           0 :             MOZ_CRASH();
     653             :     }
     654             : 
     655             :     // Before swapping, this wrapper came out of wrap(), which enforces the
     656             :     // invariant that the wrapper in the map points directly to the key.
     657          10 :     MOZ_ASSERT(Wrapper::wrappedObject(wobj) == newTarget);
     658             : 
     659             :     // Update the entry in the compartment's wrapper map to point to the old
     660             :     // wrapper, which has now been updated (via reuse or swap).
     661          10 :     MOZ_ASSERT(wobj->is<WrapperObject>());
     662          10 :     if (!wcompartment->putWrapper(cx, CrossCompartmentKey(newTarget), ObjectValue(*wobj)))
     663           0 :         MOZ_CRASH();
     664          10 : }
     665             : 
     666             : // Remap all cross-compartment wrappers pointing to |oldTarget| to point to
     667             : // |newTarget|. All wrappers are recomputed.
     668             : JS_FRIEND_API(bool)
     669           3 : js::RemapAllWrappersForObject(JSContext* cx, JSObject* oldTargetArg,
     670             :                               JSObject* newTargetArg)
     671             : {
     672           3 :     MOZ_ASSERT(!IsInsideNursery(oldTargetArg));
     673           3 :     MOZ_ASSERT(!IsInsideNursery(newTargetArg));
     674             : 
     675           6 :     RootedValue origv(cx, ObjectValue(*oldTargetArg));
     676           6 :     RootedObject newTarget(cx, newTargetArg);
     677             : 
     678           6 :     AutoWrapperVector toTransplant(cx);
     679           3 :     if (!toTransplant.reserve(cx->runtime()->numCompartments))
     680           0 :         return false;
     681             : 
     682         329 :     for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) {
     683         326 :         if (WrapperMap::Ptr wp = c->lookupWrapper(origv)) {
     684             :             // We found a wrapper. Remember and root it.
     685          10 :             toTransplant.infallibleAppend(WrapperValue(wp));
     686             :         }
     687             :     }
     688             : 
     689          13 :     for (const WrapperValue& v : toTransplant)
     690          10 :         RemapWrapper(cx, &v.toObject(), newTarget);
     691             : 
     692           3 :     return true;
     693             : }
     694             : 
     695             : JS_FRIEND_API(bool)
     696           3 : js::RecomputeWrappers(JSContext* cx, const CompartmentFilter& sourceFilter,
     697             :                       const CompartmentFilter& targetFilter)
     698             : {
     699           3 :     bool evictedNursery = false;
     700             : 
     701           6 :     AutoWrapperVector toRecompute(cx);
     702         499 :     for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) {
     703             :         // Filter by source compartment.
     704         496 :         if (!sourceFilter.match(c))
     705         480 :             continue;
     706             : 
     707          16 :         if (!evictedNursery && c->hasNurseryAllocatedWrapperEntries(targetFilter)) {
     708           0 :             EvictAllNurseries(cx->runtime());
     709           0 :             evictedNursery = true;
     710             :         }
     711             : 
     712             :         // Iterate over the wrappers, filtering appropriately.
     713          16 :         for (JSCompartment::NonStringWrapperEnum e(c, targetFilter); !e.empty(); e.popFront()) {
     714             :             // Filter out non-objects.
     715           0 :             CrossCompartmentKey& k = e.front().mutableKey();
     716           0 :             if (!k.is<JSObject*>())
     717           0 :                 continue;
     718             : 
     719             :             // Add it to the list.
     720           0 :             if (!toRecompute.append(WrapperValue(e)))
     721           0 :                 return false;
     722             :         }
     723             :     }
     724             : 
     725             :     // Recompute all the wrappers in the list.
     726           3 :     for (const WrapperValue& v : toRecompute) {
     727           0 :         JSObject* wrapper = &v.toObject();
     728           0 :         JSObject* wrapped = Wrapper::wrappedObject(wrapper);
     729           0 :         RemapWrapper(cx, wrapper, wrapped);
     730             :     }
     731             : 
     732           3 :     return true;
     733             : }

Generated by: LCOV version 1.13