Bug 438241, Mousover and mouseout don't behave properly with native anonymous content, r=jst,sr=sicking
authorOlli Pettay <Olli.Pettay@helsinki.fi>
Mon, 23 Jun 2008 11:07:06 +0300
changeset 15478 b1600c01c443d865bf5b3755e190f4909b5440d9
parent 15477 5f3f9388a1fa66ff17ac70bb05b7aedd2cb1ad36
child 15479 f23a68123d0a5e8f95c56191b95406e3fe628afb
push id1
push uservladimir@mozilla.com
push dateWed, 28 Jan 2009 20:28:27 +0000
reviewersjst, sicking
bugs438241
milestone1.9.1a1pre
Bug 438241, Mousover and mouseout don't behave properly with native anonymous content, r=jst,sr=sicking
content/base/src/nsGenericElement.cpp
--- a/content/base/src/nsGenericElement.cpp
+++ b/content/base/src/nsGenericElement.cpp
@@ -2227,62 +2227,85 @@ nsGenericElement::UnbindFromTree(PRBool 
 
 nsresult
 nsGenericElement::PreHandleEvent(nsEventChainPreVisitor& aVisitor)
 {
   return nsGenericElement::doPreHandleEvent(this, aVisitor);
 }
 
 static nsIContent*
-FindFirstNonNativeAnonymousAncestor(nsIContent* aContent)
+FindNativeAnonymousSubtreeOwner(nsIContent* aContent)
 {
-  while (aContent && aContent->IsNativeAnonymous()) {
-    aContent = aContent->GetParent();
+  if (aContent->IsInNativeAnonymousSubtree()) {
+    PRBool isNativeAnon = PR_FALSE;
+    while (aContent && !isNativeAnon) {
+      isNativeAnon = aContent->IsNativeAnonymous();
+      aContent = aContent->GetParent();
+    }
   }
   return aContent;
 }
 
 nsresult
 nsGenericElement::doPreHandleEvent(nsIContent* aContent,
                                    nsEventChainPreVisitor& aVisitor)
 {
   //FIXME! Document how this event retargeting works, Bug 329124.
   aVisitor.mCanHandle = PR_TRUE;
 
   // Don't propagate mouseover and mouseout events when mouse is moving
   // inside native anonymous content.
   PRBool isAnonForEvents = aContent->IsNativeAnonymous();
-  if (aVisitor.mEvent->message == NS_MOUSE_ENTER_SYNTH ||
-      aVisitor.mEvent->message == NS_MOUSE_EXIT_SYNTH) {
+  if ((aVisitor.mEvent->message == NS_MOUSE_ENTER_SYNTH ||
+       aVisitor.mEvent->message == NS_MOUSE_EXIT_SYNTH) &&
+      // This is an optimization - try to stop event propagation when
+      // event has just possibly been retargeted.
+      static_cast<nsISupports*>(aContent) == aVisitor.mEvent->target) {
      nsCOMPtr<nsIContent> relatedTarget =
        do_QueryInterface(static_cast<nsMouseEvent*>
                                     (aVisitor.mEvent)->relatedTarget);
     if (relatedTarget &&
         relatedTarget->GetOwnerDoc() == aContent->GetOwnerDoc()) {
 
       // If current target is anonymous for events or we know that related
       // target is descendant of an element which is anonymous for events,
       // we may want to stop event propagation.
       // If aContent is the original target, aVisitor.mRelatedTargetIsInAnon
       // must be updated.
       if (isAnonForEvents || aVisitor.mRelatedTargetIsInAnon ||
           (aVisitor.mEvent->originalTarget == aContent &&
            (aVisitor.mRelatedTargetIsInAnon =
             relatedTarget->IsInNativeAnonymousSubtree()))) {
-        nsIContent* nonAnon = FindFirstNonNativeAnonymousAncestor(aContent);
-        if (nonAnon) {
-          nsIContent* nonAnonRelated =
-            FindFirstNonNativeAnonymousAncestor(relatedTarget);
-          if (nonAnonRelated) {
-            if (nonAnon == nonAnonRelated ||
-                nsContentUtils::ContentIsDescendantOf(nonAnonRelated, nonAnon)) {
-              aVisitor.mParentTarget = nsnull;
-              // Event should not propagate to non-anon content.
-              aVisitor.mCanHandle = isAnonForEvents;
-              return NS_OK;
+        nsIContent* anonOwner = FindNativeAnonymousSubtreeOwner(aContent);
+        if (anonOwner) {
+          nsIContent* anonOwnerRelated =
+            FindNativeAnonymousSubtreeOwner(relatedTarget);
+          if (anonOwnerRelated) {
+            // Note, anonOwnerRelated may still be inside some other
+            // native anonymous subtree. The case where anonOwner is still
+            // inside native anonymous subtree will be handled when event
+            // propagates up in the DOM tree.
+            while (anonOwner != anonOwnerRelated &&
+                   anonOwnerRelated->IsInNativeAnonymousSubtree()) {
+              anonOwnerRelated = FindNativeAnonymousSubtreeOwner(anonOwnerRelated);
+            }
+            if (anonOwner == anonOwnerRelated) {
+              nsCOMPtr<nsIContent> target =
+                do_QueryInterface(aVisitor.mEvent->originalTarget);
+              // Because XBL and native anon content both do event re-targeting,
+              // static_cast<nsISupports*>(aContent) == aVisitor.mEvent->target
+              // optimization may not always work. So be paranoid and make
+              // sure we never stop event propagation when we shouldn't!
+              if (relatedTarget->FindFirstNonNativeAnonymous() ==
+                  target->FindFirstNonNativeAnonymous()) {
+                aVisitor.mParentTarget = nsnull;
+                // Event should not propagate to non-anon content.
+                aVisitor.mCanHandle = isAnonForEvents;
+                return NS_OK;
+              }
             }
           }
         }
       }
     }
   }
 
   nsCOMPtr<nsIContent> parent = aContent->GetParent();