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 id14423
push usermstange@themasta.com
push dateFri, 16 Jul 2010 13:03:44 +0000
treeherdermozilla-central@ced41ebe9e75 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs392188
milestone2.0b2pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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;
 }