1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* vim: set ts=8 sts=4 et sw=4 tw=99: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "WaiveXrayWrapper.h"
8 #include "FilteringWrapper.h"
9 #include "AddonWrapper.h"
10 #include "XrayWrapper.h"
11 #include "AccessCheck.h"
12 #include "XPCWrapper.h"
13 #include "ChromeObjectWrapper.h"
14 #include "WrapperFactory.h"
16 #include "xpcprivate.h"
18 #include "mozilla/dom/BindingUtils.h"
19 #include "jsfriendapi.h"
20 #include "mozilla/Likely.h"
21 #include "nsContentUtils.h"
25 using namespace mozilla
;
29 // When chrome pulls a naked property across the membrane using
30 // .wrappedJSObject, we want it to cross the membrane into the
31 // chrome compartment without automatically being wrapped into an
32 // X-ray wrapper. We achieve this by wrapping it into a special
33 // transparent wrapper in the origin (non-chrome) compartment. When
34 // an object with that special wrapper applied crosses into chrome,
35 // we know to not apply an X-ray wrapper.
36 const Wrapper
XrayWaiver(WrapperFactory::WAIVE_XRAY_WRAPPER_FLAG
);
38 // When objects for which we waived the X-ray wrapper cross into
39 // chrome, we wrap them into a special cross-compartment wrapper
40 // that transitively extends the waiver to all properties we get
42 const WaiveXrayWrapper
WaiveXrayWrapper::singleton(0);
45 WrapperFactory::IsCOW(JSObject
* obj
)
47 return IsWrapper(obj
) &&
48 Wrapper::wrapperHandler(obj
) == &ChromeObjectWrapper::singleton
;
52 WrapperFactory::GetXrayWaiver(HandleObject obj
)
54 // Object should come fully unwrapped but outerized.
55 MOZ_ASSERT(obj
== UncheckedUnwrap(obj
));
56 MOZ_ASSERT(!js::GetObjectClass(obj
)->ext
.outerObject
);
57 XPCWrappedNativeScope
* scope
= ObjectScope(obj
);
60 if (!scope
->mWaiverWrapperMap
)
63 JSObject
* xrayWaiver
= scope
->mWaiverWrapperMap
->Find(obj
);
65 JS::ExposeObjectToActiveJS(xrayWaiver
);
71 WrapperFactory::CreateXrayWaiver(JSContext
* cx
, HandleObject obj
)
73 // The caller is required to have already done a lookup.
74 // NB: This implictly performs the assertions of GetXrayWaiver.
75 MOZ_ASSERT(!GetXrayWaiver(obj
));
76 XPCWrappedNativeScope
* scope
= ObjectScope(obj
);
78 JSAutoCompartment
ac(cx
, obj
);
79 JSObject
* waiver
= Wrapper::New(cx
, obj
,
80 JS_GetGlobalForObject(cx
, obj
),
85 // Add the new waiver to the map. It's important that we only ever have
86 // one waiver for the lifetime of the target object.
87 if (!scope
->mWaiverWrapperMap
) {
88 scope
->mWaiverWrapperMap
=
89 JSObject2JSObjectMap::newMap(XPC_WRAPPER_MAP_LENGTH
);
90 MOZ_ASSERT(scope
->mWaiverWrapperMap
);
92 if (!scope
->mWaiverWrapperMap
->Add(cx
, obj
, waiver
))
98 WrapperFactory::WaiveXray(JSContext
* cx
, JSObject
* objArg
)
100 RootedObject
obj(cx
, objArg
);
101 obj
= UncheckedUnwrap(obj
);
102 MOZ_ASSERT(!js::IsInnerObject(obj
));
104 JSObject
* waiver
= GetXrayWaiver(obj
);
107 return CreateXrayWaiver(cx
, obj
);
110 // In general, we're trying to deprecate COWs incrementally as we introduce
111 // Xrays to the corresponding object types. But switching off COWs for certain
112 // things would be too tumultuous at present, so we punt on them for later.
114 ForceCOWBehavior(JSObject
* obj
)
116 JSProtoKey key
= IdentifyStandardInstanceOrPrototype(obj
);
117 if (key
== JSProto_Object
|| key
== JSProto_Array
|| key
== JSProto_Function
) {
118 MOZ_ASSERT(GetXrayType(obj
) == XrayForJSObject
,
119 "We should use XrayWrappers for standard ES Object, Array, and Function "
120 "instances modulo this hack");
123 // Proxies get OpaqueXrayTraits, but we still need COWs to them for now to
124 // let the SpecialPowers wrapper work.
125 if (key
== JSProto_Proxy
)
132 ShouldWaiveXray(JSContext
* cx
, JSObject
* originalObj
)
135 (void) js::UncheckedUnwrap(originalObj
, /* stopAtOuter = */ true, &flags
);
137 // If the original object did not point through an Xray waiver, we're done.
138 if (!(flags
& WrapperFactory::WAIVE_XRAY_WRAPPER_FLAG
))
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
))
147 // Otherwise, this is a case of explicitly passing a wrapper across a
148 // compartment boundary. In that case, we only want to preserve waivers
149 // in transactions between same-origin compartments.
150 JSCompartment
* oldCompartment
= js::GetObjectCompartment(originalObj
);
151 JSCompartment
* newCompartment
= js::GetContextCompartment(cx
);
153 AccessCheck::subsumesConsideringDomain(oldCompartment
, newCompartment
) &&
154 AccessCheck::subsumesConsideringDomain(newCompartment
, oldCompartment
);
159 WrapperFactory::PrepareForWrapping(JSContext
* cx
, HandleObject scope
,
160 HandleObject objArg
, HandleObject objectPassedToWrap
)
162 bool waive
= ShouldWaiveXray(cx
, objectPassedToWrap
);
163 RootedObject
obj(cx
, objArg
);
164 // Outerize any raw inner objects at the entry point here, so that we don't
165 // have to worry about them for the rest of the wrapping code.
166 if (js::IsInnerObject(obj
)) {
167 JSAutoCompartment
ac(cx
, obj
);
168 obj
= JS_ObjectToOuterObject(cx
, obj
);
169 NS_ENSURE_TRUE(obj
, nullptr);
170 // The outerization hook wraps, which means that we can end up with a
171 // CCW here if |obj| was a navigated-away-from inner. Strip any CCWs.
172 obj
= js::UncheckedUnwrap(obj
);
173 MOZ_ASSERT(js::IsOuterObject(obj
));
176 // If we've got an outer window, there's nothing special that needs to be
177 // done here, and we can move on to the next phase of wrapping. We handle
178 // this case first to allow us to assert against wrappers below.
179 if (js::IsOuterObject(obj
))
180 return waive
? WaiveXray(cx
, obj
) : obj
;
182 // Here are the rules for wrapping:
183 // We should never get a proxy here (the JS engine unwraps those for us).
184 MOZ_ASSERT(!IsWrapper(obj
));
186 // If the object being wrapped is a prototype for a standard class and the
187 // wrapper does not subsumes the wrappee, use the one from the content
188 // compartment. This is generally safer all-around, and in the COW case this
189 // lets us safely take advantage of things like .forEach() via the
190 // ChromeObjectWrapper machinery.
192 // If the prototype chain of chrome object |obj| looks like this:
194 // obj => foo => bar => chromeWin.StandardClass.prototype
196 // The prototype chain of COW(obj) looks lke this:
198 // COW(obj) => COW(foo) => COW(bar) => contentWin.StandardClass.prototype
200 // NB: We now remap all non-subsuming access of standard prototypes.
202 // NB: We need to ignore domain here so that the security relationship we
203 // compute here can't change over time. See the comment above the other
204 // subsumes call below.
205 bool subsumes
= AccessCheck::subsumes(js::GetContextCompartment(cx
),
206 js::GetObjectCompartment(obj
));
207 XrayType xrayType
= GetXrayType(obj
);
208 if (!subsumes
&& (xrayType
== NotXray
|| ForceCOWBehavior(obj
))) {
209 JSProtoKey key
= JSProto_Null
;
211 JSAutoCompartment
ac(cx
, obj
);
212 key
= IdentifyStandardPrototype(obj
);
214 if (key
!= JSProto_Null
) {
215 RootedObject
homeProto(cx
);
216 if (!JS_GetClassPrototype(cx
, key
, &homeProto
))
218 MOZ_ASSERT(homeProto
);
219 // No need to double-wrap here. We should never have waivers to
225 // Now, our object is ready to be wrapped, but several objects (notably
226 // nsJSIIDs) have a wrapper per scope. If we are about to wrap one of
227 // those objects in a security wrapper, then we need to hand back the
228 // wrapper for the new scope instead. Also, global objects don't move
229 // between scopes so for those we also want to return the wrapper. So...
230 if (!IS_WN_REFLECTOR(obj
) || !js::GetObjectParent(obj
))
231 return waive
? WaiveXray(cx
, obj
) : obj
;
233 XPCWrappedNative
* wn
= XPCWrappedNative::Get(obj
);
235 JSAutoCompartment
ac(cx
, obj
);
236 XPCCallContext
ccx(JS_CALLER
, cx
, obj
);
237 RootedObject
wrapScope(cx
, scope
);
240 if (NATIVE_HAS_FLAG(&ccx
, WantPreCreate
)) {
241 // We have a precreate hook. This object might enforce that we only
242 // ever create JS object for it.
244 // Note: this penalizes objects that only have one wrapper, but are
245 // being accessed across compartments. We would really prefer to
246 // replace the above code with a test that says "do you only have one
248 nsresult rv
= wn
->GetScriptableInfo()->GetCallback()->
249 PreCreate(wn
->Native(), cx
, scope
, wrapScope
.address());
250 NS_ENSURE_SUCCESS(rv
, waive
? WaiveXray(cx
, obj
) : obj
);
252 // If the handed back scope differs from the passed-in scope and is in
253 // a separate compartment, then this object is explicitly requesting
254 // that we don't create a second JS object for it: create a security
256 if (js::GetObjectCompartment(scope
) != js::GetObjectCompartment(wrapScope
))
257 return waive
? WaiveXray(cx
, obj
) : obj
;
259 RootedObject
currentScope(cx
, JS_GetGlobalForObject(cx
, obj
));
260 if (MOZ_UNLIKELY(wrapScope
!= currentScope
)) {
261 // The wrapper claims it wants to be in the new scope, but
262 // currently has a reflection that lives in the old scope. This
263 // can mean one of two things, both of which are rare:
265 // 1 - The object has a PreCreate hook (we checked for it above),
266 // but is deciding to request one-wrapper-per-scope (rather than
267 // one-wrapper-per-native) for some reason. Usually, a PreCreate
268 // hook indicates one-wrapper-per-native. In this case we want to
269 // make a new wrapper in the new scope.
271 // 2 - We're midway through wrapper reparenting. The document has
272 // moved to a new scope, but |wn| hasn't been moved yet, and
273 // we ended up calling JS_WrapObject() on its JS object. In this
274 // case, we want to return the existing wrapper.
276 // So we do a trick: call PreCreate _again_, but say that we're
277 // wrapping for the old scope, rather than the new one. If (1) is
278 // the case, then PreCreate will return the scope we pass to it
279 // (the old scope). If (2) is the case, PreCreate will return the
280 // scope of the document (the new scope).
281 RootedObject
probe(cx
);
282 rv
= wn
->GetScriptableInfo()->GetCallback()->
283 PreCreate(wn
->Native(), cx
, currentScope
, probe
.address());
285 // Check for case (2).
286 if (probe
!= currentScope
) {
287 MOZ_ASSERT(probe
== wrapScope
);
288 return waive
? WaiveXray(cx
, obj
) : obj
;
291 // Ok, must be case (1). Fall through and create a new wrapper.
294 // Nasty hack for late-breaking bug 781476. This will confuse identity checks,
295 // but it's probably better than any of our alternatives.
297 // Note: We have to ignore domain here. The JS engine assumes that, given a
298 // compartment c, if c->wrap(x) returns a cross-compartment wrapper at time t0,
299 // it will also return a cross-compartment wrapper for any time t1 > t0 unless
300 // an explicit transplant is performed. In particular, wrapper recomputation
301 // assumes that recomputing a wrapper will always result in a wrapper.
303 // This doesn't actually pose a security issue, because we'll still compute
304 // the correct (opaque) wrapper for the object below given the security
305 // characteristics of the two compartments.
306 if (!AccessCheck::isChrome(js::GetObjectCompartment(wrapScope
)) &&
307 AccessCheck::subsumes(js::GetObjectCompartment(wrapScope
),
308 js::GetObjectCompartment(obj
)))
310 return waive
? WaiveXray(cx
, obj
) : obj
;
315 // This public WrapNativeToJSVal API enters the compartment of 'wrapScope'
316 // so we don't have to.
319 nsXPConnect::XPConnect()->WrapNativeToJSVal(cx
, wrapScope
, wn
->Native(), nullptr,
320 &NS_GET_IID(nsISupports
), false, &v
);
321 NS_ENSURE_SUCCESS(rv
, nullptr);
323 obj
.set(&v
.toObject());
324 MOZ_ASSERT(IS_WN_REFLECTOR(obj
), "bad object");
326 // Because the underlying native didn't have a PreCreate hook, we had
327 // to a new (or possibly pre-existing) XPCWN in our compartment.
328 // This could be a problem for chrome code that passes XPCOM objects
329 // across compartments, because the effects of QI would disappear across
332 // So whenever we pull an XPCWN across compartments in this manner, we
333 // give the destination object the union of the two native sets. We try
334 // to do this cleverly in the common case to avoid too much overhead.
335 XPCWrappedNative
* newwn
= XPCWrappedNative::Get(obj
);
336 XPCNativeSet
* unionSet
= XPCNativeSet::GetNewOrUsed(newwn
->GetSet(),
337 wn
->GetSet(), false);
340 newwn
->SetSet(unionSet
);
342 return waive
? WaiveXray(cx
, obj
) : obj
;
347 DEBUG_CheckUnwrapSafety(HandleObject obj
, const js::Wrapper
* handler
,
348 JSCompartment
* origin
, JSCompartment
* target
)
350 if (AccessCheck::isChrome(target
) || xpc::IsUniversalXPConnectEnabled(target
)) {
351 // If the caller is chrome (or effectively so), unwrap should always be allowed.
352 MOZ_ASSERT(!handler
->hasSecurityPolicy());
353 } else if (CompartmentPrivate::Get(origin
)->forcePermissiveCOWs
) {
354 // Similarly, if this is a privileged scope that has opted to make itself
355 // accessible to the world (allowed only during automation), unwrap should
357 MOZ_ASSERT(!handler
->hasSecurityPolicy());
359 // Otherwise, it should depend on whether the target subsumes the origin.
360 MOZ_ASSERT(handler
->hasSecurityPolicy() == !AccessCheck::subsumesConsideringDomain(target
, origin
));
364 #define DEBUG_CheckUnwrapSafety(obj, handler, origin, target) {}
367 static const Wrapper
*
368 SelectWrapper(bool securityWrapper
, bool wantXrays
, XrayType xrayType
,
369 bool waiveXrays
, bool originIsXBLScope
)
371 // Waived Xray uses a modified CCW that has transparent behavior but
372 // transitively waives Xrays on arguments.
374 MOZ_ASSERT(!securityWrapper
);
375 return &WaiveXrayWrapper::singleton
;
378 // If we don't want or can't use Xrays, select a wrapper that's either
379 // entirely transparent or entirely opaque.
380 if (!wantXrays
|| xrayType
== NotXray
) {
381 if (!securityWrapper
)
382 return &CrossCompartmentWrapper::singleton
;
383 return &FilteringWrapper
<CrossCompartmentSecurityWrapper
, Opaque
>::singleton
;
386 // Ok, we're using Xray. If this isn't a security wrapper, use the permissive
387 // version and skip the filter.
388 if (!securityWrapper
) {
389 if (xrayType
== XrayForWrappedNative
)
390 return &PermissiveXrayXPCWN::singleton
;
391 else if (xrayType
== XrayForDOMObject
)
392 return &PermissiveXrayDOM::singleton
;
393 else if (xrayType
== XrayForJSObject
)
394 return &PermissiveXrayJS::singleton
;
395 MOZ_ASSERT(xrayType
== XrayForOpaqueObject
);
396 return &PermissiveXrayOpaque::singleton
;
399 // This is a security wrapper. Use the security versions and filter.
400 if (xrayType
== XrayForDOMObject
)
401 return &FilteringWrapper
<CrossOriginXrayWrapper
,
402 CrossOriginAccessiblePropertiesOnly
>::singleton
;
404 // There's never any reason to expose other objects to non-subsuming actors.
405 // Just use an opaque wrapper in these cases.
407 // In general, we don't want opaque function wrappers to be callable.
408 // But in the case of XBL, we rely on content being able to invoke
409 // functions exposed from the XBL scope. We could remove this exception,
410 // if needed, by using ExportFunction to generate the content-side
411 // representations of XBL methods.
412 if (originIsXBLScope
)
413 return &FilteringWrapper
<CrossCompartmentSecurityWrapper
, OpaqueWithCall
>::singleton
;
414 return &FilteringWrapper
<CrossCompartmentSecurityWrapper
, Opaque
>::singleton
;
417 static const Wrapper
*
418 SelectAddonWrapper(JSContext
* cx
, HandleObject obj
, const Wrapper
* wrapper
)
420 JSAddonId
* originAddon
= JS::AddonIdOfObject(obj
);
421 JSAddonId
* targetAddon
= JS::AddonIdOfObject(JS::CurrentGlobalOrNull(cx
));
423 MOZ_ASSERT(AccessCheck::isChrome(JS::CurrentGlobalOrNull(cx
)));
424 MOZ_ASSERT(targetAddon
);
426 if (targetAddon
== originAddon
)
429 // Add-on interposition only supports certain wrapper types, so we check if
430 // we would have used one of the supported ones.
431 if (wrapper
== &CrossCompartmentWrapper::singleton
)
432 return &AddonWrapper
<CrossCompartmentWrapper
>::singleton
;
433 else if (wrapper
== &PermissiveXrayXPCWN::singleton
)
434 return &AddonWrapper
<PermissiveXrayXPCWN
>::singleton
;
435 else if (wrapper
== &PermissiveXrayDOM::singleton
)
436 return &AddonWrapper
<PermissiveXrayDOM
>::singleton
;
438 // |wrapper| is not supported for interposition, so we don't do it.
443 WrapperFactory::Rewrap(JSContext
* cx
, HandleObject existing
, HandleObject obj
,
446 MOZ_ASSERT(!IsWrapper(obj
) ||
447 GetProxyHandler(obj
) == &XrayWaiver
||
448 js::GetObjectClass(obj
)->ext
.innerObject
,
449 "wrapped object passed to rewrap");
450 MOZ_ASSERT(!XrayUtils::IsXPCWNHolderClass(JS_GetClass(obj
)), "trying to wrap a holder");
451 MOZ_ASSERT(!js::IsInnerObject(obj
));
452 // We sometimes end up here after nsContentUtils has been shut down but before
453 // XPConnect has been shut down, so check the context stack the roundabout way.
454 MOZ_ASSERT(XPCJSRuntime::Get()->GetJSContextStack()->Peek() == cx
);
456 // Compute the information we need to select the right wrapper.
457 JSCompartment
* origin
= js::GetObjectCompartment(obj
);
458 JSCompartment
* target
= js::GetContextCompartment(cx
);
459 bool originIsChrome
= AccessCheck::isChrome(origin
);
460 bool targetIsChrome
= AccessCheck::isChrome(target
);
461 bool originSubsumesTarget
= AccessCheck::subsumesConsideringDomain(origin
, target
);
462 bool targetSubsumesOrigin
= AccessCheck::subsumesConsideringDomain(target
, origin
);
463 bool sameOrigin
= targetSubsumesOrigin
&& originSubsumesTarget
;
464 XrayType xrayType
= GetXrayType(obj
);
466 const Wrapper
* wrapper
;
469 // First, handle the special cases.
472 // If UniversalXPConnect is enabled, this is just some dumb mochitest. Use
474 if (xpc::IsUniversalXPConnectEnabled(target
)) {
475 CrashIfNotInAutomation();
476 wrapper
= &CrossCompartmentWrapper::singleton
;
479 // Let the SpecialPowers scope make its stuff easily accessible to content.
480 else if (CompartmentPrivate::Get(origin
)->forcePermissiveCOWs
) {
481 CrashIfNotInAutomation();
482 wrapper
= &CrossCompartmentWrapper::singleton
;
485 // If this is a chrome object being exposed to content without Xrays, use
488 // We make an exception for Object instances, because we still rely on COWs
489 // for those in a lot of places in the tree.
490 else if (originIsChrome
&& !targetIsChrome
&&
491 (xrayType
== NotXray
|| ForceCOWBehavior(obj
)))
493 wrapper
= &ChromeObjectWrapper::singleton
;
497 // Now, handle the regular cases.
499 // These are wrappers we can compute using a rule-based approach. In order
500 // to do so, we need to compute some parameters.
504 // The wrapper is a security wrapper (protecting the wrappee) if and
505 // only if the target does not subsume the origin.
506 bool securityWrapper
= !targetSubsumesOrigin
;
508 // Xrays are warranted if either the target or the origin don't trust
509 // each other. This is generally the case, unless the two are same-origin
510 // and the caller has not requested same-origin Xrays.
512 // Xrays are a bidirectional protection, since it affords clarity to the
513 // caller and privacy to the callee.
514 bool sameOriginXrays
= CompartmentPrivate::Get(origin
)->wantXrays
||
515 CompartmentPrivate::Get(target
)->wantXrays
;
516 bool wantXrays
= !sameOrigin
|| sameOriginXrays
;
518 // If Xrays are warranted, the caller may waive them for non-security
520 bool waiveXrays
= wantXrays
&& !securityWrapper
&& HasWaiveXrayFlag(obj
);
522 // We have slightly different behavior for the case when the object
523 // being wrapped is in an XBL scope.
524 bool originIsContentXBLScope
= IsContentXBLScope(origin
);
526 wrapper
= SelectWrapper(securityWrapper
, wantXrays
, xrayType
, waiveXrays
,
527 originIsContentXBLScope
);
529 // If we want to apply add-on interposition in the target compartment,
530 // then we try to "upgrade" the wrapper to an interposing one.
531 if (CompartmentPrivate::Get(target
)->scope
->HasInterposition())
532 wrapper
= SelectAddonWrapper(cx
, obj
, wrapper
);
535 if (!targetSubsumesOrigin
) {
536 // Do a belt-and-suspenders check against exposing eval()/Function() to
537 // non-subsuming content.
538 if (JSFunction
* fun
= JS_GetObjectFunction(obj
)) {
539 if (JS_IsBuiltinEvalFunction(fun
) || JS_IsBuiltinFunctionConstructor(fun
)) {
540 NS_WARNING("Trying to expose eval or Function to non-subsuming content!");
541 wrapper
= &FilteringWrapper
<CrossCompartmentSecurityWrapper
, Opaque
>::singleton
;
546 DEBUG_CheckUnwrapSafety(obj
, wrapper
, origin
, target
);
549 return Wrapper::Renew(cx
, existing
, obj
, wrapper
);
551 return Wrapper::New(cx
, obj
, parent
, wrapper
);
554 // Call WaiveXrayAndWrap when you have a JS object that you don't want to be
555 // wrapped in an Xray wrapper. cx->compartment is the compartment that will be
556 // using the returned object. If the object to be wrapped is already in the
557 // correct compartment, then this returns the unwrapped object.
559 WrapperFactory::WaiveXrayAndWrap(JSContext
* cx
, MutableHandleValue vp
)
561 if (vp
.isPrimitive())
562 return JS_WrapValue(cx
, vp
);
564 RootedObject
obj(cx
, &vp
.toObject());
565 if (!WaiveXrayAndWrap(cx
, &obj
))
573 WrapperFactory::WaiveXrayAndWrap(JSContext
* cx
, MutableHandleObject argObj
)
576 RootedObject
obj(cx
, js::UncheckedUnwrap(argObj
));
577 MOZ_ASSERT(!js::IsInnerObject(obj
));
578 if (js::IsObjectInContextCompartment(obj
, cx
)) {
583 // Even though waivers have no effect on access by scopes that don't subsume
584 // the underlying object, good defense-in-depth dictates that we should avoid
585 // handing out waivers to callers that can't use them. The transitive waiving
586 // machinery unconditionally calls WaiveXrayAndWrap on return values from
587 // waived functions, even though the return value might be not be same-origin
588 // with the function. So if we find ourselves trying to create a waiver for
589 // |cx|, we should check whether the caller has any business with waivers
590 // to things in |obj|'s compartment.
591 JSCompartment
* target
= js::GetContextCompartment(cx
);
592 JSCompartment
* origin
= js::GetObjectCompartment(obj
);
593 obj
= AccessCheck::subsumes(target
, origin
) ? WaiveXray(cx
, obj
) : obj
;
597 if (!JS_WrapObject(cx
, &obj
))
604 WrapperFactory::XrayWrapperNotShadowing(JSObject
* wrapper
, jsid id
)
606 ResolvingId
* rid
= ResolvingId::getResolvingIdFromWrapper(wrapper
);
607 return rid
->isXrayShadowing(id
);
611 * Calls to JS_TransplantObject* should go through these helpers here so that
612 * waivers get fixed up properly.
616 FixWaiverAfterTransplant(JSContext
* cx
, HandleObject oldWaiver
, HandleObject newobj
)
618 MOZ_ASSERT(Wrapper::wrapperHandler(oldWaiver
) == &XrayWaiver
);
619 MOZ_ASSERT(!js::IsCrossCompartmentWrapper(newobj
));
621 // Create a waiver in the new compartment. We know there's not one already
622 // because we _just_ transplanted, which means that |newobj| was either
623 // created from scratch, or was previously cross-compartment wrapper (which
624 // should have no waiver). CreateXrayWaiver asserts this.
625 JSObject
* newWaiver
= WrapperFactory::CreateXrayWaiver(cx
, newobj
);
629 // Update all the cross-compartment references to oldWaiver to point to
631 if (!js::RemapAllWrappersForObject(cx
, oldWaiver
, newWaiver
))
634 // There should be no same-compartment references to oldWaiver, and we
635 // just remapped all cross-compartment references. It's dead, so we can
636 // remove it from the map.
637 XPCWrappedNativeScope
* scope
= ObjectScope(oldWaiver
);
638 JSObject
* key
= Wrapper::wrappedObject(oldWaiver
);
639 MOZ_ASSERT(scope
->mWaiverWrapperMap
->Find(key
));
640 scope
->mWaiverWrapperMap
->Remove(key
);
645 TransplantObject(JSContext
* cx
, JS::HandleObject origobj
, JS::HandleObject target
)
647 RootedObject
oldWaiver(cx
, WrapperFactory::GetXrayWaiver(origobj
));
648 RootedObject
newIdentity(cx
, JS_TransplantObject(cx
, origobj
, target
));
649 if (!newIdentity
|| !oldWaiver
)
652 if (!FixWaiverAfterTransplant(cx
, oldWaiver
, newIdentity
))
658 GetNativeForGlobal(JSObject
* obj
)
660 MOZ_ASSERT(JS_IsGlobalObject(obj
));
662 // Every global needs to hold a native as its private or be a
663 // WebIDL object with an nsISupports DOM object.
664 MOZ_ASSERT((GetObjectClass(obj
)->flags
& (JSCLASS_PRIVATE_IS_NSISUPPORTS
|
665 JSCLASS_HAS_PRIVATE
)) ||
666 dom::UnwrapDOMObjectToISupports(obj
));
668 nsISupports
* native
= dom::UnwrapDOMObjectToISupports(obj
);
670 native
= static_cast<nsISupports
*>(js::GetObjectPrivate(obj
));
673 // In some cases (like for windows) it is a wrapped native,
674 // in other cases (sandboxes, backstage passes) it's just
675 // a direct pointer to the native. If it's a wrapped native
676 // let's unwrap it first.
677 if (nsCOMPtr
<nsIXPConnectWrappedNative
> wn
= do_QueryInterface(native
)) {
678 native
= wn
->Native();
682 nsCOMPtr
<nsIGlobalObject
> global
= do_QueryInterface(native
);
683 MOZ_ASSERT(global
, "Native held by global needs to implement nsIGlobalObject!");