Bug 751995 - Handle orphaned wrappers. r=peterv

This commit is contained in:
Bobby Holley 2012-06-02 00:24:22 +02:00
parent 2d64658038
commit e580a6cb4f
5 changed files with 136 additions and 3 deletions

View File

@ -0,0 +1,36 @@
<!DOCTYPE html>
<html class="reftest-wait">
<head>
<script>
function frameDoc() { return document.getElementById("f").contentDocument; }
function arm() {
// Create an element in the iframe.
var div = frameDoc().createElement("div");
// Force a wrapper to be created for .style.
var style = div.style;
style.color = "green";
// Adopt the element out of the iframe, leaving the |style| behind.
document.adoptNode(div);
}
function boom()
{
// Create an orphan.
arm();
// Force an iteration over all the wrappers in frameDoc's scope, causing
// us to notice the orphan.
frameDoc().write("2");
// All done.
document.documentElement.removeAttribute("class");
}
</script>
</head>
<body onload="boom();"><iframe id="f" src="data:text/html,1"></iframe></body>
</html>

View File

@ -37,5 +37,6 @@ load 648206-1.html
load 705875.html
load 720305-1.html
load 723465.html
load 751995.html
asserts(0-1) load 752038.html # We may hit bug 645229 here.
load 754311.html

View File

@ -1729,6 +1729,80 @@ XPCWrappedNative::ReparentWrapperIfFound(XPCCallContext& ccx,
return NS_OK;
}
XPCWrappedNative*
XPCWrappedNative::GetParentWrapper()
{
XPCWrappedNative *wrapper = nsnull;
JSObject *parent = js::GetObjectParent(mFlatJSObject);
if (parent && IS_WN_WRAPPER(parent)) {
wrapper = static_cast<XPCWrappedNative*>(js::GetObjectPrivate(parent));
}
return wrapper;
}
// Orphans are sad little things - If only we could treat them better. :-(
//
// When a wrapper gets reparented to another scope (for example, when calling
// adoptNode), it's entirely possible that it previously served as the parent for
// other wrappers (via PreCreate hooks). When it moves, the old mFlatJSObject is
// replaced by a cross-compartment wrapper. Its descendants really _should_ move
// too, but we have no way of locating them short of a compartment-wide sweep
// (which we believe to be prohibitively expensive).
//
// So we just leave them behind. In practice, the only time this turns out to
// be a problem is during subsequent wrapper reparenting. When this happens, we
// call into the below fixup code at the last minute and straighten things out
// before proceeding.
//
// See bug 751995 for more information.
bool
XPCWrappedNative::IsOrphan()
{
JSObject *parent = js::GetObjectParent(mFlatJSObject);
// If there's no parent, we've presumably got a global, which can't be an
// orphan by definition.
if (!parent)
return false;
// If our parent is a cross-compartment wrapper, it has left us behind.
return js::IsCrossCompartmentWrapper(parent);
}
// Recursively fix up orphans on the parent chain of a wrapper. Note that this
// can cause a wrapper to move even if IsOrphan() is false, since its parent
// might be an orphan, and fixing the parent causes this wrapper to become an
// orphan.
nsresult
XPCWrappedNative::RescueOrphans(XPCCallContext& ccx)
{
// Even if we're not an orphan at the moment, one of our ancestors might
// be. If so, we need to recursively rescue up the parent chain.
nsresult rv;
XPCWrappedNative *parentWrapper = GetParentWrapper();
if (parentWrapper && parentWrapper->IsOrphan()) {
rv = parentWrapper->RescueOrphans(ccx);
NS_ENSURE_SUCCESS(rv, rv);
}
// Now that we know our parent is in the right place, determine if we've
// been orphaned. If not, we have nothing to do.
if (!IsOrphan())
return NS_OK;
// We've been orphaned. Find where our parent went, and follow it.
JSObject *parentGhost = js::GetObjectParent(mFlatJSObject);
JSObject *realParent = js::UnwrapObject(parentGhost);
nsRefPtr<XPCWrappedNative> ignored;
return ReparentWrapperIfFound(ccx,
XPCWrappedNativeScope::
FindInJSObjectScope(ccx, parentGhost),
XPCWrappedNativeScope::
FindInJSObjectScope(ccx, realParent),
realParent, mIdentity, getter_AddRefs(ignored));
}
#define IS_TEAROFF_CLASS(clazz) \
((clazz) == &XPC_WN_Tearoff_JSClass)

View File

@ -1547,6 +1547,19 @@ MoveWrapper(XPCCallContext& ccx, XPCWrappedNative *wrapper,
return NS_OK;
}
// For performance reasons, we wait to fix up orphaned wrappers (wrappers
// whose parents have moved to another scope) until right before they
// threaten to confuse us.
//
// If this wrapper is an orphan, reunite it with its parent. If, following
// that, the wrapper is no longer in the old scope, then we don't need to
// reparent it.
MOZ_ASSERT(wrapper->GetScope() == oldScope);
nsresult rv = wrapper->RescueOrphans(ccx);
NS_ENSURE_SUCCESS(rv, rv);
if (wrapper->GetScope() != oldScope)
return NS_OK;
nsISupports *identity = wrapper->GetIdentityObject();
nsCOMPtr<nsIClassInfo> info(do_QueryInterface(identity));
@ -1571,9 +1584,9 @@ MoveWrapper(XPCCallContext& ccx, XPCWrappedNative *wrapper,
return NS_OK;
JSObject *newParent = oldScope->GetGlobalJSObject();
nsresult rv = sciWrapper.GetCallback()->PreCreate(identity, ccx,
newParent,
&newParent);
rv = sciWrapper.GetCallback()->PreCreate(identity, ccx,
newParent,
&newParent);
if (NS_FAILED(rv))
return rv;

View File

@ -2704,6 +2704,15 @@ public:
nsISupports* aCOMObj,
XPCWrappedNative** aWrapper);
// Returns the wrapper corresponding to the parent of our mFlatJSObject.
//
// If the parent does not have a WN, or if there is no parent, null is
// returned.
XPCWrappedNative *GetParentWrapper();
bool IsOrphan();
nsresult RescueOrphans(XPCCallContext& ccx);
void FlatJSObjectFinalized();
void SystemIsBeingShutDown();