tor-browser

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

WrapperFactory.cpp (32449B)


      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 "WaiveXrayWrapper.h"
      8 #include "FilteringWrapper.h"
      9 #include "XrayWrapper.h"
     10 #include "AccessCheck.h"
     11 #include "XPCWrapper.h"
     12 #include "ChromeObjectWrapper.h"
     13 #include "WrapperFactory.h"
     14 
     15 #include "xpcprivate.h"
     16 #include "XPCMaps.h"
     17 #include "mozilla/dom/BindingUtils.h"
     18 #include "jsfriendapi.h"
     19 #include "js/friend/WindowProxy.h"  // js::IsWindow, js::IsWindowProxy
     20 #include "js/Object.h"              // JS::GetPrivate, JS::GetCompartment
     21 #include "mozilla/dom/ScriptSettings.h"
     22 #include "mozilla/dom/MaybeCrossOriginObject.h"
     23 #include "nsContentUtils.h"
     24 #include "nsGlobalWindowInner.h"
     25 #include "nsXULAppAPI.h"
     26 
     27 using namespace JS;
     28 using namespace js;
     29 using namespace mozilla;
     30 
     31 namespace xpc {
     32 
     33 #ifndef MOZ_UNIFIED_BUILD
     34 extern template class FilteringWrapper<js::CrossCompartmentSecurityWrapper,
     35                                       Opaque>;
     36 extern template class FilteringWrapper<js::CrossCompartmentSecurityWrapper,
     37                                       OpaqueWithCall>;
     38 #endif
     39 
     40 // When chrome pulls a naked property across the membrane using
     41 // .wrappedJSObject, we want it to cross the membrane into the
     42 // chrome compartment without automatically being wrapped into an
     43 // X-ray wrapper. We achieve this by wrapping it into a special
     44 // transparent wrapper in the origin (non-chrome) compartment. When
     45 // an object with that special wrapper applied crosses into chrome,
     46 // we know to not apply an X-ray wrapper.
     47 const Wrapper XrayWaiver(WrapperFactory::WAIVE_XRAY_WRAPPER_FLAG);
     48 
     49 // When objects for which we waived the X-ray wrapper cross into
     50 // chrome, we wrap them into a special cross-compartment wrapper
     51 // that transitively extends the waiver to all properties we get
     52 // off it.
     53 const WaiveXrayWrapper WaiveXrayWrapper::singleton(0);
     54 
     55 bool WrapperFactory::IsOpaqueWrapper(JSObject* obj) {
     56  return IsWrapper(obj) &&
     57         Wrapper::wrapperHandler(obj) == &PermissiveXrayOpaque::singleton;
     58 }
     59 
     60 bool WrapperFactory::IsCOW(JSObject* obj) {
     61  return IsWrapper(obj) &&
     62         Wrapper::wrapperHandler(obj) == &ChromeObjectWrapper::singleton;
     63 }
     64 
     65 JSObject* WrapperFactory::GetXrayWaiver(HandleObject obj) {
     66  // Object should come fully unwrapped but outerized.
     67  MOZ_ASSERT(obj == UncheckedUnwrap(obj));
     68  MOZ_ASSERT(!js::IsWindow(obj));
     69  XPCWrappedNativeScope* scope = ObjectScope(obj);
     70  MOZ_ASSERT(scope);
     71 
     72  if (!scope->mWaiverWrapperMap) {
     73    return nullptr;
     74  }
     75 
     76  return scope->mWaiverWrapperMap->Find(obj);
     77 }
     78 
     79 JSObject* WrapperFactory::CreateXrayWaiver(JSContext* cx, HandleObject obj,
     80                                           bool allowExisting) {
     81  // The caller is required to have already done a lookup, unless it's
     82  // trying to replace an existing waiver.
     83  // NB: This implictly performs the assertions of GetXrayWaiver.
     84  MOZ_ASSERT(bool(GetXrayWaiver(obj)) == allowExisting);
     85  XPCWrappedNativeScope* scope = ObjectScope(obj);
     86 
     87  JSAutoRealm ar(cx, obj);
     88  JSObject* waiver = Wrapper::New(cx, obj, &XrayWaiver);
     89  if (!waiver) {
     90    return nullptr;
     91  }
     92 
     93  // Add the new waiver to the map. It's important that we only ever have
     94  // one waiver for the lifetime of the target object.
     95  if (!scope->mWaiverWrapperMap) {
     96    scope->mWaiverWrapperMap = mozilla::MakeUnique<JSObject2JSObjectMap>();
     97  }
     98  if (!scope->mWaiverWrapperMap->Add(cx, obj, waiver)) {
     99    return nullptr;
    100  }
    101  return waiver;
    102 }
    103 
    104 JSObject* WrapperFactory::WaiveXray(JSContext* cx, JSObject* objArg) {
    105  RootedObject obj(cx, objArg);
    106  obj = UncheckedUnwrap(obj);
    107  MOZ_ASSERT(!js::IsWindow(obj));
    108 
    109  JSObject* waiver = GetXrayWaiver(obj);
    110  if (!waiver) {
    111    waiver = CreateXrayWaiver(cx, obj);
    112  }
    113  JS::AssertObjectIsNotGray(waiver);
    114  return waiver;
    115 }
    116 
    117 /* static */
    118 bool WrapperFactory::AllowWaiver(JS::Compartment* target,
    119                                 JS::Compartment* origin) {
    120  return CompartmentPrivate::Get(target)->allowWaivers &&
    121         CompartmentOriginInfo::Subsumes(target, origin);
    122 }
    123 
    124 /* static */
    125 bool WrapperFactory::AllowWaiver(JSObject* wrapper) {
    126  MOZ_ASSERT(js::IsCrossCompartmentWrapper(wrapper));
    127  return AllowWaiver(JS::GetCompartment(wrapper),
    128                     JS::GetCompartment(js::UncheckedUnwrap(wrapper)));
    129 }
    130 
    131 inline bool ShouldWaiveXray(JSContext* cx, JSObject* originalObj) {
    132  unsigned flags;
    133  (void)js::UncheckedUnwrap(originalObj, /* stopAtWindowProxy = */ true,
    134                            &flags);
    135 
    136  // If the original object did not point through an Xray waiver, we're done.
    137  if (!(flags & WrapperFactory::WAIVE_XRAY_WRAPPER_FLAG)) {
    138    return false;
    139  }
    140 
    141  // If the original object was not a cross-compartment wrapper, that means
    142  // that the caller explicitly created a waiver. Preserve it so that things
    143  // like WaiveXrayAndWrap work.
    144  if (!(flags & Wrapper::CROSS_COMPARTMENT)) {
    145    return true;
    146  }
    147 
    148  // Otherwise, this is a case of explicitly passing a wrapper across a
    149  // compartment boundary. In that case, we only want to preserve waivers
    150  // in transactions between same-origin compartments.
    151  JS::Compartment* oldCompartment = JS::GetCompartment(originalObj);
    152  JS::Compartment* newCompartment = js::GetContextCompartment(cx);
    153  bool sameOrigin = false;
    154  if (OriginAttributes::IsRestrictOpenerAccessForFPI()) {
    155    sameOrigin =
    156        CompartmentOriginInfo::Subsumes(oldCompartment, newCompartment) &&
    157        CompartmentOriginInfo::Subsumes(newCompartment, oldCompartment);
    158  } else {
    159    sameOrigin = CompartmentOriginInfo::SubsumesIgnoringFPD(oldCompartment,
    160                                                            newCompartment) &&
    161                 CompartmentOriginInfo::SubsumesIgnoringFPD(newCompartment,
    162                                                            oldCompartment);
    163  }
    164  return sameOrigin;
    165 }
    166 
    167 // Special handling is needed when wrapping local and remote window proxies.
    168 // This function returns true if it found a window proxy and dealt with it.
    169 static bool MaybeWrapWindowProxy(JSContext* cx, HandleObject origObj,
    170                                 HandleObject obj, MutableHandleObject retObj) {
    171  bool isWindowProxy = js::IsWindowProxy(obj);
    172 
    173  if (!isWindowProxy &&
    174      !dom::IsRemoteObjectProxy(obj, dom::prototypes::id::Window)) {
    175    return false;
    176  }
    177 
    178  dom::BrowsingContext* bc = nullptr;
    179  if (isWindowProxy) {
    180    nsGlobalWindowInner* win =
    181        WindowOrNull(js::UncheckedUnwrap(obj, /* stopAtWindowProxy = */ false));
    182    if (win && win->GetOuterWindow()) {
    183      bc = win->GetOuterWindow()->GetBrowsingContext();
    184    }
    185    if (!bc) {
    186      retObj.set(obj);
    187      return true;
    188    }
    189  } else {
    190    bc = dom::GetBrowsingContext(obj);
    191    MOZ_ASSERT(bc);
    192  }
    193 
    194  // We should only have a remote window proxy if bc is in a state where we
    195  // expect remote window proxies. Otherwise, they should have been cleaned up
    196  // by a call to CleanUpDanglingRemoteOuterWindowProxies().
    197  MOZ_RELEASE_ASSERT(isWindowProxy || bc->CanHaveRemoteOuterProxies());
    198 
    199  if (bc->IsInProcess()) {
    200    retObj.set(obj);
    201  } else {
    202    // If bc is not in process, then use a remote window proxy, whether or not
    203    // obj is one already.
    204    if (!dom::GetRemoteOuterWindowProxy(cx, bc, origObj, retObj)) {
    205      MOZ_CRASH("GetRemoteOuterWindowProxy failed");
    206    }
    207  }
    208 
    209  return true;
    210 }
    211 
    212 void WrapperFactory::PrepareForWrapping(JSContext* cx, HandleObject scope,
    213                                        HandleObject origObj,
    214                                        HandleObject objArg,
    215                                        HandleObject objectPassedToWrap,
    216                                        MutableHandleObject retObj) {
    217  // The JS engine calls ToWindowProxyIfWindow and deals with dead wrappers.
    218  MOZ_ASSERT(!js::IsWindow(objArg));
    219  MOZ_ASSERT(!JS_IsDeadWrapper(objArg));
    220 
    221  bool waive = ShouldWaiveXray(cx, objectPassedToWrap);
    222  RootedObject obj(cx, objArg);
    223  retObj.set(nullptr);
    224 
    225  // There are a few cases related to window proxies that are handled first to
    226  // allow us to assert against wrappers below.
    227  if (MaybeWrapWindowProxy(cx, origObj, obj, retObj)) {
    228    if (waive) {
    229      // We don't put remote window proxies in a waiving wrapper.
    230      MOZ_ASSERT(js::IsWindowProxy(obj));
    231      retObj.set(WaiveXray(cx, retObj));
    232    }
    233    return;
    234  }
    235 
    236  // Here are the rules for wrapping:
    237  // We should never get a proxy here (the JS engine unwraps those for us).
    238  MOZ_ASSERT(!IsWrapper(obj));
    239 
    240  // Now, our object is ready to be wrapped, but several objects (notably
    241  // nsJSIIDs) have a wrapper per scope. If we are about to wrap one of
    242  // those objects in a security wrapper, then we need to hand back the
    243  // wrapper for the new scope instead. Also, global objects don't move
    244  // between scopes so for those we also want to return the wrapper. So...
    245  if (!IsWrappedNativeReflector(obj) || JS_IsGlobalObject(obj)) {
    246    retObj.set(waive ? WaiveXray(cx, obj) : obj);
    247    return;
    248  }
    249 
    250  XPCWrappedNative* wn = XPCWrappedNative::Get(obj);
    251 
    252  JSAutoRealm ar(cx, obj);
    253  XPCCallContext ccx(cx, obj);
    254  RootedObject wrapScope(cx, scope);
    255 
    256  if (ccx.GetScriptable() && ccx.GetScriptable()->WantPreCreate()) {
    257    // We have a precreate hook. This object might enforce that we only
    258    // ever create JS object for it.
    259 
    260    // Note: this penalizes objects that only have one wrapper, but are
    261    // being accessed across compartments. We would really prefer to
    262    // replace the above code with a test that says "do you only have one
    263    // wrapper?"
    264    nsresult rv = wn->GetScriptable()->PreCreate(wn->Native(), cx, scope,
    265                                                 wrapScope.address());
    266    if (NS_FAILED(rv)) {
    267      retObj.set(waive ? WaiveXray(cx, obj) : obj);
    268      return;
    269    }
    270 
    271    // If the handed back scope differs from the passed-in scope and is in
    272    // a separate compartment, then this object is explicitly requesting
    273    // that we don't create a second JS object for it: create a security
    274    // wrapper.
    275    //
    276    // Note: The only two objects that still use PreCreate are SystemGlobal
    277    // and Components, both of which unconditionally request their canonical
    278    // scope. Since SpiderMonkey only invokes the prewrap callback in
    279    // situations where the object is nominally cross-compartment, we should
    280    // always get a different scope here.
    281    MOZ_RELEASE_ASSERT(JS::GetCompartment(scope) !=
    282                       JS::GetCompartment(wrapScope));
    283    retObj.set(waive ? WaiveXray(cx, obj) : obj);
    284    return;
    285  }
    286 
    287  // This public WrapNativeToJSVal API enters the compartment of 'wrapScope'
    288  // so we don't have to.
    289  RootedValue v(cx);
    290  nsresult rv = nsXPConnect::XPConnect()->WrapNativeToJSVal(
    291      cx, wrapScope, wn->Native(), nullptr, &NS_GET_IID(nsISupports), false,
    292      &v);
    293  if (NS_FAILED(rv)) {
    294    return;
    295  }
    296 
    297  obj.set(&v.toObject());
    298  MOZ_ASSERT(IsWrappedNativeReflector(obj), "bad object");
    299  JS::AssertObjectIsNotGray(obj);  // We should never return gray reflectors.
    300 
    301  // Because the underlying native didn't have a PreCreate hook, we had
    302  // to a new (or possibly pre-existing) XPCWN in our compartment.
    303  // This could be a problem for chrome code that passes XPCOM objects
    304  // across compartments, because the effects of QI would disappear across
    305  // compartments.
    306  //
    307  // So whenever we pull an XPCWN across compartments in this manner, we
    308  // give the destination object the union of the two native sets. We try
    309  // to do this cleverly in the common case to avoid too much overhead.
    310  XPCWrappedNative* newwn = XPCWrappedNative::Get(obj);
    311  RefPtr<XPCNativeSet> unionSet =
    312      XPCNativeSet::GetNewOrUsed(cx, newwn->GetSet(), wn->GetSet(), false);
    313  if (!unionSet) {
    314    return;
    315  }
    316  newwn->SetSet(unionSet.forget());
    317 
    318  retObj.set(waive ? WaiveXray(cx, obj) : obj);
    319 }
    320 
    321 // This check is completely symmetric, so we don't need to keep track of origin
    322 // vs target here.  Two compartments may have had transparent CCWs between them
    323 // only if they are same-origin (ignoring document.domain) or have both had
    324 // document.domain set at some point and are same-site.  In either case they
    325 // will have the same SiteIdentifier, so check that first.
    326 static bool CompartmentsMayHaveHadTransparentCCWs(
    327    CompartmentPrivate* private1, CompartmentPrivate* private2) {
    328  auto& info1 = private1->originInfo;
    329  auto& info2 = private2->originInfo;
    330 
    331  if (!info1.SiteRef().Equals(info2.SiteRef())) {
    332    return false;
    333  }
    334 
    335  return info1.GetPrincipalIgnoringDocumentDomain()->FastEquals(
    336             info2.GetPrincipalIgnoringDocumentDomain()) ||
    337         (info1.HasChangedDocumentDomain() && info2.HasChangedDocumentDomain());
    338 }
    339 
    340 #ifdef DEBUG
    341 static void DEBUG_CheckUnwrapSafety(HandleObject obj,
    342                                    const js::Wrapper* handler,
    343                                    JS::Realm* origin, JS::Realm* target) {
    344  JS::Compartment* targetCompartment = JS::GetCompartmentForRealm(target);
    345  if (!js::AllowNewWrapper(targetCompartment, obj)) {
    346    // The JS engine should have returned a dead wrapper in this case and we
    347    // shouldn't even get here.
    348    MOZ_ASSERT_UNREACHABLE("CheckUnwrapSafety called for a dead wrapper");
    349  } else if (AccessCheck::isChrome(targetCompartment)) {
    350    // If the caller is chrome (or effectively so), unwrap should always be
    351    // allowed, but we might have a CrossOriginObjectWrapper here which allows
    352    // it dynamically.
    353    MOZ_ASSERT(!handler->hasSecurityPolicy() ||
    354               handler == &CrossOriginObjectWrapper::singleton);
    355  } else {
    356    // Otherwise, it should depend on whether the target subsumes the origin.
    357    bool subsumes =
    358        (OriginAttributes::IsRestrictOpenerAccessForFPI()
    359             ? AccessCheck::subsumesConsideringDomain(target, origin)
    360             : AccessCheck::subsumesConsideringDomainIgnoringFPD(target,
    361                                                                 origin));
    362    if (!subsumes) {
    363      // If the target (which is where the wrapper lives) does not subsume the
    364      // origin (which is where the wrapped object lives), then we should
    365      // generally have a security check on the wrapper here.  There is one
    366      // exception, though: things that used to be same-origin and then stopped
    367      // due to document.domain changes.  In that case we will have a
    368      // transparent cross-compartment wrapper here even though "subsumes" is no
    369      // longer true.
    370      CompartmentPrivate* originCompartmentPrivate =
    371          CompartmentPrivate::Get(origin);
    372      CompartmentPrivate* targetCompartmentPrivate =
    373          CompartmentPrivate::Get(target);
    374      if (!originCompartmentPrivate->wantXrays &&
    375          !targetCompartmentPrivate->wantXrays &&
    376          CompartmentsMayHaveHadTransparentCCWs(originCompartmentPrivate,
    377                                                targetCompartmentPrivate)) {
    378        // We should have a transparent CCW, unless we have a cross-origin
    379        // object, in which case it will be a CrossOriginObjectWrapper.
    380        MOZ_ASSERT(handler == &CrossCompartmentWrapper::singleton ||
    381                   handler == &CrossOriginObjectWrapper::singleton);
    382      } else {
    383        MOZ_ASSERT(handler->hasSecurityPolicy());
    384      }
    385    } else {
    386      // Even if target subsumes origin, we might have a wrapper with a security
    387      // policy here, if it happens to be a CrossOriginObjectWrapper.
    388      MOZ_ASSERT(!handler->hasSecurityPolicy() ||
    389                 handler == &CrossOriginObjectWrapper::singleton);
    390    }
    391  }
    392 }
    393 #else
    394 #  define DEBUG_CheckUnwrapSafety(obj, handler, origin, target) \
    395    {                                                           \
    396    }
    397 #endif
    398 
    399 const CrossOriginObjectWrapper CrossOriginObjectWrapper::singleton;
    400 
    401 bool CrossOriginObjectWrapper::dynamicCheckedUnwrapAllowed(
    402    HandleObject obj, JSContext* cx) const {
    403  MOZ_ASSERT(js::GetProxyHandler(obj) == this,
    404             "Why are we getting called for some random object?");
    405  JSObject* target = wrappedObject(obj);
    406  return dom::MaybeCrossOriginObjectMixins::IsPlatformObjectSameOrigin(cx,
    407                                                                       target);
    408 }
    409 
    410 static const Wrapper* SelectWrapper(bool securityWrapper, XrayType xrayType,
    411                                    bool waiveXrays, JSObject* obj) {
    412  // Waived Xray uses a modified CCW that has transparent behavior but
    413  // transitively waives Xrays on arguments.
    414  if (waiveXrays) {
    415    MOZ_ASSERT(!securityWrapper);
    416    return &WaiveXrayWrapper::singleton;
    417  }
    418 
    419  // If we don't want or can't use Xrays, select a wrapper that's either
    420  // entirely transparent or entirely opaque.
    421  if (xrayType == NotXray) {
    422    if (!securityWrapper) {
    423      return &CrossCompartmentWrapper::singleton;
    424    }
    425    return &FilteringWrapper<CrossCompartmentSecurityWrapper,
    426                             Opaque>::singleton;
    427  }
    428 
    429  // Ok, we're using Xray. If this isn't a security wrapper, use the permissive
    430  // version and skip the filter.
    431  if (!securityWrapper) {
    432    if (xrayType == XrayForDOMObject) {
    433      return &PermissiveXrayDOM::singleton;
    434    } else if (xrayType == XrayForJSObject) {
    435      return &PermissiveXrayJS::singleton;
    436    }
    437    MOZ_ASSERT(xrayType == XrayForOpaqueObject);
    438    return &PermissiveXrayOpaque::singleton;
    439  }
    440 
    441  // There's never any reason to expose other objects to non-subsuming actors.
    442  // Just use an opaque wrapper in these cases.
    443  return &FilteringWrapper<CrossCompartmentSecurityWrapper, Opaque>::singleton;
    444 }
    445 
    446 JSObject* WrapperFactory::Rewrap(JSContext* cx, HandleObject existing,
    447                                 HandleObject obj) {
    448  MOZ_ASSERT(!IsWrapper(obj) || GetProxyHandler(obj) == &XrayWaiver ||
    449                 js::IsWindowProxy(obj),
    450             "wrapped object passed to rewrap");
    451  MOZ_ASSERT(!js::IsWindow(obj));
    452  MOZ_ASSERT(dom::IsJSAPIActive());
    453 
    454  // Compute the information we need to select the right wrapper.
    455  JS::Realm* origin = js::GetNonCCWObjectRealm(obj);
    456  JS::Realm* target = js::GetContextRealm(cx);
    457  MOZ_ASSERT(target, "Why is our JSContext not in a Realm?");
    458  bool originIsChrome = AccessCheck::isChrome(origin);
    459  bool targetIsChrome = AccessCheck::isChrome(target);
    460  bool originSubsumesTarget =
    461      OriginAttributes::IsRestrictOpenerAccessForFPI()
    462          ? AccessCheck::subsumesConsideringDomain(origin, target)
    463          : AccessCheck::subsumesConsideringDomainIgnoringFPD(origin, target);
    464  bool targetSubsumesOrigin =
    465      OriginAttributes::IsRestrictOpenerAccessForFPI()
    466          ? AccessCheck::subsumesConsideringDomain(target, origin)
    467          : AccessCheck::subsumesConsideringDomainIgnoringFPD(target, origin);
    468  bool sameOrigin = targetSubsumesOrigin && originSubsumesTarget;
    469 
    470  const Wrapper* wrapper;
    471 
    472  CompartmentPrivate* originCompartmentPrivate =
    473      CompartmentPrivate::Get(origin);
    474  CompartmentPrivate* targetCompartmentPrivate =
    475      CompartmentPrivate::Get(target);
    476 
    477  // Track whether we decided to use a transparent wrapper because of
    478  // document.domain usage, so we don't override that decision.
    479  bool isTransparentWrapperDueToDocumentDomain = false;
    480 
    481  //
    482  // First, handle the special cases.
    483  //
    484 
    485  // Special handling for chrome objects being exposed to content.
    486  if (originIsChrome && !targetIsChrome) {
    487    // If this is a chrome function being exposed to content, we need to allow
    488    // call (but nothing else).
    489    JSProtoKey key = IdentifyStandardInstance(obj);
    490    if (key == JSProto_Function || key == JSProto_BoundFunction) {
    491      wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper,
    492                                  OpaqueWithCall>::singleton;
    493    }
    494 
    495    // For vanilla JSObjects exposed from chrome to content, we use a wrapper
    496    // that fails silently in a few cases. We'd like to get rid of this
    497    // eventually, but in their current form they don't cause much trouble.
    498    else if (key == JSProto_Object) {
    499      wrapper = &ChromeObjectWrapper::singleton;
    500    }
    501 
    502    // Otherwise we get an opaque wrapper.
    503    else {
    504      wrapper =
    505          &FilteringWrapper<CrossCompartmentSecurityWrapper, Opaque>::singleton;
    506    }
    507  }
    508 
    509  // Special handling for the web's cross-origin objects (WindowProxy and
    510  // Location).  We only need or want to do this in web-like contexts, where all
    511  // security relationships are symmetric and there are no forced Xrays.
    512  else if (originSubsumesTarget == targetSubsumesOrigin &&
    513           // Check for the more rare case of cross-origin objects before doing
    514           // the more-likely-to-pass checks for wantXrays.
    515           IsCrossOriginAccessibleObject(obj) &&
    516           (!targetSubsumesOrigin || (!originCompartmentPrivate->wantXrays &&
    517                                      !targetCompartmentPrivate->wantXrays))) {
    518    wrapper = &CrossOriginObjectWrapper::singleton;
    519  }
    520 
    521  // Special handling for other web objects.  Again, we only want this in
    522  // web-like contexts (symmetric security relationships, no forced Xrays).  In
    523  // this situation, if the two compartments may ever have had transparent CCWs
    524  // between them, we want to keep using transparent CCWs.
    525  else if (originSubsumesTarget == targetSubsumesOrigin &&
    526           !originCompartmentPrivate->wantXrays &&
    527           !targetCompartmentPrivate->wantXrays &&
    528           CompartmentsMayHaveHadTransparentCCWs(originCompartmentPrivate,
    529                                                 targetCompartmentPrivate)) {
    530    isTransparentWrapperDueToDocumentDomain = true;
    531    wrapper = &CrossCompartmentWrapper::singleton;
    532  }
    533 
    534  //
    535  // Now, handle the regular cases.
    536  //
    537  // These are wrappers we can compute using a rule-based approach. In order
    538  // to do so, we need to compute some parameters.
    539  //
    540  else {
    541    // The wrapper is a security wrapper (protecting the wrappee) if and
    542    // only if the target does not subsume the origin.
    543    bool securityWrapper = !targetSubsumesOrigin;
    544 
    545    // Xrays are warranted if either the target or the origin don't trust
    546    // each other. This is generally the case, unless the two are same-origin
    547    // and the caller has not requested same-origin Xrays.
    548    //
    549    // Xrays are a bidirectional protection, since it affords clarity to the
    550    // caller and privacy to the callee.
    551    bool sameOriginXrays = originCompartmentPrivate->wantXrays ||
    552                           targetCompartmentPrivate->wantXrays;
    553    bool wantXrays = !sameOrigin || sameOriginXrays;
    554 
    555    XrayType xrayType = wantXrays ? GetXrayType(obj) : NotXray;
    556 
    557    // If Xrays are warranted, the caller may waive them for non-security
    558    // wrappers (unless explicitly forbidden from doing so).
    559    bool waiveXrays = wantXrays && !securityWrapper &&
    560                      targetCompartmentPrivate->allowWaivers &&
    561                      HasWaiveXrayFlag(obj);
    562 
    563    wrapper = SelectWrapper(securityWrapper, xrayType, waiveXrays, obj);
    564  }
    565 
    566  if (!targetSubsumesOrigin && !isTransparentWrapperDueToDocumentDomain) {
    567    // Do a belt-and-suspenders check against exposing eval()/Function() to
    568    // non-subsuming content.
    569    if (JSFunction* fun = JS_GetObjectFunction(obj)) {
    570      if (JS_IsBuiltinEvalFunction(fun) ||
    571          JS_IsBuiltinFunctionConstructor(fun)) {
    572        NS_WARNING(
    573            "Trying to expose eval or Function to non-subsuming content!");
    574        wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper,
    575                                    Opaque>::singleton;
    576      }
    577    }
    578  }
    579 
    580  DEBUG_CheckUnwrapSafety(obj, wrapper, origin, target);
    581 
    582  if (existing) {
    583    return Wrapper::Renew(existing, obj, wrapper);
    584  }
    585 
    586  return Wrapper::New(cx, obj, wrapper);
    587 }
    588 
    589 // Call WaiveXrayAndWrap when you have a JS object that you don't want to be
    590 // wrapped in an Xray wrapper. cx->compartment is the compartment that will be
    591 // using the returned object. If the object to be wrapped is already in the
    592 // correct compartment, then this returns the unwrapped object.
    593 bool WrapperFactory::WaiveXrayAndWrap(JSContext* cx, MutableHandleValue vp) {
    594  if (vp.isPrimitive()) {
    595    return JS_WrapValue(cx, vp);
    596  }
    597 
    598  RootedObject obj(cx, &vp.toObject());
    599  if (!WaiveXrayAndWrap(cx, &obj)) {
    600    return false;
    601  }
    602 
    603  vp.setObject(*obj);
    604  return true;
    605 }
    606 
    607 bool WrapperFactory::WaiveXrayAndWrap(JSContext* cx,
    608                                      MutableHandleObject argObj) {
    609  MOZ_ASSERT(argObj);
    610  RootedObject obj(cx, js::UncheckedUnwrap(argObj));
    611  MOZ_ASSERT(!js::IsWindow(obj));
    612  if (js::IsObjectInContextCompartment(obj, cx)) {
    613    argObj.set(obj);
    614    return true;
    615  }
    616 
    617  // Even though waivers have no effect on access by scopes that don't subsume
    618  // the underlying object, good defense-in-depth dictates that we should avoid
    619  // handing out waivers to callers that can't use them. The transitive waiving
    620  // machinery unconditionally calls WaiveXrayAndWrap on return values from
    621  // waived functions, even though the return value might be not be same-origin
    622  // with the function. So if we find ourselves trying to create a waiver for
    623  // |cx|, we should check whether the caller has any business with waivers
    624  // to things in |obj|'s compartment.
    625  JS::Compartment* target = js::GetContextCompartment(cx);
    626  JS::Compartment* origin = JS::GetCompartment(obj);
    627  obj = AllowWaiver(target, origin) ? WaiveXray(cx, obj) : obj;
    628  if (!obj) {
    629    return false;
    630  }
    631 
    632  if (!JS_WrapObject(cx, &obj)) {
    633    return false;
    634  }
    635  argObj.set(obj);
    636  return true;
    637 }
    638 
    639 /*
    640 * Calls to JS_TransplantObject* should go through these helpers here so that
    641 * waivers get fixed up properly.
    642 */
    643 
    644 static bool FixWaiverAfterTransplant(JSContext* cx, HandleObject oldWaiver,
    645                                     HandleObject newobj,
    646                                     bool crossCompartmentTransplant) {
    647  MOZ_ASSERT(Wrapper::wrapperHandler(oldWaiver) == &XrayWaiver);
    648  MOZ_ASSERT(!js::IsCrossCompartmentWrapper(newobj));
    649 
    650  if (crossCompartmentTransplant) {
    651    // If the new compartment has a CCW for oldWaiver, nuke this CCW. This
    652    // prevents confusing RemapAllWrappersForObject: it would call RemapWrapper
    653    // with two same-compartment objects (the CCW and the new waiver).
    654    //
    655    // This can happen when loading a chrome page in a content frame and there
    656    // exists a CCW from the chrome compartment to oldWaiver wrapping the window
    657    // we just transplanted:
    658    //
    659    // Compartment 1  |  Compartment 2
    660    // ----------------------------------------
    661    // CCW1 -----------> oldWaiver --> CCW2 --+
    662    // newWaiver                              |
    663    // WindowProxy <--------------------------+
    664    js::NukeCrossCompartmentWrapperIfExists(cx, JS::GetCompartment(newobj),
    665                                            oldWaiver);
    666  } else {
    667    // We kept the same object identity, so the waiver should be a
    668    // waiver for our object, just in the wrong Realm.
    669    MOZ_ASSERT(newobj == Wrapper::wrappedObject(oldWaiver));
    670  }
    671 
    672  // Create a waiver in the new compartment. We know there's not one already in
    673  // the crossCompartmentTransplant case because we _just_ transplanted, which
    674  // means that |newobj| was either created from scratch, or was previously
    675  // cross-compartment wrapper (which should have no waiver). On the other hand,
    676  // in the !crossCompartmentTransplant case we know one already exists.
    677  // CreateXrayWaiver asserts all this.
    678  RootedObject newWaiver(
    679      cx, WrapperFactory::CreateXrayWaiver(
    680              cx, newobj, /* allowExisting = */ !crossCompartmentTransplant));
    681  if (!newWaiver) {
    682    return false;
    683  }
    684 
    685  if (!crossCompartmentTransplant) {
    686    // CreateXrayWaiver should have updated the map to point to the new waiver.
    687    MOZ_ASSERT(WrapperFactory::GetXrayWaiver(newobj) == newWaiver);
    688  }
    689 
    690  // Update all the cross-compartment references to oldWaiver to point to
    691  // newWaiver.
    692  if (!js::RemapAllWrappersForObject(cx, oldWaiver, newWaiver)) {
    693    return false;
    694  }
    695 
    696  if (crossCompartmentTransplant) {
    697    // There should be no same-compartment references to oldWaiver, and we
    698    // just remapped all cross-compartment references. It's dead, so we can
    699    // remove it from the map.
    700    XPCWrappedNativeScope* scope = ObjectScope(oldWaiver);
    701    JSObject* key = Wrapper::wrappedObject(oldWaiver);
    702    MOZ_ASSERT(scope->mWaiverWrapperMap->Find(key));
    703    scope->mWaiverWrapperMap->Remove(key);
    704  }
    705 
    706  return true;
    707 }
    708 
    709 JSObject* TransplantObject(JSContext* cx, JS::HandleObject origobj,
    710                           JS::HandleObject target) {
    711  RootedObject oldWaiver(cx, WrapperFactory::GetXrayWaiver(origobj));
    712  MOZ_ASSERT_IF(oldWaiver, GetNonCCWObjectRealm(oldWaiver) ==
    713                               GetNonCCWObjectRealm(origobj));
    714  RootedObject newIdentity(cx, JS_TransplantObject(cx, origobj, target));
    715  if (!newIdentity || !oldWaiver) {
    716    return newIdentity;
    717  }
    718 
    719  bool crossCompartmentTransplant = (newIdentity != origobj);
    720  if (!crossCompartmentTransplant) {
    721    // We might still have been transplanted across realms within a single
    722    // compartment.
    723    if (GetNonCCWObjectRealm(oldWaiver) == GetNonCCWObjectRealm(newIdentity)) {
    724      // The old waiver is same-realm with the new object; nothing else to do
    725      // here.
    726      return newIdentity;
    727    }
    728  }
    729 
    730  if (!FixWaiverAfterTransplant(cx, oldWaiver, newIdentity,
    731                                crossCompartmentTransplant)) {
    732    return nullptr;
    733  }
    734  return newIdentity;
    735 }
    736 
    737 JSObject* TransplantObjectRetainingXrayExpandos(JSContext* cx,
    738                                                JS::HandleObject origobj,
    739                                                JS::HandleObject target) {
    740  // Save the chain of objects that carry origobj's Xray expando properties
    741  // (from all compartments). TransplantObject will blow this away; we'll
    742  // restore it manually afterwards.
    743  RootedObject expandoChain(
    744      cx, GetXrayTraits(origobj)->detachExpandoChain(origobj));
    745 
    746  RootedObject newIdentity(cx, TransplantObject(cx, origobj, target));
    747 
    748  // Copy Xray expando properties to the new wrapper.
    749  if (!GetXrayTraits(newIdentity)
    750           ->cloneExpandoChain(cx, newIdentity, expandoChain)) {
    751    // Failure here means some expandos were not copied over. The object graph
    752    // and the Xray machinery are left in a consistent state, but mysteriously
    753    // losing these expandos is too weird to allow.
    754    MOZ_CRASH();
    755  }
    756 
    757  return newIdentity;
    758 }
    759 
    760 static void NukeXrayWaiver(JSContext* cx, JS::HandleObject obj) {
    761  RootedObject waiver(cx, WrapperFactory::GetXrayWaiver(obj));
    762  if (!waiver) {
    763    return;
    764  }
    765 
    766  XPCWrappedNativeScope* scope = ObjectScope(waiver);
    767  JSObject* key = Wrapper::wrappedObject(waiver);
    768  MOZ_ASSERT(scope->mWaiverWrapperMap->Find(key));
    769  scope->mWaiverWrapperMap->Remove(key);
    770 
    771  js::NukeNonCCWProxy(cx, waiver);
    772 
    773  // Get rid of any CCWs the waiver may have had.
    774  if (!JS_RefreshCrossCompartmentWrappers(cx, waiver)) {
    775    MOZ_CRASH();
    776  }
    777 }
    778 
    779 JSObject* TransplantObjectNukingXrayWaiver(JSContext* cx,
    780                                           JS::HandleObject origObj,
    781                                           JS::HandleObject target) {
    782  NukeXrayWaiver(cx, origObj);
    783  return JS_TransplantObject(cx, origObj, target);
    784 }
    785 
    786 nsIGlobalObject* NativeGlobal(JSObject* obj) {
    787  obj = JS::GetNonCCWObjectGlobal(obj);
    788 
    789  // Every global needs to hold a native as its first reserved slot or be a
    790  // WebIDL object with an nsISupports DOM object.
    791  MOZ_ASSERT(JS::GetClass(obj)->slot0IsISupports() ||
    792             dom::UnwrapDOMObjectToISupports(obj));
    793 
    794  nsISupports* native = dom::UnwrapDOMObjectToISupports(obj);
    795  if (!native) {
    796    native = JS::GetObjectISupports<nsISupports>(obj);
    797    MOZ_ASSERT(native);
    798 
    799    // In some cases (like for windows) it is a wrapped native,
    800    // in other cases (sandboxes, system globals) it's just
    801    // a direct pointer to the native. If it's a wrapped native
    802    // let's unwrap it first.
    803    if (nsCOMPtr<nsIXPConnectWrappedNative> wn = do_QueryInterface(native)) {
    804      native = wn->Native();
    805    }
    806  }
    807 
    808  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(native);
    809  MOZ_ASSERT(global,
    810             "Native held by global needs to implement nsIGlobalObject!");
    811 
    812  return global;
    813 }
    814 
    815 nsIGlobalObject* CurrentNativeGlobal(JSContext* cx) {
    816  return xpc::NativeGlobal(JS::CurrentGlobalOrNull(cx));
    817 }
    818 
    819 }  // namespace xpc