Fixing bug 321299. Make sure XPConnect wrappers get properly reparented when moving nodes from document to document. r=mrbkap@gmail.com, sr=bzbarsky@mit.edu

git-svn-id: svn://10.0.0.236/trunk@194099 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
jst%mozilla.jstenback.com 2006-04-11 03:49:44 +00:00
parent 11741bca71
commit 197612f082
10 changed files with 227 additions and 160 deletions

View File

@ -102,7 +102,7 @@ public:
// the actual ownerDocument of aContent may not yet be aNewDocument.
// XXXbz but then if it gets wrapped after we do this call but before its
// ownerDocument actually changes, things will break...
static nsresult ReparentContentWrapper(nsIContent *aContent,
static nsresult ReparentContentWrapper(nsIContent *aNode,
nsIContent *aNewParent,
nsIDocument *aNewDocument,
nsIDocument *aOldDocument);
@ -790,7 +790,7 @@ private:
static nsresult doReparentContentWrapper(nsIContent *aChild,
JSContext *cx,
JSObject *aOldGlobal,
JSObject *parent_obj);
JSObject *aNewGlobal);
static nsresult EnsureStringBundle(PropertiesFile aFile);

View File

@ -91,8 +91,9 @@ class nsIDocumentObserver;
// IID for the nsIDocument interface
#define NS_IDOCUMENT_IID \
{ 0xb657335d, 0x43db, 0x41f3, \
{ 0x8c, 0xc0, 0xe2, 0x29, 0x88, 0xb5, 0x99, 0x69 } }
{ 0xfa567fd5, 0x5220, 0x436c, \
{ 0xbe, 0x76, 0xdd, 0x1a, 0x78, 0xfb, 0x8c, 0x1a } }
// Flag for AddStyleSheet().
#define NS_STYLESHEET_FROM_CATALOG (1 << 0)
@ -477,6 +478,14 @@ public:
virtual nsIScriptGlobalObject* GetScriptGlobalObject() const = 0;
virtual void SetScriptGlobalObject(nsIScriptGlobalObject* aGlobalObject) = 0;
/**
* Get the object that is used as the scope for all of the content
* wrappers whose owner document is this document. Unlike the script
* global object, this never changes once it's set. Use this object
* when you're trying to find a content wrapper in XPConnect.
*/
virtual nsIScriptGlobalObject* GetScopeObject() = 0;
/**
* Return the window containing the document (the outer window).
*/

View File

@ -811,38 +811,33 @@ nsContentUtils::InProlog(nsIDOMNode *aNode)
// static
nsresult
nsContentUtils::doReparentContentWrapper(nsIContent *aChild,
nsContentUtils::doReparentContentWrapper(nsIContent *aNode,
JSContext *cx,
JSObject *aOldGlobal,
JSObject *parent_obj)
JSObject *aNewGlobal)
{
nsCOMPtr<nsIXPConnectJSObjectHolder> old_wrapper;
nsresult rv;
rv = sXPConnect->ReparentWrappedNativeIfFound(cx, aOldGlobal, parent_obj,
aChild,
rv = sXPConnect->ReparentWrappedNativeIfFound(cx, aOldGlobal, aNewGlobal,
aNode,
getter_AddRefs(old_wrapper));
NS_ENSURE_SUCCESS(rv, rv);
if (!old_wrapper) {
// If aChild isn't wrapped none of it's children are wrapped so
// there's no need to walk into aChild's children.
// Whether or not aChild is already wrapped we must iterate through
// its descendants since there's no guarantee that a descendant isn't
// wrapped even if this child is not wrapped. That used to be true
// when every DOM node's JSObject was parented at the DOM node's
// parent's JSObject, but that's no longer the case.
return NS_OK;
}
JSObject *old;
rv = old_wrapper->GetJSObject(&old);
NS_ENSURE_SUCCESS(rv, rv);
PRUint32 i, count = aChild->GetChildCount();
PRUint32 i, count = aNode->GetChildCount();
for (i = 0; i < count; i++) {
nsIContent *child = aChild->GetChildAt(i);
nsIContent *child = aNode->GetChildAt(i);
NS_ENSURE_TRUE(child, NS_ERROR_UNEXPECTED);
rv = doReparentContentWrapper(child, cx, aOldGlobal, old);
rv = doReparentContentWrapper(child, cx, aOldGlobal, aNewGlobal);
NS_ENSURE_SUCCESS(rv, rv);
}
@ -852,18 +847,18 @@ nsContentUtils::doReparentContentWrapper(nsIContent *aChild,
static JSContext *
GetContextFromDocument(nsIDocument *aDocument, JSObject** aGlobalObject)
{
nsIScriptGlobalObject *sgo = aDocument->GetScriptGlobalObject();
nsIScriptGlobalObject *sgo = aDocument->GetScopeObject();
if (!sgo) {
// No script global, no context.
*aGlobalObject = nsnull;
return nsnull;
}
*aGlobalObject = sgo->GetGlobalJSObject();
nsIScriptContext *scx = sgo->GetContext();
nsIScriptContext *scx = sgo->GetContext();
if (!scx) {
// No context left in the old scope...
@ -884,10 +879,13 @@ nsContentUtils::ReparentContentWrapper(nsIContent *aContent,
return NS_OK;
}
if (!aOldDocument) {
// If we can't find our old document we don't know what our old
// scope was so there's no way to find the old wrapper
nsIScriptGlobalObject *newSGO = aNewDocument->GetScopeObject();
JSObject *newScope;
// If we can't find our old document we don't know what our old
// scope was so there's no way to find the old wrapper, and if there
// is no new scope there's no reason to reparent.
if (!aOldDocument || !newSGO || !(newScope = newSGO->GetGlobalJSObject())) {
return NS_OK;
}
@ -906,41 +904,38 @@ nsContentUtils::ReparentContentWrapper(nsIContent *aContent,
JSObject *globalObj;
JSContext *cx = GetContextFromDocument(aOldDocument, &globalObj);
if (!cx || !globalObj) {
// No JSContext left in the old scope, or no global object around; can't
// find the old wrapper w/o the old context or global object
if (!globalObj) {
// No global object around; can't find the old wrapper w/o the old
// global object
return NS_OK;
}
nsCOMPtr<nsIXPConnectWrappedNative> wrapper;
nsresult rv =
sXPConnect->GetWrappedNativeOfNativeObject(cx, globalObj, aContent,
NS_GET_IID(nsISupports),
getter_AddRefs(wrapper));
NS_ENSURE_SUCCESS(rv, rv);
if (!cx) {
JSObject *dummy;
cx = GetContextFromDocument(aNewDocument, &dummy);
if (!wrapper) {
// aContent is not wrapped (and thus none of its children are
// wrapped) so there's no need to reparent anything.
if (!cx) {
// No context reachable from the old or new document, use the
// calling context, or the safe context if no caller can be
// found.
return NS_OK;
sThreadJSContextStack->Peek(&cx);
if (!cx) {
sThreadJSContextStack->GetSafeJSContext(&cx);
if (!cx) {
// No safe context reachable, bail.
NS_WARNING("No context reachable in ReparentContentWrapper()!");
return NS_ERROR_NOT_AVAILABLE;
}
}
}
}
// Wrap the new parent and reparent aContent. Don't bother using globalObj
// here, since it's wrong for the new parent anyway... Luckily, WrapNative
// will PreCreate and hence get the right scope.
nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
rv = sXPConnect->WrapNative(cx, ::JS_GetGlobalObject(cx), new_parent,
NS_GET_IID(nsISupports),
getter_AddRefs(holder));
NS_ENSURE_SUCCESS(rv, rv);
JSObject *obj;
rv = holder->GetJSObject(&obj);
NS_ENSURE_SUCCESS(rv, rv);
return doReparentContentWrapper(aContent, cx, globalObj, obj);
return doReparentContentWrapper(aContent, cx, globalObj, newScope);
}
nsIDocShell *

View File

@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 sw=2 et tw=78: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
@ -2118,6 +2119,13 @@ nsDocument::GetScriptGlobalObject() const
return mScriptGlobalObject;
}
nsIScriptGlobalObject*
nsDocument::GetScopeObject()
{
nsCOMPtr<nsIScriptGlobalObject> scope(do_QueryReferent(mScopeObject));
return scope;
}
void
nsDocument::SetScriptGlobalObject(nsIScriptGlobalObject *aScriptGlobalObject)
{
@ -2146,6 +2154,20 @@ nsDocument::SetScriptGlobalObject(nsIScriptGlobalObject *aScriptGlobalObject)
mScriptGlobalObject = aScriptGlobalObject;
// The scope object is immutable, only set it once.
if (!mScopeObject) {
mScopeObject = do_GetWeakReference(aScriptGlobalObject);
}
#ifdef DEBUG
{
nsCOMPtr<nsIScriptGlobalObject> scope = do_QueryReferent(mScopeObject);
NS_ASSERTION(!aScriptGlobalObject || aScriptGlobalObject == scope,
"script global and scope mismatch!");
}
#endif
if (mScriptGlobalObject) {
// Go back to using the docshell for the layout history state
mLayoutHistoryState = nsnull;

View File

@ -443,6 +443,8 @@ public:
virtual nsIScriptGlobalObject* GetScriptGlobalObject() const;
virtual void SetScriptGlobalObject(nsIScriptGlobalObject* aGlobalObject);
virtual nsIScriptGlobalObject* GetScopeObject();
/**
* Return the window containing the document (the outer window).
*/
@ -742,6 +744,11 @@ protected:
// *inner* window object.
nsCOMPtr<nsIScriptGlobalObject> mScriptGlobalObject;
// Weak reference to the scope object (aka the script global object)
// that, unlike mScriptGlobalObject, is never unset once set. This
// is a weak reference to avoid leaks due to circular references.
nsWeakPtr mScopeObject;
nsCOMPtr<nsIEventListenerManager> mListenerManager;
nsCOMPtr<nsIDOMStyleSheetList> mDOMStyleSheets;
nsCOMPtr<nsIScriptLoader> mScriptLoader;

View File

@ -698,6 +698,12 @@ nsGenericDOMDataNode::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
new_bits |= mParentPtrBits & nsIContent::kParentBitMask;
mParentPtrBits = new_bits;
nsIDocument *oldOwnerDocument = GetOwnerDoc();
nsIDocument *newOwnerDocument;
nsNodeInfoManager* nodeInfoManager;
// XXXbz sXBL/XBL2 issue!
// Set document
if (aDocument) {
mParentPtrBits |= PARENT_BIT_INDOCUMENT;
@ -705,38 +711,43 @@ nsGenericDOMDataNode::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
aDocument->SetBidiEnabled(PR_TRUE);
}
nsIDocument *ownerDocument = GetOwnerDoc();
if (aDocument != ownerDocument) {
if (ownerDocument && CouldHaveProperties()) {
// Copy UserData to the new document.
ownerDocument->CopyUserData(this, aDocument);
newOwnerDocument = aDocument;
nodeInfoManager = newOwnerDocument->NodeInfoManager();
} else {
newOwnerDocument = aParent->GetOwnerDoc();
nodeInfoManager = aParent->NodeInfo()->NodeInfoManager();
}
// Remove all properties.
ownerDocument->PropertyTable()->DeleteAllPropertiesFor(this);
}
// get a new nodeinfo
nsNodeInfoManager *nodeInfoManager = aDocument->NodeInfoManager();
nsCOMPtr<nsINodeInfo> newNodeInfo;
// optimize common cases
nsIAtom* name = mNodeInfo->NameAtom();
if (name == nsLayoutAtoms::textTagName) {
newNodeInfo = nodeInfoManager->GetTextNodeInfo();
NS_ENSURE_TRUE(newNodeInfo, NS_ERROR_OUT_OF_MEMORY);
}
else if (name == nsLayoutAtoms::commentTagName) {
newNodeInfo = nodeInfoManager->GetCommentNodeInfo();
NS_ENSURE_TRUE(newNodeInfo, NS_ERROR_OUT_OF_MEMORY);
}
else {
rv = nodeInfoManager->GetNodeInfo(name,
mNodeInfo->GetPrefixAtom(),
mNodeInfo->NamespaceID(),
getter_AddRefs(newNodeInfo));
NS_ENSURE_SUCCESS(rv, rv);
}
mNodeInfo.swap(newNodeInfo);
if (oldOwnerDocument) {
if (newOwnerDocument && CouldHaveProperties()) {
// Copy UserData to the new document.
oldOwnerDocument->CopyUserData(this, newOwnerDocument);
}
// Remove all properties.
oldOwnerDocument->PropertyTable()->DeleteAllPropertiesFor(this);
}
if (mNodeInfo->NodeInfoManager() != nodeInfoManager) {
nsCOMPtr<nsINodeInfo> newNodeInfo;
// optimize common cases
nsIAtom* name = mNodeInfo->NameAtom();
if (name == nsLayoutAtoms::textTagName) {
newNodeInfo = nodeInfoManager->GetTextNodeInfo();
NS_ENSURE_TRUE(newNodeInfo, NS_ERROR_OUT_OF_MEMORY);
}
else if (name == nsLayoutAtoms::commentTagName) {
newNodeInfo = nodeInfoManager->GetCommentNodeInfo();
NS_ENSURE_TRUE(newNodeInfo, NS_ERROR_OUT_OF_MEMORY);
}
else {
rv = nodeInfoManager->GetNodeInfo(name,
mNodeInfo->GetPrefixAtom(),
mNodeInfo->NamespaceID(),
getter_AddRefs(newNodeInfo));
NS_ENSURE_SUCCESS(rv, rv);
}
mNodeInfo.swap(newNodeInfo);
}
NS_POSTCONDITION(aDocument == GetCurrentDoc(), "Bound to wrong document");

View File

@ -1775,6 +1775,12 @@ nsGenericElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
nsresult rv;
nsIDocument *oldOwnerDocument = GetOwnerDoc();
nsIDocument *newOwnerDocument;
nsNodeInfoManager* nodeInfoManager;
// XXXbz sXBL/XBL2 issue!
// Finally, set the document
if (aDocument) {
// Notify XBL- & nsIAnonymousContentCreator-generated
@ -1789,38 +1795,42 @@ nsGenericElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
// Being added to a document.
mParentPtrBits |= PARENT_BIT_INDOCUMENT;
// check the document on the nodeinfo to see whether we need a
// new nodeinfo
// XXXbz sXBL/XBL2 issue!
nsIDocument *ownerDocument = GetOwnerDoc();
if (aDocument != ownerDocument) {
if (ownerDocument && HasProperties()) {
// Copy UserData to the new document.
ownerDocument->CopyUserData(this, aDocument);
newOwnerDocument = aDocument;
nodeInfoManager = newOwnerDocument->NodeInfoManager();
} else {
newOwnerDocument = aParent->GetOwnerDoc();
nodeInfoManager = aParent->NodeInfo()->NodeInfoManager();
}
// Remove all properties.
ownerDocument->PropertyTable()->DeleteAllPropertiesFor(this);
}
// Handle a change in our owner document.
// get a new nodeinfo
nsNodeInfoManager* nodeInfoManager = aDocument->NodeInfoManager();
if (nodeInfoManager) {
nsCOMPtr<nsINodeInfo> newNodeInfo;
rv = nodeInfoManager->GetNodeInfo(mNodeInfo->NameAtom(),
mNodeInfo->GetPrefixAtom(),
mNodeInfo->NamespaceID(),
getter_AddRefs(newNodeInfo));
NS_ENSURE_SUCCESS(rv, rv);
NS_ASSERTION(newNodeInfo, "GetNodeInfo lies");
mNodeInfo.swap(newNodeInfo);
}
if (oldOwnerDocument) {
if (newOwnerDocument && HasProperties()) {
// Copy UserData to the new document.
oldOwnerDocument->CopyUserData(this, newOwnerDocument);
}
// set a new nodeinfo on attribute nodes
nsDOMSlots *slots = GetExistingDOMSlots();
if (slots && slots->mAttributeMap) {
rv = slots->mAttributeMap->SetOwnerDocument(aDocument);
NS_ENSURE_SUCCESS(rv, rv);
}
// Remove all properties.
oldOwnerDocument->PropertyTable()->DeleteAllPropertiesFor(this);
}
if (mNodeInfo->NodeInfoManager() != nodeInfoManager) {
nsCOMPtr<nsINodeInfo> newNodeInfo;
rv = nodeInfoManager->GetNodeInfo(mNodeInfo->NameAtom(),
mNodeInfo->GetPrefixAtom(),
mNodeInfo->NamespaceID(),
getter_AddRefs(newNodeInfo));
NS_ENSURE_SUCCESS(rv, rv);
NS_ASSERTION(newNodeInfo, "GetNodeInfo lies");
mNodeInfo.swap(newNodeInfo);
}
if (newOwnerDocument && newOwnerDocument != oldOwnerDocument) {
// set a new nodeinfo on attribute nodes
nsDOMSlots *slots = GetExistingDOMSlots();
if (slots && slots->mAttributeMap) {
rv = slots->mAttributeMap->SetOwnerDocument(newOwnerDocument);
NS_ENSURE_SUCCESS(rv, rv);
}
}
@ -2428,7 +2438,7 @@ nsGenericElement::doRemoveChildAt(PRUint32 aIndex, PRBool aNotify,
NS_PRECONDITION(aKid && aKid->GetParent() == aParent &&
aKid == container->GetChildAt(aIndex) &&
container->IndexOf(aKid) == aIndex, "Bogus aKid");
container->IndexOf(aKid) == (PRInt32)aIndex, "Bogus aKid");
mozAutoDocUpdate updateBatch(aDocument, UPDATE_CONTENT_MODEL, aNotify);
@ -3104,9 +3114,8 @@ nsGenericElement::doReplaceOrInsertBefore(PRBool aReplace,
/* static */
nsresult
nsGenericElement::doRemoveChild(nsIDOMNode* aOldChild,
nsIContent* aParent, nsIDocument* aDocument,
nsIDOMNode** aReturn)
nsGenericElement::doRemoveChild(nsIDOMNode* aOldChild, nsIContent* aParent,
nsIDocument* aDocument, nsIDOMNode** aReturn)
{
NS_PRECONDITION(aParent || aDocument, "Must have document if no parent!");
NS_PRECONDITION(!aParent || aParent->GetCurrentDoc() == aDocument,
@ -3776,7 +3785,7 @@ nsGenericElement::List(FILE* out, PRInt32 aIndent) const
mNodeInfo->GetQualifiedName(buf);
fputs(NS_LossyConvertUTF16toASCII(buf).get(), out);
fprintf(out, "@%p", this);
fprintf(out, "@%p", (void *)this);
PRUint32 index, attrcount = mAttrsAndChildren.AttrCount();
for (index = 0; index < attrcount; index++) {

View File

@ -800,56 +800,70 @@ nsXULElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
new_bits |= mParentPtrBits & nsIContent::kParentBitMask;
mParentPtrBits = new_bits;
nsIDocument *oldOwnerDocument = GetOwnerDoc();
nsIDocument *newOwnerDocument;
nsNodeInfoManager* nodeInfoManager;
// XXXbz sXBL/XBL2 issue!
// Finally, set the document
if (aDocument && aDocument != GetCurrentDoc()) {
if (aDocument) {
// Notify XBL- & nsIAnonymousContentCreator-generated
// anonymous content that the document is changing.
// XXXbz ordering issues here? Probably not, since ChangeDocumentFor
// is just pretty broken anyway.... Need to get it working.
// anonymous content that the document is changing. XXXbz
// ordering issues here? Probably not, since
// ChangeDocumentFor is just pretty broken anyway.... Need to
// get it working.
// XXXbz XBL doesn't handle this (asserts), and we don't really want
// to be doing this during parsing anyway... sort this out.
// aDocument->BindingManager()->ChangeDocumentFor(this, nsnull,
// aDocument);
// Being added to a document.
mParentPtrBits |= PARENT_BIT_INDOCUMENT;
// check the document on the nodeinfo to see whether we need a
// new nodeinfo
// XXXbz sXBL/XBL2 issue!
nsIDocument *ownerDocument = GetOwnerDoc();
if (aDocument != ownerDocument) {
if (ownerDocument && HasProperties()) {
// Copy UserData to the new document.
ownerDocument->CopyUserData(this, aDocument);
newOwnerDocument = aDocument;
nodeInfoManager = newOwnerDocument->NodeInfoManager();
} else {
newOwnerDocument = aParent->GetOwnerDoc();
nodeInfoManager = aParent->NodeInfo()->NodeInfoManager();
}
// Remove all properties.
ownerDocument->PropertyTable()->
DeleteAllPropertiesFor(NS_STATIC_CAST(nsINode*, this));
}
// Handle a change in our owner document.
// get a new nodeinfo
nsNodeInfoManager* nodeInfoManager = aDocument->NodeInfoManager();
if (nodeInfoManager) {
nsCOMPtr<nsINodeInfo> newNodeInfo;
nsresult rv =
nodeInfoManager->GetNodeInfo(mNodeInfo->NameAtom(),
mNodeInfo->GetPrefixAtom(),
mNodeInfo->NamespaceID(),
getter_AddRefs(newNodeInfo));
NS_ENSURE_SUCCESS(rv, rv);
NS_ASSERTION(newNodeInfo, "GetNodeInfo lies");
mNodeInfo.swap(newNodeInfo);
}
// set a new nodeinfo on attribute nodes
nsDOMSlots *slots = GetExistingDOMSlots();
if (slots && slots->mAttributeMap) {
rv = slots->mAttributeMap->SetOwnerDocument(aDocument);
NS_ENSURE_SUCCESS(rv, rv);
}
if (oldOwnerDocument) {
if (newOwnerDocument && HasProperties()) {
// Copy UserData to the new document.
oldOwnerDocument->CopyUserData(this, aDocument);
}
// Remove all properties.
oldOwnerDocument->PropertyTable()->
DeleteAllPropertiesFor(NS_STATIC_CAST(nsINode*, this));
}
if (mNodeInfo->NodeInfoManager() != nodeInfoManager) {
nsCOMPtr<nsINodeInfo> newNodeInfo;
nsresult rv =
nodeInfoManager->GetNodeInfo(mNodeInfo->NameAtom(),
mNodeInfo->GetPrefixAtom(),
mNodeInfo->NamespaceID(),
getter_AddRefs(newNodeInfo));
NS_ENSURE_SUCCESS(rv, rv);
NS_ASSERTION(newNodeInfo, "GetNodeInfo lies");
mNodeInfo.swap(newNodeInfo);
}
if (newOwnerDocument && newOwnerDocument != oldOwnerDocument) {
// set a new nodeinfo on attribute nodes
nsDOMSlots *slots = GetExistingDOMSlots();
if (slots && slots->mAttributeMap) {
rv = slots->mAttributeMap->SetOwnerDocument(newOwnerDocument);
NS_ENSURE_SUCCESS(rv, rv);
}
}
if (aDocument) {
// we need to (re-)initialize several attributes that are dependant on
// the document. Do that now.
// XXXbz why do we have attributes depending on the current document?

View File

@ -6419,9 +6419,8 @@ nsNodeSH::PreCreate(nsISupports *nativeObj, JSContext *cx, JSObject *globalObj,
// set the parent to be the document's global object, if there
// is one
// Get the script global object from the document.
native_parent = doc->GetScriptGlobalObject();
// Get the scope object from the document.
native_parent = doc->GetScopeObject();
if (!native_parent) {
// No global object reachable from this document, use the

View File

@ -1123,7 +1123,8 @@ XPCWrappedNative::ReparentWrapperIfFound(XPCCallContext& ccx,
wrapper->mScriptableInfo = newProto->GetScriptableInfo();
}
NS_ASSERTION(!newMap->Find(wrapper), "wrapper already in new scope!");
NS_ASSERTION(!newMap->Find(wrapper->GetIdentityObject()),
"wrapper already in new scope!");
(void) newMap->Add(wrapper);
}