Bug 1302470 Part 4: Augment the IsRangeVisible function to test for opaque overdraw. r?mstange draft
authorBrad Werth <bwerth@mozilla.com>
Wed, 09 Nov 2016 16:08:56 -0800
changeset 436895 6cc2c86e0e6f7d137e749bf0f1fc1caca8d2d577
parent 436894 d6894e46ac6d084e97546e032a83662e2f95fd10
child 536473 3c66ac4b57f5dacb3a8255e42e0ce3eb76a30c5e
push id35233
push userbwerth@mozilla.com
push dateThu, 10 Nov 2016 00:14:44 +0000
reviewersmstange
bugs1302470
milestone52.0a1
Bug 1302470 Part 4: Augment the IsRangeVisible function to test for opaque overdraw. r?mstange MozReview-Commit-ID: F2qbu0WLl9O
toolkit/components/typeaheadfind/nsTypeAheadFind.cpp
--- a/toolkit/components/typeaheadfind/nsTypeAheadFind.cpp
+++ b/toolkit/components/typeaheadfind/nsTypeAheadFind.cpp
@@ -1177,63 +1177,105 @@ nsTypeAheadFind::IsRangeVisible(nsIDOMRa
   nsCOMPtr<nsIDOMRange> startPointRange = new nsRange(presShell->GetDocument());
   *aResult = IsRangeVisible(presShell, presContext, aRange, aFlushLayout,
                             getter_AddRefs(startPointRange),
                             nullptr);
   return NS_OK;
 }
 
 bool
+IsFrameVisibleInFrameStack(nsIFrame *aFrame,
+                           const nsTArray<nsIFrame*>& aOrderedFramesFrontToBack)
+{
+  // Visibility requires that aFrame appears in aOrderedFramesFrontToBack,
+  // and that overlaying frames are not entirely opaque. We stop evaluating
+  // once we reach aFrame, because we don't want frames underneath aFrame to
+  // affect the test.
+
+  // This definition is conservative; this function will return false in some
+  // cases where aFrame may actually be visible, including:
+  // a) aFrame is only partially covered by another frame.
+  // b) A frame covering aFrame is opaque but empty.
+
+  for (nsIFrame* f : aOrderedFramesFrontToBack) {
+    if (!f) {
+      continue;
+    }
+
+    if (f == aFrame) {
+      return true;
+    }
+
+    if (f->StyleEffects()->mOpacity < 1.0f) {
+      continue;
+    }
+
+    // Something fully opaque is at least partially obscuring aFrame, so
+    // aFrame is considered not visible.
+    return false;
+  }
+
+  // We didn't find aFrame, which means it wasn't visible.
+  return false;
+}
+
+bool
 nsTypeAheadFind::IsRangeVisible(nsIPresShell *aPresShell,
                                 nsPresContext *aPresContext,
                                 nsIDOMRange *aRange,
                                 bool aFlushLayout,
                                 nsIDOMRange **aFirstVisibleRange,
                                 bool *aUsesIndependentSelection)
 {
   NS_ASSERTION(aPresShell && aPresContext && aRange && aFirstVisibleRange, 
                "params are invalid");
 
-  // We need to know if the range start is visible.
-  // Otherwise, return the first visible range start 
-  // in aFirstVisibleRange
+  // We need to know if the range start and end are both visible.
+  // In all cases, return the first visible range in aFirstVisibleRange.
   aRange->CloneRange(aFirstVisibleRange);
 
   if (aFlushLayout) {
     aPresShell->FlushPendingNotifications(Flush_Layout);
   }
 
   nsCOMPtr<nsIDOMNode> node;
-  aRange->GetStartContainer(getter_AddRefs(node));
+  aRange->GetCommonAncestorContainer(getter_AddRefs(node));
 
   nsCOMPtr<nsIContent> content(do_QueryInterface(node));
-  if (!content)
+  if (!content) {
     return false;
+  }
 
   nsIFrame *frame = content->GetPrimaryFrame();
-  if (!frame)
+  if (!frame) {
     return false;  // No frame! Not visible then.
+  }
 
   // Having a primary frame doesn't mean that the range is visible inside the
   // viewport. Do a hit-test to determine that quickly and properly.
   AutoTArray<nsIFrame*,8> frames;
   nsIFrame *rootFrame = aPresShell->GetRootFrame();
   RefPtr<nsRange> range = static_cast<nsRange*>(aRange);
   RefPtr<mozilla::dom::DOMRectList> rects = range->GetClientRects(true, false);
   for (uint32_t i = 0; i < rects->Length(); ++i) {
     RefPtr<mozilla::dom::DOMRect> rect = rects->Item(i);
     nsRect r(nsPresContext::CSSPixelsToAppUnits((float)rect->X()),
              nsPresContext::CSSPixelsToAppUnits((float)rect->Y()),
              nsPresContext::CSSPixelsToAppUnits((float)rect->Width()),
              nsPresContext::CSSPixelsToAppUnits((float)rect->Height()));
     nsLayoutUtils::GetFramesForArea(rootFrame, r, frames,
       nsLayoutUtils::IGNORE_PAINT_SUPPRESSION | nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME);
   }
-  if (!frames.Length())
+  if (!frames.Length()) {
     return false;
+  }
+
+  if (!IsFrameVisibleInFrameStack(frame, frames)) {
+    return false;
+  }
 
   // Detect if we are _inside_ a text control, or something else with its own
   // selection controller.
   if (aUsesIndependentSelection) {
     *aUsesIndependentSelection = 
       (frame->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION);
   }