Bug 489127 - nodesFromRect required for better usability on mobile devices (part 1+2) [r=roc]
authorFelipe Gomes <felipc@gmail.com>
Wed, 07 Apr 2010 20:31:26 -0400
changeset 40570 a1d1e59ac1de9028a9d9b6ac2117692858b4f090
parent 40569 c417a4e9347861c0c6eabe203caf5c349e6ef8dd
child 40571 1b2e5a9c5214c2073c41b2fb1588aac3bf3d5b46
push id12673
push usermfinkle@mozilla.com
push dateThu, 08 Apr 2010 00:33:14 +0000
treeherdermozilla-central@a1d1e59ac1de [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs489127
milestone1.9.3a5pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
Bug 489127 - nodesFromRect required for better usability on mobile devices (part 1+2) [r=roc]
layout/base/nsDisplayList.cpp
layout/base/nsDisplayList.h
layout/forms/nsButtonFrameRenderer.cpp
layout/forms/nsFieldSetFrame.cpp
layout/forms/nsSelectsAreaFrame.cpp
layout/generic/nsBulletFrame.cpp
layout/generic/nsFrameSetFrame.cpp
layout/generic/nsTextFrameThebes.cpp
layout/svg/base/src/nsSVGOuterSVGFrame.cpp
layout/tables/nsTableCellFrame.cpp
layout/xul/base/src/nsBoxFrame.cpp
layout/xul/base/src/nsGroupBoxFrame.cpp
layout/xul/base/src/tree/src/nsTreeColFrame.cpp
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -880,44 +880,47 @@ nsDisplayItem* nsDisplayList::RemoveBott
 
 void nsDisplayList::DeleteAll() {
   nsDisplayItem* item;
   while ((item = RemoveBottom()) != nsnull) {
     item->~nsDisplayItem();
   }
 }
 
-nsIFrame* nsDisplayList::HitTest(nsDisplayListBuilder* aBuilder, nsPoint aPt,
-                                 nsDisplayItem::HitTestState* aState) const {
+void nsDisplayList::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
+                            nsDisplayItem::HitTestState* aState,
+                            nsTArray<nsIFrame*> *aOutFrames) const {
   PRInt32 itemBufferStart = aState->mItemBuffer.Length();
   nsDisplayItem* item;
   for (item = GetBottom(); item; item = item->GetAbove()) {
     aState->mItemBuffer.AppendElement(item);
   }
   for (PRInt32 i = aState->mItemBuffer.Length() - 1; i >= itemBufferStart; --i) {
     // Pop element off the end of the buffer. We want to shorten the buffer
     // so that recursive calls to HitTest have more buffer space.
     item = aState->mItemBuffer[i];
     aState->mItemBuffer.SetLength(i);
 
-    if (item->GetBounds(aBuilder).Contains(aPt)) {
-      nsIFrame* f = item->HitTest(aBuilder, aPt, aState);
-      // Handle the XUL 'mousethrough' feature and 'pointer-events'.
-      if (f) {
+    if (aRect.Intersects(item->GetBounds(aBuilder))) {
+      nsTArray<nsIFrame*> outFrames;
+      item->HitTest(aBuilder, aRect, aState, &outFrames);
+
+      for (PRUint32 j = 0; j < outFrames.Length(); j++) {
+        nsIFrame *f = outFrames.ElementAt(j);
+        // Handle the XUL 'mousethrough' feature and 'pointer-events'.
         if (!f->GetMouseThrough() &&
             f->GetStyleVisibility()->mPointerEvents != NS_STYLE_POINTER_EVENTS_NONE) {
-          aState->mItemBuffer.SetLength(itemBufferStart);
-          return f;
+          aOutFrames->AppendElement(f);
         }
       }
+
     }
   }
   NS_ASSERTION(aState->mItemBuffer.Length() == PRUint32(itemBufferStart),
                "How did we forget to pop some elements?");
-  return nsnull;
 }
 
 static void Sort(nsDisplayList* aList, PRInt32 aCount, nsDisplayList::SortLEQ aCmp,
                  void* aClosure) {
   if (aCount < 2)
     return;
 
   nsDisplayList list1;
@@ -1381,20 +1384,20 @@ nsDisplayWrapList::nsDisplayWrapList(nsI
   : nsDisplayItem(aFrame) {
   mList.AppendToTop(aItem);
 }
 
 nsDisplayWrapList::~nsDisplayWrapList() {
   mList.DeleteAll();
 }
 
-nsIFrame*
-nsDisplayWrapList::HitTest(nsDisplayListBuilder* aBuilder, nsPoint aPt,
-                           HitTestState* aState) {
-  return mList.HitTest(aBuilder, aPt, aState);
+void
+nsDisplayWrapList::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
+                           HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) {
+  mList.HitTest(aBuilder, aRect, aState, aOutFrames);
 }
 
 nsRect
 nsDisplayWrapList::GetBounds(nsDisplayListBuilder* aBuilder) {
   return mList.GetBounds(aBuilder);
 }
 
 PRBool
@@ -1880,62 +1883,81 @@ PRBool nsDisplayTransform::ComputeVisibi
   return PR_TRUE;
 }
 
 #ifdef DEBUG_HIT
 #include <time.h>
 #endif
 
 /* HitTest does some fun stuff with matrix transforms to obtain the answer. */
-nsIFrame *nsDisplayTransform::HitTest(nsDisplayListBuilder *aBuilder,
-                                      nsPoint aPt,
-                                      HitTestState *aState)
+void nsDisplayTransform::HitTest(nsDisplayListBuilder *aBuilder,
+                                 const nsRect& aRect,
+                                 HitTestState *aState,
+                                 nsTArray<nsIFrame*> *aOutFrames)
 {
   /* Here's how this works:
    * 1. Get the matrix.  If it's singular, abort (clearly we didn't hit
    *    anything).
    * 2. Invert the matrix.
-   * 3. Use it to transform the point into the correct space.
-   * 4. Pass that point down through to the list's version of HitTest.
+   * 3. Use it to transform the rect into the correct space.
+   * 4. Pass that rect down through to the list's version of HitTest.
    */
   float factor = nsPresContext::AppUnitsPerCSSPixel();
   gfxMatrix matrix =
     GetResultingTransformMatrix(mFrame, aBuilder->ToReferenceFrame(mFrame),
                                 factor, nsnull);
   if (matrix.IsSingular())
-    return nsnull;
+    return;
 
   /* We want to go from transformed-space to regular space.
    * Thus we have to invert the matrix, which normally does
    * the reverse operation (e.g. regular->transformed)
    */
   matrix.Invert();
 
   /* Now, apply the transform and pass it down the channel. */
-  gfxPoint result = matrix.Transform(gfxPoint(NSAppUnitsToFloatPixels(aPt.x, factor),
-                                              NSAppUnitsToFloatPixels(aPt.y, factor)));
+  nsRect resultingRect;
+  if (aRect.width == 1 && aRect.height == 1) {
+    gfxPoint point = matrix.Transform(gfxPoint(NSAppUnitsToFloatPixels(aRect.x, factor),
+                                               NSAppUnitsToFloatPixels(aRect.y, factor)));
+
+    resultingRect = nsRect(NSFloatPixelsToAppUnits(float(point.x), factor),
+                           NSFloatPixelsToAppUnits(float(point.y), factor),
+                           1, 1);
+
+  } else {
+    gfxRect originalRect(NSAppUnitsToFloatPixels(aRect.x, factor),
+                         NSAppUnitsToFloatPixels(aRect.y, factor),
+                         NSAppUnitsToFloatPixels(aRect.width, factor),
+                         NSAppUnitsToFloatPixels(aRect.height, factor));
+
+    gfxRect rect = matrix.TransformBounds(originalRect);
+
+    resultingRect = nsRect(NSFloatPixelsToAppUnits(float(rect.X()), factor),
+                           NSFloatPixelsToAppUnits(float(rect.Y()), factor),
+                           NSFloatPixelsToAppUnits(float(rect.Width()), factor),
+                           NSFloatPixelsToAppUnits(float(rect.Height()), factor));
+  }
+  
 
 #ifdef DEBUG_HIT
   printf("Frame: %p\n", dynamic_cast<void *>(mFrame));
-  printf("  Untransformed point: (%f, %f)\n", result.x, result.y);
+  printf("  Untransformed point: (%f, %f)\n", resultingRect.X(), resultingRect.Y());
+  PRUint32 originalFrameCount = aOutFrames.Length();
 #endif
 
-  nsIFrame* resultFrame =
-    mStoredList.HitTest(aBuilder,
-                        nsPoint(NSFloatPixelsToAppUnits(float(result.x), factor),
-                                NSFloatPixelsToAppUnits(float(result.y), factor)), aState);
-  
+  mStoredList.HitTest(aBuilder, resultingRect, aState, aOutFrames);
+
 #ifdef DEBUG_HIT
-  if (resultFrame)
-    printf("  Hit!  Time: %f, frame: %p\n", static_cast<double>(clock()),
-           dynamic_cast<void *>(resultFrame));
+  if (originalFrameCount != aOutFrames.Length())
+    printf("  Hit! Time: %f, first frame: %p\n", static_cast<double>(clock()),
+           dynamic_cast<void *>(aOutFrames.ElementAt(0)));
   printf("=== end of hit test ===\n");
 #endif
 
-  return resultFrame;
 }
 
 /* The bounding rectangle for the object is the overflow rectangle translated
  * by the reference point.
  */
 nsRect nsDisplayTransform::GetBounds(nsDisplayListBuilder *aBuilder)
 {
   return mFrame->GetOverflowRect() + aBuilder->ToReferenceFrame(mFrame);
@@ -2080,24 +2102,25 @@ nsDisplaySVGEffects::~nsDisplaySVGEffect
 }
 #endif
 
 PRBool nsDisplaySVGEffects::IsOpaque(nsDisplayListBuilder* aBuilder)
 {
   return PR_FALSE;
 }
 
-nsIFrame*
-nsDisplaySVGEffects::HitTest(nsDisplayListBuilder* aBuilder, nsPoint aPt,
-                             HitTestState* aState)
+void
+nsDisplaySVGEffects::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
+                             HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames)
 {
-  if (!nsSVGIntegrationUtils::HitTestFrameForEffects(mEffectsFrame,
-          aPt - aBuilder->ToReferenceFrame(mEffectsFrame)))
-    return nsnull;
-  return mList.HitTest(aBuilder, aPt, aState);
+  nsPoint rectCenter(aRect.x + aRect.width / 2, aRect.y + aRect.height / 2);
+  if (nsSVGIntegrationUtils::HitTestFrameForEffects(mEffectsFrame,
+      rectCenter - aBuilder->ToReferenceFrame(mEffectsFrame))) {
+    mList.HitTest(aBuilder, aRect, aState, aOutFrames);
+  }
 }
 
 void nsDisplaySVGEffects::Paint(nsDisplayListBuilder* aBuilder,
                                 nsIRenderingContext* aCtx)
 {
   nsSVGIntegrationUtils::PaintFramesWithEffects(aCtx,
           mEffectsFrame, mVisibleRect, aBuilder, &mList);
 }
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -510,18 +510,18 @@ public:
    * This is called after we've constructed a display list for event handling.
    * When this is called, we've already ensured that aPt is in the item's bounds.
    * 
    * @param aState must point to a HitTestState. If you don't have one,
    * just create one with the default constructor and pass it in.
    * @return the frame that the point is considered over, or nsnull if
    * this is not over any frame
    */
-  virtual nsIFrame* HitTest(nsDisplayListBuilder* aBuilder, nsPoint aPt,
-                            HitTestState* aState) { return nsnull; }
+  virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
+                       HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) {}
   /**
    * @return the frame that this display item is based on. This is used to sort
    * items by z-index and content order and for some other uses. For some items
    * that wrap item lists, this could return nsnull because there is no single
    * underlying frame; for leaf items it will never return nsnull.
    */
   inline nsIFrame* GetUnderlyingFrame() { return mFrame; }
   /**
@@ -866,18 +866,19 @@ public:
   /**
    * Get the bounds. Takes the union of the bounds of all children.
    */
   nsRect GetBounds(nsDisplayListBuilder* aBuilder) const;
   /**
    * Find the topmost display item that returns a non-null frame, and return
    * the frame.
    */
-  nsIFrame* HitTest(nsDisplayListBuilder* aBuilder, nsPoint aPt,
-                    nsDisplayItem::HitTestState* aState) const;
+  void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
+               nsDisplayItem::HitTestState* aState,
+               nsTArray<nsIFrame*> *aOutFrames) const;
 
   /**
    * This class represents a sublist of consecutive items in an nsDisplayList.
    * The first item in the sublist is mStartItem and the last item
    * is the item before mEndItem.
    * 
    * These sublists are themselves organized into a linked list of all
    * the ItemGroups associated with a given layer, via mNextItemsForLayer.
@@ -1305,18 +1306,21 @@ public:
     MOZ_COUNT_CTOR(nsDisplayBackground);
   }
 #ifdef NS_BUILD_REFCNT_LOGGING
   virtual ~nsDisplayBackground() {
     MOZ_COUNT_DTOR(nsDisplayBackground);
   }
 #endif
 
-  virtual nsIFrame* HitTest(nsDisplayListBuilder* aBuilder, nsPoint aPt,
-                            HitTestState* aState) { return mFrame; }
+  virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
+                       HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames)
+  {
+    aOutFrames->AppendElement(mFrame);
+  }
   virtual PRBool IsOpaque(nsDisplayListBuilder* aBuilder);
   virtual PRBool IsVaryingRelativeToMovingFrame(nsDisplayListBuilder* aBuilder);
   virtual PRBool IsUniform(nsDisplayListBuilder* aBuilder);
   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder);
   virtual void Paint(nsDisplayListBuilder* aBuilder, nsIRenderingContext* aCtx);
   NS_DISPLAY_DECL_NAME("Background")
 private:
     /* Used to cache mFrame->IsThemed() since it isn't a cheap call */
@@ -1404,18 +1408,21 @@ public:
     MOZ_COUNT_CTOR(nsDisplayEventReceiver);
   }
 #ifdef NS_BUILD_REFCNT_LOGGING
   virtual ~nsDisplayEventReceiver() {
     MOZ_COUNT_DTOR(nsDisplayEventReceiver);
   }
 #endif
 
-  virtual nsIFrame* HitTest(nsDisplayListBuilder* aBuilder, nsPoint aPt,
-                            HitTestState* aState) { return mFrame; }
+  virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
+                       HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames)
+  {
+    aOutFrames->AppendElement(mFrame);
+  }
   NS_DISPLAY_DECL_NAME("EventReceiver")
 };
 
 /**
  * A class that lets you wrap a display list as a display item.
  * 
  * GetUnderlyingFrame() is troublesome for wrapped lists because if the wrapped
  * list has many items, it's not clear which one has the 'underlying frame'.
@@ -1435,18 +1442,18 @@ class nsDisplayWrapList : public nsDispl
 public:
   /**
    * Takes all the items from aList and puts them in our list.
    */
   nsDisplayWrapList(nsIFrame* aFrame, nsDisplayList* aList);
   nsDisplayWrapList(nsIFrame* aFrame, nsDisplayItem* aItem);
   virtual ~nsDisplayWrapList();
   virtual Type GetType() { return TYPE_WRAPLIST; }
-  virtual nsIFrame* HitTest(nsDisplayListBuilder* aBuilder, nsPoint aPt,
-                            HitTestState* aState);
+  virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
+                       HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames);
   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder);
   virtual PRBool IsOpaque(nsDisplayListBuilder* aBuilder);
   virtual PRBool IsUniform(nsDisplayListBuilder* aBuilder);
   virtual PRBool IsVaryingRelativeToMovingFrame(nsDisplayListBuilder* aBuilder);
   virtual void Paint(nsDisplayListBuilder* aBuilder, nsIRenderingContext* aCtx);
   virtual PRBool ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                    nsRegion* aVisibleRegion,
                                    nsRegion* aVisibleRegionBeforeMove);
@@ -1582,18 +1589,18 @@ class nsDisplaySVGEffects : public nsDis
 public:
   nsDisplaySVGEffects(nsIFrame* aFrame, nsDisplayList* aList);
 #ifdef NS_BUILD_REFCNT_LOGGING
   virtual ~nsDisplaySVGEffects();
 #endif
   
   virtual Type GetType() { return TYPE_SVG_EFFECTS; }
   virtual PRBool IsOpaque(nsDisplayListBuilder* aBuilder);
-  virtual nsIFrame* HitTest(nsDisplayListBuilder* aBuilder, nsPoint aPt,
-                            HitTestState* aState);
+  virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
+                       HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames);
   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder) {
     return mBounds + aBuilder->ToReferenceFrame(mEffectsFrame);
   }
   virtual void Paint(nsDisplayListBuilder* aBuilder, nsIRenderingContext* aCtx);
   virtual PRBool ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                    nsRegion* aVisibleRegion,
                                    nsRegion* aVisibleRegionBeforeMove);  
   virtual PRBool TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem);
@@ -1639,18 +1646,18 @@ public:
   {
     return TYPE_TRANSFORM;
   }
 
 #ifdef NS_DEBUG
   nsDisplayWrapList* GetStoredList() { return &mStoredList; }
 #endif
 
-  virtual nsIFrame* HitTest(nsDisplayListBuilder *aBuilder, nsPoint aPt,
-                            HitTestState *aState);
+  virtual void HitTest(nsDisplayListBuilder *aBuilder, const nsRect& aRect,
+                       HitTestState *aState, nsTArray<nsIFrame*> *aOutFrames);
   virtual nsRect GetBounds(nsDisplayListBuilder *aBuilder);
   virtual PRBool IsOpaque(nsDisplayListBuilder *aBuilder);
   virtual PRBool IsUniform(nsDisplayListBuilder *aBuilder);
   virtual void   Paint(nsDisplayListBuilder *aBuilder,
                        nsIRenderingContext *aCtx);
   virtual PRBool ComputeVisibility(nsDisplayListBuilder *aBuilder,
                                    nsRegion *aVisibleRegion,
                                    nsRegion *aVisibleRegionBeforeMove);
--- a/layout/forms/nsButtonFrameRenderer.cpp
+++ b/layout/forms/nsButtonFrameRenderer.cpp
@@ -135,19 +135,19 @@ public:
     MOZ_COUNT_CTOR(nsDisplayButtonBorderBackground);
   }
 #ifdef NS_BUILD_REFCNT_LOGGING
   virtual ~nsDisplayButtonBorderBackground() {
     MOZ_COUNT_DTOR(nsDisplayButtonBorderBackground);
   }
 #endif  
   
-  virtual nsIFrame* HitTest(nsDisplayListBuilder* aBuilder, nsPoint aPt,
-                            HitTestState* aState) {
-    return mFrame;
+  virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
+                       HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) {
+    aOutFrames->AppendElement(mFrame);
   }
   virtual void Paint(nsDisplayListBuilder* aBuilder,
                      nsIRenderingContext* aCtx);
   NS_DISPLAY_DECL_NAME("ButtonBorderBackground")
 private:
   nsButtonFrameRenderer* mBFR;
 };
 
--- a/layout/forms/nsFieldSetFrame.cpp
+++ b/layout/forms/nsFieldSetFrame.cpp
@@ -181,30 +181,30 @@ public:
     MOZ_COUNT_CTOR(nsDisplayFieldSetBorderBackground);
   }
 #ifdef NS_BUILD_REFCNT_LOGGING
   virtual ~nsDisplayFieldSetBorderBackground() {
     MOZ_COUNT_DTOR(nsDisplayFieldSetBorderBackground);
   }
 #endif
 
-  virtual nsIFrame* HitTest(nsDisplayListBuilder* aBuilder, nsPoint aPt,
-                            HitTestState* aState);
+  virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
+                       HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames);
   virtual void Paint(nsDisplayListBuilder* aBuilder,
                      nsIRenderingContext* aCtx);
   NS_DISPLAY_DECL_NAME("FieldSetBorderBackground")
 };
 
-nsIFrame* nsDisplayFieldSetBorderBackground::HitTest(nsDisplayListBuilder* aBuilder,
-    nsPoint aPt, HitTestState* aState)
+void nsDisplayFieldSetBorderBackground::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
+                                                HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames)
 {
   // aPt is guaranteed to be in this item's bounds. We do the hit test based on the
   // frame bounds even though our background doesn't cover the whole frame.
   // It's not clear whether this is correct.
-  return mFrame;
+  aOutFrames->AppendElement(mFrame);
 }
 
 void
 nsDisplayFieldSetBorderBackground::Paint(nsDisplayListBuilder* aBuilder,
                                          nsIRenderingContext* aCtx)
 {
   static_cast<nsFieldSetFrame*>(mFrame)->
     PaintBorderBackground(*aCtx, aBuilder->ToReferenceFrame(mFrame),
--- a/layout/forms/nsSelectsAreaFrame.cpp
+++ b/layout/forms/nsSelectsAreaFrame.cpp
@@ -91,42 +91,44 @@ nsSelectsAreaFrame::IsOptionElementFrame
  * REVIEW: This is what nsSelectsAreaFrame::GetFrameForPoint used to do
  */
 class nsDisplayOptionEventGrabber : public nsDisplayWrapList {
 public:
   nsDisplayOptionEventGrabber(nsIFrame* aFrame, nsDisplayItem* aItem)
     : nsDisplayWrapList(aFrame, aItem) {}
   nsDisplayOptionEventGrabber(nsIFrame* aFrame, nsDisplayList* aList)
     : nsDisplayWrapList(aFrame, aList) {}
-  virtual nsIFrame* HitTest(nsDisplayListBuilder* aBuilder, nsPoint aPt,
-                            HitTestState* aState);
+  virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
+                       HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames);
   NS_DISPLAY_DECL_NAME("OptionEventGrabber")
 
   virtual nsDisplayWrapList* WrapWithClone(nsDisplayListBuilder* aBuilder,
                                            nsDisplayItem* aItem);
 };
 
-nsIFrame* nsDisplayOptionEventGrabber::HitTest(nsDisplayListBuilder* aBuilder,
-    nsPoint aPt, HitTestState* aState)
+void nsDisplayOptionEventGrabber::HitTest(nsDisplayListBuilder* aBuilder,
+    const nsRect& aRect, HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames)
 {
-  nsIFrame* frame = mList.HitTest(aBuilder, aPt, aState);
+  nsTArray<nsIFrame*> outFrames;
+  mList.HitTest(aBuilder, aRect, aState, &outFrames);
 
-  if (frame) {
-    nsIFrame* selectedFrame = frame;
+  for (PRUint32 i = 0; i < outFrames.Length(); i++) {
+    nsIFrame* selectedFrame = outFrames.ElementAt(i);
     while (selectedFrame &&
            !nsSelectsAreaFrame::IsOptionElementFrame(selectedFrame)) {
       selectedFrame = selectedFrame->GetParent();
     }
     if (selectedFrame) {
-      return selectedFrame;
+      aOutFrames->AppendElement(selectedFrame);
+    } else {
+      // keep the original result, which could be this frame
+      aOutFrames->AppendElement(outFrames.ElementAt(i));
     }
-    // else, keep the original result, which could be this frame
   }
 
-  return frame;
 }
 
 nsDisplayWrapList* nsDisplayOptionEventGrabber::WrapWithClone(
     nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem) {
   return new (aBuilder) nsDisplayOptionEventGrabber(aItem->GetUnderlyingFrame(), aItem);
 }
 
 class nsOptionEventGrabberWrapper : public nsDisplayWrapper
--- a/layout/generic/nsBulletFrame.cpp
+++ b/layout/generic/nsBulletFrame.cpp
@@ -189,18 +189,20 @@ public:
     MOZ_COUNT_CTOR(nsDisplayBullet);
   }
 #ifdef NS_BUILD_REFCNT_LOGGING
   virtual ~nsDisplayBullet() {
     MOZ_COUNT_DTOR(nsDisplayBullet);
   }
 #endif
 
-  virtual nsIFrame* HitTest(nsDisplayListBuilder* aBuilder, nsPoint aPt,
-                            HitTestState* aState) { return mFrame; }
+  virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
+                       HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) {
+    aOutFrames->AppendElement(mFrame);
+  }
   virtual void Paint(nsDisplayListBuilder* aBuilder,
                      nsIRenderingContext* aCtx);
   NS_DISPLAY_DECL_NAME("Bullet")
 };
 
 void nsDisplayBullet::Paint(nsDisplayListBuilder* aBuilder,
                             nsIRenderingContext* aCtx)
 {
--- a/layout/generic/nsFrameSetFrame.cpp
+++ b/layout/generic/nsFrameSetFrame.cpp
@@ -1620,18 +1620,20 @@ public:
 #ifdef NS_BUILD_REFCNT_LOGGING
   virtual ~nsDisplayFramesetBorder() {
     MOZ_COUNT_DTOR(nsDisplayFramesetBorder);
   }
 #endif
 
   // REVIEW: see old GetFrameForPoint
   // Receives events in its bounds
-  virtual nsIFrame* HitTest(nsDisplayListBuilder* aBuilder, nsPoint aPt,
-                            HitTestState* aState) { return mFrame; }
+  virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
+                       HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) {
+    aOutFrames->AppendElement(mFrame);
+  }
   virtual void Paint(nsDisplayListBuilder* aBuilder,
                      nsIRenderingContext* aCtx);
   NS_DISPLAY_DECL_NAME("FramesetBorder")
 };
 
 void nsDisplayFramesetBorder::Paint(nsDisplayListBuilder* aBuilder,
                                     nsIRenderingContext* aCtx)
 {
--- a/layout/generic/nsTextFrameThebes.cpp
+++ b/layout/generic/nsTextFrameThebes.cpp
@@ -3874,19 +3874,21 @@ public:
   virtual ~nsDisplayText() {
     MOZ_COUNT_DTOR(nsDisplayText);
   }
 #endif
 
   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder) {
     return mFrame->GetOverflowRect() + aBuilder->ToReferenceFrame(mFrame);
   }
-  virtual nsIFrame* HitTest(nsDisplayListBuilder* aBuilder, nsPoint aPt,
-                            HitTestState* aState) {
-    return nsRect(aBuilder->ToReferenceFrame(mFrame), mFrame->GetSize()).Contains(aPt) ? mFrame : nsnull;
+  virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
+                       HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) {
+    if (nsRect(aBuilder->ToReferenceFrame(mFrame), mFrame->GetSize()).Intersects(aRect)) {
+      aOutFrames->AppendElement(mFrame);
+    }
   }
   virtual void Paint(nsDisplayListBuilder* aBuilder,
                      nsIRenderingContext* aCtx);
   NS_DISPLAY_DECL_NAME("Text")
 };
 
 void
 nsDisplayText::Paint(nsDisplayListBuilder* aBuilder,
--- a/layout/svg/base/src/nsSVGOuterSVGFrame.cpp
+++ b/layout/svg/base/src/nsSVGOuterSVGFrame.cpp
@@ -425,29 +425,40 @@ public:
     MOZ_COUNT_CTOR(nsDisplaySVG);
   }
 #ifdef NS_BUILD_REFCNT_LOGGING
   virtual ~nsDisplaySVG() {
     MOZ_COUNT_DTOR(nsDisplaySVG);
   }
 #endif
 
-  virtual nsIFrame* HitTest(nsDisplayListBuilder* aBuilder, nsPoint aPt,
-                            HitTestState* aState);
+  virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
+                       HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames);
   virtual void Paint(nsDisplayListBuilder* aBuilder,
                      nsIRenderingContext* aCtx);
   NS_DISPLAY_DECL_NAME("SVGEventReceiver")
 };
 
-nsIFrame*
-nsDisplaySVG::HitTest(nsDisplayListBuilder* aBuilder, nsPoint aPt,
-                      HitTestState* aState)
+void
+nsDisplaySVG::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
+                      HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames)
 {
-  return static_cast<nsSVGOuterSVGFrame*>(mFrame)->
-    GetFrameForPoint(aPt - aBuilder->ToReferenceFrame(mFrame));
+  nsRect rectAtOrigin = aRect - aBuilder->ToReferenceFrame(mFrame);
+  nsRect thisRect(nsPoint(0,0), static_cast<nsSVGOuterSVGFrame*>(mFrame)->GetSize());
+  if (!thisRect.Intersects(rectAtOrigin))
+    return;
+
+  nsPoint rectCenter(rectAtOrigin.x + rectAtOrigin.width / 2,
+                     rectAtOrigin.y + rectAtOrigin.height / 2);
+
+  nsIFrame* frame = nsSVGUtils::HitTestChildren(static_cast<nsSVGOuterSVGFrame*>(mFrame),
+                                                rectCenter);
+  if (frame) {
+    aOutFrames->AppendElement(frame);
+  }
 }
 
 void
 nsDisplaySVG::Paint(nsDisplayListBuilder* aBuilder,
                     nsIRenderingContext* aCtx)
 {
   static_cast<nsSVGOuterSVGFrame*>(mFrame)->
     Paint(*aCtx, mVisibleRect, aBuilder->ToReferenceFrame(mFrame));
--- a/layout/tables/nsTableCellFrame.cpp
+++ b/layout/tables/nsTableCellFrame.cpp
@@ -389,18 +389,20 @@ public:
     MOZ_COUNT_CTOR(nsDisplayTableCellBackground);
   }
 #ifdef NS_BUILD_REFCNT_LOGGING
   virtual ~nsDisplayTableCellBackground() {
     MOZ_COUNT_DTOR(nsDisplayTableCellBackground);
   }
 #endif
 
-  virtual nsIFrame* HitTest(nsDisplayListBuilder* aBuilder, nsPoint aPt,
-                            HitTestState* aState) { return mFrame; }
+  virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
+                       HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) {
+    aOutFrames->AppendElement(mFrame);
+  }
   virtual void Paint(nsDisplayListBuilder* aBuilder,
                      nsIRenderingContext* aCtx);
   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder);
 
   NS_DISPLAY_DECL_NAME("TableCellBackground")
 };
 
 void nsDisplayTableCellBackground::Paint(nsDisplayListBuilder* aBuilder,
--- a/layout/xul/base/src/nsBoxFrame.cpp
+++ b/layout/xul/base/src/nsBoxFrame.cpp
@@ -1259,21 +1259,22 @@ public:
     MOZ_COUNT_CTOR(nsDisplayXULDebug);
   }
 #ifdef NS_BUILD_REFCNT_LOGGING
   virtual ~nsDisplayXULDebug() {
     MOZ_COUNT_DTOR(nsDisplayXULDebug);
   }
 #endif
 
-  virtual nsIFrame* HitTest(nsDisplayListBuilder* aBuilder, nsPoint aPt,
-                            HitTestState* aState) {
+  virtual void HitTest(nsDisplayListBuilder* aBuilder, nsRect aRect,
+                       HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) {
+    nsPoint rectCenter(aRect.x + aRect.width / 2, aRect.y + aRect.height / 2);
     static_cast<nsBoxFrame*>(mFrame)->
-      DisplayDebugInfoFor(this, aPt - aBuilder->ToReferenceFrame(mFrame));
-    return PR_TRUE;
+      DisplayDebugInfoFor(this, rectCenter - aBuilder->ToReferenceFrame(mFrame));
+    aOutFrames->AppendElement(this);
   }
   virtual void Paint(nsDisplayListBuilder* aBuilder
                      nsIRenderingContext* aCtx);
   NS_DISPLAY_DECL_NAME("XULDebug")
 };
 
 void
 nsDisplayXULDebug::Paint(nsDisplayListBuilder* aBuilder,
@@ -2131,41 +2132,48 @@ nsBoxFrame::RelayoutChildAtOrdinal(nsBox
 class nsDisplayXULEventRedirector : public nsDisplayWrapList {
 public:
   nsDisplayXULEventRedirector(nsIFrame* aFrame, nsDisplayItem* aItem,
                               nsIFrame* aTargetFrame)
     : nsDisplayWrapList(aFrame, aItem), mTargetFrame(aTargetFrame) {}
   nsDisplayXULEventRedirector(nsIFrame* aFrame, nsDisplayList* aList,
                               nsIFrame* aTargetFrame)
     : nsDisplayWrapList(aFrame, aList), mTargetFrame(aTargetFrame) {}
-  virtual nsIFrame* HitTest(nsDisplayListBuilder* aBuilder, nsPoint aPt,
-                            HitTestState* aState);
+  virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
+                       HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames);
   NS_DISPLAY_DECL_NAME("XULEventRedirector")
 private:
   nsIFrame* mTargetFrame;
 };
 
-nsIFrame* nsDisplayXULEventRedirector::HitTest(nsDisplayListBuilder* aBuilder,
-    nsPoint aPt, HitTestState* aState)
+void nsDisplayXULEventRedirector::HitTest(nsDisplayListBuilder* aBuilder,
+    const nsRect& aRect, HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames)
 {
-  nsIFrame* frame = mList.HitTest(aBuilder, aPt, aState);
-  if (!frame)
-    return nsnull;
+  nsTArray<nsIFrame*> outFrames;
+  mList.HitTest(aBuilder, aRect, aState, &outFrames);
+
+  PRInt32 originalLength = aOutFrames->Length();
+
+  for (PRUint32 i = 0; i < originalLength; i++) {
 
-  for (nsIContent* content = frame->GetContent();
-       content && content != mTargetFrame->GetContent();
-       content = content->GetParent()) {
-    if (content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::allowevents,
-                             nsGkAtoms::_true, eCaseMatters)) {
-      // Events are allowed on 'frame', so let it go.
-      return frame;
+    for (nsIContent* content = outFrames.ElementAt(i)->GetContent();
+         content && content != mTargetFrame->GetContent();
+         content = content->GetParent()) {
+      if (content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::allowevents,
+                               nsGkAtoms::_true, eCaseMatters)) {
+        // Events are allowed on 'frame', so let it go.
+        aOutFrames->AppendElement(outFrames.ElementAt(i));
+      }
     }
+
   }
-  // Treat it as a hit on the target frame itself.
-  return mTargetFrame;
+  // If no hits were found, treat it as a hit on the target frame itself
+  if (aOutFrames->Length() == originalLength) {
+    aOutFrames->AppendElement(mTargetFrame);
+  }
 }
 
 class nsXULEventRedirectorWrapper : public nsDisplayWrapper
 {
 public:
   nsXULEventRedirectorWrapper(nsIFrame* aTargetFrame)
       : mTargetFrame(aTargetFrame) {}
   virtual nsDisplayItem* WrapList(nsDisplayListBuilder* aBuilder,
--- a/layout/xul/base/src/nsGroupBoxFrame.cpp
+++ b/layout/xul/base/src/nsGroupBoxFrame.cpp
@@ -111,18 +111,20 @@ public:
     MOZ_COUNT_CTOR(nsDisplayXULGroupBackground);
   }
 #ifdef NS_BUILD_REFCNT_LOGGING
   virtual ~nsDisplayXULGroupBackground() {
     MOZ_COUNT_DTOR(nsDisplayXULGroupBackground);
   }
 #endif
 
-  virtual nsIFrame* HitTest(nsDisplayListBuilder* aBuilder, nsPoint aPt,
-                            HitTestState* aState) { return mFrame; }
+  virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
+                       HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) {
+    aOutFrames->AppendElement(mFrame);
+  }
   virtual void Paint(nsDisplayListBuilder* aBuilder,
                      nsIRenderingContext* aCtx);
   NS_DISPLAY_DECL_NAME("XULGroupBackground")
 };
 
 void
 nsDisplayXULGroupBackground::Paint(nsDisplayListBuilder* aBuilder,
                                    nsIRenderingContext* aCtx)
--- a/layout/xul/base/src/tree/src/nsTreeColFrame.cpp
+++ b/layout/xul/base/src/tree/src/nsTreeColFrame.cpp
@@ -94,34 +94,35 @@ public:
     MOZ_COUNT_CTOR(nsDisplayXULTreeColSplitterTarget);
   }
 #ifdef NS_BUILD_REFCNT_LOGGING
   virtual ~nsDisplayXULTreeColSplitterTarget() {
     MOZ_COUNT_DTOR(nsDisplayXULTreeColSplitterTarget);
   }
 #endif
 
-  virtual nsIFrame* HitTest(nsDisplayListBuilder* aBuilder, nsPoint aPt,
-                            HitTestState* aState);
+  virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
+                       HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames);
   NS_DISPLAY_DECL_NAME("XULTreeColSplitterTarget")
 };
 
-nsIFrame* 
-nsDisplayXULTreeColSplitterTarget::HitTest(nsDisplayListBuilder* aBuilder,
-                                           nsPoint aPt, HitTestState* aState)
+void
+nsDisplayXULTreeColSplitterTarget::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
+                                           HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames)
 {
-  nsPoint pt = aPt - aBuilder->ToReferenceFrame(mFrame);
-  // If we are in either the first 4 pixels or the last 4 pixels, we're going to
+  nsRect rect = aRect - aBuilder->ToReferenceFrame(mFrame);
+  // If we are in either in the first 4 pixels or the last 4 pixels, we're going to
   // do something really strange.  Check for an adjacent splitter.
   PRBool left = PR_FALSE;
   PRBool right = PR_FALSE;
-  if (mFrame->GetSize().width - nsPresContext::CSSPixelsToAppUnits(4) <= pt.x)
+  if (mFrame->GetSize().width - nsPresContext::CSSPixelsToAppUnits(4) <= rect.XMost()) {
     right = PR_TRUE;
-  else if (nsPresContext::CSSPixelsToAppUnits(4) > pt.x)
+  } else if (nsPresContext::CSSPixelsToAppUnits(4) > rect.x) {
     left = PR_TRUE;
+  }
 
   // Swap left and right for RTL trees in order to find the correct splitter
   if (mFrame->GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) {
     PRBool tmp = left;
     left = right;
     right = tmp;
   }
 
@@ -130,21 +131,20 @@ nsDisplayXULTreeColSplitterTarget::HitTe
     nsIFrame* child;
     if (left)
       child = mFrame->GetPrevSibling();
     else
       child = mFrame->GetNextSibling();
 
     if (child && child->GetContent()->NodeInfo()->Equals(nsGkAtoms::splitter,
                                                          kNameSpaceID_XUL)) {
-      return child;
+      aOutFrames->AppendElement(child);
     }
   }
-  
-  return nsnull;
+
 }
 
 nsresult
 nsTreeColFrame::BuildDisplayListForChildren(nsDisplayListBuilder*   aBuilder,
                                             const nsRect&           aDirtyRect,
                                             const nsDisplayListSet& aLists)
 {
   if (!aBuilder->IsForEventDelivery())