Bug 1436431 Part 1: Extend PresShell::GetRectVisibility to consider the target frame's scrollable ancestors. draft
authorBrad Werth <bwerth@mozilla.com>
Fri, 30 Mar 2018 13:04:42 -0700
changeset 790312 f505ccea8d917e45611a9c4f89d7042f07628c13
parent 790147 d2a4720d1c334b64d88a51678758c27ba8f03c89
child 790313 0e7a7a22cf18e9721bf89f8064770b53c48e6283
push id108493
push userbwerth@mozilla.com
push dateTue, 01 May 2018 23:23:11 +0000
bugs1436431
milestone61.0a1
Bug 1436431 Part 1: Extend PresShell::GetRectVisibility to consider the target frame's scrollable ancestors. MozReview-Commit-ID: JIP8ZRNu7fV
layout/base/PresShell.cpp
layout/base/nsIPresShell.h
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -3762,23 +3762,47 @@ PresShell::GetRectVisibility(nsIFrame* a
   if (sf) {
     scrollPortRect = sf->GetScrollPortRect();
     nsIFrame* f = do_QueryFrame(sf);
     scrollPortRect += f->GetOffsetTo(rootFrame);
   } else {
     scrollPortRect = nsRect(nsPoint(0,0), rootFrame->GetSize());
   }
 
+  // scrollPortRect has the viewport visible area relative to rootFrame.
+  nsRect visibleAreaRect(scrollPortRect);
+  // Find the intersection of this and the frame's ancestor scrollable
+  // frames. We walk the whole ancestor chain to find all the scrollable
+  // frames.
+  nsIScrollableFrame* scrollAncestorFrame =
+    nsLayoutUtils::GetNearestScrollableFrame(aFrame,
+      nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN);
+  while (scrollAncestorFrame) {
+    nsRect scrollAncestorRect = scrollAncestorFrame->GetScrollPortRect();
+    nsIFrame* f = do_QueryFrame(scrollAncestorFrame);
+    scrollAncestorRect += f->GetOffsetTo(rootFrame);
+
+    visibleAreaRect = visibleAreaRect.Intersect(scrollAncestorRect);
+
+    // Continue up the chain.
+    scrollAncestorFrame =
+      nsLayoutUtils::GetNearestScrollableFrame(f->GetParent(),
+        nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN);
+  }
+
+  // aRect is in the aFrame coordinate space, so bring it into rootFrame
+  // coordinate space.
   nsRect r = aRect + aFrame->GetOffsetTo(rootFrame);
   // If aRect is entirely visible then we don't need to ensure that
   // at least aMinTwips of it is visible
-  if (scrollPortRect.Contains(r))
+  if (visibleAreaRect.Contains(r)) {
     return nsRectVisibility_kVisible;
-
-  nsRect insetRect = scrollPortRect;
+  }
+
+  nsRect insetRect = visibleAreaRect;
   insetRect.Deflate(aMinTwips, aMinTwips);
   if (r.YMost() <= insetRect.y)
     return nsRectVisibility_kAboveViewport;
   if (r.y >= insetRect.YMost())
     return nsRectVisibility_kBelowViewport;
   if (r.XMost() <= insetRect.x)
     return nsRectVisibility_kLeftOfViewport;
   if (r.x >= insetRect.XMost())
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -825,27 +825,33 @@ public:
   virtual bool ScrollFrameRectIntoView(nsIFrame*     aFrame,
                                        const nsRect& aRect,
                                        ScrollAxis    aVertical,
                                        ScrollAxis    aHorizontal,
                                        uint32_t      aFlags) = 0;
 
   /**
    * Determine if a rectangle specified in the frame's coordinate system
-   * intersects the viewport "enough" to be considered visible.
+   * intersects "enough" with the viewport to be considered visible. This
+   * is not a strict test against the viewport -- it's a test against
+   * the intersection of the viewport and the frame's ancestor scrollable
+   * frames. If it doesn't intersect enough, return a value indicating
+   * which direction the frame's topmost ancestor scrollable frame would
+   * need to be scrolled to bring the frame into view.
    * @param aFrame frame that aRect coordinates are specified relative to
    * @param aRect rectangle in twips to test for visibility
-   * @param aMinTwips is the minimum distance in from the edge of the viewport
-   *                  that an object must be to be counted visible
+   * @param aMinTwips is the minimum distance in from the edge of the
+   *                  visible area that an object must be to be counted
+   *                  visible
    * @return nsRectVisibility_kVisible if the rect is visible
    *         nsRectVisibility_kAboveViewport
    *         nsRectVisibility_kBelowViewport
    *         nsRectVisibility_kLeftOfViewport
-   *         nsRectVisibility_kRightOfViewport rectangle is outside the viewport
-   *         in the specified direction
+   *         nsRectVisibility_kRightOfViewport rectangle is outside the
+   *         topmost ancestor scrollable frame in the specified direction
    */
   virtual nsRectVisibility GetRectVisibility(nsIFrame *aFrame,
                                              const nsRect &aRect,
                                              nscoord aMinTwips) const = 0;
 
   /**
    * Suppress notification of the frame manager that frames are
    * being destroyed.