Bug 392188 - Add a XUL attribute called "clickthrough" that determines whether an element accepts mouse events when it's in a background window. r=smaug
authorMarkus Stange <mstange@themasta.com>
Fri, 16 Jul 2010 14:50:28 +0200
changeset 47793 e18854fb9aed5bfe72a05ec21eb09e60f839ce4e
parent 47792 e5c19ff58cf98ed9f198c71587a047254ba8a3f0
child 47794 ffbbedb69cac431d91af064c1a4e659d14ae2775
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewerssmaug
bugs392188
milestone2.0b2pre
Bug 392188 - Add a XUL attribute called "clickthrough" that determines whether an element accepts mouse events when it's in a background window. r=smaug
content/base/public/nsContentUtils.h
content/base/src/nsContentUtils.cpp
content/base/src/nsGkAtomList.h
content/events/src/nsEventStateManager.cpp
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -218,16 +218,21 @@ public:
 
   /**
    * Check whether a caller is trusted to have aCapability.  This also
    * checks for UniversalXPConnect in addition to aCapability.
    */
   static PRBool   IsCallerTrustedForCapability(const char* aCapability);
 
   /**
+   * Returns the parent node of aChild crossing document boundaries.
+   */
+  static nsINode* GetCrossDocParentNode(nsINode* aChild);
+
+  /**
    * Do not ever pass null pointers to this method.  If one of your
    * nsIContents is null, you have to decide for yourself what
    * "IsDescendantOf" really means.
    *
    * @param  aPossibleDescendant node to test for being a descendant of
    *         aPossibleAncestor
    * @param  aPossibleAncestor node to test for being an ancestor of
    *         aPossibleDescendant
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -1517,16 +1517,31 @@ nsContentUtils::IsCallerTrustedForRead()
 
 PRBool
 nsContentUtils::IsCallerTrustedForWrite()
 {
   return IsCallerTrustedForCapability("UniversalBrowserWrite");
 }
 
 // static
+nsINode*
+nsContentUtils::GetCrossDocParentNode(nsINode* aChild)
+{
+  NS_PRECONDITION(aChild, "The child is null!");
+
+  nsINode* parent = aChild->GetNodeParent();
+  if (parent || !aChild->IsNodeOfType(nsINode::eDOCUMENT))
+    return parent;
+
+  nsIDocument* doc = static_cast<nsIDocument*>(aChild);
+  nsIDocument* parentDoc = doc->GetParentDocument();
+  return parentDoc ? parentDoc->FindContentForSubDocument(doc) : nsnull;
+}
+
+// static
 PRBool
 nsContentUtils::ContentIsDescendantOf(const nsINode* aPossibleDescendant,
                                       const nsINode* aPossibleAncestor)
 {
   NS_PRECONDITION(aPossibleDescendant, "The possible descendant is null!");
   NS_PRECONDITION(aPossibleAncestor, "The possible ancestor is null!");
 
   do {
@@ -1544,26 +1559,17 @@ nsContentUtils::ContentIsCrossDocDescend
                                               nsINode* aPossibleAncestor)
 {
   NS_PRECONDITION(aPossibleDescendant, "The possible descendant is null!");
   NS_PRECONDITION(aPossibleAncestor, "The possible ancestor is null!");
 
   do {
     if (aPossibleDescendant == aPossibleAncestor)
       return PR_TRUE;
-    nsINode* parent = aPossibleDescendant->GetNodeParent();
-    if (!parent && aPossibleDescendant->IsNodeOfType(nsINode::eDOCUMENT)) {
-      nsIDocument* doc = static_cast<nsIDocument*>(aPossibleDescendant);
-      nsIDocument* parentDoc = doc->GetParentDocument();
-      aPossibleDescendant = parentDoc ?
-                            parentDoc->FindContentForSubDocument(doc) : nsnull;
-    }
-    else {
-      aPossibleDescendant = parent;
-    }
+    aPossibleDescendant = GetCrossDocParentNode(aPossibleDescendant);
   } while (aPossibleDescendant);
 
   return PR_FALSE;
 }
 
 
 // static
 nsresult
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -198,16 +198,17 @@ GK_ATOM(chromemargin, "chromemargin")
 GK_ATOM(circ, "circ")
 GK_ATOM(circle, "circle")
 GK_ATOM(cite, "cite")
 GK_ATOM(_class, "class")
 GK_ATOM(classid, "classid")
 GK_ATOM(clear, "clear")
 GK_ATOM(click, "click")
 GK_ATOM(clickcount, "clickcount")
+GK_ATOM(clickthrough, "clickthrough")
 GK_ATOM(movetoclick, "movetoclick")
 GK_ATOM(clip, "clip")
 GK_ATOM(close, "close")
 GK_ATOM(closed, "closed")
 GK_ATOM(closemenu, "closemenu")
 GK_ATOM(coalesceduplicatearcs, "coalesceduplicatearcs")
 GK_ATOM(code, "code")
 GK_ATOM(codebase, "codebase")
--- a/content/events/src/nsEventStateManager.cpp
+++ b/content/events/src/nsEventStateManager.cpp
@@ -2698,16 +2698,37 @@ nsEventStateManager::DecideGestureEvent(
       }
     } //scrollableFrame
   } //ancestor chain
 
   aEvent->displayPanFeedback = displayPanFeedback;
   aEvent->panDirection = panDirection;
 }
 
+static bool
+NodeAllowsClickThrough(nsINode* aNode)
+{
+  while (aNode) {
+    if (aNode->IsElement() && aNode->AsElement()->IsXUL()) {
+      mozilla::dom::Element* element = aNode->AsElement();
+      static nsIContent::AttrValuesArray strings[] =
+        {&nsGkAtoms::always, &nsGkAtoms::never, nsnull};
+      switch (element->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::clickthrough,
+                                       strings, eCaseMatters)) {
+        case 0:
+          return true;
+        case 1:
+          return false;
+      }
+    }
+    aNode = nsContentUtils::GetCrossDocParentNode(aNode);
+  }
+  return true;
+}
+
 NS_IMETHODIMP
 nsEventStateManager::PostHandleEvent(nsPresContext* aPresContext,
                                      nsEvent *aEvent,
                                      nsIFrame* aTargetFrame,
                                      nsEventStatus* aStatus,
                                      nsIView* aView)
 {
   NS_ENSURE_ARG(aPresContext);
@@ -3161,16 +3182,29 @@ nsEventStateManager::PostHandleEvent(nsP
   case NS_MOUSE_ENTER:
     if (mCurrentTarget) {
       nsCOMPtr<nsIContent> targetContent;
       mCurrentTarget->GetContentForEvent(presContext, aEvent,
                                          getter_AddRefs(targetContent));
       SetContentState(targetContent, NS_EVENT_STATE_HOVER);
     }
     break;
+
+#ifdef XP_MACOSX
+  case NS_MOUSE_ACTIVATE:
+    if (mCurrentTarget) {
+      nsCOMPtr<nsIContent> targetContent;
+      mCurrentTarget->GetContentForEvent(presContext, aEvent,
+                                         getter_AddRefs(targetContent));
+      if (!NodeAllowsClickThrough(targetContent)) {
+        *aStatus = nsEventStatus_eConsumeNoDefault;
+      }
+    }
+    break;
+#endif
   }
 
   //Reset target frame to null to avoid mistargeting after reentrant event
   mCurrentTarget = nsnull;
 
   return ret;
 }