Bug 1302470 Part 2: Create a IsRangeRendered function to test range visibility in the display list. r=mstange,smaug
☠☠ backed out by 71218b2d1f97 ☠ ☠
authorBrad Werth <bwerth@mozilla.com>
Fri, 24 Mar 2017 14:45:41 -0700
changeset 402423 87ae43ab52ed798cec3d3970ce4a3f2fbc089980
parent 402422 a4212d9e1d0b74470916c4f751050ac80ba89497
child 402424 1d664865e8e200509288cb2eb65814c0c7ce8ab4
push id7391
push usermtabara@mozilla.com
push dateMon, 12 Jun 2017 13:08:53 +0000
treeherdermozilla-beta@2191d7f87e2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmstange, smaug
bugs1302470
milestone55.0a1
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 1302470 Part 2: Create a IsRangeRendered function to test range visibility in the display list. r=mstange,smaug MozReview-Commit-ID: u0N73luIz7
toolkit/components/typeaheadfind/nsITypeAheadFind.idl
toolkit/components/typeaheadfind/nsTypeAheadFind.cpp
toolkit/components/typeaheadfind/nsTypeAheadFind.h
--- a/toolkit/components/typeaheadfind/nsITypeAheadFind.idl
+++ b/toolkit/components/typeaheadfind/nsITypeAheadFind.idl
@@ -52,19 +52,22 @@ interface nsITypeAheadFind : nsISupports
    * the selection. */
   void setSelectionModeAndRepaint(in short toggle);
 
   /* Collapse the "found match" selection to its start.  Because not all
    * matches are owned by the same selection controller, this doesn't
    * necessarily happen automatically. */
   void collapseSelection();
 
-  /* Check if a range is visible */
+  /* Check if a range is visible using heuristics */
   boolean isRangeVisible(in nsIDOMRange aRange, in boolean aMustBeInViewPort);
 
+  /* Check if a range is actually rendered (out of viewport always false) */
+  boolean isRangeRendered(in nsIDOMRange aRange);
+
   /******************************* Attributes ******************************/
 
   readonly attribute AString searchString;
                                         // Most recent search string
   attribute boolean caseSensitive;      // Searches are case sensitive
   attribute boolean entireWord;         // Search for whole words only
   readonly attribute nsIDOMElement foundLink;
                                         // Most recent elem found, if a link
--- a/toolkit/components/typeaheadfind/nsTypeAheadFind.cpp
+++ b/toolkit/components/typeaheadfind/nsTypeAheadFind.cpp
@@ -1303,16 +1303,96 @@ nsTypeAheadFind::IsRangeVisible(nsIPresS
       (*aFirstVisibleRange)->SetStart(firstVisibleNode, startFrameOffset);
       (*aFirstVisibleRange)->SetEnd(firstVisibleNode, endFrameOffset);
     }
   }
 
   return false;
 }
 
+NS_IMETHODIMP
+nsTypeAheadFind::IsRangeRendered(nsIDOMRange *aRange,
+                                bool *aResult)
+{
+  // Jump through hoops to extract the docShell from the range.
+  nsCOMPtr<nsIDOMNode> node;
+  aRange->GetStartContainer(getter_AddRefs(node));
+  nsCOMPtr<nsIDOMDocument> document;
+  node->GetOwnerDocument(getter_AddRefs(document));
+  nsCOMPtr<mozIDOMWindowProxy> window;
+  document->GetDefaultView(getter_AddRefs(window));
+  nsCOMPtr<nsIWebNavigation> navNav (do_GetInterface(window));
+  nsCOMPtr<nsIDocShell> docShell (do_GetInterface(navNav));
+
+  // Set up the arguments needed to check if a range is visible.
+  nsCOMPtr<nsIPresShell> presShell (docShell->GetPresShell());
+  RefPtr<nsPresContext> presContext = presShell->GetPresContext();
+  *aResult = IsRangeRendered(presShell, presContext, aRange);
+  return NS_OK;
+}
+
+bool
+nsTypeAheadFind::IsRangeRendered(nsIPresShell *aPresShell,
+                                 nsPresContext *aPresContext,
+                                 nsIDOMRange *aRange)
+{
+  NS_ASSERTION(aPresShell && aPresContext && aRange,
+               "params are invalid");
+
+  nsCOMPtr<nsIDOMNode> node;
+  aRange->GetCommonAncestorContainer(getter_AddRefs(node));
+
+  nsCOMPtr<nsIContent> content(do_QueryInterface(node));
+  if (!content) {
+    return false;
+  }
+
+  nsIFrame *frame = content->GetPrimaryFrame();
+  if (!frame) {
+    return false;  // No frame! Not visible then.
+  }
+
+  if (!frame->StyleVisibility()->IsVisible()) {
+    return false;
+  }
+
+  // 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()));
+    // Append visible frames to frames array.
+    nsLayoutUtils::GetFramesForArea(rootFrame, r, frames,
+      nsLayoutUtils::IGNORE_PAINT_SUPPRESSION |
+      nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME |
+      nsLayoutUtils::ONLY_VISIBLE);
+
+    // See if any of the frames contain the content. If they do, then the range
+    // is visible. We search for the content rather than the original frame,
+    // because nsTextContinuation frames might be returned instead of the
+    // original frame.
+    for (const auto &f: frames) {
+      if (f->GetContent() == content) {
+        return true;
+      }
+    }
+
+    frames.ClearAndRetainStorage();
+  }
+
+  return false;
+}
+
 already_AddRefed<nsIPresShell>
 nsTypeAheadFind::GetPresShell()
 {
   if (!mPresShell)
     return nullptr;
 
   nsCOMPtr<nsIPresShell> shell = do_QueryReferent(mPresShell);
   if (shell) {
--- a/toolkit/components/typeaheadfind/nsTypeAheadFind.h
+++ b/toolkit/components/typeaheadfind/nsTypeAheadFind.h
@@ -43,27 +43,29 @@ protected:
 
   nsresult PrefsReset();
 
   void SaveFind();
   void PlayNotFoundSound(); 
   nsresult GetWebBrowserFind(nsIDocShell *aDocShell,
                              nsIWebBrowserFind **aWebBrowserFind);
 
-  void RangeStartsInsideLink(nsIDOMRange *aRange, nsIPresShell *aPresShell, 
+  void RangeStartsInsideLink(nsIDOMRange *aRange, nsIPresShell *aPresShell,
                              bool *aIsInsideLink, bool *aIsStartingLink);
 
-  void GetSelection(nsIPresShell *aPresShell, nsISelectionController **aSelCon, 
+  void GetSelection(nsIPresShell *aPresShell, nsISelectionController **aSelCon,
                     nsISelection **aDomSel);
   // *aNewRange may not be collapsed.  If you want to collapse it in a
   // particular way, you need to do it yourself.
   bool IsRangeVisible(nsIPresShell *aPresShell, nsPresContext *aPresContext,
                         nsIDOMRange *aRange, bool aMustBeVisible, 
                         bool aGetTopVisibleLeaf, nsIDOMRange **aNewRange,
                         bool *aUsesIndependentSelection);
+  bool IsRangeRendered(nsIPresShell *aPresShell, nsPresContext *aPresContext,
+                          nsIDOMRange *aRange);
   nsresult FindItNow(nsIPresShell *aPresShell, bool aIsLinksOnly,
                      bool aIsFirstVisiblePreferred, bool aFindPrev,
                      uint16_t* aResult);
   nsresult GetSearchContainers(nsISupports *aContainer,
                                nsISelectionController *aSelectionController,
                                bool aIsFirstVisiblePreferred,
                                bool aFindPrev, nsIPresShell **aPresShell,
                                nsPresContext **aPresContext);