diff --git a/mozilla/content/base/src/nsGkAtomList.h b/mozilla/content/base/src/nsGkAtomList.h index a96ce3440ed..0ea56c7d4fb 100755 --- a/mozilla/content/base/src/nsGkAtomList.h +++ b/mozilla/content/base/src/nsGkAtomList.h @@ -1358,6 +1358,7 @@ GK_ATOM(computedOffsetProperty, "ComputedOffsetProperty") // nsPoint* GK_ATOM(IBSplitSpecialPrevSibling, "IBSplitSpecialPrevSibling")// nsIFrame* GK_ATOM(IBSplitSpecialSibling, "IBSplitSpecialSibling") // nsIFrame* GK_ATOM(lineCursorProperty, "LineCursorProperty") // nsLineBox* +GK_ATOM(rowCursorProperty, "RowCursorProperty") // nsTableRowGroupFrame::FrameCursorData* GK_ATOM(maxElementWidthProperty, "MaxElementWidthProperty") // nscoord* GK_ATOM(outOfFlowDirtyRectProperty, "OutOfFlowDirtyRectProperty") // nsRect* GK_ATOM(overflowAreaProperty, "OverflowArea") // nsRect* diff --git a/mozilla/content/base/src/nsPropertyTable.cpp b/mozilla/content/base/src/nsPropertyTable.cpp index cb8b5833340..ff83024857d 100644 --- a/mozilla/content/base/src/nsPropertyTable.cpp +++ b/mozilla/content/base/src/nsPropertyTable.cpp @@ -217,6 +217,7 @@ nsPropertyTable::SetPropertyInternal(nsPropertyOwner aObject, if (aPropDtorFunc != propertyList->mDtorFunc || aPropDtorData != propertyList->mDtorData || aTransfer != propertyList->mTransfer) { + NS_WARNING("Destructor/data mismatch while setting property"); return NS_ERROR_INVALID_ARG; } diff --git a/mozilla/layout/tables/nsTableFrame.cpp b/mozilla/layout/tables/nsTableFrame.cpp index b544435352c..904dd517b4a 100644 --- a/mozilla/layout/tables/nsTableFrame.cpp +++ b/mozilla/layout/tables/nsTableFrame.cpp @@ -1427,12 +1427,34 @@ static PRBool CompareByTablePartRank(nsDisplayItem* aItem1, nsDisplayItem* aItem return GetTablePartRank(aItem1) <= GetTablePartRank(aItem2); } +/* static */ nsresult +nsTableFrame::GenericTraversal(nsDisplayListBuilder* aBuilder, nsFrame* aFrame, + const nsRect& aDirtyRect, const nsDisplayListSet& aLists) +{ + // This is similar to what nsContainerFrame::BuildDisplayListForNonBlockChildren + // does, except that we allow the children's background and borders to go + // in our BorderBackground list. This doesn't really affect background + // painting --- the children won't actually draw their own backgrounds + // because the nsTableFrame already drew them, unless a child has its own + // stacking context, in which case the child won't use its passed-in + // BorderBackground list anyway. It does affect cell borders though; this + // lets us get cell borders into the nsTableFrame's BorderBackground list. + nsIFrame* kid = aFrame->GetFirstChild(nsnull); + while (kid) { + nsresult rv = aFrame->BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists); + NS_ENSURE_SUCCESS(rv, rv); + kid = kid->GetNextSibling(); + } + return NS_OK; +} + /* static */ nsresult nsTableFrame::DisplayGenericTablePart(nsDisplayListBuilder* aBuilder, nsFrame* aFrame, const nsRect& aDirtyRect, const nsDisplayListSet& aLists, - PRBool aIsRoot) + PRBool aIsRoot, + DisplayGenericTablePartTraversal aTraversal) { nsDisplayList eventsBorderBackground; // If we need to sort the event backgrounds, then we'll put descendants' @@ -1451,21 +1473,9 @@ nsTableFrame::DisplayGenericTablePart(nsDisplayListBuilder* aBuilder, NS_ENSURE_SUCCESS(rv, rv); } - // This is similar to what nsContainerFrame::BuildDisplayListForNonBlockChildren - // does, except that we allow the children's background and borders to go - // in our BorderBackground list. This doesn't really affect background - // painting --- the children won't actually draw their own backgrounds - // because the nsTableFrame already drew them, unless a child has its own - // stacking context, in which case the child won't use its passed-in - // BorderBackground list anyway. It does affect cell borders though; this - // lets us get cell borders into the nsTableFrame's BorderBackground list. - nsIFrame* kid = aFrame->GetFirstChild(nsnull); - while (kid) { - nsresult rv = aFrame->BuildDisplayListForChild(aBuilder, kid, aDirtyRect, *lists); - NS_ENSURE_SUCCESS(rv, rv); - kid = kid->GetNextSibling(); - } - + nsresult rv = aTraversal(aBuilder, aFrame, aDirtyRect, *lists); + NS_ENSURE_SUCCESS(rv, rv); + if (sortEventBackgrounds) { // Ensure that the table frame event background goes before the // table rowgroups event backgrounds, before the table row event backgrounds, diff --git a/mozilla/layout/tables/nsTableFrame.h b/mozilla/layout/tables/nsTableFrame.h index e1caed5c29a..b5f6a7d3858 100644 --- a/mozilla/layout/tables/nsTableFrame.h +++ b/mozilla/layout/tables/nsTableFrame.h @@ -265,12 +265,21 @@ public: * all the the frame's children. * @param aIsRoot true if aFrame is the table frame or a table part which * happens to be the root of a stacking context + * @param aTraversal if non-null, this is the frame to start with, and we + * can stop as soon as we find a frame whose overflowRect has y-coordinate + * below the dirty rect and is non-empty */ + typedef nsresult (* DisplayGenericTablePartTraversal) + (nsDisplayListBuilder* aBuilder, nsFrame* aFrame, + const nsRect& aDirtyRect, const nsDisplayListSet& aLists); + static nsresult GenericTraversal(nsDisplayListBuilder* aBuilder, nsFrame* aFrame, + const nsRect& aDirtyRect, const nsDisplayListSet& aLists); static nsresult DisplayGenericTablePart(nsDisplayListBuilder* aBuilder, nsFrame* aFrame, const nsRect& aDirtyRect, const nsDisplayListSet& aLists, - PRBool aIsRoot); + PRBool aIsRoot, + DisplayGenericTablePartTraversal aTraversal = GenericTraversal); // Return the closest sibling of aPriorChildFrame (including aPriroChildFrame) // of type aChildType. diff --git a/mozilla/layout/tables/nsTableRowGroupFrame.cpp b/mozilla/layout/tables/nsTableRowGroupFrame.cpp index e6dace838a2..62b2b54b4ab 100644 --- a/mozilla/layout/tables/nsTableRowGroupFrame.cpp +++ b/mozilla/layout/tables/nsTableRowGroupFrame.cpp @@ -215,6 +215,58 @@ PaintRowGroupBackground(nsIFrame* aFrame, nsIRenderingContext* aCtx, painter.PaintRowGroup(NS_STATIC_CAST(nsTableRowGroupFrame*, aFrame)); } +// Handle the child-traversal part of DisplayGenericTablePart +static nsresult +DisplayRows(nsDisplayListBuilder* aBuilder, nsFrame* aFrame, + const nsRect& aDirtyRect, const nsDisplayListSet& aLists) +{ + nscoord overflowAbove; + nsTableRowGroupFrame* f = NS_STATIC_CAST(nsTableRowGroupFrame*, aFrame); + // Don't try to use the row cursor if we have to descend into placeholders; + // we might have rows containing placeholders, where the row's overflow + // area doesn't intersect the dirty rect but we need to descend into the row + // to see out of flows + nsIFrame* kid = f->GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO + ? nsnull : f->GetFirstRowContaining(aDirtyRect.y, &overflowAbove); + + if (kid) { + // have a cursor, use it + while (kid) { + if (kid->GetRect().y - overflowAbove >= aDirtyRect.YMost()) + break; + nsresult rv = f->BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists); + NS_ENSURE_SUCCESS(rv, rv); + kid = kid->GetNextSibling(); + } + return NS_OK; + } + + // No cursor. Traverse children the hard way and build a cursor while we're at it + nsTableRowGroupFrame::FrameCursorData* cursor = f->SetupRowCursor(); + kid = f->GetFirstChild(nsnull); + while (kid) { + nsresult rv = f->BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists); + if (NS_FAILED(rv)) { + f->ClearRowCursor(); + return rv; + } + + if (cursor) { + if (!cursor->AppendFrame(kid)) { + f->ClearRowCursor(); + return NS_ERROR_OUT_OF_MEMORY; + } + } + + kid = kid->GetNextSibling(); + } + if (cursor) { + cursor->FinishBuildingCursor(); + } + + return NS_OK; +} + NS_IMETHODIMP nsTableRowGroupFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, const nsRect& aDirtyRect, @@ -232,8 +284,9 @@ nsTableRowGroupFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, nsDisplayGeneric(this, PaintRowGroupBackground, "TableRowGroupBackground")); NS_ENSURE_SUCCESS(rv, rv); } - - return nsTableFrame::DisplayGenericTablePart(aBuilder, this, aDirtyRect, aLists, isRoot); + + return nsTableFrame::DisplayGenericTablePart(aBuilder, this, aDirtyRect, + aLists, isRoot, DisplayRows); } PRIntn @@ -1249,6 +1302,9 @@ nsTableRowGroupFrame::Reflow(nsPresContext* aPresContext, nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this); if (!tableFrame) return NS_ERROR_NULL_POINTER; + // Row geometry may be going to change so we need to invalidate any row cursor. + ClearRowCursor(); + // see if a special height reflow needs to occur due to having a pct height if (!NeedSpecialReflow()) nsTableFrame::CheckRequestSpecialHeightReflow(aReflowState); @@ -1369,6 +1425,8 @@ nsTableRowGroupFrame::AppendFrames(nsIAtom* aListName, { NS_ASSERTION(!aListName, "unexpected child list"); + ClearRowCursor(); + // collect the new row frames in an array nsAutoVoidArray rows; for (nsIFrame* rowFrame = aFrameList; rowFrame; @@ -1417,6 +1475,8 @@ nsTableRowGroupFrame::InsertFrames(nsIAtom* aListName, NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this, "inserting after sibling frame with different parent"); + ClearRowCursor(); + nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this); if (!tableFrame) return NS_ERROR_NULL_POINTER; @@ -1473,6 +1533,8 @@ nsTableRowGroupFrame::RemoveFrame(nsIAtom* aListName, { NS_ASSERTION(!aListName, "unexpected child list"); + ClearRowCursor(); + nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this); if (tableFrame) { if (nsLayoutAtoms::tableRowFrame == aOldFrame->GetType()) { @@ -2102,3 +2164,96 @@ nsTableRowGroupFrame::GetNextSiblingOnLine(nsIFrame*& aFrame, //end nsLineIterator methods +static void +DestroyFrameCursorData(void* aObject, nsIAtom* aPropertyName, + void* aPropertyValue, void* aData) +{ + delete NS_STATIC_CAST(nsTableRowGroupFrame::FrameCursorData*, aPropertyValue); +} + +void +nsTableRowGroupFrame::ClearRowCursor() +{ + if (!(GetStateBits() & NS_ROWGROUP_HAS_ROW_CURSOR)) + return; + + RemoveStateBits(NS_ROWGROUP_HAS_ROW_CURSOR); + DeleteProperty(nsLayoutAtoms::rowCursorProperty); +} + +nsTableRowGroupFrame::FrameCursorData* +nsTableRowGroupFrame::SetupRowCursor() +{ + if (GetStateBits() & NS_ROWGROUP_HAS_ROW_CURSOR) { + // We already have a valid row cursor. Don't waste time rebuilding it. + return nsnull; + } + + nsIFrame* f = mFrames.FirstChild(); + PRInt32 count; + for (count = 0; f && count < MIN_ROWS_NEEDING_CURSOR; ++count) { + f = f->GetNextSibling(); + } + if (!f) { + // Less than MIN_ROWS_NEEDING_CURSOR rows, so just don't bother + return nsnull; + } + + FrameCursorData* data = new FrameCursorData(); + if (!data) + return nsnull; + SetProperty(nsLayoutAtoms::rowCursorProperty, data, DestroyFrameCursorData); + AddStateBits(NS_ROWGROUP_HAS_ROW_CURSOR); + return data; +} + +nsIFrame* +nsTableRowGroupFrame::GetFirstRowContaining(nscoord aY, nscoord* aOverflowAbove) +{ + if (!(GetStateBits() & NS_ROWGROUP_HAS_ROW_CURSOR)) + return nsnull; + + FrameCursorData* property = NS_STATIC_CAST(FrameCursorData*, + GetProperty(nsLayoutAtoms::rowCursorProperty)); + PRUint32 cursorIndex = property->mCursorIndex; + PRUint32 frameCount = property->mFrames.Length(); + if (cursorIndex >= frameCount) + return nsnull; + nsIFrame* cursorFrame = property->mFrames[cursorIndex]; + + // The cursor's frame list excludes frames with empty overflow-area, so + // we don't need to check that here. + + // We use property->mOverflowBelow here instead of computing the frame's + // true overflowArea.YMost(), because it is essential for the thresholds + // to form a monotonically increasing sequence. Otherwise we would break + // encountering a row whose overflowArea.YMost() is <= aY but which has + // a row above it containing cell(s) that span to include aY. + while (cursorIndex > 0 && + cursorFrame->GetRect().YMost() + property->mOverflowBelow > aY) { + --cursorIndex; + cursorFrame = property->mFrames[cursorIndex]; + } + while (cursorIndex + 1 < frameCount && + cursorFrame->GetRect().YMost() + property->mOverflowBelow <= aY) { + ++cursorIndex; + cursorFrame = property->mFrames[cursorIndex]; + } + + property->mCursorIndex = cursorIndex; + *aOverflowAbove = property->mOverflowAbove; + return cursorFrame; +} + +PRBool +nsTableRowGroupFrame::FrameCursorData::AppendFrame(nsIFrame* aFrame) +{ + nsRect overflowRect = aFrame->GetOverflowRect(); + if (overflowRect.IsEmpty()) + return PR_TRUE; + nscoord overflowAbove = -overflowRect.y; + nscoord overflowBelow = overflowRect.YMost() - aFrame->GetSize().height; + mOverflowAbove = PR_MAX(mOverflowAbove, overflowAbove); + mOverflowBelow = PR_MAX(mOverflowBelow, overflowBelow); + return mFrames.AppendElement(aFrame) != nsnull; +} diff --git a/mozilla/layout/tables/nsTableRowGroupFrame.h b/mozilla/layout/tables/nsTableRowGroupFrame.h index de19ec3c817..c0c4efb24dd 100644 --- a/mozilla/layout/tables/nsTableRowGroupFrame.h +++ b/mozilla/layout/tables/nsTableRowGroupFrame.h @@ -42,6 +42,7 @@ #include "nsIAtom.h" #include "nsILineIterator.h" #include "nsTablePainter.h" +#include "nsTArray.h" class nsTableFrame; class nsTableRowFrame; @@ -89,6 +90,10 @@ struct nsRowGroupReflowState { #define NS_ROWGROUP_NEED_SPECIAL_REFLOW 0x20000000 // the next is also used on rows (see nsTableRowGroupFrame::InitRepeatedFrame) #define NS_REPEATED_ROW_OR_ROWGROUP 0x10000000 +#define NS_ROWGROUP_HAS_ROW_CURSOR 0x08000000 + +#define MIN_ROWS_NEEDING_CURSOR 20 + /** * nsTableRowGroupFrame is the frame that maps row groups * (HTML tags THEAD, TFOOT, and TBODY). This class cannot be reused @@ -250,6 +255,57 @@ public: #endif NS_IMETHOD GetNextSiblingOnLine(nsIFrame*& aFrame, PRInt32 aLineNumber); + // row cursor methods to speed up searching for the row(s) + // containing a point. The basic idea is that we set the cursor + // property if the rows' y and yMosts are non-decreasing (considering only + // rows with nonempty overflowAreas --- empty overflowAreas never participate + // in event handling or painting), and the rowgroup has sufficient number of + // rows. The cursor property points to a "recently used" row. If we get a + // series of requests that work on rows "near" the cursor, then we can find + // those nearby rows quickly by starting our search at the cursor. + // This code is based on the line cursor code in nsBlockFrame. It's more general + // though, and could be extracted and used elsewhere. + struct FrameCursorData { + nsTArray mFrames; + PRUint32 mCursorIndex; + nscoord mOverflowAbove; + nscoord mOverflowBelow; + + FrameCursorData() + : mFrames(MIN_ROWS_NEEDING_CURSOR), mCursorIndex(0), mOverflowAbove(0), + mOverflowBelow(0) {} + + PRBool AppendFrame(nsIFrame* aFrame); + + void FinishBuildingCursor() { + mFrames.Compact(); + } + }; + + // Clear out row cursor because we're disturbing the rows (e.g., Reflow) + void ClearRowCursor(); + + /** + * Get the first row that might contain y-coord 'aY', or nsnull if you must search + * all rows. + * The actual row returned might not contain 'aY', but if not, it is guaranteed + * to be before any row which does contain 'aY'. + * aOverflowAbove is the maximum over all rows of -row.GetOverflowRect().y. + * To find all rows that intersect the vertical interval aY/aYMost, call + * GetFirstRowContaining(aY, &overflowAbove), and then iterate through all + * rows until reaching a row where row->GetRect().y - overflowAbove >= aYMost. + * That row and all subsequent rows cannot intersect the interval. + */ + nsIFrame* GetFirstRowContaining(nscoord aY, nscoord* aOverflowAbove); + + /** + * Set up the row cursor. After this, call AppendFrame for every + * child frame in sibling order. Ensure that the child frame y and YMost values + * form non-decreasing sequences (should always be true for table rows); + * if this is violated, call ClearRowCursor(). If we return nsnull, then we + * decided not to use a cursor or we already have one set up. + */ + FrameCursorData* SetupRowCursor(); protected: nsTableRowGroupFrame(nsStyleContext* aContext);