зеркало из https://github.com/mozilla/pjs.git
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:
Родитель
58bf69e970
Коммит
d162445909
|
@ -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;
|
||||
|
|
Загрузка…
Ссылка в новой задаче