Bug 51938. Cache recently accessed line if the lines' combinedArea.y/ymosts are nondecreasing --- speeds up painting and event handling on large blocks. r+sr=bzbarsky

This commit is contained in:
roc+%cs.cmu.edu 2004-03-07 18:04:24 +00:00
Родитель 58bf69e970
Коммит d162445909
10 изменённых файлов: 614 добавлений и 86 удалений

Просмотреть файл

@ -135,6 +135,7 @@ LAYOUT_ATOM(collapseOffsetProperty, "CollapseOffsetProperty") // nsPoint*
LAYOUT_ATOM(computedOffsetProperty, "ComputedOffsetProperty") // nsPoint*
LAYOUT_ATOM(IBSplitSpecialPrevSibling, "IBSplitSpecialPrevSibling")// nsIFrame*
LAYOUT_ATOM(IBSplitSpecialSibling, "IBSplitSpecialSibling") // nsIFrame*
LAYOUT_ATOM(lineCursorProperty, "LineCursorProperty") // nsLineBox*
LAYOUT_ATOM(maxElementWidthProperty, "MaxElementWidthProperty") // nscoord*
LAYOUT_ATOM(overflowAreaProperty, "OverflowArea") // nsRect*
LAYOUT_ATOM(overflowProperty, "OverflowProperty") // list of nsIFrame*

Просмотреть файл

@ -135,6 +135,7 @@ LAYOUT_ATOM(collapseOffsetProperty, "CollapseOffsetProperty") // nsPoint*
LAYOUT_ATOM(computedOffsetProperty, "ComputedOffsetProperty") // nsPoint*
LAYOUT_ATOM(IBSplitSpecialPrevSibling, "IBSplitSpecialPrevSibling")// nsIFrame*
LAYOUT_ATOM(IBSplitSpecialSibling, "IBSplitSpecialSibling") // nsIFrame*
LAYOUT_ATOM(lineCursorProperty, "LineCursorProperty") // nsLineBox*
LAYOUT_ATOM(maxElementWidthProperty, "MaxElementWidthProperty") // nscoord*
LAYOUT_ATOM(overflowAreaProperty, "OverflowArea") // nsRect*
LAYOUT_ATOM(overflowProperty, "OverflowProperty") // list of nsIFrame*

Просмотреть файл

@ -86,6 +86,8 @@
#include "nsIDOMHTMLBodyElement.h"
#include "nsIDOMHTMLHtmlElement.h"
static const int MIN_LINES_NEEDING_CURSOR = 20;
#ifdef DEBUG
#include "nsPrintfCString.h"
#include "nsBlockDebugFlags.h"
@ -636,6 +638,11 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext,
}
}
// OK, some lines may be reflowed. Blow away any saved line cursor because
// we may invalidate the nondecreasing combinedArea.y/yMost invariant,
// and we may even delete the line with the line cursor.
ClearLineCursor();
if (IsFrameTreeTooDeep(aReflowState, aMetrics)) {
#ifdef DEBUG_kipp
{
@ -5319,6 +5326,55 @@ nsBlockFrame::PaintFloats(nsIPresContext* aPresContext,
}
}
#ifdef DEBUG
static void DebugOutputDrawLine(nsFramePaintLayer aWhichLayer, PRInt32 aDepth,
nsLineBox* aLine, PRBool aDrawn) {
if (nsBlockFrame::gNoisyDamageRepair &&
(NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer)) {
nsFrame::IndentBy(stdout, aDepth+1);
nsRect lineArea = aLine->GetCombinedArea();
printf("%s line=%p bounds=%d,%d,%d,%d ca=%d,%d,%d,%d\n",
aDrawn ? "draw" : "skip",
NS_STATIC_CAST(void*, aLine),
aLine->mBounds.x, aLine->mBounds.y,
aLine->mBounds.width, aLine->mBounds.height,
lineArea.x, lineArea.y,
lineArea.width, lineArea.height);
}
}
#endif
static inline void
PaintLine(const nsRect& aLineArea, const nsRect& aDirtyRect,
nsBlockFrame::line_iterator& aLine, PRInt32 aDepth,
PRInt32& aDrawnLines, nsIPresContext* aPresContext,
nsIRenderingContext& aRenderingContext,
nsFramePaintLayer aWhichLayer, nsBlockFrame* aFrame) {
// If the line's combined area (which includes child frames that
// stick outside of the line's bounding box or our bounding box)
// intersects the dirty rect then paint the line.
if (aLineArea.Intersects(aDirtyRect)) {
#ifdef DEBUG
DebugOutputDrawLine(aWhichLayer, aDepth, aLine.get(), PR_TRUE);
if (nsBlockFrame::gLamePaintMetrics) {
aDrawnLines++;
}
#endif
nsIFrame* kid = aLine->mFirstChild;
PRInt32 n = aLine->GetChildCount();
while (--n >= 0) {
aFrame->PaintChild(aPresContext, aRenderingContext, aDirtyRect, kid,
aWhichLayer);
kid = kid->GetNextSibling();
}
}
#ifdef DEBUG
else {
DebugOutputDrawLine(aWhichLayer, aDepth, aLine.get(), PR_FALSE);
}
#endif
}
void
nsBlockFrame::PaintChildren(nsIPresContext* aPresContext,
nsIRenderingContext& aRenderingContext,
@ -5326,6 +5382,7 @@ nsBlockFrame::PaintChildren(nsIPresContext* aPresContext,
nsFramePaintLayer aWhichLayer,
PRUint32 aFlags)
{
PRInt32 drawnLines; // Will only be used if set (gLamePaintMetrics).
#ifdef DEBUG
PRInt32 depth = 0;
if (gNoisyDamageRepair) {
@ -5334,59 +5391,56 @@ nsBlockFrame::PaintChildren(nsIPresContext* aPresContext,
}
}
PRTime start = LL_ZERO; // Initialize these variables to silence the compiler.
PRInt32 drawnLines = 0; // They will only be used if set (gLamePaintMetrics).
if (gLamePaintMetrics) {
start = PR_Now();
drawnLines = 0;
}
#endif
for (line_iterator line = begin_lines(), line_end = end_lines();
line != line_end;
++line) {
// If the line's combined area (which includes child frames that
// stick outside of the line's bounding box or our bounding box)
// intersects the dirty rect then paint the line.
if (line->CombinedAreaIntersects(aDirtyRect)) {
#ifdef DEBUG
if (gNoisyDamageRepair &&
(NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer)) {
nsRect lineCombinedArea(line->GetCombinedArea());
nsFrame::IndentBy(stdout, depth+1);
printf("draw line=%p bounds=%d,%d,%d,%d ca=%d,%d,%d,%d\n",
NS_STATIC_CAST(void*, line.get()),
line->mBounds.x, line->mBounds.y,
line->mBounds.width, line->mBounds.height,
lineCombinedArea.x, lineCombinedArea.y,
lineCombinedArea.width, lineCombinedArea.height);
}
if (gLamePaintMetrics) {
drawnLines++;
}
#endif
nsIFrame* kid = line->mFirstChild;
PRInt32 n = line->GetChildCount();
while (--n >= 0) {
PaintChild(aPresContext, aRenderingContext, aDirtyRect, kid,
aWhichLayer);
kid = kid->GetNextSibling();
nsLineBox* cursor = GetFirstLineContaining(aDirtyRect.y);
line_iterator line_end = end_lines();
if (cursor) {
for (line_iterator line = mLines.begin(cursor);
line != line_end;
++line) {
nsRect lineArea = line->GetCombinedArea();
if (!lineArea.IsEmpty()) {
// Because we have a cursor, the combinedArea.ys are non-decreasing.
// Once we've passed aDirtyRect.YMost(), we can never see it again.
if (lineArea.y >= aDirtyRect.YMost()) {
break;
}
PaintLine(lineArea, aDirtyRect, line, depth, drawnLines, aPresContext,
aRenderingContext, aWhichLayer, this);
}
}
#ifdef DEBUG
else {
if (gNoisyDamageRepair &&
(NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer)) {
nsRect lineCombinedArea(line->GetCombinedArea());
nsFrame::IndentBy(stdout, depth+1);
printf("skip line=%p bounds=%d,%d,%d,%d ca=%d,%d,%d,%d\n",
NS_STATIC_CAST(void*, line.get()),
line->mBounds.x, line->mBounds.y,
line->mBounds.width, line->mBounds.height,
lineCombinedArea.x, lineCombinedArea.y,
lineCombinedArea.width, lineCombinedArea.height);
} else {
PRBool nonDecreasingYs = PR_TRUE;
PRInt32 lineCount = 0;
nscoord lastY = PR_INT32_MIN;
nscoord lastYMost = PR_INT32_MIN;
for (line_iterator line = begin_lines();
line != line_end;
++line) {
nsRect lineArea = line->GetCombinedArea();
if (!lineArea.IsEmpty()) {
if (lineArea.y < lastY
|| lineArea.YMost() < lastYMost) {
nonDecreasingYs = PR_FALSE;
}
lastY = lineArea.y;
lastYMost = lineArea.YMost();
PaintLine(lineArea, aDirtyRect, line, depth, drawnLines, aPresContext,
aRenderingContext, aWhichLayer, this);
}
lineCount++;
}
if (nonDecreasingYs && lineCount >= MIN_LINES_NEEDING_CURSOR) {
SetupLineCursor();
}
#endif
}
if (NS_FRAME_PAINT_LAYER_FOREGROUND == aWhichLayer) {
@ -5625,6 +5679,163 @@ nsBlockFrame::HandleEvent(nsIPresContext* aPresContext,
return nsFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
}
void nsBlockFrame::ClearLineCursor() {
if (!(GetStateBits() & NS_BLOCK_HAS_LINE_CURSOR)) {
return;
}
GetProperty(GetPresContext(), nsLayoutAtoms::lineCursorProperty, PR_TRUE);
RemoveStateBits(NS_BLOCK_HAS_LINE_CURSOR);
}
void nsBlockFrame::SetupLineCursor() {
if (GetStateBits() & NS_BLOCK_HAS_LINE_CURSOR
|| mLines.empty()) {
return;
}
SetProperty(GetPresContext(), nsLayoutAtoms::lineCursorProperty,
mLines.front(), nsnull);
AddStateBits(NS_BLOCK_HAS_LINE_CURSOR);
}
nsLineBox* nsBlockFrame::GetFirstLineContaining(nscoord y) {
if (!(GetStateBits() & NS_BLOCK_HAS_LINE_CURSOR)) {
return nsnull;
}
nsLineBox* property = NS_STATIC_CAST(nsLineBox*,
GetProperty(GetPresContext(), nsLayoutAtoms::lineCursorProperty, PR_FALSE));
line_iterator cursor = mLines.begin(property);
nsRect cursorArea = cursor->GetCombinedArea();
while ((cursorArea.IsEmpty() || cursorArea.YMost() > y)
&& cursor != mLines.front()) {
cursor = cursor.prev();
cursorArea = cursor->GetCombinedArea();
}
while ((cursorArea.IsEmpty() || cursorArea.YMost() <= y)
&& cursor != mLines.back()) {
cursor = cursor.next();
cursorArea = cursor->GetCombinedArea();
}
if (cursor.get() != property) {
SetProperty(GetPresContext(), nsLayoutAtoms::lineCursorProperty,
cursor.get(), nsnull);
}
return cursor.get();
}
static inline void
GetFrameFromLine(const nsRect& aLineArea, const nsPoint& aTmp,
nsBlockFrame::line_iterator& aLine, nsIPresContext* aPresContext,
nsFramePaintLayer aWhichLayer, nsIFrame** aFrame) {
if (aLineArea.Contains(aTmp)) {
nsIFrame* kid = aLine->mFirstChild;
PRInt32 n = aLine->GetChildCount();
while (--n >= 0) {
nsIFrame *hit;
nsresult rv = kid->GetFrameForPoint(aPresContext, aTmp, aWhichLayer, &hit);
if (NS_SUCCEEDED(rv) && hit) {
*aFrame = hit;
}
kid = kid->GetNextSibling();
}
}
}
// Optimized function that uses line combined areas to skip lines
// we know can't contain the point
nsresult
nsBlockFrame::GetFrameForPointUsing(nsIPresContext* aPresContext,
const nsPoint& aPoint,
nsIAtom* aList,
nsFramePaintLayer aWhichLayer,
PRBool aConsiderSelf,
nsIFrame** aFrame)
{
if (aList) {
return nsContainerFrame::GetFrameForPointUsing(aPresContext,
aPoint, aList, aWhichLayer, aConsiderSelf, aFrame);
}
PRBool inThisFrame = mRect.Contains(aPoint);
if (! ((mState & NS_FRAME_OUTSIDE_CHILDREN) || inThisFrame ) ) {
return NS_ERROR_FAILURE;
}
*aFrame = nsnull;
nsPoint tmp(aPoint.x - mRect.x, aPoint.y - mRect.y);
nsPoint originOffset;
nsIView *view = nsnull;
nsresult rv = GetOriginToViewOffset(aPresContext, originOffset, &view);
if (NS_SUCCEEDED(rv) && view)
tmp += originOffset;
nsLineBox* cursor = GetFirstLineContaining(tmp.y);
line_iterator line_end = end_lines();
if (cursor) {
// This is the fast path for large blocks
for (line_iterator line = mLines.begin(cursor);
line != line_end;
++line) {
nsRect lineArea = line->GetCombinedArea();
// Because we have a cursor, the combinedArea.ys are non-decreasing.
// Once we've passed tmp.y, we can never see it again.
if (!lineArea.IsEmpty()) {
if (lineArea.y > tmp.y) {
break;
}
GetFrameFromLine(lineArea, tmp, line, aPresContext, aWhichLayer, aFrame);
}
}
} else {
PRBool nonDecreasingYs = PR_TRUE;
PRInt32 lineCount = 0;
nscoord lastY = PR_INT32_MIN;
nscoord lastYMost = PR_INT32_MIN;
for (line_iterator line = mLines.begin();
line != line_end;
++line) {
nsRect lineArea = line->GetCombinedArea();
if (!lineArea.IsEmpty()) {
if (lineArea.y < lastY
|| lineArea.YMost() < lastYMost) {
nonDecreasingYs = PR_FALSE;
}
lastY = lineArea.y;
lastYMost = lineArea.YMost();
GetFrameFromLine(lineArea, tmp, line, aPresContext, aWhichLayer, aFrame);
}
lineCount++;
}
if (nonDecreasingYs && lineCount >= MIN_LINES_NEEDING_CURSOR) {
SetupLineCursor();
}
}
if (*aFrame) {
return NS_OK;
}
if ( inThisFrame && aConsiderSelf ) {
if (GetStyleVisibility()->IsVisible()) {
*aFrame = this;
return NS_OK;
}
}
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsBlockFrame::GetFrameForPoint(nsIPresContext* aPresContext,

Просмотреть файл

@ -136,6 +136,37 @@ public:
NS_IMETHOD GetFrameName(nsAString& aResult) const;
NS_IMETHOD VerifyTree() const;
#endif
// line cursor methods to speed up searching for the line(s)
// containing a point. The basic idea is that we set the cursor
// property if the lines' combinedArea.ys and combinedArea.yMosts
// are non-decreasing (considering only non-empty combinedAreas;
// empty combinedAreas never participate in event handling or
// painting), and the block has sufficient number of lines. The
// cursor property points to a "recently used" line. If we get a
// series of GetFrameForPoint or Paint requests that work on lines
// "near" the cursor, then we can find those nearby lines quickly by
// starting our search at the cursor.
// Clear out line cursor because we're disturbing the lines (i.e., Reflow)
void ClearLineCursor();
// Get the first line that might contain y-coord 'y', or nsnull if you must search
// all lines. If nonnull is returned then we guarantee that the lines'
// combinedArea.ys and combinedArea.yMosts are non-decreasing.
// The actual line returned might not contain 'y', but if not, it is guaranteed
// to be before any line which does contain 'y'.
nsLineBox* GetFirstLineContaining(nscoord y);
// Set the line cursor to our first line. Only call this if you
// guarantee that the lines' combinedArea.ys and combinedArea.yMosts
// are non-decreasing.
void SetupLineCursor();
nsresult GetFrameForPointUsing(nsIPresContext* aPresContext,
const nsPoint& aPoint,
nsIAtom* aList,
nsFramePaintLayer aWhichLayer,
PRBool aConsiderSelf,
nsIFrame** aFrame);
NS_IMETHOD GetFrameForPoint(nsIPresContext* aPresContext, const nsPoint& aPoint, nsFramePaintLayer aWhichLayer, nsIFrame** aFrame);
NS_IMETHOD HandleEvent(nsIPresContext* aPresContext,
nsGUIEvent* aEvent,
@ -200,6 +231,16 @@ public:
void UndoSplitPlaceholders(nsBlockReflowState& aState,
nsIFrame* aLastPlaceholder);
virtual void PaintChild(nsIPresContext* aPresContext,
nsIRenderingContext& aRenderingContext,
const nsRect& aDirtyRect,
nsIFrame* aFrame,
nsFramePaintLayer aWhichLayer,
PRUint32 aFlags = 0) {
return nsContainerFrame::PaintChild(aPresContext, aRenderingContext,
aDirtyRect, aFrame, aWhichLayer, aFlags);
}
protected:
nsBlockFrame();
virtual ~nsBlockFrame();

Просмотреть файл

@ -68,6 +68,7 @@ class nsIChannel;
#define NS_BLOCK_NO_AUTO_MARGINS 0x00200000
#define NS_BLOCK_MARGIN_ROOT 0x00400000
#define NS_BLOCK_SPACE_MGR 0x00800000
#define NS_BLOCK_HAS_LINE_CURSOR 0x01000000
#define NS_BLOCK_FLAGS_MASK 0xFFF00000
// Factory method for creating a content iterator for generated

Просмотреть файл

@ -1063,6 +1063,16 @@ class nsLineList {
return rv;
}
iterator begin(nsLineBox* aLine)
{
iterator rv;
rv.mCurrent = aLine;
#ifdef NS_LINELIST_DEBUG_PASS_END
rv.mListLink = &mLink;
#endif
return rv;
}
const_iterator end() const
{
const_iterator rv;

Просмотреть файл

@ -86,6 +86,8 @@
#include "nsIDOMHTMLBodyElement.h"
#include "nsIDOMHTMLHtmlElement.h"
static const int MIN_LINES_NEEDING_CURSOR = 20;
#ifdef DEBUG
#include "nsPrintfCString.h"
#include "nsBlockDebugFlags.h"
@ -636,6 +638,11 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext,
}
}
// OK, some lines may be reflowed. Blow away any saved line cursor because
// we may invalidate the nondecreasing combinedArea.y/yMost invariant,
// and we may even delete the line with the line cursor.
ClearLineCursor();
if (IsFrameTreeTooDeep(aReflowState, aMetrics)) {
#ifdef DEBUG_kipp
{
@ -5319,6 +5326,55 @@ nsBlockFrame::PaintFloats(nsIPresContext* aPresContext,
}
}
#ifdef DEBUG
static void DebugOutputDrawLine(nsFramePaintLayer aWhichLayer, PRInt32 aDepth,
nsLineBox* aLine, PRBool aDrawn) {
if (nsBlockFrame::gNoisyDamageRepair &&
(NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer)) {
nsFrame::IndentBy(stdout, aDepth+1);
nsRect lineArea = aLine->GetCombinedArea();
printf("%s line=%p bounds=%d,%d,%d,%d ca=%d,%d,%d,%d\n",
aDrawn ? "draw" : "skip",
NS_STATIC_CAST(void*, aLine),
aLine->mBounds.x, aLine->mBounds.y,
aLine->mBounds.width, aLine->mBounds.height,
lineArea.x, lineArea.y,
lineArea.width, lineArea.height);
}
}
#endif
static inline void
PaintLine(const nsRect& aLineArea, const nsRect& aDirtyRect,
nsBlockFrame::line_iterator& aLine, PRInt32 aDepth,
PRInt32& aDrawnLines, nsIPresContext* aPresContext,
nsIRenderingContext& aRenderingContext,
nsFramePaintLayer aWhichLayer, nsBlockFrame* aFrame) {
// If the line's combined area (which includes child frames that
// stick outside of the line's bounding box or our bounding box)
// intersects the dirty rect then paint the line.
if (aLineArea.Intersects(aDirtyRect)) {
#ifdef DEBUG
DebugOutputDrawLine(aWhichLayer, aDepth, aLine.get(), PR_TRUE);
if (nsBlockFrame::gLamePaintMetrics) {
aDrawnLines++;
}
#endif
nsIFrame* kid = aLine->mFirstChild;
PRInt32 n = aLine->GetChildCount();
while (--n >= 0) {
aFrame->PaintChild(aPresContext, aRenderingContext, aDirtyRect, kid,
aWhichLayer);
kid = kid->GetNextSibling();
}
}
#ifdef DEBUG
else {
DebugOutputDrawLine(aWhichLayer, aDepth, aLine.get(), PR_FALSE);
}
#endif
}
void
nsBlockFrame::PaintChildren(nsIPresContext* aPresContext,
nsIRenderingContext& aRenderingContext,
@ -5326,6 +5382,7 @@ nsBlockFrame::PaintChildren(nsIPresContext* aPresContext,
nsFramePaintLayer aWhichLayer,
PRUint32 aFlags)
{
PRInt32 drawnLines; // Will only be used if set (gLamePaintMetrics).
#ifdef DEBUG
PRInt32 depth = 0;
if (gNoisyDamageRepair) {
@ -5334,59 +5391,56 @@ nsBlockFrame::PaintChildren(nsIPresContext* aPresContext,
}
}
PRTime start = LL_ZERO; // Initialize these variables to silence the compiler.
PRInt32 drawnLines = 0; // They will only be used if set (gLamePaintMetrics).
if (gLamePaintMetrics) {
start = PR_Now();
drawnLines = 0;
}
#endif
for (line_iterator line = begin_lines(), line_end = end_lines();
line != line_end;
++line) {
// If the line's combined area (which includes child frames that
// stick outside of the line's bounding box or our bounding box)
// intersects the dirty rect then paint the line.
if (line->CombinedAreaIntersects(aDirtyRect)) {
#ifdef DEBUG
if (gNoisyDamageRepair &&
(NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer)) {
nsRect lineCombinedArea(line->GetCombinedArea());
nsFrame::IndentBy(stdout, depth+1);
printf("draw line=%p bounds=%d,%d,%d,%d ca=%d,%d,%d,%d\n",
NS_STATIC_CAST(void*, line.get()),
line->mBounds.x, line->mBounds.y,
line->mBounds.width, line->mBounds.height,
lineCombinedArea.x, lineCombinedArea.y,
lineCombinedArea.width, lineCombinedArea.height);
}
if (gLamePaintMetrics) {
drawnLines++;
}
#endif
nsIFrame* kid = line->mFirstChild;
PRInt32 n = line->GetChildCount();
while (--n >= 0) {
PaintChild(aPresContext, aRenderingContext, aDirtyRect, kid,
aWhichLayer);
kid = kid->GetNextSibling();
nsLineBox* cursor = GetFirstLineContaining(aDirtyRect.y);
line_iterator line_end = end_lines();
if (cursor) {
for (line_iterator line = mLines.begin(cursor);
line != line_end;
++line) {
nsRect lineArea = line->GetCombinedArea();
if (!lineArea.IsEmpty()) {
// Because we have a cursor, the combinedArea.ys are non-decreasing.
// Once we've passed aDirtyRect.YMost(), we can never see it again.
if (lineArea.y >= aDirtyRect.YMost()) {
break;
}
PaintLine(lineArea, aDirtyRect, line, depth, drawnLines, aPresContext,
aRenderingContext, aWhichLayer, this);
}
}
#ifdef DEBUG
else {
if (gNoisyDamageRepair &&
(NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer)) {
nsRect lineCombinedArea(line->GetCombinedArea());
nsFrame::IndentBy(stdout, depth+1);
printf("skip line=%p bounds=%d,%d,%d,%d ca=%d,%d,%d,%d\n",
NS_STATIC_CAST(void*, line.get()),
line->mBounds.x, line->mBounds.y,
line->mBounds.width, line->mBounds.height,
lineCombinedArea.x, lineCombinedArea.y,
lineCombinedArea.width, lineCombinedArea.height);
} else {
PRBool nonDecreasingYs = PR_TRUE;
PRInt32 lineCount = 0;
nscoord lastY = PR_INT32_MIN;
nscoord lastYMost = PR_INT32_MIN;
for (line_iterator line = begin_lines();
line != line_end;
++line) {
nsRect lineArea = line->GetCombinedArea();
if (!lineArea.IsEmpty()) {
if (lineArea.y < lastY
|| lineArea.YMost() < lastYMost) {
nonDecreasingYs = PR_FALSE;
}
lastY = lineArea.y;
lastYMost = lineArea.YMost();
PaintLine(lineArea, aDirtyRect, line, depth, drawnLines, aPresContext,
aRenderingContext, aWhichLayer, this);
}
lineCount++;
}
if (nonDecreasingYs && lineCount >= MIN_LINES_NEEDING_CURSOR) {
SetupLineCursor();
}
#endif
}
if (NS_FRAME_PAINT_LAYER_FOREGROUND == aWhichLayer) {
@ -5625,6 +5679,163 @@ nsBlockFrame::HandleEvent(nsIPresContext* aPresContext,
return nsFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
}
void nsBlockFrame::ClearLineCursor() {
if (!(GetStateBits() & NS_BLOCK_HAS_LINE_CURSOR)) {
return;
}
GetProperty(GetPresContext(), nsLayoutAtoms::lineCursorProperty, PR_TRUE);
RemoveStateBits(NS_BLOCK_HAS_LINE_CURSOR);
}
void nsBlockFrame::SetupLineCursor() {
if (GetStateBits() & NS_BLOCK_HAS_LINE_CURSOR
|| mLines.empty()) {
return;
}
SetProperty(GetPresContext(), nsLayoutAtoms::lineCursorProperty,
mLines.front(), nsnull);
AddStateBits(NS_BLOCK_HAS_LINE_CURSOR);
}
nsLineBox* nsBlockFrame::GetFirstLineContaining(nscoord y) {
if (!(GetStateBits() & NS_BLOCK_HAS_LINE_CURSOR)) {
return nsnull;
}
nsLineBox* property = NS_STATIC_CAST(nsLineBox*,
GetProperty(GetPresContext(), nsLayoutAtoms::lineCursorProperty, PR_FALSE));
line_iterator cursor = mLines.begin(property);
nsRect cursorArea = cursor->GetCombinedArea();
while ((cursorArea.IsEmpty() || cursorArea.YMost() > y)
&& cursor != mLines.front()) {
cursor = cursor.prev();
cursorArea = cursor->GetCombinedArea();
}
while ((cursorArea.IsEmpty() || cursorArea.YMost() <= y)
&& cursor != mLines.back()) {
cursor = cursor.next();
cursorArea = cursor->GetCombinedArea();
}
if (cursor.get() != property) {
SetProperty(GetPresContext(), nsLayoutAtoms::lineCursorProperty,
cursor.get(), nsnull);
}
return cursor.get();
}
static inline void
GetFrameFromLine(const nsRect& aLineArea, const nsPoint& aTmp,
nsBlockFrame::line_iterator& aLine, nsIPresContext* aPresContext,
nsFramePaintLayer aWhichLayer, nsIFrame** aFrame) {
if (aLineArea.Contains(aTmp)) {
nsIFrame* kid = aLine->mFirstChild;
PRInt32 n = aLine->GetChildCount();
while (--n >= 0) {
nsIFrame *hit;
nsresult rv = kid->GetFrameForPoint(aPresContext, aTmp, aWhichLayer, &hit);
if (NS_SUCCEEDED(rv) && hit) {
*aFrame = hit;
}
kid = kid->GetNextSibling();
}
}
}
// Optimized function that uses line combined areas to skip lines
// we know can't contain the point
nsresult
nsBlockFrame::GetFrameForPointUsing(nsIPresContext* aPresContext,
const nsPoint& aPoint,
nsIAtom* aList,
nsFramePaintLayer aWhichLayer,
PRBool aConsiderSelf,
nsIFrame** aFrame)
{
if (aList) {
return nsContainerFrame::GetFrameForPointUsing(aPresContext,
aPoint, aList, aWhichLayer, aConsiderSelf, aFrame);
}
PRBool inThisFrame = mRect.Contains(aPoint);
if (! ((mState & NS_FRAME_OUTSIDE_CHILDREN) || inThisFrame ) ) {
return NS_ERROR_FAILURE;
}
*aFrame = nsnull;
nsPoint tmp(aPoint.x - mRect.x, aPoint.y - mRect.y);
nsPoint originOffset;
nsIView *view = nsnull;
nsresult rv = GetOriginToViewOffset(aPresContext, originOffset, &view);
if (NS_SUCCEEDED(rv) && view)
tmp += originOffset;
nsLineBox* cursor = GetFirstLineContaining(tmp.y);
line_iterator line_end = end_lines();
if (cursor) {
// This is the fast path for large blocks
for (line_iterator line = mLines.begin(cursor);
line != line_end;
++line) {
nsRect lineArea = line->GetCombinedArea();
// Because we have a cursor, the combinedArea.ys are non-decreasing.
// Once we've passed tmp.y, we can never see it again.
if (!lineArea.IsEmpty()) {
if (lineArea.y > tmp.y) {
break;
}
GetFrameFromLine(lineArea, tmp, line, aPresContext, aWhichLayer, aFrame);
}
}
} else {
PRBool nonDecreasingYs = PR_TRUE;
PRInt32 lineCount = 0;
nscoord lastY = PR_INT32_MIN;
nscoord lastYMost = PR_INT32_MIN;
for (line_iterator line = mLines.begin();
line != line_end;
++line) {
nsRect lineArea = line->GetCombinedArea();
if (!lineArea.IsEmpty()) {
if (lineArea.y < lastY
|| lineArea.YMost() < lastYMost) {
nonDecreasingYs = PR_FALSE;
}
lastY = lineArea.y;
lastYMost = lineArea.YMost();
GetFrameFromLine(lineArea, tmp, line, aPresContext, aWhichLayer, aFrame);
}
lineCount++;
}
if (nonDecreasingYs && lineCount >= MIN_LINES_NEEDING_CURSOR) {
SetupLineCursor();
}
}
if (*aFrame) {
return NS_OK;
}
if ( inThisFrame && aConsiderSelf ) {
if (GetStyleVisibility()->IsVisible()) {
*aFrame = this;
return NS_OK;
}
}
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsBlockFrame::GetFrameForPoint(nsIPresContext* aPresContext,

Просмотреть файл

@ -136,6 +136,37 @@ public:
NS_IMETHOD GetFrameName(nsAString& aResult) const;
NS_IMETHOD VerifyTree() const;
#endif
// line cursor methods to speed up searching for the line(s)
// containing a point. The basic idea is that we set the cursor
// property if the lines' combinedArea.ys and combinedArea.yMosts
// are non-decreasing (considering only non-empty combinedAreas;
// empty combinedAreas never participate in event handling or
// painting), and the block has sufficient number of lines. The
// cursor property points to a "recently used" line. If we get a
// series of GetFrameForPoint or Paint requests that work on lines
// "near" the cursor, then we can find those nearby lines quickly by
// starting our search at the cursor.
// Clear out line cursor because we're disturbing the lines (i.e., Reflow)
void ClearLineCursor();
// Get the first line that might contain y-coord 'y', or nsnull if you must search
// all lines. If nonnull is returned then we guarantee that the lines'
// combinedArea.ys and combinedArea.yMosts are non-decreasing.
// The actual line returned might not contain 'y', but if not, it is guaranteed
// to be before any line which does contain 'y'.
nsLineBox* GetFirstLineContaining(nscoord y);
// Set the line cursor to our first line. Only call this if you
// guarantee that the lines' combinedArea.ys and combinedArea.yMosts
// are non-decreasing.
void SetupLineCursor();
nsresult GetFrameForPointUsing(nsIPresContext* aPresContext,
const nsPoint& aPoint,
nsIAtom* aList,
nsFramePaintLayer aWhichLayer,
PRBool aConsiderSelf,
nsIFrame** aFrame);
NS_IMETHOD GetFrameForPoint(nsIPresContext* aPresContext, const nsPoint& aPoint, nsFramePaintLayer aWhichLayer, nsIFrame** aFrame);
NS_IMETHOD HandleEvent(nsIPresContext* aPresContext,
nsGUIEvent* aEvent,
@ -200,6 +231,16 @@ public:
void UndoSplitPlaceholders(nsBlockReflowState& aState,
nsIFrame* aLastPlaceholder);
virtual void PaintChild(nsIPresContext* aPresContext,
nsIRenderingContext& aRenderingContext,
const nsRect& aDirtyRect,
nsIFrame* aFrame,
nsFramePaintLayer aWhichLayer,
PRUint32 aFlags = 0) {
return nsContainerFrame::PaintChild(aPresContext, aRenderingContext,
aDirtyRect, aFrame, aWhichLayer, aFlags);
}
protected:
nsBlockFrame();
virtual ~nsBlockFrame();

Просмотреть файл

@ -68,6 +68,7 @@ class nsIChannel;
#define NS_BLOCK_NO_AUTO_MARGINS 0x00200000
#define NS_BLOCK_MARGIN_ROOT 0x00400000
#define NS_BLOCK_SPACE_MGR 0x00800000
#define NS_BLOCK_HAS_LINE_CURSOR 0x01000000
#define NS_BLOCK_FLAGS_MASK 0xFFF00000
// Factory method for creating a content iterator for generated

Просмотреть файл

@ -1063,6 +1063,16 @@ class nsLineList {
return rv;
}
iterator begin(nsLineBox* aLine)
{
iterator rv;
rv.mCurrent = aLine;
#ifdef NS_LINELIST_DEBUG_PASS_END
rv.mListLink = &mLink;
#endif
return rv;
}
const_iterator end() const
{
const_iterator rv;