Bug 1302470 Part 1: Create a IsRangeRendered function to test range visibility in the display list.
MozReview-Commit-ID: u0N73luIz7
--- 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
@@ -1313,16 +1313,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, true);
+ 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
@@ -55,16 +55,18 @@ protected:
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);