mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 758415 - Implement expando object infrastructure for WN Xrays. r=mrbkap
Note: This overloads the naming of some of the existing infrastructure, but the signatures etc are sufficient to disambiguate. The other infrastructure goes away in a subsequent patch. Note: We tag sandbox expandos with their global to make sure that the expandos are never shared between sandboxes. A consequence of this scheme is that an expando from a sandbox to an object will _always_ result in a GC edge back to the sandbox, meaning that the sandbox is always kept alive for the lifetime of the expando target. This could happen before, but only if a non-primitive expando was placed (since the value of the expando would live in the consumer's compartment). We could avoid this edge by using a reference-counted Identity() object instead, but I suspect it's not worth worrying about.
This commit is contained in:
parent
80690d47d6
commit
9610f7f05a
@ -327,6 +327,18 @@ inline void MorphMultiSlot(JSObject *obj)
|
||||
MOZ_ASSERT(!IS_SLIM_WRAPPER(obj));
|
||||
}
|
||||
|
||||
inline void SetExpandoChain(JSObject *obj, JSObject *chain)
|
||||
{
|
||||
MOZ_ASSERT(IS_WN_WRAPPER(obj));
|
||||
JS_SetReservedSlot(obj, WRAPPER_MULTISLOT, JS::ObjectOrNullValue(chain));
|
||||
}
|
||||
|
||||
inline JSObject* GetExpandoChain(JSObject *obj)
|
||||
{
|
||||
MOZ_ASSERT(IS_WN_WRAPPER(obj));
|
||||
return JS_GetReservedSlot(obj, WRAPPER_MULTISLOT).toObjectOrNull();
|
||||
}
|
||||
|
||||
/***************************************************************************/
|
||||
// Auto locking support class...
|
||||
|
||||
|
@ -42,6 +42,169 @@ JSClass HolderClass = {
|
||||
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub
|
||||
};
|
||||
|
||||
/*
|
||||
* Xray expando handling.
|
||||
*
|
||||
* We hang expandos for Xray wrappers off a reserved slot on the target object
|
||||
* so that same-origin compartments can share expandos for a given object. We
|
||||
* have a linked list of expando objects, one per origin. The properties on these
|
||||
* objects are generally wrappers pointing back to the compartment that applied
|
||||
* them.
|
||||
*
|
||||
* The expando objects should _never_ be exposed to script. The fact that they
|
||||
* live in the target compartment is a detail of the implementation, and does
|
||||
* not imply that code in the target compartment should be allowed to inspect
|
||||
* them. They are private to the origin that placed them.
|
||||
*/
|
||||
|
||||
enum ExpandoSlots {
|
||||
JSSLOT_EXPANDO_NEXT = 0,
|
||||
JSSLOT_EXPANDO_ORIGIN,
|
||||
JSSLOT_EXPANDO_EXCLUSIVE_GLOBAL,
|
||||
JSSLOT_EXPANDO_COUNT
|
||||
};
|
||||
|
||||
static nsIPrincipal*
|
||||
ObjectPrincipal(JSObject *obj)
|
||||
{
|
||||
return GetCompartmentPrincipal(js::GetObjectCompartment(obj));
|
||||
}
|
||||
|
||||
static nsIPrincipal*
|
||||
GetExpandoObjectPrincipal(JSObject *expandoObject)
|
||||
{
|
||||
JS::Value v = JS_GetReservedSlot(expandoObject, JSSLOT_EXPANDO_ORIGIN);
|
||||
return static_cast<nsIPrincipal*>(v.toPrivate());
|
||||
}
|
||||
|
||||
static void
|
||||
ExpandoObjectFinalize(JSFreeOp *fop, JSObject *obj)
|
||||
{
|
||||
// Release the principal.
|
||||
nsIPrincipal *principal = GetExpandoObjectPrincipal(obj);
|
||||
NS_RELEASE(principal);
|
||||
}
|
||||
|
||||
JSClass ExpandoObjectClass = {
|
||||
"XrayExpandoObject",
|
||||
JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_EXPANDO_COUNT),
|
||||
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
|
||||
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, ExpandoObjectFinalize
|
||||
};
|
||||
|
||||
bool
|
||||
ExpandoObjectMatchesConsumer(JSObject *expandoObject,
|
||||
nsIPrincipal *consumerOrigin,
|
||||
JSObject *exclusiveGlobal)
|
||||
{
|
||||
// First, compare the principals.
|
||||
nsIPrincipal *o = GetExpandoObjectPrincipal(expandoObject);
|
||||
bool equal;
|
||||
// Note that it's very important here to ignore document.domain. We
|
||||
// pull the principal for the expando object off of the first consumer
|
||||
// for a given origin, and freely share the expandos amongst multiple
|
||||
// same-origin consumers afterwards. However, this means that we have
|
||||
// no way to know whether _all_ consumers have opted in to collaboration
|
||||
// by explicitly setting document.domain. So we just mandate that expando
|
||||
// sharing is unaffected by it.
|
||||
nsresult rv = consumerOrigin->EqualsIgnoringDomain(o, &equal);
|
||||
if (NS_FAILED(rv) || !equal)
|
||||
return false;
|
||||
|
||||
// Sandboxes want exclusive expando objects.
|
||||
JSObject *owner = JS_GetReservedSlot(expandoObject,
|
||||
JSSLOT_EXPANDO_EXCLUSIVE_GLOBAL)
|
||||
.toObjectOrNull();
|
||||
if (!owner && !exclusiveGlobal)
|
||||
return true;
|
||||
return owner == exclusiveGlobal;
|
||||
}
|
||||
|
||||
JSObject *
|
||||
LookupExpandoObject(JSObject *target, nsIPrincipal *origin,
|
||||
JSObject *exclusiveGlobal)
|
||||
{
|
||||
// Iterate through the chain, looking for a same-origin object.
|
||||
JSObject *head = GetExpandoChain(target);
|
||||
while (head) {
|
||||
if (ExpandoObjectMatchesConsumer(head, origin, exclusiveGlobal))
|
||||
return head;
|
||||
head = JS_GetReservedSlot(head, JSSLOT_EXPANDO_NEXT).toObjectOrNull();
|
||||
}
|
||||
|
||||
// Not found.
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
// Convenience method for the above.
|
||||
JSObject *
|
||||
LookupExpandoObject(JSObject *target, JSObject *consumer)
|
||||
{
|
||||
JSObject *consumerGlobal = js::GetGlobalForObjectCrossCompartment(consumer);
|
||||
bool isSandbox = !strcmp(js::GetObjectJSClass(consumerGlobal)->name, "Sandbox");
|
||||
return LookupExpandoObject(target, ObjectPrincipal(consumer),
|
||||
isSandbox ? consumerGlobal : nsnull);
|
||||
}
|
||||
|
||||
JSObject *
|
||||
AttachExpandoObject(JSContext *cx, JSObject *target, nsIPrincipal *origin,
|
||||
JSObject *exclusiveGlobal)
|
||||
{
|
||||
// We should only be used for WNs.
|
||||
MOZ_ASSERT(IS_WN_WRAPPER(target));
|
||||
|
||||
// No duplicates allowed.
|
||||
MOZ_ASSERT(!LookupExpandoObject(target, origin, exclusiveGlobal));
|
||||
|
||||
// Create the expando object. We parent it directly to the target object.
|
||||
JSObject *expandoObject = JS_NewObjectWithGivenProto(cx, &ExpandoObjectClass,
|
||||
nsnull, target);
|
||||
if (!expandoObject)
|
||||
return nsnull;
|
||||
|
||||
// AddRef and store the principal.
|
||||
NS_ADDREF(origin);
|
||||
JS_SetReservedSlot(expandoObject, JSSLOT_EXPANDO_ORIGIN, PRIVATE_TO_JSVAL(origin));
|
||||
|
||||
// Note the exclusive global, if any.
|
||||
JS_SetReservedSlot(expandoObject, JSSLOT_EXPANDO_EXCLUSIVE_GLOBAL,
|
||||
OBJECT_TO_JSVAL(exclusiveGlobal));
|
||||
|
||||
// If this is our first expando object, take the opportunity to preserve
|
||||
// the wrapper. This keeps our expandos alive even if the Xray wrapper gets
|
||||
// collected.
|
||||
JSObject *chain = GetExpandoChain(target);
|
||||
if (!chain) {
|
||||
XPCWrappedNative *wn =
|
||||
static_cast<XPCWrappedNative *>(xpc_GetJSPrivate(target));
|
||||
nsRefPtr<nsXPCClassInfo> ci;
|
||||
CallQueryInterface(wn->Native(), getter_AddRefs(ci));
|
||||
if (ci)
|
||||
ci->PreserveWrapper(wn->Native());
|
||||
}
|
||||
|
||||
// Insert it at the front of the chain.
|
||||
JS_SetReservedSlot(expandoObject, JSSLOT_EXPANDO_NEXT, OBJECT_TO_JSVAL(chain));
|
||||
SetExpandoChain(target, expandoObject);
|
||||
|
||||
return expandoObject;
|
||||
}
|
||||
|
||||
JSObject *
|
||||
EnsureExpandoObject(JSContext *cx, JSObject *wrapper, JSObject *target)
|
||||
{
|
||||
JSObject *expandoObject = LookupExpandoObject(target, wrapper);
|
||||
if (!expandoObject) {
|
||||
// If the object is a sandbox, we don't want it to share expandos with
|
||||
// anyone else, so we tag it with the sandbox global.
|
||||
JSObject *consumerGlobal = js::GetGlobalForObjectCrossCompartment(wrapper);
|
||||
bool isSandbox = !strcmp(js::GetObjectJSClass(consumerGlobal)->name, "Sandbox");
|
||||
expandoObject = AttachExpandoObject(cx, target, ObjectPrincipal(wrapper),
|
||||
isSandbox ? consumerGlobal : nsnull);
|
||||
}
|
||||
return expandoObject;
|
||||
}
|
||||
|
||||
JSObject *
|
||||
createHolder(JSContext *cx, JSObject *wrappedNative, JSObject *parent)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user