зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1302470 Part 2: Use a hit-test method to determine if the rect of a range is visible on the page or not to the eye, for use in find-in-page. r=mstange,smaug
MozReview-Commit-ID: 9P7gf0GcREv
This commit is contained in:
Родитель
065fcdf91a
Коммит
d0a0fb4fcf
|
@ -825,7 +825,7 @@ public:
|
||||||
* frames under the area of a rectangle that receives a mouse event,
|
* frames under the area of a rectangle that receives a mouse event,
|
||||||
* or nullptr if there is no such frame.
|
* or nullptr if there is no such frame.
|
||||||
* @param aRect the rect, relative to the frame origin
|
* @param aRect the rect, relative to the frame origin
|
||||||
* @param aOutFrames an array to add all the frames found
|
* @param aOutFrames an array to append all the frames found
|
||||||
* @param aFlags some combination of FrameForPointFlags
|
* @param aFlags some combination of FrameForPointFlags
|
||||||
*/
|
*/
|
||||||
static nsresult GetFramesForArea(nsIFrame* aFrame, const nsRect& aRect,
|
static nsresult GetFramesForArea(nsIFrame* aFrame, const nsRect& aRect,
|
||||||
|
|
|
@ -18,7 +18,7 @@ interface nsIDocShell;
|
||||||
|
|
||||||
/****************************** nsTypeAheadFind ******************************/
|
/****************************** nsTypeAheadFind ******************************/
|
||||||
|
|
||||||
[scriptable, uuid(ae501e28-c57f-4692-ac74-410e1bed98b7)]
|
[scriptable, uuid(3cfe7906-f189-45a0-8abe-8e4437a23cae)]
|
||||||
interface nsITypeAheadFind : nsISupports
|
interface nsITypeAheadFind : nsISupports
|
||||||
{
|
{
|
||||||
/****************************** Initializer ******************************/
|
/****************************** Initializer ******************************/
|
||||||
|
@ -58,7 +58,7 @@ interface nsITypeAheadFind : nsISupports
|
||||||
void collapseSelection();
|
void collapseSelection();
|
||||||
|
|
||||||
/* Check if a range is visible */
|
/* Check if a range is visible */
|
||||||
boolean isRangeVisible(in nsIDOMRange aRange, in boolean aMustBeInViewPort);
|
boolean isRangeVisible(in nsIDOMRange aRange, in boolean aFlushLayout);
|
||||||
|
|
||||||
/******************************* Attributes ******************************/
|
/******************************* Attributes ******************************/
|
||||||
|
|
||||||
|
|
|
@ -52,6 +52,7 @@
|
||||||
#include "mozilla/dom/Link.h"
|
#include "mozilla/dom/Link.h"
|
||||||
#include "nsRange.h"
|
#include "nsRange.h"
|
||||||
#include "nsXBLBinding.h"
|
#include "nsXBLBinding.h"
|
||||||
|
#include "nsLayoutUtils.h"
|
||||||
|
|
||||||
#include "nsTypeAheadFind.h"
|
#include "nsTypeAheadFind.h"
|
||||||
|
|
||||||
|
@ -70,8 +71,6 @@ NS_IMPL_CYCLE_COLLECTION(nsTypeAheadFind, mFoundLink, mFoundEditable,
|
||||||
mStartPointRange, mEndPointRange, mSoundInterface,
|
mStartPointRange, mEndPointRange, mSoundInterface,
|
||||||
mFind, mFoundRange)
|
mFind, mFoundRange)
|
||||||
|
|
||||||
static NS_DEFINE_CID(kFrameTraversalCID, NS_FRAMETRAVERSAL_CID);
|
|
||||||
|
|
||||||
#define NS_FIND_CONTRACTID "@mozilla.org/embedcomp/rangefind;1"
|
#define NS_FIND_CONTRACTID "@mozilla.org/embedcomp/rangefind;1"
|
||||||
|
|
||||||
nsTypeAheadFind::nsTypeAheadFind():
|
nsTypeAheadFind::nsTypeAheadFind():
|
||||||
|
@ -436,8 +435,7 @@ nsTypeAheadFind::FindItNow(nsIPresShell *aPresShell, bool aIsLinksOnly,
|
||||||
}
|
}
|
||||||
|
|
||||||
bool usesIndependentSelection;
|
bool usesIndependentSelection;
|
||||||
if (!IsRangeVisible(presShell, presContext, returnRange,
|
if (!IsRangeVisible(presShell, presContext, returnRange, true,
|
||||||
aIsFirstVisiblePreferred, false,
|
|
||||||
getter_AddRefs(mStartPointRange),
|
getter_AddRefs(mStartPointRange),
|
||||||
&usesIndependentSelection) ||
|
&usesIndependentSelection) ||
|
||||||
(aIsLinksOnly && !isInsideLink) ||
|
(aIsLinksOnly && !isInsideLink) ||
|
||||||
|
@ -817,8 +815,7 @@ nsTypeAheadFind::GetSearchContainers(nsISupports *aContainer,
|
||||||
// Ensure visible range, move forward if necessary
|
// Ensure visible range, move forward if necessary
|
||||||
// This uses ignores the return value, but usese the side effect of
|
// This uses ignores the return value, but usese the side effect of
|
||||||
// IsRangeVisible. It returns the first visible range after searchRange
|
// IsRangeVisible. It returns the first visible range after searchRange
|
||||||
IsRangeVisible(presShell, presContext, mSearchRange,
|
IsRangeVisible(presShell, presContext, mSearchRange, true,
|
||||||
aIsFirstVisiblePreferred, true,
|
|
||||||
getter_AddRefs(mStartPointRange), nullptr);
|
getter_AddRefs(mStartPointRange), nullptr);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -1161,7 +1158,7 @@ nsTypeAheadFind::GetFoundRange(nsIDOMRange** aFoundRange)
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsTypeAheadFind::IsRangeVisible(nsIDOMRange *aRange,
|
nsTypeAheadFind::IsRangeVisible(nsIDOMRange *aRange,
|
||||||
bool aMustBeInViewPort,
|
bool aFlushLayout,
|
||||||
bool *aResult)
|
bool *aResult)
|
||||||
{
|
{
|
||||||
// Jump through hoops to extract the docShell from the range.
|
// Jump through hoops to extract the docShell from the range.
|
||||||
|
@ -1178,8 +1175,7 @@ nsTypeAheadFind::IsRangeVisible(nsIDOMRange *aRange,
|
||||||
nsCOMPtr<nsIPresShell> presShell (docShell->GetPresShell());
|
nsCOMPtr<nsIPresShell> presShell (docShell->GetPresShell());
|
||||||
RefPtr<nsPresContext> presContext = presShell->GetPresContext();
|
RefPtr<nsPresContext> presContext = presShell->GetPresContext();
|
||||||
nsCOMPtr<nsIDOMRange> startPointRange = new nsRange(presShell->GetDocument());
|
nsCOMPtr<nsIDOMRange> startPointRange = new nsRange(presShell->GetDocument());
|
||||||
*aResult = IsRangeVisible(presShell, presContext, aRange,
|
*aResult = IsRangeVisible(presShell, presContext, aRange, aFlushLayout,
|
||||||
aMustBeInViewPort, false,
|
|
||||||
getter_AddRefs(startPointRange),
|
getter_AddRefs(startPointRange),
|
||||||
nullptr);
|
nullptr);
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
|
@ -1188,8 +1184,8 @@ nsTypeAheadFind::IsRangeVisible(nsIDOMRange *aRange,
|
||||||
bool
|
bool
|
||||||
nsTypeAheadFind::IsRangeVisible(nsIPresShell *aPresShell,
|
nsTypeAheadFind::IsRangeVisible(nsIPresShell *aPresShell,
|
||||||
nsPresContext *aPresContext,
|
nsPresContext *aPresContext,
|
||||||
nsIDOMRange *aRange, bool aMustBeInViewPort,
|
nsIDOMRange *aRange,
|
||||||
bool aGetTopVisibleLeaf,
|
bool aFlushLayout,
|
||||||
nsIDOMRange **aFirstVisibleRange,
|
nsIDOMRange **aFirstVisibleRange,
|
||||||
bool *aUsesIndependentSelection)
|
bool *aUsesIndependentSelection)
|
||||||
{
|
{
|
||||||
|
@ -1199,8 +1195,12 @@ nsTypeAheadFind::IsRangeVisible(nsIPresShell *aPresShell,
|
||||||
// We need to know if the range start is visible.
|
// We need to know if the range start is visible.
|
||||||
// Otherwise, return the first visible range start
|
// Otherwise, return the first visible range start
|
||||||
// in aFirstVisibleRange
|
// in aFirstVisibleRange
|
||||||
|
|
||||||
aRange->CloneRange(aFirstVisibleRange);
|
aRange->CloneRange(aFirstVisibleRange);
|
||||||
|
|
||||||
|
if (aFlushLayout) {
|
||||||
|
aPresShell->FlushPendingNotifications(Flush_Layout);
|
||||||
|
}
|
||||||
|
|
||||||
nsCOMPtr<nsIDOMNode> node;
|
nsCOMPtr<nsIDOMNode> node;
|
||||||
aRange->GetStartContainer(getter_AddRefs(node));
|
aRange->GetStartContainer(getter_AddRefs(node));
|
||||||
|
|
||||||
|
@ -1212,7 +1212,22 @@ nsTypeAheadFind::IsRangeVisible(nsIPresShell *aPresShell,
|
||||||
if (!frame)
|
if (!frame)
|
||||||
return false; // No frame! Not visible then.
|
return false; // No frame! Not visible then.
|
||||||
|
|
||||||
if (!frame->StyleVisibility()->IsVisible())
|
// 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())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Detect if we are _inside_ a text control, or something else with its own
|
// Detect if we are _inside_ a text control, or something else with its own
|
||||||
|
@ -1222,89 +1237,7 @@ nsTypeAheadFind::IsRangeVisible(nsIPresShell *aPresShell,
|
||||||
(frame->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION);
|
(frame->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- We have a frame ----
|
return true;
|
||||||
if (!aMustBeInViewPort)
|
|
||||||
return true; // Don't need it to be on screen, just in rendering tree
|
|
||||||
|
|
||||||
// Get the next in flow frame that contains the range start
|
|
||||||
int32_t startRangeOffset, startFrameOffset, endFrameOffset;
|
|
||||||
aRange->GetStartOffset(&startRangeOffset);
|
|
||||||
while (true) {
|
|
||||||
frame->GetOffsets(startFrameOffset, endFrameOffset);
|
|
||||||
if (startRangeOffset < endFrameOffset)
|
|
||||||
break;
|
|
||||||
|
|
||||||
nsIFrame *nextContinuationFrame = frame->GetNextContinuation();
|
|
||||||
if (nextContinuationFrame)
|
|
||||||
frame = nextContinuationFrame;
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set up the variables we need, return true if we can't get at them all
|
|
||||||
const uint16_t kMinPixels = 12;
|
|
||||||
nscoord minDistance = nsPresContext::CSSPixelsToAppUnits(kMinPixels);
|
|
||||||
|
|
||||||
// Get the bounds of the current frame, relative to the current view.
|
|
||||||
// We don't use the more accurate AccGetBounds, because that is
|
|
||||||
// more expensive and the STATE_OFFSCREEN flag that this is used
|
|
||||||
// for only needs to be a rough indicator
|
|
||||||
nsRectVisibility rectVisibility = nsRectVisibility_kAboveViewport;
|
|
||||||
|
|
||||||
if (!aGetTopVisibleLeaf && !frame->GetRect().IsEmpty()) {
|
|
||||||
rectVisibility =
|
|
||||||
aPresShell->GetRectVisibility(frame,
|
|
||||||
nsRect(nsPoint(0,0), frame->GetSize()),
|
|
||||||
minDistance);
|
|
||||||
|
|
||||||
if (rectVisibility != nsRectVisibility_kAboveViewport) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We know that the target range isn't usable because it's not in the
|
|
||||||
// view port. Move range forward to first visible point,
|
|
||||||
// this speeds us up a lot in long documents
|
|
||||||
nsCOMPtr<nsIFrameEnumerator> frameTraversal;
|
|
||||||
nsCOMPtr<nsIFrameTraversal> trav(do_CreateInstance(kFrameTraversalCID));
|
|
||||||
if (trav)
|
|
||||||
trav->NewFrameTraversal(getter_AddRefs(frameTraversal),
|
|
||||||
aPresContext, frame,
|
|
||||||
eLeaf,
|
|
||||||
false, // aVisual
|
|
||||||
false, // aLockInScrollView
|
|
||||||
false, // aFollowOOFs
|
|
||||||
false // aSkipPopupChecks
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!frameTraversal)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
while (rectVisibility == nsRectVisibility_kAboveViewport) {
|
|
||||||
frameTraversal->Next();
|
|
||||||
frame = frameTraversal->CurrentItem();
|
|
||||||
if (!frame)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!frame->GetRect().IsEmpty()) {
|
|
||||||
rectVisibility =
|
|
||||||
aPresShell->GetRectVisibility(frame,
|
|
||||||
nsRect(nsPoint(0,0), frame->GetSize()),
|
|
||||||
minDistance);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (frame) {
|
|
||||||
nsCOMPtr<nsIDOMNode> firstVisibleNode = do_QueryInterface(frame->GetContent());
|
|
||||||
|
|
||||||
if (firstVisibleNode) {
|
|
||||||
frame->GetOffsets(startFrameOffset, endFrameOffset);
|
|
||||||
(*aFirstVisibleRange)->SetStart(firstVisibleNode, startFrameOffset);
|
|
||||||
(*aFirstVisibleRange)->SetEnd(firstVisibleNode, endFrameOffset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
already_AddRefed<nsIPresShell>
|
already_AddRefed<nsIPresShell>
|
||||||
|
|
|
@ -56,8 +56,8 @@ protected:
|
||||||
// *aNewRange may not be collapsed. If you want to collapse it in a
|
// *aNewRange may not be collapsed. If you want to collapse it in a
|
||||||
// particular way, you need to do it yourself.
|
// particular way, you need to do it yourself.
|
||||||
bool IsRangeVisible(nsIPresShell *aPresShell, nsPresContext *aPresContext,
|
bool IsRangeVisible(nsIPresShell *aPresShell, nsPresContext *aPresContext,
|
||||||
nsIDOMRange *aRange, bool aMustBeVisible,
|
nsIDOMRange *aRange, bool aFlushLayout,
|
||||||
bool aGetTopVisibleLeaf, nsIDOMRange **aNewRange,
|
nsIDOMRange **aNewRange,
|
||||||
bool *aUsesIndependentSelection);
|
bool *aUsesIndependentSelection);
|
||||||
nsresult FindItNow(nsIPresShell *aPresShell, bool aIsLinksOnly,
|
nsresult FindItNow(nsIPresShell *aPresShell, bool aIsLinksOnly,
|
||||||
bool aIsFirstVisiblePreferred, bool aFindPrev,
|
bool aIsFirstVisiblePreferred, bool aFindPrev,
|
||||||
|
|
|
@ -573,7 +573,8 @@ this.FinderIterator = {
|
||||||
let range = window.document.createRange();
|
let range = window.document.createRange();
|
||||||
range.setStart(frameEl, 0);
|
range.setStart(frameEl, 0);
|
||||||
range.setEnd(frameEl, 0);
|
range.setEnd(frameEl, 0);
|
||||||
if (!finder._fastFind.isRangeVisible(range, this._getDocShell(range), true))
|
// Pass `true` to flush layout.
|
||||||
|
if (!finder._fastFind.isRangeVisible(range, true))
|
||||||
continue;
|
continue;
|
||||||
// All conditions pass, so push the current frame and its children on the
|
// All conditions pass, so push the current frame and its children on the
|
||||||
// stack.
|
// stack.
|
||||||
|
|
Загрузка…
Ссылка в новой задаче