Revert changeset 3b2d178f7299 and changeset e52f4987ec94 (bug 605672) to see if they caused bug 617048.
authorL. David Baron <dbaron@dbaron.org>
Wed, 08 Dec 2010 14:34:18 -0800
changeset 58908 de8ec66c72beeee191d1e8ec159e552b56c764bc
parent 58590 e52f4987ec949b6e957baf517e85655e48413155
child 58909 c6377a0402153f34043a21791f10598089fdceb5
push idunknown
push userunknown
push dateunknown
bugs605672, 617048
milestone2.0b8pre
Revert changeset 3b2d178f7299 and changeset e52f4987ec94 (bug 605672) to see if they caused bug 617048.
content/base/public/nsContentUtils.h
content/base/src/nsContentUtils.cpp
content/base/src/nsDOMDocumentType.cpp
content/base/src/nsDocument.cpp
content/base/src/nsNodeUtils.cpp
content/base/src/nsNodeUtils.h
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -187,28 +187,30 @@ struct nsShortcutCandidate {
 class nsContentUtils
 {
   typedef mozilla::dom::Element Element;
 
 public:
   static nsresult Init();
 
   /**
-   * Get a scope from aNewDocument. Also get a context through the scope of one
-   * of the documents, from the stack or the safe context.
+   * Get a scope from aOldDocument and one from aNewDocument. Also get a
+   * context through one of the scopes, from the stack or the safe context.
    *
-   * @param aOldDocument The document to try to get a context from. May be null.
+   * @param aOldDocument The document to get aOldScope from.
    * @param aNewDocument The document to get aNewScope from.
    * @param aCx [out] Context gotten through one of the scopes, from the stack
    *                  or the safe context.
+   * @param aOldScope [out] Scope gotten from aOldDocument.
    * @param aNewScope [out] Scope gotten from aNewDocument.
    */
-  static nsresult GetContextAndScope(nsIDocument *aOldDocument,
-                                     nsIDocument *aNewDocument,
-                                     JSContext **aCx, JSObject **aNewScope);
+  static nsresult GetContextAndScopes(nsIDocument *aOldDocument,
+                                      nsIDocument *aNewDocument,
+                                      JSContext **aCx, JSObject **aOldScope,
+                                      JSObject **aNewScope);
 
   /**
    * When a document's scope changes (e.g., from document.open(), call this
    * function to move all content wrappers from the old scope to the new one.
    */
   static nsresult ReparentContentWrappersInScope(nsIScriptGlobalObject *aOldScope,
                                                  nsIScriptGlobalObject *aNewScope);
 
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -1366,57 +1366,74 @@ nsContentUtils::InProlog(nsINode *aNode)
 
   nsIDocument* doc = static_cast<nsIDocument*>(parent);
   nsIContent* root = doc->GetRootElement();
 
   return !root || doc->IndexOf(aNode) < doc->IndexOf(root);
 }
 
 static JSContext *
-GetContextFromDocument(nsIDocument *aDocument)
+GetContextFromDocument(nsIDocument *aDocument, JSObject** aGlobalObject)
 {
   nsIScriptGlobalObject *sgo = aDocument->GetScopeObject();
   if (!sgo) {
     // No script global, no context.
+
+    *aGlobalObject = nsnull;
+
     return nsnull;
   }
 
+  *aGlobalObject = sgo->GetGlobalJSObject();
+
   nsIScriptContext *scx = sgo->GetContext();
   if (!scx) {
-    // No context left in the scope...
+    // No context left in the old scope...
 
     return nsnull;
   }
 
   return (JSContext *)scx->GetNativeContext();
 }
 
 // static
 nsresult
-nsContentUtils::GetContextAndScope(nsIDocument *aOldDocument,
-                                   nsIDocument *aNewDocument, JSContext **aCx,
-                                   JSObject **aNewScope)
+nsContentUtils::GetContextAndScopes(nsIDocument *aOldDocument,
+                                    nsIDocument *aNewDocument, JSContext **aCx,
+                                    JSObject **aOldScope, JSObject **aNewScope)
 {
   *aCx = nsnull;
+  *aOldScope = nsnull;
   *aNewScope = nsnull;
 
-  JSObject *newScope = aNewDocument->GetWrapper();
-  JSObject *global;
-  if (!newScope) {
-    nsIScriptGlobalObject *newSGO = aNewDocument->GetScopeObject();
-    if (!newSGO || !(global = newSGO->GetGlobalJSObject())) {
-      return NS_OK;
-    }
+  JSObject *newScope = nsnull;
+  nsIScriptGlobalObject *newSGO = aNewDocument->GetScopeObject();
+  if (!newSGO || !(newScope = newSGO->GetGlobalJSObject())) {
+    return NS_OK;
   }
 
   NS_ENSURE_TRUE(sXPConnect, NS_ERROR_NOT_INITIALIZED);
 
-  JSContext *cx = aOldDocument ? GetContextFromDocument(aOldDocument) : nsnull;
+  // Make sure to get our hands on the right scope object, since
+  // GetWrappedNativeOfNativeObject doesn't call PreCreate and hence won't get
+  // the right scope if we pass in something bogus.  The right scope lives on
+  // the script global of the old document.
+  // XXXbz note that if GetWrappedNativeOfNativeObject did call PreCreate it
+  // would get the wrong scope (that of the _new_ document), so we should be
+  // glad it doesn't!
+  JSObject *oldScope = nsnull;
+  JSContext *cx = GetContextFromDocument(aOldDocument, &oldScope);
+
+  if (!oldScope) {
+    return NS_OK;
+  }
+
   if (!cx) {
-    cx = GetContextFromDocument(aNewDocument);
+    JSObject *dummy;
+    cx = GetContextFromDocument(aNewDocument, &dummy);
 
     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.
 
       sThreadJSContextStack->Peek(&cx);
 
@@ -1428,25 +1445,18 @@ nsContentUtils::GetContextAndScope(nsIDo
           NS_WARNING("No context reachable in GetContextAndScopes()!");
 
           return NS_ERROR_NOT_AVAILABLE;
         }
       }
     }
   }
 
-  if (!newScope && cx) {
-    jsval v;
-    nsresult rv = WrapNative(cx, global, aNewDocument, aNewDocument, &v);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    newScope = JSVAL_TO_OBJECT(v);
-  }
-
   *aCx = cx;
+  *aOldScope = oldScope;
   *aNewScope = newScope;
 
   return NS_OK;
 }
 
 nsresult
 nsContentUtils::ReparentContentWrappersInScope(nsIScriptGlobalObject *aOldScope,
                                                nsIScriptGlobalObject *aNewScope)
--- a/content/base/src/nsDOMDocumentType.cpp
+++ b/content/base/src/nsDOMDocumentType.cpp
@@ -263,25 +263,26 @@ nsDOMDocumentType::BindToTree(nsIDocumen
     nsCOMPtr<nsINodeInfo> newNodeInfo;
     newNodeInfo = nimgr->GetNodeInfo(mNodeInfo->NameAtom(),
                                      mNodeInfo->GetPrefixAtom(),
                                      mNodeInfo->NamespaceID());
     NS_ENSURE_TRUE(newNodeInfo, NS_ERROR_OUT_OF_MEMORY);
 
     mNodeInfo.swap(newNodeInfo);
 
-    JSObject *oldScope = GetWrapper();
-    if (oldScope) {
+    nsCOMPtr<nsIDocument> oldOwnerDoc =
+      do_QueryInterface(nsContentUtils::GetDocumentFromContext());
+    nsIDocument *newOwnerDoc = nimgr->GetDocument();
+    if (oldOwnerDoc && newOwnerDoc) {
       nsIXPConnect *xpc = nsContentUtils::XPConnect();
 
       JSContext *cx = nsnull;
-      JSObject *newScope = nsnull;
-      nsresult rv = nsContentUtils::GetContextAndScope(nsnull,
-                                                       nimgr->GetDocument(),
-                                                       &cx, &newScope);
+      JSObject *oldScope = nsnull, *newScope = nsnull;
+      nsresult rv = nsContentUtils::GetContextAndScopes(oldOwnerDoc, newOwnerDoc, &cx,
+                                                        &oldScope, &newScope);
       if (cx && xpc) {
         nsISupports *node = NS_ISUPPORTS_CAST(nsIContent*, this);
         nsCOMPtr<nsIXPConnectJSObjectHolder> oldWrapper;
         rv = xpc->ReparentWrappedNativeIfFound(cx, oldScope, newScope, node,
                                                getter_AddRefs(oldWrapper));
       }
 
       if (NS_FAILED(rv)) {
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -6077,25 +6077,27 @@ nsDocument::AdoptNode(nsIDOMNode *aAdopt
       return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
     }
   }
 
   nsIDocument *oldDocument = adoptedNode->GetOwnerDoc();
   PRBool sameDocument = oldDocument == this;
 
   JSContext *cx = nsnull;
+  JSObject *oldScope = nsnull;
   JSObject *newScope = nsnull;
-  if (!sameDocument) {
-    rv = nsContentUtils::GetContextAndScope(oldDocument, this, &cx, &newScope);
+  if (!sameDocument && oldDocument) {
+    rv = nsContentUtils::GetContextAndScopes(oldDocument, this, &cx, &oldScope,
+                                             &newScope);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   nsCOMArray<nsINode> nodesWithProperties;
   rv = nsNodeUtils::Adopt(adoptedNode, sameDocument ? nsnull : mNodeInfoManager,
-                          cx, newScope, nodesWithProperties);
+                          cx, oldScope, newScope, nodesWithProperties);
   if (NS_FAILED(rv)) {
     // Disconnect all nodes from their parents, since some have the old document
     // as their ownerDocument and some have this as their ownerDocument.
     BlastSubtreeToPieces(adoptedNode);
 
     if (!sameDocument && oldDocument) {
       PRUint32 count = nodesWithProperties.Count();
       for (PRUint32 j = 0; j < oldDocument->GetPropertyTableCount(); ++j) {
--- a/content/base/src/nsNodeUtils.cpp
+++ b/content/base/src/nsNodeUtils.cpp
@@ -439,36 +439,36 @@ nsNodeUtils::CloneNodeImpl(nsINode *aNod
 
   return NS_OK;
 }
 
 /* static */
 nsresult
 nsNodeUtils::CloneAndAdopt(nsINode *aNode, PRBool aClone, PRBool aDeep,
                            nsNodeInfoManager *aNewNodeInfoManager,
-                           JSContext *aCx, JSObject *aNewScope,
+                           JSContext *aCx, JSObject *aOldScope,
+                           JSObject *aNewScope,
                            nsCOMArray<nsINode> &aNodesWithProperties,
                            nsINode *aParent, nsINode **aResult)
 {
   NS_PRECONDITION((!aClone && aNewNodeInfoManager) || !aCx,
                   "If cloning or not getting a new nodeinfo we shouldn't "
                   "rewrap");
-  NS_PRECONDITION(!aCx || aNewScope, "Must have new scope");
+  NS_PRECONDITION(!aCx || (aOldScope && aNewScope), "Must have scopes");
   NS_PRECONDITION(!aParent || aNode->IsNodeOfType(nsINode::eCONTENT),
                   "Can't insert document or attribute nodes into a parent");
 
   *aResult = nsnull;
 
   // First deal with aNode and walk its attributes (and their children). Then,
   // if aDeep is PR_TRUE, deal with aNode's children (and recurse into their
   // attributes and children).
 
   nsresult rv;
-  JSObject *wrapper;
-  if (aCx && (wrapper = aNode->GetWrapper())) {
+  if (aCx) {
       rv = xpc_MorphSlimWrapper(aCx, aNode);
       NS_ENSURE_SUCCESS(rv, rv);
   }
 
   nsNodeInfoManager *nodeInfoManager = aNewNodeInfoManager;
 
   // aNode.
   nsINodeInfo *nodeInfo = aNode->mNodeInfo;
@@ -577,37 +577,39 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNod
       if (imageContent)
         imageContent->NotifyOwnerDocumentChanged(oldDoc);
     }
 
     if (elem) {
       elem->RecompileScriptEventListeners();
     }
 
-    if (aCx && wrapper) {
+    if (aCx) {
       nsIXPConnect *xpc = nsContentUtils::XPConnect();
       if (xpc) {
+        nsWrapperCache *cache;
+        CallQueryInterface(aNode, &cache);
         JSObject *preservedWrapper = nsnull;
 
         // If reparenting moves us to a new compartment, preserving causes
         // problems. In that case, we release ourselves and re-preserve after
         // reparenting so we're sure to have the right JS object preserved.
         // We use a JSObject stack copy of the wrapper to protect it from GC
         // under ReparentWrappedNativeIfFound.
-        if (aNode->PreservingWrapper()) {
-          preservedWrapper = wrapper;
-          nsContentUtils::ReleaseWrapper(aNode, aNode);
+        if (cache && cache->PreservingWrapper()) {
+          preservedWrapper = cache->GetWrapper();
+          nsContentUtils::ReleaseWrapper(aNode, cache);
         }
 
         nsCOMPtr<nsIXPConnectJSObjectHolder> oldWrapper;
-        rv = xpc->ReparentWrappedNativeIfFound(aCx, wrapper, aNewScope, aNode,
+        rv = xpc->ReparentWrappedNativeIfFound(aCx, aOldScope, aNewScope, aNode,
                                                getter_AddRefs(oldWrapper));
 
         if (preservedWrapper) {
-          nsContentUtils::PreserveWrapper(aNode, aNode);
+          nsContentUtils::PreserveWrapper(aNode, cache);
         }
 
         if (NS_FAILED(rv)) {
           aNode->mNodeInfo.swap(nodeInfo);
 
           return rv;
         }
       }
@@ -641,18 +643,18 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNod
   }
   // XXX End of workaround for broken attribute nodes.
   else if (aDeep || aNode->IsNodeOfType(nsINode::eATTRIBUTE)) {
     // aNode's children.
     PRUint32 i, length = aNode->GetChildCount();
     for (i = 0; i < length; ++i) {
       nsCOMPtr<nsINode> child;
       rv = CloneAndAdopt(aNode->GetChildAt(i), aClone, PR_TRUE, nodeInfoManager,
-                         aCx, aNewScope, aNodesWithProperties, clone,
-                         getter_AddRefs(child));
+                         aCx, aOldScope, aNewScope, aNodesWithProperties,
+                         clone, getter_AddRefs(child));
       NS_ENSURE_SUCCESS(rv, rv);
     }
   }
 
   // XXX setting document on some nodes not in a document so XBL will bind
   // and chrome won't break. Make XBL bind to document-less nodes!
   // XXXbz Once this is fixed, fix up the asserts in all implementations of
   // BindToTree to assert what they would like to assert, and fix the
--- a/content/base/src/nsNodeUtils.h
+++ b/content/base/src/nsNodeUtils.h
@@ -167,17 +167,17 @@ public:
    * @param aResult *aResult will contain the cloned node.
    */
   static nsresult Clone(nsINode *aNode, PRBool aDeep,
                         nsNodeInfoManager *aNewNodeInfoManager,
                         nsCOMArray<nsINode> &aNodesWithProperties,
                         nsIDOMNode **aResult)
   {
     return CloneAndAdopt(aNode, PR_TRUE, aDeep, aNewNodeInfoManager, nsnull,
-                         nsnull, aNodesWithProperties, aResult);
+                         nsnull, nsnull, aNodesWithProperties, aResult);
   }
 
   /**
    * Walks aNode, its attributes and descendant nodes. If aNewNodeInfoManager is
    * not null, it is used to create new nodeinfos for the nodes. Also reparents
    * the XPConnect wrappers for the nodes in aNewScope if aCx is not null.
    * aNodesWithProperties will be filled with all the nodes that have
    * properties.
@@ -185,26 +185,28 @@ public:
    * @param aNode Node to adopt.
    * @param aNewNodeInfoManager The nodeinfo manager to use to create new
    *                            nodeinfos for aNode and its attributes and
    *                            descendants. May be null if the nodeinfos
    *                            shouldn't be changed.
    * @param aCx Context to use for reparenting the wrappers, or null if no
    *            reparenting should be done. Must be null if aNewNodeInfoManager
    *            is null.
+   * @param aOldScope Old scope for the wrappers. May be null if aCx is null.
    * @param aNewScope New scope for the wrappers. May be null if aCx is null.
    * @param aNodesWithProperties All nodes (from amongst aNode and its
    *                             descendants) with properties.
    */
   static nsresult Adopt(nsINode *aNode, nsNodeInfoManager *aNewNodeInfoManager,
-                        JSContext *aCx, JSObject *aNewScope,
+                        JSContext *aCx, JSObject *aOldScope,
+                        JSObject *aNewScope,
                         nsCOMArray<nsINode> &aNodesWithProperties)
   {
     nsresult rv = CloneAndAdopt(aNode, PR_FALSE, PR_TRUE, aNewNodeInfoManager,
-                                aCx, aNewScope, aNodesWithProperties,
+                                aCx, aOldScope, aNewScope, aNodesWithProperties,
                                 nsnull);
 
     nsMutationGuard::DidMutate();
 
     return rv;
   }
 
   /**
@@ -270,37 +272,39 @@ private:
    *              descendants of the node
    * @param aNewNodeInfoManager The nodeinfo manager to use to create new
    *                            nodeinfos for aNode and its attributes and
    *                            descendants. May be null if the nodeinfos
    *                            shouldn't be changed.
    * @param aCx Context to use for reparenting the wrappers, or null if no
    *            reparenting should be done. Must be null if aClone is PR_TRUE or
    *            if aNewNodeInfoManager is null.
+   * @param aOldScope Old scope for the wrappers. May be null if aCx is null.
    * @param aNewScope New scope for the wrappers. May be null if aCx is null.
    * @param aNodesWithProperties All nodes (from amongst aNode and its
    *                             descendants) with properties. If aClone is
    *                             PR_TRUE every node will be followed by its
    *                             clone.
    * @param aResult If aClone is PR_FALSE then aResult must be null, else
    *                *aResult will contain the cloned node.
    */
   static nsresult CloneAndAdopt(nsINode *aNode, PRBool aClone, PRBool aDeep,
                                 nsNodeInfoManager *aNewNodeInfoManager,
-                                JSContext *aCx, JSObject *aNewScope,
+                                JSContext *aCx, JSObject *aOldScope,
+                                JSObject *aNewScope,
                                 nsCOMArray<nsINode> &aNodesWithProperties,
                                 nsIDOMNode **aResult)
   {
     NS_ASSERTION(!aClone == !aResult,
                  "aResult must be null when adopting and non-null when "
                  "cloning");
 
     nsCOMPtr<nsINode> clone;
     nsresult rv = CloneAndAdopt(aNode, aClone, aDeep, aNewNodeInfoManager,
-                                aCx, aNewScope, aNodesWithProperties,
+                                aCx, aOldScope, aNewScope, aNodesWithProperties,
                                 nsnull, getter_AddRefs(clone));
     NS_ENSURE_SUCCESS(rv, rv);
 
     return clone ? CallQueryInterface(clone, aResult) : NS_OK;
   }
 
   /**
    * See above for arguments that aren't described here.
@@ -308,14 +312,15 @@ private:
    * @param aParent If aClone is PR_TRUE the cloned node will be appended to
    *                aParent's children. May be null. If not null then aNode
    *                must be an nsIContent.
    * @param aResult If aClone is PR_TRUE then *aResult will contain the cloned
    *                node.
    */
   static nsresult CloneAndAdopt(nsINode *aNode, PRBool aClone, PRBool aDeep,
                                 nsNodeInfoManager *aNewNodeInfoManager,
-                                JSContext *aCx, JSObject *aNewScope,
+                                JSContext *aCx, JSObject *aOldScope,
+                                JSObject *aNewScope,
                                 nsCOMArray<nsINode> &aNodesWithProperties,
                                 nsINode *aParent, nsINode **aResult);
 };
 
 #endif // nsNodeUtils_h___